diff options
author | 2022-10-26 10:13:56 -0500 | |
---|---|---|
committer | 2022-10-26 10:13:56 -0500 | |
commit | 04083762810a1a9e078a7e68edab945c8063b1ab (patch) | |
tree | 784baa969ba4f3a68f3420dc909135359a58f3cd /packages/create-astro/src/index.ts | |
parent | ddf2f8390e8dcc64b44636524bdcddae977779f4 (diff) | |
download | astro-04083762810a1a9e078a7e68edab945c8063b1ab.tar.gz astro-04083762810a1a9e078a7e68edab945c8063b1ab.tar.zst astro-04083762810a1a9e078a7e68edab945c8063b1ab.zip |
[Create Astro] Improved prompts, template handling, Houston (#5088)
* feat(create-astro): add houston, improve prompts
* refactor(create-astro): move to giget
* chore: add changeset
* chore: update lockfile
* test(create-astro): update tests to match new output
* chore: prefer named functions
* fix: update template prompt
* fix: update typescript message
* chore: add explicit --skip-houston flag
* test(create-astro): skip flaky typescript test
Co-authored-by: Nate Moore <nate@astro.build>
Diffstat (limited to 'packages/create-astro/src/index.ts')
-rw-r--r-- | packages/create-astro/src/index.ts | 194 |
1 files changed, 36 insertions, 158 deletions
diff --git a/packages/create-astro/src/index.ts b/packages/create-astro/src/index.ts index 2337674ea..78036716f 100644 --- a/packages/create-astro/src/index.ts +++ b/packages/create-astro/src/index.ts @@ -1,11 +1,12 @@ /* eslint no-console: 'off' */ import { assign, parse, stringify } from 'comment-json'; -import degit from 'degit'; +import { downloadTemplate } from 'giget'; import { execa, execaCommand } from 'execa'; import fs from 'fs'; -import { bgCyan, black, bold, cyan, dim, gray, green, red, reset, yellow } from 'kleur/colors'; +import { say, label, color, generateProjectName } from '@astrojs/cli-kit'; +import { random } from '@astrojs/cli-kit/utils'; +import { bold, dim, green, red, reset, yellow } from 'kleur/colors'; import ora from 'ora'; -import os from 'os'; import path from 'path'; import prompts from 'prompts'; import detectPackageManager from 'which-pm-runs'; @@ -13,15 +14,8 @@ import yargs from 'yargs-parser'; import { loadWithRocketGradient, rocketAscii } from './gradient.js'; import { defaultLogLevel, logger } from './logger.js'; import { TEMPLATES } from './templates.js'; +import { getName, getVersion, welcome, banner, typescriptByDefault, nextSteps, info } from './messages.js'; -function wait(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -function logAndWait(message: string, ms = 100) { - console.log(message); - return wait(ms); -} // NOTE: In the v7.x version of npm, the default behavior of `npm init` was changed // to no longer require `--` to pass args and instead pass `--` directly to us. This // broke our arg parser, since `--` is a special kind of flag. Filtering for `--` here @@ -39,10 +33,6 @@ export function mkdirp(dir: string) { } } -function isEmpty(dirPath: string) { - return !fs.existsSync(dirPath) || fs.readdirSync(dirPath).length === 0; -} - // Some existing files and directories can be safely ignored when checking if a directory is a valid project directory. // https://github.com/facebook/create-react-app/blob/d960b9e38c062584ff6cfb1a70e1512509a966e7/packages/create-react-app/createReactApp.js#L907-L934 const VALID_PROJECT_DIRECTORY_SAFE_LIST = [ @@ -83,19 +73,21 @@ function isValidProjectDirectory(dirPath: string) { return conflicts.length === 0; } -const { version } = JSON.parse( - fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8') -); - const FILES_TO_REMOVE = ['.stackblitzrc', 'sandbox.config.json', 'CHANGELOG.md']; // some files are only needed for online editors when using astro.new. Remove for create-astro installs. // Please also update the installation instructions in the docs at https://github.com/withastro/docs/blob/main/src/pages/en/install/auto.md if you make any changes to the flow or wording here. export async function main() { const pkgManager = detectPackageManager()?.name || 'npm'; + const [username, version] = await Promise.all([getName(), getVersion()]); logger.debug('Verbose logging turned on'); - console.log(`\n${bold('Welcome to Astro!')} ${gray(`(create-astro v${version})`)}`); - console.log(`Lets walk through setting up your new Astro project.\n`); + if (!args.skipHouston) { + await say([ + ['Welcome', 'to', label('astro', color.bgGreen, color.black), color.green(`v${version}`) + ',', `${username}!`], + random(welcome), + ]); + await banner(version); + } let cwd = args['_'][2] as string; @@ -119,7 +111,7 @@ export async function main() { type: 'text', name: 'directory', message: 'Where would you like to create your new project?', - initial: './my-astro-site', + initial: `./${generateProjectName()}`, validate(value) { if (!isValidProjectDirectory(value)) { return notEmptyMsg(value); @@ -141,7 +133,7 @@ export async function main() { { type: 'select', name: 'template', - message: 'Which template would you like to use?', + message: 'How would you like to setup your new project?', choices: TEMPLATES, }, ], @@ -161,92 +153,22 @@ export async function main() { ? options.template : `withastro/astro/examples/${options.template}#latest`; - const emitter = degit(`${templateTarget}${hash}`, { - cache: false, - force: true, - verbose: defaultLogLevel === 'debug' ? true : false, - }); - - logger.debug('Initialized degit with following config:', `${templateTarget}${hash}`, { - cache: false, - force: true, - verbose: defaultLogLevel === 'debug' ? true : false, - }); - // Copy if (!args.dryRun) { try { - emitter.on('info', (info) => { - logger.debug(info.message); + await downloadTemplate(`${templateTarget}${hash}`, { + force: true, + provider: 'github', + cwd, + dir: '.', }); - await emitter.clone(cwd); - - // degit does not return an error when an invalid template is provided, as such we need to handle this manually - // It's not very pretty, but to the user eye, we just return a nice message and nothing weird happened - if (isValidProjectDirectory(cwd)) { - if (isEmpty(cwd)) { - fs.rmdirSync(cwd); - } - throw new Error(`Error: The provided template (${cyan(options.template)}) does not exist`); - } } catch (err: any) { - templateSpinner.fail(); - - // degit is compiled, so the stacktrace is pretty noisy. Only report the stacktrace when using verbose mode. - logger.debug(err); - console.error(red(err.message)); - - // Warning for issue #655 and other corrupted cache issue - if ( - err.message === 'zlib: unexpected end of file' || - err.message === 'TAR_BAD_ARCHIVE: Unrecognized archive format' - ) { - console.log( - yellow( - 'Local degit cache seems to be corrupted. For more information check out this issue: https://github.com/withastro/astro/issues/655. ' - ) - ); - const cacheIssueResponse = await prompts({ - type: 'confirm', - name: 'cache', - message: 'Would you like us to clear the cache and try again?', - initial: true, - }); - - if (cacheIssueResponse.cache) { - const homeDirectory = os.homedir(); - const cacheDir = path.join(homeDirectory, '.degit', 'github', 'withastro'); - - fs.rmSync(cacheDir, { recursive: true, force: true, maxRetries: 3 }); - - templateSpinner = await loadWithRocketGradient('Copying project files...'); - try { - await emitter.clone(cwd); - } catch (e: any) { - logger.debug(e); - console.error(red(e.message)); - } - } else { - console.log( - "Okay, no worries! To fix this manually, remove the folder '~/.degit/github/withastro' and rerun the command." - ); - } - } - - // Helpful message when encountering the "could not find commit hash for ..." error - if (err.code === 'MISSING_REF') { - console.log( - yellow( - "This seems to be an issue with degit. Please check if you have 'git' installed on your system, and install it if you don't have (https://git-scm.com)." - ) - ); - console.log( - yellow( - "If you do have 'git' installed, please run this command with the --verbose flag and file a new issue with the command output here: https://github.com/withastro/astro/issues" - ) - ); + fs.rmdirSync(cwd); + if (err.message.includes('404')) { + console.error(`Template ${color.underline(options.template)} does not exist!`); + } else { + console.error(err.message); } - process.exit(1); } @@ -303,7 +225,7 @@ export async function main() { installSpinner.text = green('Packages installed!'); installSpinner.succeed(); } else { - ora().info(dim(`No problem! Remember to install dependencies after setup.`)); + await info('No problem!', 'Remember to install dependencies after setup.') } const gitResponse = await prompts( @@ -329,7 +251,7 @@ export async function main() { await execaCommand('git init', { cwd }); ora().succeed('Git repository created!'); } else { - ora().info(dim(`Sounds good! You can come back and run ${cyan(`git init`)} later.`)); + await info('Sounds good!', `You can come back and run ${color.reset(`git init`)}${color.dim(' later.')}`) } const tsResponse = await prompts( @@ -338,25 +260,10 @@ export async function main() { name: 'typescript', message: 'How would you like to setup TypeScript?', choices: [ - { - title: 'Relaxed', - value: 'base', - }, - { - title: 'Strict (recommended)', - description: 'Enable `strict` typechecking rules', - value: 'strict', - }, - { - title: 'Strictest', - description: 'Enable all typechecking rules', - value: 'strictest', - }, - { - title: 'I prefer not to use TypeScript', - description: `That's cool too!`, - value: 'optout', - }, + { value: 'strict', title: 'Strict', description: '(recommended)' }, + { value: 'strictest', title: 'Strictest' }, + { value: 'base', title: 'Relaxed' }, + { value: 'unsure', title: 'Help me choose' }, ], }, { @@ -371,16 +278,9 @@ export async function main() { } ); - if (tsResponse.typescript === 'optout') { - console.log(``); - ora().warn(yellow(bold(`Astro ❤️ TypeScript!`))); - console.log(` Astro supports TypeScript inside of ".astro" component scripts, so`); - console.log(` we still need to create some TypeScript-related files in your project.`); - console.log(` You can safely ignore these files, but don't delete them!`); - console.log(dim(' (ex: tsconfig.json, src/env.d.ts)')); - console.log(``); + if (tsResponse.typescript === 'unsure') { + await typescriptByDefault(); tsResponse.typescript = 'base'; - await wait(300); } if (args.dryRun) { ora().info(dim(`--dry-run enabled, skipping.`)); @@ -415,34 +315,12 @@ export async function main() { }); ora().succeed('TypeScript settings applied!'); } - - ora().succeed('Setup complete.'); - ora({ text: green('Ready for liftoff!') }).succeed(); - await wait(300); - - console.log(`\n${bgCyan(black(' Next steps '))}\n`); - + let projectDir = path.relative(process.cwd(), cwd); const devCmd = pkgManager === 'npm' ? 'npm run dev' : `${pkgManager} dev`; + await nextSteps({ projectDir, devCmd }); - // If the project dir is the current dir, no need to tell users to cd in the folder - if (projectDir !== '/') { - await logAndWait( - `You can now ${bold(cyan('cd'))} into the ${bold(cyan(projectDir))} project directory.` - ); - } - await logAndWait( - `Run ${bold(cyan(devCmd))} to start the Astro dev server. ${bold(cyan('CTRL-C'))} to close.` - ); - await logAndWait( - `Add frameworks like ${bold(cyan('react'))} and ${bold( - cyan('tailwind') - )} to your project using ${bold(cyan('astro add'))}` - ); - await logAndWait(''); - await logAndWait(`Stuck? Come join us at ${bold(cyan('https://astro.build/chat'))}`, 750); - await logAndWait(dim('Good luck out there, astronaut.')); - await logAndWait('', 300); + await say(['Good luck out there, astronaut!']); } function emojiWithFallback(char: string, fallback: string) { |