SImplify "astro add" by removing confusing multi-select (#3715)
* wip * update create-astro for new astro add * update copy * update git prompt * Update packages/astro/src/core/logger/node.ts Co-authored-by: Chris Swithinbank <swithinbank@gmail.com> * Update packages/create-astro/test/install-step.test.js Co-authored-by: Chris Swithinbank <swithinbank@gmail.com> * update git prompt * update test Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>
This commit is contained in:
parent
bd4dac0e1a
commit
4d6d8644e6
13 changed files with 157 additions and 258 deletions
5
.changeset/lucky-apes-yell.md
Normal file
5
.changeset/lucky-apes-yell.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Update "astro add" output to remove confusing multi-select prompt.
|
5
.changeset/perfect-drinks-fold.md
Normal file
5
.changeset/perfect-drinks-fold.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Update the help output to improve formatting
|
|
@ -37,26 +37,27 @@ type CLICommand =
|
||||||
function printAstroHelp() {
|
function printAstroHelp() {
|
||||||
printHelp({
|
printHelp({
|
||||||
commandName: 'astro',
|
commandName: 'astro',
|
||||||
|
usage: '[command] [...flags]',
|
||||||
headline: 'Futuristic web development tool.',
|
headline: 'Futuristic web development tool.',
|
||||||
commands: [
|
tables: {
|
||||||
['add', 'Add an integration to your configuration.'],
|
Commands: [
|
||||||
['docs', "Launch Astro's Doc site directly from the terminal. "],
|
['add', 'Add an integration.'],
|
||||||
['dev', 'Run Astro in development mode.'],
|
['build', 'Build your project and write it to disk.'],
|
||||||
['build', 'Build a pre-compiled production-ready site.'],
|
['check', 'Check your project for errors.'],
|
||||||
['preview', 'Preview your build locally before deploying.'],
|
['dev', 'Start the development server.'],
|
||||||
['check', 'Check your project for errors.'],
|
['docs', "Open documentation in your web browser."],
|
||||||
['telemetry', 'Enable/disable anonymous data collection.'],
|
['preview', 'Preview your build locally.'],
|
||||||
['--version', 'Show the version number and exit.'],
|
['telemetry', 'Configure telemetry settings.'],
|
||||||
['--help', 'Show this help message.'],
|
],
|
||||||
],
|
'Global Flags': [
|
||||||
flags: [
|
['--config <path>', 'Specify your config file.'],
|
||||||
['--host [optional IP]', 'Expose server on network'],
|
['--root <path>', 'Specify your project root folder.'],
|
||||||
['--config <path>', 'Specify the path to the Astro config file.'],
|
['--verbose', 'Enable verbose logging.'],
|
||||||
['--root <path>', 'Specify the path to the project root folder.'],
|
['--silent', 'Disable all logging.'],
|
||||||
['--drafts', 'Include markdown draft pages in the build.'],
|
['--version', 'Show the version number and exit.'],
|
||||||
['--verbose', 'Enable verbose logging'],
|
['--help', 'Show this help message.'],
|
||||||
['--silent', 'Disable logging'],
|
],
|
||||||
],
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,14 @@ export async function update(subcommand: string, { flags, telemetry }: Telemetry
|
||||||
if (flags.help || !isValid) {
|
if (flags.help || !isValid) {
|
||||||
msg.printHelp({
|
msg.printHelp({
|
||||||
commandName: 'astro telemetry',
|
commandName: 'astro telemetry',
|
||||||
usage: '<enable|disable|reset>',
|
usage: '[command]',
|
||||||
commands: [
|
tables: {
|
||||||
['enable', 'Enable anonymous data collection.'],
|
Commands: [
|
||||||
['disable', 'Disable anonymous data collection.'],
|
['enable', 'Enable anonymous data collection.'],
|
||||||
['reset', 'Reset anonymous data collection settings.'],
|
['disable', 'Disable anonymous data collection.'],
|
||||||
],
|
['reset', 'Reset anonymous data collection settings.'],
|
||||||
|
],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
export const FIRST_PARTY_FRAMEWORKS = [
|
|
||||||
{ value: 'react', title: 'React' },
|
|
||||||
{ value: 'preact', title: 'Preact' },
|
|
||||||
{ value: 'vue', title: 'Vue' },
|
|
||||||
{ value: 'svelte', title: 'Svelte' },
|
|
||||||
{ value: 'solid-js', title: 'Solid' },
|
|
||||||
{ value: 'lit', title: 'Lit' },
|
|
||||||
];
|
|
||||||
export const FIRST_PARTY_ADDONS = [
|
|
||||||
{ value: 'tailwind', title: 'Tailwind' },
|
|
||||||
{ value: 'turbolinks', title: 'Turbolinks' },
|
|
||||||
{ value: 'partytown', title: 'Partytown' },
|
|
||||||
{ value: 'sitemap', title: 'Sitemap' },
|
|
||||||
];
|
|
||||||
export const ALIASES = new Map([
|
|
||||||
['solid', 'solid-js'],
|
|
||||||
['tailwindcss', 'tailwind'],
|
|
||||||
]);
|
|
||||||
export const CONFIG_STUB = `import { defineConfig } from 'astro/config';\n\nexport default defineConfig({});`;
|
|
||||||
export const TAILWIND_CONFIG_STUB = `/** @type {import('tailwindcss').Config} */
|
|
||||||
module.exports = {
|
|
||||||
content: ['./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}'],
|
|
||||||
theme: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [],
|
|
||||||
}\n`;
|
|
|
@ -11,14 +11,13 @@ import prompts from 'prompts';
|
||||||
import { fileURLToPath, pathToFileURL } from 'url';
|
import { fileURLToPath, pathToFileURL } from 'url';
|
||||||
import type yargs from 'yargs-parser';
|
import type yargs from 'yargs-parser';
|
||||||
import { resolveConfigURL } from '../config.js';
|
import { resolveConfigURL } from '../config.js';
|
||||||
import { debug, error, info, LogOptions } from '../logger/core.js';
|
import { debug, info, LogOptions } from '../logger/core.js';
|
||||||
import * as msg from '../messages.js';
|
import * as msg from '../messages.js';
|
||||||
import { printHelp } from '../messages.js';
|
import { printHelp } from '../messages.js';
|
||||||
import { appendForwardSlash } from '../path.js';
|
import { appendForwardSlash } from '../path.js';
|
||||||
import { apply as applyPolyfill } from '../polyfill.js';
|
import { apply as applyPolyfill } from '../polyfill.js';
|
||||||
import { parseNpmName } from '../util.js';
|
import { parseNpmName } from '../util.js';
|
||||||
import { generate, parse, t, visit } from './babel.js';
|
import { generate, parse, t, visit } from './babel.js';
|
||||||
import * as CONSTS from './consts.js';
|
|
||||||
import { ensureImport } from './imports.js';
|
import { ensureImport } from './imports.js';
|
||||||
import { wrapDefaultExport } from './wrapper.js';
|
import { wrapDefaultExport } from './wrapper.js';
|
||||||
|
|
||||||
|
@ -34,71 +33,71 @@ export interface IntegrationInfo {
|
||||||
packageName: string;
|
packageName: string;
|
||||||
dependencies: [name: string, version: string][];
|
dependencies: [name: string, version: string][];
|
||||||
}
|
}
|
||||||
|
const ALIASES = new Map([
|
||||||
|
['solid', 'solid-js'],
|
||||||
|
['tailwindcss', 'tailwind'],
|
||||||
|
]);
|
||||||
|
const ASTRO_CONFIG_STUB = `import { defineConfig } from 'astro/config';\n\ndefault defineConfig({});`;
|
||||||
|
const TAILWIND_CONFIG_STUB = `/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ['./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}'],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}\n`;
|
||||||
|
|
||||||
export default async function add(names: string[], { cwd, flags, logging, telemetry }: AddOptions) {
|
export default async function add(names: string[], { cwd, flags, logging, telemetry }: AddOptions) {
|
||||||
if (flags.help) {
|
if (flags.help || names.length === 0) {
|
||||||
printHelp({
|
printHelp({
|
||||||
commandName: 'astro add',
|
commandName: 'astro add',
|
||||||
usage: '[FLAGS] [INTEGRATIONS...]',
|
usage: '[...integrations]',
|
||||||
flags: [
|
tables: {
|
||||||
['--yes', 'Add the integration without user interaction.'],
|
Flags: [
|
||||||
['--help', 'Show this help message.'],
|
['--yes', 'Accept all prompts.'],
|
||||||
],
|
['--help', 'Show this help message.'],
|
||||||
|
],
|
||||||
|
'Example: Add a UI Framework': [
|
||||||
|
['react', 'astro add react'],
|
||||||
|
['preact', 'astro add preact'],
|
||||||
|
['vue', 'astro add vue'],
|
||||||
|
['svelte', 'astro add svelte'],
|
||||||
|
['solid-js', 'astro add solid-js'],
|
||||||
|
['lit', 'astro add lit'],
|
||||||
|
],
|
||||||
|
'Example: Add an Integration': [
|
||||||
|
['tailwind', 'astro add tailwind'],
|
||||||
|
['partytown', 'astro add partytown'],
|
||||||
|
['sitemap', 'astro add sitemap'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
description: `Check out the full integration catalog: ${cyan('https://astro.build/integrations')}`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let configURL: URL | undefined;
|
let configURL: URL | undefined;
|
||||||
const root = pathToFileURL(cwd ? path.resolve(cwd) : process.cwd());
|
const root = pathToFileURL(cwd ? path.resolve(cwd) : process.cwd());
|
||||||
// TODO: improve error handling for invalid configs
|
|
||||||
configURL = await resolveConfigURL({ cwd, flags });
|
configURL = await resolveConfigURL({ cwd, flags });
|
||||||
|
|
||||||
if (configURL?.pathname.endsWith('package.json')) {
|
|
||||||
throw new Error(
|
|
||||||
`Unable to use astro add with package.json#astro configuration! Try migrating to \`astro.config.mjs\` and try again.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
applyPolyfill();
|
applyPolyfill();
|
||||||
|
|
||||||
// If no integrations were given, prompt the user for some popular ones.
|
|
||||||
if (names.length === 0) {
|
|
||||||
const response = await prompts([
|
|
||||||
{
|
|
||||||
type: 'multiselect',
|
|
||||||
name: 'frameworks',
|
|
||||||
message: 'What frameworks would you like to enable?',
|
|
||||||
instructions: '\n Space to select. Return to submit',
|
|
||||||
choices: CONSTS.FIRST_PARTY_FRAMEWORKS,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'multiselect',
|
|
||||||
name: 'addons',
|
|
||||||
message: 'What additional integrations would you like to enable?',
|
|
||||||
instructions: '\n Space to select. Return to submit',
|
|
||||||
choices: CONSTS.FIRST_PARTY_ADDONS,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
names = [...(response.frameworks ?? []), ...(response.addons ?? [])];
|
|
||||||
}
|
|
||||||
|
|
||||||
// If still empty after prompting, exit gracefully.
|
|
||||||
if (names.length === 0) {
|
|
||||||
error(logging, null, `No integrations specified.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some packages might have a common alias! We normalize those here.
|
|
||||||
names = names.map((name) => (CONSTS.ALIASES.has(name) ? CONSTS.ALIASES.get(name)! : name));
|
|
||||||
|
|
||||||
if (configURL) {
|
if (configURL) {
|
||||||
debug('add', `Found config at ${configURL}`);
|
debug('add', `Found config at ${configURL}`);
|
||||||
} else {
|
} else {
|
||||||
info(logging, 'add', `Unable to locate a config file, generating one for you.`);
|
info(logging, 'add', `Unable to locate a config file, generating one for you.`);
|
||||||
configURL = new URL('./astro.config.mjs', appendForwardSlash(root.href));
|
configURL = new URL('./astro.config.mjs', appendForwardSlash(root.href));
|
||||||
await fs.writeFile(fileURLToPath(configURL), CONSTS.CONFIG_STUB, { encoding: 'utf-8' });
|
await fs.writeFile(fileURLToPath(configURL), ASTRO_CONFIG_STUB, { encoding: 'utf-8' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const integrations = await validateIntegrations(names);
|
// TODO: improve error handling for invalid configs
|
||||||
|
if (configURL?.pathname.endsWith('package.json')) {
|
||||||
|
throw new Error(
|
||||||
|
`Unable to use "astro add" with package.json configuration. Try migrating to \`astro.config.mjs\` and try again.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some packages might have a common alias! We normalize those here.
|
||||||
|
const integrationNames = names.map((name) => (ALIASES.has(name) ? ALIASES.get(name)! : name));
|
||||||
|
const integrations = await validateIntegrations(integrationNames);
|
||||||
|
|
||||||
let ast: t.File | null = null;
|
let ast: t.File | null = null;
|
||||||
try {
|
try {
|
||||||
|
@ -194,7 +193,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme
|
||||||
if (await askToContinue({ flags })) {
|
if (await askToContinue({ flags })) {
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
fileURLToPath(new URL('./tailwind.config.cjs', configURL)),
|
fileURLToPath(new URL('./tailwind.config.cjs', configURL)),
|
||||||
CONSTS.TAILWIND_CONFIG_STUB,
|
TAILWIND_CONFIG_STUB,
|
||||||
{ encoding: 'utf-8' }
|
{ encoding: 'utf-8' }
|
||||||
);
|
);
|
||||||
debug('add', `Generated default ./tailwind.config.cjs file`);
|
debug('add', `Generated default ./tailwind.config.cjs file`);
|
||||||
|
|
|
@ -248,29 +248,28 @@ export function printHelp({
|
||||||
commandName,
|
commandName,
|
||||||
headline,
|
headline,
|
||||||
usage,
|
usage,
|
||||||
commands,
|
tables,
|
||||||
flags,
|
description,
|
||||||
}: {
|
}: {
|
||||||
commandName: string;
|
commandName: string;
|
||||||
headline?: string;
|
headline?: string;
|
||||||
usage?: string;
|
usage?: string;
|
||||||
commands?: [command: string, help: string][];
|
tables?: Record<string, [command: string, help: string][]>;
|
||||||
flags?: [flag: string, help: string][];
|
description?: string,
|
||||||
}) {
|
}) {
|
||||||
const linebreak = () => '';
|
const linebreak = () => '';
|
||||||
const title = (label: string) => ` ${bgWhite(black(` ${label} `))}`;
|
const title = (label: string) => ` ${bgWhite(black(` ${label} `))}`;
|
||||||
const table = (rows: [string, string][], opts: { padding: number; prefix: string }) => {
|
const table = (rows: [string, string][], { padding }: { padding: number }) => {
|
||||||
const split = rows.some((row) => {
|
const split = process.stdout.columns < 60;
|
||||||
const message = `${opts.prefix}${' '.repeat(opts.padding)}${row[1]}`;
|
|
||||||
return message.length > process.stdout.columns;
|
|
||||||
});
|
|
||||||
|
|
||||||
let raw = '';
|
let raw = '';
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
raw += `${opts.prefix}${bold(`${row[0]}`.padStart(opts.padding - opts.prefix.length))}`;
|
if (split) {
|
||||||
if (split) raw += '\n ';
|
raw += ` ${row[0]}\n `;
|
||||||
raw += ' ' + dim(row[1]) + '\n';
|
} else {
|
||||||
|
raw += `${(`${row[0]}`.padStart(padding))}`;
|
||||||
|
}
|
||||||
|
raw += ' ' + dim(row[1]) + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
return raw.slice(0, -1); // remove latest \n
|
return raw.slice(0, -1); // remove latest \n
|
||||||
|
@ -291,18 +290,21 @@ export function printHelp({
|
||||||
message.push(linebreak(), ` ${green(commandName)} ${bold(usage)}`);
|
message.push(linebreak(), ` ${green(commandName)} ${bold(usage)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commands) {
|
if (tables) {
|
||||||
message.push(
|
function calculateTablePadding(rows: [string, string][]) {
|
||||||
linebreak(),
|
return rows.reduce((val, [first]) => Math.max(val, first.length), 0) + 2;
|
||||||
title('Commands'),
|
};
|
||||||
table(commands, { padding: 28, prefix: ` ${commandName || 'astro'} ` })
|
const tableEntries = Object.entries(tables);
|
||||||
);
|
const padding = Math.max(...tableEntries.map(([, rows]) => calculateTablePadding(rows)));
|
||||||
|
for (const [tableTitle, tableRows] of tableEntries) {
|
||||||
|
message.push(linebreak(), title(tableTitle), table(tableRows, { padding }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags) {
|
if (description) {
|
||||||
message.push(linebreak(), title('Flags'), table(flags, { padding: 28, prefix: ' ' }));
|
message.push(linebreak(), `${description}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(message.join('\n'));
|
console.log(message.join('\n') + '\n');
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
const currentVersion = process.versions.node;
|
const currentVersion = process.versions.node;
|
||||||
const requiredMajorVersion = parseInt(currentVersion.split('.')[0], 10);
|
const requiredMajorVersion = parseInt(currentVersion.split('.')[0], 10);
|
||||||
const minimumMajorVersion = 12;
|
const minimumMajorVersion = 14;
|
||||||
|
|
||||||
if (requiredMajorVersion < minimumMajorVersion) {
|
if (requiredMajorVersion < minimumMajorVersion) {
|
||||||
console.error(`Node.js v${currentVersion} is out of date and unsupported!`);
|
console.error(`Node.js v${currentVersion} is out of date and unsupported!`);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import degit from 'degit';
|
import degit from 'degit';
|
||||||
import { execa, execaCommand } from 'execa';
|
import { execa, execaCommand } from 'execa';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { bgCyan, black, bold, cyan, gray, green, red, yellow } from 'kleur/colors';
|
import { bgCyan, black, bold, cyan, dim, gray, green, red, yellow } from 'kleur/colors';
|
||||||
import ora from 'ora';
|
import ora from 'ora';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import prompts from 'prompts';
|
import prompts from 'prompts';
|
||||||
|
@ -11,6 +11,13 @@ import { loadWithRocketGradient, rocketAscii } from './gradient.js';
|
||||||
import { defaultLogLevel, logger } from './logger.js';
|
import { defaultLogLevel, logger } from './logger.js';
|
||||||
import { TEMPLATES } from './templates.js';
|
import { TEMPLATES } from './templates.js';
|
||||||
|
|
||||||
|
function wait(ms: number) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
function logAndWait(message: string, ms: number = 100) {
|
||||||
|
console.log(message);
|
||||||
|
return wait(ms);
|
||||||
|
}
|
||||||
// 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
|
||||||
// broke our arg parser, since `--` is a special kind of flag. Filtering for `--` here
|
// broke our arg parser, since `--` is a special kind of flag. Filtering for `--` here
|
||||||
|
@ -43,6 +50,7 @@ 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})`)}`);
|
||||||
|
console.log(`Lets walk through setting up your new Astro project.\n`);
|
||||||
|
|
||||||
let cwd = args['_'][2] as string;
|
let cwd = args['_'][2] as string;
|
||||||
|
|
||||||
|
@ -64,7 +72,7 @@ export async function main() {
|
||||||
const dirResponse = await prompts({
|
const dirResponse = await prompts({
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'directory',
|
name: 'directory',
|
||||||
message: 'Where would you like to create your app?',
|
message: 'Where would you like to create your new project?',
|
||||||
initial: './my-astro-site',
|
initial: './my-astro-site',
|
||||||
validate(value) {
|
validate(value) {
|
||||||
if (!isEmpty(value)) {
|
if (!isEmpty(value)) {
|
||||||
|
@ -84,7 +92,7 @@ export async function main() {
|
||||||
{
|
{
|
||||||
type: 'select',
|
type: 'select',
|
||||||
name: 'template',
|
name: 'template',
|
||||||
message: 'Which app template would you like to use?',
|
message: 'Which template would you like to use?',
|
||||||
choices: TEMPLATES,
|
choices: TEMPLATES,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -112,7 +120,7 @@ export async function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Copy
|
// Copy
|
||||||
if (!args.dryrun) {
|
if (!args.dryRun) {
|
||||||
try {
|
try {
|
||||||
emitter.on('info', (info) => {
|
emitter.on('info', (info) => {
|
||||||
logger.debug(info.message);
|
logger.debug(info.message);
|
||||||
|
@ -175,11 +183,9 @@ export async function main() {
|
||||||
initial: true,
|
initial: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!installResponse) {
|
if (args.dryRun) {
|
||||||
process.exit(0);
|
ora().info(dim(`--dry-run enabled, skipping.`));
|
||||||
}
|
} else 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(' 📦', '...')}`;
|
||||||
const installSpinner = await loadWithRocketGradient(installingPackagesMsg);
|
const installSpinner = await loadWithRocketGradient(installingPackagesMsg);
|
||||||
|
@ -194,69 +200,51 @@ export async function main() {
|
||||||
});
|
});
|
||||||
installSpinner.text = green('Packages installed!');
|
installSpinner.text = green('Packages installed!');
|
||||||
installSpinner.succeed();
|
installSpinner.succeed();
|
||||||
}
|
} else {
|
||||||
|
ora().info(dim(`No problem! You can install dependencies yourself after setup.`));
|
||||||
const astroAddCommand = installResponse.install
|
|
||||||
? 'astro add --yes'
|
|
||||||
: `${pkgManagerExecCommand(pkgManager)} astro@latest add --yes`;
|
|
||||||
|
|
||||||
const astroAddResponse = await prompts({
|
|
||||||
type: 'confirm',
|
|
||||||
name: 'astroAdd',
|
|
||||||
message: `Run "${astroAddCommand}?" This lets you optionally add component frameworks (ex. React), CSS frameworks (ex. Tailwind), and more.`,
|
|
||||||
initial: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!astroAddResponse) {
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!astroAddResponse.astroAdd) {
|
|
||||||
ora().info(
|
|
||||||
`No problem. You can always run "${pkgManagerExecCommand(pkgManager)} astro add" later!`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (astroAddResponse.astroAdd && !args.dryrun) {
|
|
||||||
await execaCommand(
|
|
||||||
astroAddCommand,
|
|
||||||
astroAddCommand === 'astro add --yes'
|
|
||||||
? { cwd, stdio: 'inherit', localDir: cwd, preferLocal: true }
|
|
||||||
: { cwd, stdio: 'inherit' }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const gitResponse = await prompts({
|
const gitResponse = await prompts({
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
name: 'git',
|
name: 'git',
|
||||||
message: 'Initialize a git repository?',
|
message: `Initialize a new git repository? ${dim('This can be useful to track changes.')}`,
|
||||||
initial: true,
|
initial: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!gitResponse) {
|
if (args.dryRun) {
|
||||||
process.exit(0);
|
ora().info(dim(`--dry-run enabled, skipping.`));
|
||||||
}
|
} else if (gitResponse.git) {
|
||||||
|
|
||||||
if (gitResponse.git && !args.dryrun) {
|
|
||||||
await execaCommand('git init', { cwd });
|
await execaCommand('git init', { cwd });
|
||||||
|
} else {
|
||||||
|
ora().info(
|
||||||
|
dim(`Sounds good! You can come back and run ${cyan(`git init`)} later.`)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ora({ text: green('Done. Ready for liftoff!') }).succeed();
|
ora().succeed('Setup complete.');
|
||||||
|
ora({ text: green('Ready for liftoff!') }).succeed();
|
||||||
|
await wait(300);
|
||||||
|
|
||||||
console.log(`\n${bgCyan(black(' Next steps '))}\n`);
|
console.log(`\n${bgCyan(black(' Next steps '))}\n`);
|
||||||
|
|
||||||
const projectDir = path.relative(process.cwd(), cwd);
|
let projectDir = path.relative(process.cwd(), cwd);
|
||||||
const devCmd = pkgManager === 'npm' ? 'npm run dev' : `${pkgManager} dev`;
|
const devCmd = pkgManager === 'npm' ? 'npm run dev' : `${pkgManager} dev`;
|
||||||
|
|
||||||
console.log(
|
await logAndWait(
|
||||||
`You can now ${bold(cyan('cd'))} into the ${bold(cyan(projectDir))} project directory.`
|
`You can now ${bold(cyan('cd'))} into the ${bold(cyan(projectDir))} project directory.`
|
||||||
);
|
);
|
||||||
console.log(
|
await logAndWait(
|
||||||
`Run ${bold(cyan(devCmd))} to start the Astro dev server. ${bold(cyan('CTRL-C'))} to close.`
|
`Run ${bold(cyan(devCmd))} to start the Astro dev server. ${bold(cyan('CTRL-C'))} to close.`
|
||||||
);
|
);
|
||||||
if (!installResponse.install) {
|
await logAndWait(
|
||||||
console.log(yellow(`Remember to install dependencies first!`));
|
`Add frameworks like ${bold(cyan('react'))} and ${bold(
|
||||||
}
|
cyan('tailwind')
|
||||||
console.log(`\nStuck? Come join us at ${bold(cyan('https://astro.build/chat'))}`);
|
)} to your project using ${bold(cyan('astro add'))}`
|
||||||
|
);
|
||||||
|
await logAndWait('');
|
||||||
|
await logAndWait(`Stuck? Come join us at ${bold(cyan('https://astro.build/chat'))}`, 1000);
|
||||||
|
await logAndWait(dim('Good luck out there, astronaut.'));
|
||||||
|
await logAndWait('', 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
function emojiWithFallback(char: string, fallback: string) {
|
function emojiWithFallback(char: string, fallback: string) {
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
import { setup, promiseWithTimeout, timeout, PROMPT_MESSAGES } from './utils.js';
|
|
||||||
import { sep } from 'path';
|
|
||||||
import fs from 'fs';
|
|
||||||
import os from 'os';
|
|
||||||
|
|
||||||
// reset package manager in process.env
|
|
||||||
// prevents test issues when running with pnpm
|
|
||||||
const FAKE_PACKAGE_MANAGER = 'npm';
|
|
||||||
let initialEnvValue = null;
|
|
||||||
|
|
||||||
describe('[create-astro] astro add', function () {
|
|
||||||
this.timeout(timeout);
|
|
||||||
let tempDir = '';
|
|
||||||
beforeEach(async () => {
|
|
||||||
tempDir = await fs.promises.mkdtemp(`${os.tmpdir()}${sep}`);
|
|
||||||
});
|
|
||||||
this.beforeAll(() => {
|
|
||||||
initialEnvValue = process.env.npm_config_user_agent;
|
|
||||||
process.env.npm_config_user_agent = FAKE_PACKAGE_MANAGER;
|
|
||||||
});
|
|
||||||
this.afterAll(() => {
|
|
||||||
process.env.npm_config_user_agent = initialEnvValue;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use "astro add" when user has installed dependencies', function () {
|
|
||||||
const { stdout, stdin } = setup([tempDir]);
|
|
||||||
return promiseWithTimeout((resolve) => {
|
|
||||||
const seen = new Set();
|
|
||||||
const installPrompt = PROMPT_MESSAGES.install('npm');
|
|
||||||
stdout.on('data', (chunk) => {
|
|
||||||
if (!seen.has(PROMPT_MESSAGES.template) && chunk.includes(PROMPT_MESSAGES.template)) {
|
|
||||||
seen.add(PROMPT_MESSAGES.template);
|
|
||||||
// respond with "enter key"
|
|
||||||
stdin.write('\x0D');
|
|
||||||
}
|
|
||||||
if (!seen.has(installPrompt) && chunk.includes(installPrompt)) {
|
|
||||||
seen.add(installPrompt);
|
|
||||||
stdin.write('\x0D');
|
|
||||||
}
|
|
||||||
if (chunk.includes(PROMPT_MESSAGES.astroAdd('astro add --yes'))) {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use "npx astro@latest add" when use has NOT installed dependencies', function () {
|
|
||||||
const { stdout, stdin } = setup([tempDir]);
|
|
||||||
return promiseWithTimeout((resolve) => {
|
|
||||||
const seen = new Set();
|
|
||||||
const installPrompt = PROMPT_MESSAGES.install('npm');
|
|
||||||
stdout.on('data', (chunk) => {
|
|
||||||
if (!seen.has(PROMPT_MESSAGES.template) && chunk.includes(PROMPT_MESSAGES.template)) {
|
|
||||||
seen.add(PROMPT_MESSAGES.template);
|
|
||||||
// respond with "enter key"
|
|
||||||
stdin.write('\x0D');
|
|
||||||
}
|
|
||||||
if (!seen.has(installPrompt) && chunk.includes(installPrompt)) {
|
|
||||||
seen.add(installPrompt);
|
|
||||||
// respond with "no, then enter key"
|
|
||||||
stdin.write('n\x0D');
|
|
||||||
}
|
|
||||||
if (chunk.includes(PROMPT_MESSAGES.astroAdd('npx astro@latest add --yes'))) {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -44,7 +44,6 @@ describe('[create-astro] install', function () {
|
||||||
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);
|
||||||
const astroAddPrompt = PROMPT_MESSAGES.astroAdd();
|
|
||||||
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);
|
||||||
|
@ -56,10 +55,6 @@ describe('[create-astro] install', function () {
|
||||||
// respond with "no, then enter key"
|
// respond with "no, then enter key"
|
||||||
stdin.write('n\x0D');
|
stdin.write('n\x0D');
|
||||||
}
|
}
|
||||||
if (!seen.has(astroAddPrompt) && chunk.includes(astroAddPrompt)) {
|
|
||||||
seen.add(astroAddPrompt);
|
|
||||||
stdin.write('n\x0D');
|
|
||||||
}
|
|
||||||
if (!seen.has(PROMPT_MESSAGES.git) && chunk.includes(PROMPT_MESSAGES.git)) {
|
if (!seen.has(PROMPT_MESSAGES.git) && chunk.includes(PROMPT_MESSAGES.git)) {
|
||||||
seen.add(PROMPT_MESSAGES.git);
|
seen.add(PROMPT_MESSAGES.git);
|
||||||
stdin.write('\x0D');
|
stdin.write('\x0D');
|
||||||
|
|
|
@ -24,12 +24,10 @@ export function promiseWithTimeout(testFn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PROMPT_MESSAGES = {
|
export const PROMPT_MESSAGES = {
|
||||||
directory: 'Where would you like to create your app?',
|
directory: 'Where would you like to create your new project?',
|
||||||
template: 'Which app template would you like to use?',
|
template: 'Which template would you like to use?',
|
||||||
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') =>
|
git: 'Initialize a new git repository?',
|
||||||
`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 = []) {
|
||||||
|
|
2
packages/webapi/mod.d.ts
vendored
2
packages/webapi/mod.d.ts
vendored
|
@ -1,5 +1,5 @@
|
||||||
export { pathToPosix } from './lib/utils';
|
export { pathToPosix } from './lib/utils';
|
||||||
export { AbortController, AbortSignal, alert, atob, Blob, btoa, ByteLengthQueuingStrategy, cancelAnimationFrame, cancelIdleCallback, CanvasRenderingContext2D, CharacterData, clearTimeout, Comment, CountQueuingStrategy, CSSStyleSheet, CustomElementRegistry, CustomEvent, Document, DocumentFragment, DOMException, Element, Event, EventTarget, fetch, File, FormData, Headers, HTMLBodyElement, HTMLCanvasElement, HTMLDivElement, HTMLDocument, HTMLElement, HTMLHeadElement, HTMLHtmlElement, HTMLImageElement, HTMLSpanElement, HTMLStyleElement, HTMLTemplateElement, HTMLUnknownElement, Image, ImageData, IntersectionObserver, MediaQueryList, MutationObserver, Node, NodeFilter, NodeIterator, OffscreenCanvas, ReadableByteStreamController, ReadableStream, ReadableStreamBYOBReader, ReadableStreamBYOBRequest, ReadableStreamDefaultController, ReadableStreamDefaultReader, Request, requestAnimationFrame, requestIdleCallback, ResizeObserver, Response, setTimeout, ShadowRoot, structuredClone, StyleSheet, Text, TransformStream, TreeWalker, URLPattern, Window, WritableStream, WritableStreamDefaultController, WritableStreamDefaultWriter } from './mod.js';
|
export { AbortController, AbortSignal, alert, atob, Blob, btoa, ByteLengthQueuingStrategy, cancelAnimationFrame, cancelIdleCallback, CanvasRenderingContext2D, CharacterData, clearTimeout, Comment, CountQueuingStrategy, CSSStyleSheet, CustomElementRegistry, CustomEvent, Document, DocumentFragment, DOMException, Element, Event, EventTarget, fetch, File, FormData, Headers, HTMLBodyElement, HTMLCanvasElement, HTMLDivElement, HTMLDocument, HTMLElement, HTMLHeadElement, HTMLHtmlElement, HTMLImageElement, HTMLSpanElement, HTMLStyleElement, HTMLTemplateElement, HTMLUnknownElement, Image, ImageData, IntersectionObserver, MediaQueryList, MutationObserver, Node, NodeFilter, NodeIterator, OffscreenCanvas, ReadableByteStreamController, ReadableStream, ReadableStreamBYOBReader, ReadableStreamBYOBRequest, ReadableStreamDefaultController, ReadableStreamDefaultReader, Request, requestAnimationFrame, requestIdleCallback, ResizeObserver, Response, setTimeout, ShadowRoot, structuredClone, StyleSheet, Text, TransformStream, TreeWalker, URLPattern, Window, WritableStream, WritableStreamDefaultController, WritableStreamDefaultWriter, } from './mod.js';
|
||||||
export declare const polyfill: {
|
export declare const polyfill: {
|
||||||
(target: any, options?: PolyfillOptions): any;
|
(target: any, options?: PolyfillOptions): any;
|
||||||
internals(target: any, name: string): any;
|
internals(target: any, name: string): any;
|
||||||
|
|
Loading…
Reference in a new issue