Refactor simple CLI commands (#7619)

This commit is contained in:
Bjorn Lu 2023-07-11 17:21:57 +08:00 committed by GitHub
parent 3669e2d276
commit 03c28aa629
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 123 additions and 115 deletions

View file

@ -9,35 +9,36 @@ import preferredPM from 'preferred-pm';
import prompts from 'prompts'; 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 { loadTSConfig, resolveConfigPath } from '../config/index.js'; import { loadTSConfig, resolveConfigPath } from '../../core/config/index.js';
import { import {
defaultTSConfig, defaultTSConfig,
presets, presets,
updateTSConfigForFramework, updateTSConfigForFramework,
type frameworkWithTSSettings, type frameworkWithTSSettings,
} from '../config/tsconfig.js'; } from '../../core/config/tsconfig.js';
import { debug, info, type LogOptions } from '../logger/core.js'; import { debug, info, type LogOptions } from '../../core/logger/core.js';
import * as msg from '../messages.js'; import * as msg from '../../core/messages.js';
import { printHelp } from '../messages.js'; import { printHelp } from '../../core/messages.js';
import { appendForwardSlash } from '../path.js'; import { appendForwardSlash } from '../../core/path.js';
import { apply as applyPolyfill } from '../polyfill.js'; import { apply as applyPolyfill } from '../../core/polyfill.js';
import { parseNpmName } from '../util.js'; import { parseNpmName } from '../../core/util.js';
import { generate, parse, t, visit } from './babel.js'; import { generate, parse, t, visit } from './babel.js';
import { ensureImport } from './imports.js'; import { ensureImport } from './imports.js';
import { wrapDefaultExport } from './wrapper.js'; import { wrapDefaultExport } from './wrapper.js';
export interface AddOptions { interface AddOptions {
logging: LogOptions; logging: LogOptions;
flags: yargs.Arguments; flags: yargs.Arguments;
cwd?: string; cwd?: string;
} }
export interface IntegrationInfo { interface IntegrationInfo {
id: string; id: string;
packageName: string; packageName: string;
dependencies: [name: string, version: string][]; dependencies: [name: string, version: string][];
type: 'integration' | 'adapter'; type: 'integration' | 'adapter';
} }
const ALIASES = new Map([ const ALIASES = new Map([
['solid', 'solid-js'], ['solid', 'solid-js'],
['tailwindcss', 'tailwind'], ['tailwindcss', 'tailwind'],
@ -85,7 +86,7 @@ async function getRegistry(): Promise<string> {
} }
} }
export default async function add(names: string[], { cwd, flags, logging }: AddOptions) { export async function add(names: string[], { cwd, flags, logging }: AddOptions) {
applyPolyfill(); applyPolyfill();
if (flags.help || names.length === 0) { if (flags.help || names.length === 0) {
printHelp({ printHelp({

View file

@ -0,0 +1,22 @@
import type yargs from 'yargs-parser';
import { printHelp } from '../../core/messages.js';
import { openInBrowser } from './open.js';
interface DocsOptions {
flags: yargs.Arguments;
}
export async function docs({ flags }: DocsOptions) {
if (flags.help || flags.h) {
printHelp({
commandName: 'astro docs',
tables: {
Flags: [['--help (-h)', 'See all available flags.']],
},
description: `Launches the Astro Docs website directly from the terminal.`,
});
return;
}
return await openInBrowser('https://docs.astro.build/');
}

View file

@ -1,7 +1,6 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
import fs from 'fs'; import fs from 'fs';
import * as colors from 'kleur/colors'; import * as colors from 'kleur/colors';
import { arch, platform } from 'node:os';
import type { Arguments as Flags } from 'yargs-parser'; import type { Arguments as Flags } from 'yargs-parser';
import yargs from 'yargs-parser'; import yargs from 'yargs-parser';
import { ZodError } from 'zod'; import { ZodError } from 'zod';
@ -19,7 +18,6 @@ import { enableVerboseLogging, nodeLogDestination } from '../core/logger/node.js
import { formatConfigErrorMessage, formatErrorMessage, printHelp } from '../core/messages.js'; import { formatConfigErrorMessage, formatErrorMessage, printHelp } from '../core/messages.js';
import * as event from '../events/index.js'; import * as event from '../events/index.js';
import { eventConfigError, eventError, telemetry } from '../events/index.js'; import { eventConfigError, eventError, telemetry } from '../events/index.js';
import { openInBrowser } from './open.js';
type Arguments = yargs.Arguments; type Arguments = yargs.Arguments;
type CLICommand = type CLICommand =
@ -69,62 +67,11 @@ function printAstroHelp() {
} }
/** Display --version flag */ /** Display --version flag */
async function printVersion() { function printVersion() {
console.log(); console.log();
console.log(` ${colors.bgGreen(colors.black(` astro `))} ${colors.green(`v${ASTRO_VERSION}`)}`); console.log(` ${colors.bgGreen(colors.black(` astro `))} ${colors.green(`v${ASTRO_VERSION}`)}`);
} }
async function printInfo({
cwd,
flags,
logging,
}: {
cwd?: string;
flags?: Flags;
logging: LogOptions;
}) {
const whichPm = await import('which-pm');
const packageManager = await whichPm.default(process.cwd());
let adapter = "Couldn't determine.";
let integrations = [];
const MAX_PADDING = 25;
function printRow(label: string, value: string) {
const padding = MAX_PADDING - label.length;
console.log(`${colors.bold(label)}` + ' '.repeat(padding) + `${colors.green(value)}`);
}
try {
const { userConfig } = await openConfig({
cwd,
flags,
cmd: 'info',
logging,
});
if (userConfig.adapter?.name) {
adapter = userConfig.adapter.name;
}
if (userConfig.integrations) {
integrations = (userConfig?.integrations ?? [])
.filter(Boolean)
.flat()
.map((i: any) => i?.name);
}
} catch (_e) {}
console.log();
const packageManagerName = packageManager?.name ?? "Couldn't determine.";
printRow('Astro version', `v${ASTRO_VERSION}`);
printRow('Package manager', packageManagerName);
printRow('Platform', platform());
printRow('Architecture', arch());
printRow('Adapter', adapter);
let integrationsString = "None or couldn't determine.";
if (integrations.length > 0) {
integrationsString = integrations.join(', ');
}
printRow('Integrations', integrationsString);
}
/** Determine which command the user requested */ /** Determine which command the user requested */
function resolveCommand(flags: Arguments): CLICommand { function resolveCommand(flags: Arguments): CLICommand {
const cmd = flags._[2] as string; const cmd = flags._[2] as string;
@ -171,24 +118,40 @@ async function handleConfigError(
**/ **/
async function runCommand(cmd: string, flags: yargs.Arguments) { async function runCommand(cmd: string, flags: yargs.Arguments) {
const root = flags.root; const root = flags.root;
// logLevel
let logging: LogOptions = { // These commands can run directly without parsing the user config.
dest: nodeLogDestination,
level: 'info',
};
switch (cmd) { switch (cmd) {
case 'help': case 'help':
printAstroHelp(); printAstroHelp();
return process.exit(0); return;
case 'version': case 'version':
await printVersion(); printVersion();
return process.exit(0); return;
case 'info': { case 'info': {
await printInfo({ cwd: root, flags, logging }); const { printInfo } = await import('./info/index.js');
return process.exit(0); await printInfo({ cwd: root, flags });
return;
}
case 'docs': {
telemetry.record(event.eventCliSession(cmd));
const { docs } = await import('./docs/index.js');
await docs({ flags });
return;
}
case 'telemetry': {
// Do not track session start, since the user may be trying to enable,
// disable, or modify telemetry settings.
const { update } = await import('./telemetry/index.js');
const subcommand = flags._[3]?.toString();
await update(subcommand, { flags });
return;
} }
} }
const logging: LogOptions = {
dest: nodeLogDestination,
level: 'info',
};
if (flags.verbose) { if (flags.verbose) {
logging.level = 'debug'; logging.level = 'debug';
enableVerboseLogging(); enableVerboseLogging();
@ -196,39 +159,15 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
logging.level = 'silent'; logging.level = 'silent';
} }
// Special CLI Commands: "add", "docs", "telemetry" // These commands can also be run directly without parsing the user config,
// These commands run before the user's config is parsed, and may have other special // but they rely on parsing the `logging` first.
// conditions that should be handled here, before the others.
//
switch (cmd) { switch (cmd) {
case 'add': { case 'add': {
const { default: add } = await import('../core/add/index.js');
telemetry.record(event.eventCliSession(cmd)); telemetry.record(event.eventCliSession(cmd));
const { add } = await import('./add/index.js');
const packages = flags._.slice(3) as string[]; const packages = flags._.slice(3) as string[];
return await add(packages, { cwd: root, flags, logging }); await add(packages, { cwd: root, flags, logging });
} return;
case 'docs': {
telemetry.record(event.eventCliSession(cmd));
if (flags.help || flags.h) {
printHelp({
commandName: 'astro docs',
tables: {
Flags: [['--help (-h)', 'See all available flags.']],
},
description: `Launches the Astro Docs website directly from the terminal.`,
});
return;
}
return await openInBrowser('https://docs.astro.build/');
}
case 'telemetry': {
const telemetryHandler = await import('./telemetry.js');
// Do not track session start, since the user may be trying to enable,
// disable, or modify telemetry settings.
const subcommand = flags._[3]?.toString();
return await telemetryHandler.update(subcommand, { flags });
} }
} }
@ -241,7 +180,6 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
cwd: root, cwd: root,
flags, flags,
cmd, cmd,
logging,
}).catch(async (e) => { }).catch(async (e) => {
await handleConfigError(e, { cmd, cwd: root, flags, logging }); await handleConfigError(e, { cmd, cwd: root, flags, logging });
return {} as any; return {} as any;

View file

@ -0,0 +1,53 @@
/* eslint-disable no-console */
import { arch, platform } from 'node:os';
import * as colors from 'kleur/colors';
import whichPm from 'which-pm';
import type yargs from 'yargs-parser';
import { openConfig } from '../../core/config/index.js';
import { ASTRO_VERSION } from '../../core/constants.js';
interface InfoOptions {
cwd?: string;
flags: yargs.Arguments;
}
export async function printInfo({ cwd, flags }: InfoOptions) {
const packageManager = await whichPm(process.cwd());
let adapter = "Couldn't determine.";
let integrations = [];
const MAX_PADDING = 25;
function printRow(label: string, value: string) {
const padding = MAX_PADDING - label.length;
console.log(`${colors.bold(label)}` + ' '.repeat(padding) + `${colors.green(value)}`);
}
try {
const { userConfig } = await openConfig({
cwd,
flags,
cmd: 'info',
});
if (userConfig.adapter?.name) {
adapter = userConfig.adapter.name;
}
if (userConfig.integrations) {
integrations = (userConfig?.integrations ?? [])
.filter(Boolean)
.flat()
.map((i: any) => i?.name);
}
} catch (_e) {}
console.log();
const packageManagerName = packageManager?.name ?? "Couldn't determine.";
printRow('Astro version', `v${ASTRO_VERSION}`);
printRow('Package manager', packageManagerName);
printRow('Platform', platform());
printRow('Architecture', arch());
printRow('Adapter', adapter);
let integrationsString = "None or couldn't determine.";
if (integrations.length > 0) {
integrationsString = integrations.join(', ');
}
printRow('Integrations', integrationsString);
}

View file

@ -1,9 +1,9 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
import type yargs from 'yargs-parser'; import type yargs from 'yargs-parser';
import * as msg from '../core/messages.js'; import * as msg from '../../core/messages.js';
import { telemetry } from '../events/index.js'; import { telemetry } from '../../events/index.js';
export interface TelemetryOptions { interface TelemetryOptions {
flags: yargs.Arguments; flags: yargs.Arguments;
} }

View file

@ -34,10 +34,7 @@ export function getViteConfig(inlineConfig: UserConfig) {
dest: nodeLogDestination, dest: nodeLogDestination,
level: 'info', level: 'info',
}; };
const { astroConfig: config } = await openConfig({ const { astroConfig: config } = await openConfig({ cmd });
cmd,
logging,
});
const settings = createSettings(config, inlineConfig.root); const settings = createSettings(config, inlineConfig.root);
await runHookConfigSetup({ settings, command: cmd, logging }); await runHookConfigSetup({ settings, command: cmd, logging });
const viteConfig = await createVite( const viteConfig = await createVite(

View file

@ -7,7 +7,6 @@ import path from 'path';
import { fileURLToPath, pathToFileURL } from 'url'; import { fileURLToPath, pathToFileURL } from 'url';
import { mergeConfig as mergeViteConfig } from 'vite'; import { mergeConfig as mergeViteConfig } from 'vite';
import { AstroError, AstroErrorData } from '../errors/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js';
import type { LogOptions } from '../logger/core.js';
import { arraify, isObject, isURL } from '../util.js'; import { arraify, isObject, isURL } from '../util.js';
import { createRelativeSchema } from './schema.js'; import { createRelativeSchema } from './schema.js';
import { loadConfigWithVite } from './vite-load.js'; import { loadConfigWithVite } from './vite-load.js';
@ -163,7 +162,6 @@ interface LoadConfigOptions {
flags?: Flags; flags?: Flags;
cmd: string; cmd: string;
validate?: boolean; validate?: boolean;
logging: LogOptions;
/** Invalidate when reloading a previously loaded config */ /** Invalidate when reloading a previously loaded config */
isRestart?: boolean; isRestart?: boolean;
fsMod?: typeof fs; fsMod?: typeof fs;

View file

@ -87,7 +87,6 @@ export async function restartContainer({
cwd: resolvedRoot, cwd: resolvedRoot,
flags, flags,
cmd: 'dev', cmd: 'dev',
logging,
isRestart: true, isRestart: true,
fsMod: container.fs, fsMod: container.fs,
}); });