From 4fbded2e336fd97548bf3df23d764f3a500fe5ec Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Mon, 30 Nov 2020 13:06:59 +0000 Subject: [PATCH] fix(create-gatsby): Improve install (#28318) --- packages/create-gatsby/src/index.ts | 41 +++++----- packages/create-gatsby/src/init-starter.ts | 94 ++++++++++++++++------ 2 files changed, 89 insertions(+), 46 deletions(-) diff --git a/packages/create-gatsby/src/index.ts b/packages/create-gatsby/src/index.ts index a0c620f0cad20..6b8456886e8fd 100644 --- a/packages/create-gatsby/src/index.ts +++ b/packages/create-gatsby/src/index.ts @@ -2,17 +2,18 @@ import Enquirer from "enquirer" import cmses from "./cmses.json" import styles from "./styles.json" import features from "./features.json" -import { initStarter, getPackageManager } from "./init-starter" +import { initStarter, getPackageManager, gitSetup } from "./init-starter" import { installPlugins } from "./install-plugins" import c from "ansi-colors" import path from "path" import fs from "fs" import { plugin } from "./components/plugin" import { makePluginConfigQuestions } from "./plugin-options-form" -import { center, rule, wrap } from "./components/utils" +import { center, wrap } from "./components/utils" import { stripIndent } from "common-tags" import { trackCli } from "./tracking" import crypto from "crypto" +import { reporter } from "./reporter" import { setSiteMetadata } from "./site-metadata" const sha256 = (str: string): string => @@ -51,6 +52,7 @@ const makeChoices = ( export const validateProjectName = async ( value: string ): Promise => { + value = value.trim() if (INVALID_FILENAMES.test(value)) { return `The destination "${value}" is not a valid filename. Please try again, avoiding special characters.` } @@ -139,9 +141,9 @@ export async function run(): Promise { const { version } = require(`../package.json`) - console.log(c.grey(`create-gatsby version ${version}`)) + reporter.info(c.grey(`create-gatsby version ${version}`)) - console.log( + reporter.info( ` @@ -151,23 +153,23 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} ` ) - console.log( + reporter.info( wrap( `This command will generate a new Gatsby site for you in ${c.bold( process.cwd() )} with the setup you select. ${c.white.bold( - `Let's answer some questions:\n` + `Let's answer some questions:\n\n` )}`, process.stdout.columns ) ) - console.log(``) const enquirer = new Enquirer() enquirer.use(plugin) const data = await enquirer.prompt(questions) + data.project = data.project.trim() trackCli(`CREATE_GATSBY_SELECT_OPTION`, { name: `project_name`, @@ -260,7 +262,7 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} const config = makePluginConfigQuestions(plugins) if (config.length) { - console.log( + reporter.info( `\nGreat! A few of the selections you made need to be configured. Please fill in the options for each plugin now:\n` ) @@ -274,7 +276,7 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} trackCli(`CREATE_GATSBY_SET_PLUGINS_STOP`) } - console.log(` + reporter.info(` ${c.bold(`Thanks! Here's what we'll now do:`)} @@ -292,45 +294,44 @@ ${c.bold(`Thanks! Here's what we'll now do:`)} if (!confirm) { trackCli(`CREATE_GATSBY_CANCEL`) - console.log(`OK, bye!`) + reporter.info(`OK, bye!`) return } await initStarter(DEFAULT_STARTER, data.project, packages.map(removeKey)) - console.log( - c.green(c.symbols.check) + ` Created site in ` + c.green(data.project) - ) + reporter.success(`Created site in ${c.green(data.project)}`) const fullPath = path.resolve(data.project) if (plugins.length) { - console.log(c.bold(`${w(`🔌 `)}Installing plugins...`)) + reporter.info(`${w(`🔌 `)}Setting-up plugins...`) await installPlugins(plugins, pluginConfig, fullPath, []) } await setSiteMetadata(fullPath, `title`, data.project) - const pm = await getPackageManager() + await gitSetup(data.project) + const pm = await getPackageManager() const runCommand = pm === `npm` ? `npm run` : `yarn` - console.log( + reporter.info( stripIndent` ${w(`🎉 `)}Your new Gatsby site ${c.bold( data.project - )} has been successfully bootstrapped + )} has been successfully created at ${c.bold(fullPath)}. ` ) - console.log(`Start by going to the directory with\n + reporter.info(`Start by going to the directory with\n ${c.magenta(`cd ${data.project}`)} `) - console.log(`Start the local development server with\n + reporter.info(`Start the local development server with\n ${c.magenta(`${runCommand} develop`)} `) - console.log(`See all commands at\n + reporter.info(`See all commands at\n ${c.blueBright(`https://www.gatsbyjs.com/docs/gatsby-cli/`)} `) diff --git a/packages/create-gatsby/src/init-starter.ts b/packages/create-gatsby/src/init-starter.ts index c06f84c315e1c..fa9f4e6c76fdc 100644 --- a/packages/create-gatsby/src/init-starter.ts +++ b/packages/create-gatsby/src/init-starter.ts @@ -1,21 +1,40 @@ import { execSync } from "child_process" -import execa from "execa" +import execa, { Options } from "execa" import fs from "fs-extra" import path from "path" import { reporter } from "./reporter" import { spin } from "tiny-spin" import { getConfigStore } from "./get-config-store" type PackageManager = "yarn" | "npm" +import c from "ansi-colors" -const packageMangerConfigKey = `cli.packageManager` +const packageManagerConfigKey = `cli.packageManager` + +const kebabify = (str: string): string => + str + .replace(/([a-z])([A-Z])/g, `$1-$2`) + .replace(/[^a-zA-Z]+/g, `-`) + .toLowerCase() export const getPackageManager = (): PackageManager => - getConfigStore().get(packageMangerConfigKey) + getConfigStore().get(packageManagerConfigKey) export const setPackageManager = (packageManager: PackageManager): void => { - getConfigStore().set(packageMangerConfigKey, packageManager) + getConfigStore().set(packageManagerConfigKey, packageManager) } +const ESC = `\u001b` + +export const clearLine = (count = 1): Promise => + new Promise(resolve => { + // First move the cursor up one line... + process.stderr.moveCursor(0, -count, () => { + // ... then clear that line. This is the ANSI escape sequence for "clear whole line" + // List of escape sequences: http://ascii-table.com/ansi-escape-sequences.php + process.stderr.write(`${ESC}[2K`) + resolve() + }) + }) // Checks the existence of yarn package // We use yarnpkg instead of yarn to avoid conflict with Hadoop yarn // Refer to https://github.com/yarnpkg/yarn/issues/673 @@ -31,11 +50,8 @@ const checkForYarn = (): boolean => { // Initialize newly cloned directory as a git repo const gitInit = async ( rootPath: string -): Promise> => { - reporter.info(`Initialising git in ${rootPath}`) - - return await execa(`git`, [`init`], { cwd: rootPath }) -} +): Promise> => + await execa(`git`, [`init`], { cwd: rootPath }) // Create a .gitignore file if it is missing in the new directory const maybeCreateGitIgnore = async (rootPath: string): Promise => { @@ -43,7 +59,6 @@ const maybeCreateGitIgnore = async (rootPath: string): Promise => { return } - reporter.info(`Creating minimal .gitignore in ${rootPath}`) await fs.writeFile( path.join(rootPath, `.gitignore`), `.cache\nnode_modules\npublic\n` @@ -52,8 +67,6 @@ const maybeCreateGitIgnore = async (rootPath: string): Promise => { // Create an initial git commit in the new directory const createInitialGitCommit = async (rootPath: string): Promise => { - reporter.info(`Create initial git commit in ${rootPath}`) - await execa(`git`, [`add`, `-A`], { cwd: rootPath }) // use execSync instead of spawn to handle git clients using // pgp signatures (with password) @@ -68,6 +81,28 @@ const createInitialGitCommit = async (rootPath: string): Promise => { } } +const setNameInPackage = async ( + sitePath: string, + name: string +): Promise => { + const packageJsonPath = path.join(sitePath, `package.json`) + const packageJson = await fs.readJSON(packageJsonPath) + packageJson.name = kebabify(name) + packageJson.description = `My Gatsby site` + try { + const result = await execa(`git`, [`config`, `user.name`]) + if (result.failed) { + delete packageJson.author + } else { + packageJson.author = result.stdout + } + } catch (e) { + delete packageJson.author + } + + await fs.writeJSON(packageJsonPath, packageJson) +} + // Executes `npm install` or `yarn install` in rootPath. const install = async ( rootPath: string, @@ -75,14 +110,12 @@ const install = async ( ): Promise => { const prevDir = process.cwd() - let stop = spin(`Installing packages...`) + reporter.info(`${c.blueBright(c.symbols.pointer)} Installing Gatsby...`) process.chdir(rootPath) const npmConfigUserAgent = process.env.npm_config_user_agent - const silent = `--silent` - try { if (!getPackageManager()) { if (npmConfigUserAgent?.includes(`yarn`)) { @@ -91,25 +124,33 @@ const install = async ( setPackageManager(`npm`) } } + const options: Options = { + stderr: `inherit`, + } + + const config = [`--loglevel`, `error`, `--color`, `always`] + if (getPackageManager() === `yarn` && checkForYarn()) { await fs.remove(`package-lock.json`) - const args = packages.length ? [`add`, silent, ...packages] : [silent] - await execa(`yarnpkg`, args) + const args = packages.length + ? [`add`, `--silent`, ...packages] + : [`--silent`] + await execa(`yarnpkg`, args, options) } else { await fs.remove(`yarn.lock`) - await execa(`npm`, [`install`, silent]) - stop() - stop = spin(`Installing plugins...`) - await execa(`npm`, [`install`, silent, ...packages]) - stop() + await execa(`npm`, [`install`, ...config], options) + await clearLine() + reporter.success(`Installed Gatsby`) + reporter.info(`${c.blueBright(c.symbols.pointer)} Installing plugins...`) + await execa(`npm`, [`install`, ...config, ...packages], options) + await clearLine() + reporter.success(`Installed plugins`) } } catch (e) { reporter.panic(e.message) } finally { process.chdir(prevDir) - stop() - reporter.success(`Installed packages`) } } @@ -143,7 +184,7 @@ const clone = async ( await fs.remove(path.join(rootPath, `.git`)) } -async function gitSetup(rootPath: string): Promise { +export async function gitSetup(rootPath: string): Promise { await gitInit(rootPath) await maybeCreateGitIgnore(rootPath) await createInitialGitCommit(rootPath) @@ -161,8 +202,9 @@ export async function initStarter( await clone(starter, sitePath) + await setNameInPackage(sitePath, rootPath) + await install(rootPath, packages) - await gitSetup(rootPath) // trackCli(`NEW_PROJECT_END`); }