refactor(core): collocate CLI commands and their option types (#7292)

* refactor(core): collocate CLI commands and their option types

* fixes
This commit is contained in:
Joshua Chen 2022-05-03 15:32:12 +08:00 committed by GitHub
parent 5bed55aeda
commit c7a5af7c4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 92 additions and 100 deletions

View file

@ -410,37 +410,6 @@ export type DocusaurusContext = {
// isBrowser: boolean; // Not here on purpose!
};
export type HostPortCLIOptions = {
host?: string;
port?: string;
};
export type ConfigOptions = {
config: string;
};
export type StartCLIOptions = HostPortCLIOptions &
ConfigOptions & {
hotOnly: boolean;
open: boolean;
poll: boolean | number;
locale?: string;
};
export type ServeCLIOptions = HostPortCLIOptions &
ConfigOptions & {
dir: string;
build: boolean;
};
export type BuildCLIOptions = ConfigOptions & {
bundleAnalyzer: boolean;
outDir: string;
minify: boolean;
skipBuild: boolean;
locale?: string;
};
export type LoadContext = {
siteDir: string;
generatedFilesDir: string;

View file

@ -13,10 +13,10 @@ import ReactLoadableSSRAddon from 'react-loadable-ssr-addon-v5-slorber';
import type {Configuration} from 'webpack';
import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer';
import merge from 'webpack-merge';
import {load, loadContext} from '../server';
import {load, loadContext, type LoadContextOptions} from '../server';
import {handleBrokenLinks} from '../server/brokenLinks';
import type {BuildCLIOptions, Props} from '@docusaurus/types';
import type {Props} from '@docusaurus/types';
import createClientConfig from '../webpack/client';
import createServerConfig from '../webpack/server';
import {
@ -29,6 +29,14 @@ import {loadI18n} from '../server/i18n';
import {mapAsyncSequential} from '@docusaurus/utils';
import type {HelmetServerState} from 'react-helmet-async';
export type BuildCLIOptions = Pick<
LoadContextOptions,
'config' | 'locale' | 'outDir'
> & {
bundleAnalyzer?: boolean;
minify?: boolean;
};
export async function build(
siteDir: string,
cliOptions: Partial<BuildCLIOptions>,
@ -64,8 +72,8 @@ export async function build(
}
const context = await loadContext({
siteDir,
customOutDir: cliOptions.outDir,
customConfigFilePath: cliOptions.config,
outDir: cliOptions.outDir,
config: cliOptions.config,
locale: cliOptions.locale,
localizePath: cliOptions.locale ? false : undefined,
});
@ -113,8 +121,8 @@ async function buildLocale({
const props: Props = await load({
siteDir,
customOutDir: cliOptions.outDir,
customConfigFilePath: cliOptions.config,
outDir: cliOptions.outDir,
config: cliOptions.config,
locale,
localizePath: cliOptions.locale ? false : undefined,
});

View file

@ -1,24 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {choosePort} from '../server/choosePort';
import type {HostPortCLIOptions} from '@docusaurus/types';
import {DEFAULT_PORT} from '@docusaurus/utils';
export function getCLIOptionHost(
hostOption: HostPortCLIOptions['host'],
): string {
return hostOption ?? 'localhost';
}
export async function getCLIOptionPort(
portOption: HostPortCLIOptions['port'],
host: string,
): Promise<number | null> {
const basePort = portOption ? parseInt(portOption, 10) : DEFAULT_PORT;
return choosePort(host, basePort);
}

View file

@ -9,12 +9,18 @@ import fs from 'fs-extra';
import shell from 'shelljs';
import logger from '@docusaurus/logger';
import {hasSSHProtocol, buildSshUrl, buildHttpsUrl} from '@docusaurus/utils';
import {loadContext} from '../server';
import {loadContext, type LoadContextOptions} from '../server';
import {build} from './build';
import type {BuildCLIOptions} from '@docusaurus/types';
import path from 'path';
import os from 'os';
export type DeployCLIOptions = Pick<
LoadContextOptions,
'config' | 'locale' | 'outDir'
> & {
skipBuild?: boolean;
};
// GIT_PASS env variable should not appear in logs
function obfuscateGitPass(str: string) {
const gitPass = process.env.GIT_PASS;
@ -36,12 +42,12 @@ function shellExecLog(cmd: string) {
export async function deploy(
siteDir: string,
cliOptions: Partial<BuildCLIOptions>,
cliOptions: Partial<DeployCLIOptions>,
): Promise<void> {
const {outDir, siteConfig, siteConfigPath} = await loadContext({
siteDir,
customConfigFilePath: cliOptions.config,
customOutDir: cliOptions.outDir,
config: cliOptions.config,
outDir: cliOptions.outDir,
});
if (typeof siteConfig.trailingSlash === 'undefined') {

View file

@ -9,11 +9,17 @@ import http from 'http';
import serveHandler from 'serve-handler';
import logger from '@docusaurus/logger';
import path from 'path';
import type {LoadContextOptions} from '../server';
import {loadSiteConfig} from '../server/config';
import {build} from './build';
import {getCLIOptionHost, getCLIOptionPort} from './commandUtils';
import {getHostPort, type HostPortOptions} from '../server/getHostPort';
import {DEFAULT_BUILD_DIR_NAME} from '@docusaurus/utils';
import type {ServeCLIOptions} from '@docusaurus/types';
export type ServeCLIOptions = HostPortOptions &
Pick<LoadContextOptions, 'config'> & {
dir?: string;
build?: boolean;
};
export async function serve(
siteDir: string,
@ -33,8 +39,7 @@ export async function serve(
);
}
const host: string = getCLIOptionHost(cliOptions.host);
const port: number | null = await getCLIOptionPort(cliOptions.port, host);
const {host, port} = await getHostPort(cliOptions);
if (port === null) {
process.exit();

View file

@ -17,17 +17,23 @@ import evalSourceMapMiddleware from 'react-dev-utils/evalSourceMapMiddleware';
import webpack from 'webpack';
import WebpackDevServer from 'webpack-dev-server';
import merge from 'webpack-merge';
import {load} from '../server';
import type {StartCLIOptions} from '@docusaurus/types';
import {load, type LoadContextOptions} from '../server';
import createClientConfig from '../webpack/client';
import {
applyConfigureWebpack,
applyConfigurePostCss,
getHttpsConfig,
} from '../webpack/utils';
import {getCLIOptionHost, getCLIOptionPort} from './commandUtils';
import {getHostPort, type HostPortOptions} from '../server/getHostPort';
import {getTranslationsLocaleDirPath} from '../server/translations/translations';
export type StartCLIOptions = HostPortOptions &
Pick<LoadContextOptions, 'locale' | 'config'> & {
hotOnly?: boolean;
open?: boolean;
poll?: boolean | number;
};
export async function start(
siteDir: string,
cliOptions: Partial<StartCLIOptions>,
@ -39,7 +45,7 @@ export async function start(
function loadSite() {
return load({
siteDir,
customConfigFilePath: cliOptions.config,
config: cliOptions.config,
locale: cliOptions.locale,
localizePath: undefined, // Should this be configurable?
});
@ -50,8 +56,7 @@ export async function start(
const protocol: string = process.env.HTTPS === 'true' ? 'https' : 'http';
const host: string = getCLIOptionHost(cliOptions.host);
const port: number | null = await getCLIOptionPort(cliOptions.port, host);
const {host, port} = await getHostPort(cliOptions);
if (port === null) {
process.exit();

View file

@ -11,14 +11,14 @@ import path from 'path';
import _ from 'lodash';
import {Globby, posixPath, THEME_PATH} from '@docusaurus/utils';
import type {SwizzleAction, SwizzleComponentConfig} from '@docusaurus/types';
import type {SwizzleOptions} from './common';
import type {SwizzleCLIOptions} from './common';
import {askSwizzleAction} from './prompts';
export const SwizzleActions: SwizzleAction[] = ['wrap', 'eject'];
export async function getAction(
componentConfig: SwizzleComponentConfig,
options: Pick<SwizzleOptions, 'wrap' | 'eject'>,
options: Pick<SwizzleCLIOptions, 'wrap' | 'eject'>,
): Promise<SwizzleAction> {
if (options.wrap) {
return 'wrap';

View file

@ -61,7 +61,7 @@ export type SwizzlePlugin = {
export type SwizzleContext = {plugins: SwizzlePlugin[]};
export type SwizzleOptions = {
export type SwizzleCLIOptions = {
typescript: boolean;
danger: boolean;
list: boolean;
@ -70,8 +70,8 @@ export type SwizzleOptions = {
};
export function normalizeOptions(
options: Partial<SwizzleOptions>,
): SwizzleOptions {
options: Partial<SwizzleCLIOptions>,
): SwizzleCLIOptions {
return {
typescript: options.typescript ?? false,
danger: options.danger ?? false,

View file

@ -10,7 +10,7 @@ import {getThemeName, getThemePath, getThemeNames} from './themes';
import {getThemeComponents, getComponentName} from './components';
import {helpTables, themeComponentsTable} from './tables';
import type {SwizzleAction, SwizzleComponentConfig} from '@docusaurus/types';
import type {SwizzleOptions, SwizzlePlugin} from './common';
import type {SwizzleCLIOptions, SwizzlePlugin} from './common';
import {normalizeOptions} from './common';
import type {ActionResult} from './actions';
import {eject, getAction, wrap} from './actions';
@ -25,7 +25,7 @@ async function listAllThemeComponents({
}: {
themeNames: string[];
plugins: SwizzlePlugin[];
typescript: SwizzleOptions['typescript'];
typescript: SwizzleCLIOptions['typescript'];
}) {
const themeComponentsTables = (
await Promise.all(
@ -90,7 +90,7 @@ export async function swizzle(
siteDir: string,
themeNameParam: string | undefined,
componentNameParam: string | undefined,
optionsParam: Partial<SwizzleOptions>,
optionsParam: Partial<SwizzleCLIOptions>,
): Promise<void> {
const options = normalizeOptions(optionsParam);
const {list, danger, typescript} = options;

View file

@ -5,9 +5,9 @@
* LICENSE file in the root directory of this source tree.
*/
import type {ConfigOptions, InitializedPlugin} from '@docusaurus/types';
import type {InitializedPlugin} from '@docusaurus/types';
import path from 'path';
import {loadContext} from '../server';
import {loadContext, type LoadContextOptions} from '../server';
import {initPlugins} from '../server/plugins/init';
import {
@ -23,6 +23,12 @@ import {
} from '../server/translations/translationsExtractor';
import {getCustomBabelConfigFilePath, getBabelOptions} from '../webpack/utils';
export type WriteTranslationsCLIOptions = Pick<
LoadContextOptions,
'config' | 'locale'
> &
WriteTranslationsOptions;
/**
* This is a hack, so that @docusaurus/theme-common translations are extracted!
* A theme doesn't have a way to express that one of its dependency (like
@ -74,13 +80,11 @@ async function writePluginTranslationFiles({
export async function writeTranslations(
siteDir: string,
options: Partial<
WriteTranslationsOptions & ConfigOptions & {locale?: string}
>,
options: Partial<WriteTranslationsCLIOptions>,
): Promise<void> {
const context = await loadContext({
siteDir,
customConfigFilePath: options.config,
config: options.config,
locale: options.locale,
});
const plugins = await initPlugins(context);

View file

@ -8,6 +8,7 @@
import {execSync, type ExecSyncOptionsWithStringEncoding} from 'child_process';
import detect from 'detect-port';
import logger from '@docusaurus/logger';
import {DEFAULT_PORT} from '@docusaurus/utils';
import prompts from 'prompts';
const execOptions: ExecSyncOptionsWithStringEncoding = {
@ -48,7 +49,7 @@ function getProcessForPort(port: number): string | null {
* port is already being used. This feature was heavily inspired by
* create-react-app and uses many of the same utility functions to implement it.
*/
export async function choosePort(
async function choosePort(
host: string,
defaultPort: number,
): Promise<number | null> {
@ -85,3 +86,18 @@ Would you like to run the app on another port instead?`),
throw err;
}
}
export type HostPortOptions = {
host?: string;
port?: string;
};
export async function getHostPort(options: HostPortOptions): Promise<{
host: string;
port: number | null;
}> {
const host = options.host ?? 'localhost';
const basePort = options.port ? parseInt(options.port, 10) : DEFAULT_PORT;
const port = await choosePort(host, basePort);
return {host, port};
}

View file

@ -31,10 +31,10 @@ import type {DocusaurusConfig, LoadContext, Props} from '@docusaurus/types';
export type LoadContextOptions = {
/** Usually the CWD; can be overridden with command argument. */
siteDir: string;
/** Can be customized with `--out-dir` option */
customOutDir?: string;
/** Can be customized with `--config` option */
customConfigFilePath?: string;
/** Custom output directory. Can be customized with `--out-dir` option */
outDir?: string;
/** Custom config path. Can be customized with `--config` option */
config?: string;
/** Default is `i18n.defaultLocale` */
locale?: string;
/**
@ -55,7 +55,12 @@ export type LoadContextOptions = {
export async function loadContext(
options: LoadContextOptions,
): Promise<LoadContext> {
const {siteDir, customOutDir, locale, customConfigFilePath} = options;
const {
siteDir,
outDir: baseOutDir = DEFAULT_BUILD_DIR_NAME,
locale,
config: customConfigFilePath,
} = options;
const generatedFilesDir = path.resolve(siteDir, GENERATED_FILES_DIR_NAME);
const {siteConfig: initialSiteConfig, siteConfigPath} = await loadSiteConfig({
@ -72,7 +77,7 @@ export async function loadContext(
pathType: 'url',
});
const outDir = localizePath({
path: path.resolve(siteDir, customOutDir ?? DEFAULT_BUILD_DIR_NAME),
path: path.resolve(siteDir, baseOutDir),
i18n,
options,
pathType: 'fs',

View file

@ -37,9 +37,7 @@ describe('initPlugins', () => {
it('throws user-friendly error message for plugins with bad values', async () => {
await expect(() =>
loadSite({
customConfigFilePath: 'badPlugins.docusaurus.config.js',
}),
loadSite({config: 'badPlugins.docusaurus.config.js'}),
).rejects.toThrowErrorMatchingSnapshot();
});
});