From fe162a35645eaf318aba13ac3c63356312f6a866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E5=90=91=E5=A4=9C?= Date: Fri, 5 Jul 2024 19:10:35 +0800 Subject: [PATCH] refactor(cli): divide all commands - Remove experimental warning prevention - Split all commands into separate files --- packages/cli/src/commands/build.ts | 73 ++++++++ packages/cli/src/commands/clean.ts | 37 ++++ packages/cli/src/commands/dev.ts | 55 ++++++ packages/cli/src/commands/preview.ts | 50 +++++ packages/cli/src/commands/watch.ts | 75 ++++++++ packages/cli/src/index.ts | 267 +-------------------------- packages/cli/src/utils.ts | 11 -- 7 files changed, 295 insertions(+), 273 deletions(-) create mode 100644 packages/cli/src/commands/build.ts create mode 100644 packages/cli/src/commands/clean.ts create mode 100644 packages/cli/src/commands/dev.ts create mode 100644 packages/cli/src/commands/preview.ts create mode 100644 packages/cli/src/commands/watch.ts diff --git a/packages/cli/src/commands/build.ts b/packages/cli/src/commands/build.ts new file mode 100644 index 000000000..bdc05fcdb --- /dev/null +++ b/packages/cli/src/commands/build.ts @@ -0,0 +1,73 @@ +import { defineCommand } from 'citty'; +import { getOptionFromBuildOption } from '../config.js'; +import { + FarmCLIBuildOptions, + FarmCLICommonOptions, + NormalizedFarmCLIBuildOptions +} from '../types.js'; +import { + handleAsyncOperationErrors, + resolveCliConfig, + resolveCore +} from '../utils.js'; + +export default defineCommand({ + meta: { + name: 'build', + description: 'compile the project in production mode' + }, + args: { + root: { + type: 'positional', + description: 'root path', + required: false, + valueHint: 'path' + }, + outDir: { + type: 'string', + alias: 'o', + description: 'output directory' + }, + input: { + type: 'string', + alias: 'i', + description: 'input file path' + }, + watch: { type: 'boolean', alias: 'w', description: 'watch file change' }, + target: { + type: 'string', + description: 'transpile targetEnv node, browser' + }, + format: { + type: 'string', + description: 'transpile format esm, commonjs' + }, + sourcemap: { + type: 'boolean', + description: 'output source maps for build' + }, + treeShaking: { + type: 'boolean', + description: 'Eliminate useless code without side effects' + }, + minify: { + type: 'boolean', + description: 'code compression at build time' + } + }, + async run({ args }: { args: FarmCLICommonOptions & FarmCLIBuildOptions }) { + const { root, configPath } = resolveCliConfig( + args.root, + args.config ?? args.c + ); + + const defaultOptions = { + root, + configPath, + ...getOptionFromBuildOption(args as NormalizedFarmCLIBuildOptions) + }; + + const { build } = await resolveCore(); + handleAsyncOperationErrors(build(defaultOptions), 'error during build'); + } +}); diff --git a/packages/cli/src/commands/clean.ts b/packages/cli/src/commands/clean.ts new file mode 100644 index 000000000..87d8893c4 --- /dev/null +++ b/packages/cli/src/commands/clean.ts @@ -0,0 +1,37 @@ +import { defineCommand } from 'citty'; +import { FarmCLICommonOptions, ICleanOptions } from '../types.js'; +import { resolveCliConfig, resolveCore } from '../utils.js'; + +export default defineCommand({ + meta: { + name: 'clean', + description: 'Clean up the cache built incrementally' + }, + args: { + root: { + type: 'positional', + description: 'root path', + required: false, + valueHint: 'path' + }, + recursive: { + type: 'boolean', + alias: 'r', + description: + 'Recursively search for node_modules directories and clean them' + } + }, + async run({ args }: { args: FarmCLICommonOptions & ICleanOptions }) { + const { root } = resolveCliConfig(args.root, args.config); + + const { clean } = await resolveCore(); + try { + await clean(root, args.recursive); + } catch (e) { + const { Logger } = await import('@farmfe/core'); + const logger = new Logger(); + logger.error(`Failed to clean cache: \n ${e.stack}`); + process.exit(1); + } + } +}); diff --git a/packages/cli/src/commands/dev.ts b/packages/cli/src/commands/dev.ts new file mode 100644 index 000000000..047060d9e --- /dev/null +++ b/packages/cli/src/commands/dev.ts @@ -0,0 +1,55 @@ +import { defineCommand } from 'citty'; +import { + FarmCLICommonOptions, + FarmCLIServerOptions, + GlobalFarmCLIOptions +} from '../types.js'; +import { + handleAsyncOperationErrors, + resolveCliConfig, + resolveCommandOptions, + resolveCore +} from '../utils.js'; + +export default defineCommand({ + meta: { + name: 'dev', + description: + 'Compile the project in dev mode and serve it with farm dev server' + }, + args: { + root: { + type: 'positional', + description: 'root path', + required: false, + valueHint: 'path' + }, + lazy: { type: 'boolean', alias: 'l', description: 'lazyCompilation' }, + host: { type: 'string', description: 'specify host' }, + port: { type: 'string', description: 'specify port' }, + open: { type: 'boolean', description: 'open browser on server start' }, + hmr: { type: 'boolean', description: 'enable hot module replacement' }, + cors: { type: 'boolean', description: 'enable cors' }, + strictPort: { + type: 'boolean', + description: 'specified port is already in use, exit with error' + } + }, + async run({ args }: { args: FarmCLICommonOptions & FarmCLIServerOptions }) { + const { root, configPath } = resolveCliConfig(args.root, args.config); + + const resolvedOptions = resolveCommandOptions(args as GlobalFarmCLIOptions); + const defaultOptions = { + root, + compilation: { + lazyCompilation: args.lazy + }, + server: resolvedOptions, + clearScreen: args.clearScreen, + configPath, + mode: args.mode + }; + const { start } = await resolveCore(); + handleAsyncOperationErrors(start(defaultOptions), 'Failed to start server'); + } +}); diff --git a/packages/cli/src/commands/preview.ts b/packages/cli/src/commands/preview.ts new file mode 100644 index 000000000..3852dc221 --- /dev/null +++ b/packages/cli/src/commands/preview.ts @@ -0,0 +1,50 @@ +import { defineCommand } from 'citty'; +import { + FarmCLICommonOptions, + FarmCLIPreviewOptions, + GlobalFarmCLIOptions +} from '../types.js'; +import { + handleAsyncOperationErrors, + resolveCliConfig, + resolveCommandOptions, + resolveCore +} from '../utils.js'; + +export default defineCommand({ + meta: { + name: 'preview', + description: 'compile the project in watch mode' + }, + args: { + root: { + type: 'positional', + description: 'root path', + required: false, + valueHint: 'path' + }, + port: { type: 'string', description: 'specify port' }, + open: { + type: 'boolean', + description: 'open browser on server preview start' + } + }, + async run({ args }: { args: FarmCLICommonOptions & FarmCLIPreviewOptions }) { + const { root, configPath } = resolveCliConfig(args.root, args.config); + + const resolvedOptions = resolveCommandOptions(args as GlobalFarmCLIOptions); + const defaultOptions = { + root, + mode: args.mode, + server: resolvedOptions, + configPath, + port: resolvedOptions.port + }; + + const { preview } = await resolveCore(); + handleAsyncOperationErrors( + preview(defaultOptions), + 'Failed to start preview server' + ); + } +}); diff --git a/packages/cli/src/commands/watch.ts b/packages/cli/src/commands/watch.ts new file mode 100644 index 000000000..e378c196e --- /dev/null +++ b/packages/cli/src/commands/watch.ts @@ -0,0 +1,75 @@ +import { defineCommand } from 'citty'; +import { getOptionFromBuildOption } from '../config.js'; +import { + FarmCLIBuildOptions, + GlobalFarmCLIOptions, + NormalizedFarmCLIBuildOptions +} from '../types.js'; +import { + handleAsyncOperationErrors, + resolveCliConfig, + resolveCore +} from '../utils.js'; + +export default defineCommand({ + meta: { + name: 'watch', + description: 'watch file change' + }, + args: { + root: { + type: 'positional', + description: 'root path', + required: false, + valueHint: 'path' + }, + outDir: { + type: 'string', + alias: 'o', + description: 'output directory' + }, + input: { + type: 'string', + alias: 'i', + description: 'input file path' + }, + target: { + type: 'string', + description: 'transpile targetEnv node, browser' + }, + format: { + type: 'string', + description: 'transpile format esm, commonjs' + }, + sourcemap: { + type: 'boolean', + description: 'output source maps for build' + }, + treeShaking: { + type: 'boolean', + description: 'Eliminate useless code without side effects' + }, + minify: { + type: 'boolean', + description: 'code compression at build time' + } + }, + async run({ args }: { args: FarmCLIBuildOptions & GlobalFarmCLIOptions }) { + const { root, configPath } = resolveCliConfig( + args.root, + args.config ?? args.c + ); + + const defaultOptions = { + root, + configPath, + ...getOptionFromBuildOption(args as NormalizedFarmCLIBuildOptions) + }; + + const { watch } = await resolveCore(); + handleAsyncOperationErrors( + watch(defaultOptions), + 'error during watch project' + ); + } +}); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index a89f4a549..4118c7432 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,268 +1,17 @@ import { readFileSync } from 'node:fs'; import { defineCommand, runMain } from 'citty'; -import { getOptionFromBuildOption } from './config.js'; -import { - handleAsyncOperationErrors, - preventExperimentalWarning, - resolveCliConfig, - resolveCommandOptions, - resolveCore -} from './utils.js'; -import type { - FarmCLIBuildOptions, - FarmCLICommonOptions, - FarmCLIPreviewOptions, - FarmCLIServerOptions, - GlobalFarmCLIOptions, - ICleanOptions, - NormalizedFarmCLIBuildOptions -} from './types.js'; +import buildCommand from './commands/build.js'; +import cleanCommand from './commands/clean.js'; +import devCommand from './commands/dev.js'; +import previewCommand from './commands/preview.js'; +import watchCommand from './commands/watch.js'; const { version } = JSON.parse( readFileSync(new URL('../package.json', import.meta.url)).toString() ); -const devCommand = defineCommand({ - meta: { - name: 'dev', - description: - 'Compile the project in dev mode and serve it with farm dev server' - }, - args: { - root: { - type: 'positional', - description: 'root path', - required: false, - valueHint: 'path' - }, - lazy: { type: 'boolean', alias: 'l', description: 'lazyCompilation' }, - host: { type: 'string', description: 'specify host' }, - port: { type: 'string', description: 'specify port' }, - open: { type: 'boolean', description: 'open browser on server start' }, - hmr: { type: 'boolean', description: 'enable hot module replacement' }, - cors: { type: 'boolean', description: 'enable cors' }, - strictPort: { - type: 'boolean', - description: 'specified port is already in use, exit with error' - } - }, - async run({ args }: { args: FarmCLICommonOptions & FarmCLIServerOptions }) { - const { root, configPath } = resolveCliConfig(args.root, args.config); - - const resolvedOptions = resolveCommandOptions(args as GlobalFarmCLIOptions); - const defaultOptions = { - root, - compilation: { - lazyCompilation: args.lazy - }, - server: resolvedOptions, - clearScreen: args.clearScreen, - configPath, - mode: args.mode - }; - const { start } = await resolveCore(); - handleAsyncOperationErrors(start(defaultOptions), 'Failed to start server'); - } -}); - -const buildCommand = defineCommand({ - meta: { - name: 'build', - description: 'compile the project in production mode' - }, - args: { - root: { - type: 'positional', - description: 'root path', - required: false, - valueHint: 'path' - }, - outDir: { - type: 'string', - alias: 'o', - description: 'output directory' - }, - input: { - type: 'string', - alias: 'i', - description: 'input file path' - }, - watch: { type: 'boolean', alias: 'w', description: 'watch file change' }, - target: { - type: 'string', - description: 'transpile targetEnv node, browser' - }, - format: { - type: 'string', - description: 'transpile format esm, commonjs' - }, - sourcemap: { - type: 'boolean', - description: 'output source maps for build' - }, - treeShaking: { - type: 'boolean', - description: 'Eliminate useless code without side effects' - }, - minify: { - type: 'boolean', - description: 'code compression at build time' - } - }, - async run({ args }: { args: FarmCLICommonOptions & FarmCLIBuildOptions }) { - const { root, configPath } = resolveCliConfig( - args.root, - args.config ?? args.c - ); - - const defaultOptions = { - root, - configPath, - ...getOptionFromBuildOption(args as NormalizedFarmCLIBuildOptions) - }; - - const { build } = await resolveCore(); - handleAsyncOperationErrors(build(defaultOptions), 'error during build'); - } -}); - -const watchCommand = defineCommand({ - meta: { - name: 'watch', - description: 'watch file change' - }, - args: { - root: { - type: 'positional', - description: 'root path', - required: false, - valueHint: 'path' - }, - outDir: { - type: 'string', - alias: 'o', - description: 'output directory' - }, - input: { - type: 'string', - alias: 'i', - description: 'input file path' - }, - target: { - type: 'string', - description: 'transpile targetEnv node, browser' - }, - format: { - type: 'string', - description: 'transpile format esm, commonjs' - }, - sourcemap: { - type: 'boolean', - description: 'output source maps for build' - }, - treeShaking: { - type: 'boolean', - description: 'Eliminate useless code without side effects' - }, - minify: { - type: 'boolean', - description: 'code compression at build time' - } - }, - async run({ args }: { args: FarmCLIBuildOptions & GlobalFarmCLIOptions }) { - const { root, configPath } = resolveCliConfig( - args.root, - args.config ?? args.c - ); - - const defaultOptions = { - root, - configPath, - ...getOptionFromBuildOption(args as NormalizedFarmCLIBuildOptions) - }; - - const { watch } = await resolveCore(); - handleAsyncOperationErrors( - watch(defaultOptions), - 'error during watch project' - ); - } -}); - -const previewCommand = defineCommand({ - meta: { - name: 'preview', - description: 'compile the project in watch mode' - }, - args: { - root: { - type: 'positional', - description: 'root path', - required: false, - valueHint: 'path' - }, - port: { type: 'string', description: 'specify port' }, - open: { - type: 'boolean', - description: 'open browser on server preview start' - } - }, - async run({ args }: { args: FarmCLICommonOptions & FarmCLIPreviewOptions }) { - const { root, configPath } = resolveCliConfig(args.root, args.config); - - const resolvedOptions = resolveCommandOptions(args as GlobalFarmCLIOptions); - const defaultOptions = { - root, - mode: args.mode, - server: resolvedOptions, - configPath, - port: resolvedOptions.port - }; - - const { preview } = await resolveCore(); - handleAsyncOperationErrors( - preview(defaultOptions), - 'Failed to start preview server' - ); - } -}); - -const cleanCommand = defineCommand({ - meta: { - name: 'clean', - description: 'Clean up the cache built incrementally' - }, - args: { - root: { - type: 'positional', - description: 'root path', - required: false, - valueHint: 'path' - }, - recursive: { - type: 'boolean', - alias: 'r', - description: - 'Recursively search for node_modules directories and clean them' - } - }, - async run({ args }: { args: FarmCLICommonOptions & ICleanOptions }) { - const { root } = resolveCliConfig(args.root, args.config); - - const { clean } = await resolveCore(); - try { - await clean(root, args.recursive); - } catch (e) { - const { Logger } = await import('@farmfe/core'); - const logger = new Logger(); - logger.error(`Failed to clean cache: \n ${e.stack}`); - process.exit(1); - } - } -}); - const main = defineCommand({ meta: { name: 'farm', @@ -293,12 +42,6 @@ const main = defineCommand({ } }); -// warning::: use mdn browser compatibility data with experimental warning in terminal so prevent experimental warning -// we don't use it in `@farmfe/core` package because -// we need to prevent it in cli package but we don't prevent it in core package -// We only keep the original code environment. -preventExperimentalWarning(); - // default to start a development server if (process.argv.slice(2).length === 0) runMain(main, { rawArgs: process.argv.slice(2).concat(['dev']) }); diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index 73652f5e6..64f88cd36 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -164,17 +164,6 @@ export async function handleAsyncOperationErrors( } } -// prevent node experimental warning -export function preventExperimentalWarning() { - const defaultEmit = process.emit; - process.emit = function (...args: any[]) { - if (args[1].name === 'ExperimentalWarning') { - return undefined; - } - return defaultEmit.call(this, ...args); - }; -} - export function resolveRootPath(rootPath = '') { return rootPath && path.isAbsolute(rootPath) ? rootPath