refactor(cli): make the CLI an even thinner wrapper around command functions (#7583)

This commit is contained in:
Joshua Chen 2022-06-16 01:28:07 +08:00 committed by GitHub
parent b4d93b9bd0
commit 0114f00069
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 54 additions and 50 deletions

View file

@ -8,7 +8,6 @@
// @ts-check // @ts-check
import fs from 'fs-extra';
import logger from '@docusaurus/logger'; import logger from '@docusaurus/logger';
import cli from 'commander'; import cli from 'commander';
import {DOCUSAURUS_VERSION} from '@docusaurus/utils'; import {DOCUSAURUS_VERSION} from '@docusaurus/utils';
@ -27,8 +26,6 @@ import beforeCli from './beforeCli.mjs';
await beforeCli(); await beforeCli();
const resolveDir = (dir = '.') => fs.realpath(dir);
cli.version(DOCUSAURUS_VERSION).usage('<command> [options]'); cli.version(DOCUSAURUS_VERSION).usage('<command> [options]');
cli cli
@ -54,9 +51,9 @@ cli
'--no-minify', '--no-minify',
'build website without minimizing JS bundles (default: false)', 'build website without minimizing JS bundles (default: false)',
) )
.action(async (siteDir, options) => { // @ts-expect-error: Promise<string> is not assignable to Promise<void>... but
await build(await resolveDir(siteDir), options); // good enough here.
}); .action(build);
cli cli
.command('swizzle [themeName] [componentName] [siteDir]') .command('swizzle [themeName] [componentName] [siteDir]')
@ -80,9 +77,7 @@ cli
'copy TypeScript theme files when possible (default: false)', 'copy TypeScript theme files when possible (default: false)',
) )
.option('--danger', 'enable swizzle for unsafe component of themes') .option('--danger', 'enable swizzle for unsafe component of themes')
.action(async (themeName, componentName, siteDir, options) => .action(swizzle);
swizzle(await resolveDir(siteDir), themeName, componentName, options),
);
cli cli
.command('deploy [siteDir]') .command('deploy [siteDir]')
@ -103,9 +98,7 @@ cli
'--skip-build', '--skip-build',
'skip building website before deploy it (default: false)', 'skip building website before deploy it (default: false)',
) )
.action(async (siteDir, options) => .action(deploy);
deploy(await resolveDir(siteDir), options),
);
cli cli
.command('start [siteDir]') .command('start [siteDir]')
@ -130,9 +123,7 @@ cli
'--no-minify', '--no-minify',
'build website without minimizing JS bundles (default: false)', 'build website without minimizing JS bundles (default: false)',
) )
.action(async (siteDir, options) => .action(start);
start(await resolveDir(siteDir), options),
);
cli cli
.command('serve [siteDir]') .command('serve [siteDir]')
@ -152,14 +143,12 @@ cli
'--no-open', '--no-open',
'do not open page in the browser (default: false, or true in CI)', 'do not open page in the browser (default: false, or true in CI)',
) )
.action(async (siteDir, options) => .action(serve);
serve(await resolveDir(siteDir), options),
);
cli cli
.command('clear [siteDir]') .command('clear [siteDir]')
.description('Remove build artifacts.') .description('Remove build artifacts.')
.action(async (siteDir) => clear(await resolveDir(siteDir))); .action(clear);
cli cli
.command('write-translations [siteDir]') .command('write-translations [siteDir]')
@ -180,9 +169,7 @@ cli
'--messagePrefix <messagePrefix>', '--messagePrefix <messagePrefix>',
'Allows to init new written messages with a given prefix. This might help you to highlight untranslated message by making them stand out in the UI (default: "")', 'Allows to init new written messages with a given prefix. This might help you to highlight untranslated message by making them stand out in the UI (default: "")',
) )
.action(async (siteDir, options) => .action(writeTranslations);
writeTranslations(await resolveDir(siteDir), options),
);
cli cli
.command('write-heading-ids [siteDir] [files...]') .command('write-heading-ids [siteDir] [files...]')
@ -192,9 +179,7 @@ cli
"keep the headings' casing, otherwise make all lowercase (default: false)", "keep the headings' casing, otherwise make all lowercase (default: false)",
) )
.option('--overwrite', 'overwrite existing heading IDs (default: false)') .option('--overwrite', 'overwrite existing heading IDs (default: false)')
.action(async (siteDir, files, options) => .action(writeHeadingIds);
writeHeadingIds(await resolveDir(siteDir), files, options),
);
cli.arguments('<command>').action((cmd) => { cli.arguments('<command>').action((cmd) => {
cli.outputHelp(); cli.outputHelp();
@ -221,7 +206,7 @@ function isInternalCommand(command) {
} }
if (!isInternalCommand(process.argv.slice(2)[0])) { if (!isInternalCommand(process.argv.slice(2)[0])) {
await externalCommand(cli, await resolveDir('.')); await externalCommand(cli);
} }
if (!process.argv.slice(2).length) { if (!process.argv.slice(2).length) {

View file

@ -38,14 +38,16 @@ export type BuildCLIOptions = Pick<
}; };
export async function build( export async function build(
siteDir: string, siteDirParam: string = '.',
cliOptions: Partial<BuildCLIOptions>, cliOptions: Partial<BuildCLIOptions> = {},
// When running build, we force terminate the process to prevent async // When running build, we force terminate the process to prevent async
// operations from never returning. However, if run as part of docusaurus // operations from never returning. However, if run as part of docusaurus
// deploy, we have to let deploy finish. // deploy, we have to let deploy finish.
// See https://github.com/facebook/docusaurus/pull/2496 // See https://github.com/facebook/docusaurus/pull/2496
forceTerminate: boolean = true, forceTerminate: boolean = true,
): Promise<string> { ): Promise<string> {
const siteDir = await fs.realpath(siteDirParam);
['SIGINT', 'SIGTERM'].forEach((sig) => { ['SIGINT', 'SIGTERM'].forEach((sig) => {
process.on(sig, () => process.exit()); process.on(sig, () => process.exit());
}); });

View file

@ -26,7 +26,9 @@ async function removePath(entry: {path: string; description: string}) {
} }
} }
export async function clear(siteDir: string): Promise<void> { export async function clear(siteDirParam: string = '.'): Promise<void> {
const siteDir = await fs.realpath(siteDirParam);
const generatedFolder = { const generatedFolder = {
path: path.join(siteDir, GENERATED_FILES_DIR_NAME), path: path.join(siteDir, GENERATED_FILES_DIR_NAME),
description: 'generated folder', description: 'generated folder',

View file

@ -41,9 +41,11 @@ function shellExecLog(cmd: string) {
} }
export async function deploy( export async function deploy(
siteDir: string, siteDirParam: string = '.',
cliOptions: Partial<DeployCLIOptions>, cliOptions: Partial<DeployCLIOptions> = {},
): Promise<void> { ): Promise<void> {
const siteDir = await fs.realpath(siteDirParam);
const {outDir, siteConfig, siteConfigPath} = await loadContext({ const {outDir, siteConfig, siteConfigPath} = await loadContext({
siteDir, siteDir,
config: cliOptions.config, config: cliOptions.config,

View file

@ -5,14 +5,13 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import fs from 'fs-extra';
import {loadContext} from '../server'; import {loadContext} from '../server';
import {initPlugins} from '../server/plugins/init'; import {initPlugins} from '../server/plugins/init';
import type {CommanderStatic} from 'commander'; import type {CommanderStatic} from 'commander';
export async function externalCommand( export async function externalCommand(cli: CommanderStatic): Promise<void> {
cli: CommanderStatic, const siteDir = await fs.realpath('.');
siteDir: string,
): Promise<void> {
const context = await loadContext({siteDir}); const context = await loadContext({siteDir});
const plugins = await initPlugins(context); const plugins = await initPlugins(context);

View file

@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import fs from 'fs-extra';
import http from 'http'; import http from 'http';
import path from 'path'; import path from 'path';
import logger from '@docusaurus/logger'; import logger from '@docusaurus/logger';
@ -24,9 +25,11 @@ export type ServeCLIOptions = HostPortOptions &
}; };
export async function serve( export async function serve(
siteDir: string, siteDirParam: string = '.',
cliOptions: Partial<ServeCLIOptions>, cliOptions: Partial<ServeCLIOptions> = {},
): Promise<void> { ): Promise<void> {
const siteDir = await fs.realpath(siteDirParam);
const buildDir = cliOptions.dir ?? DEFAULT_BUILD_DIR_NAME; const buildDir = cliOptions.dir ?? DEFAULT_BUILD_DIR_NAME;
let dir = path.resolve(siteDir, buildDir); let dir = path.resolve(siteDir, buildDir);

View file

@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import fs from 'fs-extra';
import path from 'path'; import path from 'path';
import _ from 'lodash'; import _ from 'lodash';
import logger from '@docusaurus/logger'; import logger from '@docusaurus/logger';
@ -35,9 +36,11 @@ export type StartCLIOptions = HostPortOptions &
}; };
export async function start( export async function start(
siteDir: string, siteDirParam: string = '.',
cliOptions: Partial<StartCLIOptions>, cliOptions: Partial<StartCLIOptions> = {},
): Promise<void> { ): Promise<void> {
const siteDir = await fs.realpath(siteDirParam);
process.env.NODE_ENV = 'development'; process.env.NODE_ENV = 'development';
process.env.BABEL_ENV = 'development'; process.env.BABEL_ENV = 'development';
logger.info('Starting the development server...'); logger.info('Starting the development server...');

View file

@ -111,7 +111,7 @@ async function createTestSite() {
component: string; component: string;
typescript?: boolean; typescript?: boolean;
}) { }) {
return swizzleWithExit(siteDir, FixtureThemeName, component, { return swizzleWithExit(FixtureThemeName, component, siteDir, {
wrap: true, wrap: true,
danger: true, danger: true,
typescript, typescript,
@ -125,7 +125,7 @@ async function createTestSite() {
component: string; component: string;
typescript?: boolean; typescript?: boolean;
}) { }) {
return swizzleWithExit(siteDir, FixtureThemeName, component, { return swizzleWithExit(FixtureThemeName, component, siteDir, {
eject: true, eject: true,
danger: true, danger: true,
typescript, typescript,

View file

@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import fs from 'fs-extra';
import logger from '@docusaurus/logger'; import logger from '@docusaurus/logger';
import {getThemeName, getThemePath, getThemeNames} from './themes'; import {getThemeName, getThemePath, getThemeNames} from './themes';
import {getThemeComponents, getComponentName} from './components'; import {getThemeComponents, getComponentName} from './components';
@ -87,11 +88,13 @@ If you want to swizzle it, use the code=${'--danger'} flag, or confirm that you
} }
export async function swizzle( export async function swizzle(
siteDir: string, themeNameParam: string | undefined = undefined,
themeNameParam: string | undefined, componentNameParam: string | undefined = undefined,
componentNameParam: string | undefined, siteDirParam: string = '.',
optionsParam: Partial<SwizzleCLIOptions>, optionsParam: Partial<SwizzleCLIOptions> = {},
): Promise<void> { ): Promise<void> {
const siteDir = await fs.realpath(siteDirParam);
const options = normalizeOptions(optionsParam); const options = normalizeOptions(optionsParam);
const {list, danger, typescript} = options; const {list, danger, typescript} = options;

View file

@ -41,10 +41,12 @@ async function getPathsToWatch(siteDir: string): Promise<string[]> {
} }
export async function writeHeadingIds( export async function writeHeadingIds(
siteDir: string, siteDirParam: string = '.',
files: string[] | undefined, files: string[] = [],
options: WriteHeadingIDOptions, options: WriteHeadingIDOptions = {},
): Promise<void> { ): Promise<void> {
const siteDir = await fs.realpath(siteDirParam);
const markdownFiles = await safeGlobby( const markdownFiles = await safeGlobby(
files ?? (await getPathsToWatch(siteDir)), files ?? (await getPathsToWatch(siteDir)),
{ {

View file

@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import fs from 'fs-extra';
import path from 'path'; import path from 'path';
import {loadContext, type LoadContextOptions} from '../server'; import {loadContext, type LoadContextOptions} from '../server';
import {initPlugins} from '../server/plugins/init'; import {initPlugins} from '../server/plugins/init';
@ -75,9 +76,11 @@ async function writePluginTranslationFiles({
} }
export async function writeTranslations( export async function writeTranslations(
siteDir: string, siteDirParam: string = '.',
options: Partial<WriteTranslationsCLIOptions>, options: Partial<WriteTranslationsCLIOptions> = {},
): Promise<void> { ): Promise<void> {
const siteDir = await fs.realpath(siteDirParam);
const context = await loadContext({ const context = await loadContext({
siteDir, siteDir,
config: options.config, config: options.config,