clean up config loading and flag merging (#2469)

This commit is contained in:
Fred K. Schott 2022-01-25 15:07:51 -08:00 committed by GitHub
parent 831a9adc20
commit c70c4ea211
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 71 deletions

View file

@ -19,6 +19,18 @@ export interface AstroComponentMetadata {
componentExport?: { value: string; namespace?: boolean };
}
/** The flags supported by the Astro CLI */
export interface CLIFlags {
projectRoot?: string;
site?: string;
sitemap?: boolean;
hostname?: string;
port?: number;
config?: string;
experimentalStaticBuild?: boolean;
drafts?: boolean;
}
/**
* Astro.* available in all components
* Docs: https://docs.astro.build/reference/api-reference/#astro-global
@ -115,6 +127,11 @@ export interface AstroUserConfig {
* Default: false
*/
drafts?: boolean;
/**
* Experimental: Enables "static build mode" for faster builds.
* Default: false
*/
experimentalStaticBuild?: boolean;
};
/** Options for the development server run with `astro dev`. */
devOptions?: {

View file

@ -15,54 +15,7 @@ import { check } from './check.js';
import { formatConfigError, loadConfig } from '../core/config.js';
type Arguments = yargs.Arguments;
type cliCommand = 'help' | 'version' | 'dev' | 'build' | 'preview' | 'reload' | 'check';
interface CLIState {
cmd: cliCommand;
options: {
projectRoot?: string;
site?: string;
sitemap?: boolean;
hostname?: string;
port?: number;
config?: string;
experimentalStaticBuild?: boolean;
drafts?: boolean;
};
}
/** Determine which action the user requested */
function resolveArgs(flags: Arguments): CLIState {
const options: CLIState['options'] = {
projectRoot: typeof flags.projectRoot === 'string' ? flags.projectRoot : undefined,
site: typeof flags.site === 'string' ? flags.site : undefined,
sitemap: typeof flags.sitemap === 'boolean' ? flags.sitemap : undefined,
port: typeof flags.port === 'number' ? flags.port : undefined,
config: typeof flags.config === 'string' ? flags.config : undefined,
hostname: typeof flags.hostname === 'string' ? flags.hostname : undefined,
experimentalStaticBuild: typeof flags.experimentalStaticBuild === 'boolean' ? flags.experimentalStaticBuild : false,
drafts: typeof flags.drafts === 'boolean' ? flags.drafts : false,
};
if (flags.version) {
return { cmd: 'version', options };
} else if (flags.help) {
return { cmd: 'help', options };
}
const cmd = flags._[2];
switch (cmd) {
case 'dev':
return { cmd: 'dev', options };
case 'build':
return { cmd: 'build', options };
case 'preview':
return { cmd: 'preview', options };
case 'check':
return { cmd: 'check', options };
default:
return { cmd: 'help', options };
}
}
type CLICommand = 'help' | 'version' | 'dev' | 'build' | 'preview' | 'reload' | 'check';
/** Display --help flag */
function printHelp() {
@ -95,24 +48,28 @@ async function printVersion() {
console.log(pkgVersion);
}
/** Merge CLI flags & config options (CLI flags take priority) */
function mergeCLIFlags(astroConfig: AstroConfig, flags: CLIState['options']) {
if (typeof flags.sitemap === 'boolean') astroConfig.buildOptions.sitemap = flags.sitemap;
if (typeof flags.site === 'string') astroConfig.buildOptions.site = flags.site;
if (typeof flags.port === 'number') astroConfig.devOptions.port = flags.port;
if (typeof flags.hostname === 'string') astroConfig.devOptions.hostname = flags.hostname;
if (typeof flags.experimentalStaticBuild === 'boolean') astroConfig.buildOptions.experimentalStaticBuild = flags.experimentalStaticBuild;
if (typeof flags.drafts === 'boolean') astroConfig.buildOptions.drafts = flags.drafts;
/** Determine which command the user requested */
function resolveCommand(flags: Arguments): CLICommand {
if (flags.version) {
return 'version';
} else if (flags.help) {
return 'help';
}
const cmd = flags._[2];
const supportedCommands = new Set(['dev', 'build', 'preview', 'check']);
if (supportedCommands.has(cmd)) {
return cmd as 'dev' | 'build' | 'preview' | 'check';
}
return 'help';
}
/** The primary CLI action */
export async function cli(args: string[]) {
const flags = yargs(args);
const state = resolveArgs(flags);
const options = { ...state.options };
const projectRoot = options.projectRoot || flags._[3];
const cmd = resolveCommand(flags);
const projectRoot = flags.projectRoot || flags._[3];
switch (state.cmd) {
switch (cmd) {
case 'help':
printHelp();
return process.exit(0);
@ -131,8 +88,7 @@ export async function cli(args: string[]) {
if (flags.silent) logging.level = 'silent';
let config: AstroConfig;
try {
config = await loadConfig({ cwd: projectRoot, filename: options.config });
mergeCLIFlags(config, options);
config = await loadConfig({ cwd: projectRoot, flags });
} catch (err) {
if (err instanceof z.ZodError) {
console.error(formatConfigError(err));
@ -142,7 +98,7 @@ export async function cli(args: string[]) {
process.exit(1);
}
switch (state.cmd) {
switch (cmd) {
case 'dev': {
try {
await devServer(config, { logging });
@ -154,6 +110,7 @@ export async function cli(args: string[]) {
return;
}
case 'build': {
try {
await build(config, { logging });
@ -163,10 +120,12 @@ export async function cli(args: string[]) {
}
return;
}
case 'check': {
const ret = await check(config);
return process.exit(ret);
}
case 'preview': {
try {
await preview(config, { logging }); // this will keep running
@ -175,8 +134,9 @@ export async function cli(args: string[]) {
}
return;
}
default: {
throw new Error(`Error running ${state.cmd}`);
throw new Error(`Error running ${cmd}`);
}
}
}

View file

@ -1,4 +1,5 @@
import type { AstroConfig, AstroUserConfig } from '../@types/astro';
import type { AstroConfig, AstroUserConfig, CLIFlags } from '../@types/astro';
import type { Arguments as Flags } from 'yargs-parser';
import * as colors from 'kleur/colors';
import path from 'path';
@ -116,19 +117,47 @@ function addTrailingSlash(str: string): string {
return str.replace(/\/*$/, '/');
}
/** Convert the generic "yargs" flag object into our own, custom TypeScript object. */
function resolveFlags(flags: Partial<Flags>): CLIFlags {
return {
projectRoot: typeof flags.projectRoot === 'string' ? flags.projectRoot : undefined,
site: typeof flags.site === 'string' ? flags.site : undefined,
sitemap: typeof flags.sitemap === 'boolean' ? flags.sitemap : undefined,
port: typeof flags.port === 'number' ? flags.port : undefined,
config: typeof flags.config === 'string' ? flags.config : undefined,
hostname: typeof flags.hostname === 'string' ? flags.hostname : undefined,
experimentalStaticBuild: typeof flags.experimentalStaticBuild === 'boolean' ? flags.experimentalStaticBuild : false,
drafts: typeof flags.drafts === 'boolean' ? flags.drafts : false,
};
}
/** Merge CLI flags & user config object (CLI flags take priority) */
function mergeCLIFlags(astroConfig: AstroUserConfig, flags: CLIFlags) {
astroConfig.buildOptions = astroConfig.buildOptions || {};
astroConfig.devOptions = astroConfig.devOptions || {};
if (typeof flags.sitemap === 'boolean') astroConfig.buildOptions.sitemap = flags.sitemap;
if (typeof flags.site === 'string') astroConfig.buildOptions.site = flags.site;
if (typeof flags.port === 'number') astroConfig.devOptions.port = flags.port;
if (typeof flags.hostname === 'string') astroConfig.devOptions.hostname = flags.hostname;
if (typeof flags.experimentalStaticBuild === 'boolean') astroConfig.buildOptions.experimentalStaticBuild = flags.experimentalStaticBuild;
if (typeof flags.drafts === 'boolean') astroConfig.buildOptions.drafts = flags.drafts;
return astroConfig;
}
interface LoadConfigOptions {
cwd?: string;
filename?: string;
flags?: Flags;
}
/** Attempt to load an `astro.config.mjs` file */
export async function loadConfig(options: LoadConfigOptions): Promise<AstroConfig> {
const root = options.cwd ? path.resolve(options.cwd) : process.cwd();
export async function loadConfig(configOptions: LoadConfigOptions): Promise<AstroConfig> {
const root = configOptions.cwd ? path.resolve(configOptions.cwd) : process.cwd();
const flags = resolveFlags(configOptions.flags || {});
let userConfig: AstroUserConfig = {};
let userConfigPath: string | undefined;
if (options.filename) {
userConfigPath = /^\.*\//.test(options.filename) ? options.filename : `./${options.filename}`;
if (flags?.config) {
userConfigPath = /^\.*\//.test(flags.config) ? flags.config : `./${flags.config}`;
userConfigPath = fileURLToPath(new URL(userConfigPath, `file://${root}/`));
}
// Automatically load config file using Proload
@ -138,7 +167,9 @@ export async function loadConfig(options: LoadConfigOptions): Promise<AstroConfi
userConfig = config.value;
}
// normalize, validate, and return
return validateConfig(userConfig, root);
const mergedConfig = mergeCLIFlags(userConfig, flags);
const validatedConfig = await validateConfig(mergedConfig, root);
return validatedConfig;
}
export function formatConfigError(err: z.ZodError) {