Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clean up config loading and flag merging #2469

Merged
merged 2 commits into from
Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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?: {
Expand Down
91 changes: 27 additions & 64 deletions packages/astro/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -95,24 +48,35 @@ 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];
switch (cmd) {
case 'dev':
return 'dev';
case 'build':
return 'build';
case 'preview':
return 'preview';
case 'check':
return 'check';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is switch necessary here? Why not something like...

const validCommands = new Set(['dev', 'build', 'preview', 'check']);
if (validCommands.has(cmd)) return cmd;
return 'help'

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

default:
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);
Expand All @@ -131,8 +95,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));
Expand All @@ -142,7 +105,7 @@ export async function cli(args: string[]) {
process.exit(1);
}

switch (state.cmd) {
switch (cmd) {
case 'dev': {
try {
await devServer(config, { logging });
Expand Down Expand Up @@ -176,7 +139,7 @@ export async function cli(args: string[]) {
return;
}
default: {
throw new Error(`Error running ${state.cmd}`);
throw new Error(`Error running ${cmd}`);
}
}
}
Expand Down
45 changes: 38 additions & 7 deletions packages/astro/src/core/config.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down