From 8c329d2390d690e668fa0180f5e4fabbf6080753 Mon Sep 17 00:00:00 2001 From: "gaoyuan.1226" Date: Thu, 28 Nov 2024 11:15:33 +0800 Subject: [PATCH 1/7] feat: restart --- packages/core/package.json | 1 + packages/core/prebundle.config.mjs | 1 + packages/core/rslib.config.ts | 1 + packages/core/src/build.ts | 33 ++++++++++--- packages/core/src/cli/commands.ts | 10 ++-- packages/core/src/config.ts | 7 ++- packages/core/src/restart.ts | 76 ++++++++++++++++++++++++++++++ packages/core/src/utils/helper.ts | 27 +++++++++++ packages/core/tests/config.test.ts | 16 +++---- packages/core/tsconfig.json | 1 + tests/scripts/shared.ts | 2 +- 11 files changed, 150 insertions(+), 25 deletions(-) create mode 100644 packages/core/src/restart.ts diff --git a/packages/core/package.json b/packages/core/package.json index c69df3c48..e04d35874 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -44,6 +44,7 @@ "devDependencies": { "@rslib/tsconfig": "workspace:*", "@types/fs-extra": "^11.0.4", + "chokidar": "^4.0.1", "commander": "^12.1.0", "fs-extra": "^11.2.0", "memfs": "^4.14.0", diff --git a/packages/core/prebundle.config.mjs b/packages/core/prebundle.config.mjs index 21b450c7e..642da159a 100644 --- a/packages/core/prebundle.config.mjs +++ b/packages/core/prebundle.config.mjs @@ -13,6 +13,7 @@ export default { }, dependencies: [ 'commander', + 'chokidar', { name: 'rslog', afterBundle(task) { diff --git a/packages/core/rslib.config.ts b/packages/core/rslib.config.ts index f96c1f93a..ac2a90e47 100644 --- a/packages/core/rslib.config.ts +++ b/packages/core/rslib.config.ts @@ -25,6 +25,7 @@ export default defineConfig({ externals: { picocolors: '../compiled/picocolors/index.js', commander: '../compiled/commander/index.js', + chokidar: '../compiled/chokidar/index.js', rslog: '../compiled/rslog/index.js', }, }, diff --git a/packages/core/src/build.ts b/packages/core/src/build.ts index d689cb65b..f3ce5a82e 100644 --- a/packages/core/src/build.ts +++ b/packages/core/src/build.ts @@ -1,12 +1,18 @@ import { type RsbuildInstance, createRsbuild } from '@rsbuild/core'; import type { BuildOptions } from './cli/commands'; -import { composeRsbuildEnvironments, pruneEnvironments } from './config'; -import type { RslibConfig } from './types/config'; +import { + composeRsbuildEnvironments, + loadConfig, + pruneEnvironments, +} from './config'; +import { onBeforeRestartServer, watchFilesForRestart } from './restart'; + +export async function build(options?: BuildOptions): Promise { + const { content: config, filePath: configFilePath } = await loadConfig({ + path: options?.config, + envMode: options?.envMode, + }); -export async function build( - config: RslibConfig, - options?: BuildOptions, -): Promise { const environments = await composeRsbuildEnvironments(config); const rsbuildInstance = await createRsbuild({ rsbuildConfig: { @@ -14,9 +20,22 @@ export async function build( }, }); - await rsbuildInstance.build({ + const buildInstance = await rsbuildInstance.build({ watch: options?.watch, }); + if (options?.watch) { + const files: string[] = []; + files.push(configFilePath); + + onBeforeRestartServer(buildInstance.close); + + watchFilesForRestart(files, async () => { + await build(options); + }); + } else { + await buildInstance.close(); + } + return rsbuildInstance; } diff --git a/packages/core/src/cli/commands.ts b/packages/core/src/cli/commands.ts index b738f3f6d..4da4ee7fb 100644 --- a/packages/core/src/cli/commands.ts +++ b/packages/core/src/cli/commands.ts @@ -60,11 +60,7 @@ export function runCli(): void { .description('build the library for production') .action(async (options: BuildOptions) => { try { - const rslibConfig = await loadConfig({ - path: options.config, - envMode: options.envMode, - }); - await build(rslibConfig, options); + await build(options); } catch (err) { logger.error('Failed to build.'); logger.error(err); @@ -88,7 +84,7 @@ export function runCli(): void { .action(async (options: InspectOptions) => { try { // TODO: inspect should output Rslib's config - const rslibConfig = await loadConfig({ + const { content: rslibConfig } = await loadConfig({ path: options.config, envMode: options.envMode, }); @@ -115,7 +111,7 @@ export function runCli(): void { .description('start Rsbuild dev server of Module Federation format') .action(async (options: CommonOptions) => { try { - const rslibConfig = await loadConfig({ + const { content: rslibConfig } = await loadConfig({ path: options.config, envMode: options.envMode, }); diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index abe2f76a4..4d2e3bd36 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -112,7 +112,10 @@ export async function loadConfig({ cwd?: string; path?: string; envMode?: string; -}): Promise { +}): Promise<{ + content: RslibConfig; + filePath: string; +}> { const configFilePath = resolveConfigPath(cwd, path); const { content } = await loadRsbuildConfig({ cwd: dirname(configFilePath), @@ -120,7 +123,7 @@ export async function loadConfig({ envMode, }); - return content as RslibConfig; + return { content: content as RslibConfig, filePath: configFilePath }; } const composeExternalsWarnConfig = ( diff --git a/packages/core/src/restart.ts b/packages/core/src/restart.ts new file mode 100644 index 000000000..f24d24099 --- /dev/null +++ b/packages/core/src/restart.ts @@ -0,0 +1,76 @@ +import path from 'node:path'; +import chokidar from 'chokidar'; +import { color, debounce, isTTY } from './utils/helper'; +import { logger } from './utils/logger'; + +export async function watchFilesForRestart( + files: string[], + restart: () => Promise, +): Promise { + if (!files.length) { + return; + } + + const watcher = chokidar.watch(files, { + ignoreInitial: true, + // If watching fails due to read permissions, the errors will be suppressed silently. + ignorePermissionErrors: true, + ignored: ['**/node_modules/**', '**/.git/**', '**/.DS_Store/**'], + }); + + const callback = debounce( + async (filePath) => { + watcher.close(); + + await beforeRestart({ filePath }); + await restart(); + }, + // set 300ms debounce to avoid restart frequently + 300, + ); + + watcher.on('add', callback); + watcher.on('change', callback); + watcher.on('unlink', callback); +} + +type Cleaner = () => Promise | unknown; + +let cleaners: Cleaner[] = []; + +/** + * Add a cleaner to handle side effects + */ +export const onBeforeRestartServer = (cleaner: Cleaner): void => { + cleaners.push(cleaner); +}; + +const clearConsole = () => { + if (isTTY() && !process.env.DEBUG) { + process.stdout.write('\x1B[H\x1B[2J'); + } +}; + +const beforeRestart = async ({ + filePath, + clear = true, +}: { + filePath?: string; + clear?: boolean; +} = {}): Promise => { + if (clear) { + clearConsole(); + } + + if (filePath) { + const filename = path.basename(filePath); + logger.info(`Restart because ${color.yellow(filename)} is changed.\n`); + } else { + logger.info('Restarting...\n'); + } + + for (const cleaner of cleaners) { + await cleaner(); + } + cleaners = []; +}; diff --git a/packages/core/src/utils/helper.ts b/packages/core/src/utils/helper.ts index 9ead63e39..6e496cdec 100644 --- a/packages/core/src/utils/helper.ts +++ b/packages/core/src/utils/helper.ts @@ -201,4 +201,31 @@ export function checkMFPlugin(config: LibConfig): boolean { return added; } +export function debounce void>( + func: T, + wait: number, +): (...args: Parameters) => void { + let timeoutId: ReturnType | null = null; + + return (...args: Parameters) => { + if (timeoutId !== null) { + clearTimeout(timeoutId); + } + + timeoutId = setTimeout(() => { + func(...args); + }, wait); + }; +} + +/** + * Check if running in a TTY context + */ +export const isTTY = (type: 'stdin' | 'stdout' = 'stdout'): boolean => { + return ( + (type === 'stdin' ? process.stdin.isTTY : process.stdout.isTTY) && + !process.env.CI + ); +}; + export { color }; diff --git a/packages/core/tests/config.test.ts b/packages/core/tests/config.test.ts index d766030e9..e73c2b240 100644 --- a/packages/core/tests/config.test.ts +++ b/packages/core/tests/config.test.ts @@ -9,7 +9,7 @@ describe('Should load config file correctly', () => { test('Load config.js in cjs project', async () => { const fixtureDir = join(__dirname, 'fixtures/config/cjs'); const configFilePath = join(fixtureDir, 'rslib.config.js'); - const config = await loadConfig({ path: configFilePath }); + const { content: config } = await loadConfig({ path: configFilePath }); expect(config).toEqual({ lib: [], source: { @@ -26,7 +26,7 @@ describe('Should load config file correctly', () => { test('Load config.mjs in cjs project', async () => { const fixtureDir = join(__dirname, 'fixtures/config/cjs'); const configFilePath = join(fixtureDir, 'rslib.config.mjs'); - const config = await loadConfig({ path: configFilePath }); + const { content: config } = await loadConfig({ path: configFilePath }); expect(config).toEqual({ lib: [], source: { @@ -43,7 +43,7 @@ describe('Should load config file correctly', () => { test('Load config.ts in cjs project', async () => { const fixtureDir = join(__dirname, 'fixtures/config/cjs'); const configFilePath = join(fixtureDir, 'rslib.config.ts'); - const config = await loadConfig({ path: configFilePath }); + const { content: config } = await loadConfig({ path: configFilePath }); expect(config).toEqual({ lib: [], source: { @@ -60,7 +60,7 @@ describe('Should load config file correctly', () => { test('Load config.cjs with defineConfig in cjs project', async () => { const fixtureDir = join(__dirname, 'fixtures/config/cjs'); const configFilePath = join(fixtureDir, 'rslib.config.cjs'); - const config = await loadConfig({ path: configFilePath }); + const { content: config } = await loadConfig({ path: configFilePath }); expect(config).toEqual({ lib: [], source: { @@ -77,7 +77,7 @@ describe('Should load config file correctly', () => { test('Load config.js in esm project', async () => { const fixtureDir = join(__dirname, 'fixtures/config/esm'); const configFilePath = join(fixtureDir, 'rslib.config.js'); - const config = await loadConfig({ path: configFilePath }); + const { content: config } = await loadConfig({ path: configFilePath }); expect(config).toEqual({ lib: [], source: { @@ -94,7 +94,7 @@ describe('Should load config file correctly', () => { test('Load config.cjs in esm project', async () => { const fixtureDir = join(__dirname, 'fixtures/config/esm'); const configFilePath = join(fixtureDir, 'rslib.config.cjs'); - const config = await loadConfig({ path: configFilePath }); + const { content: config } = await loadConfig({ path: configFilePath }); expect(config).toEqual({ lib: [], source: { @@ -111,7 +111,7 @@ describe('Should load config file correctly', () => { test('Load config.ts in esm project', async () => { const fixtureDir = join(__dirname, 'fixtures/config/esm'); const configFilePath = join(fixtureDir, 'rslib.config.ts'); - const config = await loadConfig({ path: configFilePath }); + const { content: config } = await loadConfig({ path: configFilePath }); expect(config).toEqual({ lib: [], source: { @@ -128,7 +128,7 @@ describe('Should load config file correctly', () => { test('Load config.mjs with defineConfig in esm project', async () => { const fixtureDir = join(__dirname, 'fixtures/config/esm'); const configFilePath = join(fixtureDir, 'rslib.config.mjs'); - const config = await loadConfig({ path: configFilePath }); + const { content: config } = await loadConfig({ path: configFilePath }); expect(config).toMatchObject({ lib: [], source: { diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 248a3738f..0632ef420 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -12,6 +12,7 @@ "moduleResolution": "Bundler", "paths": { "commander": ["./compiled/commander"], + "chokidar": ["./compiled/chokidar"], "picocolors": ["./compiled/picocolors"], "rslog": ["./compiled/rslog"] } diff --git a/tests/scripts/shared.ts b/tests/scripts/shared.ts index f61c0cb90..c11d5d19f 100644 --- a/tests/scripts/shared.ts +++ b/tests/scripts/shared.ts @@ -199,7 +199,7 @@ export async function rslibBuild({ path?: string; modifyConfig?: (config: RslibConfig) => void; }) { - const rslibConfig = await loadConfig({ + const { content: rslibConfig } = await loadConfig({ cwd, path, }); From a712a08c0ffcdd8acdbfb311d233f4199d42abba Mon Sep 17 00:00:00 2001 From: "gaoyuan.1226" Date: Thu, 28 Nov 2024 15:34:06 +0800 Subject: [PATCH 2/7] feat: support restart when rslib config changed --- packages/core/prebundle.config.mjs | 6 ++- packages/core/src/cli/build.ts | 27 +++++----- packages/core/src/cli/commands.ts | 8 ++- packages/plugin-dts/src/index.ts | 14 +++++- pnpm-lock.yaml | 2 + .../integration/cli/build-watch/build.test.ts | 50 +++++++++++++++++++ .../integration/cli/build-watch/package.json | 6 +++ .../cli/build-watch/rslib.config.ts | 13 +++++ .../integration/cli/build-watch/src/index.ts | 1 + .../integration/cli/build-watch/tsconfig.json | 9 ++++ tests/scripts/helper.ts | 30 +++++++++++ 11 files changed, 150 insertions(+), 16 deletions(-) create mode 100644 tests/integration/cli/build-watch/build.test.ts create mode 100644 tests/integration/cli/build-watch/package.json create mode 100644 tests/integration/cli/build-watch/rslib.config.ts create mode 100644 tests/integration/cli/build-watch/src/index.ts create mode 100644 tests/integration/cli/build-watch/tsconfig.json diff --git a/packages/core/prebundle.config.mjs b/packages/core/prebundle.config.mjs index 642da159a..81cbefc54 100644 --- a/packages/core/prebundle.config.mjs +++ b/packages/core/prebundle.config.mjs @@ -13,7 +13,11 @@ export default { }, dependencies: [ 'commander', - 'chokidar', + { + name: 'chokidar', + // strip sourcemap comment + prettier: true, + }, { name: 'rslog', afterBundle(task) { diff --git a/packages/core/src/cli/build.ts b/packages/core/src/cli/build.ts index b2dba0786..61898590c 100644 --- a/packages/core/src/cli/build.ts +++ b/packages/core/src/cli/build.ts @@ -1,20 +1,16 @@ import { type RsbuildInstance, createRsbuild } from '@rsbuild/core'; -import { - composeRsbuildEnvironments, - loadConfig, - pruneEnvironments, -} from '../config'; +import { composeRsbuildEnvironments, pruneEnvironments } from '../config'; import { onBeforeRestartServer, watchFilesForRestart } from '../restart'; +import type { RslibConfig } from '../types/config'; import type { BuildOptions } from './commands'; +import { loadRslibConfig } from './init'; export async function build( - options: BuildOptions = {}, + config: RslibConfig, + options: Pick & { + configFilePath?: string; + } = {}, ): Promise { - const { content: config, filePath: configFilePath } = await loadConfig({ - path: options?.config, - envMode: options?.envMode, - }); - const environments = await composeRsbuildEnvironments(config); const rsbuildInstance = await createRsbuild({ rsbuildConfig: { @@ -28,12 +24,17 @@ export async function build( if (options?.watch) { const files: string[] = []; - files.push(configFilePath); + options.configFilePath && files.push(options?.configFilePath); onBeforeRestartServer(buildInstance.close); watchFilesForRestart(files, async () => { - await build(options); + const { content: rslibConfig, filePath: configFilePath } = + await loadRslibConfig(options); + await build(rslibConfig, { + configFilePath, + ...options, + }); }); } else { await buildInstance.close(); diff --git a/packages/core/src/cli/commands.ts b/packages/core/src/cli/commands.ts index 3b15cae74..118e8563f 100644 --- a/packages/core/src/cli/commands.ts +++ b/packages/core/src/cli/commands.ts @@ -62,7 +62,13 @@ export function runCli(): void { .description('build the library for production') .action(async (options: BuildOptions) => { try { - await build(options); + const { content: rslibConfig, filePath: configFilePath } = + await loadRslibConfig(options); + await build(rslibConfig, { + configFilePath, + lib: options.lib, + watch: options.watch, + }); } catch (err) { logger.error('Failed to build.'); logger.error(err); diff --git a/packages/plugin-dts/src/index.ts b/packages/plugin-dts/src/index.ts index 2cee27645..89a71a358 100644 --- a/packages/plugin-dts/src/index.ts +++ b/packages/plugin-dts/src/index.ts @@ -1,4 +1,4 @@ -import { fork } from 'node:child_process'; +import { type ChildProcess, fork } from 'node:child_process'; import { dirname, extname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { type RsbuildConfig, type RsbuildPlugin, logger } from '@rsbuild/core'; @@ -60,6 +60,7 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({ const dtsPromises: Promise[] = []; let promisesResult: TaskResult[] = []; + let childProcesses: ChildProcess[] = []; api.onBeforeEnvironmentCompile( ({ isWatch, isFirstCompile, environment }) => { @@ -74,6 +75,8 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({ stdio: 'inherit', }); + childProcesses.push(childProcess); + // TODO: @microsoft/api-extractor only support single entry to bundle DTS // use first element of Record type entry config const dtsEntry = processSourceEntry( @@ -141,5 +144,14 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({ }, order: 'post', }); + + api.onCloseBuild(() => { + for (const childProcess of childProcesses) { + if (!childProcess.killed) { + childProcess.kill(); + } + } + childProcesses = []; + }); }, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6218336b..8b5b8d4fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -547,6 +547,8 @@ importers: tests/integration/cli/build: {} + tests/integration/cli/build-watch: {} + tests/integration/cli/build/custom-root: {} tests/integration/cli/inspect: {} diff --git a/tests/integration/cli/build-watch/build.test.ts b/tests/integration/cli/build-watch/build.test.ts new file mode 100644 index 000000000..a117a7bb2 --- /dev/null +++ b/tests/integration/cli/build-watch/build.test.ts @@ -0,0 +1,50 @@ +import { exec } from 'node:child_process'; +import path from 'node:path'; +import fse from 'fs-extra'; +import { awaitFileExists } from 'test-helper'; +import { describe, test } from 'vitest'; + +describe('build --watch command', async () => { + test('basic', async () => { + const distPath = path.join(__dirname, 'dist'); + await fse.remove(distPath); + + const tempConfigFile = path.join(__dirname, 'test-temp-rslib.config.mjs'); + + fse.outputFileSync( + tempConfigFile, + `import { defineConfig } from '@rslib/core'; +import { generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [generateBundleEsmConfig()], +}); + `, + ); + + const process = exec(`npx rslib build --watch -c ${tempConfigFile}`, { + cwd: __dirname, + }); + + const distEsmIndexFile = path.join(__dirname, 'dist/esm/index.js'); + + await awaitFileExists(distEsmIndexFile); + + await fse.remove(distPath); + + fse.outputFileSync( + tempConfigFile, + `import { defineConfig } from '@rslib/core'; +import { generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [generateBundleEsmConfig()], +}); + `, + ); + + await awaitFileExists(distEsmIndexFile); + + process.kill(); + }); +}); diff --git a/tests/integration/cli/build-watch/package.json b/tests/integration/cli/build-watch/package.json new file mode 100644 index 000000000..180c1ba16 --- /dev/null +++ b/tests/integration/cli/build-watch/package.json @@ -0,0 +1,6 @@ +{ + "name": "cli-build-watch-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/tests/integration/cli/build-watch/rslib.config.ts b/tests/integration/cli/build-watch/rslib.config.ts new file mode 100644 index 000000000..e5affdca1 --- /dev/null +++ b/tests/integration/cli/build-watch/rslib.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + dts: true, + }), + generateBundleCjsConfig({ + dts: true, + }), + ], +}); diff --git a/tests/integration/cli/build-watch/src/index.ts b/tests/integration/cli/build-watch/src/index.ts new file mode 100644 index 000000000..3329a7d97 --- /dev/null +++ b/tests/integration/cli/build-watch/src/index.ts @@ -0,0 +1 @@ +export const foo = 'foo'; diff --git a/tests/integration/cli/build-watch/tsconfig.json b/tests/integration/cli/build-watch/tsconfig.json new file mode 100644 index 000000000..fcb8d6133 --- /dev/null +++ b/tests/integration/cli/build-watch/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "rootDir": "src", + "baseUrl": ".", + "composite": true + }, + "include": ["src"] +} diff --git a/tests/scripts/helper.ts b/tests/scripts/helper.ts index ef2860f45..bc693803f 100644 --- a/tests/scripts/helper.ts +++ b/tests/scripts/helper.ts @@ -61,3 +61,33 @@ export const proxyConsole = ( }, }; }; + +export const waitFor = async ( + fn: () => boolean, + { + maxChecks = 100, + interval = 20, + }: { + maxChecks?: number; + interval?: number; + } = {}, +) => { + let checks = 0; + + while (checks < maxChecks) { + if (fn()) { + return true; + } + checks++; + await new Promise((resolve) => setTimeout(resolve, interval)); + } + + return false; +}; + +export const awaitFileExists = async (dir: string) => { + const result = await waitFor(() => fse.existsSync(dir), { interval: 50 }); + if (!result) { + throw new Error(`awaitFileExists failed: ${dir}`); + } +}; From 8ab1121e61a6752298a1700791bff9d044c48c04 Mon Sep 17 00:00:00 2001 From: "gaoyuan.1226" Date: Thu, 28 Nov 2024 15:40:36 +0800 Subject: [PATCH 3/7] fix: update --- packages/core/src/cli/build.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/src/cli/build.ts b/packages/core/src/cli/build.ts index 61898590c..dfb748e20 100644 --- a/packages/core/src/cli/build.ts +++ b/packages/core/src/cli/build.ts @@ -24,13 +24,17 @@ export async function build( if (options?.watch) { const files: string[] = []; - options.configFilePath && files.push(options?.configFilePath); + + if (options.configFilePath) { + files.push(options.configFilePath); + } onBeforeRestartServer(buildInstance.close); watchFilesForRestart(files, async () => { const { content: rslibConfig, filePath: configFilePath } = await loadRslibConfig(options); + await build(rslibConfig, { configFilePath, ...options, From 65ce9a4c9ccf0e30d9cc10b2a7171525babc1085 Mon Sep 17 00:00:00 2001 From: "gaoyuan.1226" Date: Thu, 28 Nov 2024 17:30:18 +0800 Subject: [PATCH 4/7] chore: move to cli & support mf restart --- packages/core/src/cli/build.ts | 25 +++-------------- packages/core/src/cli/commands.ts | 37 +++++++++++++++++++------- packages/core/src/cli/mf.ts | 5 +++- packages/core/src/{ => cli}/restart.ts | 4 +-- packages/plugin-dts/src/index.ts | 7 +++-- 5 files changed, 41 insertions(+), 37 deletions(-) rename packages/core/src/{ => cli}/restart.ts (94%) diff --git a/packages/core/src/cli/build.ts b/packages/core/src/cli/build.ts index dfb748e20..7ccd7d268 100644 --- a/packages/core/src/cli/build.ts +++ b/packages/core/src/cli/build.ts @@ -1,15 +1,12 @@ import { type RsbuildInstance, createRsbuild } from '@rsbuild/core'; import { composeRsbuildEnvironments, pruneEnvironments } from '../config'; -import { onBeforeRestartServer, watchFilesForRestart } from '../restart'; import type { RslibConfig } from '../types/config'; import type { BuildOptions } from './commands'; -import { loadRslibConfig } from './init'; +import { onBeforeRestartServer } from './restart'; export async function build( config: RslibConfig, - options: Pick & { - configFilePath?: string; - } = {}, + options: Pick = {}, ): Promise { const environments = await composeRsbuildEnvironments(config); const rsbuildInstance = await createRsbuild({ @@ -22,24 +19,8 @@ export async function build( watch: options.watch, }); - if (options?.watch) { - const files: string[] = []; - - if (options.configFilePath) { - files.push(options.configFilePath); - } - + if (options.watch) { onBeforeRestartServer(buildInstance.close); - - watchFilesForRestart(files, async () => { - const { content: rslibConfig, filePath: configFilePath } = - await loadRslibConfig(options); - - await build(rslibConfig, { - configFilePath, - ...options, - }); - }); } else { await buildInstance.close(); } diff --git a/packages/core/src/cli/commands.ts b/packages/core/src/cli/commands.ts index 118e8563f..37087f393 100644 --- a/packages/core/src/cli/commands.ts +++ b/packages/core/src/cli/commands.ts @@ -5,6 +5,7 @@ import { build } from './build'; import { loadRslibConfig } from './init'; import { inspect } from './inspect'; import { startMFDevServer } from './mf'; +import { watchFilesForRestart } from './restart'; export type CommonOptions = { root?: string; @@ -62,13 +63,20 @@ export function runCli(): void { .description('build the library for production') .action(async (options: BuildOptions) => { try { - const { content: rslibConfig, filePath: configFilePath } = - await loadRslibConfig(options); - await build(rslibConfig, { - configFilePath, - lib: options.lib, - watch: options.watch, - }); + const cliBuild = async () => { + const { content: rslibConfig, filePath } = + await loadRslibConfig(options); + + await build(rslibConfig, options); + + if (options?.watch) { + watchFilesForRestart([filePath], async () => { + await cliBuild(); + }); + } + }; + + await cliBuild(); } catch (err) { logger.error('Failed to build.'); logger.error(err); @@ -110,9 +118,18 @@ export function runCli(): void { .description('start Rsbuild dev server of Module Federation format') .action(async (options: CommonOptions) => { try { - const { content: rslibConfig } = await loadRslibConfig(options); - // TODO: support lib option in mf dev server - await startMFDevServer(rslibConfig); + const mfDev = async () => { + const { content: rslibConfig, filePath } = + await loadRslibConfig(options); + // TODO: support lib option in mf dev server + await startMFDevServer(rslibConfig); + + watchFilesForRestart([filePath], async () => { + await mfDev(); + }); + }; + + await mfDev(); } catch (err) { logger.error('Failed to start mf dev.'); logger.error(err); diff --git a/packages/core/src/cli/mf.ts b/packages/core/src/cli/mf.ts index 352f89f01..3ad4b0ac6 100644 --- a/packages/core/src/cli/mf.ts +++ b/packages/core/src/cli/mf.ts @@ -2,6 +2,7 @@ import { createRsbuild, mergeRsbuildConfig } from '@rsbuild/core'; import type { RsbuildConfig, RsbuildInstance } from '@rsbuild/core'; import { composeCreateRsbuildConfig } from '../config'; import type { RslibConfig } from '../types'; +import { onBeforeRestartServer } from './restart'; export async function startMFDevServer( config: RslibConfig, @@ -27,7 +28,9 @@ async function initMFRsbuild( const rsbuildInstance = await createRsbuild({ rsbuildConfig: mfRsbuildConfig.config, }); - await rsbuildInstance.startDevServer(); + const devServer = await rsbuildInstance.startDevServer(); + + onBeforeRestartServer(devServer.server.close); return rsbuildInstance; } diff --git a/packages/core/src/restart.ts b/packages/core/src/cli/restart.ts similarity index 94% rename from packages/core/src/restart.ts rename to packages/core/src/cli/restart.ts index f24d24099..3e8ae3b81 100644 --- a/packages/core/src/restart.ts +++ b/packages/core/src/cli/restart.ts @@ -1,7 +1,7 @@ import path from 'node:path'; import chokidar from 'chokidar'; -import { color, debounce, isTTY } from './utils/helper'; -import { logger } from './utils/logger'; +import { color, debounce, isTTY } from '../utils/helper'; +import { logger } from '../utils/logger'; export async function watchFilesForRestart( files: string[], diff --git a/packages/plugin-dts/src/index.ts b/packages/plugin-dts/src/index.ts index 89a71a358..d5c3d0692 100644 --- a/packages/plugin-dts/src/index.ts +++ b/packages/plugin-dts/src/index.ts @@ -145,13 +145,16 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({ order: 'post', }); - api.onCloseBuild(() => { + const killProcesses = () => { for (const childProcess of childProcesses) { if (!childProcess.killed) { childProcess.kill(); } } childProcesses = []; - }); + }; + + api.onCloseBuild(killProcesses); + api.onCloseDevServer(killProcesses); }, }); From b2428ed355ce7761096e73839e67ce125ca56daa Mon Sep 17 00:00:00 2001 From: "gaoyuan.1226" Date: Thu, 28 Nov 2024 17:44:27 +0800 Subject: [PATCH 5/7] fix: update --- packages/core/src/cli/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/cli/commands.ts b/packages/core/src/cli/commands.ts index 37087f393..2f8b7b2b2 100644 --- a/packages/core/src/cli/commands.ts +++ b/packages/core/src/cli/commands.ts @@ -69,7 +69,7 @@ export function runCli(): void { await build(rslibConfig, options); - if (options?.watch) { + if (options.watch) { watchFilesForRestart([filePath], async () => { await cliBuild(); }); From 76ec11b55d2f0517db3c1b423f0b8e3e36c6f24b Mon Sep 17 00:00:00 2001 From: "gaoyuan.1226" Date: Thu, 28 Nov 2024 18:50:29 +0800 Subject: [PATCH 6/7] fix: update --- packages/core/src/cli/build.ts | 4 ++-- packages/core/src/cli/commands.ts | 6 +++--- packages/core/src/cli/mf.ts | 4 ++-- packages/core/src/cli/restart.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/core/src/cli/build.ts b/packages/core/src/cli/build.ts index 7ccd7d268..c305c84d5 100644 --- a/packages/core/src/cli/build.ts +++ b/packages/core/src/cli/build.ts @@ -2,7 +2,7 @@ import { type RsbuildInstance, createRsbuild } from '@rsbuild/core'; import { composeRsbuildEnvironments, pruneEnvironments } from '../config'; import type { RslibConfig } from '../types/config'; import type { BuildOptions } from './commands'; -import { onBeforeRestartServer } from './restart'; +import { onBeforeRestart } from './restart'; export async function build( config: RslibConfig, @@ -20,7 +20,7 @@ export async function build( }); if (options.watch) { - onBeforeRestartServer(buildInstance.close); + onBeforeRestart(buildInstance.close); } else { await buildInstance.close(); } diff --git a/packages/core/src/cli/commands.ts b/packages/core/src/cli/commands.ts index 2f8b7b2b2..32720dab8 100644 --- a/packages/core/src/cli/commands.ts +++ b/packages/core/src/cli/commands.ts @@ -118,18 +118,18 @@ export function runCli(): void { .description('start Rsbuild dev server of Module Federation format') .action(async (options: CommonOptions) => { try { - const mfDev = async () => { + const cliMfDev = async () => { const { content: rslibConfig, filePath } = await loadRslibConfig(options); // TODO: support lib option in mf dev server await startMFDevServer(rslibConfig); watchFilesForRestart([filePath], async () => { - await mfDev(); + await cliMfDev(); }); }; - await mfDev(); + await cliMfDev(); } catch (err) { logger.error('Failed to start mf dev.'); logger.error(err); diff --git a/packages/core/src/cli/mf.ts b/packages/core/src/cli/mf.ts index 41975d28e..be87ae68d 100644 --- a/packages/core/src/cli/mf.ts +++ b/packages/core/src/cli/mf.ts @@ -2,7 +2,7 @@ import { createRsbuild, mergeRsbuildConfig } from '@rsbuild/core'; import type { RsbuildConfig, RsbuildInstance } from '@rsbuild/core'; import { composeCreateRsbuildConfig } from '../config'; import type { RslibConfig } from '../types'; -import { onBeforeRestartServer } from './restart'; +import { onBeforeRestart } from './restart'; export async function startMFDevServer( config: RslibConfig, @@ -30,7 +30,7 @@ async function initMFRsbuild( }); const devServer = await rsbuildInstance.startDevServer(); - onBeforeRestartServer(devServer.server.close); + onBeforeRestart(devServer.server.close); return rsbuildInstance; } diff --git a/packages/core/src/cli/restart.ts b/packages/core/src/cli/restart.ts index 3e8ae3b81..667393b15 100644 --- a/packages/core/src/cli/restart.ts +++ b/packages/core/src/cli/restart.ts @@ -41,7 +41,7 @@ let cleaners: Cleaner[] = []; /** * Add a cleaner to handle side effects */ -export const onBeforeRestartServer = (cleaner: Cleaner): void => { +export const onBeforeRestart = (cleaner: Cleaner): void => { cleaners.push(cleaner); }; From 9e66c9c21cacbf338c40cf9fb982ffc4af63b606 Mon Sep 17 00:00:00 2001 From: "gaoyuan.1226" Date: Thu, 28 Nov 2024 19:09:22 +0800 Subject: [PATCH 7/7] fix: update test --- tests/integration/cli/build-watch/build.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/cli/build-watch/build.test.ts b/tests/integration/cli/build-watch/build.test.ts index a117a7bb2..7329f26e3 100644 --- a/tests/integration/cli/build-watch/build.test.ts +++ b/tests/integration/cli/build-watch/build.test.ts @@ -7,7 +7,7 @@ import { describe, test } from 'vitest'; describe('build --watch command', async () => { test('basic', async () => { const distPath = path.join(__dirname, 'dist'); - await fse.remove(distPath); + fse.removeSync(distPath); const tempConfigFile = path.join(__dirname, 'test-temp-rslib.config.mjs'); @@ -30,7 +30,7 @@ export default defineConfig({ await awaitFileExists(distEsmIndexFile); - await fse.remove(distPath); + fse.removeSync(distPath); fse.outputFileSync( tempConfigFile,