clean up config loading and flag merging (#2469)
This commit is contained in:
parent
831a9adc20
commit
c70c4ea211
3 changed files with 79 additions and 71 deletions
|
@ -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?: {
|
||||
|
|
|
@ -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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue