mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 02:44:55 +00:00
Apply review feedback
Signed-off-by: Rémi de Juvigny <remi@hey.com>
This commit is contained in:
parent
e327d8ccfd
commit
24d42f385f
@ -48,23 +48,42 @@ To create a Strapi template, you need to publish a public GitHub repository that
|
||||
|
||||
First, a template's only concern should be to adapt Strapi to a use case. It should not deal with environment-specific configs, like databases, or upload and email providers. This is to make sure that templates stay maintainable, and to avoid conflicts with other CLI options like `--quickstart`.
|
||||
|
||||
Second, a template must follow the following file structure. If any unexpected file or directory is found, the installation will crash.
|
||||
Second, a template must follow the following file structure.
|
||||
|
||||
### File structure
|
||||
|
||||
- `README.md`: to document your template
|
||||
- `.gitignore`: to remove files from Git
|
||||
- `template.json`: to extend the Strapi app's default `package.json`
|
||||
- `/template`: where you can extend the file contents of a Strapi project. All the children are optional
|
||||
- `README.md`: the readme of an app made with this template
|
||||
- `.env.example`: to specify required environment variables
|
||||
- `api/`: for collections and single types
|
||||
- `components/` for components
|
||||
- `config/` can only include the `functions` directory (things like `bootstrap.js` or `404.js`), because other config files are environment-specific.
|
||||
- `data/` to store the data imported by a seed script
|
||||
- `plugins/` for custom Strapi plugins
|
||||
- `public/` to serve files
|
||||
- `scripts/` for custom scripts
|
||||
You can add as many files as you want to the root of your template repository. But it must at least have a `template.json` file and a `template` directory.
|
||||
|
||||
The `template.json` is used to extend the Strapi app's default `package.json`. You can put all the properties that should overwrite the default `package.json` in a root `package` property. For example, a `template.json` might look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"package": {
|
||||
"dependencies": {
|
||||
"strapi-plugin-graphql": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"custom": "node ./scripts/custom.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `template` directory is where you can extend the file contents of a Strapi project. All the children are optional, you should only include the files that will overwrite the default Strapi app.
|
||||
|
||||
Only the following contents are allowed inside the `template` directory:
|
||||
|
||||
- `README.md`: the readme of an app made with this template
|
||||
- `.env.example`: to specify required environment variables
|
||||
- `api/`: for collections and single types
|
||||
- `components/` for components
|
||||
- `config/` can only include the `functions` directory (things like `bootstrap.js` or `404.js`), because other config files are environment-specific.
|
||||
- `data/` to store the data imported by a seed script
|
||||
- `plugins/` for custom Strapi plugins
|
||||
- `public/` to serve files
|
||||
- `scripts/` for custom scripts
|
||||
|
||||
If any unexpected file or directory is found, the installation will crash.
|
||||
|
||||
### Step by step
|
||||
|
||||
@ -73,8 +92,8 @@ After reading the above rules, follow these steps to create your template:
|
||||
1. Create a standard Strapi app with `create-strapi-app`, using the `--quickstart` option.
|
||||
2. Customize your app to match the needs of your use case.
|
||||
3. Outside of Strapi, create a new directory for your template.
|
||||
4. Create `template.json`, `.gitignore` and `README.md` files in your template directory.
|
||||
5. If you have modified your app's `package.json`, include these changes (and _only_ these changes) in `template.json`. Otherwise, leave it as an empty object.
|
||||
4. Create a `template.json` file in your template directory.
|
||||
5. If you have modified your app's `package.json`, include these changes (and _only_ these changes) in `template.json` in a `package` property. Otherwise, leave it as an empty object.
|
||||
6. Create a `/template` subdirectory.
|
||||
7. Think of all the files you have modified in your app, and copy them to the `/template` directory
|
||||
8. Publish the root template project on GitHub. Make sure that the repository is public, and that the code is on the `master` branch.
|
||||
|
||||
@ -2,9 +2,7 @@
|
||||
|
||||
const commander = require('commander');
|
||||
const packageJson = require('./package.json');
|
||||
// TODO: restore this line instead:
|
||||
// const generateNewApp = require('strapi-generate-new');
|
||||
const generateNewApp = require('../strapi-generate-new');
|
||||
const generateNewApp = require('strapi-generate-new');
|
||||
|
||||
const program = new commander.Command(packageJson.name);
|
||||
|
||||
|
||||
@ -70,7 +70,12 @@ module.exports = async function createProject(scope, { client, connection, depen
|
||||
// merge template files if a template is specified
|
||||
const hasTemplate = Boolean(scope.template);
|
||||
if (hasTemplate) {
|
||||
await mergeTemplate(scope.template, rootPath);
|
||||
try {
|
||||
await mergeTemplate(scope.template, rootPath);
|
||||
} catch (error) {
|
||||
await fse.remove(scope.rootPath);
|
||||
stopProcess(`⛔️ Template installation failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
await fse.remove(scope.rootPath);
|
||||
|
||||
@ -3,40 +3,60 @@
|
||||
const path = require('path');
|
||||
const fse = require('fs-extra');
|
||||
const fetch = require('node-fetch');
|
||||
const unzip = require('unzip-stream');
|
||||
const tar = require('tar');
|
||||
const _ = require('lodash');
|
||||
const chalk = require('chalk');
|
||||
const gitInfo = require('hosted-git-info');
|
||||
|
||||
// Specify all the files and directories a template can have
|
||||
const allowChildren = '*';
|
||||
const allowedTemplateTree = {
|
||||
// Root template files
|
||||
'template.json': true,
|
||||
const allowedTemplateContents = {
|
||||
'README.md': true,
|
||||
'.gitignore': true,
|
||||
// Template contents
|
||||
template: {
|
||||
'README.md': true,
|
||||
'.env.example': true,
|
||||
api: allowChildren,
|
||||
components: allowChildren,
|
||||
config: {
|
||||
functions: allowChildren,
|
||||
},
|
||||
data: allowChildren,
|
||||
plugins: allowChildren,
|
||||
public: allowChildren,
|
||||
scripts: allowChildren,
|
||||
'.env.example': true,
|
||||
api: allowChildren,
|
||||
components: allowChildren,
|
||||
config: {
|
||||
functions: allowChildren,
|
||||
},
|
||||
data: allowChildren,
|
||||
plugins: allowChildren,
|
||||
public: allowChildren,
|
||||
scripts: allowChildren,
|
||||
};
|
||||
|
||||
// Make sure the template has the required top-level structure
|
||||
async function checkTemplateRootStructure(templatePath) {
|
||||
// Make sure the root of the repo has a template.json and a template/ folder
|
||||
const templateJsonPath = path.resolve(templatePath, 'template.json');
|
||||
try {
|
||||
const hasTemplateJson = !fse.statSync(templateJsonPath).isDirectory();
|
||||
if (!hasTemplateJson) {
|
||||
throw Error();
|
||||
}
|
||||
} catch (error) {
|
||||
throw Error(`A template must have a root ${chalk.green('template.json')} file`);
|
||||
}
|
||||
|
||||
// Make sure the root of the repo has a template.json and a template/ folder
|
||||
const templateDirPath = path.resolve(templatePath, 'template');
|
||||
try {
|
||||
const hasTemplateDir = fse.statSync(templateDirPath).isDirectory();
|
||||
if (!hasTemplateDir) {
|
||||
throw Error();
|
||||
}
|
||||
} catch (error) {
|
||||
throw Error(`A template must have a root ${chalk.green('template/')} directory`);
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse template tree to make sure each file and folder is allowed
|
||||
async function checkTemplateStructure(templatePath) {
|
||||
// Recursively check if each item in the template is allowed
|
||||
async function checkTemplateContentsStructure(templateContentsPath) {
|
||||
// Recursively check if each item in a directory is allowed
|
||||
const checkPathContents = (pathToCheck, parents) => {
|
||||
const contents = fse.readdirSync(pathToCheck);
|
||||
contents.forEach(item => {
|
||||
const nextParents = [...parents, item];
|
||||
const matchingTreeValue = _.get(allowedTemplateTree, nextParents);
|
||||
const matchingTreeValue = _.get(allowedTemplateContents, nextParents);
|
||||
|
||||
// Treat files and directories separately
|
||||
const itemPath = path.resolve(pathToCheck, item);
|
||||
@ -44,7 +64,9 @@ async function checkTemplateStructure(templatePath) {
|
||||
|
||||
if (matchingTreeValue === undefined) {
|
||||
// Unknown paths are forbidden
|
||||
throw Error(`Illegal template structure, unknown path ${nextParents.join('/')}`);
|
||||
throw Error(
|
||||
`Illegal template structure, unknown path ${chalk.green(nextParents.join('/'))}`
|
||||
);
|
||||
}
|
||||
|
||||
if (matchingTreeValue === true) {
|
||||
@ -53,8 +75,8 @@ async function checkTemplateStructure(templatePath) {
|
||||
return;
|
||||
}
|
||||
throw Error(
|
||||
`Illegal template structure, expected a file and got a directory at ${nextParents.join(
|
||||
'/'
|
||||
`Illegal template structure, expected a file and got a directory at ${chalk.green(
|
||||
nextParents.join('/')
|
||||
)}`
|
||||
);
|
||||
}
|
||||
@ -67,59 +89,60 @@ async function checkTemplateStructure(templatePath) {
|
||||
// Check if the contents of the directory are allowed
|
||||
checkPathContents(itemPath, nextParents);
|
||||
} else {
|
||||
throw Error(`Illegal template structure, unknow file ${nextParents.join('/')}`);
|
||||
throw Error(
|
||||
`Illegal template structure, unknow file ${chalk.green(nextParents.join('/'))}`
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
checkPathContents(templatePath, []);
|
||||
checkPathContents(templateContentsPath, []);
|
||||
}
|
||||
|
||||
function getRepoInfo(githubURL) {
|
||||
const { type, user, project } = gitInfo.fromUrl(githubURL);
|
||||
// Make sure it's a github url
|
||||
const address = githubURL.split('://')[1];
|
||||
if (!address.startsWith('github.com')) {
|
||||
if (type !== 'github') {
|
||||
throw Error('A Strapi template can only be a GitHub URL');
|
||||
}
|
||||
|
||||
// Parse github address into parts
|
||||
const [, username, name, ...rest] = address.split('/');
|
||||
const isRepo = username != null && name != null;
|
||||
if (!isRepo || rest.length > 0) {
|
||||
throw Error('A template URL must be the root of a GitHub repository');
|
||||
}
|
||||
|
||||
return { username, name };
|
||||
return { user, project };
|
||||
}
|
||||
|
||||
async function downloadGithubRepo(repoInfo, rootPath) {
|
||||
const { username, name, branch = 'master' } = repoInfo;
|
||||
const codeload = `https://codeload.github.com/${username}/${name}/zip/${branch}`;
|
||||
const templatePath = path.resolve(rootPath, 'tmp-template');
|
||||
async function downloadGithubRepo(repoInfo, templatePath) {
|
||||
// Download from GitHub
|
||||
const { user, project } = repoInfo;
|
||||
const codeload = `https://codeload.github.com/${user}/${project}/tar.gz/master`;
|
||||
const response = await fetch(codeload);
|
||||
await new Promise(resolve => {
|
||||
response.body.pipe(unzip.Extract({ path: templatePath })).on('close', resolve);
|
||||
});
|
||||
|
||||
return templatePath;
|
||||
// Store locally
|
||||
fse.mkdirSync(templatePath);
|
||||
await new Promise(resolve => {
|
||||
response.body.pipe(tar.extract({ strip: 1, cwd: templatePath })).on('close', resolve);
|
||||
});
|
||||
}
|
||||
|
||||
// Merge the template's template.json into the Strapi project's package.json
|
||||
async function mergePackageJSON(rootPath, templatePath, repoInfo) {
|
||||
// Import the package.json and template.json templates
|
||||
// Import the package.json and template.json objects
|
||||
const packageJSON = require(path.resolve(rootPath, 'package.json'));
|
||||
const templateJSON = require(path.resolve(templatePath, 'template.json'));
|
||||
|
||||
if (!templateJSON.package) {
|
||||
// Nothing to overwrite
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the template.json doesn't overwrite the UUID
|
||||
if (templateJSON.strapi && templateJSON.strapi.uuid) {
|
||||
if (templateJSON.package.strapi && templateJSON.package.strapi.uuid) {
|
||||
throw Error('A template cannot overwrite the Strapi UUID');
|
||||
}
|
||||
|
||||
// Use lodash to deeply merge them
|
||||
const mergedConfig = _.merge(packageJSON, templateJSON);
|
||||
const mergedConfig = _.merge(packageJSON, templateJSON.package);
|
||||
|
||||
// Prefix Strapi UUID with starter info
|
||||
const prefix = `STARTER:${repoInfo.username}/${repoInfo.name}:`;
|
||||
const prefix = `STARTER:${repoInfo.user}/${repoInfo.project}:`;
|
||||
mergedConfig.strapi = {
|
||||
uuid: prefix + mergedConfig.strapi.uuid,
|
||||
};
|
||||
@ -146,19 +169,27 @@ async function mergeFilesAndDirectories(rootPath, templatePath) {
|
||||
}
|
||||
|
||||
module.exports = async function mergeTemplate(templateUrl, rootPath) {
|
||||
// Download template repository to a temporary directory
|
||||
// Parse template info
|
||||
const repoInfo = getRepoInfo(templateUrl);
|
||||
console.log(`Installing ${repoInfo.username}/${repoInfo.name} template.`);
|
||||
const templateParentPath = await downloadGithubRepo(repoInfo, rootPath);
|
||||
const templatePath = path.resolve(templateParentPath, fse.readdirSync(templateParentPath)[0]);
|
||||
const { user, project } = repoInfo;
|
||||
console.log(`Installing ${chalk.yellow(`${user}/${project}`)} template.`);
|
||||
|
||||
// Download template repository to a temporary directory
|
||||
const templatePath = path.resolve(rootPath, '.tmp-template');
|
||||
try {
|
||||
await downloadGithubRepo(repoInfo, templatePath);
|
||||
} catch (error) {
|
||||
throw Error(`Could not download ${chalk.yellow(`${user}/${project}`)} repository`);
|
||||
}
|
||||
|
||||
// Make sure the downloaded template matches the required format
|
||||
await checkTemplateStructure(templatePath);
|
||||
await checkTemplateRootStructure(templatePath);
|
||||
await checkTemplateContentsStructure(path.resolve(templatePath, 'template'));
|
||||
|
||||
// Merge contents of the template in the project
|
||||
await mergePackageJSON(rootPath, templatePath, repoInfo);
|
||||
await mergeFilesAndDirectories(rootPath, templatePath);
|
||||
|
||||
// Delete the downloaded template repo
|
||||
await fse.remove(templateParentPath);
|
||||
await fse.remove(templatePath);
|
||||
};
|
||||
|
||||
@ -17,12 +17,13 @@
|
||||
"chalk": "^2.4.2",
|
||||
"execa": "^1.0.0",
|
||||
"fs-extra": "^8.0.1",
|
||||
"hosted-git-info": "3.0.5",
|
||||
"inquirer": "^6.3.1",
|
||||
"lodash": "4.17.19",
|
||||
"node-fetch": "^1.7.3",
|
||||
"node-machine-id": "^1.1.10",
|
||||
"ora": "^3.4.0",
|
||||
"unzip-stream": "0.3.0",
|
||||
"tar": "6.0.5",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
72
yarn.lock
72
yarn.lock
@ -4622,14 +4622,6 @@ binary-extensions@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
|
||||
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
|
||||
|
||||
binary@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79"
|
||||
integrity sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=
|
||||
dependencies:
|
||||
buffers "~0.1.1"
|
||||
chainsaw "~0.1.0"
|
||||
|
||||
bindings@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
|
||||
@ -4939,11 +4931,6 @@ buffer@^5.1.0:
|
||||
base64-js "^1.0.2"
|
||||
ieee754 "^1.1.4"
|
||||
|
||||
buffers@~0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb"
|
||||
integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s=
|
||||
|
||||
buildmail@3.10.0:
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/buildmail/-/buildmail-3.10.0.tgz#c6826d716e7945bb6f6b1434b53985e029a03159"
|
||||
@ -5178,13 +5165,6 @@ ccount@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17"
|
||||
integrity sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==
|
||||
|
||||
chainsaw@~0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98"
|
||||
integrity sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=
|
||||
dependencies:
|
||||
traverse ">=0.3.0 <0.4"
|
||||
|
||||
chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
|
||||
@ -5302,6 +5282,11 @@ chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.3:
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
|
||||
|
||||
chownr@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
|
||||
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
|
||||
|
||||
chrome-trace-event@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4"
|
||||
@ -9293,6 +9278,13 @@ homedir-polyfill@^1.0.1:
|
||||
dependencies:
|
||||
parse-passwd "^1.0.0"
|
||||
|
||||
hosted-git-info@3.0.5:
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.5.tgz#bea87905ef7317442e8df3087faa3c842397df03"
|
||||
integrity sha512-i4dpK6xj9BIpVOTboXIlKG9+8HMKggcrMX7WA24xZtKwX0TPelq/rbaS5rCKeNX8sJXZJGdSxpnEGtta+wismQ==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
hosted-git-info@^2.1.4, hosted-git-info@^2.7.1:
|
||||
version "2.8.8"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
|
||||
@ -12116,6 +12108,13 @@ lru-cache@^5.0.0, lru-cache@^5.1.1:
|
||||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
lru-cache@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
|
||||
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
lru_map@^0.3.3:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd"
|
||||
@ -12643,6 +12642,14 @@ minizlib@^2.1.0:
|
||||
minipass "^3.0.0"
|
||||
yallist "^4.0.0"
|
||||
|
||||
minizlib@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
|
||||
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
|
||||
dependencies:
|
||||
minipass "^3.0.0"
|
||||
yallist "^4.0.0"
|
||||
|
||||
mississippi@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
|
||||
@ -18134,6 +18141,18 @@ tar-stream@^2.0.0, tar-stream@^2.1.0:
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^3.1.1"
|
||||
|
||||
tar@6.0.5:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f"
|
||||
integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==
|
||||
dependencies:
|
||||
chownr "^2.0.0"
|
||||
fs-minipass "^2.0.0"
|
||||
minipass "^3.0.0"
|
||||
minizlib "^2.1.1"
|
||||
mkdirp "^1.0.3"
|
||||
yallist "^4.0.0"
|
||||
|
||||
tar@^4, tar@^4.4.10, tar@^4.4.12, tar@^4.4.8:
|
||||
version "4.4.13"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
|
||||
@ -18494,11 +18513,6 @@ tr46@^2.0.2:
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
|
||||
"traverse@>=0.3.0 <0.4":
|
||||
version "0.3.9"
|
||||
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9"
|
||||
integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=
|
||||
|
||||
tree-kill@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||
@ -18849,14 +18863,6 @@ unzip-response@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97"
|
||||
integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=
|
||||
|
||||
unzip-stream@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/unzip-stream/-/unzip-stream-0.3.0.tgz#c30c054cd6b0d64b13a23cd3ece911eb0b2b52d8"
|
||||
integrity sha512-NG1h/MdGIX3HzyqMjyj1laBCmlPYhcO4xEy7gEqqzGiSLw7XqDQCnY4nYSn5XSaH8mQ6TFkaujrO8d/PIZN85A==
|
||||
dependencies:
|
||||
binary "^0.3.0"
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
upath@^1.1.1, upath@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user