[create-astro] Finalize developer experience... with gradients 🚀 (#3313)
* wip: port gradient helpers from sandbox ideas
* feat: wire up rocket gradient 🚀
* feat: wire up rocket gradient on install step
* refactor: update "next steps" wording
* deps: add chalk (for rendering gradient)
* chore: changeset
* chore: clean up sstray template string
This commit is contained in:
parent
f40705d906
commit
1a5335ed9a
5 changed files with 124 additions and 24 deletions
5
.changeset/strong-students-tie.md
Normal file
5
.changeset/strong-students-tie.md
Normal file
|
@ -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.
|
|
@ -30,6 +30,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/degit": "^2.8.3",
|
"@types/degit": "^2.8.3",
|
||||||
"@types/prompts": "^2.0.14",
|
"@types/prompts": "^2.0.14",
|
||||||
|
"chalk": "^5.0.1",
|
||||||
"degit": "^2.8.4",
|
"degit": "^2.8.4",
|
||||||
"execa": "^6.1.0",
|
"execa": "^6.1.0",
|
||||||
"kleur": "^4.1.4",
|
"kleur": "^4.1.4",
|
||||||
|
|
91
packages/create-astro/src/gradient.ts
Normal file
91
packages/create-astro/src/gradient.ts
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import ora from 'ora';
|
||||||
import { TEMPLATES } from './templates.js';
|
import { TEMPLATES } from './templates.js';
|
||||||
import { logger, defaultLogLevel } from './logger.js';
|
import { logger, defaultLogLevel } from './logger.js';
|
||||||
import { execa, execaCommand } from 'execa';
|
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
|
// 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
|
// to no longer require `--` to pass args and instead pass `--` directly to us. This
|
||||||
|
@ -42,10 +43,6 @@ export async function main() {
|
||||||
logger.debug('Verbose logging turned on');
|
logger.debug('Verbose logging turned on');
|
||||||
console.log(`\n${bold('Welcome to Astro!')} ${gray(`(create-astro v${version})`)}`);
|
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;
|
let cwd = args['_'][2] as string;
|
||||||
|
|
||||||
if (cwd && isEmpty(cwd)) {
|
if (cwd && isEmpty(cwd)) {
|
||||||
|
@ -95,6 +92,8 @@ export async function main() {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const templateSpinner = await loadWithRocketGradient('Copying project files...');
|
||||||
|
|
||||||
const hash = args.commit ? `#${args.commit}` : '';
|
const hash = args.commit ? `#${args.commit}` : '';
|
||||||
|
|
||||||
const templateTarget = `withastro/astro/examples/${options.template}#latest`;
|
const templateTarget = `withastro/astro/examples/${options.template}#latest`;
|
||||||
|
@ -111,8 +110,6 @@ export async function main() {
|
||||||
verbose: defaultLogLevel === 'debug' ? true : false,
|
verbose: defaultLogLevel === 'debug' ? true : false,
|
||||||
});
|
});
|
||||||
|
|
||||||
spinner = ora({ color: 'green', text: 'Copying project files...' }).start();
|
|
||||||
|
|
||||||
// Copy
|
// Copy
|
||||||
if (!args.dryrun) {
|
if (!args.dryrun) {
|
||||||
try {
|
try {
|
||||||
|
@ -152,7 +149,7 @@ export async function main() {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
spinner.fail();
|
templateSpinner.fail();
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,8 +164,8 @@ export async function main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
spinner.succeed();
|
templateSpinner.text = green('Template copied!');
|
||||||
console.log(bold(green('✔') + ' Done!'));
|
templateSpinner.succeed();
|
||||||
|
|
||||||
const installResponse = await prompts({
|
const installResponse = await prompts({
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
|
@ -184,15 +181,18 @@ export async function main() {
|
||||||
if (installResponse.install && !args.dryrun) {
|
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();
|
const installSpinner = await loadWithRocketGradient(installingPackagesMsg);
|
||||||
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}`;
|
installSpinner.text = `${rocketAscii} ${installingPackagesMsg}\n${bold(
|
||||||
|
`[${pkgManager}]`
|
||||||
|
)} ${data}`;
|
||||||
});
|
});
|
||||||
installExec.on('error', (error) => reject(error));
|
installExec.on('error', (error) => reject(error));
|
||||||
installExec.on('close', () => resolve());
|
installExec.on('close', () => resolve());
|
||||||
});
|
});
|
||||||
spinner.succeed();
|
installSpinner.text = green('Packages installed!');
|
||||||
|
installSpinner.succeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
const astroAddCommand = installResponse.install
|
const astroAddCommand = installResponse.install
|
||||||
|
@ -240,21 +240,22 @@ export async function main() {
|
||||||
await execaCommand('git init', { cwd });
|
await execaCommand('git init', { cwd });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ora({ text: green('Done. Ready for liftoff!') }).succeed();
|
||||||
console.log(`\n${bgCyan(black(' Next steps '))}\n`);
|
console.log(`\n${bgCyan(black(' Next steps '))}\n`);
|
||||||
|
|
||||||
const relative = path.relative(process.cwd(), cwd);
|
const projectDir = path.relative(process.cwd(), cwd);
|
||||||
const startCommand = [];
|
const devCmd = pkgManager === 'npm' ? 'npm run dev' : `${pkgManager} dev`;
|
||||||
if (relative !== '') {
|
|
||||||
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(
|
||||||
console.log(`Stuck? Visit us at ${cyan('https://astro.build/chat')}\n`);
|
`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) {
|
||||||
|
console.log(yellow(`Remember to install dependencies first!`));
|
||||||
|
}
|
||||||
|
console.log(`\nStuck? Come join us at ${bold(cyan('https://astro.build/chat'))}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function emojiWithFallback(char: string, fallback: string) {
|
function emojiWithFallback(char: string, fallback: string) {
|
||||||
|
|
|
@ -1256,6 +1256,7 @@ importers:
|
||||||
'@types/yargs-parser': ^21.0.0
|
'@types/yargs-parser': ^21.0.0
|
||||||
astro-scripts: workspace:*
|
astro-scripts: workspace:*
|
||||||
chai: ^4.3.6
|
chai: ^4.3.6
|
||||||
|
chalk: ^5.0.1
|
||||||
degit: ^2.8.4
|
degit: ^2.8.4
|
||||||
execa: ^6.1.0
|
execa: ^6.1.0
|
||||||
kleur: ^4.1.4
|
kleur: ^4.1.4
|
||||||
|
@ -1267,6 +1268,7 @@ importers:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/degit': 2.8.3
|
'@types/degit': 2.8.3
|
||||||
'@types/prompts': 2.0.14
|
'@types/prompts': 2.0.14
|
||||||
|
chalk: 5.0.1
|
||||||
degit: 2.8.4
|
degit: 2.8.4
|
||||||
execa: 6.1.0
|
execa: 6.1.0
|
||||||
kleur: 4.1.4
|
kleur: 4.1.4
|
||||||
|
|
Loading…
Reference in a new issue