From 731b64cea5b64298b680f0437b0e5d3ac3c9ef87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Thu, 18 Nov 2021 17:39:40 +0100 Subject: [PATCH 01/12] Update create-strapi-starter to v4 --- .../create-strapi-starter.js | 16 +-- .../cli/create-strapi-starter/package.json | 9 +- .../utils/build-starter.js | 61 ++++++------ .../utils/fetch-github.js | 97 ------------------- .../utils/fetch-npm-starter.js | 61 ++++++++++++ .../app/lib/utils/fetch-npm-template.js | 2 +- yarn.lock | 22 ----- 7 files changed, 107 insertions(+), 161 deletions(-) delete mode 100644 packages/cli/create-strapi-starter/utils/fetch-github.js create mode 100644 packages/cli/create-strapi-starter/utils/fetch-npm-starter.js diff --git a/packages/cli/create-strapi-starter/create-strapi-starter.js b/packages/cli/create-strapi-starter/create-strapi-starter.js index c14270d56d..b4dbb2b12f 100644 --- a/packages/cli/create-strapi-starter/create-strapi-starter.js +++ b/packages/cli/create-strapi-starter/create-strapi-starter.js @@ -12,7 +12,7 @@ const program = new commander.Command(packageJson.name); program .version(packageJson.version) - .arguments('[directory], [starterurl]') + .arguments('[directory], [starter]') .option('--use-npm', 'Force usage of npm instead of yarn to create the project') .option('--debug', 'Display database connection error') .option('--quickstart', 'Quickstart app creation') @@ -28,16 +28,16 @@ program .description( 'Create a fullstack monorepo application using the strapi backend template specified in the provided starter' ) - .action((directory, starterUrl, programArgs) => { - const projectArgs = { projectName: directory, starterUrl }; + .action((directory, starter, programArgs) => { + const projectArgs = { projectName: directory, starter }; initProject(projectArgs, programArgs); }); function generateApp(projectArgs, programArgs) { - if (!projectArgs.projectName || !projectArgs.starterUrl) { + if (!projectArgs.projectName || !projectArgs.starter) { console.error( - 'Please specify the and of your project when using --quickstart' + 'Please specify the and of your project when using --quickstart' ); // eslint-disable-next-line no-process-exit process.exit(1); @@ -47,16 +47,16 @@ function generateApp(projectArgs, programArgs) { } async function initProject(projectArgs, program) { - const { projectName, starterUrl } = projectArgs; + const { projectName, starter } = projectArgs; if (program.quickstart) { return generateApp(projectArgs, program); } - const prompt = await promptUser(projectName, starterUrl); + const prompt = await promptUser(projectName, starter); const promptProjectArgs = { projectName: prompt.directory || projectName, - starterUrl: prompt.starter || starterUrl, + starter: prompt.starter || starter, }; const programArgs = { diff --git a/packages/cli/create-strapi-starter/package.json b/packages/cli/create-strapi-starter/package.json index 8adcfd5568..817eada9d1 100644 --- a/packages/cli/create-strapi-starter/package.json +++ b/packages/cli/create-strapi-starter/package.json @@ -16,18 +16,15 @@ "create-strapi-starter": "./index.js" }, "dependencies": { + "@strapi/generate-new": "3.6.8", "chalk": "4.1.1", "ci-info": "3.1.1", "commander": "7.1.0", - "execa": "5.0.0", + "execa": "^1.0.0", "fs-extra": "9.1.0", - "git-url-parse": "11.4.4", "inquirer": "8.2.0", "js-yaml": "4.1.0", - "node-fetch": "^2.6.1", - "ora": "5.4.0", - "@strapi/generate-new": "3.6.8", - "tar": "6.1.11" + "ora": "5.4.0" }, "scripts": { "test": "echo \"no tests yet\"" diff --git a/packages/cli/create-strapi-starter/utils/build-starter.js b/packages/cli/create-strapi-starter/utils/build-starter.js index 6130208ed0..f8cfc45ff7 100644 --- a/packages/cli/create-strapi-starter/utils/build-starter.js +++ b/packages/cli/create-strapi-starter/utils/build-starter.js @@ -12,19 +12,19 @@ const generateNewApp = require('@strapi/generate-new'); const hasYarn = require('./has-yarn'); const { runInstall, runApp, initGit } = require('./child-process'); -const { getRepoInfo, downloadGitHubRepo } = require('./fetch-github'); +const { getStarterPackageInfo, downloadNpmStarter } = require('./fetch-npm-starter'); const logger = require('./logger'); const stopProcess = require('./stop-process'); /** * @param {string} - filePath Path to starter.json file */ -function readStarterJson(filePath, starterUrl) { +function readStarterJson(filePath, starter) { try { const data = fse.readFileSync(filePath); return JSON.parse(data); } catch (err) { - stopProcess(`Could not find ${chalk.yellow('starter.json')} in ${chalk.yellow(starterUrl)}`); + stopProcess(`Could not find ${chalk.yellow('starter.json')} in ${chalk.yellow(starter)}`); } } @@ -88,28 +88,36 @@ async function installWithLogs(path) { /** * @param {Object} projectArgs - The arguments for create a project * @param {string|null} projectArgs.projectName - The name/path of project - * @param {string|null} projectArgs.starterUrl - The GitHub repo of the starter + * @param {string|null} projectArgs.starter - The npm package of the starter * @param {Object} program - Commands for generating new application */ -module.exports = async function buildStarter(programArgs, program) { - let { projectName, starterUrl } = programArgs; - - // Fetch repo info - const repoInfo = await getRepoInfo(starterUrl); - const { fullName } = repoInfo; - - // Create temporary directory for starter - const tmpDir = await fse.mkdtemp(join(os.tmpdir(), 'strapi-')); - - // Download repo inside temporary directory - await downloadGitHubRepo(repoInfo, tmpDir); - - const starterJson = readStarterJson(join(tmpDir, 'starter.json'), starterUrl); +module.exports = async function buildStarter({ projectName, starter }, program) { + let starterPath; + let starterParentPath; + let starterPackageInfo = {}; + const isLocalStarter = ['./', '../', '/'].some(filePrefix => starter.startsWith(filePrefix)); + console.log('STARTER', starter); // Project directory const rootPath = resolve(projectName); const projectBasename = basename(rootPath); + if (isLocalStarter) { + // Starter is a local directory + console.log('Installing local starter.'); + starterPath = resolve(rootPath, '..', starter); + } else { + // Starter should be an npm package. Fetch starter info + starterPackageInfo = await getStarterPackageInfo(starter); + console.log(`Installing ${chalk.yellow(starterPackageInfo.name)} starter.`); + + // Download starter repository to a temporary directory + starterParentPath = await fse.mkdtemp(join(os.tmpdir(), 'strapi-')); + starterPath = await downloadNpmStarter(starterPackageInfo, starterParentPath); + } + + const starterJson = readStarterJson(join(starterPath, 'starter.json'), starter); + try { await fse.ensureDir(rootPath); } catch (error) { @@ -119,10 +127,8 @@ module.exports = async function buildStarter(programArgs, program) { // Copy the downloaded frontend folder to the project folder const frontendPath = join(rootPath, 'frontend'); - const starterDir = (await fse.pathExists(join(tmpDir, 'starter'))) ? 'starter' : 'frontend'; - try { - await fse.copy(join(tmpDir, starterDir), frontendPath, { + await fse.copy(join(starterPath, 'starter'), frontendPath, { overwrite: true, recursive: true, }); @@ -130,15 +136,16 @@ module.exports = async function buildStarter(programArgs, program) { stopProcess(`Failed to create ${chalk.yellow(frontendPath)}: ${error.message}`); } - // Delete temporary directory - await fse.remove(tmpDir); + // Delete the starter directory if it was downloaded + if (!isLocalStarter) { + await fse.remove(starterParentPath); + } - const fullUrl = `https://github.com/${fullName}`; // Set command options for Strapi app const generateStrapiAppOptions = { ...program, - starter: fullUrl, - template: starterJson.template, + starter: starterPackageInfo.name, + template: `${starterJson.template.name}@${starterJson.template.version}`, run: false, }; @@ -147,7 +154,7 @@ module.exports = async function buildStarter(programArgs, program) { // Install frontend dependencies console.log(`Creating Strapi starter frontend at ${chalk.green(frontendPath)}.`); - console.log(`Installing ${chalk.yellow(fullName)} starter`); + console.log(`Installing ${chalk.yellow(starterPackageInfo.name)} starter`); await installWithLogs(frontendPath); // Setup monorepo diff --git a/packages/cli/create-strapi-starter/utils/fetch-github.js b/packages/cli/create-strapi-starter/utils/fetch-github.js deleted file mode 100644 index 4fb8e66f83..0000000000 --- a/packages/cli/create-strapi-starter/utils/fetch-github.js +++ /dev/null @@ -1,97 +0,0 @@ -'use strict'; - -const tar = require('tar'); -const fetch = require('node-fetch'); -const parseGitUrl = require('git-url-parse'); -const chalk = require('chalk'); - -const stopProcess = require('./stop-process'); - -function parseShorthand(starter) { - // Determine if it is comes from another owner - if (starter.includes('/')) { - const [owner, partialName] = starter.split('/'); - const name = `strapi-starter-${partialName}`; - return { - name, - fullName: `${owner}/${name}`, - }; - } - - const name = `strapi-starter-${starter}`; - 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 starter information for ${chalk.yellow( - repo - )}. Make sure it is publicly accessible on github.` - ); - } - - const { default_branch } = await response.json(); - return default_branch; -} - -/** - * @param {string} starter GitHub URL or shorthand to a starter project. - */ -async function getRepoInfo(starter) { - const { name, full_name: fullName, ref, filepath, protocols, source } = parseGitUrl(starter); - - if (protocols.length === 0) { - const repoInfo = parseShorthand(starter); - return { - ...repoInfo, - branch: await getDefaultBranch(repoInfo.fullName), - usedShorthand: true, - }; - } - - if (source !== 'github.com') { - stopProcess(`GitHub URL not found for: ${chalk.yellow(starter)}.`); - } - - 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) { - const { fullName, branch, usedShorthand } = repoInfo; - - const codeload = `https://codeload.github.com/${fullName}/tar.gz/${branch}`; - const response = await fetch(codeload); - - if (!response.ok) { - const message = usedShorthand ? `using the shorthand` : `using the url`; - stopProcess(`Could not download the repository ${message}: ${chalk.yellow(fullName)}.`); - } - - await new Promise(resolve => { - response.body.pipe(tar.extract({ strip: 1, cwd: tmpDir })).on('close', resolve); - }); -} - -module.exports = { getRepoInfo, downloadGitHubRepo }; diff --git a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js new file mode 100644 index 0000000000..fc27e86305 --- /dev/null +++ b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js @@ -0,0 +1,61 @@ +'use strict'; + +const path = require('path'); +const execa = require('execa'); +const chalk = require('chalk'); +const stopProcess = require('./stop-process'); + +/** + * 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} + */ +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 }; +} + +/** + * @param {string} starter - The name of the starter as provided by the user. + * @returns {Object} - The full name of the starter package's name on npm + */ +async function getStarterPackageInfo(starter) { + // Check if starter is a shorthand + try { + const longhand = `@strapi/starter-${starter}`; + const packageInfo = await 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(starter); + } catch (error) { + stopProcess(`Could not find package ${chalk.green(starter)} on npm`); + } +} + +/** + * @param {Object} packageInfo - Starter's npm package information + * @param {string} packageInfo.name + * @param {string} packageInfo.version + * @param {string} parentDir - Path inside of which we install the starter. + */ +async function downloadNpmStarter({ name, version }, parentDir) { + // Download from npm + await execa.shell(`npm install ${name}@${version} --no-save --silent`, { + cwd: parentDir, + }); + + // Return the path of the actual starter + const exactStarterPath = path.dirname( + require.resolve(`${name}/package.json`, { paths: [parentDir] }) + ); + return exactStarterPath; +} + +module.exports = { getStarterPackageInfo, downloadNpmStarter }; diff --git a/packages/generators/app/lib/utils/fetch-npm-template.js b/packages/generators/app/lib/utils/fetch-npm-template.js index 2afe8a1a9c..a782dcadc0 100644 --- a/packages/generators/app/lib/utils/fetch-npm-template.js +++ b/packages/generators/app/lib/utils/fetch-npm-template.js @@ -34,7 +34,7 @@ async function getTemplatePackageInfo(template) { try { return getPackageInfo(template); } catch (error) { - throw new Error(`Could not find package ${chalk.green('template.json')} on npm`); + throw new Error(`Could not find package ${chalk.green(template)} on npm`); } } diff --git a/yarn.lock b/yarn.lock index b1060512b6..c540406973 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10830,21 +10830,6 @@ execa@1.0.0, execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" - integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - execa@5.1.1, execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -11869,13 +11854,6 @@ git-up@^4.0.0: is-ssh "^1.3.0" parse-url "^6.0.0" -git-url-parse@11.4.4: - version "11.4.4" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.4.4.tgz#5d747debc2469c17bc385719f7d0427802d83d77" - integrity sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw== - dependencies: - git-up "^4.0.0" - 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" From e3b7404434ca8f664304f78d3a85a461750d1790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Thu, 18 Nov 2021 18:33:35 +0100 Subject: [PATCH 02/12] Remove log --- packages/cli/create-strapi-starter/utils/build-starter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli/create-strapi-starter/utils/build-starter.js b/packages/cli/create-strapi-starter/utils/build-starter.js index f8cfc45ff7..5deaadf1a1 100644 --- a/packages/cli/create-strapi-starter/utils/build-starter.js +++ b/packages/cli/create-strapi-starter/utils/build-starter.js @@ -96,7 +96,6 @@ module.exports = async function buildStarter({ projectName, starter }, program) let starterParentPath; let starterPackageInfo = {}; const isLocalStarter = ['./', '../', '/'].some(filePrefix => starter.startsWith(filePrefix)); - console.log('STARTER', starter); // Project directory const rootPath = resolve(projectName); From 5a71284e8051aa7850d9a4e84a691f6962d3d2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Mon, 22 Nov 2021 10:37:00 +0100 Subject: [PATCH 03/12] Fix starter prompt --- .../cli/create-strapi-starter/package.json | 2 - .../utils/prompt-user.js | 50 ++----------------- yarn.lock | 7 --- 3 files changed, 4 insertions(+), 55 deletions(-) diff --git a/packages/cli/create-strapi-starter/package.json b/packages/cli/create-strapi-starter/package.json index 06344f059b..f84f948f99 100644 --- a/packages/cli/create-strapi-starter/package.json +++ b/packages/cli/create-strapi-starter/package.json @@ -23,8 +23,6 @@ "execa": "^1.0.0", "fs-extra": "9.1.0", "inquirer": "8.2.0", - "js-yaml": "4.1.0", - "node-fetch": "^2.6.1", "ora": "5.4.0" }, "scripts": { diff --git a/packages/cli/create-strapi-starter/utils/prompt-user.js b/packages/cli/create-strapi-starter/utils/prompt-user.js index 3f184a73df..b161505947 100644 --- a/packages/cli/create-strapi-starter/utils/prompt-user.js +++ b/packages/cli/create-strapi-starter/utils/prompt-user.js @@ -1,8 +1,6 @@ 'use strict'; const inquirer = require('inquirer'); -const fetch = require('node-fetch'); -const yaml = require('js-yaml'); /** * @param {string|null} projectName - The name/path of project @@ -55,50 +53,10 @@ module.exports = async function promptUser(projectName, starter, program) { * @returns Prompt question object */ async function getStarterQuestion() { - const content = await getStarterData(); - - // Fallback to manual input when fetch fails - if (!content) { - return { - type: 'input', - message: 'Please provide the GitHub URL for the starter you would like to use:', - }; - } - - const choices = content.map(option => { - const name = option.title.replace('Starter', ''); - - return { - name, - value: `https://github.com/${option.repo}`, - }; - }); - + // Ask user to manually input his starter + // TODO: find way to suggest the possible v4 starters return { - type: 'list', - message: - 'Which starter would you like to use? (Starters are fullstack Strapi applications designed for a specific use case)', - pageSize: choices.length, - choices, + type: 'input', + message: 'Please provide the npm package name of the starter you want to use:', }; } - -/** - * - * @returns JSON starter data - */ -async function getStarterData() { - const response = await fetch( - `https://api.github.com/repos/strapi/community-content/contents/starters/starters.yml` - ); - - if (!response.ok) { - return null; - } - - const { content } = await response.json(); - const buff = Buffer.from(content, 'base64'); - const stringified = buff.toString('utf-8'); - - return yaml.load(stringified); -} diff --git a/yarn.lock b/yarn.lock index 4ec3ce7203..b8040b0ba1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15333,13 +15333,6 @@ js-yaml@3.x, js-yaml@^3.10.0, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" From 9c33487894a009eac2bee2833d0b7fdb57bd4e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 23 Nov 2021 10:32:04 +0100 Subject: [PATCH 04/12] Apply feedback part 1 --- .../utils/build-starter.js | 35 +++++++++++-------- .../app/lib/utils/fetch-npm-template.js | 2 +- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/cli/create-strapi-starter/utils/build-starter.js b/packages/cli/create-strapi-starter/utils/build-starter.js index 5deaadf1a1..76814ae8c4 100644 --- a/packages/cli/create-strapi-starter/utils/build-starter.js +++ b/packages/cli/create-strapi-starter/utils/build-starter.js @@ -85,26 +85,16 @@ async function installWithLogs(path) { console.log(`Dependencies installed ${chalk.green('successfully')}.`); } -/** - * @param {Object} projectArgs - The arguments for create a project - * @param {string|null} projectArgs.projectName - The name/path of project - * @param {string|null} projectArgs.starter - The npm package of the starter - * @param {Object} program - Commands for generating new application - */ -module.exports = async function buildStarter({ projectName, starter }, program) { +async function getStarterInfo(starter) { + const isLocalStarter = ['./', '../', '/'].some(filePrefix => starter.startsWith(filePrefix)); let starterPath; let starterParentPath; let starterPackageInfo = {}; - const isLocalStarter = ['./', '../', '/'].some(filePrefix => starter.startsWith(filePrefix)); - - // Project directory - const rootPath = resolve(projectName); - const projectBasename = basename(rootPath); if (isLocalStarter) { // Starter is a local directory console.log('Installing local starter.'); - starterPath = resolve(rootPath, '..', starter); + starterPath = resolve(starter); } else { // Starter should be an npm package. Fetch starter info starterPackageInfo = await getStarterPackageInfo(starter); @@ -115,6 +105,21 @@ module.exports = async function buildStarter({ projectName, starter }, program) starterPath = await downloadNpmStarter(starterPackageInfo, starterParentPath); } + return { isLocalStarter, starterPath, starterParentPath, starterPackageInfo }; +} + +/** + * @param {Object} projectArgs - The arguments for create a project + * @param {string|null} projectArgs.projectName - The name/path of project + * @param {string|null} projectArgs.starter - The npm package of the starter + * @param {Object} program - Commands for generating new application + */ +module.exports = async function buildStarter({ projectName, starter }, program) { + const { isLocalStarter, starterPath, starterParentPath, starterPackageInfo } = getStarterInfo(); + + // Project directory + const rootPath = resolve(projectName); + const projectBasename = basename(rootPath); const starterJson = readStarterJson(join(starterPath, 'starter.json'), starter); try { @@ -152,8 +157,8 @@ module.exports = async function buildStarter({ projectName, starter }, program) await generateNewApp(join(rootPath, 'backend'), generateStrapiAppOptions); // Install frontend dependencies - console.log(`Creating Strapi starter frontend at ${chalk.green(frontendPath)}.`); - console.log(`Installing ${chalk.yellow(starterPackageInfo.name)} starter`); + console.log(`Creating Strapi starter frontend at ${chalk.yellow(frontendPath)}.`); + console.log('Installing frontend dependencies'); await installWithLogs(frontendPath); // Setup monorepo diff --git a/packages/generators/app/lib/utils/fetch-npm-template.js b/packages/generators/app/lib/utils/fetch-npm-template.js index a782dcadc0..e727b01f0d 100644 --- a/packages/generators/app/lib/utils/fetch-npm-template.js +++ b/packages/generators/app/lib/utils/fetch-npm-template.js @@ -34,7 +34,7 @@ async function getTemplatePackageInfo(template) { try { return getPackageInfo(template); } catch (error) { - throw new Error(`Could not find package ${chalk.green(template)} on npm`); + throw new Error(`Could not find package ${chalk.yellow(template)} on npm`); } } From 0e76ce4af97db6e4864b76044f1d45ec4d0c849f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 23 Nov 2021 12:57:53 +0100 Subject: [PATCH 05/12] Apply PR feedback --- .../utils/build-starter.js | 36 +++++++++++---- .../utils/fetch-npm-starter.js | 46 ++++++++++++++----- .../create-strapi-starter/utils/has-yarn.js | 5 +- 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/packages/cli/create-strapi-starter/utils/build-starter.js b/packages/cli/create-strapi-starter/utils/build-starter.js index 76814ae8c4..59d250406c 100644 --- a/packages/cli/create-strapi-starter/utils/build-starter.js +++ b/packages/cli/create-strapi-starter/utils/build-starter.js @@ -31,9 +31,10 @@ function readStarterJson(filePath, starter) { /** * @param {string} rootPath - Path to the project directory * @param {string} projectName - Name of the project + * @param {boolean} useYarn - Use yarn instead of npm */ -async function initPackageJson(rootPath, projectName) { - const packageManager = hasYarn() ? 'yarn --cwd' : 'npm run --prefix'; +async function initPackageJson(rootPath, projectName, useYarn) { + const packageManager = useYarn ? 'yarn --cwd' : 'npm run --prefix'; try { await fse.writeJson( @@ -85,8 +86,13 @@ async function installWithLogs(path) { console.log(`Dependencies installed ${chalk.green('successfully')}.`); } -async function getStarterInfo(starter) { +/** + * @param {string} starter The name of the starter as provided by the user + * @param {boolean} useYarn Use yarn instead of npm + */ +async function getStarterInfo(starter, useYarn) { const isLocalStarter = ['./', '../', '/'].some(filePrefix => starter.startsWith(filePrefix)); + let starterPath; let starterParentPath; let starterPackageInfo = {}; @@ -97,25 +103,31 @@ async function getStarterInfo(starter) { starterPath = resolve(starter); } else { // Starter should be an npm package. Fetch starter info - starterPackageInfo = await getStarterPackageInfo(starter); + starterPackageInfo = await getStarterPackageInfo(starter, useYarn); console.log(`Installing ${chalk.yellow(starterPackageInfo.name)} starter.`); // Download starter repository to a temporary directory starterParentPath = await fse.mkdtemp(join(os.tmpdir(), 'strapi-')); - starterPath = await downloadNpmStarter(starterPackageInfo, starterParentPath); + starterPath = await downloadNpmStarter(starterPackageInfo, starterParentPath, useYarn); } return { isLocalStarter, starterPath, starterParentPath, starterPackageInfo }; } /** - * @param {Object} projectArgs - The arguments for create a project + * @param {Object} projectArgs - The arguments for create a project * @param {string|null} projectArgs.projectName - The name/path of project * @param {string|null} projectArgs.starter - The npm package of the starter - * @param {Object} program - Commands for generating new application + * @param {Object} program - Commands for generating new application */ module.exports = async function buildStarter({ projectName, starter }, program) { - const { isLocalStarter, starterPath, starterParentPath, starterPackageInfo } = getStarterInfo(); + const hasYarnInstalled = hasYarn(); + const { + isLocalStarter, + starterPath, + starterParentPath, + starterPackageInfo, + } = await getStarterInfo(starter, hasYarnInstalled); // Project directory const rootPath = resolve(projectName); @@ -149,9 +161,13 @@ module.exports = async function buildStarter({ projectName, starter }, program) const generateStrapiAppOptions = { ...program, starter: starterPackageInfo.name, - template: `${starterJson.template.name}@${starterJson.template.version}`, run: false, }; + if (starterPackageInfo.version) { + starterPackageInfo.template = `${starterJson.template.name}@${starterJson.template.version}`; + } else { + starterPackageInfo.template = starterJson.template.name; + } // Create strapi app using the template await generateNewApp(join(rootPath, 'backend'), generateStrapiAppOptions); @@ -162,7 +178,7 @@ module.exports = async function buildStarter({ projectName, starter }, program) await installWithLogs(frontendPath); // Setup monorepo - initPackageJson(rootPath, projectBasename); + initPackageJson(rootPath, projectBasename, hasYarnInstalled); // Add gitignore try { diff --git a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js index fc27e86305..f4c0cc6d3e 100644 --- a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js +++ b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js @@ -7,10 +7,22 @@ const stopProcess = require('./stop-process'); /** * 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 + * @param {string} packageName Name to look up on npm, may include a specific version + * @param {boolean} useYarn Yarn instead of npm * @returns {Object} */ -async function getPackageInfo(packageName) { +async function getPackageInfo(packageName, useYarn) { + // Use yarn if possible because it's faster + if (useYarn) { + const { stdout } = await execa.shell(`yarn info ${packageName} --json`); + const yarnInfo = JSON.parse(stdout); + return { + name: yarnInfo.data.name, + version: yarnInfo.data.version, + }; + } + + // Fallback to npm 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); @@ -18,14 +30,16 @@ async function getPackageInfo(packageName) { } /** - * @param {string} starter - The name of the starter as provided by the user. - * @returns {Object} - The full name of the starter package's name on npm + * Get the version and full package name of the starter + * @param {string} starter - The name of the starter as provided by the user + * @param {boolean} useYarn - Use yarn instead of npm + * @returns {Object} - Full name and version of the starter package on npm */ -async function getStarterPackageInfo(starter) { +async function getStarterPackageInfo(starter, useYarn) { // Check if starter is a shorthand try { const longhand = `@strapi/starter-${starter}`; - const packageInfo = await getPackageInfo(longhand); + const packageInfo = await getPackageInfo(longhand, useYarn); // Hasn't crashed so it is indeed a shorthand return packageInfo; } catch (error) { @@ -40,16 +54,24 @@ async function getStarterPackageInfo(starter) { } /** + * Download a starter package from the npm registry * @param {Object} packageInfo - Starter's npm package information * @param {string} packageInfo.name * @param {string} packageInfo.version - * @param {string} parentDir - Path inside of which we install the starter. + * @param {string} parentDir - Path inside of which we install the starter + * @param {boolean} useYarn - Use yarn instead of npm */ -async function downloadNpmStarter({ name, version }, parentDir) { - // Download from npm - await execa.shell(`npm install ${name}@${version} --no-save --silent`, { - cwd: parentDir, - }); +async function downloadNpmStarter({ name, version }, parentDir, useYarn) { + // Download from npm, using yarn if possible + if (useYarn) { + await execa.shell(`yarn add ${name}@${version} --no-lockfile --silent`, { + cwd: parentDir, + }); + } else { + await execa.shell(`npm install ${name}@${version} --no-save --silent`, { + cwd: parentDir, + }); + } // Return the path of the actual starter const exactStarterPath = path.dirname( diff --git a/packages/cli/create-strapi-starter/utils/has-yarn.js b/packages/cli/create-strapi-starter/utils/has-yarn.js index 5fe5a3f459..f54fac3d8d 100644 --- a/packages/cli/create-strapi-starter/utils/has-yarn.js +++ b/packages/cli/create-strapi-starter/utils/has-yarn.js @@ -4,9 +4,8 @@ const execa = require('execa'); module.exports = function hasYarn() { try { - const { exitCode } = execa.sync('yarn --version', { shell: true }); - - if (exitCode === 0) return true; + const { code } = execa.shellSync('yarnpkg --version'); + if (code === 0) return true; return false; } catch (err) { return false; From 2ba7e8e061626ad656ac8e0b873afd9dddb52fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 23 Nov 2021 16:58:16 +0100 Subject: [PATCH 06/12] Apply new feedback --- .../cli/create-strapi-starter/package.json | 2 +- .../utils/build-starter.js | 32 +++++++++++-------- .../utils/child-process.js | 18 +++++++---- .../utils/fetch-npm-starter.js | 19 +++++------ .../create-strapi-starter/utils/has-yarn.js | 8 ++--- yarn.lock | 2 +- 6 files changed, 46 insertions(+), 35 deletions(-) diff --git a/packages/cli/create-strapi-starter/package.json b/packages/cli/create-strapi-starter/package.json index f84f948f99..18ddc5f8a1 100644 --- a/packages/cli/create-strapi-starter/package.json +++ b/packages/cli/create-strapi-starter/package.json @@ -20,7 +20,7 @@ "chalk": "4.1.1", "ci-info": "3.1.1", "commander": "7.1.0", - "execa": "^1.0.0", + "execa": "^5.1.1", "fs-extra": "9.1.0", "inquirer": "8.2.0", "ora": "5.4.0" diff --git a/packages/cli/create-strapi-starter/utils/build-starter.js b/packages/cli/create-strapi-starter/utils/build-starter.js index 59d250406c..69ae5b1fa2 100644 --- a/packages/cli/create-strapi-starter/utils/build-starter.js +++ b/packages/cli/create-strapi-starter/utils/build-starter.js @@ -29,11 +29,12 @@ function readStarterJson(filePath, starter) { } /** - * @param {string} rootPath - Path to the project directory - * @param {string} projectName - Name of the project - * @param {boolean} useYarn - Use yarn instead of npm + * @param {Object} options + * @param {string} options.rootPath - Path to the project directory + * @param {string} options.projectName - Name of the project + * @param {boolean} options.useYarn - Use yarn instead of npm */ -async function initPackageJson(rootPath, projectName, useYarn) { +async function initPackageJson({ rootPath, projectName, useYarn }) { const packageManager = useYarn ? 'yarn --cwd' : 'npm run --prefix'; try { @@ -64,9 +65,11 @@ async function initPackageJson(rootPath, projectName, useYarn) { } /** - * @param {string} path - The directory path for install + * @param {Object} options + * @param {string} options.path The directory path for install + * @param {boolean} options.useYarn Use yarn instead of npm */ -async function installWithLogs(path) { +async function installWithLogs(options) { const installPrefix = chalk.yellow('Installing dependencies:'); const loader = ora(installPrefix).start(); const logInstall = (chunk = '') => { @@ -76,7 +79,7 @@ async function installWithLogs(path) { .join(' ')}`; }; - const runner = runInstall(path); + const runner = runInstall(options); runner.stdout.on('data', logInstall); runner.stderr.on('data', logInstall); @@ -87,10 +90,11 @@ async function installWithLogs(path) { } /** - * @param {string} starter The name of the starter as provided by the user - * @param {boolean} useYarn Use yarn instead of npm + * @param {Object} options + * @param {string} options.starter The name of the starter as provided by the user + * @param {boolean} options.useYarn Use yarn instead of npm */ -async function getStarterInfo(starter, useYarn) { +async function getStarterInfo({ starter, useYarn }) { const isLocalStarter = ['./', '../', '/'].some(filePrefix => starter.startsWith(filePrefix)); let starterPath; @@ -121,13 +125,13 @@ async function getStarterInfo(starter, useYarn) { * @param {Object} program - Commands for generating new application */ module.exports = async function buildStarter({ projectName, starter }, program) { - const hasYarnInstalled = hasYarn(); + const hasYarnInstalled = await hasYarn(); const { isLocalStarter, starterPath, starterParentPath, starterPackageInfo, - } = await getStarterInfo(starter, hasYarnInstalled); + } = await getStarterInfo({ starter, useYarn: hasYarnInstalled }); // Project directory const rootPath = resolve(projectName); @@ -178,7 +182,7 @@ module.exports = async function buildStarter({ projectName, starter }, program) await installWithLogs(frontendPath); // Setup monorepo - initPackageJson(rootPath, projectBasename, hasYarnInstalled); + initPackageJson({ rootPath, projectBasename, useYarn: hasYarnInstalled }); // Add gitignore try { @@ -195,5 +199,5 @@ module.exports = async function buildStarter({ projectName, starter }, program) } console.log(chalk.green('Starting the app')); - await runApp(rootPath); + await runApp({ rootPath, useYarn: hasYarnInstalled }); }; diff --git a/packages/cli/create-strapi-starter/utils/child-process.js b/packages/cli/create-strapi-starter/utils/child-process.js index e44b60705f..7bb08264e4 100644 --- a/packages/cli/create-strapi-starter/utils/child-process.js +++ b/packages/cli/create-strapi-starter/utils/child-process.js @@ -2,14 +2,15 @@ const { execSync } = require('child_process'); const execa = require('execa'); -const hasYarn = require('./has-yarn'); const logger = require('./logger'); /** - * @param {string} path Path to directory (frontend, backend) + * @param {Object} options + * @param {string} options.path Path to directory (frontend, backend) + * @param {boolean} options.useYarn Use yarn instead of npm */ -function runInstall(path) { - if (hasYarn()) { +function runInstall({ path, useYarn }) { + if (useYarn) { return execa('yarn', ['install'], { cwd: path, stdin: 'ignore', @@ -19,8 +20,13 @@ function runInstall(path) { return execa('npm', ['install'], { cwd: path, stdin: 'ignore' }); } -function runApp(rootPath) { - if (hasYarn()) { +/** + * @param {Object} options + * @param {string} options.rootPath + * @param {boolean} options.useYarn + */ +function runApp({ rootPath, useYarn }) { + if (useYarn) { return execa('yarn', ['develop'], { stdio: 'inherit', cwd: rootPath, diff --git a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js index f4c0cc6d3e..a3c4ac2bd2 100644 --- a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js +++ b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js @@ -7,14 +7,15 @@ const stopProcess = require('./stop-process'); /** * 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 - * @param {boolean} useYarn Yarn instead of npm + * @param {Object} options + * @param {string} options.packageName Name to look up on npm, may include a specific version + * @param {boolean} options.useYarn Yarn instead of npm * @returns {Object} */ -async function getPackageInfo(packageName, useYarn) { +async function getPackageInfo({ packageName, useYarn }) { // Use yarn if possible because it's faster if (useYarn) { - const { stdout } = await execa.shell(`yarn info ${packageName} --json`); + const { stdout } = await execa.command(`yarn info ${packageName} --json`); const yarnInfo = JSON.parse(stdout); return { name: yarnInfo.data.name, @@ -23,7 +24,7 @@ async function getPackageInfo(packageName, useYarn) { } // Fallback to npm - const { stdout } = await execa.shell(`npm view ${packageName} name version --silent`); + const { stdout } = await execa.command(`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 }; @@ -39,7 +40,7 @@ async function getStarterPackageInfo(starter, useYarn) { // Check if starter is a shorthand try { const longhand = `@strapi/starter-${starter}`; - const packageInfo = await getPackageInfo(longhand, useYarn); + const packageInfo = await getPackageInfo({ packageName: longhand, useYarn }); // Hasn't crashed so it is indeed a shorthand return packageInfo; } catch (error) { @@ -47,7 +48,7 @@ async function getStarterPackageInfo(starter, useYarn) { } // Fetch version of the non-shorthand package try { - return getPackageInfo(starter); + return getPackageInfo({ packageName: starter, useYarn }); } catch (error) { stopProcess(`Could not find package ${chalk.green(starter)} on npm`); } @@ -64,11 +65,11 @@ async function getStarterPackageInfo(starter, useYarn) { async function downloadNpmStarter({ name, version }, parentDir, useYarn) { // Download from npm, using yarn if possible if (useYarn) { - await execa.shell(`yarn add ${name}@${version} --no-lockfile --silent`, { + await execa.command(`yarn add ${name}@${version} --no-lockfile --silent`, { cwd: parentDir, }); } else { - await execa.shell(`npm install ${name}@${version} --no-save --silent`, { + await execa.command(`npm install ${name}@${version} --no-save --silent`, { cwd: parentDir, }); } diff --git a/packages/cli/create-strapi-starter/utils/has-yarn.js b/packages/cli/create-strapi-starter/utils/has-yarn.js index f54fac3d8d..0fe1f8f3dc 100644 --- a/packages/cli/create-strapi-starter/utils/has-yarn.js +++ b/packages/cli/create-strapi-starter/utils/has-yarn.js @@ -2,11 +2,11 @@ const execa = require('execa'); -module.exports = function hasYarn() { +module.exports = async function hasYarn() { try { - const { code } = execa.shellSync('yarnpkg --version'); - if (code === 0) return true; - return false; + const { exitCode } = await execa.commandSync('yarn --version', { shell: true }); + + if (exitCode === 0) return true; } catch (err) { return false; } diff --git a/yarn.lock b/yarn.lock index b8040b0ba1..90747e17ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11567,7 +11567,7 @@ execa@1.0.0, execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@5.1.1, execa@^5.0.0: +execa@5.1.1, execa@^5.0.0, execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== From 288d3aeeb544b09207df25543813742180717932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 23 Nov 2021 17:36:58 +0100 Subject: [PATCH 07/12] Fix frontend dependencies install --- packages/cli/create-strapi-starter/utils/build-starter.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/cli/create-strapi-starter/utils/build-starter.js b/packages/cli/create-strapi-starter/utils/build-starter.js index 69ae5b1fa2..4d395a7076 100644 --- a/packages/cli/create-strapi-starter/utils/build-starter.js +++ b/packages/cli/create-strapi-starter/utils/build-starter.js @@ -3,7 +3,6 @@ const { resolve, join, basename } = require('path'); const os = require('os'); const fse = require('fs-extra'); - const ora = require('ora'); const ciEnv = require('ci-info'); const chalk = require('chalk'); @@ -179,7 +178,7 @@ module.exports = async function buildStarter({ projectName, starter }, program) // Install frontend dependencies console.log(`Creating Strapi starter frontend at ${chalk.yellow(frontendPath)}.`); console.log('Installing frontend dependencies'); - await installWithLogs(frontendPath); + await installWithLogs({ path: frontendPath, useYarn: hasYarnInstalled }); // Setup monorepo initPackageJson({ rootPath, projectBasename, useYarn: hasYarnInstalled }); From 49097b2947ccc0c8539dd6171ea1da8b048099b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 23 Nov 2021 17:44:26 +0100 Subject: [PATCH 08/12] Fix monorepo dependencies install --- packages/cli/create-strapi-starter/utils/build-starter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/create-strapi-starter/utils/build-starter.js b/packages/cli/create-strapi-starter/utils/build-starter.js index 4d395a7076..56e606d6cc 100644 --- a/packages/cli/create-strapi-starter/utils/build-starter.js +++ b/packages/cli/create-strapi-starter/utils/build-starter.js @@ -191,7 +191,7 @@ module.exports = async function buildStarter({ projectName, starter }, program) logger.warn(`Failed to create file: ${chalk.yellow('.gitignore')}`); } - await installWithLogs(rootPath); + await installWithLogs({ path: rootPath, useYarn: hasYarnInstalled }); if (!ciEnv.isCI) { await initGit(rootPath); From 965d27fa02fd6908db602a25069795f304167f2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Tue, 23 Nov 2021 19:09:56 +0100 Subject: [PATCH 09/12] Apply newest feedback --- .../utils/build-starter.js | 26 +++++++++---------- .../utils/child-process.js | 20 ++++++-------- .../utils/fetch-npm-starter.js | 8 +++--- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/packages/cli/create-strapi-starter/utils/build-starter.js b/packages/cli/create-strapi-starter/utils/build-starter.js index 56e606d6cc..6ffcc172fb 100644 --- a/packages/cli/create-strapi-starter/utils/build-starter.js +++ b/packages/cli/create-strapi-starter/utils/build-starter.js @@ -28,12 +28,12 @@ function readStarterJson(filePath, starter) { } /** + * @param {string} rootPath - Path to the project directory + * @param {string} projectName - Name of the project * @param {Object} options - * @param {string} options.rootPath - Path to the project directory - * @param {string} options.projectName - Name of the project * @param {boolean} options.useYarn - Use yarn instead of npm */ -async function initPackageJson({ rootPath, projectName, useYarn }) { +async function initPackageJson(rootPath, projectName, { useYarn } ) { const packageManager = useYarn ? 'yarn --cwd' : 'npm run --prefix'; try { @@ -64,11 +64,11 @@ async function initPackageJson({ rootPath, projectName, useYarn }) { } /** + * @param {string} path The directory path for install * @param {Object} options - * @param {string} options.path The directory path for install * @param {boolean} options.useYarn Use yarn instead of npm */ -async function installWithLogs(options) { +async function installWithLogs(path, options) { const installPrefix = chalk.yellow('Installing dependencies:'); const loader = ora(installPrefix).start(); const logInstall = (chunk = '') => { @@ -78,7 +78,7 @@ async function installWithLogs(options) { .join(' ')}`; }; - const runner = runInstall(options); + const runner = runInstall(path, options); runner.stdout.on('data', logInstall); runner.stderr.on('data', logInstall); @@ -89,11 +89,11 @@ async function installWithLogs(options) { } /** + * @param {string} starter The name of the starter as provided by the user * @param {Object} options - * @param {string} options.starter The name of the starter as provided by the user * @param {boolean} options.useYarn Use yarn instead of npm */ -async function getStarterInfo({ starter, useYarn }) { +async function getStarterInfo(starter, { useYarn }) { const isLocalStarter = ['./', '../', '/'].some(filePrefix => starter.startsWith(filePrefix)); let starterPath; @@ -130,7 +130,7 @@ module.exports = async function buildStarter({ projectName, starter }, program) starterPath, starterParentPath, starterPackageInfo, - } = await getStarterInfo({ starter, useYarn: hasYarnInstalled }); + } = await getStarterInfo(starter, { useYarn: hasYarnInstalled }); // Project directory const rootPath = resolve(projectName); @@ -178,10 +178,10 @@ module.exports = async function buildStarter({ projectName, starter }, program) // Install frontend dependencies console.log(`Creating Strapi starter frontend at ${chalk.yellow(frontendPath)}.`); console.log('Installing frontend dependencies'); - await installWithLogs({ path: frontendPath, useYarn: hasYarnInstalled }); + await installWithLogs(frontendPath, { useYarn: hasYarnInstalled }); // Setup monorepo - initPackageJson({ rootPath, projectBasename, useYarn: hasYarnInstalled }); + initPackageJson(rootPath, projectBasename, { useYarn: hasYarnInstalled }); // Add gitignore try { @@ -191,12 +191,12 @@ module.exports = async function buildStarter({ projectName, starter }, program) logger.warn(`Failed to create file: ${chalk.yellow('.gitignore')}`); } - await installWithLogs({ path: rootPath, useYarn: hasYarnInstalled }); + await installWithLogs(rootPath, { useYarn: hasYarnInstalled }); if (!ciEnv.isCI) { await initGit(rootPath); } console.log(chalk.green('Starting the app')); - await runApp({ rootPath, useYarn: hasYarnInstalled }); + await runApp(rootPath, { useYarn: hasYarnInstalled }); }; diff --git a/packages/cli/create-strapi-starter/utils/child-process.js b/packages/cli/create-strapi-starter/utils/child-process.js index 7bb08264e4..893743b183 100644 --- a/packages/cli/create-strapi-starter/utils/child-process.js +++ b/packages/cli/create-strapi-starter/utils/child-process.js @@ -5,27 +5,23 @@ const execa = require('execa'); const logger = require('./logger'); /** + * @param {string} path Path to directory (frontend, backend) * @param {Object} options - * @param {string} options.path Path to directory (frontend, backend) * @param {boolean} options.useYarn Use yarn instead of npm */ -function runInstall({ path, useYarn }) { - if (useYarn) { - return execa('yarn', ['install'], { - cwd: path, - stdin: 'ignore', - }); - } - - return execa('npm', ['install'], { cwd: path, stdin: 'ignore' }); +function runInstall(path, { useYarn }) { + return execa(useYarn ? 'yarn' : 'npm', ['install'], { + cwd: path, + stdin: 'ignore', + }); } /** + * @param {string} rootPath * @param {Object} options - * @param {string} options.rootPath * @param {boolean} options.useYarn */ -function runApp({ rootPath, useYarn }) { +function runApp(rootPath, { useYarn }) { if (useYarn) { return execa('yarn', ['develop'], { stdio: 'inherit', diff --git a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js index a3c4ac2bd2..7a7aa410b5 100644 --- a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js +++ b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js @@ -7,12 +7,12 @@ const stopProcess = require('./stop-process'); /** * 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 * @param {Object} options - * @param {string} options.packageName Name to look up on npm, may include a specific version * @param {boolean} options.useYarn Yarn instead of npm * @returns {Object} */ -async function getPackageInfo({ packageName, useYarn }) { +async function getPackageInfo(packageName, { useYarn }) { // Use yarn if possible because it's faster if (useYarn) { const { stdout } = await execa.command(`yarn info ${packageName} --json`); @@ -40,7 +40,7 @@ async function getStarterPackageInfo(starter, useYarn) { // Check if starter is a shorthand try { const longhand = `@strapi/starter-${starter}`; - const packageInfo = await getPackageInfo({ packageName: longhand, useYarn }); + const packageInfo = await getPackageInfo(longhand, { useYarn }); // Hasn't crashed so it is indeed a shorthand return packageInfo; } catch (error) { @@ -48,7 +48,7 @@ async function getStarterPackageInfo(starter, useYarn) { } // Fetch version of the non-shorthand package try { - return getPackageInfo({ packageName: starter, useYarn }); + return getPackageInfo(starter, { useYarn }); } catch (error) { stopProcess(`Could not find package ${chalk.green(starter)} on npm`); } From 983ea2ccf94eb8bae422ce4d86929e192e1f1401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Wed, 24 Nov 2021 09:52:26 +0100 Subject: [PATCH 10/12] Add default option objects --- packages/cli/create-strapi-starter/utils/build-starter.js | 4 ++-- packages/cli/create-strapi-starter/utils/child-process.js | 4 ++-- packages/cli/create-strapi-starter/utils/fetch-npm-starter.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/create-strapi-starter/utils/build-starter.js b/packages/cli/create-strapi-starter/utils/build-starter.js index 6ffcc172fb..38c99df429 100644 --- a/packages/cli/create-strapi-starter/utils/build-starter.js +++ b/packages/cli/create-strapi-starter/utils/build-starter.js @@ -33,7 +33,7 @@ function readStarterJson(filePath, starter) { * @param {Object} options * @param {boolean} options.useYarn - Use yarn instead of npm */ -async function initPackageJson(rootPath, projectName, { useYarn } ) { +async function initPackageJson(rootPath, projectName, { useYarn } = {}) { const packageManager = useYarn ? 'yarn --cwd' : 'npm run --prefix'; try { @@ -93,7 +93,7 @@ async function installWithLogs(path, options) { * @param {Object} options * @param {boolean} options.useYarn Use yarn instead of npm */ -async function getStarterInfo(starter, { useYarn }) { +async function getStarterInfo(starter, { useYarn } = {}) { const isLocalStarter = ['./', '../', '/'].some(filePrefix => starter.startsWith(filePrefix)); let starterPath; diff --git a/packages/cli/create-strapi-starter/utils/child-process.js b/packages/cli/create-strapi-starter/utils/child-process.js index 893743b183..9ba241e5df 100644 --- a/packages/cli/create-strapi-starter/utils/child-process.js +++ b/packages/cli/create-strapi-starter/utils/child-process.js @@ -9,7 +9,7 @@ const logger = require('./logger'); * @param {Object} options * @param {boolean} options.useYarn Use yarn instead of npm */ -function runInstall(path, { useYarn }) { +function runInstall(path, { useYarn } = {}) { return execa(useYarn ? 'yarn' : 'npm', ['install'], { cwd: path, stdin: 'ignore', @@ -21,7 +21,7 @@ function runInstall(path, { useYarn }) { * @param {Object} options * @param {boolean} options.useYarn */ -function runApp(rootPath, { useYarn }) { +function runApp(rootPath, { useYarn } = {}) { if (useYarn) { return execa('yarn', ['develop'], { stdio: 'inherit', diff --git a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js index 7a7aa410b5..00541c975d 100644 --- a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js +++ b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js @@ -12,7 +12,7 @@ const stopProcess = require('./stop-process'); * @param {boolean} options.useYarn Yarn instead of npm * @returns {Object} */ -async function getPackageInfo(packageName, { useYarn }) { +async function getPackageInfo(packageName, { useYarn } = {}) { // Use yarn if possible because it's faster if (useYarn) { const { stdout } = await execa.command(`yarn info ${packageName} --json`); From 11bbe7616762a405061ee52d977767a8a995f0b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Wed, 24 Nov 2021 10:02:28 +0100 Subject: [PATCH 11/12] Change error log color --- packages/cli/create-strapi-starter/utils/fetch-npm-starter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js index 00541c975d..e0535819f7 100644 --- a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js +++ b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js @@ -50,7 +50,7 @@ async function getStarterPackageInfo(starter, useYarn) { try { return getPackageInfo(starter, { useYarn }); } catch (error) { - stopProcess(`Could not find package ${chalk.green(starter)} on npm`); + stopProcess(`Could not find package ${chalk.yellow(starter)} on npm`); } } From 0e419be70f589f1a1763ac9fb318a00a6847aa59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20de=20Juvigny?= Date: Wed, 24 Nov 2021 10:09:34 +0100 Subject: [PATCH 12/12] useYarn in options object --- .../cli/create-strapi-starter/utils/build-starter.js | 4 ++-- .../create-strapi-starter/utils/fetch-npm-starter.js | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/cli/create-strapi-starter/utils/build-starter.js b/packages/cli/create-strapi-starter/utils/build-starter.js index 38c99df429..6654462df5 100644 --- a/packages/cli/create-strapi-starter/utils/build-starter.js +++ b/packages/cli/create-strapi-starter/utils/build-starter.js @@ -106,12 +106,12 @@ async function getStarterInfo(starter, { useYarn } = {}) { starterPath = resolve(starter); } else { // Starter should be an npm package. Fetch starter info - starterPackageInfo = await getStarterPackageInfo(starter, useYarn); + starterPackageInfo = await getStarterPackageInfo(starter, { useYarn }); console.log(`Installing ${chalk.yellow(starterPackageInfo.name)} starter.`); // Download starter repository to a temporary directory starterParentPath = await fse.mkdtemp(join(os.tmpdir(), 'strapi-')); - starterPath = await downloadNpmStarter(starterPackageInfo, starterParentPath, useYarn); + starterPath = await downloadNpmStarter(starterPackageInfo, starterParentPath, { useYarn }); } return { isLocalStarter, starterPath, starterParentPath, starterPackageInfo }; diff --git a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js index e0535819f7..d6563aaba2 100644 --- a/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js +++ b/packages/cli/create-strapi-starter/utils/fetch-npm-starter.js @@ -33,10 +33,11 @@ async function getPackageInfo(packageName, { useYarn } = {}) { /** * Get the version and full package name of the starter * @param {string} starter - The name of the starter as provided by the user - * @param {boolean} useYarn - Use yarn instead of npm + * @param {Object} options + * @param {boolean} options.useYarn - Use yarn instead of npm * @returns {Object} - Full name and version of the starter package on npm */ -async function getStarterPackageInfo(starter, useYarn) { +async function getStarterPackageInfo(starter, { useYarn } = {}) { // Check if starter is a shorthand try { const longhand = `@strapi/starter-${starter}`; @@ -60,9 +61,10 @@ async function getStarterPackageInfo(starter, useYarn) { * @param {string} packageInfo.name * @param {string} packageInfo.version * @param {string} parentDir - Path inside of which we install the starter - * @param {boolean} useYarn - Use yarn instead of npm + * @param {Object} options + * @param {boolean} options.useYarn - Use yarn instead of npm */ -async function downloadNpmStarter({ name, version }, parentDir, useYarn) { +async function downloadNpmStarter({ name, version }, parentDir, { useYarn } = {}) { // Download from npm, using yarn if possible if (useYarn) { await execa.command(`yarn add ${name}@${version} --no-lockfile --silent`, {