@4399ywkf/core 5.1.1 → 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/cli/index.js CHANGED
@@ -879,7 +879,7 @@ function createMockMiddleware(mockPath, options) {
879
879
  scanMockFiles(mockPath);
880
880
  return async (_req, _res, next) => {
881
881
  if (delay > 0) {
882
- await new Promise((resolve4) => setTimeout(resolve4, delay));
882
+ await new Promise((resolve5) => setTimeout(resolve5, delay));
883
883
  }
884
884
  next();
885
885
  };
@@ -1833,11 +1833,11 @@ var zustandPlugin = createPlugin((options = {}) => ({
1833
1833
  const hooks = {
1834
1834
  modifyRspackConfig(rspackConfig) {
1835
1835
  logger.info("Zustand \u5DF2\u542F\u7528");
1836
- const resolve4 = rspackConfig.resolve || {};
1837
- const alias = resolve4.alias || {};
1836
+ const resolve5 = rspackConfig.resolve || {};
1837
+ const alias = resolve5.alias || {};
1838
1838
  alias["@store"] = storePath;
1839
- resolve4.alias = alias;
1840
- rspackConfig.resolve = resolve4;
1839
+ resolve5.alias = alias;
1840
+ rspackConfig.resolve = resolve5;
1841
1841
  return rspackConfig;
1842
1842
  },
1843
1843
  generateFiles(_ctx) {
@@ -2191,32 +2191,39 @@ function findConfigFile(cwd) {
2191
2191
  }
2192
2192
  return null;
2193
2193
  }
2194
- async function loadTsConfig(configPath) {
2194
+ async function loadTsConfig(configPath, fresh = false) {
2195
2195
  const jiti = (await import("jiti")).default;
2196
2196
  const loader = jiti(configPath, {
2197
- interopDefault: true
2197
+ interopDefault: true,
2198
+ // fresh(热重启)时必须禁用缓存:jiti 的 moduleCache 默认与 Node 原生 require 缓存集成,
2199
+ // 即使新建 jiti 实例,loader(configPath) 仍会命中 Node 全局缓存读回旧配置;
2200
+ // fsCache 为转译产物缓存,一并关闭以确保读到最新源码。
2201
+ ...fresh ? { moduleCache: false, fsCache: false } : {}
2198
2202
  });
2199
2203
  const config = loader(configPath);
2200
2204
  return config.default || config;
2201
2205
  }
2202
- async function loadJsConfig(configPath) {
2203
- 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
+ }
2204
2211
  const module = await import(fileUrl);
2205
2212
  return module.default || module;
2206
2213
  }
2207
- async function loadConfigFile(configPath) {
2214
+ async function loadConfigFile(configPath, opts = {}) {
2208
2215
  const ext = extname(configPath);
2209
2216
  if (ext === ".ts" || ext === ".mts") {
2210
- return loadTsConfig(configPath);
2217
+ return loadTsConfig(configPath, opts.fresh);
2211
2218
  }
2212
- return loadJsConfig(configPath);
2219
+ return loadJsConfig(configPath, opts.fresh);
2213
2220
  }
2214
2221
  function mergeConfig(userConfig, baseConfig = defaultConfig) {
2215
2222
  return deepmerge(baseConfig, userConfig, {
2216
2223
  arrayMerge: (_, sourceArray) => sourceArray
2217
2224
  });
2218
2225
  }
2219
- async function resolveConfig(cwd) {
2226
+ async function resolveConfig(cwd, opts = {}) {
2220
2227
  const configPath = findConfigFile(cwd);
2221
2228
  if (!configPath) {
2222
2229
  console.warn("\u26A0\uFE0F \u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6 (ywkf.config.ts)\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E");
@@ -2226,7 +2233,7 @@ async function resolveConfig(cwd) {
2226
2233
  };
2227
2234
  }
2228
2235
  try {
2229
- const userConfig = await loadConfigFile(configPath);
2236
+ const userConfig = await loadConfigFile(configPath, opts);
2230
2237
  const normalized = normalizeConfig(userConfig);
2231
2238
  const config = mergeConfig(normalized);
2232
2239
  return { config, configPath };
@@ -3374,16 +3381,6 @@ var YwkfGeneratorPlugin = class {
3374
3381
  );
3375
3382
  this.initialized = true;
3376
3383
  }
3377
- resetState() {
3378
- this.initialized = false;
3379
- this.pluginHooks = [];
3380
- this.generator = null;
3381
- }
3382
- async regenerate() {
3383
- if (this.generator) {
3384
- await this.generator.generate();
3385
- }
3386
- }
3387
3384
  apply(compiler) {
3388
3385
  const pluginName = "YwkfGeneratorPlugin";
3389
3386
  compiler.hooks.beforeCompile.tapAsync(pluginName, async (_params, callback) => {
@@ -3399,14 +3396,13 @@ var YwkfGeneratorPlugin = class {
3399
3396
  }
3400
3397
  });
3401
3398
  if (this.options.isDev && !this.isWatching) {
3402
- this.watchPages();
3403
- this.watchConfig();
3399
+ this.watchPages(compiler);
3404
3400
  }
3405
3401
  }
3406
3402
  /**
3407
3403
  * 监听页面目录变化
3408
3404
  */
3409
- watchPages() {
3405
+ watchPages(compiler) {
3410
3406
  const { config, cwd } = this.options;
3411
3407
  const pagesDir = join11(cwd, config.router.pagesDir || "src/pages");
3412
3408
  if (!existsSync11(pagesDir)) {
@@ -3428,47 +3424,11 @@ var YwkfGeneratorPlugin = class {
3428
3424
  }
3429
3425
  }, 500);
3430
3426
  });
3431
- process.on("exit", () => {
3432
- watcher.close();
3433
- });
3434
- }
3435
- /**
3436
- * 监听配置文件变化,重新加载配置并重新生成 .ywkf 文件
3437
- */
3438
- watchConfig() {
3439
- const { cwd } = this.options;
3440
- const configPath = findConfigFile(cwd);
3441
- if (!configPath) {
3442
- return;
3443
- }
3444
- let debounceTimer = null;
3445
- const watcher = watch(configPath, async () => {
3446
- if (debounceTimer) {
3447
- clearTimeout(debounceTimer);
3448
- }
3449
- debounceTimer = setTimeout(async () => {
3450
- try {
3451
- console.log(`
3452
- [ywkf] \u68C0\u6D4B\u5230\u914D\u7F6E\u6587\u4EF6\u53D8\u5316\uFF0C\u91CD\u65B0\u751F\u6210 .ywkf \u76EE\u5F55...
3453
- `);
3454
- const userConfig = await loadConfigFile(configPath);
3455
- const newConfig = mergeConfig(userConfig);
3456
- this.options.config = newConfig;
3457
- this.options.pluginConfigs = newConfig.plugins;
3458
- this.resetState();
3459
- await this.initialize();
3460
- await this.regenerate();
3461
- console.log(
3462
- ` [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
3463
- `
3464
- );
3465
- } catch (error) {
3466
- console.error(` [ywkf] \u914D\u7F6E\u91CD\u8F7D\u5931\u8D25:`, error);
3467
- }
3468
- }, 500);
3469
- });
3470
- process.on("exit", () => {
3427
+ compiler.hooks.shutdown.tap("YwkfGeneratorPlugin", () => {
3428
+ if (debounceTimer) clearTimeout(debounceTimer);
3471
3429
  watcher.close();
3430
+ this.isWatching = false;
3431
+ this.hasGenerated = false;
3472
3432
  });
3473
3433
  }
3474
3434
  };
@@ -4338,7 +4298,7 @@ function createProgressHandler(printer) {
4338
4298
  printer.updateProgress(percent, message);
4339
4299
  };
4340
4300
  }
4341
- function registerShortcuts(printer, opts) {
4301
+ function registerShortcuts(getPrinter, opts) {
4342
4302
  if (!process.stdin.isTTY) return;
4343
4303
  process.stdin.setEncoding("utf-8");
4344
4304
  process.stdin.setRawMode?.(false);
@@ -4350,10 +4310,10 @@ function registerShortcuts(printer, opts) {
4350
4310
  buffer = "";
4351
4311
  switch (cmd) {
4352
4312
  case "h":
4353
- printer.printShortcuts();
4313
+ getPrinter().printShortcuts();
4354
4314
  break;
4355
4315
  case "o": {
4356
- const url = `http://localhost:${opts.port}/`;
4316
+ const url = `http://localhost:${opts.getPort()}/`;
4357
4317
  import("child_process").then((cp) => {
4358
4318
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
4359
4319
  cp.exec(`${command} ${url}`);
@@ -4435,7 +4395,7 @@ async function build(options = {}) {
4435
4395
  rspackConfig.stats = "none";
4436
4396
  rspackConfig.infrastructureLogging = { level: "none" };
4437
4397
  const compiler = rspack4(rspackConfig);
4438
- const stats = await new Promise((resolve4, reject) => {
4398
+ const stats = await new Promise((resolve5, reject) => {
4439
4399
  compiler.run((err, stats2) => {
4440
4400
  if (err) {
4441
4401
  reject(err);
@@ -4445,13 +4405,13 @@ async function build(options = {}) {
4445
4405
  reject(new Error("Build failed: no stats available"));
4446
4406
  return;
4447
4407
  }
4448
- resolve4(stats2);
4408
+ resolve5(stats2);
4449
4409
  });
4450
4410
  });
4451
- await new Promise((resolve4, reject) => {
4411
+ await new Promise((resolve5, reject) => {
4452
4412
  compiler.close((err) => {
4453
4413
  if (err) reject(err);
4454
- else resolve4();
4414
+ else resolve5();
4455
4415
  });
4456
4416
  });
4457
4417
  const hasErrors = stats.hasErrors();
@@ -4482,17 +4442,17 @@ import chalk3 from "chalk";
4482
4442
  // src/cli/port.ts
4483
4443
  import net from "net";
4484
4444
  function isPortAvailable(port, host) {
4485
- return new Promise((resolve4) => {
4445
+ return new Promise((resolve5) => {
4486
4446
  const server = net.createServer();
4487
4447
  server.once("error", (err) => {
4488
4448
  if (err.code === "EADDRINUSE") {
4489
- resolve4(false);
4449
+ resolve5(false);
4490
4450
  } else {
4491
- resolve4(false);
4451
+ resolve5(false);
4492
4452
  }
4493
4453
  });
4494
4454
  server.once("listening", () => {
4495
- server.close(() => resolve4(true));
4455
+ server.close(() => resolve5(true));
4496
4456
  });
4497
4457
  server.listen(port, host);
4498
4458
  });
@@ -4509,88 +4469,211 @@ async function getAvailablePort(preferredPort, host) {
4509
4469
  );
4510
4470
  }
4511
4471
 
4472
+ // src/cli/restart-watcher.ts
4473
+ import { existsSync as existsSync13, watch as watch2 } from "fs";
4474
+ import { resolve as resolve4 } from "path";
4475
+ function isTriggerFile(filename, mode) {
4476
+ if (!filename) return false;
4477
+ if (CONFIG_FILES.includes(filename)) return true;
4478
+ if (filename === ".env" || filename === ".env.public" || filename === `.env.${mode}`) {
4479
+ return true;
4480
+ }
4481
+ return false;
4482
+ }
4483
+ function normalizeFilename(filename) {
4484
+ if (typeof filename === "string") return filename;
4485
+ if (Buffer.isBuffer(filename)) return filename.toString("utf8");
4486
+ return null;
4487
+ }
4488
+ function watchForRestart(opts) {
4489
+ const { cwd, mode, onChange, debounceMs = 300 } = opts;
4490
+ const watchers = [];
4491
+ let timer = null;
4492
+ const trigger = (filename) => {
4493
+ const name = normalizeFilename(filename);
4494
+ if (!isTriggerFile(name, mode)) return;
4495
+ if (timer) clearTimeout(timer);
4496
+ timer = setTimeout(() => {
4497
+ timer = null;
4498
+ Promise.resolve(onChange()).catch((err) => {
4499
+ console.error("[ywkf] \u81EA\u52A8\u91CD\u542F\u56DE\u8C03\u51FA\u9519:", err);
4500
+ });
4501
+ }, debounceMs);
4502
+ };
4503
+ watchers.push(watch2(cwd, { recursive: false }, (_event, filename) => trigger(filename)));
4504
+ const envDir = resolve4(cwd, "config/env");
4505
+ if (existsSync13(envDir)) {
4506
+ watchers.push(watch2(envDir, { recursive: false }, (_event, filename) => trigger(filename)));
4507
+ }
4508
+ return {
4509
+ close: () => {
4510
+ if (timer) clearTimeout(timer);
4511
+ for (const w of watchers) w.close();
4512
+ }
4513
+ };
4514
+ }
4515
+
4512
4516
  // src/cli/dev.ts
4513
- async function dev(options = {}) {
4514
- const cwd = options.cwd || process.cwd();
4515
- const mode = options.mode || "development";
4516
- try {
4517
- preloadEnv(cwd, mode, "development");
4518
- const { config } = await resolveConfig(cwd);
4519
- loadEnv(config, cwd, mode, "development");
4520
- const pluginManager = new PluginManager(config, cwd, true);
4521
- if (config.plugins && config.plugins.length > 0) {
4522
- await pluginManager.loadPlugins(config.plugins);
4517
+ function restoreEnv(snapshot) {
4518
+ for (const key of Object.keys(process.env)) {
4519
+ if (!(key in snapshot)) {
4520
+ delete process.env[key];
4523
4521
  }
4524
- await pluginManager.runBeforeDevServer();
4525
- let rspackConfig = createRspackConfig(config, cwd, { isDev: true });
4526
- rspackConfig = await pluginManager.applyRspackConfigHooks(rspackConfig);
4527
- const host = config.dev.host || "localhost";
4528
- const preferredPort = config.dev.port || 3e3;
4529
- const port = await getAvailablePort(preferredPort, host);
4530
- const pluginNames = pluginManager.getPluginNames();
4531
- if (port !== preferredPort) {
4532
- console.log(chalk3.yellow(` Port ${preferredPort} is in use, using ${port} instead.
4522
+ }
4523
+ for (const key of Object.keys(snapshot)) {
4524
+ process.env[key] = snapshot[key];
4525
+ }
4526
+ }
4527
+ async function loadFullConfig(cwd, mode, envSnapshot, opts = {}) {
4528
+ restoreEnv(envSnapshot);
4529
+ preloadEnv(cwd, mode, "development");
4530
+ const { config } = await resolveConfig(cwd, { fresh: opts.fresh });
4531
+ loadEnv(config, cwd, mode, "development");
4532
+ return config;
4533
+ }
4534
+ async function startDevServer(cwd, config) {
4535
+ const pluginManager = new PluginManager(config, cwd, true);
4536
+ if (config.plugins && config.plugins.length > 0) {
4537
+ await pluginManager.loadPlugins(config.plugins);
4538
+ }
4539
+ await pluginManager.runBeforeDevServer();
4540
+ let rspackConfig = createRspackConfig(config, cwd, { isDev: true });
4541
+ rspackConfig = await pluginManager.applyRspackConfigHooks(rspackConfig);
4542
+ const host = config.dev.host || "localhost";
4543
+ const preferredPort = config.dev.port || 3e3;
4544
+ const port = await getAvailablePort(preferredPort, host);
4545
+ const pluginNames = pluginManager.getPluginNames();
4546
+ if (port !== preferredPort) {
4547
+ console.log(chalk3.yellow(` Port ${preferredPort} is in use, using ${port} instead.
4533
4548
  `));
4534
- }
4535
- const printer = new DevPrinter(host, port, pluginNames);
4536
- printer.printBanner();
4537
- printer.printBuildStart();
4538
- printer.updateProgress(0, "preparing");
4539
- rspackConfig.plugins = rspackConfig.plugins || [];
4540
- rspackConfig.plugins.push(new rspack5.ProgressPlugin(createProgressHandler(printer)));
4541
- rspackConfig.stats = "none";
4542
- rspackConfig.infrastructureLogging = { level: "none" };
4543
- if (rspackConfig.devServer) {
4544
- const ds = rspackConfig.devServer;
4545
- const existingClient = ds.client ?? {};
4546
- ds.client = {
4547
- ...existingClient,
4548
- logging: "warn",
4549
- overlay: false,
4550
- progress: false
4551
- };
4552
- }
4553
- const compiler = rspack5(rspackConfig);
4554
- compiler.hooks.done.tap("ywkf-dev-printer", (stats) => {
4555
- const hasErrors = stats.hasErrors();
4556
- if (hasErrors) {
4557
- const info = stats.toJson({ errors: true });
4558
- console.log();
4559
- console.log(chalk3.red(" Compile error:"));
4560
- for (const err of info.errors || []) {
4561
- console.log(chalk3.red(` ${err.message}`));
4562
- }
4563
- console.log();
4549
+ }
4550
+ const printer = new DevPrinter(host, port, pluginNames);
4551
+ printer.printBanner();
4552
+ printer.printBuildStart();
4553
+ printer.updateProgress(0, "preparing");
4554
+ rspackConfig.plugins = rspackConfig.plugins || [];
4555
+ rspackConfig.plugins.push(new rspack5.ProgressPlugin(createProgressHandler(printer)));
4556
+ rspackConfig.stats = "none";
4557
+ rspackConfig.infrastructureLogging = { level: "none" };
4558
+ if (rspackConfig.devServer) {
4559
+ const ds = rspackConfig.devServer;
4560
+ const existingClient = ds.client ?? {};
4561
+ ds.client = {
4562
+ ...existingClient,
4563
+ logging: "warn",
4564
+ overlay: false,
4565
+ progress: false
4566
+ };
4567
+ }
4568
+ const compiler = rspack5(rspackConfig);
4569
+ compiler.hooks.done.tap("ywkf-dev-printer", (stats) => {
4570
+ const hasErrors = stats.hasErrors();
4571
+ if (hasErrors) {
4572
+ const info = stats.toJson({ errors: true });
4573
+ console.log();
4574
+ console.log(chalk3.red(" Compile error:"));
4575
+ for (const err of info.errors || []) {
4576
+ console.log(chalk3.red(` ${err.message}`));
4564
4577
  }
4565
- printer.printBuildDone(hasErrors);
4566
- });
4567
- compiler.hooks.invalid.tap("ywkf-dev-printer", () => {
4568
- printer.markRebuildStart();
4569
- printer.updateProgress(0, "rebuilding");
4578
+ console.log();
4579
+ }
4580
+ printer.printBuildDone(hasErrors);
4581
+ });
4582
+ compiler.hooks.invalid.tap("ywkf-dev-printer", () => {
4583
+ printer.markRebuildStart();
4584
+ printer.updateProgress(0, "rebuilding");
4585
+ });
4586
+ const devServerOptions = {
4587
+ ...rspackConfig.devServer || {},
4588
+ host,
4589
+ port,
4590
+ setupExitSignals: false
4591
+ };
4592
+ const server = new RspackDevServer(devServerOptions, compiler);
4593
+ await server.start();
4594
+ await pluginManager.runAfterDevServer({ host, port });
4595
+ let closed = false;
4596
+ const close = async () => {
4597
+ if (closed) return;
4598
+ closed = true;
4599
+ await server.stop();
4600
+ await new Promise((resolve5, reject) => {
4601
+ compiler.close((err) => {
4602
+ if (err) reject(err);
4603
+ else resolve5();
4604
+ });
4570
4605
  });
4571
- const devServerOptions = {
4572
- ...rspackConfig.devServer || {},
4573
- host,
4574
- port
4606
+ };
4607
+ return { printer, host, port, close };
4608
+ }
4609
+ async function dev(options = {}) {
4610
+ const cwd = options.cwd || process.cwd();
4611
+ const mode = options.mode || "development";
4612
+ const envSnapshot = { ...process.env };
4613
+ try {
4614
+ const config = await loadFullConfig(cwd, mode, envSnapshot);
4615
+ let current = await startDevServer(cwd, config);
4616
+ let shuttingDown = false;
4617
+ const shutdown = async () => {
4618
+ if (shuttingDown) return;
4619
+ shuttingDown = true;
4620
+ await current.close();
4621
+ process.exit(0);
4575
4622
  };
4576
- const server = new RspackDevServer(devServerOptions, compiler);
4577
- await server.start();
4578
- await pluginManager.runAfterDevServer({ host, port });
4579
- registerShortcuts(printer, {
4580
- port,
4623
+ registerShortcuts(() => current.printer, {
4624
+ getPort: () => current.port,
4581
4625
  onQuit: async () => {
4582
4626
  console.log(chalk3.gray("\n Shutting down...\n"));
4583
- await server.stop();
4584
- process.exit(0);
4627
+ await shutdown();
4585
4628
  }
4586
4629
  });
4587
4630
  const signals = ["SIGINT", "SIGTERM"];
4588
4631
  for (const signal of signals) {
4589
- process.on(signal, async () => {
4590
- await server.stop();
4591
- process.exit(0);
4632
+ process.on(signal, () => {
4633
+ void shutdown();
4592
4634
  });
4593
4635
  }
4636
+ let isRestarting = false;
4637
+ let pending = false;
4638
+ watchForRestart({
4639
+ cwd,
4640
+ mode,
4641
+ onChange: async () => {
4642
+ if (isRestarting) {
4643
+ pending = true;
4644
+ return;
4645
+ }
4646
+ isRestarting = true;
4647
+ try {
4648
+ do {
4649
+ pending = false;
4650
+ console.log(chalk3.cyan("\n \u27F3 \u68C0\u6D4B\u5230\u914D\u7F6E\u53D8\u66F4\uFF0C\u6B63\u5728\u91CD\u542F dev server...\n"));
4651
+ let nextConfig;
4652
+ try {
4653
+ nextConfig = await loadFullConfig(cwd, mode, envSnapshot, { fresh: true });
4654
+ } catch (error) {
4655
+ console.error(
4656
+ 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:")
4657
+ );
4658
+ console.error(error);
4659
+ restoreEnv(envSnapshot);
4660
+ continue;
4661
+ }
4662
+ try {
4663
+ await current.close();
4664
+ current = await startDevServer(cwd, nextConfig);
4665
+ } catch (error) {
4666
+ console.error(
4667
+ 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:")
4668
+ );
4669
+ console.error(error);
4670
+ }
4671
+ } while (pending);
4672
+ } finally {
4673
+ isRestarting = false;
4674
+ }
4675
+ }
4676
+ });
4594
4677
  } catch (error) {
4595
4678
  console.error();
4596
4679
  console.error(chalk3.red(" \u2716 Dev server failed to start"));