Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[create-astro] Finalize developer experience... with gradients 🚀 #3313

Merged
merged 7 commits into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/strong-students-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'create-astro': patch
---

Update "next steps" with more informative text on each CLI command. Oh, and gradients. A lot more gradients.
1 change: 1 addition & 0 deletions packages/create-astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"dependencies": {
"@types/degit": "^2.8.3",
"@types/prompts": "^2.0.14",
"chalk": "^5.0.1",
Copy link
Contributor Author

@bholmesdev bholmesdev May 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used to render characters by custom hex values for the gradient. This should only add 1-2 KB to the final install size, which was much better than other "gradient generators" on npm. We can also swap out kleur for chalk if we feel really strongly about optimization (kept kleur for consistency with Astro core though)

"degit": "^2.8.4",
"execa": "^6.1.0",
"kleur": "^4.1.4",
Expand Down
91 changes: 91 additions & 0 deletions packages/create-astro/src/gradient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import chalk from 'chalk';
import ora from 'ora';
import type { Ora } from 'ora';

const gradientColors = [
`#ff5e00`,
`#ff4c29`,
`#ff383f`,
`#ff2453`,
`#ff0565`,
`#ff007b`,
`#f5008b`,
`#e6149c`,
`#d629ae`,
`#c238bd`,
];

export const rocketAscii = '■■▶';

// get a reference to scroll through while loading
// visual representation of what this generates:
// gradientColors: "..xxXX"
// referenceGradient: "..xxXXXXxx....xxXX"
const referenceGradient = [
...gradientColors,
// draw the reverse of the gradient without
// accidentally mutating the gradient (ugh, reverse())
...[...gradientColors].reverse(),
...gradientColors,
];

// async-friendly setTimeout
const sleep = (time: number) =>
new Promise((resolve) => {
setTimeout(resolve, time);
});

function getGradientAnimFrames() {
const frames = [];
for (let start = 0; start < gradientColors.length * 2; start++) {
const end = start + gradientColors.length - 1;
frames.push(
referenceGradient
.slice(start, end)
.map((g) => chalk.bgHex(g)(' '))
.join('')
);
}
return frames;
}

function getIntroAnimFrames() {
const frames = [];
for (let end = 1; end <= gradientColors.length; end++) {
const leadingSpacesArr = Array.from(
new Array(Math.abs(gradientColors.length - end - 1)),
() => ' '
);
const gradientArr = gradientColors.slice(0, end).map((g) => chalk.bgHex(g)(' '));
frames.push([...leadingSpacesArr, ...gradientArr].join(''));
}
return frames;
}

/**
* Generate loading spinner with rocket flames!
* @param text display text next to rocket
* @returns Ora spinner for running .stop()
*/
export async function loadWithRocketGradient(text: string): Promise<Ora> {
const frames = getIntroAnimFrames();
const intro = ora({
spinner: {
interval: 30,
frames,
},
text: `${rocketAscii} ${text}`,
});
intro.start();
await sleep((frames.length - 1) * intro.interval);
intro.stop();
const spinner = ora({
spinner: {
interval: 80,
frames: getGradientAnimFrames(),
},
text: `${rocketAscii} ${text}`,
}).start();

return spinner;
}
47 changes: 24 additions & 23 deletions packages/create-astro/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ora from 'ora';
import { TEMPLATES } from './templates.js';
import { logger, defaultLogLevel } from './logger.js';
import { execa, execaCommand } from 'execa';
import { loadWithRocketGradient, rocketAscii } from './gradient.js';

// 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
Expand Down Expand Up @@ -42,10 +43,6 @@ export async function main() {
logger.debug('Verbose logging turned on');
console.log(`\n${bold('Welcome to Astro!')} ${gray(`(create-astro v${version})`)}`);

let spinner = ora({ color: 'green', text: 'Prepare for liftoff.' });

spinner.succeed();

let cwd = args['_'][2] as string;

if (cwd && isEmpty(cwd)) {
Expand Down Expand Up @@ -95,6 +92,8 @@ export async function main() {
process.exit(1);
}

const templateSpinner = await loadWithRocketGradient('Copying project files...');

const hash = args.commit ? `#${args.commit}` : '';

const templateTarget = `withastro/astro/examples/${options.template}#latest`;
Expand All @@ -111,8 +110,6 @@ export async function main() {
verbose: defaultLogLevel === 'debug' ? true : false,
});

spinner = ora({ color: 'green', text: 'Copying project files...' }).start();

// Copy
if (!args.dryrun) {
try {
Expand Down Expand Up @@ -152,7 +149,7 @@ export async function main() {
)
);
}
spinner.fail();
templateSpinner.fail();
process.exit(1);
}

Expand All @@ -167,8 +164,8 @@ export async function main() {
);
}

spinner.succeed();
console.log(bold(green('✔') + ' Done!'));
templateSpinner.text = green('Template copied!');
templateSpinner.succeed();

const installResponse = await prompts({
type: 'confirm',
Expand All @@ -184,15 +181,18 @@ export async function main() {
if (installResponse.install && !args.dryrun) {
const installExec = execa(pkgManager, ['install'], { cwd });
const installingPackagesMsg = `Installing packages${emojiWithFallback(' 📦', '...')}`;
spinner = ora({ color: 'green', text: installingPackagesMsg }).start();
const installSpinner = await loadWithRocketGradient(installingPackagesMsg);
await new Promise<void>((resolve, reject) => {
installExec.stdout?.on('data', function (data) {
spinner.text = `${installingPackagesMsg}\n${bold(`[${pkgManager}]`)} ${data}`;
installSpinner.text = `${rocketAscii} ${installingPackagesMsg}\n${bold(
`[${pkgManager}]`
)} ${data}`;
});
installExec.on('error', (error) => reject(error));
installExec.on('close', () => resolve());
});
spinner.succeed();
installSpinner.text = green('Packages installed!');
installSpinner.succeed();
}

const astroAddCommand = installResponse.install
Expand Down Expand Up @@ -240,21 +240,22 @@ export async function main() {
await execaCommand('git init', { cwd });
}

ora({ text: green('Done. Ready for liftoff!') }).succeed();
console.log(`\n${bgCyan(black(' Next steps '))}\n`);

const relative = path.relative(process.cwd(), cwd);
const startCommand = [];
if (relative !== '') {
startCommand.push(bold(cyan(`cd ${relative}`)));
}
const projectDir = path.relative(process.cwd(), cwd);
const devCmd = pkgManager === 'npm' ? 'npm run dev' : `${pkgManager} dev`;

console.log(
`You can now ${bold(cyan('cd'))} into the ${bold(cyan(projectDir))} project directory.`
);
console.log(
`Run ${bold(cyan(devCmd))} to start the Astro dev server. ${bold(cyan('CTRL-C'))} to close.`
);
if (!installResponse.install) {
startCommand.push(bold(cyan(`${pkgManager} install`)));
console.log(yellow(`Remember to ${'install dependencies'} first!`));
}
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(`Stuck? Visit us at ${cyan('https://astro.build/chat')}\n`);
console.log(`\nStuck? Come join us at ${bold(cyan('https://astro.build/chat'))}`);
}

function emojiWithFallback(char: string, fallback: string) {
Expand Down
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.