diff --git a/packages/core/__mocks__/rslog.cjs b/packages/core/__mocks__/rslog.cjs index 2c9d1a237..c21fbe60e 100644 --- a/packages/core/__mocks__/rslog.cjs +++ b/packages/core/__mocks__/rslog.cjs @@ -2,5 +2,6 @@ module.exports = { logger: { warn: () => {}, override: () => {}, + debug: () => {}, }, }; diff --git a/packages/core/src/cli/commands.ts b/packages/core/src/cli/commands.ts index 0358abe26..6e8e89b68 100644 --- a/packages/core/src/cli/commands.ts +++ b/packages/core/src/cli/commands.ts @@ -1,9 +1,10 @@ import type { LogLevel, RsbuildMode } from '@rsbuild/core'; import cac, { type CAC } from 'cac'; import type { ConfigLoader } from '../config'; +import type { Format } from '../types/config'; import { logger } from '../utils/logger'; import { build } from './build'; -import { init } from './init'; +import { initConfig } from './initConfig'; import { inspect } from './inspect'; import { startMFDevServer } from './mf'; import { watchFilesForRestart } from './restart'; @@ -16,6 +17,19 @@ export type CommonOptions = { lib?: string[]; configLoader?: ConfigLoader; logLevel?: LogLevel; + format?: Format; + entry?: string[]; + distPath?: string; + bundle?: boolean; + syntax?: string; + target?: string; + dts?: boolean; + external?: string[]; + minify?: boolean; + clean?: boolean; + autoExtension?: boolean; + autoExternal?: boolean; + tsconfig?: string; }; export type BuildOptions = CommonOptions & { @@ -84,13 +98,52 @@ export function runCli(): void { buildCommand .option('-w, --watch', 'turn on watch mode, watch for changes and rebuild') + .option( + '--entry ', + 'set entry file or pattern (repeatable) (e.g. --entry="src/*" or --entry="main=src/main.ts")', + { + type: [String], + default: [], + }, + ) + .option('--dist-path ', 'set output directory') + .option('--bundle', 'enable bundle mode (use --no-bundle to disable)') + .option( + '--format ', + 'specify the output format (esm | cjs | umd | mf | iife)', + ) + .option( + '--syntax ', + 'set build syntax target (e.g. --syntax=es2018 or --syntax=["node 14", "Chrome 103"])', + ) + .option('--target ', 'set runtime target (web | node)') + .option('--dts', 'emit declaration files (use --no-dts to disable)') + .option('--external ', 'add package to externals (repeatable)', { + type: [String], + default: [], + }) + .option('--minify', 'minify output (use --no-minify to disable)') + .option( + '--clean', + 'clean dist directory before build (use --no-clean to disable)', + ) + .option( + '--auto-extension', + 'control automatic extension redirect (use --no-auto-extension to disable)', + ) + .option( + '--auto-external', + 'control automatic dependency externalization (use --no-auto-external to disable)', + ) + .option( + '--tsconfig ', + 'use specific tsconfig (relative to project root)', + ) .action(async (options: BuildOptions) => { try { const cliBuild = async () => { - const { config, watchFiles } = await init(options); - + const { config, watchFiles } = await initConfig(options); await build(config, options); - if (options.watch) { watchFilesForRestart(watchFiles, async () => { await cliBuild(); @@ -120,7 +173,7 @@ export function runCli(): void { .action(async (options: InspectOptions) => { try { // TODO: inspect should output Rslib's config - const { config } = await init(options); + const { config } = await initConfig(options); await inspect(config, { lib: options.lib, mode: options.mode, @@ -137,7 +190,7 @@ export function runCli(): void { mfDevCommand.action(async (options: CommonOptions) => { try { const cliMfDev = async () => { - const { config, watchFiles } = await init(options); + const { config, watchFiles } = await initConfig(options); await startMFDevServer(config, { lib: options.lib, }); diff --git a/packages/core/src/cli/init.ts b/packages/core/src/cli/init.ts deleted file mode 100644 index 2845675e1..000000000 --- a/packages/core/src/cli/init.ts +++ /dev/null @@ -1,56 +0,0 @@ -import path from 'node:path'; -import { loadEnv } from '@rsbuild/core'; -import { loadConfig } from '../config'; -import type { RslibConfig } from '../types'; -import { getAbsolutePath } from '../utils/helper'; -import type { CommonOptions } from './commands'; -import { onBeforeRestart } from './restart'; - -const getEnvDir = (cwd: string, envDir?: string) => { - if (envDir) { - return path.isAbsolute(envDir) ? envDir : path.resolve(cwd, envDir); - } - return cwd; -}; - -export async function init(options: CommonOptions): Promise<{ - config: RslibConfig; - configFilePath: string; - watchFiles: string[]; -}> { - const cwd = process.cwd(); - const root = options.root ? getAbsolutePath(cwd, options.root) : cwd; - const envs = loadEnv({ - cwd: getEnvDir(root, options.envDir), - mode: options.envMode, - }); - - onBeforeRestart(envs.cleanup); - - const { content: config, filePath: configFilePath } = await loadConfig({ - cwd: root, - path: options.config, - envMode: options.envMode, - loader: options.configLoader, - }); - - config.source ||= {}; - config.source.define = { - ...envs.publicVars, - ...config.source.define, - }; - - if (options.root) { - config.root = root; - } - - if (options.logLevel) { - config.logLevel = options.logLevel; - } - - return { - config, - configFilePath, - watchFiles: [configFilePath, ...envs.filePaths], - }; -} diff --git a/packages/core/src/cli/initConfig.ts b/packages/core/src/cli/initConfig.ts new file mode 100644 index 000000000..2b0c3d87a --- /dev/null +++ b/packages/core/src/cli/initConfig.ts @@ -0,0 +1,162 @@ +import path from 'node:path'; +import util from 'node:util'; +import { loadEnv, type RsbuildEntry } from '@rsbuild/core'; +import { loadConfig } from '../config'; +import type { + EcmaScriptVersion, + RsbuildConfigOutputTarget, + RslibConfig, + Syntax, +} from '../types'; +import { getAbsolutePath } from '../utils/helper'; +import { logger } from '../utils/logger'; +import type { CommonOptions } from './commands'; +import { onBeforeRestart } from './restart'; + +const getEnvDir = (cwd: string, envDir?: string) => { + if (envDir) { + return path.isAbsolute(envDir) ? envDir : path.resolve(cwd, envDir); + } + return cwd; +}; + +export const parseEntryOption = ( + entries?: string[], +): Record | undefined => { + if (!entries || entries.length === 0) { + return undefined; + } + + const parsed: Record = {}; + let unnamedIndex = 0; + + for (const rawEntry of entries) { + const value = rawEntry?.trim(); + if (!value) { + continue; + } + + const equalIndex = value.indexOf('='); + if (equalIndex > -1) { + const name = value.slice(0, equalIndex).trim(); + const entryPath = value.slice(equalIndex + 1).trim(); + if (name && entryPath) { + parsed[name] = entryPath; + continue; + } + } + + unnamedIndex += 1; + const key = unnamedIndex === 1 ? 'index' : `entry${unnamedIndex}`; + parsed[key] = value; + } + + return Object.keys(parsed).length === 0 ? undefined : parsed; +}; + +export const parseSyntaxOption = (syntax?: string): Syntax | undefined => { + if (!syntax) { + return undefined; + } + + const trimmed = syntax.trim(); + if (!trimmed) { + return undefined; + } + + if (trimmed.startsWith('[')) { + try { + const parsed = JSON.parse(trimmed); + if (Array.isArray(parsed)) { + return parsed; + } + } catch (e) { + const reason = e instanceof Error ? e.message : String(e); + throw new Error( + `Failed to parse --syntax option "${trimmed}" as JSON array: ${reason}`, + ); + } + } + + return trimmed as EcmaScriptVersion; +}; + +const applyCliOptions = ( + config: RslibConfig, + options: CommonOptions, + root: string, +): void => { + if (options.root) config.root = root; + if (options.logLevel) config.logLevel = options.logLevel; + + for (const lib of config.lib) { + if (options.format !== undefined) lib.format = options.format; + if (options.bundle !== undefined) lib.bundle = options.bundle; + if (options.dts !== undefined) lib.dts = options.dts; + if (options.autoExtension !== undefined) + lib.autoExtension = options.autoExtension; + if (options.autoExternal !== undefined) + lib.autoExternal = options.autoExternal; + if (options.tsconfig !== undefined) { + lib.source ||= {}; + lib.source.tsconfigPath = options.tsconfig; + } + const entry = parseEntryOption(options.entry); + if (entry !== undefined) { + lib.source ||= {}; + lib.source.entry = entry as RsbuildEntry; + } + const syntax = parseSyntaxOption(options.syntax); + if (syntax !== undefined) lib.syntax = syntax; + const output = lib.output ?? {}; + if (options.target !== undefined) + output.target = options.target as RsbuildConfigOutputTarget; + if (options.minify !== undefined) output.minify = options.minify; + if (options.clean !== undefined) output.cleanDistPath = options.clean; + const externals = options.external?.filter(Boolean) ?? []; + if (externals.length > 0) output.externals = externals; + if (options.distPath) { + output.distPath ??= {}; + output.distPath.root = options.distPath; + } + } +}; + +export async function initConfig(options: CommonOptions): Promise<{ + config: RslibConfig; + configFilePath: string; + watchFiles: string[]; +}> { + const cwd = process.cwd(); + const root = options.root ? getAbsolutePath(cwd, options.root) : cwd; + const envs = loadEnv({ + cwd: getEnvDir(root, options.envDir), + mode: options.envMode, + }); + + onBeforeRestart(envs.cleanup); + + const { content: config, filePath: configFilePath } = await loadConfig({ + cwd: root, + path: options.config, + envMode: options.envMode, + loader: options.configLoader, + }); + + config.source ||= {}; + config.source.define = { + ...envs.publicVars, + ...config.source.define, + }; + + applyCliOptions(config, options, root); + + logger.debug('Rslib config used to generate Rsbuild environments:'); + logger.debug(`\n${util.inspect(config, { depth: null, colors: true })}`); + + return { + config, + configFilePath, + watchFiles: [configFilePath, ...envs.filePaths], + }; +} diff --git a/packages/core/tests/cli.test.ts b/packages/core/tests/cli.test.ts new file mode 100644 index 000000000..741af4e2a --- /dev/null +++ b/packages/core/tests/cli.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, test } from '@rstest/core'; +import { parseEntryOption, parseSyntaxOption } from '../src/cli/initConfig'; + +describe('parseEntryOption', () => { + test('returns undefined when entries are missing or empty', () => { + expect(parseEntryOption()).toBeUndefined(); + expect(parseEntryOption([])).toBeUndefined(); + expect(parseEntryOption(['', ' '])).toBeUndefined(); + }); + + test('parses named and positional entries with trimming', () => { + const result = parseEntryOption([ + ' main = ./src/main.ts ', + ' ./src/utils.ts ', + 'entry=./src/entry.ts', + './src/extra.ts', + ]); + + expect(result).toEqual({ + main: './src/main.ts', + index: './src/utils.ts', + entry: './src/entry.ts', + entry2: './src/extra.ts', + }); + }); +}); + +describe('parseSyntaxOption', () => { + test('returns undefined for missing or whitespace values', () => { + expect(parseSyntaxOption()).toBeUndefined(); + expect(parseSyntaxOption('')).toBeUndefined(); + expect(parseSyntaxOption(' ')).toBeUndefined(); + }); + + test('returns the trimmed ECMAScript version when not a JSON array', () => { + expect(parseSyntaxOption(' es2020 ')).toBe('es2020'); + }); + + test('parses JSON array syntax', () => { + expect(parseSyntaxOption('["chrome 120", "firefox 115"]')).toEqual([ + 'chrome 120', + 'firefox 115', + ]); + }); + + test('throws descriptive error when JSON parsing fails', () => { + const parseInvalidSyntax = () => parseSyntaxOption('[invalid'); + expect(parseInvalidSyntax).toThrowError( + /Failed to parse --syntax option "\[inv.*JSON array/, + ); + }); +}); diff --git a/packages/core/tests/config.test.ts b/packages/core/tests/config.test.ts index cd72a17e7..7573a2604 100644 --- a/packages/core/tests/config.test.ts +++ b/packages/core/tests/config.test.ts @@ -2,6 +2,7 @@ import { join } from 'node:path'; import { pluginModuleFederation } from '@module-federation/rsbuild-plugin'; import { inspect } from '@rslib/core'; import { describe, expect, rs, test } from '@rstest/core'; +import { initConfig } from '../src/cli/initConfig'; import { composeCreateRsbuildConfig, composeRsbuildEnvironments, @@ -149,6 +150,68 @@ describe('Should load config file correctly', () => { }); }); +describe('CLI options', () => { + test('applies build CLI overrides for common flags', async () => { + const fixtureDir = join(__dirname, 'fixtures/config/cli-options'); + const configFilePath = join(fixtureDir, 'rslib.config.ts'); + + const { config } = await initConfig({ + config: configFilePath, + entry: ['index=src/main.ts', 'utils=src/utils.ts'], + distPath: 'build', + bundle: false, + syntax: 'es2018', + target: 'node', + dts: true, + external: ['react', 'react-dom'], + minify: true, + clean: true, + autoExtension: false, + autoExternal: false, + tsconfig: 'tsconfig.build.json', + }); + + expect(config).toMatchInlineSnapshot(` + { + "_privateMeta": { + "configFilePath": "/tests/fixtures/config/cli-options/rslib.config.ts", + }, + "lib": [ + { + "autoExtension": false, + "autoExternal": false, + "bundle": false, + "dts": true, + "output": { + "cleanDistPath": true, + "distPath": { + "root": "build", + }, + "externals": [ + "react", + "react-dom", + ], + "minify": true, + "target": "node", + }, + "source": { + "entry": { + "index": "src/main.ts", + "utils": "src/utils.ts", + }, + "tsconfigPath": "tsconfig.build.json", + }, + "syntax": "es2018", + }, + ], + "source": { + "define": {}, + }, + } + `); + }); +}); + describe('Should compose create Rsbuild config correctly', () => { test('Merge Rsbuild config in each format', async () => { const rslibConfig: RslibConfig = { diff --git a/packages/core/tests/fixtures/config/cli-options/rslib.config.ts b/packages/core/tests/fixtures/config/cli-options/rslib.config.ts new file mode 100644 index 000000000..3beb7be1d --- /dev/null +++ b/packages/core/tests/fixtures/config/cli-options/rslib.config.ts @@ -0,0 +1,27 @@ +import { defineConfig } from '@rslib/core'; + +export default defineConfig({ + lib: [ + { + bundle: true, + source: { + entry: { + index: './src/index.ts', + }, + }, + output: { + distPath: { + root: './dist', + }, + externals: ['lodash'], + minify: false, + cleanDistPath: 'auto', + target: 'web', + }, + autoExtension: true, + autoExternal: true, + dts: false, + syntax: 'esnext', + }, + ], +}); diff --git a/packages/core/tests/fixtures/config/format/rslib.config.ts b/packages/core/tests/fixtures/config/format/rslib.config.ts new file mode 100644 index 000000000..661a769e8 --- /dev/null +++ b/packages/core/tests/fixtures/config/format/rslib.config.ts @@ -0,0 +1,12 @@ +export default { + lib: [ + { + id: 'esm-lib', + format: 'esm', + }, + { + id: 'cjs-lib', + format: 'cjs', + }, + ], +}; diff --git a/packages/core/tests/tsconfig.json b/packages/core/tests/tsconfig.json index 947f4992f..1a0a4968f 100644 --- a/packages/core/tests/tsconfig.json +++ b/packages/core/tests/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "@rslib/tsconfig/base", - "include": ["."], + "include": [".", "../src/env.d.ts"], "exclude": ["**/node_modules"], "references": [] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1dfd67af0..a598e35d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -683,6 +683,10 @@ importers: tests/integration/cli/build/custom-root: {} + tests/integration/cli/build/no-config: {} + + tests/integration/cli/build/options: {} + tests/integration/cli/env: {} tests/integration/cli/inspect: {} diff --git a/tests/integration/cli/build/build.test.ts b/tests/integration/cli/build/build.test.ts index cd2101d47..a9277a848 100644 --- a/tests/integration/cli/build/build.test.ts +++ b/tests/integration/cli/build/build.test.ts @@ -1,7 +1,13 @@ import path from 'node:path'; import { describe, expect, test } from '@rstest/core'; import fse from 'fs-extra'; -import { buildAndGetResults, globContentJSON, runCliSync } from 'test-helper'; +import stripAnsi from 'strip-ansi'; +import { + buildAndGetResults, + extractRslibConfig, + globContentJSON, + runCliSync, +} from 'test-helper'; describe('build command', async () => { test('basic', async () => { @@ -135,4 +141,85 @@ describe('build command', async () => { ] `); }); + + test('should throw error if config file is absent, but it should work if the future', async () => { + const fixturePath = path.join(__dirname, 'no-config'); + await fse.remove(path.join(fixturePath, 'dist')); + + expect(() => + runCliSync('build --format cjs', { + cwd: fixturePath, + }), + ).toThrowError(/rslib\.config not found in/); + }); + + test('build options', async () => { + const fixturePath = path.join(__dirname, 'options'); + await fse.remove(path.join(fixturePath, 'dist')); + + const stdout = runCliSync( + 'build ' + + '--entry="./src/*" ' + + '--dist-path=dist/ok ' + + '--no-bundle ' + + '--format=esm ' + + '--syntax=es2015 ' + + '--target=web ' + + '--dts=true ' + + '--external=./bar ' + + '--minify=false ' + + '--auto-extension=false', + { + cwd: fixturePath, + env: { + ...process.env, + DEBUG: 'rslib', + }, + }, + ); + + const rslibConfigText = stripAnsi(extractRslibConfig(stdout)); + expect(rslibConfigText).toMatchInlineSnapshot(` + "{ + lib: [ + { + format: 'esm', + output: { + distPath: { root: 'dist/ok' }, + target: 'web', + minify: false, + externals: [ './bar' ] + }, + bundle: false, + dts: true, + autoExtension: 'false', + source: { entry: { index: './src/*' } }, + syntax: 'es2015' + } + ], + _privateMeta: { + configFilePath: '/tests/integration/cli/build/options/rslib.config.ts' + }, + source: { define: {} } + }" + `); + + const files = await globContentJSON(path.join(fixturePath, 'dist')); + const fileNames = Object.keys(files).sort(); + expect(fileNames).toMatchInlineSnapshot(` + [ + "/tests/integration/cli/build/options/dist/ok/foo.d.ts", + "/tests/integration/cli/build/options/dist/ok/foo.js", + ] + `); + + expect(files[fileNames[1]!]).toMatchInlineSnapshot(` + " + ;// CONCATENATED MODULE: ./src/foo.ts + const foo = 1000; + + export { foo }; + " + `); + }); }); diff --git a/tests/integration/cli/build/no-config/package.json b/tests/integration/cli/build/no-config/package.json new file mode 100644 index 000000000..6d6d8a24a --- /dev/null +++ b/tests/integration/cli/build/no-config/package.json @@ -0,0 +1,6 @@ +{ + "name": "cli-build-no-config-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/cli/build/no-config/src/index.ts b/tests/integration/cli/build/no-config/src/index.ts new file mode 100644 index 000000000..68a0993f6 --- /dev/null +++ b/tests/integration/cli/build/no-config/src/index.ts @@ -0,0 +1 @@ +export const withoutConfig = 'works'; diff --git a/tests/integration/cli/build/options/package.json b/tests/integration/cli/build/options/package.json new file mode 100644 index 000000000..274514d75 --- /dev/null +++ b/tests/integration/cli/build/options/package.json @@ -0,0 +1,6 @@ +{ + "name": "cli-build-options-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/cli/build/options/rslib.config.ts b/tests/integration/cli/build/options/rslib.config.ts new file mode 100644 index 000000000..c8c1901c9 --- /dev/null +++ b/tests/integration/cli/build/options/rslib.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from '@rslib/core'; + +export default defineConfig({ + lib: [ + { + format: 'cjs', + output: { + distPath: { + root: './dist/override', + }, + }, + }, + ], +}); diff --git a/tests/integration/cli/build/options/src/foo.ts b/tests/integration/cli/build/options/src/foo.ts new file mode 100644 index 000000000..dd3bf28f2 --- /dev/null +++ b/tests/integration/cli/build/options/src/foo.ts @@ -0,0 +1 @@ +export const foo = 1_000; diff --git a/tests/integration/cli/build/options/tsconfig.json b/tests/integration/cli/build/options/tsconfig.json new file mode 100644 index 000000000..888d3e460 --- /dev/null +++ b/tests/integration/cli/build/options/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["src"] +} diff --git a/tests/scripts/shared.ts b/tests/scripts/shared.ts index c6e453636..bef47bcd7 100644 --- a/tests/scripts/shared.ts +++ b/tests/scripts/shared.ts @@ -1,5 +1,6 @@ import assert from 'node:assert'; import { + type ExecException, type ExecOptions, type ExecSyncOptions, exec, @@ -27,7 +28,17 @@ export const rslibBinPath = join( ); export function runCliSync(command: string, options?: ExecSyncOptions) { - return execSync(`node ${rslibBinPath} ${command}`, options); + try { + const stdout = execSync(`node ${rslibBinPath} ${command}`, options); + return stdout.toString(); + } catch (error) { + const execError = error as ExecException; + const stderr = execError?.stderr?.toString(); + const stdout = execError?.stdout?.toString(); + const message = stderr?.trim() || stdout?.trim() || execError?.message; + + throw new Error(message ?? 'Command failed', { cause: error }); + } } export function runCli(command: string, options?: ExecOptions) { @@ -38,6 +49,35 @@ export function getCwdByExample(exampleName: string) { return join(__dirname, '../../examples', exampleName); } +export function extractRslibConfig(log: string): string { + const markerPattern = /Rslib config used to generate Rsbuild environments/; + const lines = log.split(/\r?\n/); + const markerIndex = lines.findIndex((line) => markerPattern.test(line)); + + if (markerIndex === -1) { + return ''; + } + + const startIndex = markerIndex + 2; + + if (startIndex >= lines.length || lines[startIndex] !== '{') { + return ''; + } + + let endIndex = startIndex + 1; + + while (endIndex < lines.length && lines[endIndex] !== '}') { + endIndex += 1; + } + + if (endIndex >= lines.length) { + return ''; + } + + const configText = lines.slice(startIndex, endIndex + 1).join('\n'); + return configText; +} + export function generateBundleEsmConfig(config: LibConfig = {}): LibConfig { const esmBasicConfig: LibConfig = { output: { diff --git a/website/docs/en/guide/basic/cli.mdx b/website/docs/en/guide/basic/cli.mdx index e279cf7e6..7848f22b2 100644 --- a/website/docs/en/guide/basic/cli.mdx +++ b/website/docs/en/guide/basic/cli.mdx @@ -46,7 +46,20 @@ Usage: $ rslib build Options: - -w --watch turn on watch mode, watch for changes and rebuild + -w, --watch turn on watch mode, watch for changes and rebuild + --entry set entry file or pattern (repeatable) (e.g. --entry="src/*" or --entry="main=src/main.ts") + --dist-path set root output directory + --bundle enable bundle mode (use --no-bundle to disable) + --format specify the output format (esm | cjs | umd | mf | iife) + --syntax set build syntax target (e.g. --syntax=es2018 or --syntax=["node 14", "Chrome 103"]) + --target set runtime target (web | node) + --dts emit declaration files (use --no-dts to disable) + --external add package to externals (repeatable) + --minify minify output (use --no-minify to disable) + --clean clean dist directory before build (use --no-clean to disable) + --auto-extension control automatic extension redirect (use --no-auto-extension to disable) + --auto-external control automatic dependency externalization (use --no-auto-external to disable) + --tsconfig use specific tsconfig (relative to project root) ``` ### Environment variables diff --git a/website/docs/zh/guide/basic/cli.mdx b/website/docs/zh/guide/basic/cli.mdx index 27c1df60b..a8077b17d 100644 --- a/website/docs/zh/guide/basic/cli.mdx +++ b/website/docs/zh/guide/basic/cli.mdx @@ -46,7 +46,20 @@ Usage: $ rslib build Options: - -w --watch 开启 watch 模式, 监听文件变更并重新构建 + -w, --watch 开启 watch 模式,监听文件变更并重新构建 + --entry 设置入口文件或模式(可重复),例如:--entry="src/*" 或 --entry="main=src/main.ts" + --dist-path 指定根输出目录 + --bundle 启用打包模式(使用 --no-bundle 关闭) + --format 指定输出格式(esm | cjs | umd | mf | iife) + --syntax 指定语法目标,例如:--syntax=es2018 或 --syntax=["node 14", "Chrome 103"] + --target 指定运行时目标(web | node) + --dts 生成声明文件(使用 --no-dts 关闭) + --external 将依赖加入 external(可重复) + --minify 压缩构建产物(使用 --no-minify 关闭) + --clean 构建前清理输出目录(使用 --no-clean 关闭) + --auto-extension 控制自动扩展名补全(使用 --no-auto-extension 关闭) + --auto-external 控制自动依赖 external 化(使用 --no-auto-external 关闭) + --tsconfig 指定使用的 tsconfig(相对于项目根目录) ``` ### 环境变量