@4399ywkf/core 5.0.32 → 5.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -25,7 +25,6 @@ declare function build(options?: {
25
25
  * 启动开发服务器
26
26
  *
27
27
  * @param options.mode - env 文件模式,决定加载 .env.{mode},默认 "development"
28
- * 例如 --mode staging 会加载 .env.staging,但 NODE_ENV 仍为 "development"
29
28
  */
30
29
  declare function dev(options?: {
31
30
  cwd?: string;
@@ -129,17 +128,11 @@ declare class YwkfGeneratorPlugin {
129
128
  * 初始化插件钩子
130
129
  */
131
130
  private initialize;
132
- private resetState;
133
- private regenerate;
134
131
  apply(compiler: Compiler): void;
135
132
  /**
136
133
  * 监听页面目录变化
137
134
  */
138
135
  private watchPages;
139
- /**
140
- * 监听配置文件变化,重新加载配置并重新生成 .ywkf 文件
141
- */
142
- private watchConfig;
143
136
  }
144
137
 
145
138
  export { GeneratorContext, PluginConfig, PluginHooks, YwkfConfig, YwkfGenerator, YwkfGeneratorPlugin, type YwkfGeneratorPluginOptions, build, dev };
package/dist/index.js CHANGED
@@ -377,8 +377,7 @@ var garfishPlugin = createPlugin((options = {}) => ({
377
377
  if (!master) {
378
378
  rspackConfig.output = {
379
379
  ...rspackConfig.output,
380
- library: finalAppName,
381
- libraryTarget: "umd",
380
+ library: { type: "umd", name: finalAppName },
382
381
  globalObject: "window",
383
382
  chunkLoadingGlobal: `garfish_${finalAppName}`
384
383
  };
@@ -877,7 +876,7 @@ function createMockMiddleware(mockPath, options) {
877
876
  scanMockFiles(mockPath);
878
877
  return async (_req, _res, next) => {
879
878
  if (delay > 0) {
880
- await new Promise((resolve4) => setTimeout(resolve4, delay));
879
+ await new Promise((resolve5) => setTimeout(resolve5, delay));
881
880
  }
882
881
  next();
883
882
  };
@@ -1831,11 +1830,11 @@ var zustandPlugin = createPlugin((options = {}) => ({
1831
1830
  const hooks = {
1832
1831
  modifyRspackConfig(rspackConfig) {
1833
1832
  logger.info("Zustand \u5DF2\u542F\u7528");
1834
- const resolve4 = rspackConfig.resolve || {};
1835
- const alias = resolve4.alias || {};
1833
+ const resolve5 = rspackConfig.resolve || {};
1834
+ const alias = resolve5.alias || {};
1836
1835
  alias["@store"] = storePath;
1837
- resolve4.alias = alias;
1838
- rspackConfig.resolve = resolve4;
1836
+ resolve5.alias = alias;
1837
+ rspackConfig.resolve = resolve5;
1839
1838
  return rspackConfig;
1840
1839
  },
1841
1840
  generateFiles(_ctx) {
@@ -2192,32 +2191,39 @@ function findConfigFile(cwd) {
2192
2191
  }
2193
2192
  return null;
2194
2193
  }
2195
- async function loadTsConfig(configPath) {
2194
+ async function loadTsConfig(configPath, fresh = false) {
2196
2195
  const jiti = (await import("jiti")).default;
2197
2196
  const loader = jiti(configPath, {
2198
- interopDefault: true
2197
+ interopDefault: true,
2198
+ // fresh(热重启)时必须禁用缓存:jiti 的 moduleCache 默认与 Node 原生 require 缓存集成,
2199
+ // 即使新建 jiti 实例,loader(configPath) 仍会命中 Node 全局缓存读回旧配置;
2200
+ // fsCache 为转译产物缓存,一并关闭以确保读到最新源码。
2201
+ ...fresh ? { moduleCache: false, fsCache: false } : {}
2199
2202
  });
2200
2203
  const config = loader(configPath);
2201
2204
  return config.default || config;
2202
2205
  }
2203
- async function loadJsConfig(configPath) {
2204
- const fileUrl = pathToFileURL(configPath).href;
2206
+ async function loadJsConfig(configPath, fresh = false) {
2207
+ let fileUrl = pathToFileURL(configPath).href;
2208
+ if (fresh) {
2209
+ fileUrl += `?t=${Date.now()}`;
2210
+ }
2205
2211
  const module = await import(fileUrl);
2206
2212
  return module.default || module;
2207
2213
  }
2208
- async function loadConfigFile(configPath) {
2214
+ async function loadConfigFile(configPath, opts = {}) {
2209
2215
  const ext = extname(configPath);
2210
2216
  if (ext === ".ts" || ext === ".mts") {
2211
- return loadTsConfig(configPath);
2217
+ return loadTsConfig(configPath, opts.fresh);
2212
2218
  }
2213
- return loadJsConfig(configPath);
2219
+ return loadJsConfig(configPath, opts.fresh);
2214
2220
  }
2215
2221
  function mergeConfig(userConfig, baseConfig = defaultConfig) {
2216
2222
  return deepmerge(baseConfig, userConfig, {
2217
2223
  arrayMerge: (_, sourceArray) => sourceArray
2218
2224
  });
2219
2225
  }
2220
- async function resolveConfig(cwd) {
2226
+ async function resolveConfig(cwd, opts = {}) {
2221
2227
  const configPath = findConfigFile(cwd);
2222
2228
  if (!configPath) {
2223
2229
  console.warn("\u26A0\uFE0F \u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6 (ywkf.config.ts)\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E");
@@ -2227,7 +2233,7 @@ async function resolveConfig(cwd) {
2227
2233
  };
2228
2234
  }
2229
2235
  try {
2230
- const userConfig = await loadConfigFile(configPath);
2236
+ const userConfig = await loadConfigFile(configPath, opts);
2231
2237
  const normalized = normalizeConfig(userConfig);
2232
2238
  const config = mergeConfig(normalized);
2233
2239
  return { config, configPath };
@@ -2475,7 +2481,7 @@ import { RsdoctorRspackPlugin } from "@rsdoctor/rspack-plugin";
2475
2481
 
2476
2482
  // src/rspack/dev.ts
2477
2483
  import { createRequire as createRequire2 } from "module";
2478
- import ReactRefreshPlugin from "@rspack/plugin-react-refresh";
2484
+ import { ReactRefreshRspackPlugin } from "@rspack/plugin-react-refresh";
2479
2485
  import { merge } from "webpack-merge";
2480
2486
 
2481
2487
  // src/rspack/base.ts
@@ -3378,16 +3384,6 @@ var YwkfGeneratorPlugin = class {
3378
3384
  );
3379
3385
  this.initialized = true;
3380
3386
  }
3381
- resetState() {
3382
- this.initialized = false;
3383
- this.pluginHooks = [];
3384
- this.generator = null;
3385
- }
3386
- async regenerate() {
3387
- if (this.generator) {
3388
- await this.generator.generate();
3389
- }
3390
- }
3391
3387
  apply(compiler) {
3392
3388
  const pluginName = "YwkfGeneratorPlugin";
3393
3389
  compiler.hooks.beforeCompile.tapAsync(pluginName, async (_params, callback) => {
@@ -3403,14 +3399,13 @@ var YwkfGeneratorPlugin = class {
3403
3399
  }
3404
3400
  });
3405
3401
  if (this.options.isDev && !this.isWatching) {
3406
- this.watchPages();
3407
- this.watchConfig();
3402
+ this.watchPages(compiler);
3408
3403
  }
3409
3404
  }
3410
3405
  /**
3411
3406
  * 监听页面目录变化
3412
3407
  */
3413
- watchPages() {
3408
+ watchPages(compiler) {
3414
3409
  const { config, cwd } = this.options;
3415
3410
  const pagesDir = join11(cwd, config.router.pagesDir || "src/pages");
3416
3411
  if (!existsSync11(pagesDir)) {
@@ -3432,47 +3427,11 @@ var YwkfGeneratorPlugin = class {
3432
3427
  }
3433
3428
  }, 500);
3434
3429
  });
3435
- process.on("exit", () => {
3436
- watcher.close();
3437
- });
3438
- }
3439
- /**
3440
- * 监听配置文件变化,重新加载配置并重新生成 .ywkf 文件
3441
- */
3442
- watchConfig() {
3443
- const { cwd } = this.options;
3444
- const configPath = findConfigFile(cwd);
3445
- if (!configPath) {
3446
- return;
3447
- }
3448
- let debounceTimer = null;
3449
- const watcher = watch(configPath, async () => {
3450
- if (debounceTimer) {
3451
- clearTimeout(debounceTimer);
3452
- }
3453
- debounceTimer = setTimeout(async () => {
3454
- try {
3455
- console.log(`
3456
- [ywkf] \u68C0\u6D4B\u5230\u914D\u7F6E\u6587\u4EF6\u53D8\u5316\uFF0C\u91CD\u65B0\u751F\u6210 .ywkf \u76EE\u5F55...
3457
- `);
3458
- const userConfig = await loadConfigFile(configPath);
3459
- const newConfig = mergeConfig(userConfig);
3460
- this.options.config = newConfig;
3461
- this.options.pluginConfigs = newConfig.plugins;
3462
- this.resetState();
3463
- await this.initialize();
3464
- await this.regenerate();
3465
- console.log(
3466
- ` [ywkf] .ywkf \u76EE\u5F55\u5DF2\u91CD\u65B0\u751F\u6210\u3002\u90E8\u5206\u914D\u7F6E\u53D8\u66F4\uFF08\u5982\u7AEF\u53E3\u3001\u4EE3\u7406\uFF09\u9700\u91CD\u542F dev server \u751F\u6548\u3002
3467
- `
3468
- );
3469
- } catch (error) {
3470
- console.error(` [ywkf] \u914D\u7F6E\u91CD\u8F7D\u5931\u8D25:`, error);
3471
- }
3472
- }, 500);
3473
- });
3474
- process.on("exit", () => {
3430
+ compiler.hooks.shutdown.tap("YwkfGeneratorPlugin", () => {
3431
+ if (debounceTimer) clearTimeout(debounceTimer);
3475
3432
  watcher.close();
3433
+ this.isWatching = false;
3434
+ this.hasGenerated = false;
3476
3435
  });
3477
3436
  }
3478
3437
  };
@@ -3641,9 +3600,8 @@ function createBaseConfig(config, cwd, options = {}) {
3641
3600
  ].filter(Boolean),
3642
3601
  output: {
3643
3602
  assetModuleFilename: "images/[hash][ext]",
3644
- library: microFrontend.enabled ? `${appCName}-[name]` : void 0,
3603
+ library: microFrontend.enabled ? { type: "umd", name: `${appCName}-[name]` } : void 0,
3645
3604
  chunkFilename: "[name].[contenthash].js",
3646
- libraryTarget: microFrontend.enabled ? "umd" : void 0,
3647
3605
  globalObject: microFrontend.enabled ? "window" : void 0,
3648
3606
  chunkLoadingGlobal: microFrontend.enabled ? `chunk_global_${appName}` : void 0,
3649
3607
  publicPath: output.publicPath ?? "/",
@@ -3864,7 +3822,7 @@ function createDevConfig(config, cwd) {
3864
3822
  clean: false
3865
3823
  },
3866
3824
  stats: "errors-only",
3867
- plugins: [new ReactRefreshPlugin()],
3825
+ plugins: [new ReactRefreshRspackPlugin()],
3868
3826
  devServer: {
3869
3827
  host: dev2.host,
3870
3828
  port: dev2.port,
@@ -4245,7 +4203,6 @@ var DevPrinter = class {
4245
4203
  */
4246
4204
  printBuildStart() {
4247
4205
  this.startTime = Date.now();
4248
- this.compileDone = false;
4249
4206
  const label = chalk.gray("start");
4250
4207
  console.log(` ${label} build started...`);
4251
4208
  }
@@ -4340,11 +4297,11 @@ var DevPrinter = class {
4340
4297
  }
4341
4298
  };
4342
4299
  function createProgressHandler(printer) {
4343
- return (percent, message, ..._details) => {
4300
+ return (percent, message) => {
4344
4301
  printer.updateProgress(percent, message);
4345
4302
  };
4346
4303
  }
4347
- function registerShortcuts(printer, opts) {
4304
+ function registerShortcuts(getPrinter, opts) {
4348
4305
  if (!process.stdin.isTTY) return;
4349
4306
  process.stdin.setEncoding("utf-8");
4350
4307
  process.stdin.setRawMode?.(false);
@@ -4356,10 +4313,10 @@ function registerShortcuts(printer, opts) {
4356
4313
  buffer = "";
4357
4314
  switch (cmd) {
4358
4315
  case "h":
4359
- printer.printShortcuts();
4316
+ getPrinter().printShortcuts();
4360
4317
  break;
4361
4318
  case "o": {
4362
- const url = `http://localhost:${opts.port}/`;
4319
+ const url = `http://localhost:${opts.getPort()}/`;
4363
4320
  import("child_process").then((cp) => {
4364
4321
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
4365
4322
  cp.exec(`${command} ${url}`);
@@ -4441,7 +4398,7 @@ async function build(options = {}) {
4441
4398
  rspackConfig.stats = "none";
4442
4399
  rspackConfig.infrastructureLogging = { level: "none" };
4443
4400
  const compiler = rspack4(rspackConfig);
4444
- const stats = await new Promise((resolve4, reject) => {
4401
+ const stats = await new Promise((resolve5, reject) => {
4445
4402
  compiler.run((err, stats2) => {
4446
4403
  if (err) {
4447
4404
  reject(err);
@@ -4451,13 +4408,13 @@ async function build(options = {}) {
4451
4408
  reject(new Error("Build failed: no stats available"));
4452
4409
  return;
4453
4410
  }
4454
- resolve4(stats2);
4411
+ resolve5(stats2);
4455
4412
  });
4456
4413
  });
4457
- await new Promise((resolve4, reject) => {
4414
+ await new Promise((resolve5, reject) => {
4458
4415
  compiler.close((err) => {
4459
4416
  if (err) reject(err);
4460
- else resolve4();
4417
+ else resolve5();
4461
4418
  });
4462
4419
  });
4463
4420
  const hasErrors = stats.hasErrors();
@@ -4488,17 +4445,17 @@ import chalk3 from "chalk";
4488
4445
  // src/cli/port.ts
4489
4446
  import net from "net";
4490
4447
  function isPortAvailable(port, host) {
4491
- return new Promise((resolve4) => {
4448
+ return new Promise((resolve5) => {
4492
4449
  const server = net.createServer();
4493
4450
  server.once("error", (err) => {
4494
4451
  if (err.code === "EADDRINUSE") {
4495
- resolve4(false);
4452
+ resolve5(false);
4496
4453
  } else {
4497
- resolve4(false);
4454
+ resolve5(false);
4498
4455
  }
4499
4456
  });
4500
4457
  server.once("listening", () => {
4501
- server.close(() => resolve4(true));
4458
+ server.close(() => resolve5(true));
4502
4459
  });
4503
4460
  server.listen(port, host);
4504
4461
  });
@@ -4515,88 +4472,211 @@ async function getAvailablePort(preferredPort, host) {
4515
4472
  );
4516
4473
  }
4517
4474
 
4475
+ // src/cli/restart-watcher.ts
4476
+ import { existsSync as existsSync13, watch as watch2 } from "fs";
4477
+ import { resolve as resolve4 } from "path";
4478
+ function isTriggerFile(filename, mode) {
4479
+ if (!filename) return false;
4480
+ if (CONFIG_FILES.includes(filename)) return true;
4481
+ if (filename === ".env" || filename === ".env.public" || filename === `.env.${mode}`) {
4482
+ return true;
4483
+ }
4484
+ return false;
4485
+ }
4486
+ function normalizeFilename(filename) {
4487
+ if (typeof filename === "string") return filename;
4488
+ if (Buffer.isBuffer(filename)) return filename.toString("utf8");
4489
+ return null;
4490
+ }
4491
+ function watchForRestart(opts) {
4492
+ const { cwd, mode, onChange, debounceMs = 300 } = opts;
4493
+ const watchers = [];
4494
+ let timer = null;
4495
+ const trigger = (filename) => {
4496
+ const name = normalizeFilename(filename);
4497
+ if (!isTriggerFile(name, mode)) return;
4498
+ if (timer) clearTimeout(timer);
4499
+ timer = setTimeout(() => {
4500
+ timer = null;
4501
+ Promise.resolve(onChange()).catch((err) => {
4502
+ console.error("[ywkf] \u81EA\u52A8\u91CD\u542F\u56DE\u8C03\u51FA\u9519:", err);
4503
+ });
4504
+ }, debounceMs);
4505
+ };
4506
+ watchers.push(watch2(cwd, { recursive: false }, (_event, filename) => trigger(filename)));
4507
+ const envDir = resolve4(cwd, "config/env");
4508
+ if (existsSync13(envDir)) {
4509
+ watchers.push(watch2(envDir, { recursive: false }, (_event, filename) => trigger(filename)));
4510
+ }
4511
+ return {
4512
+ close: () => {
4513
+ if (timer) clearTimeout(timer);
4514
+ for (const w of watchers) w.close();
4515
+ }
4516
+ };
4517
+ }
4518
+
4518
4519
  // src/cli/dev.ts
4519
- async function dev(options = {}) {
4520
- const cwd = options.cwd || process.cwd();
4521
- const mode = options.mode || "development";
4522
- try {
4523
- preloadEnv(cwd, mode, "development");
4524
- const { config } = await resolveConfig(cwd);
4525
- loadEnv(config, cwd, mode, "development");
4526
- const pluginManager = new PluginManager(config, cwd, true);
4527
- if (config.plugins && config.plugins.length > 0) {
4528
- await pluginManager.loadPlugins(config.plugins);
4520
+ function restoreEnv(snapshot) {
4521
+ for (const key of Object.keys(process.env)) {
4522
+ if (!(key in snapshot)) {
4523
+ delete process.env[key];
4529
4524
  }
4530
- await pluginManager.runBeforeDevServer();
4531
- let rspackConfig = createRspackConfig(config, cwd, { isDev: true });
4532
- rspackConfig = await pluginManager.applyRspackConfigHooks(rspackConfig);
4533
- const host = config.dev.host || "localhost";
4534
- const preferredPort = config.dev.port || 3e3;
4535
- const port = await getAvailablePort(preferredPort, host);
4536
- const pluginNames = pluginManager.getPluginNames();
4537
- if (port !== preferredPort) {
4538
- console.log(chalk3.yellow(` Port ${preferredPort} is in use, using ${port} instead.
4525
+ }
4526
+ for (const key of Object.keys(snapshot)) {
4527
+ process.env[key] = snapshot[key];
4528
+ }
4529
+ }
4530
+ async function loadFullConfig(cwd, mode, envSnapshot, opts = {}) {
4531
+ restoreEnv(envSnapshot);
4532
+ preloadEnv(cwd, mode, "development");
4533
+ const { config } = await resolveConfig(cwd, { fresh: opts.fresh });
4534
+ loadEnv(config, cwd, mode, "development");
4535
+ return config;
4536
+ }
4537
+ async function startDevServer(cwd, config) {
4538
+ const pluginManager = new PluginManager(config, cwd, true);
4539
+ if (config.plugins && config.plugins.length > 0) {
4540
+ await pluginManager.loadPlugins(config.plugins);
4541
+ }
4542
+ await pluginManager.runBeforeDevServer();
4543
+ let rspackConfig = createRspackConfig(config, cwd, { isDev: true });
4544
+ rspackConfig = await pluginManager.applyRspackConfigHooks(rspackConfig);
4545
+ const host = config.dev.host || "localhost";
4546
+ const preferredPort = config.dev.port || 3e3;
4547
+ const port = await getAvailablePort(preferredPort, host);
4548
+ const pluginNames = pluginManager.getPluginNames();
4549
+ if (port !== preferredPort) {
4550
+ console.log(chalk3.yellow(` Port ${preferredPort} is in use, using ${port} instead.
4539
4551
  `));
4540
- }
4541
- const printer = new DevPrinter(host, port, pluginNames);
4542
- printer.printBanner();
4543
- printer.printBuildStart();
4544
- printer.updateProgress(0, "preparing");
4545
- rspackConfig.plugins = rspackConfig.plugins || [];
4546
- rspackConfig.plugins.push(new rspack5.ProgressPlugin(createProgressHandler(printer)));
4547
- rspackConfig.stats = "none";
4548
- rspackConfig.infrastructureLogging = { level: "none" };
4549
- if (rspackConfig.devServer) {
4550
- const ds = rspackConfig.devServer;
4551
- const existingClient = ds.client ?? {};
4552
- ds.client = {
4553
- ...existingClient,
4554
- logging: "warn",
4555
- overlay: false,
4556
- progress: false
4557
- };
4558
- }
4559
- const compiler = rspack5(rspackConfig);
4560
- compiler.hooks.done.tap("ywkf-dev-printer", (stats) => {
4561
- const hasErrors = stats.hasErrors();
4562
- if (hasErrors) {
4563
- const info = stats.toJson({ errors: true });
4564
- console.log();
4565
- console.log(chalk3.red(" Compile error:"));
4566
- for (const err of info.errors || []) {
4567
- console.log(chalk3.red(` ${err.message}`));
4568
- }
4569
- console.log();
4552
+ }
4553
+ const printer = new DevPrinter(host, port, pluginNames);
4554
+ printer.printBanner();
4555
+ printer.printBuildStart();
4556
+ printer.updateProgress(0, "preparing");
4557
+ rspackConfig.plugins = rspackConfig.plugins || [];
4558
+ rspackConfig.plugins.push(new rspack5.ProgressPlugin(createProgressHandler(printer)));
4559
+ rspackConfig.stats = "none";
4560
+ rspackConfig.infrastructureLogging = { level: "none" };
4561
+ if (rspackConfig.devServer) {
4562
+ const ds = rspackConfig.devServer;
4563
+ const existingClient = ds.client ?? {};
4564
+ ds.client = {
4565
+ ...existingClient,
4566
+ logging: "warn",
4567
+ overlay: false,
4568
+ progress: false
4569
+ };
4570
+ }
4571
+ const compiler = rspack5(rspackConfig);
4572
+ compiler.hooks.done.tap("ywkf-dev-printer", (stats) => {
4573
+ const hasErrors = stats.hasErrors();
4574
+ if (hasErrors) {
4575
+ const info = stats.toJson({ errors: true });
4576
+ console.log();
4577
+ console.log(chalk3.red(" Compile error:"));
4578
+ for (const err of info.errors || []) {
4579
+ console.log(chalk3.red(` ${err.message}`));
4570
4580
  }
4571
- printer.printBuildDone(hasErrors);
4572
- });
4573
- compiler.hooks.invalid.tap("ywkf-dev-printer", () => {
4574
- printer.markRebuildStart();
4575
- printer.updateProgress(0, "rebuilding");
4581
+ console.log();
4582
+ }
4583
+ printer.printBuildDone(hasErrors);
4584
+ });
4585
+ compiler.hooks.invalid.tap("ywkf-dev-printer", () => {
4586
+ printer.markRebuildStart();
4587
+ printer.updateProgress(0, "rebuilding");
4588
+ });
4589
+ const devServerOptions = {
4590
+ ...rspackConfig.devServer || {},
4591
+ host,
4592
+ port,
4593
+ setupExitSignals: false
4594
+ };
4595
+ const server = new RspackDevServer(devServerOptions, compiler);
4596
+ await server.start();
4597
+ await pluginManager.runAfterDevServer({ host, port });
4598
+ let closed = false;
4599
+ const close = async () => {
4600
+ if (closed) return;
4601
+ closed = true;
4602
+ await server.stop();
4603
+ await new Promise((resolve5, reject) => {
4604
+ compiler.close((err) => {
4605
+ if (err) reject(err);
4606
+ else resolve5();
4607
+ });
4576
4608
  });
4577
- const devServerOptions = {
4578
- ...rspackConfig.devServer || {},
4579
- host,
4580
- port
4609
+ };
4610
+ return { printer, host, port, close };
4611
+ }
4612
+ async function dev(options = {}) {
4613
+ const cwd = options.cwd || process.cwd();
4614
+ const mode = options.mode || "development";
4615
+ const envSnapshot = { ...process.env };
4616
+ try {
4617
+ const config = await loadFullConfig(cwd, mode, envSnapshot);
4618
+ let current = await startDevServer(cwd, config);
4619
+ let shuttingDown = false;
4620
+ const shutdown = async () => {
4621
+ if (shuttingDown) return;
4622
+ shuttingDown = true;
4623
+ await current.close();
4624
+ process.exit(0);
4581
4625
  };
4582
- const server = new RspackDevServer(devServerOptions, compiler);
4583
- await server.start();
4584
- await pluginManager.runAfterDevServer({ host, port });
4585
- registerShortcuts(printer, {
4586
- port,
4626
+ registerShortcuts(() => current.printer, {
4627
+ getPort: () => current.port,
4587
4628
  onQuit: async () => {
4588
4629
  console.log(chalk3.gray("\n Shutting down...\n"));
4589
- await server.stop();
4590
- process.exit(0);
4630
+ await shutdown();
4591
4631
  }
4592
4632
  });
4593
4633
  const signals = ["SIGINT", "SIGTERM"];
4594
4634
  for (const signal of signals) {
4595
- process.on(signal, async () => {
4596
- await server.stop();
4597
- process.exit(0);
4635
+ process.on(signal, () => {
4636
+ void shutdown();
4598
4637
  });
4599
4638
  }
4639
+ let isRestarting = false;
4640
+ let pending = false;
4641
+ watchForRestart({
4642
+ cwd,
4643
+ mode,
4644
+ onChange: async () => {
4645
+ if (isRestarting) {
4646
+ pending = true;
4647
+ return;
4648
+ }
4649
+ isRestarting = true;
4650
+ try {
4651
+ do {
4652
+ pending = false;
4653
+ console.log(chalk3.cyan("\n \u27F3 \u68C0\u6D4B\u5230\u914D\u7F6E\u53D8\u66F4\uFF0C\u6B63\u5728\u91CD\u542F dev server...\n"));
4654
+ let nextConfig;
4655
+ try {
4656
+ nextConfig = await loadFullConfig(cwd, mode, envSnapshot, { fresh: true });
4657
+ } catch (error) {
4658
+ console.error(
4659
+ chalk3.red(" \u2716 \u914D\u7F6E\u52A0\u8F7D\u5931\u8D25\uFF0C\u4FDD\u6301\u5F53\u524D dev server \u8FD0\u884C\uFF08\u4FEE\u6B63\u540E\u4FDD\u5B58\u53EF\u91CD\u8BD5\uFF09:")
4660
+ );
4661
+ console.error(error);
4662
+ restoreEnv(envSnapshot);
4663
+ continue;
4664
+ }
4665
+ try {
4666
+ await current.close();
4667
+ current = await startDevServer(cwd, nextConfig);
4668
+ } catch (error) {
4669
+ console.error(
4670
+ chalk3.red(" \u2716 dev server \u91CD\u542F\u5931\u8D25\uFF0C\u5F53\u524D\u670D\u52A1\u4E0D\u53EF\u7528\uFF0C\u8BF7\u4FEE\u6B63\u540E\u4FDD\u5B58\u91CD\u8BD5:")
4671
+ );
4672
+ console.error(error);
4673
+ }
4674
+ } while (pending);
4675
+ } finally {
4676
+ isRestarting = false;
4677
+ }
4678
+ }
4679
+ });
4600
4680
  } catch (error) {
4601
4681
  console.error();
4602
4682
  console.error(chalk3.red(" \u2716 Dev server failed to start"));
@@ -4606,7 +4686,7 @@ async function dev(options = {}) {
4606
4686
  }
4607
4687
 
4608
4688
  // src/router/plugin.ts
4609
- import { existsSync as existsSync13, watch as watch2 } from "fs";
4689
+ import { existsSync as existsSync14, watch as watch3 } from "fs";
4610
4690
  import { join as join14 } from "path";
4611
4691
  var ConventionalRoutePlugin = class {
4612
4692
  options;
@@ -4626,7 +4706,7 @@ var ConventionalRoutePlugin = class {
4626
4706
  callback();
4627
4707
  });
4628
4708
  if (this.options.watch && !this.isWatching) {
4629
- this.watchPages();
4709
+ this.watchPages(compiler);
4630
4710
  }
4631
4711
  }
4632
4712
  /**
@@ -4661,13 +4741,13 @@ var ConventionalRoutePlugin = class {
4661
4741
  /**
4662
4742
  * 监听页面目录变化
4663
4743
  */
4664
- watchPages() {
4665
- if (!existsSync13(this.options.pagesDir)) {
4744
+ watchPages(compiler) {
4745
+ if (!existsSync14(this.options.pagesDir)) {
4666
4746
  return;
4667
4747
  }
4668
4748
  this.isWatching = true;
4669
4749
  let debounceTimer = null;
4670
- const watcher = watch2(this.options.pagesDir, { recursive: true }, (_eventType, filename) => {
4750
+ const watcher = watch3(this.options.pagesDir, { recursive: true }, (_eventType, filename) => {
4671
4751
  if (!filename?.match(/\.(tsx?|jsx?)$/)) {
4672
4752
  return;
4673
4753
  }
@@ -4679,8 +4759,12 @@ var ConventionalRoutePlugin = class {
4679
4759
  this.generateRoutes();
4680
4760
  }, 500);
4681
4761
  });
4682
- process.on("exit", () => {
4762
+ compiler.hooks.shutdown.tap("ConventionalRoutePlugin", () => {
4763
+ if (debounceTimer) clearTimeout(debounceTimer);
4683
4764
  watcher.close();
4765
+ this.isWatching = false;
4766
+ this.hasGenerated = false;
4767
+ this.lastGeneratedContent = "";
4684
4768
  });
4685
4769
  }
4686
4770
  };