From 4fef307c468855d454e4982db552cca69accc962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Fri, 24 Sep 2021 14:49:41 +0200 Subject: [PATCH 01/35] Re-enable templates --- .../create-strapi-app/utils/prompt-user.js | 20 +++++++++---------- packages/generators/app/lib/index.js | 12 ----------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/packages/cli/create-strapi-app/utils/prompt-user.js b/packages/cli/create-strapi-app/utils/prompt-user.js index 60014e819a..4c41cf1a6a 100644 --- a/packages/cli/create-strapi-app/utils/prompt-user.js +++ b/packages/cli/create-strapi-app/utils/prompt-user.js @@ -57,11 +57,12 @@ async function getTemplateQuestion() { } /** - * + * @param {string|null} projectName - The name of the project + * @param {string|null} template - The template the project should use * @returns Array of prompt question objects */ // TODO: re-enabled once the template have been migrated to V4 -async function getPromptQuestions(projectName /*, template */) { +async function getPromptQuestions(projectName, template) { return [ { type: 'input', @@ -85,14 +86,13 @@ async function getPromptQuestions(projectName /*, template */) { }, ], }, - // TODO: re-enabled once the template have been migrated to V4 - // { - // type: 'confirm', - // name: 'useTemplate', - // when: !template, - // message: - // 'Would you like to use a template? (Templates are Strapi configurations designed for a specific use case)', - // }, + { + type: 'confirm', + name: 'useTemplate', + when: !template, + message: + 'Would you like to use a template? (Templates are Strapi configurations designed for a specific use case)', + }, ]; } diff --git a/packages/generators/app/lib/index.js b/packages/generators/app/lib/index.js index 66e29dda89..f5d2f03458 100644 --- a/packages/generators/app/lib/index.js +++ b/packages/generators/app/lib/index.js @@ -18,19 +18,7 @@ sentry.init({ dsn: 'https://841d2b2c9b4d4b43a4cde92794cb705a@sentry.io/1762059', }); -// TODO: to remove after the templates are updated for V4 -const warnTemplatesAreDisabled = cliArguments => { - if (cliArguments.template) { - console.log( - '❌ Templates have been disabled for the Beta version of V4 and will be re-enabled soon!' - ); - process.exit(); - } -}; - module.exports = (projectDirectory, cliArguments) => { - warnTemplatesAreDisabled(cliArguments); - checkRequirements(); const rootPath = resolve(projectDirectory); From 0cc1c0bea041b34d5dcc0caedd8a965c1443e2cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Fri, 24 Sep 2021 15:09:28 +0200 Subject: [PATCH 02/35] Update allowed template contents --- .../generators/app/lib/utils/merge-template.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index a2f44a2328..9513036cf0 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -9,19 +9,22 @@ const chalk = require('chalk'); const { getRepoInfo, downloadGitHubRepo } = require('./fetch-github'); // Specify all the files and directories a template can have -const allowChildren = '*'; +const allowFile = Symbol(); +const allowChildren = Symbol(); const allowedTemplateContents = { - 'README.md': true, - '.env.example': true, + 'README.md': allowFile, + '.env.example': allowFile, + 'package.json': allowFile, src: { + admin: allowChildren, api: allowChildren, components: allowChildren, + middlewares: allowChildren, + policies: allowChildren, plugins: allowChildren, }, - config: { - functions: allowChildren, - }, data: allowChildren, + database: allowChildren, public: allowChildren, scripts: allowChildren, }; @@ -135,7 +138,7 @@ async function checkTemplateContentsStructure(templateContentsPath) { ); } - if (matchingTreeValue === true) { + if (matchingTreeValue === allowFile) { if (!isDirectory) { // All good, the file is allowed return; From 3656d7b181f29111baacd2988f1f2b0822e6d199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Mon, 27 Sep 2021 19:16:08 +0200 Subject: [PATCH 03/35] Use npm to host templates instead of github --- .../generators/app/lib/utils/fetch-github.js | 94 ------------------- .../app/lib/utils/fetch-npm-template.js | 57 +++++++++++ .../app/lib/utils/merge-template.js | 46 +++++---- packages/generators/app/package.json | 4 +- 4 files changed, 88 insertions(+), 113 deletions(-) delete mode 100644 packages/generators/app/lib/utils/fetch-github.js create mode 100644 packages/generators/app/lib/utils/fetch-npm-template.js diff --git a/packages/generators/app/lib/utils/fetch-github.js b/packages/generators/app/lib/utils/fetch-github.js deleted file mode 100644 index abaf21faf2..0000000000 --- a/packages/generators/app/lib/utils/fetch-github.js +++ /dev/null @@ -1,94 +0,0 @@ -'use strict'; - -const fetch = require('node-fetch'); -const tar = require('tar'); -const parseGitUrl = require('git-url-parse'); -const chalk = require('chalk'); - -const stopProcess = require('./stop-process'); - -function parseShorthand(template) { - // Determine if it is comes from another owner - if (template.includes('/')) { - const [owner, partialName] = template.split('/'); - const name = `strapi-template-${partialName}`; - return { - name, - fullName: `${owner}/${name}`, - }; - } - - const name = `strapi-template-${template}`; - return { - name, - fullName: `strapi/${name}`, - }; -} - -/** - * @param {string} repo The full name of the repository. - */ -async function getDefaultBranch(repo) { - const response = await fetch(`https://api.github.com/repos/${repo}`); - if (!response.ok) { - stopProcess( - `Could not find the information for ${chalk.yellow( - repo - )}. Make sure it is publicly accessible on github.` - ); - } - - const { default_branch } = await response.json(); - return default_branch; -} - -/** - * @param {string} template GitHub URL or shorthand to a template project. - */ -async function getRepoInfo(template) { - const { name, full_name: fullName, ref, filepath, protocols, source } = parseGitUrl(template); - - if (protocols.length === 0) { - const repoInfo = parseShorthand(template); - return { - ...repoInfo, - branch: await getDefaultBranch(repoInfo.fullName), - usedShorthand: true, - }; - } - - if (source !== 'github.com') { - stopProcess(`GitHub URL not found for: ${chalk.yellow(template)}.`); - } - - let branch; - if (ref) { - // Append the filepath to the parsed ref since a branch name could contain '/' - // If so, the rest of the branch name will be considered 'filepath' by 'parseGitUrl' - branch = filepath ? `${ref}/${filepath}` : ref; - } else { - branch = await getDefaultBranch(fullName); - } - - return { name, fullName, branch }; -} - -/** - * @param {string} repoInfo GitHub repository information (full name, branch...). - * @param {string} tmpDir Path to the destination temporary directory. - */ -async function downloadGitHubRepo(repoInfo, tmpDir) { - // Download from GitHub - const { fullName, branch } = repoInfo; - const codeload = `https://codeload.github.com/${fullName}/tar.gz/${branch}`; - const response = await fetch(codeload); - if (!response.ok) { - throw Error(`Could not download the ${chalk.yellow(fullName)} repository.`); - } - - await new Promise(resolve => { - response.body.pipe(tar.extract({ strip: 1, cwd: tmpDir })).on('close', resolve); - }); -} - -module.exports = { getRepoInfo, downloadGitHubRepo }; diff --git a/packages/generators/app/lib/utils/fetch-npm-template.js b/packages/generators/app/lib/utils/fetch-npm-template.js new file mode 100644 index 0000000000..9fac49c3c2 --- /dev/null +++ b/packages/generators/app/lib/utils/fetch-npm-template.js @@ -0,0 +1,57 @@ +'use strict'; + +const execSync = require('child_process').execSync; +const path = require('path'); +// const fetch = require('node-fetch'); +const chalk = require('chalk'); + +/** + * Gets the package version on npm. Will fail if the package does not exist + * @param {string} packageName - Name to look up on npm, may include a specific version + * @returns {Object} + */ +function getPackageInfo(packageName) { + const npmInfo = execSync(`npm view ${packageName} name version --silent`).toString(); + // Use regex to parse name and version from CLI result + const [name, version] = npmInfo.match(/(?<=')(.*?)(?=')/gm); + return { name, version }; +} + +/** + * @param {string} template - The name of the template as provided by the user. Can be a shorthand. + * @returns {Object} - The full name of the template package's name on npm + */ +async function getTemplatePackageInfo(template) { + // Check if template is a s horthand + try { + const longhand = `@strapi/template-${template}`; + const packageInfo = getPackageInfo(longhand); + // Hasn't crashed so it is indeed a shorthand + return packageInfo; + } catch (error) { + // Ignore error, we now know it's not a shorthand + } + // Fetch version of the non-shorthand package + try { + return getPackageInfo(template); + } catch (error) { + throw new Error(`Could not find package ${chalk.green('template.json')} on npm`); + } +} + +/** + * @param {Object} packageInfo - Template's npm package information + * @param {string} packageInfo.name + * @param {string} packageInfo.version + * @param {string} parentDir - Path inside of which we install the template. + */ +function downloadNpmTemplate({ name, version }, parentDir) { + // Download from npm + execSync(`cd ${parentDir} && npm install ${name}@${version} --no-save --silent`).toString(); + + // Return the path of the actual template + const exactTemplatePath = path.resolve(parentDir, 'node_modules', name); + return exactTemplatePath; +} + +module.exports = { getTemplatePackageInfo, downloadNpmTemplate }; diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index 9513036cf0..0ffce69320 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -5,8 +5,7 @@ const path = require('path'); const fse = require('fs-extra'); const _ = require('lodash'); const chalk = require('chalk'); - -const { getRepoInfo, downloadGitHubRepo } = require('./fetch-github'); +const { getTemplatePackageInfo, downloadNpmTemplate } = require('./fetch-npm-template'); // Specify all the files and directories a template can have const allowFile = Symbol(); @@ -15,6 +14,7 @@ const allowedTemplateContents = { 'README.md': allowFile, '.env.example': allowFile, 'package.json': allowFile, + config: allowChildren, src: { admin: allowChildren, api: allowChildren, @@ -30,34 +30,37 @@ const allowedTemplateContents = { }; /** - * merge template with new project being created + * Merge template with new project being created * @param {string} scope project creation params * @param {string} rootPath project path */ module.exports = async function mergeTemplate(scope, rootPath) { // Parse template info - const repoInfo = await getRepoInfo(scope.template); - const { fullName } = repoInfo; - console.log(`Installing ${chalk.yellow(fullName)} template.`); + const templatePackageInfo = await getTemplatePackageInfo(scope.template); + console.log(templatePackageInfo); + console.log(`Installing ${chalk.yellow(templatePackageInfo.name)} template.`); // Download template repository to a temporary directory - const templatePath = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-')); - await downloadGitHubRepo(repoInfo, templatePath); + const templateParentPath = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-')); + const templatePath = downloadNpmTemplate(templatePackageInfo, templateParentPath); // Make sure the downloaded template matches the required format const { templateConfig } = await checkTemplateRootStructure(templatePath, scope); await checkTemplateContentsStructure(path.resolve(templatePath, 'template')); // Merge contents of the template in the project - const fullTemplateUrl = `https://github.com/${fullName}`; - await mergePackageJSON(rootPath, templateConfig, fullTemplateUrl); + await mergePackageJSON({ rootPath, templateConfig, templatePackageInfo }); await mergeFilesAndDirectories(rootPath, templatePath); // Delete the downloaded template repo - await fse.remove(templatePath); + await fse.remove(templateParentPath); }; -// Make sure the template has the required top-level structure +/** + * Make sure the template has the required top-level structure + * @param {string} templatePath - Path of the locally downloaded template + * @param {Object} scope - Information about the Strapi app's config + */ async function checkTemplateRootStructure(templatePath, scope) { // Make sure the root of the repo has a template.json or a template.js file const templateJsonPath = path.join(templatePath, 'template.json'); @@ -118,7 +121,10 @@ async function checkTemplateRootStructure(templatePath, scope) { return { templateConfig }; } -// Traverse template tree to make sure each file and folder is allowed +/** + * Traverse template tree to make sure each file and folder is allowed + * @param {string} templateContentsPath + */ async function checkTemplateContentsStructure(templateContentsPath) { // Recursively check if each item in a directory is allowed const checkPathContents = (pathToCheck, parents) => { @@ -168,8 +174,16 @@ async function checkTemplateContentsStructure(templateContentsPath) { checkPathContents(templateContentsPath, []); } -// Merge the template's template.json into the Strapi project's package.json -async function mergePackageJSON(rootPath, templateConfig, templateUrl) { +/** + * Merge the template's template.json into the Strapi project's package.json + * @param {Object} config + * @param {string} config.rootPath + * @param {string} config.templateConfig + * @param {Object} config.templatePackageInfo - Info about the template's package on npm + * @param {Object} config.templatePackageInfo.name - The name of the template's package on npm + * @param {Object} config.templatePackageInfo.version - The name of the template's package on npm + */ +async function mergePackageJSON({ rootPath, templateConfig, templatePackageName }) { // Import the package.json as an object const packageJSON = require(path.resolve(rootPath, 'package.json')); @@ -187,7 +201,7 @@ async function mergePackageJSON(rootPath, templateConfig, templateUrl) { const mergedConfig = _.merge(packageJSON, templateConfig.package); // Add starter info to package.json - _.set(mergedConfig, 'strapi.template', templateUrl); + _.set(mergedConfig, 'strapi.template', templatePackageName); // Save the merged config as the new package.json const packageJSONPath = path.join(rootPath, 'package.json'); diff --git a/packages/generators/app/package.json b/packages/generators/app/package.json index 473c8a254d..540491fa49 100644 --- a/packages/generators/app/package.json +++ b/packages/generators/app/package.json @@ -1,6 +1,6 @@ { "name": "@strapi/generate-new", - "version": "3.6.8", + "version": "4.0.0-next.13", "description": "Generate a new Strapi application.", "homepage": "https://strapi.io", "keywords": [ @@ -17,13 +17,11 @@ "chalk": "^4.1.1", "execa": "^1.0.0", "fs-extra": "^9.1.0", - "git-url-parse": "^11.4.4", "inquirer": "^6.3.1", "lodash": "4.17.21", "node-fetch": "^2.6.1", "node-machine-id": "^1.1.10", "ora": "^5.4.0", - "tar": "6.1.11", "uuid": "^3.3.2" }, "scripts": { From 44d2cc003868a4e415c7825fee3e6303f7ff87f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 28 Sep 2021 14:20:12 +0200 Subject: [PATCH 04/35] Don't allow config folder --- packages/generators/app/lib/utils/merge-template.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index 0ffce69320..9e0be02eb8 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -14,7 +14,6 @@ const allowedTemplateContents = { 'README.md': allowFile, '.env.example': allowFile, 'package.json': allowFile, - config: allowChildren, src: { admin: allowChildren, api: allowChildren, From 114d69f60bbe919a43479e4cb827a197355c173b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Thu, 30 Sep 2021 11:42:24 +0200 Subject: [PATCH 05/35] Support local templates --- .../app/lib/utils/fetch-npm-template.js | 4 +-- .../app/lib/utils/merge-template.js | 36 +++++++++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/generators/app/lib/utils/fetch-npm-template.js b/packages/generators/app/lib/utils/fetch-npm-template.js index 9fac49c3c2..18a5f23d26 100644 --- a/packages/generators/app/lib/utils/fetch-npm-template.js +++ b/packages/generators/app/lib/utils/fetch-npm-template.js @@ -18,11 +18,11 @@ function getPackageInfo(packageName) { } /** - * @param {string} template - The name of the template as provided by the user. Can be a shorthand. + * @param {string} template - The name of the template as provided by the user. * @returns {Object} - The full name of the template package's name on npm */ async function getTemplatePackageInfo(template) { - // Check if template is a s horthand + // Check if template is a shorthand try { const longhand = `@strapi/template-${template}`; const packageInfo = getPackageInfo(longhand); diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index 9e0be02eb8..1b2a512084 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -34,14 +34,24 @@ const allowedTemplateContents = { * @param {string} rootPath project path */ module.exports = async function mergeTemplate(scope, rootPath) { - // Parse template info - const templatePackageInfo = await getTemplatePackageInfo(scope.template); - console.log(templatePackageInfo); - console.log(`Installing ${chalk.yellow(templatePackageInfo.name)} template.`); + let templatePath; + let templateParentPath; + let templatePackageInfo = {}; + const isLocalTemplate = Boolean(scope.template.match(/^file:/)); - // Download template repository to a temporary directory - const templateParentPath = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-')); - const templatePath = downloadNpmTemplate(templatePackageInfo, templateParentPath); + if (isLocalTemplate) { + // Template is a local directory + console.log('Installing local template.'); + templatePath = path.resolve(rootPath, '..', scope.template.match(/^file:(.*)?$/)[1]); + } else { + // Template should be an npm package. Fetch template info + templatePackageInfo = await getTemplatePackageInfo(scope.template); + console.log(`Installing ${chalk.yellow(templatePackageInfo.name)} template.`); + + // Download template repository to a temporary directory + templateParentPath = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-')); + templatePath = downloadNpmTemplate(templatePackageInfo, templateParentPath); + } // Make sure the downloaded template matches the required format const { templateConfig } = await checkTemplateRootStructure(templatePath, scope); @@ -51,8 +61,10 @@ module.exports = async function mergeTemplate(scope, rootPath) { await mergePackageJSON({ rootPath, templateConfig, templatePackageInfo }); await mergeFilesAndDirectories(rootPath, templatePath); - // Delete the downloaded template repo - await fse.remove(templateParentPath); + // Delete the template directory if it was downloaded + if (!isLocalTemplate) { + await fse.remove(templateParentPath); + } }; /** @@ -182,7 +194,7 @@ async function checkTemplateContentsStructure(templateContentsPath) { * @param {Object} config.templatePackageInfo.name - The name of the template's package on npm * @param {Object} config.templatePackageInfo.version - The name of the template's package on npm */ -async function mergePackageJSON({ rootPath, templateConfig, templatePackageName }) { +async function mergePackageJSON({ rootPath, templateConfig, templatePackageInfo }) { // Import the package.json as an object const packageJSON = require(path.resolve(rootPath, 'package.json')); @@ -200,7 +212,9 @@ async function mergePackageJSON({ rootPath, templateConfig, templatePackageName const mergedConfig = _.merge(packageJSON, templateConfig.package); // Add starter info to package.json - _.set(mergedConfig, 'strapi.template', templatePackageName); + if (templatePackageInfo.name) { + _.set(mergedConfig, 'strapi.template', templatePackageInfo.name); + } // Save the merged config as the new package.json const packageJSONPath = path.join(rootPath, 'package.json'); From 68a7771ffa55badd93d87a3150b8d6f04e116b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Fri, 15 Oct 2021 13:50:47 +0200 Subject: [PATCH 06/35] Fix dependencies --- packages/generators/app/package.json | 2 +- yarn.lock | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/generators/app/package.json b/packages/generators/app/package.json index 540491fa49..efaf9f0337 100644 --- a/packages/generators/app/package.json +++ b/packages/generators/app/package.json @@ -1,6 +1,6 @@ { "name": "@strapi/generate-new", - "version": "4.0.0-next.13", + "version": "4.0.0-beta.4", "description": "Generate a new Strapi application.", "homepage": "https://strapi.io", "keywords": [ diff --git a/yarn.lock b/yarn.lock index e3511a9396..e6b414f0a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4602,6 +4602,20 @@ resolve-from "^5.0.0" store2 "^2.12.0" +"@strapi/generate-new@3.6.8": + version "4.0.0-beta.4" + dependencies: + "@sentry/node" "6.3.0" + chalk "^4.1.1" + execa "^1.0.0" + fs-extra "^9.1.0" + inquirer "^6.3.1" + lodash "4.17.21" + node-fetch "^2.6.1" + node-machine-id "^1.1.10" + ora "^5.4.0" + uuid "^3.3.2" + "@strapi/icons@0.0.1-alpha.50": version "0.0.1-alpha.50" resolved "https://registry.yarnpkg.com/@strapi/icons/-/icons-0.0.1-alpha.50.tgz#f62e0d329ab28942eced4aefed9b7cfd73f1373e" @@ -11883,7 +11897,7 @@ git-url-parse@11.4.4: dependencies: git-up "^4.0.0" -git-url-parse@^11.1.2, git-url-parse@^11.4.4: +git-url-parse@^11.1.2: version "11.6.0" resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605" integrity sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g== From a85d7afcf7487cd1b4464fa8944e06621bb8db55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 19 Oct 2021 14:57:19 +0200 Subject: [PATCH 07/35] add allowed files to templates --- packages/generators/app/lib/utils/merge-template.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index 1b2a512084..fa3bd93562 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -15,6 +15,8 @@ const allowedTemplateContents = { '.env.example': allowFile, 'package.json': allowFile, src: { + 'index.js': allowFile, + 'bootstrap.js': allowFile, admin: allowChildren, api: allowChildren, components: allowChildren, From 3f4fb48a418243b6a6850aa61361bb1f47d9c1ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Mon, 25 Oct 2021 12:30:53 +0200 Subject: [PATCH 08/35] Add semver template check --- .../app/lib/utils/merge-template.js | 31 +++++++++++++------ packages/generators/app/package.json | 2 ++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index fa3bd93562..fe9defdd4a 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -5,6 +5,7 @@ const path = require('path'); const fse = require('fs-extra'); const _ = require('lodash'); const chalk = require('chalk'); +const semver = require('semver'); const { getTemplatePackageInfo, downloadNpmTemplate } = require('./fetch-npm-template'); // Specify all the files and directories a template can have @@ -14,16 +15,7 @@ const allowedTemplateContents = { 'README.md': allowFile, '.env.example': allowFile, 'package.json': allowFile, - src: { - 'index.js': allowFile, - 'bootstrap.js': allowFile, - admin: allowChildren, - api: allowChildren, - components: allowChildren, - middlewares: allowChildren, - policies: allowChildren, - plugins: allowChildren, - }, + src: allowChildren, data: allowChildren, database: allowChildren, public: allowChildren, @@ -55,6 +47,9 @@ module.exports = async function mergeTemplate(scope, rootPath) { templatePath = downloadNpmTemplate(templatePackageInfo, templateParentPath); } + // Make sure the template is compatible with this version of strapi + checkTemplateCompat(rootPath, scope.strapiVersion); + // Make sure the downloaded template matches the required format const { templateConfig } = await checkTemplateRootStructure(templatePath, scope); await checkTemplateContentsStructure(path.resolve(templatePath, 'template')); @@ -69,6 +64,22 @@ module.exports = async function mergeTemplate(scope, rootPath) { } }; +function checkTemplateCompat({ rootPath, strapiVersion, templatePackageInfo }) { + const packageJSON = require(path.resolve(rootPath, 'package.json')); + const compatibleStrapiRange = packageJSON.strapi; + // Throw error if not compatible + const isCompatible = semver.satisfies(strapiVersion, compatibleStrapiRange); + if (!isCompatible) { + const { name, version } = templatePackageInfo; + throw new Error(` + The template ${chalk.green( + `${name}@${version}` + )} is not compatible with Strapi version ${strapiVersion}. + It will only work with Strapi versions in the range ${chalk.green(compatibleStrapiRange)}. + `); + } +} + /** * Make sure the template has the required top-level structure * @param {string} templatePath - Path of the locally downloaded template diff --git a/packages/generators/app/package.json b/packages/generators/app/package.json index efaf9f0337..c2df2c839e 100644 --- a/packages/generators/app/package.json +++ b/packages/generators/app/package.json @@ -22,6 +22,8 @@ "node-fetch": "^2.6.1", "node-machine-id": "^1.1.10", "ora": "^5.4.0", + "semver": "7.3.5", + "tar": "6.1.11", "uuid": "^3.3.2" }, "scripts": { From 0b073e9c95e4b54c332e931b405b23c764e1c0ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 26 Oct 2021 11:46:14 +0200 Subject: [PATCH 09/35] Fix template semver check --- .../app/lib/utils/merge-template.js | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index fe9defdd4a..2aae01a67d 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -48,7 +48,7 @@ module.exports = async function mergeTemplate(scope, rootPath) { } // Make sure the template is compatible with this version of strapi - checkTemplateCompat(rootPath, scope.strapiVersion); + checkTemplateCompat(templatePath, scope.strapiVersion); // Make sure the downloaded template matches the required format const { templateConfig } = await checkTemplateRootStructure(templatePath, scope); @@ -64,18 +64,40 @@ module.exports = async function mergeTemplate(scope, rootPath) { } }; -function checkTemplateCompat({ rootPath, strapiVersion, templatePackageInfo }) { - const packageJSON = require(path.resolve(rootPath, 'package.json')); +/** + * Make sure the template is compatible with a specific Strapi version + * @param {string} templatePath - Where the template is installed + * @param {string} strapiVersion - Strapi version of the app being created + */ +function checkTemplateCompat(templatePath, strapiVersion) { + const packageJSON = require(path.resolve(templatePath, 'package.json')); const compatibleStrapiRange = packageJSON.strapi; - // Throw error if not compatible - const isCompatible = semver.satisfies(strapiVersion, compatibleStrapiRange); + + // Make sure the Strapi compatibility range is set + if (compatibleStrapiRange == null) { + throw new Error('This template does not specify a range of compatible Strapi versions'); + } + + // Check that the range is set using proper semver + const validCompatibleStrapiRange = semver.validRange(compatibleStrapiRange); + if (!validCompatibleStrapiRange) { + throw new Error( + 'Please use semver to specify the range of Strapi versions compatible with this plugin' + ); + } + + // Check if the template is compatible with this Strapi version + const coercedStrapiVersion = semver.coerce(strapiVersion, { includePrerelease: true }); + const isCompatible = semver.satisfies(coercedStrapiVersion, validCompatibleStrapiRange, { + includePrerelease: true, + }); if (!isCompatible) { - const { name, version } = templatePackageInfo; throw new Error(` - The template ${chalk.green( - `${name}@${version}` - )} is not compatible with Strapi version ${strapiVersion}. - It will only work with Strapi versions in the range ${chalk.green(compatibleStrapiRange)}. + This template is not compatible with Strapi version ${strapiVersion}. + It will only work with Strapi versions in the range ${chalk.green( + JSON.stringify(compatibleStrapiRange) + )}. + Try using a different Strapi version, or a different version of this template instead `); } } From 9f3d7a4860ee59e39ddafd68c532ca18201ecbfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 26 Oct 2021 20:01:26 +0200 Subject: [PATCH 10/35] Apply feedback --- .../create-strapi-app/utils/prompt-user.js | 1 - .../strapi/lib/commands/generate-template.js | 4 +- .../app/lib/utils/fetch-npm-template.js | 16 ++-- .../app/lib/utils/merge-template.js | 81 ++++++------------- packages/generators/app/package.json | 2 +- 5 files changed, 36 insertions(+), 68 deletions(-) diff --git a/packages/cli/create-strapi-app/utils/prompt-user.js b/packages/cli/create-strapi-app/utils/prompt-user.js index 4c41cf1a6a..797b279861 100644 --- a/packages/cli/create-strapi-app/utils/prompt-user.js +++ b/packages/cli/create-strapi-app/utils/prompt-user.js @@ -61,7 +61,6 @@ async function getTemplateQuestion() { * @param {string|null} template - The template the project should use * @returns Array of prompt question objects */ -// TODO: re-enabled once the template have been migrated to V4 async function getPromptQuestions(projectName, template) { return [ { diff --git a/packages/core/strapi/lib/commands/generate-template.js b/packages/core/strapi/lib/commands/generate-template.js index 20a35c1841..4a898b2933 100644 --- a/packages/core/strapi/lib/commands/generate-template.js +++ b/packages/core/strapi/lib/commands/generate-template.js @@ -55,9 +55,7 @@ async function writeTemplateJson(rootPath) { */ async function templateConfigExists(rootPath) { const jsonConfig = await fse.pathExists(join(rootPath, 'template.json')); - const functionConfig = await fse.pathExists(join(rootPath, 'template.js')); - - return jsonConfig || functionConfig; + return Boolean(jsonConfig); } module.exports = async function generateTemplate(directory) { diff --git a/packages/generators/app/lib/utils/fetch-npm-template.js b/packages/generators/app/lib/utils/fetch-npm-template.js index 18a5f23d26..d670cb13c1 100644 --- a/packages/generators/app/lib/utils/fetch-npm-template.js +++ b/packages/generators/app/lib/utils/fetch-npm-template.js @@ -1,8 +1,7 @@ 'use strict'; -const execSync = require('child_process').execSync; const path = require('path'); -// const fetch = require('node-fetch'); +const execa = require('execa'); const chalk = require('chalk'); /** @@ -11,9 +10,9 @@ const chalk = require('chalk'); * @returns {Object} */ function getPackageInfo(packageName) { - const npmInfo = execSync(`npm view ${packageName} name version --silent`).toString(); + const { stdout } = execa.shellSync(`npm view ${packageName} name version --silent`); // Use regex to parse name and version from CLI result - const [name, version] = npmInfo.match(/(?<=')(.*?)(?=')/gm); + const [name, version] = stdout.match(/(?<=')(.*?)(?=')/gm); return { name, version }; } @@ -47,10 +46,15 @@ async function getTemplatePackageInfo(template) { */ function downloadNpmTemplate({ name, version }, parentDir) { // Download from npm - execSync(`cd ${parentDir} && npm install ${name}@${version} --no-save --silent`).toString(); + execa.shellSync(`npm install ${name}@${version} --no-save --silent`, { + cwd: parentDir, + }); // Return the path of the actual template - const exactTemplatePath = path.resolve(parentDir, 'node_modules', name); + const exactTemplatePath = require.resolve(path.join('node_modules', name), { + paths: [parentDir], + }); + // const exactTemplatePath = path.resolve(parentDir, 'node_modules', name); return exactTemplatePath; } diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index 2aae01a67d..1c6182df7e 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -31,12 +31,12 @@ module.exports = async function mergeTemplate(scope, rootPath) { let templatePath; let templateParentPath; let templatePackageInfo = {}; - const isLocalTemplate = Boolean(scope.template.match(/^file:/)); + const isLocalTemplate = ['./', '../'].some(filePrefix => scope.template.startsWith(filePrefix)); if (isLocalTemplate) { // Template is a local directory console.log('Installing local template.'); - templatePath = path.resolve(rootPath, '..', scope.template.match(/^file:(.*)?$/)[1]); + templatePath = path.resolve(rootPath, '..', scope.template); } else { // Template should be an npm package. Fetch template info templatePackageInfo = await getTemplatePackageInfo(scope.template); @@ -51,7 +51,7 @@ module.exports = async function mergeTemplate(scope, rootPath) { checkTemplateCompat(templatePath, scope.strapiVersion); // Make sure the downloaded template matches the required format - const { templateConfig } = await checkTemplateRootStructure(templatePath, scope); + const templateConfig = await checkTemplateRootStructure(templatePath, scope); await checkTemplateContentsStructure(path.resolve(templatePath, 'template')); // Merge contents of the template in the project @@ -70,34 +70,31 @@ module.exports = async function mergeTemplate(scope, rootPath) { * @param {string} strapiVersion - Strapi version of the app being created */ function checkTemplateCompat(templatePath, strapiVersion) { - const packageJSON = require(path.resolve(templatePath, 'package.json')); - const compatibleStrapiRange = packageJSON.strapi; + const templateJSON = require(path.resolve(templatePath, 'template.json')); + const compatibleStrapiConstraint = templateJSON.strapi.supportedVersion; - // Make sure the Strapi compatibility range is set - if (compatibleStrapiRange == null) { - throw new Error('This template does not specify a range of compatible Strapi versions'); + // Make sure the Strapi compatibility constraint is set + if (compatibleStrapiConstraint == null) { + throw new Error("This template does not specify the Strapi versions it's compatible with"); } // Check that the range is set using proper semver - const validCompatibleStrapiRange = semver.validRange(compatibleStrapiRange); + const validCompatibleStrapiRange = semver.validRange(compatibleStrapiConstraint); if (!validCompatibleStrapiRange) { throw new Error( - 'Please use semver to specify the range of Strapi versions compatible with this plugin' + 'Please use a valid semver constraint to specify which Strapi versions this template is compatible with' ); } // Check if the template is compatible with this Strapi version - const coercedStrapiVersion = semver.coerce(strapiVersion, { includePrerelease: true }); - const isCompatible = semver.satisfies(coercedStrapiVersion, validCompatibleStrapiRange, { - includePrerelease: true, - }); + const isCompatible = semver.satisfies(strapiVersion, validCompatibleStrapiRange); if (!isCompatible) { throw new Error(` This template is not compatible with Strapi version ${strapiVersion}. - It will only work with Strapi versions in the range ${chalk.green( - JSON.stringify(compatibleStrapiRange) + It will only work with Strapi versions in the constraint ${chalk.green( + JSON.stringify(compatibleStrapiConstraint) )}. - Try using a different Strapi version, or a different version of this template instead + Try using a different Strapi version, or a different version of this template instead. `); } } @@ -105,49 +102,19 @@ function checkTemplateCompat(templatePath, strapiVersion) { /** * Make sure the template has the required top-level structure * @param {string} templatePath - Path of the locally downloaded template - * @param {Object} scope - Information about the Strapi app's config + * @returns {Object} - The template config object */ -async function checkTemplateRootStructure(templatePath, scope) { - // Make sure the root of the repo has a template.json or a template.js file +async function checkTemplateRootStructure(templatePath) { + // Make sure the root of the repo has a template.json file const templateJsonPath = path.join(templatePath, 'template.json'); - const templateFunctionPath = path.join(templatePath, 'template.js'); - - // Store the template config, whether it comes from a JSON or a function - let templateConfig = {}; - - const hasJsonConfig = fse.existsSync(templateJsonPath); - if (hasJsonConfig) { - const jsonStat = await fse.stat(templateJsonPath); - if (!jsonStat.isFile()) { - throw new Error(`A template's ${chalk.green('template.json')} must be a file`); - } - templateConfig = require(templateJsonPath); + if (!fse.existsSync(templateJsonPath)) { + throw new Error(`A template must have a ${chalk.green('template.json')} root file`); + } + if (fse.statSync(templateJsonPath).isFile()) { + throw new Error(`A template's ${chalk.green('template.json')} must be a file`); } - const hasFunctionConfig = fse.existsSync(templateFunctionPath); - if (hasFunctionConfig) { - const functionStat = await fse.stat(templateFunctionPath); - if (!functionStat.isFile()) { - throw new Error(`A template's ${chalk.green('template.js')} must be a file`); - } - // Get the config by passing the scope to the function - templateConfig = require(templateFunctionPath)(scope); - } - - // Make sure there's exactly one template config file - if (!hasJsonConfig && !hasFunctionConfig) { - throw new Error( - `A template must have either a ${chalk.green('template.json')} or a ${chalk.green( - 'template.js' - )} root file` - ); - } else if (hasJsonConfig && hasFunctionConfig) { - throw new Error( - `A template cannot have both ${chalk.green('template.json')} and ${chalk.green( - 'template.js' - )} root files` - ); - } + const templateConfig = require(templateJsonPath); // Make sure the root of the repo has a template folder const templateDirPath = path.join(templatePath, 'template'); @@ -164,7 +131,7 @@ async function checkTemplateRootStructure(templatePath, scope) { throw error; } - return { templateConfig }; + return templateConfig; } /** diff --git a/packages/generators/app/package.json b/packages/generators/app/package.json index c2df2c839e..f9d1a4a647 100644 --- a/packages/generators/app/package.json +++ b/packages/generators/app/package.json @@ -1,6 +1,6 @@ { "name": "@strapi/generate-new", - "version": "4.0.0-beta.4", + "version": "3.6.8", "description": "Generate a new Strapi application.", "homepage": "https://strapi.io", "keywords": [ From 0369700e2670e0a98c24c1c8222591dd9123b673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Wed, 27 Oct 2021 13:22:15 +0200 Subject: [PATCH 11/35] Fix isFile check --- packages/generators/app/lib/utils/merge-template.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index 1c6182df7e..4108aa2d95 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -110,7 +110,7 @@ async function checkTemplateRootStructure(templatePath) { if (!fse.existsSync(templateJsonPath)) { throw new Error(`A template must have a ${chalk.green('template.json')} root file`); } - if (fse.statSync(templateJsonPath).isFile()) { + if (!fse.statSync(templateJsonPath).isFile()) { throw new Error(`A template's ${chalk.green('template.json')} must be a file`); } From 51feb183c0da9f308da9fce6ce1a4048fc2bed3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Wed, 27 Oct 2021 13:23:35 +0200 Subject: [PATCH 12/35] Update packages/generators/app/lib/utils/merge-template.js Co-authored-by: Alexandre BODIN --- packages/generators/app/lib/utils/merge-template.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index 4108aa2d95..b6be7fbbdd 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -213,7 +213,7 @@ async function mergePackageJSON({ rootPath, templateConfig, templatePackageInfo // Use lodash to deeply merge them const mergedConfig = _.merge(packageJSON, templateConfig.package); - // Add starter info to package.json + // Add template info to package.json if (templatePackageInfo.name) { _.set(mergedConfig, 'strapi.template', templatePackageInfo.name); } From 9f39e0220c939b177bca06246cc6e8f2d82d15bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Wed, 27 Oct 2021 16:03:35 +0200 Subject: [PATCH 13/35] Apply new feedback --- .../app/lib/utils/merge-template.js | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index b6be7fbbdd..954086997d 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -3,7 +3,7 @@ const os = require('os'); const path = require('path'); const fse = require('fs-extra'); -const _ = require('lodash'); +const _ = require('lodash/fp'); const chalk = require('chalk'); const semver = require('semver'); const { getTemplatePackageInfo, downloadNpmTemplate } = require('./fetch-npm-template'); @@ -71,10 +71,10 @@ module.exports = async function mergeTemplate(scope, rootPath) { */ function checkTemplateCompat(templatePath, strapiVersion) { const templateJSON = require(path.resolve(templatePath, 'template.json')); - const compatibleStrapiConstraint = templateJSON.strapi.supportedVersion; + const compatibleStrapiConstraint = _.get('strapi.supportedVersion', templateJSON); // Make sure the Strapi compatibility constraint is set - if (compatibleStrapiConstraint == null) { + if (_.isNil(compatibleStrapiConstraint)) { throw new Error("This template does not specify the Strapi versions it's compatible with"); } @@ -107,10 +107,12 @@ function checkTemplateCompat(templatePath, strapiVersion) { async function checkTemplateRootStructure(templatePath) { // Make sure the root of the repo has a template.json file const templateJsonPath = path.join(templatePath, 'template.json'); - if (!fse.existsSync(templateJsonPath)) { + const templateJsonExists = await fse.exists(templateJsonPath); + if (!templateJsonExists) { throw new Error(`A template must have a ${chalk.green('template.json')} root file`); } - if (!fse.statSync(templateJsonPath).isFile()) { + const templateJsonStat = await fse.stat(templateJsonPath); + if (!templateJsonStat.isFile()) { throw new Error(`A template's ${chalk.green('template.json')} must be a file`); } @@ -140,15 +142,15 @@ async function checkTemplateRootStructure(templatePath) { */ 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 checkPathContents = async (pathToCheck, parents) => { + const contents = await fse.readdir(pathToCheck); + for (const item of contents) { const nextParents = [...parents, item]; - const matchingTreeValue = _.get(allowedTemplateContents, nextParents); + const matchingTreeValue = _.get(nextParents, allowedTemplateContents); // Treat files and directories separately const itemPath = path.resolve(pathToCheck, item); - const isDirectory = fse.statSync(itemPath).isDirectory(); + const isDirectory = (await fse.stat(itemPath)).isDirectory(); if (matchingTreeValue === undefined) { // Unknown paths are forbidden @@ -175,16 +177,16 @@ async function checkTemplateContentsStructure(templateContentsPath) { return; } // Check if the contents of the directory are allowed - checkPathContents(itemPath, nextParents); + await checkPathContents(itemPath, nextParents); } else { throw Error( `Illegal template structure, unknow file ${chalk.green(nextParents.join('/'))}` ); } - }); + } }; - checkPathContents(templateContentsPath, []); + await checkPathContents(templateContentsPath, []); } /** @@ -211,11 +213,11 @@ async function mergePackageJSON({ rootPath, templateConfig, templatePackageInfo } // Use lodash to deeply merge them - const mergedConfig = _.merge(packageJSON, templateConfig.package); + const mergedConfig = _.merge(templateConfig.package, packageJSON); // Add template info to package.json if (templatePackageInfo.name) { - _.set(mergedConfig, 'strapi.template', templatePackageInfo.name); + _.set('strapi.template', templatePackageInfo.name, mergedConfig); } // Save the merged config as the new package.json From 93ad75c49314b1455bd62f8ed25a49908df2838c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Wed, 27 Oct 2021 16:37:30 +0200 Subject: [PATCH 14/35] async execa shells --- .../generators/app/lib/utils/fetch-npm-template.js | 10 +++++----- packages/generators/app/lib/utils/merge-template.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/generators/app/lib/utils/fetch-npm-template.js b/packages/generators/app/lib/utils/fetch-npm-template.js index d670cb13c1..e2b987f2ba 100644 --- a/packages/generators/app/lib/utils/fetch-npm-template.js +++ b/packages/generators/app/lib/utils/fetch-npm-template.js @@ -9,8 +9,8 @@ const chalk = require('chalk'); * @param {string} packageName - Name to look up on npm, may include a specific version * @returns {Object} */ -function getPackageInfo(packageName) { - const { stdout } = execa.shellSync(`npm view ${packageName} name version --silent`); +async function getPackageInfo(packageName) { + const { stdout } = await execa.shell(`npm view ${packageName} name version --silent`); // Use regex to parse name and version from CLI result const [name, version] = stdout.match(/(?<=')(.*?)(?=')/gm); return { name, version }; @@ -24,7 +24,7 @@ async function getTemplatePackageInfo(template) { // Check if template is a shorthand try { const longhand = `@strapi/template-${template}`; - const packageInfo = getPackageInfo(longhand); + const packageInfo = await getPackageInfo(longhand); // Hasn't crashed so it is indeed a shorthand return packageInfo; } catch (error) { @@ -44,9 +44,9 @@ async function getTemplatePackageInfo(template) { * @param {string} packageInfo.version * @param {string} parentDir - Path inside of which we install the template. */ -function downloadNpmTemplate({ name, version }, parentDir) { +async function downloadNpmTemplate({ name, version }, parentDir) { // Download from npm - execa.shellSync(`npm install ${name}@${version} --no-save --silent`, { + await execa.shell(`npm install ${name}@${version} --no-save --silent`, { cwd: parentDir, }); diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index 954086997d..5e58ccf4ff 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -44,7 +44,7 @@ module.exports = async function mergeTemplate(scope, rootPath) { // Download template repository to a temporary directory templateParentPath = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-')); - templatePath = downloadNpmTemplate(templatePackageInfo, templateParentPath); + templatePath = await downloadNpmTemplate(templatePackageInfo, templateParentPath); } // Make sure the template is compatible with this version of strapi From 35731f36c0abb058bbf81932481c9bd0e1af1f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Wed, 27 Oct 2021 18:18:28 +0200 Subject: [PATCH 15/35] fix yarn lock --- yarn.lock | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index e6b414f0a9..bfe16302ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4602,20 +4602,6 @@ resolve-from "^5.0.0" store2 "^2.12.0" -"@strapi/generate-new@3.6.8": - version "4.0.0-beta.4" - dependencies: - "@sentry/node" "6.3.0" - chalk "^4.1.1" - execa "^1.0.0" - fs-extra "^9.1.0" - inquirer "^6.3.1" - lodash "4.17.21" - node-fetch "^2.6.1" - node-machine-id "^1.1.10" - ora "^5.4.0" - uuid "^3.3.2" - "@strapi/icons@0.0.1-alpha.50": version "0.0.1-alpha.50" resolved "https://registry.yarnpkg.com/@strapi/icons/-/icons-0.0.1-alpha.50.tgz#f62e0d329ab28942eced4aefed9b7cfd73f1373e" From 8109ef9bb2bee2d648185f07e8b36c1346d7cacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Thu, 28 Oct 2021 18:11:57 +0200 Subject: [PATCH 16/35] remove dependency check --- .../app/lib/utils/merge-template.js | 39 ------------------- packages/generators/app/package.json | 1 - 2 files changed, 40 deletions(-) diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index 5e58ccf4ff..255f7de436 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -5,7 +5,6 @@ const path = require('path'); const fse = require('fs-extra'); const _ = require('lodash/fp'); const chalk = require('chalk'); -const semver = require('semver'); const { getTemplatePackageInfo, downloadNpmTemplate } = require('./fetch-npm-template'); // Specify all the files and directories a template can have @@ -47,9 +46,6 @@ module.exports = async function mergeTemplate(scope, rootPath) { templatePath = await downloadNpmTemplate(templatePackageInfo, templateParentPath); } - // Make sure the template is compatible with this version of strapi - checkTemplateCompat(templatePath, scope.strapiVersion); - // Make sure the downloaded template matches the required format const templateConfig = await checkTemplateRootStructure(templatePath, scope); await checkTemplateContentsStructure(path.resolve(templatePath, 'template')); @@ -64,41 +60,6 @@ module.exports = async function mergeTemplate(scope, rootPath) { } }; -/** - * Make sure the template is compatible with a specific Strapi version - * @param {string} templatePath - Where the template is installed - * @param {string} strapiVersion - Strapi version of the app being created - */ -function checkTemplateCompat(templatePath, strapiVersion) { - const templateJSON = require(path.resolve(templatePath, 'template.json')); - const compatibleStrapiConstraint = _.get('strapi.supportedVersion', templateJSON); - - // Make sure the Strapi compatibility constraint is set - if (_.isNil(compatibleStrapiConstraint)) { - throw new Error("This template does not specify the Strapi versions it's compatible with"); - } - - // Check that the range is set using proper semver - const validCompatibleStrapiRange = semver.validRange(compatibleStrapiConstraint); - if (!validCompatibleStrapiRange) { - throw new Error( - 'Please use a valid semver constraint to specify which Strapi versions this template is compatible with' - ); - } - - // Check if the template is compatible with this Strapi version - const isCompatible = semver.satisfies(strapiVersion, validCompatibleStrapiRange); - if (!isCompatible) { - throw new Error(` - This template is not compatible with Strapi version ${strapiVersion}. - It will only work with Strapi versions in the constraint ${chalk.green( - JSON.stringify(compatibleStrapiConstraint) - )}. - Try using a different Strapi version, or a different version of this template instead. - `); - } -} - /** * Make sure the template has the required top-level structure * @param {string} templatePath - Path of the locally downloaded template diff --git a/packages/generators/app/package.json b/packages/generators/app/package.json index f9d1a4a647..4f6e3172bd 100644 --- a/packages/generators/app/package.json +++ b/packages/generators/app/package.json @@ -22,7 +22,6 @@ "node-fetch": "^2.6.1", "node-machine-id": "^1.1.10", "ora": "^5.4.0", - "semver": "7.3.5", "tar": "6.1.11", "uuid": "^3.3.2" }, From 0609389b5a98344510a6ecacfcc6c824061584f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Thu, 28 Oct 2021 18:50:41 +0200 Subject: [PATCH 17/35] Disable templates prompt --- .../cli/create-strapi-app/utils/prompt-user.js | 17 +++++++++-------- .../strapi/lib/commands/generate-template.js | 4 +++- .../generators/app/lib/utils/merge-template.js | 4 +++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/cli/create-strapi-app/utils/prompt-user.js b/packages/cli/create-strapi-app/utils/prompt-user.js index 797b279861..9e8d2f4b62 100644 --- a/packages/cli/create-strapi-app/utils/prompt-user.js +++ b/packages/cli/create-strapi-app/utils/prompt-user.js @@ -61,7 +61,7 @@ async function getTemplateQuestion() { * @param {string|null} template - The template the project should use * @returns Array of prompt question objects */ -async function getPromptQuestions(projectName, template) { +async function getPromptQuestions(projectName /*, template*/) { return [ { type: 'input', @@ -85,13 +85,14 @@ async function getPromptQuestions(projectName, template) { }, ], }, - { - type: 'confirm', - name: 'useTemplate', - when: !template, - message: - 'Would you like to use a template? (Templates are Strapi configurations designed for a specific use case)', - }, + // TODO: re-enable once we know where to list the official compatible templates + // { + // type: 'confirm', + // name: 'useTemplate', + // when: !template, + // message: + // 'Would you like to use a template? (Templates are Strapi configurations designed for a specific use case)', + // }, ]; } diff --git a/packages/core/strapi/lib/commands/generate-template.js b/packages/core/strapi/lib/commands/generate-template.js index 4a898b2933..fab579b887 100644 --- a/packages/core/strapi/lib/commands/generate-template.js +++ b/packages/core/strapi/lib/commands/generate-template.js @@ -4,6 +4,7 @@ const { resolve, join, basename } = require('path'); const fse = require('fs-extra'); const chalk = require('chalk'); const inquirer = require('inquirer'); +const _ = require('lodash/fp'); // All directories that a template could need const TEMPLATE_CONTENT = ['api', 'components', 'config/functions/bootstrap.js', 'data']; @@ -55,7 +56,8 @@ async function writeTemplateJson(rootPath) { */ async function templateConfigExists(rootPath) { const jsonConfig = await fse.pathExists(join(rootPath, 'template.json')); - return Boolean(jsonConfig); + const configExists = !_.isNil(jsonConfig); + return configExists; } module.exports = async function generateTemplate(directory) { diff --git a/packages/generators/app/lib/utils/merge-template.js b/packages/generators/app/lib/utils/merge-template.js index 255f7de436..6cf780bbbb 100644 --- a/packages/generators/app/lib/utils/merge-template.js +++ b/packages/generators/app/lib/utils/merge-template.js @@ -30,7 +30,9 @@ module.exports = async function mergeTemplate(scope, rootPath) { let templatePath; let templateParentPath; let templatePackageInfo = {}; - const isLocalTemplate = ['./', '../'].some(filePrefix => scope.template.startsWith(filePrefix)); + const isLocalTemplate = ['./', '../', '/'].some(filePrefix => + scope.template.startsWith(filePrefix) + ); if (isLocalTemplate) { // Template is a local directory From 40657b3a18f0888366559105374f7def9c6fb6d0 Mon Sep 17 00:00:00 2001 From: Dieter Stinglhamber Date: Tue, 2 Nov 2021 13:56:50 +0100 Subject: [PATCH 18/35] add static list of reserved names --- .../core/content-type-builder/server/services/builder.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/core/content-type-builder/server/services/builder.js b/packages/core/content-type-builder/server/services/builder.js index 3afadd7eb2..0754afcde3 100644 --- a/packages/core/content-type-builder/server/services/builder.js +++ b/packages/core/content-type-builder/server/services/builder.js @@ -1,11 +1,10 @@ 'use strict'; module.exports = () => ({ - // TODO: Implement getReservedNames() { return { - models: [], - attributes: [], + models: ['boolean', 'date', 'date-time', 'time', 'upload'], + attributes: ['id', 'created_at', 'updated_at', 'created_by', 'updated_by', 'published_at'], }; // strapi.db.getReservedNames(); }, From b1dbfc6bdcd96a40e7a415799546915349e56279 Mon Sep 17 00:00:00 2001 From: ronronscelestes Date: Tue, 2 Nov 2021 15:53:06 +0100 Subject: [PATCH 19/35] fixed webhook table + updated tests --- .../pages/SettingsPage/pages/Webhooks/ListView/index.js | 6 +++--- .../admin/src/pages/Roles/CreatePage/tests/index.test.js | 8 ++++---- .../admin/src/pages/Roles/EditPage/tests/index.test.js | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Webhooks/ListView/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Webhooks/ListView/index.js index 6289bc81ac..0df51c947c 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Webhooks/ListView/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Webhooks/ListView/index.js @@ -331,7 +331,7 @@ const ListView = () => { onValueChange={handleSelectAllCheckbox} /> - + {formatMessage({ id: 'Settings.webhooks.form.name', @@ -339,7 +339,7 @@ const ListView = () => { })} - + {formatMessage({ id: 'Settings.webhooks.form.url', @@ -347,7 +347,7 @@ const ListView = () => { })} - + {formatMessage({ id: 'Settings.webhooks.list.th.status', diff --git a/packages/plugins/users-permissions/admin/src/pages/Roles/CreatePage/tests/index.test.js b/packages/plugins/users-permissions/admin/src/pages/Roles/CreatePage/tests/index.test.js index 0ce4400eea..1d60255520 100644 --- a/packages/plugins/users-permissions/admin/src/pages/Roles/CreatePage/tests/index.test.js +++ b/packages/plugins/users-permissions/admin/src/pages/Roles/CreatePage/tests/index.test.js @@ -770,10 +770,6 @@ describe('Admin | containers | RoleCreatePage', () => { background: #d9d8ff; } - .c40:hover:not([aria-disabled='true']) svg path { - fill: #4945ff; - } - .c47 { background: transparent; border: none; @@ -861,6 +857,10 @@ describe('Admin | containers | RoleCreatePage', () => { height: 5.5rem; } + .c44:hover svg path { + fill: #4945ff; + } + @media (max-width:68.75rem) { .c20 { grid-column: span; diff --git a/packages/plugins/users-permissions/admin/src/pages/Roles/EditPage/tests/index.test.js b/packages/plugins/users-permissions/admin/src/pages/Roles/EditPage/tests/index.test.js index 4255641b7d..6ba8b542b8 100644 --- a/packages/plugins/users-permissions/admin/src/pages/Roles/EditPage/tests/index.test.js +++ b/packages/plugins/users-permissions/admin/src/pages/Roles/EditPage/tests/index.test.js @@ -853,10 +853,6 @@ describe('Admin | containers | RoleEditPage', () => { background: #d9d8ff; } - .c47:hover:not([aria-disabled='true']) svg path { - fill: #4945ff; - } - .c54 { background: transparent; border: none; @@ -944,6 +940,10 @@ describe('Admin | containers | RoleEditPage', () => { height: 5.5rem; } + .c51:hover svg path { + fill: #4945ff; + } + @media (max-width:68.75rem) { .c27 { grid-column: span; From 8c4af58ca5aad2bbc514fa14f2fdce289e0da6d8 Mon Sep 17 00:00:00 2001 From: Dieter Stinglhamber Date: Tue, 2 Nov 2021 15:56:24 +0100 Subject: [PATCH 20/35] add camelCase variations --- .../server/services/builder.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/core/content-type-builder/server/services/builder.js b/packages/core/content-type-builder/server/services/builder.js index 0754afcde3..c3d41f8db2 100644 --- a/packages/core/content-type-builder/server/services/builder.js +++ b/packages/core/content-type-builder/server/services/builder.js @@ -3,8 +3,20 @@ module.exports = () => ({ getReservedNames() { return { - models: ['boolean', 'date', 'date-time', 'time', 'upload'], - attributes: ['id', 'created_at', 'updated_at', 'created_by', 'updated_by', 'published_at'], + models: ['boolean', 'date', 'date-time', 'dateTime', 'time', 'upload'], + attributes: [ + 'id', + 'created_at', + 'createdAt', + 'updated_at', + 'updatedAt', + 'created_by', + 'createdBy', + 'updated_by', + 'updatedBy', + 'published_at', + 'publishedAt', + ], }; // strapi.db.getReservedNames(); }, From 0e838226c822aab0209050c285cf2144c4405218 Mon Sep 17 00:00:00 2001 From: ronronscelestes Date: Tue, 2 Nov 2021 16:56:43 +0100 Subject: [PATCH 21/35] made box clickable to add an entry --- .../components/ComponentInitializer/index.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/ComponentInitializer/index.js b/packages/core/admin/admin/src/content-manager/components/ComponentInitializer/index.js index b474c2710c..d6094f2138 100644 --- a/packages/core/admin/admin/src/content-manager/components/ComponentInitializer/index.js +++ b/packages/core/admin/admin/src/content-manager/components/ComponentInitializer/index.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useRef } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import { useIntl } from 'react-intl'; @@ -11,6 +11,10 @@ import { Text } from '@strapi/design-system/Text'; import { pxToRem } from '@strapi/helper-plugin'; import { getTrad } from '../../utils'; +const CursorBox = styled(Box)` + cursor: pointer; +`; + const IconButton = styled(BaseButton)` border: none; padding: 0; @@ -30,18 +34,26 @@ const IconButton = styled(BaseButton)` const ComponentInitializer = ({ isReadOnly, onClick }) => { const { formatMessage } = useIntl(); + const addEntryButtonRef = useRef(); + + const handleBoxClick = () => { + if (addEntryButtonRef.current) { + addEntryButtonRef.current.click(); + } + }; return ( - - + @@ -54,7 +66,7 @@ const ComponentInitializer = ({ isReadOnly, onClick }) => { - + ); }; From d398b37cf44afee31edc9920878b30c59727830a Mon Sep 17 00:00:00 2001 From: ronronscelestes Date: Tue, 2 Nov 2021 17:15:15 +0100 Subject: [PATCH 22/35] fixed tests --- .../admin/src/pages/Roles/CreatePage/tests/index.test.js | 8 ++++---- .../admin/src/pages/Roles/EditPage/tests/index.test.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/plugins/users-permissions/admin/src/pages/Roles/CreatePage/tests/index.test.js b/packages/plugins/users-permissions/admin/src/pages/Roles/CreatePage/tests/index.test.js index 0ce4400eea..1d60255520 100644 --- a/packages/plugins/users-permissions/admin/src/pages/Roles/CreatePage/tests/index.test.js +++ b/packages/plugins/users-permissions/admin/src/pages/Roles/CreatePage/tests/index.test.js @@ -770,10 +770,6 @@ describe('Admin | containers | RoleCreatePage', () => { background: #d9d8ff; } - .c40:hover:not([aria-disabled='true']) svg path { - fill: #4945ff; - } - .c47 { background: transparent; border: none; @@ -861,6 +857,10 @@ describe('Admin | containers | RoleCreatePage', () => { height: 5.5rem; } + .c44:hover svg path { + fill: #4945ff; + } + @media (max-width:68.75rem) { .c20 { grid-column: span; diff --git a/packages/plugins/users-permissions/admin/src/pages/Roles/EditPage/tests/index.test.js b/packages/plugins/users-permissions/admin/src/pages/Roles/EditPage/tests/index.test.js index 4255641b7d..6ba8b542b8 100644 --- a/packages/plugins/users-permissions/admin/src/pages/Roles/EditPage/tests/index.test.js +++ b/packages/plugins/users-permissions/admin/src/pages/Roles/EditPage/tests/index.test.js @@ -853,10 +853,6 @@ describe('Admin | containers | RoleEditPage', () => { background: #d9d8ff; } - .c47:hover:not([aria-disabled='true']) svg path { - fill: #4945ff; - } - .c54 { background: transparent; border: none; @@ -944,6 +940,10 @@ describe('Admin | containers | RoleEditPage', () => { height: 5.5rem; } + .c51:hover svg path { + fill: #4945ff; + } + @media (max-width:68.75rem) { .c27 { grid-column: span; From 7b9ca4d9075190c714f0d81d604c4f57562f4601 Mon Sep 17 00:00:00 2001 From: ronronscelestes Date: Tue, 2 Nov 2021 17:52:22 +0100 Subject: [PATCH 23/35] feedback fixes --- .../components/ComponentInitializer/index.js | 37 ++++++------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/ComponentInitializer/index.js b/packages/core/admin/admin/src/content-manager/components/ComponentInitializer/index.js index d6094f2138..ba4f5758f3 100644 --- a/packages/core/admin/admin/src/content-manager/components/ComponentInitializer/index.js +++ b/packages/core/admin/admin/src/content-manager/components/ComponentInitializer/index.js @@ -1,30 +1,21 @@ -import React, { useRef } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import { useIntl } from 'react-intl'; import PlusCircle from '@strapi/icons/PlusCircle'; import { Box } from '@strapi/design-system/Box'; -import { BaseButton } from '@strapi/design-system/BaseButton'; import { Stack } from '@strapi/design-system/Stack'; import { Flex } from '@strapi/design-system/Flex'; import { Text } from '@strapi/design-system/Text'; import { pxToRem } from '@strapi/helper-plugin'; import { getTrad } from '../../utils'; -const CursorBox = styled(Box)` - cursor: pointer; -`; - -const IconButton = styled(BaseButton)` - border: none; - padding: 0; - background: transparent; - cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')}; +const IconWrapper = styled.span` > svg { width: ${pxToRem(24)}; height: ${pxToRem(24)}; > circle { - fill: ${({ theme }) => theme.colors.primary200}!important; + fill: ${({ theme }) => theme.colors.primary200}; } > path { fill: ${({ theme }) => theme.colors.primary600}; @@ -34,28 +25,24 @@ const IconButton = styled(BaseButton)` const ComponentInitializer = ({ isReadOnly, onClick }) => { const { formatMessage } = useIntl(); - const addEntryButtonRef = useRef(); - - const handleBoxClick = () => { - if (addEntryButtonRef.current) { - addEntryButtonRef.current.click(); - } - }; return ( - - + - + @@ -66,7 +53,7 @@ const ComponentInitializer = ({ isReadOnly, onClick }) => { - + ); }; From a10953ffb95e8a644143d7769520107dcad73fbd Mon Sep 17 00:00:00 2001 From: ronronscelestes <71838159+ronronscelestes@users.noreply.github.com> Date: Tue, 2 Nov 2021 18:00:31 +0100 Subject: [PATCH 24/35] QA/ Wysiwyg link button size (#11441) * attempt to fix link svg in wysiwyg * fixed more label button * fixed tests --- .../components/Wysiwyg/WysiwygNav.js | 14 ++++++++++---- .../components/Wysiwyg/WysiwygStyles.js | 7 +++++++ .../src/pages/Roles/CreatePage/tests/index.test.js | 8 ++++---- .../src/pages/Roles/EditPage/tests/index.test.js | 8 ++++---- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/packages/core/admin/admin/src/content-manager/components/Wysiwyg/WysiwygNav.js b/packages/core/admin/admin/src/content-manager/components/Wysiwyg/WysiwygNav.js index 44d8e6592e..329bbbce21 100644 --- a/packages/core/admin/admin/src/content-manager/components/Wysiwyg/WysiwygNav.js +++ b/packages/core/admin/admin/src/content-manager/components/Wysiwyg/WysiwygNav.js @@ -18,7 +18,13 @@ import Image from '@strapi/icons/Picture'; import Link from '@strapi/icons/Link'; import Quote from '@strapi/icons/Quote'; import More from '@strapi/icons/More'; -import { MainButtons, CustomIconButton, MoreButton, IconButtonGroupMargin } from './WysiwygStyles'; +import { + MainButtons, + CustomIconButton, + MoreButton, + IconButtonGroupMargin, + CustomLinkIconButton, +} from './WysiwygStyles'; const WysiwygNav = ({ editorRef, @@ -75,7 +81,7 @@ const WysiwygNav = ({ /> - } /> + } />