Feat: [create astro] git step (#3227)

* feat: add git init step

* fix: update unit tests

* feat: simplify next steps for copy pasteability

* docs: add clarifying comment on test stdin spoofing

* docs: remove "empty" from git repo message

* fix: update git step text for test

* fix: remove redundant --dryrun flag

* refactor: simplify next steps with &&

* chore: changeset
This commit is contained in:
Ben Holmes 2022-04-29 11:45:43 -04:00 committed by GitHub
parent 6cd9ef577e
commit c8f5fa35c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 30 deletions

View file

@ -0,0 +1,5 @@
---
'create-astro': minor
---
Add "initialize git repository" step to simplify our next steps suggestion. We now give you a one-liner to easily paste in your terminal and start the dev server!

View file

@ -1,6 +1,6 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { bold, cyan, gray, green, red, yellow } from 'kleur/colors'; import { bgCyan, black, bold, cyan, gray, green, red, yellow } from 'kleur/colors';
import prompts from 'prompts'; import prompts from 'prompts';
import degit from 'degit'; import degit from 'degit';
import yargs from 'yargs-parser'; import yargs from 'yargs-parser';
@ -181,19 +181,17 @@ export async function main() {
process.exit(0); process.exit(0);
} }
if (installResponse.install) { if (installResponse.install && !args.dryrun) {
const installExec = execa(pkgManager, ['install'], { cwd }); const installExec = execa(pkgManager, ['install'], { cwd });
const installingPackagesMsg = `Installing packages${emojiWithFallback(' 📦', '...')}`; const installingPackagesMsg = `Installing packages${emojiWithFallback(' 📦', '...')}`;
spinner = ora({ color: 'green', text: installingPackagesMsg }).start(); spinner = ora({ color: 'green', text: installingPackagesMsg }).start();
if (!args.dryrun) { await new Promise<void>((resolve, reject) => {
await new Promise<void>((resolve, reject) => { installExec.stdout?.on('data', function (data) {
installExec.stdout?.on('data', function (data) { spinner.text = `${installingPackagesMsg}\n${bold(`[${pkgManager}]`)} ${data}`;
spinner.text = `${installingPackagesMsg}\n${bold(`[${pkgManager}]`)} ${data}`;
});
installExec.on('error', (error) => reject(error));
installExec.on('close', () => resolve());
}); });
} installExec.on('error', (error) => reject(error));
installExec.on('close', () => resolve());
});
spinner.succeed(); spinner.succeed();
} }
@ -227,26 +225,36 @@ export async function main() {
); );
} }
console.log('\nNext steps:'); const gitResponse = await prompts({
let i = 1; type: 'confirm',
const relative = path.relative(process.cwd(), cwd); name: 'git',
if (relative !== '') { message: 'Initialize a git repository?',
console.log(` ${i++}: ${bold(cyan(`cd ${relative}`))}`); initial: true,
});
if (!gitResponse) {
process.exit(0);
} }
if (!installResponse.install) { if (gitResponse.git && !args.dryrun) {
console.log(` ${i++}: ${bold(cyan(`${pkgManager} install`))}`); await execaCommand('git init', { cwd });
} }
console.log(
` ${i++}: ${bold( console.log(`\n${bgCyan(black(' Next steps '))}\n`);
cyan('git init && git add -A && git commit -m "Initial commit"')
)} (optional step)` const relative = path.relative(process.cwd(), cwd);
); const startCommand = [];
const runCommand = pkgManager === 'npm' ? 'npm run dev' : `${pkgManager} dev`; if (relative !== '') {
console.log(` ${i++}: ${bold(cyan(runCommand))}`); startCommand.push(bold(cyan(`cd ${relative}`)));
}
if (!installResponse.install) {
startCommand.push(bold(cyan(`${pkgManager} install`)));
}
startCommand.push(bold(cyan(pkgManager === 'npm' ? 'npm run dev' : `${pkgManager} dev`)));
console.log(startCommand.join(' && '));
console.log(`\nTo close the dev server, hit ${bold(cyan('Ctrl-C'))}`); console.log(`\nTo close the dev server, hit ${bold(cyan('Ctrl-C'))}`);
console.log(`\nStuck? Visit us at ${cyan('https://astro.build/chat')}\n`); console.log(`Stuck? Visit us at ${cyan('https://astro.build/chat')}\n`);
} }
function emojiWithFallback(char: string, fallback: string) { function emojiWithFallback(char: string, fallback: string) {

View file

@ -23,13 +23,14 @@ describe('[create-astro] astro add', function () {
}); });
it('should use "astro add" when user has installed dependencies', function () { it('should use "astro add" when user has installed dependencies', function () {
const { stdout, stdin } = setup([tempDir, '--dryrun']); const { stdout, stdin } = setup([tempDir]);
return promiseWithTimeout((resolve) => { return promiseWithTimeout((resolve) => {
const seen = new Set(); const seen = new Set();
const installPrompt = PROMPT_MESSAGES.install('npm'); const installPrompt = PROMPT_MESSAGES.install('npm');
stdout.on('data', (chunk) => { stdout.on('data', (chunk) => {
if (!seen.has(PROMPT_MESSAGES.template) && chunk.includes(PROMPT_MESSAGES.template)) { if (!seen.has(PROMPT_MESSAGES.template) && chunk.includes(PROMPT_MESSAGES.template)) {
seen.add(PROMPT_MESSAGES.template); seen.add(PROMPT_MESSAGES.template);
// respond with "enter key"
stdin.write('\x0D'); stdin.write('\x0D');
} }
if (!seen.has(installPrompt) && chunk.includes(installPrompt)) { if (!seen.has(installPrompt) && chunk.includes(installPrompt)) {
@ -44,17 +45,19 @@ describe('[create-astro] astro add', function () {
}); });
it('should use "npx astro@latest add" when use has NOT installed dependencies', function () { it('should use "npx astro@latest add" when use has NOT installed dependencies', function () {
const { stdout, stdin } = setup([tempDir, '--dryrun']); const { stdout, stdin } = setup([tempDir]);
return promiseWithTimeout((resolve) => { return promiseWithTimeout((resolve) => {
const seen = new Set(); const seen = new Set();
const installPrompt = PROMPT_MESSAGES.install('npm'); const installPrompt = PROMPT_MESSAGES.install('npm');
stdout.on('data', (chunk) => { stdout.on('data', (chunk) => {
if (!seen.has(PROMPT_MESSAGES.template) && chunk.includes(PROMPT_MESSAGES.template)) { if (!seen.has(PROMPT_MESSAGES.template) && chunk.includes(PROMPT_MESSAGES.template)) {
seen.add(PROMPT_MESSAGES.template); seen.add(PROMPT_MESSAGES.template);
// respond with "enter key"
stdin.write('\x0D'); stdin.write('\x0D');
} }
if (!seen.has(installPrompt) && chunk.includes(installPrompt)) { if (!seen.has(installPrompt) && chunk.includes(installPrompt)) {
seen.add(installPrompt); seen.add(installPrompt);
// respond with "no, then enter key"
stdin.write('n\x0D'); stdin.write('n\x0D');
} }
if (chunk.includes(PROMPT_MESSAGES.astroAdd('npx astro@latest add --yes'))) { if (chunk.includes(PROMPT_MESSAGES.astroAdd('npx astro@latest add --yes'))) {

View file

@ -21,13 +21,14 @@ describe('[create-astro] install', function () {
}); });
it('should respect package manager in prompt', function () { it('should respect package manager in prompt', function () {
const { stdout, stdin } = setup([tempDir, '--dryrun']); const { stdout, stdin } = setup([tempDir]);
return promiseWithTimeout((resolve) => { return promiseWithTimeout((resolve) => {
const seen = new Set(); const seen = new Set();
const installPrompt = PROMPT_MESSAGES.install(FAKE_PACKAGE_MANAGER); const installPrompt = PROMPT_MESSAGES.install(FAKE_PACKAGE_MANAGER);
stdout.on('data', (chunk) => { stdout.on('data', (chunk) => {
if (!seen.has(PROMPT_MESSAGES.template) && chunk.includes(PROMPT_MESSAGES.template)) { if (!seen.has(PROMPT_MESSAGES.template) && chunk.includes(PROMPT_MESSAGES.template)) {
seen.add(PROMPT_MESSAGES.template); seen.add(PROMPT_MESSAGES.template);
// respond with "enter key"
stdin.write('\x0D'); stdin.write('\x0D');
} }
if (!seen.has(installPrompt) && chunk.includes(installPrompt)) { if (!seen.has(installPrompt) && chunk.includes(installPrompt)) {
@ -39,7 +40,7 @@ describe('[create-astro] install', function () {
}); });
it('should respect package manager in next steps', function () { it('should respect package manager in next steps', function () {
const { stdout, stdin } = setup([tempDir, '--dryrun']); const { stdout, stdin } = setup([tempDir]);
return promiseWithTimeout((resolve) => { return promiseWithTimeout((resolve) => {
const seen = new Set(); const seen = new Set();
const installPrompt = PROMPT_MESSAGES.install(FAKE_PACKAGE_MANAGER); const installPrompt = PROMPT_MESSAGES.install(FAKE_PACKAGE_MANAGER);
@ -47,14 +48,20 @@ describe('[create-astro] install', function () {
stdout.on('data', (chunk) => { stdout.on('data', (chunk) => {
if (!seen.has(PROMPT_MESSAGES.template) && chunk.includes(PROMPT_MESSAGES.template)) { if (!seen.has(PROMPT_MESSAGES.template) && chunk.includes(PROMPT_MESSAGES.template)) {
seen.add(PROMPT_MESSAGES.template); seen.add(PROMPT_MESSAGES.template);
// respond with "enter key"
stdin.write('\x0D'); stdin.write('\x0D');
} }
if (!seen.has(installPrompt) && chunk.includes(installPrompt)) { if (!seen.has(installPrompt) && chunk.includes(installPrompt)) {
seen.add(installPrompt); seen.add(installPrompt);
// respond with "no, then enter key"
stdin.write('n\x0D'); stdin.write('n\x0D');
} }
if (!seen.has(astroAddPrompt) && chunk.includes(astroAddPrompt)) { if (!seen.has(astroAddPrompt) && chunk.includes(astroAddPrompt)) {
seen.add(astroAddPrompt); seen.add(astroAddPrompt);
stdin.write('n\x0D');
}
if (!seen.has(PROMPT_MESSAGES.git) && chunk.includes(PROMPT_MESSAGES.git)) {
seen.add(PROMPT_MESSAGES.git);
stdin.write('\x0D'); stdin.write('\x0D');
} }
if (chunk.includes('banana dev')) { if (chunk.includes('banana dev')) {

View file

@ -29,10 +29,11 @@ export const PROMPT_MESSAGES = {
install: (pkgManager) => `Would you like us to run "${pkgManager} install?"`, install: (pkgManager) => `Would you like us to run "${pkgManager} install?"`,
astroAdd: (astroAddCommand = 'npx astro@latest add --yes') => astroAdd: (astroAddCommand = 'npx astro@latest add --yes') =>
`Run "${astroAddCommand}?" This lets you optionally add component frameworks (ex. React), CSS frameworks (ex. Tailwind), and more.`, `Run "${astroAddCommand}?" This lets you optionally add component frameworks (ex. React), CSS frameworks (ex. Tailwind), and more.`,
git: 'Initialize a git repository?',
}; };
export function setup(args = []) { export function setup(args = []) {
const { stdout, stdin } = execa('../create-astro.mjs', args, { cwd: testDir }); const { stdout, stdin } = execa('../create-astro.mjs', [...args, '--dryrun'], { cwd: testDir });
return { return {
stdin, stdin,
stdout, stdout,