summaryrefslogtreecommitdiff
path: root/packages/create-astro/src/index.ts
diff options
context:
space:
mode:
authorGravatar Nate Moore <natemoo-re@users.noreply.github.com> 2022-10-26 10:13:56 -0500
committerGravatar GitHub <noreply@github.com> 2022-10-26 10:13:56 -0500
commit04083762810a1a9e078a7e68edab945c8063b1ab (patch)
tree784baa969ba4f3a68f3420dc909135359a58f3cd /packages/create-astro/src/index.ts
parentddf2f8390e8dcc64b44636524bdcddae977779f4 (diff)
downloadastro-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.ts194
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) {