@nasti-toolchain/nasti 2.3.1 → 2.4.0

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.js CHANGED
@@ -10,10 +10,10 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
10
10
  if (typeof require !== "undefined") return require.apply(this, arguments);
11
11
  throw Error('Dynamic require of "' + x + '" is not supported');
12
12
  });
13
- var __glob = (map) => (path18) => {
14
- var fn = map[path18];
13
+ var __glob = (map) => (path19) => {
14
+ var fn = map[path19];
15
15
  if (fn) return fn();
16
- throw new Error("Module not found in bundle: " + path18);
16
+ throw new Error("Module not found in bundle: " + path19);
17
17
  };
18
18
  var __esm = (fn, res) => function __init() {
19
19
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
@@ -481,6 +481,7 @@ async function resolveConfig(inlineConfig = {}, command) {
481
481
  if (envOptions.build) Object.assign(resolved.build, envOptions.build);
482
482
  resolved.environments.client = {
483
483
  consumer,
484
+ buildEnabled: envOptions.buildEnabled ?? true,
484
485
  entry: normalizeEnvironmentEntries(envOptions.entry, root),
485
486
  html: path.resolve(
486
487
  root,
@@ -495,6 +496,7 @@ async function resolveConfig(inlineConfig = {}, command) {
495
496
  }
496
497
  resolved.environments[name] = {
497
498
  consumer,
499
+ buildEnabled: envOptions.buildEnabled ?? true,
498
500
  entry: normalizeEnvironmentEntries(envOptions.entry, root),
499
501
  html: envOptions.consumer === "client" && envOptions.html ? path.resolve(root, envOptions.html) : void 0,
500
502
  driver: envOptions.driver,
@@ -721,6 +723,7 @@ function resolvePlugin(config) {
721
723
  }
722
724
  if (!source.startsWith("/") && !source.startsWith(".")) {
723
725
  if (vueRuntimeEntry && source === "vue") return vueRuntimeEntry;
726
+ if (config.command === "build") return null;
724
727
  try {
725
728
  const resolved = require2.resolve(source, {
726
729
  paths: [importer ? path2.dirname(importer) : config.root]
@@ -852,27 +855,27 @@ var require_process = __commonJS({
852
855
  var require_filesystem = __commonJS({
853
856
  "node_modules/detect-libc/lib/filesystem.js"(exports, module) {
854
857
  "use strict";
855
- var fs13 = __require("fs");
858
+ var fs14 = __require("fs");
856
859
  var LDD_PATH = "/usr/bin/ldd";
857
860
  var SELF_PATH = "/proc/self/exe";
858
861
  var MAX_LENGTH = 2048;
859
- var readFileSync = (path18) => {
860
- const fd = fs13.openSync(path18, "r");
862
+ var readFileSync = (path19) => {
863
+ const fd = fs14.openSync(path19, "r");
861
864
  const buffer = Buffer.alloc(MAX_LENGTH);
862
- const bytesRead = fs13.readSync(fd, buffer, 0, MAX_LENGTH, 0);
863
- fs13.close(fd, () => {
865
+ const bytesRead = fs14.readSync(fd, buffer, 0, MAX_LENGTH, 0);
866
+ fs14.close(fd, () => {
864
867
  });
865
868
  return buffer.subarray(0, bytesRead);
866
869
  };
867
- var readFile = (path18) => new Promise((resolve, reject) => {
868
- fs13.open(path18, "r", (err, fd) => {
870
+ var readFile = (path19) => new Promise((resolve, reject) => {
871
+ fs14.open(path19, "r", (err, fd) => {
869
872
  if (err) {
870
873
  reject(err);
871
874
  } else {
872
875
  const buffer = Buffer.alloc(MAX_LENGTH);
873
- fs13.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
876
+ fs14.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
874
877
  resolve(buffer.subarray(0, bytesRead));
875
- fs13.close(fd, () => {
878
+ fs14.close(fd, () => {
876
879
  });
877
880
  });
878
881
  }
@@ -984,11 +987,11 @@ var require_detect_libc = __commonJS({
984
987
  }
985
988
  return null;
986
989
  };
987
- var familyFromInterpreterPath = (path18) => {
988
- if (path18) {
989
- if (path18.includes("/ld-musl-")) {
990
+ var familyFromInterpreterPath = (path19) => {
991
+ if (path19) {
992
+ if (path19.includes("/ld-musl-")) {
990
993
  return MUSL;
991
- } else if (path18.includes("/ld-linux-")) {
994
+ } else if (path19.includes("/ld-linux-")) {
992
995
  return GLIBC;
993
996
  }
994
997
  }
@@ -1035,8 +1038,8 @@ var require_detect_libc = __commonJS({
1035
1038
  cachedFamilyInterpreter = null;
1036
1039
  try {
1037
1040
  const selfContent = await readFile(SELF_PATH);
1038
- const path18 = interpreterPath(selfContent);
1039
- cachedFamilyInterpreter = familyFromInterpreterPath(path18);
1041
+ const path19 = interpreterPath(selfContent);
1042
+ cachedFamilyInterpreter = familyFromInterpreterPath(path19);
1040
1043
  } catch (e) {
1041
1044
  }
1042
1045
  return cachedFamilyInterpreter;
@@ -1048,8 +1051,8 @@ var require_detect_libc = __commonJS({
1048
1051
  cachedFamilyInterpreter = null;
1049
1052
  try {
1050
1053
  const selfContent = readFileSync(SELF_PATH);
1051
- const path18 = interpreterPath(selfContent);
1052
- cachedFamilyInterpreter = familyFromInterpreterPath(path18);
1054
+ const path19 = interpreterPath(selfContent);
1055
+ cachedFamilyInterpreter = familyFromInterpreterPath(path19);
1053
1056
  } catch (e) {
1054
1057
  }
1055
1058
  return cachedFamilyInterpreter;
@@ -2113,8 +2116,8 @@ function vuePlugin(config) {
2113
2116
  let descriptor = descriptorCache.get(filePath);
2114
2117
  if (!descriptor) {
2115
2118
  try {
2116
- const fs13 = await import("fs");
2117
- const source = fs13.readFileSync(filePath, "utf-8");
2119
+ const fs14 = await import("fs");
2120
+ const source = fs14.readFileSync(filePath, "utf-8");
2118
2121
  const parsed = sfc.parse(source, { filename: filePath });
2119
2122
  if (parsed.errors.length) return null;
2120
2123
  descriptor = parsed.descriptor;
@@ -2335,16 +2338,27 @@ window.__vite_plugin_react_preamble_installed__ = true;
2335
2338
  // src/plugins/builtins.ts
2336
2339
  function resolvePluginList(config, userPlugins, opts = {}) {
2337
2340
  const isServe = config.command === "serve";
2341
+ let environmentOptions;
2342
+ if (opts.environmentName) {
2343
+ environmentOptions = config.environments[opts.environmentName];
2344
+ if (!environmentOptions) {
2345
+ throw new Error(
2346
+ `[nasti] unknown environment "${opts.environmentName}" \u2014 declare it in config.environments`
2347
+ );
2348
+ }
2349
+ }
2350
+ const pluginConfig = environmentOptions ? { ...config, resolve: environmentOptions.resolve, build: environmentOptions.build } : config;
2351
+ const consumer = opts.consumer ?? environmentOptions?.consumer;
2338
2352
  return [
2339
2353
  // vuePlugin 排最前(enforce: 'pre' 语义):.vue 先编译成 JS 再走后续管道
2340
- ...config.framework === "vue" ? [vuePlugin(config)] : [],
2341
- resolvePlugin(config),
2342
- cssPlugin(config, opts.cssEngine, opts.consumer),
2343
- assetsPlugin(config),
2344
- ...isServe ? [htmlPlugin(config)] : [],
2354
+ ...config.framework === "vue" ? [vuePlugin(pluginConfig)] : [],
2355
+ resolvePlugin(pluginConfig),
2356
+ cssPlugin(pluginConfig, opts.cssEngine, consumer),
2357
+ assetsPlugin(pluginConfig),
2358
+ ...isServe ? [htmlPlugin(pluginConfig)] : [],
2345
2359
  ...userPlugins,
2346
2360
  // cssPostPlugin 最后(enforce: 'post' 语义):renderChunk 聚合抽取
2347
- ...!isServe && opts.cssEngine ? [cssPostPlugin(config, opts.cssEngine)] : []
2361
+ ...!isServe && opts.cssEngine ? [cssPostPlugin(pluginConfig, opts.cssEngine)] : []
2348
2362
  ];
2349
2363
  }
2350
2364
  var init_builtins = __esm({
@@ -2474,17 +2488,35 @@ var init_plugin_container = __esm({
2474
2488
  }
2475
2489
  });
2476
2490
 
2491
+ // src/core/url.ts
2492
+ function removeTimestampQuery(url) {
2493
+ const hashIndex = url.indexOf("#");
2494
+ const hash = hashIndex >= 0 ? url.slice(hashIndex) : "";
2495
+ const withoutHash = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
2496
+ const queryIndex = withoutHash.indexOf("?");
2497
+ if (queryIndex < 0) return url;
2498
+ const pathname = withoutHash.slice(0, queryIndex);
2499
+ const query = withoutHash.slice(queryIndex + 1).split("&").filter((part) => !/^t=\d+$/.test(part)).join("&");
2500
+ return pathname + (query ? `?${query}` : "") + hash;
2501
+ }
2502
+ var init_url = __esm({
2503
+ "src/core/url.ts"() {
2504
+ "use strict";
2505
+ }
2506
+ });
2507
+
2477
2508
  // src/core/module-graph.ts
2478
2509
  var ModuleGraph;
2479
2510
  var init_module_graph = __esm({
2480
2511
  "src/core/module-graph.ts"() {
2481
2512
  "use strict";
2513
+ init_url();
2482
2514
  ModuleGraph = class {
2483
2515
  urlToModuleMap = /* @__PURE__ */ new Map();
2484
2516
  idToModuleMap = /* @__PURE__ */ new Map();
2485
2517
  fileToModulesMap = /* @__PURE__ */ new Map();
2486
2518
  getModuleByUrl(url) {
2487
- return this.urlToModuleMap.get(url);
2519
+ return this.urlToModuleMap.get(removeTimestampQuery(url));
2488
2520
  }
2489
2521
  getModuleById(id) {
2490
2522
  return this.idToModuleMap.get(id);
@@ -2493,10 +2525,11 @@ var init_module_graph = __esm({
2493
2525
  return this.fileToModulesMap.get(file);
2494
2526
  }
2495
2527
  async ensureEntryFromUrl(url) {
2496
- let mod = this.urlToModuleMap.get(url);
2528
+ const normalizedUrl = removeTimestampQuery(url);
2529
+ let mod = this.urlToModuleMap.get(normalizedUrl);
2497
2530
  if (mod) return mod;
2498
- mod = this.createModule(url);
2499
- this.urlToModuleMap.set(url, mod);
2531
+ mod = this.createModule(normalizedUrl);
2532
+ this.urlToModuleMap.set(normalizedUrl, mod);
2500
2533
  return mod;
2501
2534
  }
2502
2535
  createModule(url, id) {
@@ -2510,6 +2543,7 @@ var init_module_graph = __esm({
2510
2543
  acceptedHmrDeps: /* @__PURE__ */ new Set(),
2511
2544
  transformResult: null,
2512
2545
  lastHMRTimestamp: 0,
2546
+ invalidationVersion: 0,
2513
2547
  isSelfAccepting: false
2514
2548
  };
2515
2549
  this.idToModuleMap.set(mod.id, mod);
@@ -2552,10 +2586,64 @@ var init_module_graph = __esm({
2552
2586
  }
2553
2587
  }
2554
2588
  }
2589
+ /**
2590
+ * 用一次转换得到的信息原子更新 import 与 HMR accept 关系。
2591
+ * 依赖节点会在真正被浏览器请求前预先创建,这样入口模块先转换时也能建立完整图。
2592
+ */
2593
+ async updateModuleInfo(mod, importedUrls, acceptedUrls, isSelfAccepting, expectedInvalidationVersion) {
2594
+ const importedModules = await Promise.all(
2595
+ [...importedUrls].map((url) => this.ensureEntryFromUrl(url))
2596
+ );
2597
+ const acceptedModules = await Promise.all(
2598
+ [...acceptedUrls].map((url) => this.ensureEntryFromUrl(url))
2599
+ );
2600
+ if (expectedInvalidationVersion !== void 0 && mod.invalidationVersion !== expectedInvalidationVersion) {
2601
+ return null;
2602
+ }
2603
+ const previousImports = new Set(mod.importedModules);
2604
+ for (const imported of previousImports) {
2605
+ imported.importers.delete(mod);
2606
+ }
2607
+ mod.importedModules.clear();
2608
+ mod.acceptedHmrDeps.clear();
2609
+ for (const imported of importedModules) {
2610
+ mod.importedModules.add(imported);
2611
+ imported.importers.add(mod);
2612
+ }
2613
+ for (const accepted of acceptedModules) {
2614
+ mod.acceptedHmrDeps.add(accepted);
2615
+ }
2616
+ mod.isSelfAccepting = isSelfAccepting;
2617
+ const pruned = /* @__PURE__ */ new Set();
2618
+ for (const imported of previousImports) {
2619
+ if (!mod.importedModules.has(imported) && imported.importers.size === 0) {
2620
+ pruned.add(imported);
2621
+ }
2622
+ }
2623
+ return pruned;
2624
+ }
2555
2625
  /** 使模块的转换缓存失效 */
2556
- invalidateModule(mod) {
2626
+ invalidateModule(mod, timestamp = Date.now()) {
2557
2627
  mod.transformResult = null;
2558
- mod.lastHMRTimestamp = Date.now();
2628
+ mod.lastHMRTimestamp = timestamp;
2629
+ mod.invalidationVersion++;
2630
+ }
2631
+ /**
2632
+ * 仅失效到 HMR 边界:显式接受依赖的模块本身不会重执行;自接受模块需要失效,
2633
+ * 但不再继续影响其 importer。这样既能传播依赖时间戳,也不会隐式重复副作用。
2634
+ */
2635
+ invalidateModuleAndImporters(mod, timestamp = Date.now(), seen = /* @__PURE__ */ new Set()) {
2636
+ if (seen.has(mod)) return;
2637
+ seen.add(mod);
2638
+ this.invalidateModule(mod, timestamp);
2639
+ for (const importer of mod.importers) {
2640
+ if (importer.acceptedHmrDeps.has(mod)) continue;
2641
+ if (importer.isSelfAccepting) {
2642
+ this.invalidateModule(importer, timestamp);
2643
+ continue;
2644
+ }
2645
+ this.invalidateModuleAndImporters(importer, timestamp, seen);
2646
+ }
2559
2647
  }
2560
2648
  /** 使所有模块缓存失效 */
2561
2649
  invalidateAll() {
@@ -2566,34 +2654,32 @@ var init_module_graph = __esm({
2566
2654
  /** 获取 HMR 传播边界 - 从变更模块向上遍历找到接受更新的边界 */
2567
2655
  getHmrBoundaries(mod) {
2568
2656
  const boundaries = [];
2569
- const visited = /* @__PURE__ */ new Set();
2570
- const propagate = (node, via) => {
2571
- if (visited.has(node)) return true;
2572
- visited.add(node);
2573
- if (node.isSelfAccepting) {
2574
- boundaries.push({ boundary: node, acceptedVia: via });
2575
- return true;
2657
+ const traversed = /* @__PURE__ */ new Set();
2658
+ const addBoundary = (boundary, acceptedVia) => {
2659
+ if (!boundaries.some(
2660
+ (item) => item.boundary === boundary && item.acceptedVia === acceptedVia
2661
+ )) {
2662
+ boundaries.push({ boundary, acceptedVia });
2576
2663
  }
2577
- if (node.acceptedHmrDeps.has(via)) {
2578
- boundaries.push({ boundary: node, acceptedVia: via });
2664
+ };
2665
+ const propagate = (node) => {
2666
+ if (traversed.has(node)) return true;
2667
+ traversed.add(node);
2668
+ if (node.isSelfAccepting) {
2669
+ addBoundary(node, node);
2579
2670
  return true;
2580
2671
  }
2581
2672
  if (node.importers.size === 0) return false;
2582
2673
  for (const importer of node.importers) {
2583
- if (!propagate(importer, node)) return false;
2674
+ if (importer.acceptedHmrDeps.has(node)) {
2675
+ addBoundary(importer, node);
2676
+ continue;
2677
+ }
2678
+ if (!propagate(importer)) return false;
2584
2679
  }
2585
2680
  return true;
2586
2681
  };
2587
- if (mod.isSelfAccepting) {
2588
- boundaries.push({ boundary: mod, acceptedVia: mod });
2589
- return boundaries;
2590
- }
2591
- for (const importer of mod.importers) {
2592
- if (!propagate(importer, mod)) {
2593
- return [];
2594
- }
2595
- }
2596
- return boundaries;
2682
+ return propagate(mod) ? boundaries : [];
2597
2683
  }
2598
2684
  };
2599
2685
  }
@@ -2689,6 +2775,7 @@ var init_environment = __esm({
2689
2775
  moduleGraph;
2690
2776
  candidatePlugins;
2691
2777
  pluginApi;
2778
+ buildMetadata = {};
2692
2779
  initialized = false;
2693
2780
  constructor(name, config, init = {}) {
2694
2781
  const options = config.environments[name];
@@ -2745,6 +2832,22 @@ var init_environment = __esm({
2745
2832
  logger: this.config.logger
2746
2833
  };
2747
2834
  }
2835
+ setBuildMetadata(metadata) {
2836
+ const { entries: currentEntries, ...currentMetadata } = this.buildMetadata;
2837
+ const { entries, ...nextMetadata } = metadata;
2838
+ this.buildMetadata = {
2839
+ ...currentMetadata,
2840
+ ...nextMetadata,
2841
+ ...currentEntries || entries ? { entries: { ...currentEntries, ...entries } } : {}
2842
+ };
2843
+ }
2844
+ getBuildMetadata() {
2845
+ const { entries, ...metadata } = this.buildMetadata;
2846
+ return {
2847
+ ...metadata,
2848
+ ...entries ? { entries: { ...entries } } : {}
2849
+ };
2850
+ }
2748
2851
  async close() {
2749
2852
  try {
2750
2853
  await this.driver?.close?.(this.getDriverContext());
@@ -2909,6 +3012,134 @@ var init_reporter = __esm({
2909
3012
  }
2910
3013
  });
2911
3014
 
3015
+ // src/core/build-app-context.ts
3016
+ import fs6 from "fs";
3017
+ import path9 from "path";
3018
+ function createBuildAppContext(config, results) {
3019
+ const output = [];
3020
+ const emitted = /* @__PURE__ */ new Set();
3021
+ const outDir = path9.resolve(config.root, config.build.outDir);
3022
+ let environmentArtifacts;
3023
+ return {
3024
+ config,
3025
+ results,
3026
+ get output() {
3027
+ return Object.freeze([...output]);
3028
+ },
3029
+ getResult(environmentName) {
3030
+ return results[environmentName];
3031
+ },
3032
+ getArtifact(environmentName, fileName) {
3033
+ const normalized = normalizeEnvironmentFileName(fileName);
3034
+ return results[environmentName]?.output.find(
3035
+ (artifact) => normalizeEnvironmentFileName(artifact.fileName) === normalized
3036
+ );
3037
+ },
3038
+ getEntry(environmentName, entryName) {
3039
+ const result = results[environmentName];
3040
+ const fileName = result?.entries?.[entryName];
3041
+ if (!fileName) return void 0;
3042
+ return result.output.find(
3043
+ (artifact) => normalizeEnvironmentFileName(artifact.fileName) === normalizeEnvironmentFileName(fileName)
3044
+ );
3045
+ },
3046
+ getManifest(environmentName) {
3047
+ return results[environmentName]?.manifest;
3048
+ },
3049
+ emitFile(file) {
3050
+ const fileName = normalizeAppFileName(file.fileName);
3051
+ const collisionKey = artifactCollisionKey(fileName);
3052
+ if (emitted.has(collisionKey)) {
3053
+ throw new Error(`[nasti] app artifact already emitted: ${fileName}`);
3054
+ }
3055
+ environmentArtifacts ??= collectEnvironmentArtifacts(config, results, outDir);
3056
+ if (environmentArtifacts.has(collisionKey)) {
3057
+ throw new Error(`[nasti] app artifact conflicts with environment output: ${fileName}`);
3058
+ }
3059
+ const target = path9.resolve(outDir, ...fileName.split("/"));
3060
+ const relative = path9.relative(outDir, target);
3061
+ if (relative.startsWith("..") || path9.isAbsolute(relative)) {
3062
+ throw new Error(`[nasti] app artifact must stay inside build.outDir: ${file.fileName}`);
3063
+ }
3064
+ assertNoSymlinkComponents(outDir, fileName);
3065
+ fs6.mkdirSync(path9.dirname(target), { recursive: true });
3066
+ fs6.writeFileSync(target, file.source);
3067
+ const artifact = {
3068
+ ...file,
3069
+ fileName,
3070
+ type: "asset"
3071
+ };
3072
+ emitted.add(collisionKey);
3073
+ output.push(artifact);
3074
+ return fileName;
3075
+ }
3076
+ };
3077
+ }
3078
+ function normalizeEnvironmentFileName(fileName) {
3079
+ return path9.posix.normalize(fileName.replace(/\\/g, "/").replace(/^\.\//, ""));
3080
+ }
3081
+ function isInvalidEnvironmentFileName(fileName) {
3082
+ return !fileName || fileName === "." || fileName === ".." || fileName.startsWith("../") || path9.posix.isAbsolute(fileName) || /^[A-Za-z]:\//.test(fileName);
3083
+ }
3084
+ function normalizeAppFileName(fileName) {
3085
+ const normalized = normalizeEnvironmentFileName(fileName);
3086
+ if (isInvalidEnvironmentFileName(normalized)) {
3087
+ throw new Error(`[nasti] invalid app artifact fileName: ${fileName}`);
3088
+ }
3089
+ return normalized;
3090
+ }
3091
+ function artifactCollisionKey(fileName) {
3092
+ return normalizeEnvironmentFileName(fileName).toLowerCase();
3093
+ }
3094
+ function collectEnvironmentArtifacts(config, results, appOutDir) {
3095
+ const occupied = /* @__PURE__ */ new Set();
3096
+ for (const [environmentName, result] of Object.entries(results)) {
3097
+ const environment = config.environments[environmentName];
3098
+ if (!environment) continue;
3099
+ const environmentOutDir = path9.resolve(config.root, environment.build.outDir);
3100
+ for (const artifact of result.output) {
3101
+ const artifactPath = path9.resolve(
3102
+ environmentOutDir,
3103
+ ...normalizeEnvironmentFileName(artifact.fileName).split("/")
3104
+ );
3105
+ const relative = path9.relative(appOutDir, artifactPath);
3106
+ if (!relative.startsWith("..") && !path9.isAbsolute(relative)) {
3107
+ occupied.add(artifactCollisionKey(relative));
3108
+ }
3109
+ }
3110
+ }
3111
+ return occupied;
3112
+ }
3113
+ function assertNoSymlinkComponents(outDir, fileName) {
3114
+ let current = outDir;
3115
+ for (const segment of fileName.split("/")) {
3116
+ current = path9.join(current, segment);
3117
+ let stats;
3118
+ try {
3119
+ stats = fs6.lstatSync(current);
3120
+ } catch (error) {
3121
+ if (error.code === "ENOENT") continue;
3122
+ throw error;
3123
+ }
3124
+ if (stats.isSymbolicLink()) {
3125
+ throw new Error(`[nasti] app artifact path cannot traverse a symlink: ${fileName}`);
3126
+ }
3127
+ }
3128
+ }
3129
+ function inferEnvironmentEntries(output) {
3130
+ const entries = {};
3131
+ for (const artifact of output) {
3132
+ if (artifact.type !== "chunk" || !artifact.isEntry || !artifact.name) continue;
3133
+ entries[artifact.name] = normalizeEnvironmentFileName(artifact.fileName);
3134
+ }
3135
+ return Object.keys(entries).length > 0 ? entries : void 0;
3136
+ }
3137
+ var init_build_app_context = __esm({
3138
+ "src/core/build-app-context.ts"() {
3139
+ "use strict";
3140
+ }
3141
+ });
3142
+
2912
3143
  // src/build/index.ts
2913
3144
  var build_exports = {};
2914
3145
  __export(build_exports, {
@@ -2918,8 +3149,8 @@ __export(build_exports, {
2918
3149
  resolveClientEntries: () => resolveClientEntries,
2919
3150
  toRolldownPlugins: () => toRolldownPlugins
2920
3151
  });
2921
- import path9 from "path";
2922
- import fs6 from "fs";
3152
+ import path10 from "path";
3153
+ import fs7 from "fs";
2923
3154
  import { builtinModules } from "module";
2924
3155
  import { rolldown } from "rolldown";
2925
3156
  import pc4 from "picocolors";
@@ -2927,9 +3158,14 @@ function getRolldownOptions(environment, entryPoints, rolldownPlugins) {
2927
3158
  const config = environment.config;
2928
3159
  const envOptions = environment.options;
2929
3160
  const isServer = environment.consumer === "server";
2930
- const outDir = path9.resolve(config.root, envOptions.build.outDir);
3161
+ const outDir = path10.resolve(config.root, envOptions.build.outDir);
2931
3162
  const assetsDir = envOptions.build.assetsDir;
2932
- const { output: userOutput, transform: userTransform, ...restInputOptions } = envOptions.build.rolldownOptions;
3163
+ const {
3164
+ output: userOutput,
3165
+ transform: userTransform,
3166
+ resolve: userResolve,
3167
+ ...restInputOptions
3168
+ } = envOptions.build.rolldownOptions;
2933
3169
  const vueDefine = config.framework === "vue" ? {
2934
3170
  __VUE_OPTIONS_API__: "true",
2935
3171
  __VUE_PROD_DEVTOOLS__: "false",
@@ -2943,19 +3179,22 @@ function getRolldownOptions(environment, entryPoints, rolldownPlugins) {
2943
3179
  input: entryPoints,
2944
3180
  transform: { ...userTransform, define: mergedDefine },
2945
3181
  plugins: rolldownPlugins,
3182
+ // client/server consumer 都必须使用当前环境的 conditions/mainFields;Lynx
3183
+ // BG/MT 通常同为 client consumer,但仍可能声明不同的运行时条件。
3184
+ resolve: {
3185
+ ...userResolve ?? {},
3186
+ // Environment API 是条件解析的唯一高层入口,优先于继承来的底层选项。
3187
+ conditionNames: envOptions.resolve.conditions,
3188
+ mainFields: envOptions.resolve.mainFields
3189
+ },
2946
3190
  ...isServer ? {
2947
3191
  platform: restInputOptions.platform ?? "node",
2948
- resolve: {
2949
- conditionNames: envOptions.resolve.conditions,
2950
- mainFields: envOptions.resolve.mainFields,
2951
- ...restInputOptions.resolve
2952
- },
2953
3192
  // server 产物:node 内建恒外部化;bare specifier 默认外部化
2954
3193
  //(同 Vite ssr.external 默认 —— 依赖由 node_modules 运行时解析),
2955
3194
  // 相对/绝对/虚拟模块照常打包。需要内联依赖时经 rolldownOptions.external 覆盖。
2956
3195
  external: restInputOptions.external ?? ((id) => {
2957
3196
  if (NODE_BUILTINS.has(id)) return true;
2958
- return !id.startsWith(".") && !path9.isAbsolute(id) && !id.startsWith("\0");
3197
+ return !id.startsWith(".") && !path10.isAbsolute(id) && !id.startsWith("\0");
2959
3198
  })
2960
3199
  } : {}
2961
3200
  };
@@ -2982,27 +3221,122 @@ function getRolldownOptions(environment, entryPoints, rolldownPlugins) {
2982
3221
  };
2983
3222
  return { inputOptions, outputOptions, outDir };
2984
3223
  }
2985
- function toRolldownPlugins(plugins) {
3224
+ function toRolldownPlugins(plugins, environment) {
3225
+ const wrap = (hook) => {
3226
+ if (!hook) return hook;
3227
+ return function(...args) {
3228
+ return hook.apply(attachEnvironment(this, environment), args);
3229
+ };
3230
+ };
2986
3231
  return plugins.map((p) => ({
2987
3232
  name: p.name,
2988
- resolveId: p.resolveId,
2989
- load: p.load,
2990
- transform: p.transform,
2991
- buildStart: p.buildStart,
2992
- buildEnd: p.buildEnd,
3233
+ resolveId: wrap(p.resolveId),
3234
+ load: wrap(p.load),
3235
+ transform: wrap(p.transform),
3236
+ buildStart: wrap(p.buildStart),
3237
+ buildEnd: wrap(p.buildEnd),
2993
3238
  // closeBundle 在 bundle.close() 时触发 —— PWA manifest/SW 等终态产物依赖
2994
- closeBundle: p.closeBundle,
2995
- renderChunk: p.renderChunk,
2996
- augmentChunkHash: p.augmentChunkHash,
2997
- generateBundle: p.generateBundle
3239
+ closeBundle: wrap(p.closeBundle),
3240
+ renderChunk: wrap(p.renderChunk),
3241
+ augmentChunkHash: wrap(p.augmentChunkHash),
3242
+ generateBundle: wrap(p.generateBundle)
2998
3243
  }));
2999
3244
  }
3245
+ function attachEnvironment(context, environment) {
3246
+ if (context?.environment === environment) return context;
3247
+ try {
3248
+ Object.defineProperty(context, "environment", {
3249
+ configurable: true,
3250
+ enumerable: false,
3251
+ writable: false,
3252
+ value: environment
3253
+ });
3254
+ return context;
3255
+ } catch {
3256
+ return new Proxy(context, {
3257
+ get(target, property) {
3258
+ if (property === "environment") return environment;
3259
+ const value = Reflect.get(target, property, target);
3260
+ return typeof value === "function" ? value.bind(target) : value;
3261
+ },
3262
+ set(target, property, value) {
3263
+ return Reflect.set(target, property, value, target);
3264
+ }
3265
+ });
3266
+ }
3267
+ }
3268
+ function finalizeEnvironmentResult(environment, result) {
3269
+ const metadata = environment.getBuildMetadata();
3270
+ const inferredEntries = inferEnvironmentEntries(result.output);
3271
+ const entries = {
3272
+ ...inferredEntries,
3273
+ ...metadata.entries,
3274
+ ...result.entries
3275
+ };
3276
+ const normalizedEntries = Object.fromEntries(
3277
+ Object.entries(entries).map(([name, fileName]) => {
3278
+ const normalized = normalizeEnvironmentFileName(fileName);
3279
+ if (isInvalidEnvironmentFileName(normalized)) {
3280
+ throw new Error(
3281
+ `[nasti] environment "${environment.name}" returned invalid entry "${name}": ${fileName}`
3282
+ );
3283
+ }
3284
+ return [name, normalized];
3285
+ })
3286
+ );
3287
+ return {
3288
+ ...metadata,
3289
+ ...result,
3290
+ output: result.output,
3291
+ ...Object.keys(normalizedEntries).length > 0 ? { entries: normalizedEntries } : {}
3292
+ };
3293
+ }
3294
+ function prepareBuildOutputDirectories(config, buildableNames) {
3295
+ const directories = /* @__PURE__ */ new Set();
3296
+ const protectedPaths = /* @__PURE__ */ new Set();
3297
+ const clientIsBuilt = buildableNames.includes("client");
3298
+ if (!clientIsBuilt && config.build.emptyOutDir) {
3299
+ directories.add(path10.resolve(config.root, config.build.outDir));
3300
+ }
3301
+ for (const name of buildableNames) {
3302
+ const environment = config.environments[name];
3303
+ const outDir = path10.resolve(config.root, environment.build.outDir);
3304
+ if (!environment.build.emptyOutDir) {
3305
+ protectedPaths.add(outDir);
3306
+ continue;
3307
+ }
3308
+ if (!environment.driver) directories.add(outDir);
3309
+ }
3310
+ const containsPath = (parent, child) => {
3311
+ const relative = path10.relative(parent, child);
3312
+ return relative === "" || !relative.startsWith("..") && !path10.isAbsolute(relative);
3313
+ };
3314
+ const roots = [...directories].filter(
3315
+ (directory) => ![...protectedPaths].some((protectedPath) => containsPath(directory, protectedPath))
3316
+ ).sort((a, b) => a.length - b.length).filter(
3317
+ (directory, index2, all) => !all.slice(0, index2).some((parent) => containsPath(parent, directory))
3318
+ );
3319
+ for (const directory of roots) {
3320
+ if (fs7.existsSync(directory)) fs7.rmSync(directory, { recursive: true, force: true });
3321
+ }
3322
+ }
3323
+ function assertDriverBuildResult(environment, result) {
3324
+ const output = result != null && typeof result === "object" ? result.output : void 0;
3325
+ const hasValidOutput = Array.isArray(output) && output.every(
3326
+ (artifact) => artifact != null && typeof artifact === "object" && typeof artifact.fileName === "string" && typeof artifact.type === "string"
3327
+ );
3328
+ if (!hasValidOutput) {
3329
+ throw new Error(
3330
+ `[nasti] environment "${environment.name}" driver "${environment.driver?.name}" returned an invalid build result; expected { output: EnvironmentBuildOutput[] }`
3331
+ );
3332
+ }
3333
+ }
3000
3334
  function resolveClientEntries(config, html) {
3001
3335
  const configuredEntries = config.environments.client?.entry ?? [];
3002
3336
  if (configuredEntries.length > 0) return configuredEntries;
3003
3337
  const entryPoints = [];
3004
3338
  const htmlFile = config.environments.client?.html;
3005
- const htmlDir = htmlFile ? path9.dirname(htmlFile) : config.root;
3339
+ const htmlDir = htmlFile ? path10.dirname(htmlFile) : config.root;
3006
3340
  if (html) {
3007
3341
  const scriptMatches = html.matchAll(/<script[^>]+src=["']([^"']+)["'][^>]*>/gi);
3008
3342
  for (const match of scriptMatches) {
@@ -3010,7 +3344,7 @@ function resolveClientEntries(config, html) {
3010
3344
  if (src && !src.startsWith("http")) {
3011
3345
  const cleanSrc = src.split(/[?#]/, 1)[0];
3012
3346
  entryPoints.push(
3013
- cleanSrc.startsWith("/") ? path9.resolve(config.root, cleanSrc.replace(/^\//, "")) : path9.resolve(htmlDir, cleanSrc)
3347
+ cleanSrc.startsWith("/") ? path10.resolve(config.root, cleanSrc.replace(/^\//, "")) : path10.resolve(htmlDir, cleanSrc)
3014
3348
  );
3015
3349
  }
3016
3350
  }
@@ -3018,8 +3352,8 @@ function resolveClientEntries(config, html) {
3018
3352
  if (entryPoints.length === 0) {
3019
3353
  const fallbackEntries = ["src/main.ts", "src/main.tsx", "src/main.js", "src/index.ts", "src/index.tsx", "src/index.js"];
3020
3354
  for (const entry of fallbackEntries) {
3021
- const fullPath = path9.resolve(config.root, entry);
3022
- if (fs6.existsSync(fullPath)) {
3355
+ const fullPath = path10.resolve(config.root, entry);
3356
+ if (fs7.existsSync(fullPath)) {
3023
3357
  entryPoints.push(fullPath);
3024
3358
  break;
3025
3359
  }
@@ -3047,16 +3381,20 @@ async function build(inlineConfig = {}) {
3047
3381
  const startTime = performance.now();
3048
3382
  logger.info(
3049
3383
  pc4.cyan(`
3050
- nasti v${"2.3.1"} `) + pc4.green(`building for ${config.mode}...`)
3384
+ nasti v${"2.4.0"} `) + pc4.green(`building for ${config.mode}...`)
3051
3385
  );
3052
3386
  debug4?.(`root: ${config.root}`);
3053
- const buildableNames = Object.keys(config.environments).filter(
3054
- (name) => name === "client" || config.environments[name].entry.length > 0 || !!config.environments[name].driver
3055
- );
3387
+ const buildableNames = Object.keys(config.environments).filter((name) => {
3388
+ const environment = config.environments[name];
3389
+ if (!environment.buildEnabled) return false;
3390
+ return name === "client" || environment.entry.length > 0 || !!environment.driver;
3391
+ });
3056
3392
  buildableNames.sort((a, b) => a === "client" ? -1 : b === "client" ? 1 : 0);
3393
+ prepareBuildOutputDirectories(config, buildableNames);
3057
3394
  const environments = {};
3058
3395
  const environmentResults = {};
3059
3396
  const initializedEnvironments = [];
3397
+ const buildAppContext = createBuildAppContext(config, environmentResults);
3060
3398
  let clientOutput = [];
3061
3399
  let buildFailed = false;
3062
3400
  try {
@@ -3072,7 +3410,7 @@ nasti v${"2.3.1"} `) + pc4.green(`building for ${config.mode}...`)
3072
3410
  }
3073
3411
  const pluginApi = getPluginApi(config);
3074
3412
  for (const plugin of config.plugins) {
3075
- await plugin.afterBuildApp?.(environmentResults, pluginApi);
3413
+ await plugin.afterBuildApp?.(environmentResults, pluginApi, buildAppContext);
3076
3414
  }
3077
3415
  } catch (error) {
3078
3416
  buildFailed = true;
@@ -3099,22 +3437,31 @@ nasti v${"2.3.1"} `) + pc4.green(`building for ${config.mode}...`)
3099
3437
  }
3100
3438
  }
3101
3439
  const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
3102
- const totalSize = Object.values(environments).flat().reduce((sum, chunk) => {
3440
+ const allOutput = [...Object.values(environments).flat(), ...buildAppContext.output];
3441
+ const totalSize = allOutput.reduce((sum, chunk) => {
3103
3442
  const content = chunk.type === "chunk" ? chunk.code : chunk.source;
3104
3443
  if (content == null) return sum;
3105
3444
  return sum + (typeof content === "string" ? Buffer.byteLength(content) : content.byteLength);
3106
3445
  }, 0);
3107
- const fileCount = Object.values(environments).flat().length;
3446
+ const fileCount = allOutput.length;
3108
3447
  const envSuffix = buildableNames.length > 1 ? ` (${buildableNames.join(" + ")})` : "";
3109
3448
  logger.info(pc4.green(`\u2713 built in ${elapsed}s`) + pc4.dim(envSuffix));
3110
3449
  logger.info(pc4.dim(` ${fileCount} files, ${displaySize(totalSize)} total \u2192 ${config.build.outDir}/`));
3111
- return { output: clientOutput, environments, environmentResults };
3450
+ return {
3451
+ output: clientOutput,
3452
+ environments,
3453
+ environmentResults,
3454
+ appOutput: [...buildAppContext.output]
3455
+ };
3112
3456
  }
3113
3457
  async function buildClientEnvironment(config) {
3114
3458
  const logger = config.logger;
3115
- const outDir = path9.resolve(config.root, config.build.outDir);
3459
+ const outDir = path10.resolve(config.root, config.build.outDir);
3116
3460
  const cssEngine = createCssEngine();
3117
- const pluginList = resolvePluginList(config, config.plugins, { cssEngine });
3461
+ const pluginList = resolvePluginList(config, config.plugins, {
3462
+ cssEngine,
3463
+ environmentName: "client"
3464
+ });
3118
3465
  const clientEnv = new NastiEnvironment("client", config, {
3119
3466
  mode: "build",
3120
3467
  plugins: pluginList,
@@ -3129,13 +3476,11 @@ async function buildClientEnvironment(config) {
3129
3476
  );
3130
3477
  }
3131
3478
  const result = await clientEnv.driver.build(clientEnv.getDriverContext());
3132
- return { environment: clientEnv, result };
3133
- }
3134
- if (config.build.emptyOutDir && fs6.existsSync(outDir)) {
3135
- fs6.rmSync(outDir, { recursive: true, force: true });
3479
+ assertDriverBuildResult(clientEnv, result);
3480
+ return { environment: clientEnv, result: finalizeEnvironmentResult(clientEnv, result) };
3136
3481
  }
3137
- fs6.mkdirSync(outDir, { recursive: true });
3138
- const htmlFile = config.environments.client.html ?? path9.resolve(config.root, "index.html");
3482
+ fs7.mkdirSync(outDir, { recursive: true });
3483
+ const htmlFile = config.environments.client.html ?? path10.resolve(config.root, "index.html");
3139
3484
  const html = await readHtmlFile(config.root, htmlFile);
3140
3485
  const entryPoints = resolveClientEntries(config, html);
3141
3486
  if (entryPoints.length === 0) {
@@ -3145,7 +3490,7 @@ async function buildClientEnvironment(config) {
3145
3490
  const nativeReporter = config.logLevel === "silent" ? null : await tryNativeReporterPlugin(config, logger);
3146
3491
  const rolldownPlugins = [
3147
3492
  createOxcTransformPlugin(config, clientEnv),
3148
- ...toRolldownPlugins(allPlugins),
3493
+ ...toRolldownPlugins(allPlugins, clientEnv),
3149
3494
  ...nativeReporter ? [nativeReporter] : []
3150
3495
  ];
3151
3496
  const { inputOptions, outputOptions } = getRolldownOptions(
@@ -3182,13 +3527,16 @@ async function buildClientEnvironment(config) {
3182
3527
  );
3183
3528
  }
3184
3529
  }
3185
- fs6.writeFileSync(path9.resolve(outDir, "index.html"), processedHtml);
3530
+ fs7.writeFileSync(path10.resolve(outDir, "index.html"), processedHtml);
3186
3531
  }
3187
3532
  if (!nativeReporter && config.logLevel !== "silent") {
3188
3533
  reportBuildOutput(output, config, logger);
3189
3534
  }
3190
3535
  warnLargeChunks(output, config, logger);
3191
- return { environment: clientEnv, result: { output } };
3536
+ return {
3537
+ environment: clientEnv,
3538
+ result: finalizeEnvironmentResult(clientEnv, { output })
3539
+ };
3192
3540
  } catch (error) {
3193
3541
  try {
3194
3542
  await clientEnv.close();
@@ -3204,7 +3552,10 @@ async function buildClientEnvironment(config) {
3204
3552
  async function buildServerEnvironment(config, name) {
3205
3553
  const envOptions = config.environments[name];
3206
3554
  const logger = config.logger;
3207
- const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
3555
+ const pluginList = resolvePluginList(config, config.plugins, {
3556
+ consumer: envOptions.consumer,
3557
+ environmentName: name
3558
+ });
3208
3559
  const environment = new NastiEnvironment(name, config, {
3209
3560
  mode: "build",
3210
3561
  plugins: pluginList,
@@ -3220,38 +3571,39 @@ async function buildServerEnvironment(config, name) {
3220
3571
  }
3221
3572
  try {
3222
3573
  const result = await environment.driver.build(environment.getDriverContext());
3223
- return { environment, result };
3574
+ assertDriverBuildResult(environment, result);
3575
+ return { environment, result: finalizeEnvironmentResult(environment, result) };
3224
3576
  } catch (error) {
3225
3577
  await environment.close();
3226
3578
  throw error;
3227
3579
  }
3228
3580
  }
3229
3581
  for (const entry of envOptions.entry) {
3230
- if (!fs6.existsSync(entry)) {
3582
+ if (!fs7.existsSync(entry)) {
3231
3583
  await environment.close();
3232
3584
  throw new Error(`[nasti] environment "${name}" entry not found: ${entry}`);
3233
3585
  }
3234
3586
  }
3235
3587
  const rolldownPlugins = [
3236
3588
  createOxcTransformPlugin(config, environment),
3237
- ...toRolldownPlugins(environment.plugins)
3589
+ ...toRolldownPlugins(environment.plugins, environment)
3238
3590
  ];
3239
3591
  const { inputOptions, outputOptions, outDir } = getRolldownOptions(
3240
3592
  environment,
3241
3593
  envOptions.entry,
3242
3594
  rolldownPlugins
3243
3595
  );
3244
- if (envOptions.build.emptyOutDir && fs6.existsSync(outDir)) {
3245
- fs6.rmSync(outDir, { recursive: true, force: true });
3246
- }
3247
- fs6.mkdirSync(outDir, { recursive: true });
3596
+ fs7.mkdirSync(outDir, { recursive: true });
3248
3597
  const bundle2 = await rolldown(inputOptions);
3249
3598
  const { output } = await bundle2.write(outputOptions);
3250
3599
  await bundle2.close();
3251
3600
  logger.info(
3252
- pc4.dim(` [${name}] `) + output.map((o) => path9.join(envOptions.build.outDir, o.fileName)).join(pc4.dim(", "))
3601
+ pc4.dim(` [${name}] `) + output.map((o) => path10.join(envOptions.build.outDir, o.fileName)).join(pc4.dim(", "))
3253
3602
  );
3254
- return { environment, result: { output } };
3603
+ return {
3604
+ environment,
3605
+ result: finalizeEnvironmentResult(environment, { output })
3606
+ };
3255
3607
  }
3256
3608
  function injectCssLinks(html, cssEngine, config) {
3257
3609
  const cssLinkTags = [];
@@ -3278,9 +3630,9 @@ function escapeRegExp(string) {
3278
3630
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3279
3631
  }
3280
3632
  function replaceEntryScript(html, facadeModuleId, fileName, config, htmlFile, urlPrefix) {
3281
- const rootRelative = path9.relative(config.root, facadeModuleId).split(path9.sep).join("/");
3282
- const resolvedHtmlFile = path9.resolve(config.root, htmlFile);
3283
- const htmlRelative = path9.relative(path9.dirname(resolvedHtmlFile), facadeModuleId).split(path9.sep).join("/");
3633
+ const rootRelative = path10.relative(config.root, facadeModuleId).split(path10.sep).join("/");
3634
+ const resolvedHtmlFile = path10.resolve(config.root, htmlFile);
3635
+ const htmlRelative = path10.relative(path10.dirname(resolvedHtmlFile), facadeModuleId).split(path10.sep).join("/");
3284
3636
  const candidates = /* @__PURE__ */ new Set([
3285
3637
  rootRelative,
3286
3638
  `/${rootRelative}`,
@@ -3310,6 +3662,7 @@ var init_build = __esm({
3310
3662
  init_reporter();
3311
3663
  init_debug();
3312
3664
  init_plugin_api();
3665
+ init_build_app_context();
3313
3666
  debug4 = createDebugger("nasti:build");
3314
3667
  NODE_BUILTINS = /* @__PURE__ */ new Set([...builtinModules, ...builtinModules.map((m) => `node:${m}`)]);
3315
3668
  }
@@ -3367,20 +3720,22 @@ __export(middleware_exports, {
3367
3720
  transformMiddleware: () => transformMiddleware,
3368
3721
  transformRequest: () => transformRequest
3369
3722
  });
3370
- import path11 from "path";
3371
- import fs8 from "fs";
3723
+ import path12 from "path";
3724
+ import fs9 from "fs";
3372
3725
  import { createRequire as createRequire3 } from "module";
3373
3726
  import { fileURLToPath, pathToFileURL as pathToFileURL3 } from "url";
3374
3727
  import pc6 from "picocolors";
3375
- function getReactRefreshRuntimeEsm() {
3376
- if (__refreshRuntimeCache) return __refreshRuntimeCache;
3728
+ function getReactRefreshRuntimeEsm(includeBoundaryHelpers = false) {
3729
+ if (__refreshRuntimeCache) {
3730
+ return includeBoundaryHelpers ? __refreshRuntimeCache + REACT_REFRESH_BOUNDARY_HELPERS : __refreshRuntimeCache;
3731
+ }
3377
3732
  let cjsPath;
3378
3733
  try {
3379
3734
  const pkgPath = __require2.resolve("react-refresh/package.json");
3380
- cjsPath = path11.join(path11.dirname(pkgPath), "cjs", "react-refresh-runtime.development.js");
3735
+ cjsPath = path12.join(path12.dirname(pkgPath), "cjs", "react-refresh-runtime.development.js");
3381
3736
  } catch (err) {
3382
- cjsPath = path11.resolve(__dirname_esm, "../../node_modules/react-refresh/cjs/react-refresh-runtime.development.js");
3383
- if (!fs8.existsSync(cjsPath)) {
3737
+ cjsPath = path12.resolve(__dirname_esm, "../../node_modules/react-refresh/cjs/react-refresh-runtime.development.js");
3738
+ if (!fs9.existsSync(cjsPath)) {
3384
3739
  const origMsg = err instanceof Error ? err.message : String(err);
3385
3740
  throw new Error(
3386
3741
  `[nasti] Missing dependency "react-refresh". Install it with: npm install react-refresh
@@ -3388,7 +3743,7 @@ Original resolve error: ${origMsg}`
3388
3743
  );
3389
3744
  }
3390
3745
  }
3391
- const cjsSource = fs8.readFileSync(cjsPath, "utf-8");
3746
+ const cjsSource = fs9.readFileSync(cjsPath, "utf-8");
3392
3747
  __refreshRuntimeCache = `// Wrapped react-refresh runtime -> ESM
3393
3748
  const exports = {};
3394
3749
  const module = { exports };
@@ -3408,7 +3763,7 @@ export const findAffectedHostInstances = __rt.findAffectedHostInstances;
3408
3763
  export const collectCustomHooksForSignature = __rt.collectCustomHooksForSignature;
3409
3764
  export default __rt;
3410
3765
  `;
3411
- return __refreshRuntimeCache;
3766
+ return includeBoundaryHelpers ? __refreshRuntimeCache + REACT_REFRESH_BOUNDARY_HELPERS : __refreshRuntimeCache;
3412
3767
  }
3413
3768
  function buildReactRefreshWrapper(moduleUrl, transformedCode) {
3414
3769
  const urlLit = JSON.stringify(moduleUrl);
@@ -3434,22 +3789,40 @@ window.$RefreshReg$ = prevRefreshReg;
3434
3789
  window.$RefreshSig$ = prevRefreshSig;
3435
3790
 
3436
3791
  if (__nasti_hot__) {
3437
- __nasti_hot__.accept(() => {
3438
- clearTimeout(window.__nasti_refresh_timer__);
3439
- window.__nasti_refresh_timer__ = setTimeout(() => {
3440
- RefreshRuntime.performReactRefresh();
3441
- }, 30);
3792
+ let __nasti_current_exports__;
3793
+ __nasti_hot__.accept((nextExports) => {
3794
+ if (!nextExports) return;
3795
+ if (!__nasti_current_exports__) {
3796
+ __nasti_hot__.invalidate('Could not Fast Refresh (previous exports unavailable)');
3797
+ return;
3798
+ }
3799
+ const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(
3800
+ ${urlLit},
3801
+ __nasti_current_exports__,
3802
+ nextExports,
3803
+ );
3804
+ if (invalidateMessage) __nasti_hot__.invalidate(invalidateMessage);
3805
+ });
3806
+ RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
3807
+ __nasti_current_exports__ = currentExports;
3808
+ RefreshRuntime.registerExportsForReactRefresh(${urlLit}, currentExports);
3442
3809
  });
3443
3810
  }
3444
3811
  `;
3445
3812
  }
3446
3813
  function injectImportMetaHot(code, moduleUrl) {
3447
- if (!/\bimport\.meta\.hot\b/.test(code)) return code;
3814
+ const hotRE = /\bimport\.meta\.hot\b/g;
3815
+ const matches = [...maskStringsAndComments(code).matchAll(hotRE)];
3816
+ if (matches.length === 0) return code;
3817
+ for (const match of matches.reverse()) {
3818
+ const start = match.index;
3819
+ code = code.slice(0, start) + "__nasti_hot__" + code.slice(start + match[0].length);
3820
+ }
3448
3821
  const urlLit = JSON.stringify(moduleUrl);
3449
3822
  const header = `import { createHotContext as __nasti_createHotContext__ } from "/@nasti/client";
3450
3823
  const __nasti_hot__ = __nasti_createHotContext__(${urlLit});
3451
3824
  `;
3452
- return header + code.replace(/\bimport\.meta\.hot\b/g, "__nasti_hot__");
3825
+ return header + code;
3453
3826
  }
3454
3827
  function transformMiddleware(ctx) {
3455
3828
  ctx.envDefine = buildEnvDefine(
@@ -3525,13 +3898,14 @@ function transformMiddleware(ctx) {
3525
3898
  }
3526
3899
  async function transformRequest(url, ctx) {
3527
3900
  const { config, pluginContainer, moduleGraph } = ctx;
3901
+ url = removeTimestampQuery(url);
3528
3902
  const cleanReqUrl = url.split("?")[0];
3529
3903
  const cached2 = moduleGraph.getModuleByUrl(url);
3530
3904
  if (cached2?.transformResult) {
3531
3905
  return cached2.transformResult;
3532
3906
  }
3533
3907
  if (cleanReqUrl === "/@react-refresh") {
3534
- return { code: getReactRefreshRuntimeEsm() };
3908
+ return { code: getReactRefreshRuntimeEsm(true) };
3535
3909
  }
3536
3910
  if (cleanReqUrl.startsWith("/@modules/") && url.includes("?")) {
3537
3911
  const idParam = new URLSearchParams(url.slice(url.indexOf("?") + 1)).get("id");
@@ -3539,8 +3913,8 @@ async function transformRequest(url, ctx) {
3539
3913
  let realIdValid = false;
3540
3914
  try {
3541
3915
  if (idParam) {
3542
- realId = fs8.realpathSync(idParam);
3543
- realIdValid = fs8.statSync(realId).isFile() && (realId.includes(`${path11.sep}node_modules${path11.sep}`) || isUnderRoot(realId, config.root));
3916
+ realId = fs9.realpathSync(idParam);
3917
+ realIdValid = fs9.statSync(realId).isFile() && (realId.includes(`${path12.sep}node_modules${path12.sep}`) || isUnderRoot(realId, config.root));
3544
3918
  }
3545
3919
  } catch {
3546
3920
  realId = null;
@@ -3567,6 +3941,8 @@ async function transformRequest(url, ctx) {
3567
3941
  }
3568
3942
  const rawQuery = url.includes("?") ? url.slice(url.indexOf("?") + 1) : "";
3569
3943
  if (rawQuery && !/^t=\d+$/.test(rawQuery)) {
3944
+ const mod2 = await moduleGraph.ensureEntryFromUrl(url);
3945
+ const transformVersion2 = mod2.invalidationVersion;
3570
3946
  const loaded = await pluginContainer.load(url);
3571
3947
  if (loaded != null) {
3572
3948
  let code2 = typeof loaded === "string" ? loaded : loaded.code;
@@ -3574,30 +3950,43 @@ async function transformRequest(url, ctx) {
3574
3950
  if (transformed != null) {
3575
3951
  code2 = typeof transformed === "string" ? transformed : transformed.code;
3576
3952
  }
3577
- const mod2 = await moduleGraph.ensureEntryFromUrl(url);
3578
- moduleGraph.registerModule(mod2, cleanReqUrl);
3579
- code2 = injectImportMetaHot(code2, url);
3953
+ const parentFile = resolveUrlToFile(cleanReqUrl, config.root) ?? cleanReqUrl;
3954
+ moduleGraph.registerModule(mod2, parentFile);
3955
+ const hotInfo2 = rewriteHotAcceptDeps(code2, config, parentFile);
3956
+ code2 = injectImportMetaHot(hotInfo2.code, url);
3580
3957
  code2 = replaceEnvInCode(code2, ctx.envDefine ?? buildEnvDefine(
3581
3958
  loadEnv(config.mode, config.root, config.envPrefix),
3582
3959
  config.mode
3583
3960
  ));
3584
- code2 = rewriteImports(code2, config, cleanReqUrl);
3961
+ const importedUrls2 = /* @__PURE__ */ new Set();
3962
+ code2 = rewriteImports(code2, config, parentFile, importedUrls2, moduleGraph);
3963
+ const pruned2 = await moduleGraph.updateModuleInfo(
3964
+ mod2,
3965
+ importedUrls2,
3966
+ hotInfo2.acceptedUrls,
3967
+ hotInfo2.isSelfAccepting,
3968
+ transformVersion2
3969
+ );
3585
3970
  const transformResult2 = { code: code2 };
3586
- mod2.transformResult = transformResult2;
3971
+ if (pruned2) {
3972
+ if (pruned2.size > 0) ctx.onPrune?.([...pruned2].map((item) => item.url));
3973
+ mod2.transformResult = transformResult2;
3974
+ }
3587
3975
  return transformResult2;
3588
3976
  }
3589
3977
  }
3590
3978
  const filePath = resolveUrlToFile(url, config.root);
3591
- if (!filePath || !fs8.existsSync(filePath)) return null;
3979
+ if (!filePath || !fs9.existsSync(filePath)) return null;
3592
3980
  const mod = await moduleGraph.ensureEntryFromUrl(url);
3593
3981
  moduleGraph.registerModule(mod, filePath);
3982
+ const transformVersion = mod.invalidationVersion;
3594
3983
  if (cleanReqUrl.startsWith("/@modules/")) {
3595
3984
  const code2 = await bundlePackageAsEsm(filePath, config.root);
3596
3985
  const transformResult2 = { code: code2 };
3597
3986
  mod.transformResult = transformResult2;
3598
3987
  return transformResult2;
3599
3988
  }
3600
- let code = fs8.readFileSync(filePath, "utf-8");
3989
+ let code = fs9.readFileSync(filePath, "utf-8");
3601
3990
  const pluginResult = await pluginContainer.transform(code, filePath);
3602
3991
  if (pluginResult) {
3603
3992
  code = typeof pluginResult === "string" ? pluginResult : pluginResult.code;
@@ -3617,9 +4006,10 @@ async function transformRequest(url, ctx) {
3617
4006
  if (useRefresh) {
3618
4007
  code = buildReactRefreshWrapper(stableUrl, code);
3619
4008
  wrappedWithRefresh = true;
3620
- mod.isSelfAccepting = true;
3621
4009
  }
3622
4010
  }
4011
+ const hotInfo = rewriteHotAcceptDeps(code, config, filePath);
4012
+ code = hotInfo.code;
3623
4013
  if (!wrappedWithRefresh) {
3624
4014
  code = injectImportMetaHot(code, stableUrl);
3625
4015
  }
@@ -3628,9 +4018,20 @@ async function transformRequest(url, ctx) {
3628
4018
  config.mode
3629
4019
  );
3630
4020
  code = replaceEnvInCode(code, envDefine);
3631
- code = rewriteImports(code, config, filePath);
4021
+ const importedUrls = /* @__PURE__ */ new Set();
4022
+ code = rewriteImports(code, config, filePath, importedUrls, moduleGraph);
4023
+ const pruned = await moduleGraph.updateModuleInfo(
4024
+ mod,
4025
+ importedUrls,
4026
+ hotInfo.acceptedUrls,
4027
+ wrappedWithRefresh || hotInfo.isSelfAccepting,
4028
+ transformVersion
4029
+ );
3632
4030
  const transformResult = { code };
3633
- mod.transformResult = transformResult;
4031
+ if (pruned) {
4032
+ if (pruned.size > 0) ctx.onPrune?.([...pruned].map((item) => item.url));
4033
+ mod.transformResult = transformResult;
4034
+ }
3634
4035
  return transformResult;
3635
4036
  }
3636
4037
  async function loadVirtualModule(spec, ctx) {
@@ -3638,7 +4039,7 @@ async function loadVirtualModule(spec, ctx) {
3638
4039
  const resolved = await pluginContainer.resolveId(spec);
3639
4040
  if (resolved == null) return null;
3640
4041
  const resolvedId = typeof resolved === "string" ? resolved : resolved.id;
3641
- const looksVirtual = resolvedId.startsWith("\0") || !fs8.existsSync(resolvedId);
4042
+ const looksVirtual = resolvedId.startsWith("\0") || !fs9.existsSync(resolvedId);
3642
4043
  if (!looksVirtual) return null;
3643
4044
  const loadResult = await pluginContainer.load(resolvedId);
3644
4045
  if (loadResult == null) return null;
@@ -3651,7 +4052,7 @@ async function loadVirtualModule(spec, ctx) {
3651
4052
  loadEnv(config.mode, config.root, config.envPrefix),
3652
4053
  config.mode
3653
4054
  ));
3654
- const anchor = path11.join(config.root, "__nasti_virtual__.ts");
4055
+ const anchor = path12.join(config.root, "__nasti_virtual__.ts");
3655
4056
  code = rewriteImports(code, config, anchor);
3656
4057
  return { id: resolvedId, result: { code } };
3657
4058
  }
@@ -3677,7 +4078,7 @@ async function doBundlePackage(entryFile, root) {
3677
4078
  await bundle2.close();
3678
4079
  let code = result.output[0].code;
3679
4080
  code = code.replace(/process\.env\.NODE_ENV/g, '"development"');
3680
- const externalBaseDir = path11.dirname(entryFile);
4081
+ const externalBaseDir = path12.dirname(entryFile);
3681
4082
  code = code.replace(
3682
4083
  /^(import\b[^;'"]*?\bfrom\s+)(['"])([^'"./][^'"]*)(\2)/gm,
3683
4084
  (_, prefix, q, spec) => `${prefix}${q}${externalSpecToModuleUrl(spec, externalBaseDir, root)}${q}`
@@ -3695,16 +4096,16 @@ async function doBundlePackage(entryFile, root) {
3695
4096
  return code;
3696
4097
  }
3697
4098
  async function tryGenerateSubpathShim(entryFile, root) {
3698
- const NM = `${path11.sep}node_modules${path11.sep}`;
4099
+ const NM = `${path12.sep}node_modules${path12.sep}`;
3699
4100
  if (!entryFile.includes(NM)) return null;
3700
4101
  let pkgDir = null;
3701
4102
  let pkgName = null;
3702
- let dir = path11.dirname(entryFile);
4103
+ let dir = path12.dirname(entryFile);
3703
4104
  while (true) {
3704
- const pkgJsonPath = path11.join(dir, "package.json");
3705
- if (fs8.existsSync(pkgJsonPath)) {
4105
+ const pkgJsonPath = path12.join(dir, "package.json");
4106
+ if (fs9.existsSync(pkgJsonPath)) {
3706
4107
  try {
3707
- const pkg = JSON.parse(fs8.readFileSync(pkgJsonPath, "utf-8"));
4108
+ const pkg = JSON.parse(fs9.readFileSync(pkgJsonPath, "utf-8"));
3708
4109
  if (typeof pkg?.name === "string" && pkg.name) {
3709
4110
  pkgDir = dir;
3710
4111
  pkgName = pkg.name;
@@ -3713,16 +4114,16 @@ async function tryGenerateSubpathShim(entryFile, root) {
3713
4114
  } catch {
3714
4115
  }
3715
4116
  }
3716
- const parent = path11.dirname(dir);
4117
+ const parent = path12.dirname(dir);
3717
4118
  if (parent === dir) return null;
3718
4119
  dir = parent;
3719
4120
  if (!dir.includes(NM)) return null;
3720
4121
  }
3721
4122
  if (!pkgDir || !pkgName) return null;
3722
- const entryExt = path11.extname(entryFile);
4123
+ const entryExt = path12.extname(entryFile);
3723
4124
  const mainEntry = pickMainEntryByExtension(pkgDir, entryExt);
3724
4125
  if (!mainEntry) return null;
3725
- if (path11.resolve(mainEntry) === path11.resolve(entryFile)) return null;
4126
+ if (path12.resolve(mainEntry) === path12.resolve(entryFile)) return null;
3726
4127
  let mainNs;
3727
4128
  let subNs;
3728
4129
  try {
@@ -3746,7 +4147,7 @@ async function tryGenerateSubpathShim(entryFile, root) {
3746
4147
  if (mainNs["default"] !== subNs["default"]) return null;
3747
4148
  }
3748
4149
  const rootMain = resolveNodeModule(root, pkgName);
3749
- const mainEntryUrl = rootMain && rootMain.startsWith(pkgDir + path11.sep) ? `/@modules/${pkgName}` : `/@modules/${pkgName}?id=${encodeURIComponent(mainEntry)}`;
4150
+ const mainEntryUrl = rootMain && rootMain.startsWith(pkgDir + path12.sep) ? `/@modules/${pkgName}` : `/@modules/${pkgName}?id=${encodeURIComponent(mainEntry)}`;
3750
4151
  const lines = [
3751
4152
  `// Nasti subpath shim \u2192 ${pkgName} (avoid duplicate bundling)`,
3752
4153
  `import * as __pkg from "${mainEntryUrl}";`
@@ -3760,10 +4161,10 @@ async function tryGenerateSubpathShim(entryFile, root) {
3760
4161
  return lines.join("\n") + "\n";
3761
4162
  }
3762
4163
  function pickMainEntryByExtension(pkgDir, preferredExt) {
3763
- const pkgJsonPath = path11.join(pkgDir, "package.json");
4164
+ const pkgJsonPath = path12.join(pkgDir, "package.json");
3764
4165
  let pkg;
3765
4166
  try {
3766
- pkg = JSON.parse(fs8.readFileSync(pkgJsonPath, "utf-8"));
4167
+ pkg = JSON.parse(fs9.readFileSync(pkgJsonPath, "utf-8"));
3767
4168
  } catch {
3768
4169
  return null;
3769
4170
  }
@@ -3782,14 +4183,14 @@ function pickMainEntryByExtension(pkgDir, preferredExt) {
3782
4183
  if (typeof pkg.module === "string") candidates.push(pkg.module);
3783
4184
  if (typeof pkg.main === "string") candidates.push(pkg.main);
3784
4185
  for (const cand of candidates) {
3785
- if (path11.extname(cand) === preferredExt) {
3786
- const full = path11.resolve(pkgDir, cand);
3787
- if (fs8.existsSync(full)) return full;
4186
+ if (path12.extname(cand) === preferredExt) {
4187
+ const full = path12.resolve(pkgDir, cand);
4188
+ if (fs9.existsSync(full)) return full;
3788
4189
  }
3789
4190
  }
3790
4191
  for (const cand of candidates) {
3791
- const full = path11.resolve(pkgDir, cand);
3792
- if (fs8.existsSync(full)) return full;
4192
+ const full = path12.resolve(pkgDir, cand);
4193
+ if (fs9.existsSync(full)) return full;
3793
4194
  }
3794
4195
  return null;
3795
4196
  }
@@ -3835,72 +4236,231 @@ async function injectCjsNamedExports(code, entryFile) {
3835
4236
  return code;
3836
4237
  }
3837
4238
  }
3838
- function rewriteImports(code, config, filePath) {
4239
+ function rewriteImports(code, config, filePath, importedUrls, moduleGraph) {
4240
+ const resolveSpec = createModuleSpecifierResolver(config, filePath);
4241
+ const transformSpec = (spec) => {
4242
+ const resolved = removeTimestampQuery(resolveSpec(spec));
4243
+ importedUrls?.add(resolved);
4244
+ const timestamp = moduleGraph?.getModuleByUrl(resolved)?.lastHMRTimestamp ?? 0;
4245
+ return timestamp > 0 ? appendTimestampQuery(resolved, timestamp) : resolved;
4246
+ };
4247
+ return code.replace(
4248
+ /\bfrom\s+(['"])([^'"]+)\1/g,
4249
+ (_m, q, s) => `from ${q}${transformSpec(s)}${q}`
4250
+ ).replace(
4251
+ /\bimport\s+(['"])([^'"]+)\1/g,
4252
+ (_m, q, s) => `import ${q}${transformSpec(s)}${q}`
4253
+ ).replace(
4254
+ /\bimport\s*\(\s*(['"])([^'"]+)\1\s*\)/g,
4255
+ (_m, q, s) => `import(${q}${transformSpec(s)}${q})`
4256
+ );
4257
+ }
4258
+ function createModuleSpecifierResolver(config, filePath) {
3839
4259
  const root = config.root;
3840
- const fileDir = path11.dirname(filePath);
4260
+ const fileDir = path12.dirname(filePath);
3841
4261
  const aliasEntries = Object.entries(config.resolve.alias).sort(
3842
4262
  ([a], [b]) => b.length - a.length
3843
4263
  );
3844
- const toRootUrl = (abs) => "/" + path11.relative(root, abs).replace(/\\/g, "/");
3845
- const transformSpec = (spec) => {
3846
- const suffixMatch = spec.match(/[?#].*$/);
4264
+ const toRootUrl = (abs) => "/" + path12.relative(root, abs).replace(/\\/g, "/");
4265
+ return (specifier) => {
4266
+ const suffixMatch = specifier.match(/[?#].*$/);
3847
4267
  const suffix = suffixMatch ? suffixMatch[0] : "";
3848
- const baseSpec = suffix ? spec.slice(0, -suffix.length) : spec;
4268
+ const baseSpec = suffix ? specifier.slice(0, -suffix.length) : specifier;
3849
4269
  for (const [key, value] of aliasEntries) {
3850
4270
  if (baseSpec === key || baseSpec.startsWith(key + "/")) {
3851
4271
  const aliasBase = resolveAliasTarget2(value, root);
3852
4272
  const sub = baseSpec.slice(key.length).replace(/^\//, "");
3853
- const target = sub ? path11.join(aliasBase, sub) : aliasBase;
4273
+ const target = sub ? path12.join(aliasBase, sub) : aliasBase;
3854
4274
  const resolved = tryResolveDiskPath(target);
3855
- return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : spec;
4275
+ return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : specifier;
3856
4276
  }
3857
4277
  }
3858
4278
  if (baseSpec.startsWith("./") || baseSpec.startsWith("../")) {
3859
- const target = path11.resolve(fileDir, baseSpec);
3860
- const resolved = tryResolveDiskPath(target);
3861
- return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : spec;
4279
+ const resolved = tryResolveDiskPath(path12.resolve(fileDir, baseSpec));
4280
+ return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : specifier;
3862
4281
  }
3863
4282
  if (baseSpec.startsWith("/") && !baseSpec.startsWith("/@")) {
3864
- const target = path11.join(root, baseSpec.replace(/^\//, ""));
3865
- const resolved = tryResolveDiskPath(target);
3866
- return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : spec;
4283
+ const resolved = tryResolveDiskPath(path12.join(root, baseSpec.replace(/^\//, "")));
4284
+ return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : specifier;
3867
4285
  }
3868
- if (baseSpec.startsWith("/")) return spec;
3869
- return `/@modules/${spec}`;
4286
+ if (baseSpec.startsWith("/")) return specifier;
4287
+ return `/@modules/${specifier}`;
3870
4288
  };
3871
- return code.replace(
3872
- /\bfrom\s+(['"])([^'"]+)\1/g,
3873
- (_m, q, s) => `from ${q}${transformSpec(s)}${q}`
3874
- ).replace(
3875
- /\bimport\s+(['"])([^'"]+)\1/g,
3876
- (_m, q, s) => `import ${q}${transformSpec(s)}${q}`
3877
- ).replace(
3878
- /\bimport\s*\(\s*(['"])([^'"]+)\1\s*\)/g,
3879
- (_m, q, s) => `import(${q}${transformSpec(s)}${q})`
3880
- );
4289
+ }
4290
+ function rewriteHotAcceptDeps(code, config, filePath) {
4291
+ const acceptedUrls = /* @__PURE__ */ new Set();
4292
+ const edits = [];
4293
+ const resolveSpec = createModuleSpecifierResolver(config, filePath);
4294
+ const acceptRE = /(?:\bimport\.meta\.hot|\b__nasti_hot__)(?:(?:\?\.)|\.)accept\s*\(/g;
4295
+ const searchableCode = maskStringsAndComments(code);
4296
+ let isSelfAccepting = false;
4297
+ let match;
4298
+ while (match = acceptRE.exec(searchableCode)) {
4299
+ let cursor = match.index + match[0].length;
4300
+ const skipTrivia = () => {
4301
+ while (cursor < code.length) {
4302
+ if (/\s/.test(code[cursor])) {
4303
+ cursor++;
4304
+ continue;
4305
+ }
4306
+ if (code[cursor] === "/" && code[cursor + 1] === "/") {
4307
+ cursor += 2;
4308
+ while (cursor < code.length && code[cursor] !== "\n") cursor++;
4309
+ continue;
4310
+ }
4311
+ if (code[cursor] === "/" && code[cursor + 1] === "*") {
4312
+ cursor += 2;
4313
+ while (cursor < code.length && !(code[cursor] === "*" && code[cursor + 1] === "/")) cursor++;
4314
+ cursor += 2;
4315
+ continue;
4316
+ }
4317
+ break;
4318
+ }
4319
+ };
4320
+ skipTrivia();
4321
+ const first = code[cursor];
4322
+ if (!first || first === ")" || first !== "[" && first !== "'" && first !== '"' && first !== "`") {
4323
+ isSelfAccepting = true;
4324
+ continue;
4325
+ }
4326
+ const readLiteral = () => {
4327
+ const quote = code[cursor];
4328
+ if (quote !== "'" && quote !== '"' && quote !== "`") return;
4329
+ const start = cursor;
4330
+ cursor++;
4331
+ let raw = "";
4332
+ while (cursor < code.length) {
4333
+ const char = code[cursor];
4334
+ if (char === "\\") {
4335
+ raw += code[cursor + 1] ?? "";
4336
+ cursor += 2;
4337
+ continue;
4338
+ }
4339
+ if (char === quote) {
4340
+ cursor++;
4341
+ const resolved = removeTimestampQuery(resolveSpec(raw));
4342
+ acceptedUrls.add(resolved);
4343
+ edits.push({ start, end: cursor, value: JSON.stringify(resolved) });
4344
+ return;
4345
+ }
4346
+ if (quote === "`" && char === "$" && code[cursor + 1] === "{") return;
4347
+ raw += char;
4348
+ cursor++;
4349
+ }
4350
+ };
4351
+ if (first === "[") {
4352
+ cursor++;
4353
+ while (cursor < code.length) {
4354
+ skipTrivia();
4355
+ if (code[cursor] === ",") {
4356
+ cursor++;
4357
+ skipTrivia();
4358
+ }
4359
+ if (code[cursor] === "]") break;
4360
+ const before = cursor;
4361
+ readLiteral();
4362
+ if (cursor === before) break;
4363
+ }
4364
+ } else {
4365
+ readLiteral();
4366
+ }
4367
+ }
4368
+ for (const edit of edits.sort((a, b) => b.start - a.start)) {
4369
+ code = code.slice(0, edit.start) + edit.value + code.slice(edit.end);
4370
+ }
4371
+ return { code, acceptedUrls, isSelfAccepting };
4372
+ }
4373
+ function maskStringsAndComments(code) {
4374
+ const masked = code.split("");
4375
+ let state = "code";
4376
+ const isRegexStart = (index2) => {
4377
+ let previous = index2 - 1;
4378
+ while (previous >= 0 && /\s/.test(code[previous])) previous--;
4379
+ return previous < 0 || "=(:,!&|?{};[]+-*%^~<>".includes(code[previous]);
4380
+ };
4381
+ for (let i = 0; i < code.length; i++) {
4382
+ const char = code[i];
4383
+ const next = code[i + 1];
4384
+ if (state === "code") {
4385
+ if (char === "'") state = "single";
4386
+ else if (char === '"') state = "double";
4387
+ else if (char === "`") state = "template";
4388
+ else if (char === "/" && next === "/") state = "line-comment";
4389
+ else if (char === "/" && next === "*") state = "block-comment";
4390
+ else if (char === "/" && isRegexStart(i)) state = "regex";
4391
+ else continue;
4392
+ masked[i] = " ";
4393
+ continue;
4394
+ }
4395
+ if (state === "line-comment") {
4396
+ if (char === "\n") {
4397
+ state = "code";
4398
+ } else {
4399
+ masked[i] = " ";
4400
+ }
4401
+ continue;
4402
+ }
4403
+ if (state === "block-comment") {
4404
+ masked[i] = char === "\n" ? "\n" : " ";
4405
+ if (char === "*" && next === "/") {
4406
+ masked[i + 1] = " ";
4407
+ i++;
4408
+ state = "code";
4409
+ }
4410
+ continue;
4411
+ }
4412
+ if (state === "regex" || state === "regex-class") {
4413
+ masked[i] = char === "\n" ? "\n" : " ";
4414
+ if (char === "\\") {
4415
+ if (i + 1 < code.length) masked[++i] = " ";
4416
+ } else if (state === "regex" && char === "[") {
4417
+ state = "regex-class";
4418
+ } else if (state === "regex-class" && char === "]") {
4419
+ state = "regex";
4420
+ } else if (state === "regex" && char === "/") {
4421
+ state = "code";
4422
+ }
4423
+ continue;
4424
+ }
4425
+ masked[i] = char === "\n" ? "\n" : " ";
4426
+ if (char === "\\") {
4427
+ if (i + 1 < code.length) masked[++i] = " ";
4428
+ continue;
4429
+ }
4430
+ if (state === "single" && char === "'" || state === "double" && char === '"' || state === "template" && char === "`") {
4431
+ state = "code";
4432
+ }
4433
+ }
4434
+ return masked.join("");
3881
4435
  }
3882
4436
  function resolveAliasTarget2(value, root) {
3883
- if (path11.isAbsolute(value) && fs8.existsSync(value)) return value;
3884
- if (value.startsWith("/")) return path11.join(root, value.slice(1));
3885
- return path11.resolve(root, value);
4437
+ if (path12.isAbsolute(value) && fs9.existsSync(value)) return value;
4438
+ if (value.startsWith("/")) return path12.join(root, value.slice(1));
4439
+ return path12.resolve(root, value);
3886
4440
  }
3887
4441
  function tryResolveDiskPath(target) {
3888
- if (fs8.existsSync(target) && fs8.statSync(target).isFile()) return target;
4442
+ if (fs9.existsSync(target) && fs9.statSync(target).isFile()) return target;
3889
4443
  for (const ext of RESOLVE_EXTENSIONS) {
3890
4444
  const withExt = target + ext;
3891
- if (fs8.existsSync(withExt) && fs8.statSync(withExt).isFile()) return withExt;
4445
+ if (fs9.existsSync(withExt) && fs9.statSync(withExt).isFile()) return withExt;
3892
4446
  }
3893
- if (fs8.existsSync(target) && fs8.statSync(target).isDirectory()) {
4447
+ if (fs9.existsSync(target) && fs9.statSync(target).isDirectory()) {
3894
4448
  for (const ext of RESOLVE_EXTENSIONS) {
3895
- const idx = path11.join(target, "index" + ext);
3896
- if (fs8.existsSync(idx) && fs8.statSync(idx).isFile()) return idx;
4449
+ const idx = path12.join(target, "index" + ext);
4450
+ if (fs9.existsSync(idx) && fs9.statSync(idx).isFile()) return idx;
3897
4451
  }
3898
4452
  }
3899
4453
  return null;
3900
4454
  }
3901
4455
  function isUnderRoot(abs, root) {
3902
- const rel = path11.relative(root, abs);
3903
- return !!rel && !rel.startsWith("..") && !path11.isAbsolute(rel);
4456
+ const rel = path12.relative(root, abs);
4457
+ return !!rel && !rel.startsWith("..") && !path12.isAbsolute(rel);
4458
+ }
4459
+ function appendTimestampQuery(url, timestamp) {
4460
+ const hashIndex = url.indexOf("#");
4461
+ const hash = hashIndex >= 0 ? url.slice(hashIndex) : "";
4462
+ const withoutHash = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
4463
+ return `${withoutHash}${withoutHash.includes("?") ? "&" : "?"}t=${timestamp}${hash}`;
3904
4464
  }
3905
4465
  function externalSpecToModuleUrl(spec, baseDir, root) {
3906
4466
  const resolved = resolveNodeModule(baseDir, spec);
@@ -3913,7 +4473,7 @@ function resolveNodeModule(baseDir, moduleName) {
3913
4473
  const resolved = resolveNodeModuleEntry(baseDir, moduleName);
3914
4474
  if (!resolved) return null;
3915
4475
  try {
3916
- return fs8.realpathSync(resolved);
4476
+ return fs9.realpathSync(resolved);
3917
4477
  } catch {
3918
4478
  return resolved;
3919
4479
  }
@@ -3933,21 +4493,21 @@ function resolveNodeModuleEntry(root, moduleName) {
3933
4493
  let pkgDir = null;
3934
4494
  let dir = root;
3935
4495
  for (; ; ) {
3936
- const candidate = path11.join(dir, "node_modules", pkgName);
3937
- if (fs8.existsSync(candidate)) {
4496
+ const candidate = path12.join(dir, "node_modules", pkgName);
4497
+ if (fs9.existsSync(candidate)) {
3938
4498
  pkgDir = candidate;
3939
4499
  break;
3940
4500
  }
3941
- const parent = path11.dirname(dir);
4501
+ const parent = path12.dirname(dir);
3942
4502
  if (parent === dir) break;
3943
4503
  dir = parent;
3944
4504
  }
3945
4505
  if (!pkgDir) return null;
3946
- const pkgJsonPath = path11.join(pkgDir, "package.json");
3947
- if (!fs8.existsSync(pkgJsonPath)) return null;
4506
+ const pkgJsonPath = path12.join(pkgDir, "package.json");
4507
+ if (!fs9.existsSync(pkgJsonPath)) return null;
3948
4508
  let pkg;
3949
4509
  try {
3950
- pkg = JSON.parse(fs8.readFileSync(pkgJsonPath, "utf-8"));
4510
+ pkg = JSON.parse(fs9.readFileSync(pkgJsonPath, "utf-8"));
3951
4511
  } catch {
3952
4512
  return null;
3953
4513
  }
@@ -3960,32 +4520,32 @@ function resolveNodeModuleEntry(root, moduleName) {
3960
4520
  const subDirs = [""];
3961
4521
  for (const field of ["module", "main"]) {
3962
4522
  if (typeof pkg[field] === "string") {
3963
- const dir2 = path11.dirname(pkg[field]);
4523
+ const dir2 = path12.dirname(pkg[field]);
3964
4524
  if (dir2 && dir2 !== "." && !subDirs.includes(dir2)) subDirs.push(dir2);
3965
4525
  }
3966
4526
  }
3967
4527
  for (const dir2 of subDirs) {
3968
- const direct = path11.join(pkgDir, dir2, subpath);
3969
- if (fs8.existsSync(direct) && fs8.statSync(direct).isFile()) return direct;
4528
+ const direct = path12.join(pkgDir, dir2, subpath);
4529
+ if (fs9.existsSync(direct) && fs9.statSync(direct).isFile()) return direct;
3970
4530
  for (const ext of RESOLVE_EXTENSIONS) {
3971
- if (fs8.existsSync(direct + ext)) return direct + ext;
4531
+ if (fs9.existsSync(direct + ext)) return direct + ext;
3972
4532
  }
3973
4533
  }
3974
4534
  return null;
3975
4535
  }
3976
4536
  for (const field of ["module", "jsnext:main", "jsnext", "main"]) {
3977
4537
  if (typeof pkg[field] === "string") {
3978
- const entry = path11.join(pkgDir, pkg[field]);
3979
- if (fs8.existsSync(entry)) return entry;
4538
+ const entry = path12.join(pkgDir, pkg[field]);
4539
+ if (fs9.existsSync(entry)) return entry;
3980
4540
  }
3981
4541
  }
3982
- const indexFallback = path11.join(pkgDir, "index.js");
3983
- if (fs8.existsSync(indexFallback)) return indexFallback;
4542
+ const indexFallback = path12.join(pkgDir, "index.js");
4543
+ if (fs9.existsSync(indexFallback)) return indexFallback;
3984
4544
  return null;
3985
4545
  }
3986
4546
  function resolvePackageExports(exports, key, pkgDir) {
3987
4547
  if (typeof exports === "string") {
3988
- return key === "." ? path11.join(pkgDir, exports) : null;
4548
+ return key === "." ? path12.join(pkgDir, exports) : null;
3989
4549
  }
3990
4550
  const entry = exports[key];
3991
4551
  if (entry === void 0) {
@@ -3997,7 +4557,7 @@ function resolvePackageExports(exports, key, pkgDir) {
3997
4557
  return resolveExportValue(entry, pkgDir);
3998
4558
  }
3999
4559
  function resolveExportValue(value, pkgDir) {
4000
- if (typeof value === "string") return path11.join(pkgDir, value);
4560
+ if (typeof value === "string") return path12.join(pkgDir, value);
4001
4561
  if (Array.isArray(value)) {
4002
4562
  for (const item of value) {
4003
4563
  const r = resolveExportValue(item, pkgDir);
@@ -4021,17 +4581,17 @@ function resolveUrlToFile(url, root) {
4021
4581
  const moduleName = cleanUrl.slice("/@modules/".length);
4022
4582
  return resolveNodeModule(root, moduleName);
4023
4583
  }
4024
- const filePath = path11.resolve(root, cleanUrl.replace(/^\//, ""));
4025
- if (fs8.existsSync(filePath) && fs8.statSync(filePath).isFile()) {
4584
+ const filePath = path12.resolve(root, cleanUrl.replace(/^\//, ""));
4585
+ if (fs9.existsSync(filePath) && fs9.statSync(filePath).isFile()) {
4026
4586
  return filePath;
4027
4587
  }
4028
4588
  for (const ext of RESOLVE_EXTENSIONS) {
4029
4589
  const withExt = filePath + ext;
4030
- if (fs8.existsSync(withExt)) return withExt;
4590
+ if (fs9.existsSync(withExt)) return withExt;
4031
4591
  }
4032
4592
  for (const ext of RESOLVE_EXTENSIONS) {
4033
- const indexFile = path11.join(filePath, "index" + ext);
4034
- if (fs8.existsSync(indexFile)) return indexFile;
4593
+ const indexFile = path12.join(filePath, "index" + ext);
4594
+ if (fs9.existsSync(indexFile)) return indexFile;
4035
4595
  }
4036
4596
  return null;
4037
4597
  }
@@ -4039,36 +4599,35 @@ function isModuleRequest(url) {
4039
4599
  const cleanUrl = url.split("?")[0];
4040
4600
  if (/\.(ts|tsx|jsx|js|mjs|vue|css|json)$/.test(cleanUrl)) return true;
4041
4601
  if (cleanUrl.startsWith("/@modules/")) return true;
4042
- if (!path11.extname(cleanUrl)) return true;
4602
+ if (!path12.extname(cleanUrl)) return true;
4043
4603
  return false;
4044
4604
  }
4045
4605
  function getHmrClientCode() {
4046
4606
  return `
4047
4607
  // Nasti HMR Client
4048
- const socket = new WebSocket(\`ws://\${location.host}\`, 'nasti-hmr');
4608
+ const socketProtocol = location.protocol === 'https:' ? 'wss' : 'ws';
4609
+ const socket = new WebSocket(socketProtocol + '://' + location.host, 'nasti-hmr');
4049
4610
  const hotModulesMap = new Map();
4050
4611
  const disposeMap = new Map();
4051
4612
  const pruneMap = new Map();
4613
+ const dataMap = new Map();
4614
+ let updateQueue = [];
4615
+ let pendingUpdateQueue = false;
4052
4616
 
4053
4617
  socket.addEventListener('message', async ({ data }) => {
4054
4618
  const payload = JSON.parse(data);
4055
4619
  switch (payload.type) {
4056
4620
  case 'connected':
4057
- console.log('[nasti] connected.');
4621
+ console.debug('[nasti] connected.');
4058
4622
  clearErrorOverlay();
4059
4623
  break;
4060
4624
  case 'update':
4061
4625
  try {
4062
- await Promise.all(payload.updates.map((update) => {
4063
- if (update.type === 'js-update') {
4064
- return fetchUpdate(update);
4065
- } else if (update.type === 'css-update') {
4066
- return updateCss(update.path);
4067
- }
4068
- }));
4626
+ // CSS \u5728 unbundled \u6A21\u5F0F\u4E0B\u4E5F\u662F\u4F1A\u6CE8\u5165 <style> \u7684 JS \u6A21\u5757\uFF0C\u548C\u666E\u901A JS
4627
+ // \u4E00\u6837\u91CD\u65B0 import \u624D\u80FD\u6267\u884C dispose/accept \u5E76\u4FDD\u6301\u9875\u9762\u72B6\u6001\u3002
4628
+ await Promise.all(payload.updates.map(queueUpdate));
4069
4629
  clearErrorOverlay();
4070
- console.log('[nasti] HMR update complete, reloading page');
4071
- location.reload();
4630
+ console.debug('[nasti] HMR update complete.');
4072
4631
  } catch (err) {
4073
4632
  console.error('[nasti] HMR update failed:', err);
4074
4633
  showErrorOverlay(err);
@@ -4079,10 +4638,17 @@ socket.addEventListener('message', async ({ data }) => {
4079
4638
  location.reload();
4080
4639
  break;
4081
4640
  case 'prune':
4082
- payload.paths.forEach((p) => {
4083
- const cb = pruneMap.get(p);
4084
- if (cb) cb();
4085
- });
4641
+ await Promise.all(payload.paths.map(async (path) => {
4642
+ const data = dataMap.get(path);
4643
+ const dispose = disposeMap.get(path);
4644
+ const prune = pruneMap.get(path);
4645
+ if (dispose) await dispose(data);
4646
+ if (prune) await prune(data);
4647
+ hotModulesMap.delete(path);
4648
+ disposeMap.delete(path);
4649
+ pruneMap.delete(path);
4650
+ dataMap.delete(path);
4651
+ }));
4086
4652
  break;
4087
4653
  case 'error':
4088
4654
  console.error('[nasti] error:', payload.err.message);
@@ -4091,33 +4657,64 @@ socket.addEventListener('message', async ({ data }) => {
4091
4657
  }
4092
4658
  });
4093
4659
 
4094
- // \u81EA\u52A8\u91CD\u8FDE\uFF08\u65AD\u7EBF\u65F6\u6307\u6570\u9000\u907F\uFF09
4660
+ // \u670D\u52A1\u91CD\u542F\u540E\u65E7\u6A21\u5757\u56FE\u5DF2\u5931\u6548\uFF0C\u91CD\u8FDE\u65F6\u6574\u9875\u5237\u65B0\u662F\u5FC5\u8981\u515C\u5E95\uFF1B\u6B63\u5E38 update \u4E0D\u518D\u5237\u65B0\u3002
4095
4661
  let reconnectTimer = 0;
4096
4662
  socket.addEventListener('close', () => {
4097
4663
  clearTimeout(reconnectTimer);
4098
4664
  reconnectTimer = setTimeout(() => location.reload(), 1000);
4099
4665
  });
4100
4666
 
4667
+ /**
4668
+ * \u540C\u4E00\u6279\u66F4\u65B0\u5148\u5168\u90E8\u62C9\u53D6\uFF0C\u518D\u6309\u670D\u52A1\u7AEF\u6D88\u606F\u987A\u5E8F\u6267\u884C accept \u56DE\u8C03\uFF0C\u907F\u514D HTTP \u5F80\u8FD4\u901F\u5EA6
4669
+ * \u6539\u53D8\u6A21\u5757\u5E94\u7528\u987A\u5E8F\u3002\u8FD9\u4E0E Vite HMRClient \u7684 fetch/apply \u4E24\u9636\u6BB5\u4E00\u81F4\u3002
4670
+ */
4671
+ async function queueUpdate(update) {
4672
+ updateQueue.push(fetchUpdate(update));
4673
+ if (pendingUpdateQueue) return;
4674
+
4675
+ pendingUpdateQueue = true;
4676
+ await Promise.resolve();
4677
+ pendingUpdateQueue = false;
4678
+ const loading = updateQueue;
4679
+ updateQueue = [];
4680
+ const applyUpdates = await Promise.all(loading);
4681
+ for (const apply of applyUpdates) {
4682
+ if (apply) apply();
4683
+ }
4684
+ }
4685
+
4101
4686
  async function fetchUpdate(update) {
4102
4687
  const mod = hotModulesMap.get(update.path);
4103
- // \u5148\u8DD1 dispose\uFF08\u7ED9\u6A21\u5757\u673A\u4F1A\u6E05\u7406\u526F\u4F5C\u7528\uFF09
4104
- const dispose = disposeMap.get(update.path);
4105
- if (dispose) dispose();
4688
+ // \u5C1A\u672A\u5728\u5F53\u524D\u9875\u9762\u52A0\u8F7D\u7684\u52A8\u6001\u6A21\u5757\u4E0D\u9700\u8981\u66F4\u65B0\u3002
4689
+ if (!mod) return;
4106
4690
 
4107
- const newMod = await import(update.acceptedPath + '?t=' + update.timestamp);
4108
- if (mod) {
4109
- // \u590D\u5236\u56DE\u8C03\u6570\u7EC4\u907F\u514D\u56DE\u8C03\u5185\u90E8\u53C8\u4FEE\u6539 hotModulesMap \u9020\u6210\u8FED\u4EE3\u5F02\u5E38
4110
- [...mod.callbacks].forEach((cb) => cb(newMod));
4111
- }
4691
+ // \u5FC5\u987B\u5728\u91CD\u65B0 import \u524D\u786E\u5B9A\u65E7\u56DE\u8C03\uFF1B\u65B0\u6A21\u5757\u6267\u884C createHotContext \u65F6\u4F1A\u6E05\u7A7A\u5E76\u6CE8\u518C\u65B0\u56DE\u8C03\u3002
4692
+ const qualifiedCallbacks = mod.callbacks.filter(({ deps }) =>
4693
+ deps.includes(update.acceptedPath)
4694
+ );
4695
+ const isSelfUpdate = update.path === update.acceptedPath;
4696
+ if (!isSelfUpdate && qualifiedCallbacks.length === 0) return;
4697
+
4698
+ const dispose = disposeMap.get(update.acceptedPath);
4699
+ if (dispose) await dispose(dataMap.get(update.acceptedPath));
4700
+ const newMod = await import(appendTimestampQuery(update.acceptedPath, update.timestamp));
4701
+
4702
+ return () => {
4703
+ for (const { deps, fn } of qualifiedCallbacks) {
4704
+ fn(deps.map((dep) => dep === update.acceptedPath ? newMod : undefined));
4705
+ }
4706
+ const detail = isSelfUpdate
4707
+ ? update.path
4708
+ : update.acceptedPath + ' via ' + update.path;
4709
+ console.debug('[nasti] hot updated:', detail);
4710
+ };
4112
4711
  }
4113
4712
 
4114
- function updateCss(path) {
4115
- const el = document.querySelector(\`style[data-nasti-css="\${path}"]\`);
4116
- if (el) {
4117
- return fetch(path + '?t=' + Date.now())
4118
- .then(r => r.text())
4119
- .then(css => { el.textContent = css; });
4120
- }
4713
+ function appendTimestampQuery(url, timestamp) {
4714
+ const hashIndex = url.indexOf('#');
4715
+ const hash = hashIndex >= 0 ? url.slice(hashIndex) : '';
4716
+ const withoutHash = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
4717
+ return withoutHash + (withoutHash.includes('?') ? '&' : '?') + 't=' + timestamp + hash;
4121
4718
  }
4122
4719
 
4123
4720
  function clearErrorOverlay() {
@@ -4145,23 +4742,30 @@ function showErrorOverlay(err) {
4145
4742
  document.body.appendChild(overlay);
4146
4743
  }
4147
4744
 
4148
- /**
4149
- * \u751F\u6210 import.meta.hot \u7684 hot context\u3002
4150
- * \u5173\u952E\u7EA6\u675F\uFF1A\u540C\u4E00 ownerPath \u7684 accept \u56DE\u8C03\u5FC5\u987B\u66FF\u6362\uFF08\u4E0D\u662F append\uFF09\u3002
4151
- * \u6BCF\u6B21\u6A21\u5757\u91CD\u65B0 import \u90FD\u4F1A\u8C03\u7528 createHotContext\uFF0C\u65E7\u56DE\u8C03\u4F1A\u88AB fetchUpdate \u8C03\u7528\u540E\u7ACB\u5373\u88AB\u65B0 import
4152
- * \u91CC\u7684 accept \u66FF\u6362\u3002\u4E0D\u66FF\u6362\u7684\u8BDD\u6BCF\u7F16\u8F91\u4E00\u6B21\u5C31\u591A\u4E00\u4E2A\u56DE\u8C03\uFF0C\u8D8A\u8DD1\u8D8A\u6162\u3002
4153
- */
4154
4745
  export function createHotContext(ownerPath) {
4746
+ if (!dataMap.has(ownerPath)) dataMap.set(ownerPath, {});
4747
+
4748
+ // \u6A21\u5757\u91CD\u65B0\u6267\u884C\u65F6\u4E22\u5F03\u65E7 accept \u56DE\u8C03\uFF0C\u4F46\u4FDD\u7559\u540C\u4E00\u4E2A hot.data \u5BF9\u8C61\u3002
4749
+ const existing = hotModulesMap.get(ownerPath);
4750
+ if (existing) existing.callbacks = [];
4751
+
4752
+ const acceptDeps = (deps, callback = () => {}) => {
4753
+ const mod = hotModulesMap.get(ownerPath) || { id: ownerPath, callbacks: [] };
4754
+ mod.callbacks.push({ deps, fn: callback });
4755
+ hotModulesMap.set(ownerPath, mod);
4756
+ };
4757
+
4155
4758
  return {
4156
4759
  accept(deps, callback) {
4157
- // \u81EA\u63A5\u53D7: hot.accept() \u6216 hot.accept(callback)
4158
4760
  if (typeof deps === 'function' || deps === undefined) {
4159
- hotModulesMap.set(ownerPath, { callbacks: [deps || (() => {})] });
4160
- return;
4761
+ acceptDeps([ownerPath], ([mod]) => deps?.(mod));
4762
+ } else if (typeof deps === 'string') {
4763
+ acceptDeps([deps], ([mod]) => callback?.(mod));
4764
+ } else if (Array.isArray(deps)) {
4765
+ acceptDeps(deps, callback);
4766
+ } else {
4767
+ throw new Error('invalid hot.accept() usage');
4161
4768
  }
4162
- // \u4F9D\u8D56\u63A5\u53D7: hot.accept(deps, callback)\uFF0C\u591A\u6B21\u8C03\u7528\u8FFD\u52A0
4163
- const existing = hotModulesMap.get(ownerPath)?.callbacks ?? [];
4164
- hotModulesMap.set(ownerPath, { callbacks: [...existing, callback] });
4165
4769
  },
4166
4770
  prune(callback) {
4167
4771
  pruneMap.set(ownerPath, callback);
@@ -4172,21 +4776,85 @@ export function createHotContext(ownerPath) {
4172
4776
  invalidate() {
4173
4777
  location.reload();
4174
4778
  },
4175
- data: {},
4779
+ data: dataMap.get(ownerPath),
4176
4780
  };
4177
4781
  }
4178
4782
  `;
4179
4783
  }
4180
- var __dirname_esm, __require2, __refreshRuntimeCache, REACT_REFRESH_GLOBAL_PREAMBLE, esmBundleCache, VALID_IDENT, RESOLVE_EXTENSIONS, ESM_CONDITIONS;
4784
+ var __dirname_esm, __require2, __refreshRuntimeCache, REACT_REFRESH_BOUNDARY_HELPERS, REACT_REFRESH_GLOBAL_PREAMBLE, esmBundleCache, VALID_IDENT, RESOLVE_EXTENSIONS, ESM_CONDITIONS;
4181
4785
  var init_middleware = __esm({
4182
4786
  "src/server/middleware.ts"() {
4183
4787
  "use strict";
4184
4788
  init_transformer();
4185
4789
  init_html();
4186
4790
  init_env();
4187
- __dirname_esm = path11.dirname(fileURLToPath(import.meta.url));
4791
+ init_url();
4792
+ __dirname_esm = path12.dirname(fileURLToPath(import.meta.url));
4188
4793
  __require2 = createRequire3(import.meta.url);
4189
4794
  __refreshRuntimeCache = null;
4795
+ REACT_REFRESH_BOUNDARY_HELPERS = `
4796
+ function __nastiIsPlainObject(obj) {
4797
+ return Object.prototype.toString.call(obj) === '[object Object]' &&
4798
+ (obj.constructor === Object || obj.constructor === undefined);
4799
+ }
4800
+ function __nastiIsCompoundComponent(type) {
4801
+ if (!__nastiIsPlainObject(type)) return false;
4802
+ for (const key in type) {
4803
+ if (!isLikelyComponentType(type[key])) return false;
4804
+ }
4805
+ return true;
4806
+ }
4807
+ export function registerExportsForReactRefresh(filename, moduleExports) {
4808
+ for (const key in moduleExports) {
4809
+ if (key === '__esModule') continue;
4810
+ const value = moduleExports[key];
4811
+ if (isLikelyComponentType(value)) {
4812
+ register(value, filename + ' export ' + key);
4813
+ } else if (__nastiIsCompoundComponent(value)) {
4814
+ for (const subKey in value) {
4815
+ register(value[subKey], filename + ' export ' + key + '-' + subKey);
4816
+ }
4817
+ }
4818
+ }
4819
+ }
4820
+ let __nastiRefreshTimer;
4821
+ function __nastiEnqueueRefresh() {
4822
+ clearTimeout(__nastiRefreshTimer);
4823
+ __nastiRefreshTimer = setTimeout(() => performReactRefresh(), 16);
4824
+ }
4825
+ function __nastiCheckExports(ignored, exports, predicate) {
4826
+ for (const key in exports) {
4827
+ if (ignored.includes(key)) continue;
4828
+ if (!predicate(key, exports[key])) return key;
4829
+ }
4830
+ return true;
4831
+ }
4832
+ export function validateRefreshBoundaryAndEnqueueUpdate(id, prevExports, nextExports) {
4833
+ const ignored = window.__getReactRefreshIgnoredExports?.({ id }) ?? [];
4834
+ if (__nastiCheckExports(ignored, prevExports, (key) => key in nextExports) !== true) {
4835
+ return 'Could not Fast Refresh (export removed)';
4836
+ }
4837
+ if (__nastiCheckExports(ignored, nextExports, (key) => key in prevExports) !== true) {
4838
+ return 'Could not Fast Refresh (new export)';
4839
+ }
4840
+ let hasExports = false;
4841
+ const compatible = __nastiCheckExports(ignored, nextExports, (key, value) => {
4842
+ hasExports = true;
4843
+ return isLikelyComponentType(value) ||
4844
+ __nastiIsCompoundComponent(value) ||
4845
+ prevExports[key] === value;
4846
+ });
4847
+ if (!hasExports) {
4848
+ return 'Could not Fast Refresh (no exports)';
4849
+ }
4850
+ if (compatible === true) {
4851
+ __nastiEnqueueRefresh();
4852
+ return;
4853
+ }
4854
+ return 'Could not Fast Refresh ("' + compatible + '" export is incompatible)';
4855
+ }
4856
+ export const __hmr_import = (module) => import(module);
4857
+ `;
4190
4858
  REACT_REFRESH_GLOBAL_PREAMBLE = `
4191
4859
  import RefreshRuntime from "/@react-refresh";
4192
4860
  RefreshRuntime.injectIntoGlobalHook(window);
@@ -4202,27 +4870,29 @@ window.__vite_plugin_react_preamble_installed__ = true;
4202
4870
  });
4203
4871
 
4204
4872
  // src/server/hmr.ts
4205
- import path12 from "path";
4206
- import fs9 from "fs";
4873
+ import path13 from "path";
4874
+ import fs10 from "fs";
4207
4875
  import pc7 from "picocolors";
4208
4876
  async function handleFileChange(file, server) {
4209
4877
  const { moduleGraph, ws, config } = server;
4210
4878
  const logger = config.logger;
4211
- const relativePath = "/" + path12.relative(config.root, file);
4212
- const shortFile = path12.relative(config.root, file);
4879
+ const relativePath = "/" + path13.relative(config.root, file);
4880
+ const shortFile = path13.relative(config.root, file);
4213
4881
  const mods = moduleGraph.getModulesByFile(file);
4214
4882
  if (!mods || mods.size === 0) {
4215
4883
  return;
4216
4884
  }
4217
4885
  const updates = [];
4218
4886
  const timestamp = Date.now();
4887
+ const graph = moduleGraph;
4888
+ const invalidatedModules = /* @__PURE__ */ new Set();
4219
4889
  for (const mod of mods) {
4220
- moduleGraph.invalidateModule(mod);
4890
+ graph.invalidateModuleAndImporters(mod, timestamp, invalidatedModules);
4221
4891
  const ctx = {
4222
4892
  file,
4223
4893
  timestamp,
4224
4894
  modules: [mod],
4225
- read: () => fs9.readFileSync(file, "utf-8"),
4895
+ read: () => fs10.readFileSync(file, "utf-8"),
4226
4896
  server
4227
4897
  };
4228
4898
  let affectedModules = [mod];
@@ -4235,19 +4905,25 @@ async function handleFileChange(file, server) {
4235
4905
  }
4236
4906
  }
4237
4907
  for (const affected of affectedModules) {
4238
- const boundaries = moduleGraph.getHmrBoundaries(affected);
4908
+ graph.invalidateModuleAndImporters(affected, timestamp, invalidatedModules);
4909
+ const boundaries = graph.getHmrBoundaries(affected);
4239
4910
  if (boundaries.length === 0) {
4240
4911
  logger.info(pc7.green("page reload ") + pc7.dim(shortFile), { timestamp: true });
4241
4912
  ws.send({ type: "full-reload", path: relativePath });
4242
4913
  return;
4243
4914
  }
4244
- for (const { boundary } of boundaries) {
4245
- updates.push({
4915
+ for (const { boundary, acceptedVia } of boundaries) {
4916
+ const update = {
4246
4917
  type: boundary.type === "css" ? "css-update" : "js-update",
4247
4918
  path: boundary.url,
4248
- acceptedPath: affected.url,
4919
+ acceptedPath: acceptedVia.url,
4249
4920
  timestamp
4250
- });
4921
+ };
4922
+ if (!updates.some(
4923
+ (existing) => existing.type === update.type && existing.path === update.path && existing.acceptedPath === update.acceptedPath
4924
+ )) {
4925
+ updates.push(update);
4926
+ }
4251
4927
  }
4252
4928
  }
4253
4929
  }
@@ -4271,8 +4947,8 @@ __export(runnable_environment_exports, {
4271
4947
  NastiModuleRunner: () => NastiModuleRunner,
4272
4948
  createModuleRunner: () => createModuleRunner
4273
4949
  });
4274
- import path13 from "path";
4275
- import fs10 from "fs";
4950
+ import path14 from "path";
4951
+ import fs11 from "fs";
4276
4952
  import { builtinModules as builtinModules3, createRequire as createRequire4 } from "module";
4277
4953
  import { pathToFileURL as pathToFileURL4 } from "url";
4278
4954
  function createModuleRunner(environment) {
@@ -4306,7 +4982,7 @@ var init_runnable_environment = __esm({
4306
4982
  this.config.mode,
4307
4983
  ssrDefineOverrides(environment.consumer)
4308
4984
  );
4309
- this.require = createRequire4(path13.join(this.config.root, "package.json"));
4985
+ this.require = createRequire4(path14.join(this.config.root, "package.json"));
4310
4986
  const handlers = {
4311
4987
  fetchModule: async (id, importer) => this.fetchModule(id, importer),
4312
4988
  getBuiltins: () => [/^node:/, ...builtinModules3]
@@ -4330,9 +5006,9 @@ var init_runnable_environment = __esm({
4330
5006
  this.cache.clear();
4331
5007
  }
4332
5008
  resolveToId(rawUrl) {
4333
- if (path13.isAbsolute(rawUrl) && fs10.existsSync(rawUrl.split("?")[0])) return rawUrl;
5009
+ if (path14.isAbsolute(rawUrl) && fs11.existsSync(rawUrl.split("?")[0])) return rawUrl;
4334
5010
  const clean = rawUrl.replace(/^\//, "");
4335
- return path13.resolve(this.config.root, clean);
5011
+ return path14.resolve(this.config.root, clean);
4336
5012
  }
4337
5013
  /**
4338
5014
  * fetchModule(invoke 契约方法):环境管线产出 runner 可执行代码。
@@ -4341,14 +5017,14 @@ var init_runnable_environment = __esm({
4341
5017
  */
4342
5018
  async fetchModule(id, importer) {
4343
5019
  if (NODE_BUILTINS3.has(id)) return { externalize: id };
4344
- if (!id.startsWith(".") && !path13.isAbsolute(id) && !id.startsWith("\0")) {
5020
+ if (!id.startsWith(".") && !path14.isAbsolute(id) && !id.startsWith("\0")) {
4345
5021
  return { externalize: id };
4346
5022
  }
4347
5023
  const container = this.environment.pluginContainer;
4348
5024
  let resolvedId = id;
4349
5025
  if (id.startsWith(".") && importer) {
4350
5026
  const resolved = await container.resolveId(id, importer);
4351
- resolvedId = resolved ? typeof resolved === "string" ? resolved : resolved.id : path13.resolve(path13.dirname(importer.split("?")[0]), id);
5027
+ resolvedId = resolved ? typeof resolved === "string" ? resolved : resolved.id : path14.resolve(path14.dirname(importer.split("?")[0]), id);
4352
5028
  }
4353
5029
  resolvedId = this.completeExtension(resolvedId);
4354
5030
  const cleanId = resolvedId.split("?")[0];
@@ -4356,8 +5032,8 @@ var init_runnable_environment = __esm({
4356
5032
  const loaded = await container.load(resolvedId);
4357
5033
  if (loaded != null) {
4358
5034
  code = typeof loaded === "string" ? loaded : loaded.code;
4359
- } else if (fs10.existsSync(cleanId)) {
4360
- code = fs10.readFileSync(cleanId, "utf-8");
5035
+ } else if (fs11.existsSync(cleanId)) {
5036
+ code = fs11.readFileSync(cleanId, "utf-8");
4361
5037
  } else {
4362
5038
  throw new Error(`[nasti:ssr] cannot load module: ${resolvedId}`);
4363
5039
  }
@@ -4390,19 +5066,19 @@ var init_runnable_environment = __esm({
4390
5066
  completeExtension(id) {
4391
5067
  const clean = id.split("?")[0];
4392
5068
  const query = id.includes("?") ? id.slice(id.indexOf("?")) : "";
4393
- if (fs10.existsSync(clean) && fs10.statSync(clean).isFile()) return id;
5069
+ if (fs11.existsSync(clean) && fs11.statSync(clean).isFile()) return id;
4394
5070
  const jsMatch = clean.match(/^(.*)\.([mc]?)jsx?$/);
4395
5071
  if (jsMatch) {
4396
5072
  for (const tsExt of [`.${jsMatch[2]}ts`, `.${jsMatch[2]}tsx`]) {
4397
- if (fs10.existsSync(jsMatch[1] + tsExt)) return jsMatch[1] + tsExt + query;
5073
+ if (fs11.existsSync(jsMatch[1] + tsExt)) return jsMatch[1] + tsExt + query;
4398
5074
  }
4399
5075
  }
4400
5076
  for (const ext of this.config.resolve.extensions) {
4401
- if (fs10.existsSync(clean + ext)) return clean + ext + query;
5077
+ if (fs11.existsSync(clean + ext)) return clean + ext + query;
4402
5078
  }
4403
5079
  for (const ext of this.config.resolve.extensions) {
4404
- const indexPath = path13.join(clean, `index${ext}`);
4405
- if (fs10.existsSync(indexPath)) return indexPath;
5080
+ const indexPath = path14.join(clean, `index${ext}`);
5081
+ if (fs11.existsSync(indexPath)) return indexPath;
4406
5082
  }
4407
5083
  return id;
4408
5084
  }
@@ -4429,10 +5105,10 @@ var init_runnable_environment = __esm({
4429
5105
  return;
4430
5106
  }
4431
5107
  const ssrImport = async (dep) => {
4432
- if (NODE_BUILTINS3.has(dep) || !dep.startsWith(".") && !path13.isAbsolute(dep) && !dep.startsWith("\0")) {
5108
+ if (NODE_BUILTINS3.has(dep) || !dep.startsWith(".") && !path14.isAbsolute(dep) && !dep.startsWith("\0")) {
4433
5109
  return this.importExternal(dep);
4434
5110
  }
4435
- const depId = dep.startsWith(".") ? this.completeExtension(path13.resolve(path13.dirname(fetched.id.split("?")[0]), dep)) : this.completeExtension(dep);
5111
+ const depId = dep.startsWith(".") ? this.completeExtension(path14.resolve(path14.dirname(fetched.id.split("?")[0]), dep)) : this.completeExtension(dep);
4436
5112
  return this.instantiate(depId);
4437
5113
  };
4438
5114
  const ssrExportAll = (sourceModule) => {
@@ -4464,7 +5140,7 @@ var init_runnable_environment = __esm({
4464
5140
  }
4465
5141
  async importExternal(spec) {
4466
5142
  try {
4467
- return await (spec.startsWith("node:") || !path13.isAbsolute(spec) ? import(this.resolveExternalSpecifier(spec)) : import(pathToFileURL4(spec).href));
5143
+ return await (spec.startsWith("node:") || !path14.isAbsolute(spec) ? import(this.resolveExternalSpecifier(spec)) : import(pathToFileURL4(spec).href));
4468
5144
  } catch (err) {
4469
5145
  throw new Error(`[nasti:ssr] failed to import external "${spec}": ${err.message}`);
4470
5146
  }
@@ -4490,7 +5166,7 @@ var dev_engine_exports = {};
4490
5166
  __export(dev_engine_exports, {
4491
5167
  createBundledDevServer: () => createBundledDevServer
4492
5168
  });
4493
- import path14 from "path";
5169
+ import path15 from "path";
4494
5170
  import crypto3 from "crypto";
4495
5171
  import { WebSocketServer as WsServer2 } from "ws";
4496
5172
  import pc8 from "picocolors";
@@ -4526,7 +5202,7 @@ async function createBundledDevServer(opts) {
4526
5202
  createReactRefreshRuntimePlugin(entryPoints),
4527
5203
  createBundledOxcRefreshPlugin()
4528
5204
  ] : [],
4529
- ...stripCatchAllLoad(toRolldownPlugins(clientEnv.plugins)),
5205
+ ...stripCatchAllLoad(toRolldownPlugins(clientEnv.plugins, clientEnv)),
4530
5206
  ...useReactRefresh ? [
4531
5207
  refreshWrapperFn({
4532
5208
  cwd: config.root,
@@ -4575,7 +5251,7 @@ async function createBundledDevServer(opts) {
4575
5251
  }
4576
5252
  const url = `/${patchPath}`;
4577
5253
  logger.info(
4578
- pc8.green("hmr update ") + pc8.dim(changedFiles.map((f) => path14.relative(config.root, f)).join(", ")),
5254
+ pc8.green("hmr update ") + pc8.dim(changedFiles.map((f) => path15.relative(config.root, f)).join(", ")),
4579
5255
  { timestamp: true }
4580
5256
  );
4581
5257
  sendTo(clientId, { type: "hmr:update", path: url, url });
@@ -4711,7 +5387,7 @@ async function createBundledDevServer(opts) {
4711
5387
  return;
4712
5388
  }
4713
5389
  res.setHeader("ETag", hit.etag);
4714
- res.setHeader("Content-Type", MIME_TYPES[path14.extname(fileName)] ?? "application/octet-stream");
5390
+ res.setHeader("Content-Type", MIME_TYPES[path15.extname(fileName)] ?? "application/octet-stream");
4715
5391
  res.setHeader("Cache-Control", "no-cache");
4716
5392
  res.end(hit.content);
4717
5393
  return;
@@ -4747,7 +5423,7 @@ function stripCatchAllLoad(plugins) {
4747
5423
  );
4748
5424
  }
4749
5425
  function createReactRefreshRuntimePlugin(entryPoints) {
4750
- const entryIds = new Set(entryPoints.map((p) => path14.resolve(p)));
5426
+ const entryIds = new Set(entryPoints.map((p) => path15.resolve(p)));
4751
5427
  return {
4752
5428
  name: "nasti:bundled-react-refresh",
4753
5429
  resolveId(source) {
@@ -4765,7 +5441,7 @@ function createReactRefreshRuntimePlugin(entryPoints) {
4765
5441
  return null;
4766
5442
  },
4767
5443
  transform(code, id) {
4768
- if (!entryIds.has(path14.resolve(id.split("?")[0]))) return null;
5444
+ if (!entryIds.has(path15.resolve(id.split("?")[0]))) return null;
4769
5445
  return { code: `import ${JSON.stringify(PREAMBLE_SPEC)};
4770
5446
  ${code}`, map: null };
4771
5447
  }
@@ -4936,7 +5612,7 @@ __export(server_exports, {
4936
5612
  createServer: () => createServer
4937
5613
  });
4938
5614
  import http from "http";
4939
- import path15 from "path";
5615
+ import path16 from "path";
4940
5616
  import os from "os";
4941
5617
  import connect from "connect";
4942
5618
  import sirv from "sirv";
@@ -4946,7 +5622,9 @@ async function createServer(inlineConfig = {}) {
4946
5622
  const startTime = performance.now();
4947
5623
  const config = await resolveConfig(inlineConfig, "serve");
4948
5624
  const logger = config.logger;
4949
- const allPlugins = resolvePluginList(config, config.plugins);
5625
+ const allPlugins = resolvePluginList(config, config.plugins, {
5626
+ environmentName: "client"
5627
+ });
4950
5628
  const configWithPlugins = { ...config, plugins: allPlugins };
4951
5629
  const app = connect();
4952
5630
  const httpServer = http.createServer(app);
@@ -4963,7 +5641,10 @@ async function createServer(inlineConfig = {}) {
4963
5641
  for (const name of Object.keys(config.environments)) {
4964
5642
  if (name === "client") continue;
4965
5643
  const consumer = config.environments[name].consumer;
4966
- const envPlugins = resolvePluginList(config, config.plugins, { consumer });
5644
+ const envPlugins = resolvePluginList(config, config.plugins, {
5645
+ consumer,
5646
+ environmentName: name
5647
+ });
4967
5648
  environments[name] = new NastiEnvironment(name, config, {
4968
5649
  mode: "dev",
4969
5650
  plugins: envPlugins,
@@ -4998,14 +5679,14 @@ async function createServer(inlineConfig = {}) {
4998
5679
  app.use(bundledServer.middleware);
4999
5680
  }
5000
5681
  const ignoredSegments = /* @__PURE__ */ new Set(["node_modules", ".git", ".nasti"]);
5001
- const outDirAbs = path15.resolve(config.root, config.build.outDir);
5682
+ const outDirAbs = path16.resolve(config.root, config.build.outDir);
5002
5683
  const watcher = watch(config.root, {
5003
5684
  ignored: (filePath) => {
5004
5685
  if (filePath === config.root) return false;
5005
- if (filePath === outDirAbs || filePath.startsWith(outDirAbs + path15.sep)) return true;
5006
- const rel = path15.relative(config.root, filePath);
5007
- if (!rel || rel.startsWith("..") || path15.isAbsolute(rel)) return false;
5008
- for (const seg of rel.split(path15.sep)) {
5686
+ if (filePath === outDirAbs || filePath.startsWith(outDirAbs + path16.sep)) return true;
5687
+ const rel = path16.relative(config.root, filePath);
5688
+ if (!rel || rel.startsWith("..") || path16.isAbsolute(rel)) return false;
5689
+ for (const seg of rel.split(path16.sep)) {
5009
5690
  if (ignoredSegments.has(seg)) return true;
5010
5691
  }
5011
5692
  return false;
@@ -5106,7 +5787,7 @@ async function createServer(inlineConfig = {}) {
5106
5787
  const readyIn = Math.ceil(performance.now() - startTime);
5107
5788
  logger.info(
5108
5789
  `
5109
- ${pc9.cyan(pc9.bold("NASTI"))} ${pc9.cyan(`v${"2.3.1"}`)} ${pc9.dim("ready in")} ${pc9.bold(readyIn)} ${pc9.dim("ms")}
5790
+ ${pc9.cyan(pc9.bold("NASTI"))} ${pc9.cyan(`v${"2.4.0"}`)} ${pc9.dim("ready in")} ${pc9.bold(readyIn)} ${pc9.dim("ms")}
5110
5791
  `
5111
5792
  );
5112
5793
  printServerUrls(
@@ -5134,7 +5815,12 @@ async function createServer(inlineConfig = {}) {
5134
5815
  },
5135
5816
  async transformRequest(url) {
5136
5817
  const { transformRequest: transformRequest2 } = await Promise.resolve().then(() => (init_middleware(), middleware_exports));
5137
- return transformRequest2(url, { config: configWithPlugins, pluginContainer, moduleGraph });
5818
+ return transformRequest2(url, {
5819
+ config: configWithPlugins,
5820
+ pluginContainer,
5821
+ moduleGraph,
5822
+ onPrune: (paths) => ws.send({ type: "prune", paths })
5823
+ });
5138
5824
  },
5139
5825
  async ssrLoadModule(url) {
5140
5826
  const runner = await getSsrRunner();
@@ -5194,9 +5880,10 @@ async function createServer(inlineConfig = {}) {
5194
5880
  app.use(transformMiddleware({
5195
5881
  config: configWithPlugins,
5196
5882
  pluginContainer,
5197
- moduleGraph
5883
+ moduleGraph,
5884
+ onPrune: (paths) => ws.send({ type: "prune", paths })
5198
5885
  }));
5199
- const publicDir = path15.resolve(config.root, "public");
5886
+ const publicDir = path16.resolve(config.root, "public");
5200
5887
  app.use(sirv(publicDir, { dev: true, etag: true }));
5201
5888
  app.use(sirv(config.root, { dev: true, etag: true }));
5202
5889
  const postMiddlewares = [];
@@ -5244,8 +5931,8 @@ init_build();
5244
5931
  // src/build/electron.ts
5245
5932
  init_config();
5246
5933
  init_resolve();
5247
- import path10 from "path";
5248
- import fs7 from "fs";
5934
+ import path11 from "path";
5935
+ import fs8 from "fs";
5249
5936
  import { rolldown as rolldown2 } from "rolldown";
5250
5937
  import pc5 from "picocolors";
5251
5938
 
@@ -5289,16 +5976,16 @@ async function buildElectron(inlineConfig = {}) {
5289
5976
  const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
5290
5977
  const startTime = performance.now();
5291
5978
  assertElectronVersion(config);
5292
- console.log(pc5.cyan("\n\u26A1 nasti build (electron)") + pc5.dim(` v${"2.3.1"}`));
5979
+ console.log(pc5.cyan("\n\u26A1 nasti build (electron)") + pc5.dim(` v${"2.4.0"}`));
5293
5980
  console.log(pc5.dim(` root: ${config.root}`));
5294
5981
  console.log(pc5.dim(` mode: ${config.mode}`));
5295
5982
  console.log(pc5.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
5296
- const outDir = path10.resolve(config.root, config.build.outDir);
5297
- if (config.build.emptyOutDir && fs7.existsSync(outDir)) {
5298
- fs7.rmSync(outDir, { recursive: true, force: true });
5983
+ const outDir = path11.resolve(config.root, config.build.outDir);
5984
+ if (config.build.emptyOutDir && fs8.existsSync(outDir)) {
5985
+ fs8.rmSync(outDir, { recursive: true, force: true });
5299
5986
  }
5300
- fs7.mkdirSync(outDir, { recursive: true });
5301
- const rendererOutDir = path10.join(outDir, "renderer");
5987
+ fs8.mkdirSync(outDir, { recursive: true });
5988
+ const rendererOutDir = path11.join(outDir, "renderer");
5302
5989
  const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
5303
5990
  await build2(createElectronRendererConfig(config, inlineConfig, {
5304
5991
  build: {
@@ -5307,8 +5994,8 @@ async function buildElectron(inlineConfig = {}) {
5307
5994
  emptyOutDir: false
5308
5995
  }
5309
5996
  }));
5310
- const mainEntry = path10.resolve(config.root, config.electron.main);
5311
- if (!fs7.existsSync(mainEntry)) {
5997
+ const mainEntry = path11.resolve(config.root, config.electron.main);
5998
+ if (!fs8.existsSync(mainEntry)) {
5312
5999
  throw new Error(
5313
6000
  `Electron main entry not found: ${config.electron.main}
5314
6001
  \u5728 nasti.config.ts \u7684 electron.main \u6307\u5B9A\u4E3B\u8FDB\u7A0B\u5165\u53E3\u6587\u4EF6\u3002`
@@ -5322,11 +6009,11 @@ async function buildElectron(inlineConfig = {}) {
5322
6009
  const preloadEntries = normalizePreload(config.electron.preload, config.root);
5323
6010
  const preloadFiles = [];
5324
6011
  for (const entry of preloadEntries) {
5325
- if (!fs7.existsSync(entry)) {
6012
+ if (!fs8.existsSync(entry)) {
5326
6013
  console.warn(pc5.yellow(` \u26A0 preload entry not found, skipped: ${entry}`));
5327
6014
  continue;
5328
6015
  }
5329
- const base = path10.basename(entry).replace(/\.[^.]+$/, "");
6016
+ const base = path11.basename(entry).replace(/\.[^.]+$/, "");
5330
6017
  const out = outFileName(outDir, base, config.electron.preloadFormat);
5331
6018
  await bundleNode(config, entry, {
5332
6019
  outFile: out,
@@ -5338,10 +6025,10 @@ async function buildElectron(inlineConfig = {}) {
5338
6025
  const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
5339
6026
  console.log(pc5.green(`
5340
6027
  \u2713 Electron build complete in ${elapsed}s`));
5341
- console.log(pc5.dim(` renderer: ${path10.relative(config.root, rendererOutDir)}/`));
5342
- console.log(pc5.dim(` main: ${path10.relative(config.root, mainFile)}`));
6028
+ console.log(pc5.dim(` renderer: ${path11.relative(config.root, rendererOutDir)}/`));
6029
+ console.log(pc5.dim(` main: ${path11.relative(config.root, mainFile)}`));
5343
6030
  for (const pf of preloadFiles) {
5344
- console.log(pc5.dim(` preload: ${path10.relative(config.root, pf)}`));
6031
+ console.log(pc5.dim(` preload: ${path11.relative(config.root, pf)}`));
5345
6032
  }
5346
6033
  console.log();
5347
6034
  return { rendererOutDir, mainFile, preloadFiles };
@@ -5379,7 +6066,7 @@ async function bundleNode(config, entry, opts) {
5379
6066
  },
5380
6067
  plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
5381
6068
  });
5382
- fs7.mkdirSync(path10.dirname(opts.outFile), { recursive: true });
6069
+ fs8.mkdirSync(path11.dirname(opts.outFile), { recursive: true });
5383
6070
  await bundle2.write({
5384
6071
  sourcemap: !!config.build.sourcemap,
5385
6072
  minify: !!config.build.minify,
@@ -5390,7 +6077,7 @@ async function bundleNode(config, entry, opts) {
5390
6077
  codeSplitting: false
5391
6078
  });
5392
6079
  await bundle2.close();
5393
- console.log(pc5.dim(` \u2713 ${opts.label} \u2192 ${path10.relative(config.root, opts.outFile)}`));
6080
+ console.log(pc5.dim(` \u2713 ${opts.label} \u2192 ${path11.relative(config.root, opts.outFile)}`));
5394
6081
  return opts.outFile;
5395
6082
  }
5396
6083
  function createElectronRendererConfig(config, inlineConfig = {}, overrides = {}) {
@@ -5414,11 +6101,11 @@ function createElectronRendererConfig(config, inlineConfig = {}, overrides = {})
5414
6101
  }
5415
6102
  function outFileName(outDir, base, format) {
5416
6103
  const ext = format === "cjs" ? ".cjs" : ".mjs";
5417
- return path10.join(outDir, base + ext);
6104
+ return path11.join(outDir, base + ext);
5418
6105
  }
5419
6106
  function normalizePreload(preload, root) {
5420
6107
  const list = Array.isArray(preload) ? preload : preload ? [preload] : [];
5421
- return list.map((p) => path10.resolve(root, p));
6108
+ return list.map((p) => path11.resolve(root, p));
5422
6109
  }
5423
6110
  function assertElectronVersion(config) {
5424
6111
  const min = config.electron.minVersion;
@@ -5433,9 +6120,9 @@ function assertElectronVersion(config) {
5433
6120
  }
5434
6121
  function detectInstalledElectron(root) {
5435
6122
  try {
5436
- const pkgPath = path10.resolve(root, "node_modules/electron/package.json");
5437
- if (!fs7.existsSync(pkgPath)) return null;
5438
- const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
6123
+ const pkgPath = path11.resolve(root, "node_modules/electron/package.json");
6124
+ if (!fs8.existsSync(pkgPath)) return null;
6125
+ const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
5439
6126
  const major = parseInt(String(pkg.version).split(".")[0], 10);
5440
6127
  return Number.isFinite(major) ? major : null;
5441
6128
  } catch {
@@ -5448,8 +6135,8 @@ init_server();
5448
6135
 
5449
6136
  // src/server/electron-dev.ts
5450
6137
  init_config();
5451
- import path16 from "path";
5452
- import fs11 from "fs";
6138
+ import path17 from "path";
6139
+ import fs12 from "fs";
5453
6140
  import { createRequire as createRequire5 } from "module";
5454
6141
  import { spawn } from "child_process";
5455
6142
  import chokidar from "chokidar";
@@ -5462,7 +6149,7 @@ async function startElectronDev(inlineConfig = {}) {
5462
6149
  const { noSpawn, ...rest } = inlineConfig;
5463
6150
  const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
5464
6151
  warnElectronVersion(config);
5465
- console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.3.1"}`));
6152
+ console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.4.0"}`));
5466
6153
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
5467
6154
  const server = await createServer2({
5468
6155
  ...rest,
@@ -5472,11 +6159,11 @@ async function startElectronDev(inlineConfig = {}) {
5472
6159
  await server.listen();
5473
6160
  const devUrl = `http://localhost:${server.config.server.port}` + electronRendererDevPath(config.electron.renderer);
5474
6161
  console.log(pc10.dim(` renderer: ${devUrl}`));
5475
- const stageDir = path16.resolve(config.root, ".nasti");
5476
- fs11.mkdirSync(stageDir, { recursive: true });
5477
- const mainEntry = path16.resolve(config.root, config.electron.main);
6162
+ const stageDir = path17.resolve(config.root, ".nasti");
6163
+ fs12.mkdirSync(stageDir, { recursive: true });
6164
+ const mainEntry = path17.resolve(config.root, config.electron.main);
5478
6165
  const preloadEntries = normalizePreload(config.electron.preload, config.root);
5479
- const builtMainFile = path16.join(stageDir, "main" + extFor(config.electron.mainFormat));
6166
+ const builtMainFile = path17.join(stageDir, "main" + extFor(config.electron.mainFormat));
5480
6167
  const builtPreloadFiles = [];
5481
6168
  const compileAll = async () => {
5482
6169
  await compileNode(config, mainEntry, {
@@ -5486,9 +6173,9 @@ async function startElectronDev(inlineConfig = {}) {
5486
6173
  });
5487
6174
  builtPreloadFiles.length = 0;
5488
6175
  for (const entry of preloadEntries) {
5489
- if (!fs11.existsSync(entry)) continue;
5490
- const base = path16.basename(entry).replace(/\.[^.]+$/, "");
5491
- const out = path16.join(stageDir, base + extFor(config.electron.preloadFormat));
6176
+ if (!fs12.existsSync(entry)) continue;
6177
+ const base = path17.basename(entry).replace(/\.[^.]+$/, "");
6178
+ const out = path17.join(stageDir, base + extFor(config.electron.preloadFormat));
5492
6179
  await compileNode(config, entry, {
5493
6180
  outFile: out,
5494
6181
  format: config.electron.preloadFormat,
@@ -5527,7 +6214,7 @@ async function startElectronDev(inlineConfig = {}) {
5527
6214
  };
5528
6215
  spawnElectron();
5529
6216
  if (config.electron.autoRestart) {
5530
- const watchTargets = [mainEntry, ...preloadEntries].filter(fs11.existsSync);
6217
+ const watchTargets = [mainEntry, ...preloadEntries].filter(fs12.existsSync);
5531
6218
  const watcher = chokidar.watch(watchTargets, { ignoreInitial: true });
5532
6219
  let restarting = null;
5533
6220
  let pending = false;
@@ -5607,7 +6294,7 @@ async function compileNode(config, entry, opts) {
5607
6294
  platform: "node",
5608
6295
  plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
5609
6296
  });
5610
- fs11.mkdirSync(path16.dirname(opts.outFile), { recursive: true });
6297
+ fs12.mkdirSync(path17.dirname(opts.outFile), { recursive: true });
5611
6298
  await bundle2.write({
5612
6299
  file: opts.outFile,
5613
6300
  format: opts.format === "cjs" ? "cjs" : "esm",
@@ -5620,18 +6307,18 @@ async function compileNode(config, entry, opts) {
5620
6307
  await bundle2.close();
5621
6308
  }
5622
6309
  function electronRendererDevPath(renderer) {
5623
- const normalized = renderer.split(path16.sep).join("/").replace(/^\.?\//, "");
6310
+ const normalized = renderer.split(path17.sep).join("/").replace(/^\.?\//, "");
5624
6311
  return normalized === "index.html" ? "/" : `/${normalized}`;
5625
6312
  }
5626
6313
  function resolveElectronBinary(config) {
5627
- if (config.electron.electronPath && fs11.existsSync(config.electron.electronPath)) {
6314
+ if (config.electron.electronPath && fs12.existsSync(config.electron.electronPath)) {
5628
6315
  return config.electron.electronPath;
5629
6316
  }
5630
6317
  try {
5631
- const require2 = createRequire5(path16.resolve(config.root, "package.json"));
6318
+ const require2 = createRequire5(path17.resolve(config.root, "package.json"));
5632
6319
  const pathFile = require2.resolve("electron");
5633
6320
  const electronModule = require2(pathFile);
5634
- if (typeof electronModule === "string" && fs11.existsSync(electronModule)) {
6321
+ if (typeof electronModule === "string" && fs12.existsSync(electronModule)) {
5635
6322
  return electronModule;
5636
6323
  }
5637
6324
  } catch {
@@ -5658,8 +6345,8 @@ function warnElectronVersion(config) {
5658
6345
  }
5659
6346
 
5660
6347
  // src/plugins/monaco-editor.ts
5661
- import path17 from "path";
5662
- import fs12 from "fs";
6348
+ import path18 from "path";
6349
+ import fs13 from "fs";
5663
6350
  import crypto4 from "crypto";
5664
6351
  import { createRequire as createRequire6 } from "module";
5665
6352
  var DEFAULT_WORKERS = {
@@ -5680,9 +6367,9 @@ function normalizePublicPath(p) {
5680
6367
  }
5681
6368
  function readMonacoVersion(root) {
5682
6369
  try {
5683
- const require2 = createRequire6(path17.resolve(root, "package.json"));
6370
+ const require2 = createRequire6(path18.resolve(root, "package.json"));
5684
6371
  const pkgJsonPath = require2.resolve("monaco-editor/package.json", { paths: [root] });
5685
- const pkg = JSON.parse(fs12.readFileSync(pkgJsonPath, "utf-8"));
6372
+ const pkg = JSON.parse(fs13.readFileSync(pkgJsonPath, "utf-8"));
5686
6373
  return typeof pkg.version === "string" ? pkg.version : "unknown";
5687
6374
  } catch {
5688
6375
  return "unknown";
@@ -5702,20 +6389,20 @@ function monacoEditorPlugin(options = {}) {
5702
6389
  let cacheDir = "";
5703
6390
  const building = /* @__PURE__ */ new Map();
5704
6391
  async function buildWorker(worker) {
5705
- const cacheFile = path17.join(cacheDir, `${worker.label}.worker.js`);
5706
- if (fs12.existsSync(cacheFile)) return cacheFile;
6392
+ const cacheFile = path18.join(cacheDir, `${worker.label}.worker.js`);
6393
+ if (fs13.existsSync(cacheFile)) return cacheFile;
5707
6394
  const existing = building.get(worker.label);
5708
6395
  if (existing) return existing;
5709
6396
  const task = (async () => {
5710
6397
  const { rolldown: rolldown4 } = await import("rolldown");
5711
- const require2 = createRequire6(path17.resolve(resolvedConfig.root, "package.json"));
6398
+ const require2 = createRequire6(path18.resolve(resolvedConfig.root, "package.json"));
5712
6399
  let entry;
5713
6400
  try {
5714
6401
  entry = require2.resolve(worker.entry, { paths: [resolvedConfig.root] });
5715
6402
  } catch {
5716
6403
  entry = require2.resolve(worker.entry + ".js", { paths: [resolvedConfig.root] });
5717
6404
  }
5718
- fs12.mkdirSync(cacheDir, { recursive: true });
6405
+ fs13.mkdirSync(cacheDir, { recursive: true });
5719
6406
  const bundle2 = await rolldown4({
5720
6407
  input: entry,
5721
6408
  platform: "browser"
@@ -5775,12 +6462,12 @@ function monacoEditorPlugin(options = {}) {
5775
6462
  resolvedConfig = config;
5776
6463
  const version = readMonacoVersion(config.root);
5777
6464
  const key = crypto4.createHash("sha1").update(version + "|" + publicPath).digest("hex").slice(0, 8);
5778
- cacheDir = path17.resolve(config.root, "node_modules/.nasti/monaco", key);
6465
+ cacheDir = path18.resolve(config.root, "node_modules/.nasti/monaco", key);
5779
6466
  },
5780
6467
  async configureServer(server) {
5781
6468
  const shouldBuild = !isCDN(publicPath) || forceBuildCDN;
5782
6469
  const watcher = server.watcher;
5783
- const monacoDir = path17.resolve(resolvedConfig.root, "node_modules/monaco-editor");
6470
+ const monacoDir = path18.resolve(resolvedConfig.root, "node_modules/monaco-editor");
5784
6471
  try {
5785
6472
  watcher?.unwatch?.(monacoDir);
5786
6473
  } catch {
@@ -5810,7 +6497,7 @@ function monacoEditorPlugin(options = {}) {
5810
6497
  const file = await buildWorker(worker);
5811
6498
  res.setHeader("Content-Type", "application/javascript; charset=utf-8");
5812
6499
  res.setHeader("Cache-Control", "public, max-age=604800, immutable");
5813
- fs12.createReadStream(file).pipe(res);
6500
+ fs13.createReadStream(file).pipe(res);
5814
6501
  } catch (e) {
5815
6502
  res.statusCode = 500;
5816
6503
  res.end(`Monaco worker build failed: ${e.message}`);
@@ -5845,16 +6532,16 @@ self.monaco = monaco;`,
5845
6532
  resolvedConfig.root,
5846
6533
  resolvedConfig.build.outDir,
5847
6534
  resolvedConfig.base
5848
- ) : isCDN(publicPath) ? path17.resolve(resolvedConfig.root, resolvedConfig.build.outDir, "monaco") : path17.resolve(
6535
+ ) : isCDN(publicPath) ? path18.resolve(resolvedConfig.root, resolvedConfig.build.outDir, "monaco") : path18.resolve(
5849
6536
  resolvedConfig.root,
5850
6537
  resolvedConfig.build.outDir,
5851
6538
  publicPath.replace(/^\//, "")
5852
6539
  );
5853
- fs12.mkdirSync(outDir, { recursive: true });
6540
+ fs13.mkdirSync(outDir, { recursive: true });
5854
6541
  for (const worker of workers) {
5855
6542
  try {
5856
6543
  const cacheFile = await buildWorker(worker);
5857
- fs12.copyFileSync(cacheFile, path17.join(outDir, `${worker.label}.worker.js`));
6544
+ fs13.copyFileSync(cacheFile, path18.join(outDir, `${worker.label}.worker.js`));
5858
6545
  } catch (e) {
5859
6546
  throw new Error(
5860
6547
  `[nasti:monaco-editor] worker build failed for "${worker.label}": ${e.message}