@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/cli.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) => (path17) => {
14
- var fn = map[path17];
13
+ var __glob = (map) => (path18) => {
14
+ var fn = map[path18];
15
15
  if (fn) return fn();
16
- throw new Error("Module not found in bundle: " + path17);
16
+ throw new Error("Module not found in bundle: " + path18);
17
17
  };
18
18
  var __esm = (fn, res) => function __init() {
19
19
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
@@ -484,6 +484,7 @@ async function resolveConfig(inlineConfig = {}, command) {
484
484
  if (envOptions.build) Object.assign(resolved.build, envOptions.build);
485
485
  resolved.environments.client = {
486
486
  consumer,
487
+ buildEnabled: envOptions.buildEnabled ?? true,
487
488
  entry: normalizeEnvironmentEntries(envOptions.entry, root),
488
489
  html: path.resolve(
489
490
  root,
@@ -498,6 +499,7 @@ async function resolveConfig(inlineConfig = {}, command) {
498
499
  }
499
500
  resolved.environments[name] = {
500
501
  consumer,
502
+ buildEnabled: envOptions.buildEnabled ?? true,
501
503
  entry: normalizeEnvironmentEntries(envOptions.entry, root),
502
504
  html: envOptions.consumer === "client" && envOptions.html ? path.resolve(root, envOptions.html) : void 0,
503
505
  driver: envOptions.driver,
@@ -787,17 +789,35 @@ var init_plugin_container = __esm({
787
789
  }
788
790
  });
789
791
 
792
+ // src/core/url.ts
793
+ function removeTimestampQuery(url) {
794
+ const hashIndex = url.indexOf("#");
795
+ const hash = hashIndex >= 0 ? url.slice(hashIndex) : "";
796
+ const withoutHash = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
797
+ const queryIndex = withoutHash.indexOf("?");
798
+ if (queryIndex < 0) return url;
799
+ const pathname = withoutHash.slice(0, queryIndex);
800
+ const query = withoutHash.slice(queryIndex + 1).split("&").filter((part) => !/^t=\d+$/.test(part)).join("&");
801
+ return pathname + (query ? `?${query}` : "") + hash;
802
+ }
803
+ var init_url = __esm({
804
+ "src/core/url.ts"() {
805
+ "use strict";
806
+ }
807
+ });
808
+
790
809
  // src/core/module-graph.ts
791
810
  var ModuleGraph;
792
811
  var init_module_graph = __esm({
793
812
  "src/core/module-graph.ts"() {
794
813
  "use strict";
814
+ init_url();
795
815
  ModuleGraph = class {
796
816
  urlToModuleMap = /* @__PURE__ */ new Map();
797
817
  idToModuleMap = /* @__PURE__ */ new Map();
798
818
  fileToModulesMap = /* @__PURE__ */ new Map();
799
819
  getModuleByUrl(url) {
800
- return this.urlToModuleMap.get(url);
820
+ return this.urlToModuleMap.get(removeTimestampQuery(url));
801
821
  }
802
822
  getModuleById(id) {
803
823
  return this.idToModuleMap.get(id);
@@ -806,10 +826,11 @@ var init_module_graph = __esm({
806
826
  return this.fileToModulesMap.get(file);
807
827
  }
808
828
  async ensureEntryFromUrl(url) {
809
- let mod = this.urlToModuleMap.get(url);
829
+ const normalizedUrl = removeTimestampQuery(url);
830
+ let mod = this.urlToModuleMap.get(normalizedUrl);
810
831
  if (mod) return mod;
811
- mod = this.createModule(url);
812
- this.urlToModuleMap.set(url, mod);
832
+ mod = this.createModule(normalizedUrl);
833
+ this.urlToModuleMap.set(normalizedUrl, mod);
813
834
  return mod;
814
835
  }
815
836
  createModule(url, id) {
@@ -823,6 +844,7 @@ var init_module_graph = __esm({
823
844
  acceptedHmrDeps: /* @__PURE__ */ new Set(),
824
845
  transformResult: null,
825
846
  lastHMRTimestamp: 0,
847
+ invalidationVersion: 0,
826
848
  isSelfAccepting: false
827
849
  };
828
850
  this.idToModuleMap.set(mod.id, mod);
@@ -865,10 +887,64 @@ var init_module_graph = __esm({
865
887
  }
866
888
  }
867
889
  }
890
+ /**
891
+ * 用一次转换得到的信息原子更新 import 与 HMR accept 关系。
892
+ * 依赖节点会在真正被浏览器请求前预先创建,这样入口模块先转换时也能建立完整图。
893
+ */
894
+ async updateModuleInfo(mod, importedUrls, acceptedUrls, isSelfAccepting, expectedInvalidationVersion) {
895
+ const importedModules = await Promise.all(
896
+ [...importedUrls].map((url) => this.ensureEntryFromUrl(url))
897
+ );
898
+ const acceptedModules = await Promise.all(
899
+ [...acceptedUrls].map((url) => this.ensureEntryFromUrl(url))
900
+ );
901
+ if (expectedInvalidationVersion !== void 0 && mod.invalidationVersion !== expectedInvalidationVersion) {
902
+ return null;
903
+ }
904
+ const previousImports = new Set(mod.importedModules);
905
+ for (const imported of previousImports) {
906
+ imported.importers.delete(mod);
907
+ }
908
+ mod.importedModules.clear();
909
+ mod.acceptedHmrDeps.clear();
910
+ for (const imported of importedModules) {
911
+ mod.importedModules.add(imported);
912
+ imported.importers.add(mod);
913
+ }
914
+ for (const accepted of acceptedModules) {
915
+ mod.acceptedHmrDeps.add(accepted);
916
+ }
917
+ mod.isSelfAccepting = isSelfAccepting;
918
+ const pruned = /* @__PURE__ */ new Set();
919
+ for (const imported of previousImports) {
920
+ if (!mod.importedModules.has(imported) && imported.importers.size === 0) {
921
+ pruned.add(imported);
922
+ }
923
+ }
924
+ return pruned;
925
+ }
868
926
  /** 使模块的转换缓存失效 */
869
- invalidateModule(mod) {
927
+ invalidateModule(mod, timestamp = Date.now()) {
870
928
  mod.transformResult = null;
871
- mod.lastHMRTimestamp = Date.now();
929
+ mod.lastHMRTimestamp = timestamp;
930
+ mod.invalidationVersion++;
931
+ }
932
+ /**
933
+ * 仅失效到 HMR 边界:显式接受依赖的模块本身不会重执行;自接受模块需要失效,
934
+ * 但不再继续影响其 importer。这样既能传播依赖时间戳,也不会隐式重复副作用。
935
+ */
936
+ invalidateModuleAndImporters(mod, timestamp = Date.now(), seen = /* @__PURE__ */ new Set()) {
937
+ if (seen.has(mod)) return;
938
+ seen.add(mod);
939
+ this.invalidateModule(mod, timestamp);
940
+ for (const importer of mod.importers) {
941
+ if (importer.acceptedHmrDeps.has(mod)) continue;
942
+ if (importer.isSelfAccepting) {
943
+ this.invalidateModule(importer, timestamp);
944
+ continue;
945
+ }
946
+ this.invalidateModuleAndImporters(importer, timestamp, seen);
947
+ }
872
948
  }
873
949
  /** 使所有模块缓存失效 */
874
950
  invalidateAll() {
@@ -879,34 +955,32 @@ var init_module_graph = __esm({
879
955
  /** 获取 HMR 传播边界 - 从变更模块向上遍历找到接受更新的边界 */
880
956
  getHmrBoundaries(mod) {
881
957
  const boundaries = [];
882
- const visited = /* @__PURE__ */ new Set();
883
- const propagate = (node, via) => {
884
- if (visited.has(node)) return true;
885
- visited.add(node);
886
- if (node.isSelfAccepting) {
887
- boundaries.push({ boundary: node, acceptedVia: via });
888
- return true;
958
+ const traversed = /* @__PURE__ */ new Set();
959
+ const addBoundary = (boundary, acceptedVia) => {
960
+ if (!boundaries.some(
961
+ (item) => item.boundary === boundary && item.acceptedVia === acceptedVia
962
+ )) {
963
+ boundaries.push({ boundary, acceptedVia });
889
964
  }
890
- if (node.acceptedHmrDeps.has(via)) {
891
- boundaries.push({ boundary: node, acceptedVia: via });
965
+ };
966
+ const propagate = (node) => {
967
+ if (traversed.has(node)) return true;
968
+ traversed.add(node);
969
+ if (node.isSelfAccepting) {
970
+ addBoundary(node, node);
892
971
  return true;
893
972
  }
894
973
  if (node.importers.size === 0) return false;
895
974
  for (const importer of node.importers) {
896
- if (!propagate(importer, node)) return false;
975
+ if (importer.acceptedHmrDeps.has(node)) {
976
+ addBoundary(importer, node);
977
+ continue;
978
+ }
979
+ if (!propagate(importer)) return false;
897
980
  }
898
981
  return true;
899
982
  };
900
- if (mod.isSelfAccepting) {
901
- boundaries.push({ boundary: mod, acceptedVia: mod });
902
- return boundaries;
903
- }
904
- for (const importer of mod.importers) {
905
- if (!propagate(importer, mod)) {
906
- return [];
907
- }
908
- }
909
- return boundaries;
983
+ return propagate(mod) ? boundaries : [];
910
984
  }
911
985
  };
912
986
  }
@@ -1045,6 +1119,7 @@ var init_environment = __esm({
1045
1119
  moduleGraph;
1046
1120
  candidatePlugins;
1047
1121
  pluginApi;
1122
+ buildMetadata = {};
1048
1123
  initialized = false;
1049
1124
  constructor(name, config, init = {}) {
1050
1125
  const options = config.environments[name];
@@ -1101,6 +1176,22 @@ var init_environment = __esm({
1101
1176
  logger: this.config.logger
1102
1177
  };
1103
1178
  }
1179
+ setBuildMetadata(metadata) {
1180
+ const { entries: currentEntries, ...currentMetadata } = this.buildMetadata;
1181
+ const { entries, ...nextMetadata } = metadata;
1182
+ this.buildMetadata = {
1183
+ ...currentMetadata,
1184
+ ...nextMetadata,
1185
+ ...currentEntries || entries ? { entries: { ...currentEntries, ...entries } } : {}
1186
+ };
1187
+ }
1188
+ getBuildMetadata() {
1189
+ const { entries, ...metadata } = this.buildMetadata;
1190
+ return {
1191
+ ...metadata,
1192
+ ...entries ? { entries: { ...entries } } : {}
1193
+ };
1194
+ }
1104
1195
  async close() {
1105
1196
  try {
1106
1197
  await this.driver?.close?.(this.getDriverContext());
@@ -1354,8 +1445,10 @@ import fs4 from "fs";
1354
1445
  import { createRequire } from "module";
1355
1446
  import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "url";
1356
1447
  import pc3 from "picocolors";
1357
- function getReactRefreshRuntimeEsm() {
1358
- if (__refreshRuntimeCache) return __refreshRuntimeCache;
1448
+ function getReactRefreshRuntimeEsm(includeBoundaryHelpers = false) {
1449
+ if (__refreshRuntimeCache) {
1450
+ return includeBoundaryHelpers ? __refreshRuntimeCache + REACT_REFRESH_BOUNDARY_HELPERS : __refreshRuntimeCache;
1451
+ }
1359
1452
  let cjsPath;
1360
1453
  try {
1361
1454
  const pkgPath = __require2.resolve("react-refresh/package.json");
@@ -1390,7 +1483,7 @@ export const findAffectedHostInstances = __rt.findAffectedHostInstances;
1390
1483
  export const collectCustomHooksForSignature = __rt.collectCustomHooksForSignature;
1391
1484
  export default __rt;
1392
1485
  `;
1393
- return __refreshRuntimeCache;
1486
+ return includeBoundaryHelpers ? __refreshRuntimeCache + REACT_REFRESH_BOUNDARY_HELPERS : __refreshRuntimeCache;
1394
1487
  }
1395
1488
  function buildReactRefreshWrapper(moduleUrl, transformedCode) {
1396
1489
  const urlLit = JSON.stringify(moduleUrl);
@@ -1416,22 +1509,40 @@ window.$RefreshReg$ = prevRefreshReg;
1416
1509
  window.$RefreshSig$ = prevRefreshSig;
1417
1510
 
1418
1511
  if (__nasti_hot__) {
1419
- __nasti_hot__.accept(() => {
1420
- clearTimeout(window.__nasti_refresh_timer__);
1421
- window.__nasti_refresh_timer__ = setTimeout(() => {
1422
- RefreshRuntime.performReactRefresh();
1423
- }, 30);
1512
+ let __nasti_current_exports__;
1513
+ __nasti_hot__.accept((nextExports) => {
1514
+ if (!nextExports) return;
1515
+ if (!__nasti_current_exports__) {
1516
+ __nasti_hot__.invalidate('Could not Fast Refresh (previous exports unavailable)');
1517
+ return;
1518
+ }
1519
+ const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(
1520
+ ${urlLit},
1521
+ __nasti_current_exports__,
1522
+ nextExports,
1523
+ );
1524
+ if (invalidateMessage) __nasti_hot__.invalidate(invalidateMessage);
1525
+ });
1526
+ RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
1527
+ __nasti_current_exports__ = currentExports;
1528
+ RefreshRuntime.registerExportsForReactRefresh(${urlLit}, currentExports);
1424
1529
  });
1425
1530
  }
1426
1531
  `;
1427
1532
  }
1428
1533
  function injectImportMetaHot(code, moduleUrl) {
1429
- if (!/\bimport\.meta\.hot\b/.test(code)) return code;
1534
+ const hotRE = /\bimport\.meta\.hot\b/g;
1535
+ const matches = [...maskStringsAndComments(code).matchAll(hotRE)];
1536
+ if (matches.length === 0) return code;
1537
+ for (const match of matches.reverse()) {
1538
+ const start = match.index;
1539
+ code = code.slice(0, start) + "__nasti_hot__" + code.slice(start + match[0].length);
1540
+ }
1430
1541
  const urlLit = JSON.stringify(moduleUrl);
1431
1542
  const header = `import { createHotContext as __nasti_createHotContext__ } from "/@nasti/client";
1432
1543
  const __nasti_hot__ = __nasti_createHotContext__(${urlLit});
1433
1544
  `;
1434
- return header + code.replace(/\bimport\.meta\.hot\b/g, "__nasti_hot__");
1545
+ return header + code;
1435
1546
  }
1436
1547
  function transformMiddleware(ctx) {
1437
1548
  ctx.envDefine = buildEnvDefine(
@@ -1507,13 +1618,14 @@ function transformMiddleware(ctx) {
1507
1618
  }
1508
1619
  async function transformRequest(url, ctx) {
1509
1620
  const { config, pluginContainer, moduleGraph } = ctx;
1621
+ url = removeTimestampQuery(url);
1510
1622
  const cleanReqUrl = url.split("?")[0];
1511
1623
  const cached2 = moduleGraph.getModuleByUrl(url);
1512
1624
  if (cached2?.transformResult) {
1513
1625
  return cached2.transformResult;
1514
1626
  }
1515
1627
  if (cleanReqUrl === "/@react-refresh") {
1516
- return { code: getReactRefreshRuntimeEsm() };
1628
+ return { code: getReactRefreshRuntimeEsm(true) };
1517
1629
  }
1518
1630
  if (cleanReqUrl.startsWith("/@modules/") && url.includes("?")) {
1519
1631
  const idParam = new URLSearchParams(url.slice(url.indexOf("?") + 1)).get("id");
@@ -1549,6 +1661,8 @@ async function transformRequest(url, ctx) {
1549
1661
  }
1550
1662
  const rawQuery = url.includes("?") ? url.slice(url.indexOf("?") + 1) : "";
1551
1663
  if (rawQuery && !/^t=\d+$/.test(rawQuery)) {
1664
+ const mod2 = await moduleGraph.ensureEntryFromUrl(url);
1665
+ const transformVersion2 = mod2.invalidationVersion;
1552
1666
  const loaded = await pluginContainer.load(url);
1553
1667
  if (loaded != null) {
1554
1668
  let code2 = typeof loaded === "string" ? loaded : loaded.code;
@@ -1556,16 +1670,28 @@ async function transformRequest(url, ctx) {
1556
1670
  if (transformed != null) {
1557
1671
  code2 = typeof transformed === "string" ? transformed : transformed.code;
1558
1672
  }
1559
- const mod2 = await moduleGraph.ensureEntryFromUrl(url);
1560
- moduleGraph.registerModule(mod2, cleanReqUrl);
1561
- code2 = injectImportMetaHot(code2, url);
1673
+ const parentFile = resolveUrlToFile(cleanReqUrl, config.root) ?? cleanReqUrl;
1674
+ moduleGraph.registerModule(mod2, parentFile);
1675
+ const hotInfo2 = rewriteHotAcceptDeps(code2, config, parentFile);
1676
+ code2 = injectImportMetaHot(hotInfo2.code, url);
1562
1677
  code2 = replaceEnvInCode(code2, ctx.envDefine ?? buildEnvDefine(
1563
1678
  loadEnv(config.mode, config.root, config.envPrefix),
1564
1679
  config.mode
1565
1680
  ));
1566
- code2 = rewriteImports(code2, config, cleanReqUrl);
1681
+ const importedUrls2 = /* @__PURE__ */ new Set();
1682
+ code2 = rewriteImports(code2, config, parentFile, importedUrls2, moduleGraph);
1683
+ const pruned2 = await moduleGraph.updateModuleInfo(
1684
+ mod2,
1685
+ importedUrls2,
1686
+ hotInfo2.acceptedUrls,
1687
+ hotInfo2.isSelfAccepting,
1688
+ transformVersion2
1689
+ );
1567
1690
  const transformResult2 = { code: code2 };
1568
- mod2.transformResult = transformResult2;
1691
+ if (pruned2) {
1692
+ if (pruned2.size > 0) ctx.onPrune?.([...pruned2].map((item) => item.url));
1693
+ mod2.transformResult = transformResult2;
1694
+ }
1569
1695
  return transformResult2;
1570
1696
  }
1571
1697
  }
@@ -1573,6 +1699,7 @@ async function transformRequest(url, ctx) {
1573
1699
  if (!filePath || !fs4.existsSync(filePath)) return null;
1574
1700
  const mod = await moduleGraph.ensureEntryFromUrl(url);
1575
1701
  moduleGraph.registerModule(mod, filePath);
1702
+ const transformVersion = mod.invalidationVersion;
1576
1703
  if (cleanReqUrl.startsWith("/@modules/")) {
1577
1704
  const code2 = await bundlePackageAsEsm(filePath, config.root);
1578
1705
  const transformResult2 = { code: code2 };
@@ -1599,9 +1726,10 @@ async function transformRequest(url, ctx) {
1599
1726
  if (useRefresh) {
1600
1727
  code = buildReactRefreshWrapper(stableUrl, code);
1601
1728
  wrappedWithRefresh = true;
1602
- mod.isSelfAccepting = true;
1603
1729
  }
1604
1730
  }
1731
+ const hotInfo = rewriteHotAcceptDeps(code, config, filePath);
1732
+ code = hotInfo.code;
1605
1733
  if (!wrappedWithRefresh) {
1606
1734
  code = injectImportMetaHot(code, stableUrl);
1607
1735
  }
@@ -1610,9 +1738,20 @@ async function transformRequest(url, ctx) {
1610
1738
  config.mode
1611
1739
  );
1612
1740
  code = replaceEnvInCode(code, envDefine);
1613
- code = rewriteImports(code, config, filePath);
1741
+ const importedUrls = /* @__PURE__ */ new Set();
1742
+ code = rewriteImports(code, config, filePath, importedUrls, moduleGraph);
1743
+ const pruned = await moduleGraph.updateModuleInfo(
1744
+ mod,
1745
+ importedUrls,
1746
+ hotInfo.acceptedUrls,
1747
+ wrappedWithRefresh || hotInfo.isSelfAccepting,
1748
+ transformVersion
1749
+ );
1614
1750
  const transformResult = { code };
1615
- mod.transformResult = transformResult;
1751
+ if (pruned) {
1752
+ if (pruned.size > 0) ctx.onPrune?.([...pruned].map((item) => item.url));
1753
+ mod.transformResult = transformResult;
1754
+ }
1616
1755
  return transformResult;
1617
1756
  }
1618
1757
  async function loadVirtualModule(spec, ctx) {
@@ -1817,49 +1956,202 @@ async function injectCjsNamedExports(code, entryFile) {
1817
1956
  return code;
1818
1957
  }
1819
1958
  }
1820
- function rewriteImports(code, config, filePath) {
1959
+ function rewriteImports(code, config, filePath, importedUrls, moduleGraph) {
1960
+ const resolveSpec = createModuleSpecifierResolver(config, filePath);
1961
+ const transformSpec = (spec) => {
1962
+ const resolved = removeTimestampQuery(resolveSpec(spec));
1963
+ importedUrls?.add(resolved);
1964
+ const timestamp = moduleGraph?.getModuleByUrl(resolved)?.lastHMRTimestamp ?? 0;
1965
+ return timestamp > 0 ? appendTimestampQuery(resolved, timestamp) : resolved;
1966
+ };
1967
+ return code.replace(
1968
+ /\bfrom\s+(['"])([^'"]+)\1/g,
1969
+ (_m, q, s) => `from ${q}${transformSpec(s)}${q}`
1970
+ ).replace(
1971
+ /\bimport\s+(['"])([^'"]+)\1/g,
1972
+ (_m, q, s) => `import ${q}${transformSpec(s)}${q}`
1973
+ ).replace(
1974
+ /\bimport\s*\(\s*(['"])([^'"]+)\1\s*\)/g,
1975
+ (_m, q, s) => `import(${q}${transformSpec(s)}${q})`
1976
+ );
1977
+ }
1978
+ function createModuleSpecifierResolver(config, filePath) {
1821
1979
  const root = config.root;
1822
1980
  const fileDir = path4.dirname(filePath);
1823
1981
  const aliasEntries = Object.entries(config.resolve.alias).sort(
1824
1982
  ([a], [b]) => b.length - a.length
1825
1983
  );
1826
1984
  const toRootUrl = (abs) => "/" + path4.relative(root, abs).replace(/\\/g, "/");
1827
- const transformSpec = (spec) => {
1828
- const suffixMatch = spec.match(/[?#].*$/);
1985
+ return (specifier) => {
1986
+ const suffixMatch = specifier.match(/[?#].*$/);
1829
1987
  const suffix = suffixMatch ? suffixMatch[0] : "";
1830
- const baseSpec = suffix ? spec.slice(0, -suffix.length) : spec;
1988
+ const baseSpec = suffix ? specifier.slice(0, -suffix.length) : specifier;
1831
1989
  for (const [key, value] of aliasEntries) {
1832
1990
  if (baseSpec === key || baseSpec.startsWith(key + "/")) {
1833
1991
  const aliasBase = resolveAliasTarget(value, root);
1834
1992
  const sub = baseSpec.slice(key.length).replace(/^\//, "");
1835
1993
  const target = sub ? path4.join(aliasBase, sub) : aliasBase;
1836
1994
  const resolved = tryResolveDiskPath(target);
1837
- return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : spec;
1995
+ return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : specifier;
1838
1996
  }
1839
1997
  }
1840
1998
  if (baseSpec.startsWith("./") || baseSpec.startsWith("../")) {
1841
- const target = path4.resolve(fileDir, baseSpec);
1842
- const resolved = tryResolveDiskPath(target);
1843
- return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : spec;
1999
+ const resolved = tryResolveDiskPath(path4.resolve(fileDir, baseSpec));
2000
+ return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : specifier;
1844
2001
  }
1845
2002
  if (baseSpec.startsWith("/") && !baseSpec.startsWith("/@")) {
1846
- const target = path4.join(root, baseSpec.replace(/^\//, ""));
1847
- const resolved = tryResolveDiskPath(target);
1848
- return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : spec;
2003
+ const resolved = tryResolveDiskPath(path4.join(root, baseSpec.replace(/^\//, "")));
2004
+ return resolved && isUnderRoot(resolved, root) ? toRootUrl(resolved) + suffix : specifier;
1849
2005
  }
1850
- if (baseSpec.startsWith("/")) return spec;
1851
- return `/@modules/${spec}`;
2006
+ if (baseSpec.startsWith("/")) return specifier;
2007
+ return `/@modules/${specifier}`;
1852
2008
  };
1853
- return code.replace(
1854
- /\bfrom\s+(['"])([^'"]+)\1/g,
1855
- (_m, q, s) => `from ${q}${transformSpec(s)}${q}`
1856
- ).replace(
1857
- /\bimport\s+(['"])([^'"]+)\1/g,
1858
- (_m, q, s) => `import ${q}${transformSpec(s)}${q}`
1859
- ).replace(
1860
- /\bimport\s*\(\s*(['"])([^'"]+)\1\s*\)/g,
1861
- (_m, q, s) => `import(${q}${transformSpec(s)}${q})`
1862
- );
2009
+ }
2010
+ function rewriteHotAcceptDeps(code, config, filePath) {
2011
+ const acceptedUrls = /* @__PURE__ */ new Set();
2012
+ const edits = [];
2013
+ const resolveSpec = createModuleSpecifierResolver(config, filePath);
2014
+ const acceptRE = /(?:\bimport\.meta\.hot|\b__nasti_hot__)(?:(?:\?\.)|\.)accept\s*\(/g;
2015
+ const searchableCode = maskStringsAndComments(code);
2016
+ let isSelfAccepting = false;
2017
+ let match;
2018
+ while (match = acceptRE.exec(searchableCode)) {
2019
+ let cursor = match.index + match[0].length;
2020
+ const skipTrivia = () => {
2021
+ while (cursor < code.length) {
2022
+ if (/\s/.test(code[cursor])) {
2023
+ cursor++;
2024
+ continue;
2025
+ }
2026
+ if (code[cursor] === "/" && code[cursor + 1] === "/") {
2027
+ cursor += 2;
2028
+ while (cursor < code.length && code[cursor] !== "\n") cursor++;
2029
+ continue;
2030
+ }
2031
+ if (code[cursor] === "/" && code[cursor + 1] === "*") {
2032
+ cursor += 2;
2033
+ while (cursor < code.length && !(code[cursor] === "*" && code[cursor + 1] === "/")) cursor++;
2034
+ cursor += 2;
2035
+ continue;
2036
+ }
2037
+ break;
2038
+ }
2039
+ };
2040
+ skipTrivia();
2041
+ const first = code[cursor];
2042
+ if (!first || first === ")" || first !== "[" && first !== "'" && first !== '"' && first !== "`") {
2043
+ isSelfAccepting = true;
2044
+ continue;
2045
+ }
2046
+ const readLiteral = () => {
2047
+ const quote = code[cursor];
2048
+ if (quote !== "'" && quote !== '"' && quote !== "`") return;
2049
+ const start = cursor;
2050
+ cursor++;
2051
+ let raw = "";
2052
+ while (cursor < code.length) {
2053
+ const char = code[cursor];
2054
+ if (char === "\\") {
2055
+ raw += code[cursor + 1] ?? "";
2056
+ cursor += 2;
2057
+ continue;
2058
+ }
2059
+ if (char === quote) {
2060
+ cursor++;
2061
+ const resolved = removeTimestampQuery(resolveSpec(raw));
2062
+ acceptedUrls.add(resolved);
2063
+ edits.push({ start, end: cursor, value: JSON.stringify(resolved) });
2064
+ return;
2065
+ }
2066
+ if (quote === "`" && char === "$" && code[cursor + 1] === "{") return;
2067
+ raw += char;
2068
+ cursor++;
2069
+ }
2070
+ };
2071
+ if (first === "[") {
2072
+ cursor++;
2073
+ while (cursor < code.length) {
2074
+ skipTrivia();
2075
+ if (code[cursor] === ",") {
2076
+ cursor++;
2077
+ skipTrivia();
2078
+ }
2079
+ if (code[cursor] === "]") break;
2080
+ const before = cursor;
2081
+ readLiteral();
2082
+ if (cursor === before) break;
2083
+ }
2084
+ } else {
2085
+ readLiteral();
2086
+ }
2087
+ }
2088
+ for (const edit of edits.sort((a, b) => b.start - a.start)) {
2089
+ code = code.slice(0, edit.start) + edit.value + code.slice(edit.end);
2090
+ }
2091
+ return { code, acceptedUrls, isSelfAccepting };
2092
+ }
2093
+ function maskStringsAndComments(code) {
2094
+ const masked = code.split("");
2095
+ let state = "code";
2096
+ const isRegexStart = (index2) => {
2097
+ let previous = index2 - 1;
2098
+ while (previous >= 0 && /\s/.test(code[previous])) previous--;
2099
+ return previous < 0 || "=(:,!&|?{};[]+-*%^~<>".includes(code[previous]);
2100
+ };
2101
+ for (let i = 0; i < code.length; i++) {
2102
+ const char = code[i];
2103
+ const next = code[i + 1];
2104
+ if (state === "code") {
2105
+ if (char === "'") state = "single";
2106
+ else if (char === '"') state = "double";
2107
+ else if (char === "`") state = "template";
2108
+ else if (char === "/" && next === "/") state = "line-comment";
2109
+ else if (char === "/" && next === "*") state = "block-comment";
2110
+ else if (char === "/" && isRegexStart(i)) state = "regex";
2111
+ else continue;
2112
+ masked[i] = " ";
2113
+ continue;
2114
+ }
2115
+ if (state === "line-comment") {
2116
+ if (char === "\n") {
2117
+ state = "code";
2118
+ } else {
2119
+ masked[i] = " ";
2120
+ }
2121
+ continue;
2122
+ }
2123
+ if (state === "block-comment") {
2124
+ masked[i] = char === "\n" ? "\n" : " ";
2125
+ if (char === "*" && next === "/") {
2126
+ masked[i + 1] = " ";
2127
+ i++;
2128
+ state = "code";
2129
+ }
2130
+ continue;
2131
+ }
2132
+ if (state === "regex" || state === "regex-class") {
2133
+ masked[i] = char === "\n" ? "\n" : " ";
2134
+ if (char === "\\") {
2135
+ if (i + 1 < code.length) masked[++i] = " ";
2136
+ } else if (state === "regex" && char === "[") {
2137
+ state = "regex-class";
2138
+ } else if (state === "regex-class" && char === "]") {
2139
+ state = "regex";
2140
+ } else if (state === "regex" && char === "/") {
2141
+ state = "code";
2142
+ }
2143
+ continue;
2144
+ }
2145
+ masked[i] = char === "\n" ? "\n" : " ";
2146
+ if (char === "\\") {
2147
+ if (i + 1 < code.length) masked[++i] = " ";
2148
+ continue;
2149
+ }
2150
+ if (state === "single" && char === "'" || state === "double" && char === '"' || state === "template" && char === "`") {
2151
+ state = "code";
2152
+ }
2153
+ }
2154
+ return masked.join("");
1863
2155
  }
1864
2156
  function resolveAliasTarget(value, root) {
1865
2157
  if (path4.isAbsolute(value) && fs4.existsSync(value)) return value;
@@ -1884,6 +2176,12 @@ function isUnderRoot(abs, root) {
1884
2176
  const rel = path4.relative(root, abs);
1885
2177
  return !!rel && !rel.startsWith("..") && !path4.isAbsolute(rel);
1886
2178
  }
2179
+ function appendTimestampQuery(url, timestamp) {
2180
+ const hashIndex = url.indexOf("#");
2181
+ const hash = hashIndex >= 0 ? url.slice(hashIndex) : "";
2182
+ const withoutHash = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
2183
+ return `${withoutHash}${withoutHash.includes("?") ? "&" : "?"}t=${timestamp}${hash}`;
2184
+ }
1887
2185
  function externalSpecToModuleUrl(spec, baseDir, root) {
1888
2186
  const resolved = resolveNodeModule(baseDir, spec);
1889
2187
  if (!resolved) return `/@modules/${spec}`;
@@ -2027,30 +2325,29 @@ function isModuleRequest(url) {
2027
2325
  function getHmrClientCode() {
2028
2326
  return `
2029
2327
  // Nasti HMR Client
2030
- const socket = new WebSocket(\`ws://\${location.host}\`, 'nasti-hmr');
2328
+ const socketProtocol = location.protocol === 'https:' ? 'wss' : 'ws';
2329
+ const socket = new WebSocket(socketProtocol + '://' + location.host, 'nasti-hmr');
2031
2330
  const hotModulesMap = new Map();
2032
2331
  const disposeMap = new Map();
2033
2332
  const pruneMap = new Map();
2333
+ const dataMap = new Map();
2334
+ let updateQueue = [];
2335
+ let pendingUpdateQueue = false;
2034
2336
 
2035
2337
  socket.addEventListener('message', async ({ data }) => {
2036
2338
  const payload = JSON.parse(data);
2037
2339
  switch (payload.type) {
2038
2340
  case 'connected':
2039
- console.log('[nasti] connected.');
2341
+ console.debug('[nasti] connected.');
2040
2342
  clearErrorOverlay();
2041
2343
  break;
2042
2344
  case 'update':
2043
2345
  try {
2044
- await Promise.all(payload.updates.map((update) => {
2045
- if (update.type === 'js-update') {
2046
- return fetchUpdate(update);
2047
- } else if (update.type === 'css-update') {
2048
- return updateCss(update.path);
2049
- }
2050
- }));
2346
+ // CSS \u5728 unbundled \u6A21\u5F0F\u4E0B\u4E5F\u662F\u4F1A\u6CE8\u5165 <style> \u7684 JS \u6A21\u5757\uFF0C\u548C\u666E\u901A JS
2347
+ // \u4E00\u6837\u91CD\u65B0 import \u624D\u80FD\u6267\u884C dispose/accept \u5E76\u4FDD\u6301\u9875\u9762\u72B6\u6001\u3002
2348
+ await Promise.all(payload.updates.map(queueUpdate));
2051
2349
  clearErrorOverlay();
2052
- console.log('[nasti] HMR update complete, reloading page');
2053
- location.reload();
2350
+ console.debug('[nasti] HMR update complete.');
2054
2351
  } catch (err) {
2055
2352
  console.error('[nasti] HMR update failed:', err);
2056
2353
  showErrorOverlay(err);
@@ -2061,10 +2358,17 @@ socket.addEventListener('message', async ({ data }) => {
2061
2358
  location.reload();
2062
2359
  break;
2063
2360
  case 'prune':
2064
- payload.paths.forEach((p) => {
2065
- const cb = pruneMap.get(p);
2066
- if (cb) cb();
2067
- });
2361
+ await Promise.all(payload.paths.map(async (path) => {
2362
+ const data = dataMap.get(path);
2363
+ const dispose = disposeMap.get(path);
2364
+ const prune = pruneMap.get(path);
2365
+ if (dispose) await dispose(data);
2366
+ if (prune) await prune(data);
2367
+ hotModulesMap.delete(path);
2368
+ disposeMap.delete(path);
2369
+ pruneMap.delete(path);
2370
+ dataMap.delete(path);
2371
+ }));
2068
2372
  break;
2069
2373
  case 'error':
2070
2374
  console.error('[nasti] error:', payload.err.message);
@@ -2073,33 +2377,64 @@ socket.addEventListener('message', async ({ data }) => {
2073
2377
  }
2074
2378
  });
2075
2379
 
2076
- // \u81EA\u52A8\u91CD\u8FDE\uFF08\u65AD\u7EBF\u65F6\u6307\u6570\u9000\u907F\uFF09
2380
+ // \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
2077
2381
  let reconnectTimer = 0;
2078
2382
  socket.addEventListener('close', () => {
2079
2383
  clearTimeout(reconnectTimer);
2080
2384
  reconnectTimer = setTimeout(() => location.reload(), 1000);
2081
2385
  });
2082
2386
 
2387
+ /**
2388
+ * \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
2389
+ * \u6539\u53D8\u6A21\u5757\u5E94\u7528\u987A\u5E8F\u3002\u8FD9\u4E0E Vite HMRClient \u7684 fetch/apply \u4E24\u9636\u6BB5\u4E00\u81F4\u3002
2390
+ */
2391
+ async function queueUpdate(update) {
2392
+ updateQueue.push(fetchUpdate(update));
2393
+ if (pendingUpdateQueue) return;
2394
+
2395
+ pendingUpdateQueue = true;
2396
+ await Promise.resolve();
2397
+ pendingUpdateQueue = false;
2398
+ const loading = updateQueue;
2399
+ updateQueue = [];
2400
+ const applyUpdates = await Promise.all(loading);
2401
+ for (const apply of applyUpdates) {
2402
+ if (apply) apply();
2403
+ }
2404
+ }
2405
+
2083
2406
  async function fetchUpdate(update) {
2084
2407
  const mod = hotModulesMap.get(update.path);
2085
- // \u5148\u8DD1 dispose\uFF08\u7ED9\u6A21\u5757\u673A\u4F1A\u6E05\u7406\u526F\u4F5C\u7528\uFF09
2086
- const dispose = disposeMap.get(update.path);
2087
- if (dispose) dispose();
2408
+ // \u5C1A\u672A\u5728\u5F53\u524D\u9875\u9762\u52A0\u8F7D\u7684\u52A8\u6001\u6A21\u5757\u4E0D\u9700\u8981\u66F4\u65B0\u3002
2409
+ if (!mod) return;
2088
2410
 
2089
- const newMod = await import(update.acceptedPath + '?t=' + update.timestamp);
2090
- if (mod) {
2091
- // \u590D\u5236\u56DE\u8C03\u6570\u7EC4\u907F\u514D\u56DE\u8C03\u5185\u90E8\u53C8\u4FEE\u6539 hotModulesMap \u9020\u6210\u8FED\u4EE3\u5F02\u5E38
2092
- [...mod.callbacks].forEach((cb) => cb(newMod));
2093
- }
2411
+ // \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
2412
+ const qualifiedCallbacks = mod.callbacks.filter(({ deps }) =>
2413
+ deps.includes(update.acceptedPath)
2414
+ );
2415
+ const isSelfUpdate = update.path === update.acceptedPath;
2416
+ if (!isSelfUpdate && qualifiedCallbacks.length === 0) return;
2417
+
2418
+ const dispose = disposeMap.get(update.acceptedPath);
2419
+ if (dispose) await dispose(dataMap.get(update.acceptedPath));
2420
+ const newMod = await import(appendTimestampQuery(update.acceptedPath, update.timestamp));
2421
+
2422
+ return () => {
2423
+ for (const { deps, fn } of qualifiedCallbacks) {
2424
+ fn(deps.map((dep) => dep === update.acceptedPath ? newMod : undefined));
2425
+ }
2426
+ const detail = isSelfUpdate
2427
+ ? update.path
2428
+ : update.acceptedPath + ' via ' + update.path;
2429
+ console.debug('[nasti] hot updated:', detail);
2430
+ };
2094
2431
  }
2095
2432
 
2096
- function updateCss(path) {
2097
- const el = document.querySelector(\`style[data-nasti-css="\${path}"]\`);
2098
- if (el) {
2099
- return fetch(path + '?t=' + Date.now())
2100
- .then(r => r.text())
2101
- .then(css => { el.textContent = css; });
2102
- }
2433
+ function appendTimestampQuery(url, timestamp) {
2434
+ const hashIndex = url.indexOf('#');
2435
+ const hash = hashIndex >= 0 ? url.slice(hashIndex) : '';
2436
+ const withoutHash = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
2437
+ return withoutHash + (withoutHash.includes('?') ? '&' : '?') + 't=' + timestamp + hash;
2103
2438
  }
2104
2439
 
2105
2440
  function clearErrorOverlay() {
@@ -2127,23 +2462,30 @@ function showErrorOverlay(err) {
2127
2462
  document.body.appendChild(overlay);
2128
2463
  }
2129
2464
 
2130
- /**
2131
- * \u751F\u6210 import.meta.hot \u7684 hot context\u3002
2132
- * \u5173\u952E\u7EA6\u675F\uFF1A\u540C\u4E00 ownerPath \u7684 accept \u56DE\u8C03\u5FC5\u987B\u66FF\u6362\uFF08\u4E0D\u662F append\uFF09\u3002
2133
- * \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
2134
- * \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
2135
- */
2136
2465
  export function createHotContext(ownerPath) {
2466
+ if (!dataMap.has(ownerPath)) dataMap.set(ownerPath, {});
2467
+
2468
+ // \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
2469
+ const existing = hotModulesMap.get(ownerPath);
2470
+ if (existing) existing.callbacks = [];
2471
+
2472
+ const acceptDeps = (deps, callback = () => {}) => {
2473
+ const mod = hotModulesMap.get(ownerPath) || { id: ownerPath, callbacks: [] };
2474
+ mod.callbacks.push({ deps, fn: callback });
2475
+ hotModulesMap.set(ownerPath, mod);
2476
+ };
2477
+
2137
2478
  return {
2138
2479
  accept(deps, callback) {
2139
- // \u81EA\u63A5\u53D7: hot.accept() \u6216 hot.accept(callback)
2140
2480
  if (typeof deps === 'function' || deps === undefined) {
2141
- hotModulesMap.set(ownerPath, { callbacks: [deps || (() => {})] });
2142
- return;
2481
+ acceptDeps([ownerPath], ([mod]) => deps?.(mod));
2482
+ } else if (typeof deps === 'string') {
2483
+ acceptDeps([deps], ([mod]) => callback?.(mod));
2484
+ } else if (Array.isArray(deps)) {
2485
+ acceptDeps(deps, callback);
2486
+ } else {
2487
+ throw new Error('invalid hot.accept() usage');
2143
2488
  }
2144
- // \u4F9D\u8D56\u63A5\u53D7: hot.accept(deps, callback)\uFF0C\u591A\u6B21\u8C03\u7528\u8FFD\u52A0
2145
- const existing = hotModulesMap.get(ownerPath)?.callbacks ?? [];
2146
- hotModulesMap.set(ownerPath, { callbacks: [...existing, callback] });
2147
2489
  },
2148
2490
  prune(callback) {
2149
2491
  pruneMap.set(ownerPath, callback);
@@ -2154,21 +2496,85 @@ export function createHotContext(ownerPath) {
2154
2496
  invalidate() {
2155
2497
  location.reload();
2156
2498
  },
2157
- data: {},
2499
+ data: dataMap.get(ownerPath),
2158
2500
  };
2159
2501
  }
2160
2502
  `;
2161
2503
  }
2162
- var __dirname_esm, __require2, __refreshRuntimeCache, REACT_REFRESH_GLOBAL_PREAMBLE, esmBundleCache, VALID_IDENT, RESOLVE_EXTENSIONS, ESM_CONDITIONS;
2504
+ var __dirname_esm, __require2, __refreshRuntimeCache, REACT_REFRESH_BOUNDARY_HELPERS, REACT_REFRESH_GLOBAL_PREAMBLE, esmBundleCache, VALID_IDENT, RESOLVE_EXTENSIONS, ESM_CONDITIONS;
2163
2505
  var init_middleware = __esm({
2164
2506
  "src/server/middleware.ts"() {
2165
2507
  "use strict";
2166
2508
  init_transformer();
2167
2509
  init_html();
2168
2510
  init_env();
2511
+ init_url();
2169
2512
  __dirname_esm = path4.dirname(fileURLToPath(import.meta.url));
2170
2513
  __require2 = createRequire(import.meta.url);
2171
2514
  __refreshRuntimeCache = null;
2515
+ REACT_REFRESH_BOUNDARY_HELPERS = `
2516
+ function __nastiIsPlainObject(obj) {
2517
+ return Object.prototype.toString.call(obj) === '[object Object]' &&
2518
+ (obj.constructor === Object || obj.constructor === undefined);
2519
+ }
2520
+ function __nastiIsCompoundComponent(type) {
2521
+ if (!__nastiIsPlainObject(type)) return false;
2522
+ for (const key in type) {
2523
+ if (!isLikelyComponentType(type[key])) return false;
2524
+ }
2525
+ return true;
2526
+ }
2527
+ export function registerExportsForReactRefresh(filename, moduleExports) {
2528
+ for (const key in moduleExports) {
2529
+ if (key === '__esModule') continue;
2530
+ const value = moduleExports[key];
2531
+ if (isLikelyComponentType(value)) {
2532
+ register(value, filename + ' export ' + key);
2533
+ } else if (__nastiIsCompoundComponent(value)) {
2534
+ for (const subKey in value) {
2535
+ register(value[subKey], filename + ' export ' + key + '-' + subKey);
2536
+ }
2537
+ }
2538
+ }
2539
+ }
2540
+ let __nastiRefreshTimer;
2541
+ function __nastiEnqueueRefresh() {
2542
+ clearTimeout(__nastiRefreshTimer);
2543
+ __nastiRefreshTimer = setTimeout(() => performReactRefresh(), 16);
2544
+ }
2545
+ function __nastiCheckExports(ignored, exports, predicate) {
2546
+ for (const key in exports) {
2547
+ if (ignored.includes(key)) continue;
2548
+ if (!predicate(key, exports[key])) return key;
2549
+ }
2550
+ return true;
2551
+ }
2552
+ export function validateRefreshBoundaryAndEnqueueUpdate(id, prevExports, nextExports) {
2553
+ const ignored = window.__getReactRefreshIgnoredExports?.({ id }) ?? [];
2554
+ if (__nastiCheckExports(ignored, prevExports, (key) => key in nextExports) !== true) {
2555
+ return 'Could not Fast Refresh (export removed)';
2556
+ }
2557
+ if (__nastiCheckExports(ignored, nextExports, (key) => key in prevExports) !== true) {
2558
+ return 'Could not Fast Refresh (new export)';
2559
+ }
2560
+ let hasExports = false;
2561
+ const compatible = __nastiCheckExports(ignored, nextExports, (key, value) => {
2562
+ hasExports = true;
2563
+ return isLikelyComponentType(value) ||
2564
+ __nastiIsCompoundComponent(value) ||
2565
+ prevExports[key] === value;
2566
+ });
2567
+ if (!hasExports) {
2568
+ return 'Could not Fast Refresh (no exports)';
2569
+ }
2570
+ if (compatible === true) {
2571
+ __nastiEnqueueRefresh();
2572
+ return;
2573
+ }
2574
+ return 'Could not Fast Refresh ("' + compatible + '" export is incompatible)';
2575
+ }
2576
+ export const __hmr_import = (module) => import(module);
2577
+ `;
2172
2578
  REACT_REFRESH_GLOBAL_PREAMBLE = `
2173
2579
  import RefreshRuntime from "/@react-refresh";
2174
2580
  RefreshRuntime.injectIntoGlobalHook(window);
@@ -2198,8 +2604,10 @@ async function handleFileChange(file, server) {
2198
2604
  }
2199
2605
  const updates = [];
2200
2606
  const timestamp = Date.now();
2607
+ const graph = moduleGraph;
2608
+ const invalidatedModules = /* @__PURE__ */ new Set();
2201
2609
  for (const mod of mods) {
2202
- moduleGraph.invalidateModule(mod);
2610
+ graph.invalidateModuleAndImporters(mod, timestamp, invalidatedModules);
2203
2611
  const ctx = {
2204
2612
  file,
2205
2613
  timestamp,
@@ -2217,19 +2625,25 @@ async function handleFileChange(file, server) {
2217
2625
  }
2218
2626
  }
2219
2627
  for (const affected of affectedModules) {
2220
- const boundaries = moduleGraph.getHmrBoundaries(affected);
2628
+ graph.invalidateModuleAndImporters(affected, timestamp, invalidatedModules);
2629
+ const boundaries = graph.getHmrBoundaries(affected);
2221
2630
  if (boundaries.length === 0) {
2222
2631
  logger.info(pc4.green("page reload ") + pc4.dim(shortFile), { timestamp: true });
2223
2632
  ws.send({ type: "full-reload", path: relativePath });
2224
2633
  return;
2225
2634
  }
2226
- for (const { boundary } of boundaries) {
2227
- updates.push({
2635
+ for (const { boundary, acceptedVia } of boundaries) {
2636
+ const update = {
2228
2637
  type: boundary.type === "css" ? "css-update" : "js-update",
2229
2638
  path: boundary.url,
2230
- acceptedPath: affected.url,
2639
+ acceptedPath: acceptedVia.url,
2231
2640
  timestamp
2232
- });
2641
+ };
2642
+ if (!updates.some(
2643
+ (existing) => existing.type === update.type && existing.path === update.path && existing.acceptedPath === update.acceptedPath
2644
+ )) {
2645
+ updates.push(update);
2646
+ }
2233
2647
  }
2234
2648
  }
2235
2649
  }
@@ -2299,6 +2713,7 @@ function resolvePlugin(config) {
2299
2713
  }
2300
2714
  if (!source.startsWith("/") && !source.startsWith(".")) {
2301
2715
  if (vueRuntimeEntry && source === "vue") return vueRuntimeEntry;
2716
+ if (config.command === "build") return null;
2302
2717
  try {
2303
2718
  const resolved = require2.resolve(source, {
2304
2719
  paths: [importer ? path6.dirname(importer) : config.root]
@@ -2387,27 +2802,27 @@ var require_process = __commonJS({
2387
2802
  var require_filesystem = __commonJS({
2388
2803
  "node_modules/detect-libc/lib/filesystem.js"(exports, module) {
2389
2804
  "use strict";
2390
- var fs12 = __require("fs");
2805
+ var fs13 = __require("fs");
2391
2806
  var LDD_PATH = "/usr/bin/ldd";
2392
2807
  var SELF_PATH = "/proc/self/exe";
2393
2808
  var MAX_LENGTH = 2048;
2394
- var readFileSync = (path17) => {
2395
- const fd = fs12.openSync(path17, "r");
2809
+ var readFileSync = (path18) => {
2810
+ const fd = fs13.openSync(path18, "r");
2396
2811
  const buffer = Buffer.alloc(MAX_LENGTH);
2397
- const bytesRead = fs12.readSync(fd, buffer, 0, MAX_LENGTH, 0);
2398
- fs12.close(fd, () => {
2812
+ const bytesRead = fs13.readSync(fd, buffer, 0, MAX_LENGTH, 0);
2813
+ fs13.close(fd, () => {
2399
2814
  });
2400
2815
  return buffer.subarray(0, bytesRead);
2401
2816
  };
2402
- var readFile = (path17) => new Promise((resolve, reject) => {
2403
- fs12.open(path17, "r", (err, fd) => {
2817
+ var readFile = (path18) => new Promise((resolve, reject) => {
2818
+ fs13.open(path18, "r", (err, fd) => {
2404
2819
  if (err) {
2405
2820
  reject(err);
2406
2821
  } else {
2407
2822
  const buffer = Buffer.alloc(MAX_LENGTH);
2408
- fs12.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
2823
+ fs13.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
2409
2824
  resolve(buffer.subarray(0, bytesRead));
2410
- fs12.close(fd, () => {
2825
+ fs13.close(fd, () => {
2411
2826
  });
2412
2827
  });
2413
2828
  }
@@ -2519,11 +2934,11 @@ var require_detect_libc = __commonJS({
2519
2934
  }
2520
2935
  return null;
2521
2936
  };
2522
- var familyFromInterpreterPath = (path17) => {
2523
- if (path17) {
2524
- if (path17.includes("/ld-musl-")) {
2937
+ var familyFromInterpreterPath = (path18) => {
2938
+ if (path18) {
2939
+ if (path18.includes("/ld-musl-")) {
2525
2940
  return MUSL;
2526
- } else if (path17.includes("/ld-linux-")) {
2941
+ } else if (path18.includes("/ld-linux-")) {
2527
2942
  return GLIBC;
2528
2943
  }
2529
2944
  }
@@ -2570,8 +2985,8 @@ var require_detect_libc = __commonJS({
2570
2985
  cachedFamilyInterpreter = null;
2571
2986
  try {
2572
2987
  const selfContent = await readFile(SELF_PATH);
2573
- const path17 = interpreterPath(selfContent);
2574
- cachedFamilyInterpreter = familyFromInterpreterPath(path17);
2988
+ const path18 = interpreterPath(selfContent);
2989
+ cachedFamilyInterpreter = familyFromInterpreterPath(path18);
2575
2990
  } catch (e) {
2576
2991
  }
2577
2992
  return cachedFamilyInterpreter;
@@ -2583,8 +2998,8 @@ var require_detect_libc = __commonJS({
2583
2998
  cachedFamilyInterpreter = null;
2584
2999
  try {
2585
3000
  const selfContent = readFileSync(SELF_PATH);
2586
- const path17 = interpreterPath(selfContent);
2587
- cachedFamilyInterpreter = familyFromInterpreterPath(path17);
3001
+ const path18 = interpreterPath(selfContent);
3002
+ cachedFamilyInterpreter = familyFromInterpreterPath(path18);
2588
3003
  } catch (e) {
2589
3004
  }
2590
3005
  return cachedFamilyInterpreter;
@@ -3610,8 +4025,8 @@ function vuePlugin(config) {
3610
4025
  let descriptor = descriptorCache.get(filePath);
3611
4026
  if (!descriptor) {
3612
4027
  try {
3613
- const fs12 = await import("fs");
3614
- const source = fs12.readFileSync(filePath, "utf-8");
4028
+ const fs13 = await import("fs");
4029
+ const source = fs13.readFileSync(filePath, "utf-8");
3615
4030
  const parsed = sfc.parse(source, { filename: filePath });
3616
4031
  if (parsed.errors.length) return null;
3617
4032
  descriptor = parsed.descriptor;
@@ -3749,16 +4164,27 @@ var init_vue = __esm({
3749
4164
  // src/plugins/builtins.ts
3750
4165
  function resolvePluginList(config, userPlugins, opts = {}) {
3751
4166
  const isServe = config.command === "serve";
4167
+ let environmentOptions;
4168
+ if (opts.environmentName) {
4169
+ environmentOptions = config.environments[opts.environmentName];
4170
+ if (!environmentOptions) {
4171
+ throw new Error(
4172
+ `[nasti] unknown environment "${opts.environmentName}" \u2014 declare it in config.environments`
4173
+ );
4174
+ }
4175
+ }
4176
+ const pluginConfig = environmentOptions ? { ...config, resolve: environmentOptions.resolve, build: environmentOptions.build } : config;
4177
+ const consumer = opts.consumer ?? environmentOptions?.consumer;
3752
4178
  return [
3753
4179
  // vuePlugin 排最前(enforce: 'pre' 语义):.vue 先编译成 JS 再走后续管道
3754
- ...config.framework === "vue" ? [vuePlugin(config)] : [],
3755
- resolvePlugin(config),
3756
- cssPlugin(config, opts.cssEngine, opts.consumer),
3757
- assetsPlugin(config),
3758
- ...isServe ? [htmlPlugin(config)] : [],
4180
+ ...config.framework === "vue" ? [vuePlugin(pluginConfig)] : [],
4181
+ resolvePlugin(pluginConfig),
4182
+ cssPlugin(pluginConfig, opts.cssEngine, consumer),
4183
+ assetsPlugin(pluginConfig),
4184
+ ...isServe ? [htmlPlugin(pluginConfig)] : [],
3759
4185
  ...userPlugins,
3760
4186
  // cssPostPlugin 最后(enforce: 'post' 语义):renderChunk 聚合抽取
3761
- ...!isServe && opts.cssEngine ? [cssPostPlugin(config, opts.cssEngine)] : []
4187
+ ...!isServe && opts.cssEngine ? [cssPostPlugin(pluginConfig, opts.cssEngine)] : []
3762
4188
  ];
3763
4189
  }
3764
4190
  var init_builtins = __esm({
@@ -4082,6 +4508,134 @@ var init_reporter = __esm({
4082
4508
  }
4083
4509
  });
4084
4510
 
4511
+ // src/core/build-app-context.ts
4512
+ import fs9 from "fs";
4513
+ import path12 from "path";
4514
+ function createBuildAppContext(config, results) {
4515
+ const output = [];
4516
+ const emitted = /* @__PURE__ */ new Set();
4517
+ const outDir = path12.resolve(config.root, config.build.outDir);
4518
+ let environmentArtifacts;
4519
+ return {
4520
+ config,
4521
+ results,
4522
+ get output() {
4523
+ return Object.freeze([...output]);
4524
+ },
4525
+ getResult(environmentName) {
4526
+ return results[environmentName];
4527
+ },
4528
+ getArtifact(environmentName, fileName) {
4529
+ const normalized = normalizeEnvironmentFileName(fileName);
4530
+ return results[environmentName]?.output.find(
4531
+ (artifact) => normalizeEnvironmentFileName(artifact.fileName) === normalized
4532
+ );
4533
+ },
4534
+ getEntry(environmentName, entryName) {
4535
+ const result = results[environmentName];
4536
+ const fileName = result?.entries?.[entryName];
4537
+ if (!fileName) return void 0;
4538
+ return result.output.find(
4539
+ (artifact) => normalizeEnvironmentFileName(artifact.fileName) === normalizeEnvironmentFileName(fileName)
4540
+ );
4541
+ },
4542
+ getManifest(environmentName) {
4543
+ return results[environmentName]?.manifest;
4544
+ },
4545
+ emitFile(file) {
4546
+ const fileName = normalizeAppFileName(file.fileName);
4547
+ const collisionKey = artifactCollisionKey(fileName);
4548
+ if (emitted.has(collisionKey)) {
4549
+ throw new Error(`[nasti] app artifact already emitted: ${fileName}`);
4550
+ }
4551
+ environmentArtifacts ??= collectEnvironmentArtifacts(config, results, outDir);
4552
+ if (environmentArtifacts.has(collisionKey)) {
4553
+ throw new Error(`[nasti] app artifact conflicts with environment output: ${fileName}`);
4554
+ }
4555
+ const target = path12.resolve(outDir, ...fileName.split("/"));
4556
+ const relative = path12.relative(outDir, target);
4557
+ if (relative.startsWith("..") || path12.isAbsolute(relative)) {
4558
+ throw new Error(`[nasti] app artifact must stay inside build.outDir: ${file.fileName}`);
4559
+ }
4560
+ assertNoSymlinkComponents(outDir, fileName);
4561
+ fs9.mkdirSync(path12.dirname(target), { recursive: true });
4562
+ fs9.writeFileSync(target, file.source);
4563
+ const artifact = {
4564
+ ...file,
4565
+ fileName,
4566
+ type: "asset"
4567
+ };
4568
+ emitted.add(collisionKey);
4569
+ output.push(artifact);
4570
+ return fileName;
4571
+ }
4572
+ };
4573
+ }
4574
+ function normalizeEnvironmentFileName(fileName) {
4575
+ return path12.posix.normalize(fileName.replace(/\\/g, "/").replace(/^\.\//, ""));
4576
+ }
4577
+ function isInvalidEnvironmentFileName(fileName) {
4578
+ return !fileName || fileName === "." || fileName === ".." || fileName.startsWith("../") || path12.posix.isAbsolute(fileName) || /^[A-Za-z]:\//.test(fileName);
4579
+ }
4580
+ function normalizeAppFileName(fileName) {
4581
+ const normalized = normalizeEnvironmentFileName(fileName);
4582
+ if (isInvalidEnvironmentFileName(normalized)) {
4583
+ throw new Error(`[nasti] invalid app artifact fileName: ${fileName}`);
4584
+ }
4585
+ return normalized;
4586
+ }
4587
+ function artifactCollisionKey(fileName) {
4588
+ return normalizeEnvironmentFileName(fileName).toLowerCase();
4589
+ }
4590
+ function collectEnvironmentArtifacts(config, results, appOutDir) {
4591
+ const occupied = /* @__PURE__ */ new Set();
4592
+ for (const [environmentName, result] of Object.entries(results)) {
4593
+ const environment = config.environments[environmentName];
4594
+ if (!environment) continue;
4595
+ const environmentOutDir = path12.resolve(config.root, environment.build.outDir);
4596
+ for (const artifact of result.output) {
4597
+ const artifactPath = path12.resolve(
4598
+ environmentOutDir,
4599
+ ...normalizeEnvironmentFileName(artifact.fileName).split("/")
4600
+ );
4601
+ const relative = path12.relative(appOutDir, artifactPath);
4602
+ if (!relative.startsWith("..") && !path12.isAbsolute(relative)) {
4603
+ occupied.add(artifactCollisionKey(relative));
4604
+ }
4605
+ }
4606
+ }
4607
+ return occupied;
4608
+ }
4609
+ function assertNoSymlinkComponents(outDir, fileName) {
4610
+ let current = outDir;
4611
+ for (const segment of fileName.split("/")) {
4612
+ current = path12.join(current, segment);
4613
+ let stats;
4614
+ try {
4615
+ stats = fs9.lstatSync(current);
4616
+ } catch (error) {
4617
+ if (error.code === "ENOENT") continue;
4618
+ throw error;
4619
+ }
4620
+ if (stats.isSymbolicLink()) {
4621
+ throw new Error(`[nasti] app artifact path cannot traverse a symlink: ${fileName}`);
4622
+ }
4623
+ }
4624
+ }
4625
+ function inferEnvironmentEntries(output) {
4626
+ const entries = {};
4627
+ for (const artifact of output) {
4628
+ if (artifact.type !== "chunk" || !artifact.isEntry || !artifact.name) continue;
4629
+ entries[artifact.name] = normalizeEnvironmentFileName(artifact.fileName);
4630
+ }
4631
+ return Object.keys(entries).length > 0 ? entries : void 0;
4632
+ }
4633
+ var init_build_app_context = __esm({
4634
+ "src/core/build-app-context.ts"() {
4635
+ "use strict";
4636
+ }
4637
+ });
4638
+
4085
4639
  // src/build/index.ts
4086
4640
  var build_exports = {};
4087
4641
  __export(build_exports, {
@@ -4091,8 +4645,8 @@ __export(build_exports, {
4091
4645
  resolveClientEntries: () => resolveClientEntries,
4092
4646
  toRolldownPlugins: () => toRolldownPlugins
4093
4647
  });
4094
- import path12 from "path";
4095
- import fs9 from "fs";
4648
+ import path13 from "path";
4649
+ import fs10 from "fs";
4096
4650
  import { builtinModules as builtinModules2 } from "module";
4097
4651
  import { rolldown } from "rolldown";
4098
4652
  import pc6 from "picocolors";
@@ -4100,9 +4654,14 @@ function getRolldownOptions(environment, entryPoints, rolldownPlugins) {
4100
4654
  const config = environment.config;
4101
4655
  const envOptions = environment.options;
4102
4656
  const isServer = environment.consumer === "server";
4103
- const outDir = path12.resolve(config.root, envOptions.build.outDir);
4657
+ const outDir = path13.resolve(config.root, envOptions.build.outDir);
4104
4658
  const assetsDir = envOptions.build.assetsDir;
4105
- const { output: userOutput, transform: userTransform, ...restInputOptions } = envOptions.build.rolldownOptions;
4659
+ const {
4660
+ output: userOutput,
4661
+ transform: userTransform,
4662
+ resolve: userResolve,
4663
+ ...restInputOptions
4664
+ } = envOptions.build.rolldownOptions;
4106
4665
  const vueDefine = config.framework === "vue" ? {
4107
4666
  __VUE_OPTIONS_API__: "true",
4108
4667
  __VUE_PROD_DEVTOOLS__: "false",
@@ -4116,19 +4675,22 @@ function getRolldownOptions(environment, entryPoints, rolldownPlugins) {
4116
4675
  input: entryPoints,
4117
4676
  transform: { ...userTransform, define: mergedDefine },
4118
4677
  plugins: rolldownPlugins,
4678
+ // client/server consumer 都必须使用当前环境的 conditions/mainFields;Lynx
4679
+ // BG/MT 通常同为 client consumer,但仍可能声明不同的运行时条件。
4680
+ resolve: {
4681
+ ...userResolve ?? {},
4682
+ // Environment API 是条件解析的唯一高层入口,优先于继承来的底层选项。
4683
+ conditionNames: envOptions.resolve.conditions,
4684
+ mainFields: envOptions.resolve.mainFields
4685
+ },
4119
4686
  ...isServer ? {
4120
4687
  platform: restInputOptions.platform ?? "node",
4121
- resolve: {
4122
- conditionNames: envOptions.resolve.conditions,
4123
- mainFields: envOptions.resolve.mainFields,
4124
- ...restInputOptions.resolve
4125
- },
4126
4688
  // server 产物:node 内建恒外部化;bare specifier 默认外部化
4127
4689
  //(同 Vite ssr.external 默认 —— 依赖由 node_modules 运行时解析),
4128
4690
  // 相对/绝对/虚拟模块照常打包。需要内联依赖时经 rolldownOptions.external 覆盖。
4129
4691
  external: restInputOptions.external ?? ((id) => {
4130
4692
  if (NODE_BUILTINS2.has(id)) return true;
4131
- return !id.startsWith(".") && !path12.isAbsolute(id) && !id.startsWith("\0");
4693
+ return !id.startsWith(".") && !path13.isAbsolute(id) && !id.startsWith("\0");
4132
4694
  })
4133
4695
  } : {}
4134
4696
  };
@@ -4155,27 +4717,122 @@ function getRolldownOptions(environment, entryPoints, rolldownPlugins) {
4155
4717
  };
4156
4718
  return { inputOptions, outputOptions, outDir };
4157
4719
  }
4158
- function toRolldownPlugins(plugins) {
4720
+ function toRolldownPlugins(plugins, environment) {
4721
+ const wrap = (hook) => {
4722
+ if (!hook) return hook;
4723
+ return function(...args) {
4724
+ return hook.apply(attachEnvironment(this, environment), args);
4725
+ };
4726
+ };
4159
4727
  return plugins.map((p) => ({
4160
4728
  name: p.name,
4161
- resolveId: p.resolveId,
4162
- load: p.load,
4163
- transform: p.transform,
4164
- buildStart: p.buildStart,
4165
- buildEnd: p.buildEnd,
4729
+ resolveId: wrap(p.resolveId),
4730
+ load: wrap(p.load),
4731
+ transform: wrap(p.transform),
4732
+ buildStart: wrap(p.buildStart),
4733
+ buildEnd: wrap(p.buildEnd),
4166
4734
  // closeBundle 在 bundle.close() 时触发 —— PWA manifest/SW 等终态产物依赖
4167
- closeBundle: p.closeBundle,
4168
- renderChunk: p.renderChunk,
4169
- augmentChunkHash: p.augmentChunkHash,
4170
- generateBundle: p.generateBundle
4735
+ closeBundle: wrap(p.closeBundle),
4736
+ renderChunk: wrap(p.renderChunk),
4737
+ augmentChunkHash: wrap(p.augmentChunkHash),
4738
+ generateBundle: wrap(p.generateBundle)
4171
4739
  }));
4172
4740
  }
4741
+ function attachEnvironment(context, environment) {
4742
+ if (context?.environment === environment) return context;
4743
+ try {
4744
+ Object.defineProperty(context, "environment", {
4745
+ configurable: true,
4746
+ enumerable: false,
4747
+ writable: false,
4748
+ value: environment
4749
+ });
4750
+ return context;
4751
+ } catch {
4752
+ return new Proxy(context, {
4753
+ get(target, property) {
4754
+ if (property === "environment") return environment;
4755
+ const value = Reflect.get(target, property, target);
4756
+ return typeof value === "function" ? value.bind(target) : value;
4757
+ },
4758
+ set(target, property, value) {
4759
+ return Reflect.set(target, property, value, target);
4760
+ }
4761
+ });
4762
+ }
4763
+ }
4764
+ function finalizeEnvironmentResult(environment, result) {
4765
+ const metadata = environment.getBuildMetadata();
4766
+ const inferredEntries = inferEnvironmentEntries(result.output);
4767
+ const entries = {
4768
+ ...inferredEntries,
4769
+ ...metadata.entries,
4770
+ ...result.entries
4771
+ };
4772
+ const normalizedEntries = Object.fromEntries(
4773
+ Object.entries(entries).map(([name, fileName]) => {
4774
+ const normalized = normalizeEnvironmentFileName(fileName);
4775
+ if (isInvalidEnvironmentFileName(normalized)) {
4776
+ throw new Error(
4777
+ `[nasti] environment "${environment.name}" returned invalid entry "${name}": ${fileName}`
4778
+ );
4779
+ }
4780
+ return [name, normalized];
4781
+ })
4782
+ );
4783
+ return {
4784
+ ...metadata,
4785
+ ...result,
4786
+ output: result.output,
4787
+ ...Object.keys(normalizedEntries).length > 0 ? { entries: normalizedEntries } : {}
4788
+ };
4789
+ }
4790
+ function prepareBuildOutputDirectories(config, buildableNames) {
4791
+ const directories = /* @__PURE__ */ new Set();
4792
+ const protectedPaths = /* @__PURE__ */ new Set();
4793
+ const clientIsBuilt = buildableNames.includes("client");
4794
+ if (!clientIsBuilt && config.build.emptyOutDir) {
4795
+ directories.add(path13.resolve(config.root, config.build.outDir));
4796
+ }
4797
+ for (const name of buildableNames) {
4798
+ const environment = config.environments[name];
4799
+ const outDir = path13.resolve(config.root, environment.build.outDir);
4800
+ if (!environment.build.emptyOutDir) {
4801
+ protectedPaths.add(outDir);
4802
+ continue;
4803
+ }
4804
+ if (!environment.driver) directories.add(outDir);
4805
+ }
4806
+ const containsPath = (parent, child) => {
4807
+ const relative = path13.relative(parent, child);
4808
+ return relative === "" || !relative.startsWith("..") && !path13.isAbsolute(relative);
4809
+ };
4810
+ const roots = [...directories].filter(
4811
+ (directory) => ![...protectedPaths].some((protectedPath) => containsPath(directory, protectedPath))
4812
+ ).sort((a, b) => a.length - b.length).filter(
4813
+ (directory, index2, all) => !all.slice(0, index2).some((parent) => containsPath(parent, directory))
4814
+ );
4815
+ for (const directory of roots) {
4816
+ if (fs10.existsSync(directory)) fs10.rmSync(directory, { recursive: true, force: true });
4817
+ }
4818
+ }
4819
+ function assertDriverBuildResult(environment, result) {
4820
+ const output = result != null && typeof result === "object" ? result.output : void 0;
4821
+ const hasValidOutput = Array.isArray(output) && output.every(
4822
+ (artifact) => artifact != null && typeof artifact === "object" && typeof artifact.fileName === "string" && typeof artifact.type === "string"
4823
+ );
4824
+ if (!hasValidOutput) {
4825
+ throw new Error(
4826
+ `[nasti] environment "${environment.name}" driver "${environment.driver?.name}" returned an invalid build result; expected { output: EnvironmentBuildOutput[] }`
4827
+ );
4828
+ }
4829
+ }
4173
4830
  function resolveClientEntries(config, html) {
4174
4831
  const configuredEntries = config.environments.client?.entry ?? [];
4175
4832
  if (configuredEntries.length > 0) return configuredEntries;
4176
4833
  const entryPoints = [];
4177
4834
  const htmlFile = config.environments.client?.html;
4178
- const htmlDir = htmlFile ? path12.dirname(htmlFile) : config.root;
4835
+ const htmlDir = htmlFile ? path13.dirname(htmlFile) : config.root;
4179
4836
  if (html) {
4180
4837
  const scriptMatches = html.matchAll(/<script[^>]+src=["']([^"']+)["'][^>]*>/gi);
4181
4838
  for (const match of scriptMatches) {
@@ -4183,7 +4840,7 @@ function resolveClientEntries(config, html) {
4183
4840
  if (src && !src.startsWith("http")) {
4184
4841
  const cleanSrc = src.split(/[?#]/, 1)[0];
4185
4842
  entryPoints.push(
4186
- cleanSrc.startsWith("/") ? path12.resolve(config.root, cleanSrc.replace(/^\//, "")) : path12.resolve(htmlDir, cleanSrc)
4843
+ cleanSrc.startsWith("/") ? path13.resolve(config.root, cleanSrc.replace(/^\//, "")) : path13.resolve(htmlDir, cleanSrc)
4187
4844
  );
4188
4845
  }
4189
4846
  }
@@ -4191,8 +4848,8 @@ function resolveClientEntries(config, html) {
4191
4848
  if (entryPoints.length === 0) {
4192
4849
  const fallbackEntries = ["src/main.ts", "src/main.tsx", "src/main.js", "src/index.ts", "src/index.tsx", "src/index.js"];
4193
4850
  for (const entry of fallbackEntries) {
4194
- const fullPath = path12.resolve(config.root, entry);
4195
- if (fs9.existsSync(fullPath)) {
4851
+ const fullPath = path13.resolve(config.root, entry);
4852
+ if (fs10.existsSync(fullPath)) {
4196
4853
  entryPoints.push(fullPath);
4197
4854
  break;
4198
4855
  }
@@ -4220,16 +4877,20 @@ async function build(inlineConfig = {}) {
4220
4877
  const startTime = performance.now();
4221
4878
  logger.info(
4222
4879
  pc6.cyan(`
4223
- nasti v${"2.3.1"} `) + pc6.green(`building for ${config.mode}...`)
4880
+ nasti v${"2.4.0"} `) + pc6.green(`building for ${config.mode}...`)
4224
4881
  );
4225
4882
  debug5?.(`root: ${config.root}`);
4226
- const buildableNames = Object.keys(config.environments).filter(
4227
- (name) => name === "client" || config.environments[name].entry.length > 0 || !!config.environments[name].driver
4228
- );
4883
+ const buildableNames = Object.keys(config.environments).filter((name) => {
4884
+ const environment = config.environments[name];
4885
+ if (!environment.buildEnabled) return false;
4886
+ return name === "client" || environment.entry.length > 0 || !!environment.driver;
4887
+ });
4229
4888
  buildableNames.sort((a, b) => a === "client" ? -1 : b === "client" ? 1 : 0);
4889
+ prepareBuildOutputDirectories(config, buildableNames);
4230
4890
  const environments = {};
4231
4891
  const environmentResults = {};
4232
4892
  const initializedEnvironments = [];
4893
+ const buildAppContext = createBuildAppContext(config, environmentResults);
4233
4894
  let clientOutput = [];
4234
4895
  let buildFailed = false;
4235
4896
  try {
@@ -4245,7 +4906,7 @@ nasti v${"2.3.1"} `) + pc6.green(`building for ${config.mode}...`)
4245
4906
  }
4246
4907
  const pluginApi = getPluginApi(config);
4247
4908
  for (const plugin of config.plugins) {
4248
- await plugin.afterBuildApp?.(environmentResults, pluginApi);
4909
+ await plugin.afterBuildApp?.(environmentResults, pluginApi, buildAppContext);
4249
4910
  }
4250
4911
  } catch (error) {
4251
4912
  buildFailed = true;
@@ -4272,22 +4933,31 @@ nasti v${"2.3.1"} `) + pc6.green(`building for ${config.mode}...`)
4272
4933
  }
4273
4934
  }
4274
4935
  const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
4275
- const totalSize = Object.values(environments).flat().reduce((sum, chunk) => {
4936
+ const allOutput = [...Object.values(environments).flat(), ...buildAppContext.output];
4937
+ const totalSize = allOutput.reduce((sum, chunk) => {
4276
4938
  const content = chunk.type === "chunk" ? chunk.code : chunk.source;
4277
4939
  if (content == null) return sum;
4278
4940
  return sum + (typeof content === "string" ? Buffer.byteLength(content) : content.byteLength);
4279
4941
  }, 0);
4280
- const fileCount = Object.values(environments).flat().length;
4942
+ const fileCount = allOutput.length;
4281
4943
  const envSuffix = buildableNames.length > 1 ? ` (${buildableNames.join(" + ")})` : "";
4282
4944
  logger.info(pc6.green(`\u2713 built in ${elapsed}s`) + pc6.dim(envSuffix));
4283
4945
  logger.info(pc6.dim(` ${fileCount} files, ${displaySize(totalSize)} total \u2192 ${config.build.outDir}/`));
4284
- return { output: clientOutput, environments, environmentResults };
4946
+ return {
4947
+ output: clientOutput,
4948
+ environments,
4949
+ environmentResults,
4950
+ appOutput: [...buildAppContext.output]
4951
+ };
4285
4952
  }
4286
4953
  async function buildClientEnvironment(config) {
4287
4954
  const logger = config.logger;
4288
- const outDir = path12.resolve(config.root, config.build.outDir);
4955
+ const outDir = path13.resolve(config.root, config.build.outDir);
4289
4956
  const cssEngine = createCssEngine();
4290
- const pluginList = resolvePluginList(config, config.plugins, { cssEngine });
4957
+ const pluginList = resolvePluginList(config, config.plugins, {
4958
+ cssEngine,
4959
+ environmentName: "client"
4960
+ });
4291
4961
  const clientEnv = new NastiEnvironment("client", config, {
4292
4962
  mode: "build",
4293
4963
  plugins: pluginList,
@@ -4302,13 +4972,11 @@ async function buildClientEnvironment(config) {
4302
4972
  );
4303
4973
  }
4304
4974
  const result = await clientEnv.driver.build(clientEnv.getDriverContext());
4305
- return { environment: clientEnv, result };
4306
- }
4307
- if (config.build.emptyOutDir && fs9.existsSync(outDir)) {
4308
- fs9.rmSync(outDir, { recursive: true, force: true });
4975
+ assertDriverBuildResult(clientEnv, result);
4976
+ return { environment: clientEnv, result: finalizeEnvironmentResult(clientEnv, result) };
4309
4977
  }
4310
- fs9.mkdirSync(outDir, { recursive: true });
4311
- const htmlFile = config.environments.client.html ?? path12.resolve(config.root, "index.html");
4978
+ fs10.mkdirSync(outDir, { recursive: true });
4979
+ const htmlFile = config.environments.client.html ?? path13.resolve(config.root, "index.html");
4312
4980
  const html = await readHtmlFile(config.root, htmlFile);
4313
4981
  const entryPoints = resolveClientEntries(config, html);
4314
4982
  if (entryPoints.length === 0) {
@@ -4318,7 +4986,7 @@ async function buildClientEnvironment(config) {
4318
4986
  const nativeReporter = config.logLevel === "silent" ? null : await tryNativeReporterPlugin(config, logger);
4319
4987
  const rolldownPlugins = [
4320
4988
  createOxcTransformPlugin(config, clientEnv),
4321
- ...toRolldownPlugins(allPlugins),
4989
+ ...toRolldownPlugins(allPlugins, clientEnv),
4322
4990
  ...nativeReporter ? [nativeReporter] : []
4323
4991
  ];
4324
4992
  const { inputOptions, outputOptions } = getRolldownOptions(
@@ -4355,13 +5023,16 @@ async function buildClientEnvironment(config) {
4355
5023
  );
4356
5024
  }
4357
5025
  }
4358
- fs9.writeFileSync(path12.resolve(outDir, "index.html"), processedHtml);
5026
+ fs10.writeFileSync(path13.resolve(outDir, "index.html"), processedHtml);
4359
5027
  }
4360
5028
  if (!nativeReporter && config.logLevel !== "silent") {
4361
5029
  reportBuildOutput(output, config, logger);
4362
5030
  }
4363
5031
  warnLargeChunks(output, config, logger);
4364
- return { environment: clientEnv, result: { output } };
5032
+ return {
5033
+ environment: clientEnv,
5034
+ result: finalizeEnvironmentResult(clientEnv, { output })
5035
+ };
4365
5036
  } catch (error) {
4366
5037
  try {
4367
5038
  await clientEnv.close();
@@ -4377,7 +5048,10 @@ async function buildClientEnvironment(config) {
4377
5048
  async function buildServerEnvironment(config, name) {
4378
5049
  const envOptions = config.environments[name];
4379
5050
  const logger = config.logger;
4380
- const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
5051
+ const pluginList = resolvePluginList(config, config.plugins, {
5052
+ consumer: envOptions.consumer,
5053
+ environmentName: name
5054
+ });
4381
5055
  const environment = new NastiEnvironment(name, config, {
4382
5056
  mode: "build",
4383
5057
  plugins: pluginList,
@@ -4393,38 +5067,39 @@ async function buildServerEnvironment(config, name) {
4393
5067
  }
4394
5068
  try {
4395
5069
  const result = await environment.driver.build(environment.getDriverContext());
4396
- return { environment, result };
5070
+ assertDriverBuildResult(environment, result);
5071
+ return { environment, result: finalizeEnvironmentResult(environment, result) };
4397
5072
  } catch (error) {
4398
5073
  await environment.close();
4399
5074
  throw error;
4400
5075
  }
4401
5076
  }
4402
5077
  for (const entry of envOptions.entry) {
4403
- if (!fs9.existsSync(entry)) {
5078
+ if (!fs10.existsSync(entry)) {
4404
5079
  await environment.close();
4405
5080
  throw new Error(`[nasti] environment "${name}" entry not found: ${entry}`);
4406
5081
  }
4407
5082
  }
4408
5083
  const rolldownPlugins = [
4409
5084
  createOxcTransformPlugin(config, environment),
4410
- ...toRolldownPlugins(environment.plugins)
5085
+ ...toRolldownPlugins(environment.plugins, environment)
4411
5086
  ];
4412
5087
  const { inputOptions, outputOptions, outDir } = getRolldownOptions(
4413
5088
  environment,
4414
5089
  envOptions.entry,
4415
5090
  rolldownPlugins
4416
5091
  );
4417
- if (envOptions.build.emptyOutDir && fs9.existsSync(outDir)) {
4418
- fs9.rmSync(outDir, { recursive: true, force: true });
4419
- }
4420
- fs9.mkdirSync(outDir, { recursive: true });
5092
+ fs10.mkdirSync(outDir, { recursive: true });
4421
5093
  const bundle2 = await rolldown(inputOptions);
4422
5094
  const { output } = await bundle2.write(outputOptions);
4423
5095
  await bundle2.close();
4424
5096
  logger.info(
4425
- pc6.dim(` [${name}] `) + output.map((o) => path12.join(envOptions.build.outDir, o.fileName)).join(pc6.dim(", "))
5097
+ pc6.dim(` [${name}] `) + output.map((o) => path13.join(envOptions.build.outDir, o.fileName)).join(pc6.dim(", "))
4426
5098
  );
4427
- return { environment, result: { output } };
5099
+ return {
5100
+ environment,
5101
+ result: finalizeEnvironmentResult(environment, { output })
5102
+ };
4428
5103
  }
4429
5104
  function injectCssLinks(html, cssEngine, config) {
4430
5105
  const cssLinkTags = [];
@@ -4451,9 +5126,9 @@ function escapeRegExp(string) {
4451
5126
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4452
5127
  }
4453
5128
  function replaceEntryScript(html, facadeModuleId, fileName, config, htmlFile, urlPrefix) {
4454
- const rootRelative = path12.relative(config.root, facadeModuleId).split(path12.sep).join("/");
4455
- const resolvedHtmlFile = path12.resolve(config.root, htmlFile);
4456
- const htmlRelative = path12.relative(path12.dirname(resolvedHtmlFile), facadeModuleId).split(path12.sep).join("/");
5129
+ const rootRelative = path13.relative(config.root, facadeModuleId).split(path13.sep).join("/");
5130
+ const resolvedHtmlFile = path13.resolve(config.root, htmlFile);
5131
+ const htmlRelative = path13.relative(path13.dirname(resolvedHtmlFile), facadeModuleId).split(path13.sep).join("/");
4457
5132
  const candidates = /* @__PURE__ */ new Set([
4458
5133
  rootRelative,
4459
5134
  `/${rootRelative}`,
@@ -4483,6 +5158,7 @@ var init_build = __esm({
4483
5158
  init_reporter();
4484
5159
  init_debug();
4485
5160
  init_plugin_api();
5161
+ init_build_app_context();
4486
5162
  debug5 = createDebugger("nasti:build");
4487
5163
  NODE_BUILTINS2 = /* @__PURE__ */ new Set([...builtinModules2, ...builtinModules2.map((m) => `node:${m}`)]);
4488
5164
  }
@@ -4493,7 +5169,7 @@ var dev_engine_exports = {};
4493
5169
  __export(dev_engine_exports, {
4494
5170
  createBundledDevServer: () => createBundledDevServer
4495
5171
  });
4496
- import path13 from "path";
5172
+ import path14 from "path";
4497
5173
  import crypto3 from "crypto";
4498
5174
  import { WebSocketServer as WsServer2 } from "ws";
4499
5175
  import pc7 from "picocolors";
@@ -4529,7 +5205,7 @@ async function createBundledDevServer(opts) {
4529
5205
  createReactRefreshRuntimePlugin(entryPoints),
4530
5206
  createBundledOxcRefreshPlugin()
4531
5207
  ] : [],
4532
- ...stripCatchAllLoad(toRolldownPlugins(clientEnv.plugins)),
5208
+ ...stripCatchAllLoad(toRolldownPlugins(clientEnv.plugins, clientEnv)),
4533
5209
  ...useReactRefresh ? [
4534
5210
  refreshWrapperFn({
4535
5211
  cwd: config.root,
@@ -4578,7 +5254,7 @@ async function createBundledDevServer(opts) {
4578
5254
  }
4579
5255
  const url = `/${patchPath}`;
4580
5256
  logger.info(
4581
- pc7.green("hmr update ") + pc7.dim(changedFiles.map((f) => path13.relative(config.root, f)).join(", ")),
5257
+ pc7.green("hmr update ") + pc7.dim(changedFiles.map((f) => path14.relative(config.root, f)).join(", ")),
4582
5258
  { timestamp: true }
4583
5259
  );
4584
5260
  sendTo(clientId, { type: "hmr:update", path: url, url });
@@ -4714,7 +5390,7 @@ async function createBundledDevServer(opts) {
4714
5390
  return;
4715
5391
  }
4716
5392
  res.setHeader("ETag", hit.etag);
4717
- res.setHeader("Content-Type", MIME_TYPES[path13.extname(fileName)] ?? "application/octet-stream");
5393
+ res.setHeader("Content-Type", MIME_TYPES[path14.extname(fileName)] ?? "application/octet-stream");
4718
5394
  res.setHeader("Cache-Control", "no-cache");
4719
5395
  res.end(hit.content);
4720
5396
  return;
@@ -4750,7 +5426,7 @@ function stripCatchAllLoad(plugins) {
4750
5426
  );
4751
5427
  }
4752
5428
  function createReactRefreshRuntimePlugin(entryPoints) {
4753
- const entryIds = new Set(entryPoints.map((p) => path13.resolve(p)));
5429
+ const entryIds = new Set(entryPoints.map((p) => path14.resolve(p)));
4754
5430
  return {
4755
5431
  name: "nasti:bundled-react-refresh",
4756
5432
  resolveId(source) {
@@ -4768,7 +5444,7 @@ function createReactRefreshRuntimePlugin(entryPoints) {
4768
5444
  return null;
4769
5445
  },
4770
5446
  transform(code, id) {
4771
- if (!entryIds.has(path13.resolve(id.split("?")[0]))) return null;
5447
+ if (!entryIds.has(path14.resolve(id.split("?")[0]))) return null;
4772
5448
  return { code: `import ${JSON.stringify(PREAMBLE_SPEC)};
4773
5449
  ${code}`, map: null };
4774
5450
  }
@@ -4939,7 +5615,7 @@ __export(server_exports, {
4939
5615
  createServer: () => createServer
4940
5616
  });
4941
5617
  import http from "http";
4942
- import path14 from "path";
5618
+ import path15 from "path";
4943
5619
  import os from "os";
4944
5620
  import connect from "connect";
4945
5621
  import sirv from "sirv";
@@ -4949,7 +5625,9 @@ async function createServer(inlineConfig = {}) {
4949
5625
  const startTime = performance.now();
4950
5626
  const config = await resolveConfig(inlineConfig, "serve");
4951
5627
  const logger = config.logger;
4952
- const allPlugins = resolvePluginList(config, config.plugins);
5628
+ const allPlugins = resolvePluginList(config, config.plugins, {
5629
+ environmentName: "client"
5630
+ });
4953
5631
  const configWithPlugins = { ...config, plugins: allPlugins };
4954
5632
  const app = connect();
4955
5633
  const httpServer = http.createServer(app);
@@ -4966,7 +5644,10 @@ async function createServer(inlineConfig = {}) {
4966
5644
  for (const name of Object.keys(config.environments)) {
4967
5645
  if (name === "client") continue;
4968
5646
  const consumer = config.environments[name].consumer;
4969
- const envPlugins = resolvePluginList(config, config.plugins, { consumer });
5647
+ const envPlugins = resolvePluginList(config, config.plugins, {
5648
+ consumer,
5649
+ environmentName: name
5650
+ });
4970
5651
  environments[name] = new NastiEnvironment(name, config, {
4971
5652
  mode: "dev",
4972
5653
  plugins: envPlugins,
@@ -5001,14 +5682,14 @@ async function createServer(inlineConfig = {}) {
5001
5682
  app.use(bundledServer.middleware);
5002
5683
  }
5003
5684
  const ignoredSegments = /* @__PURE__ */ new Set(["node_modules", ".git", ".nasti"]);
5004
- const outDirAbs = path14.resolve(config.root, config.build.outDir);
5685
+ const outDirAbs = path15.resolve(config.root, config.build.outDir);
5005
5686
  const watcher = watch(config.root, {
5006
5687
  ignored: (filePath) => {
5007
5688
  if (filePath === config.root) return false;
5008
- if (filePath === outDirAbs || filePath.startsWith(outDirAbs + path14.sep)) return true;
5009
- const rel = path14.relative(config.root, filePath);
5010
- if (!rel || rel.startsWith("..") || path14.isAbsolute(rel)) return false;
5011
- for (const seg of rel.split(path14.sep)) {
5689
+ if (filePath === outDirAbs || filePath.startsWith(outDirAbs + path15.sep)) return true;
5690
+ const rel = path15.relative(config.root, filePath);
5691
+ if (!rel || rel.startsWith("..") || path15.isAbsolute(rel)) return false;
5692
+ for (const seg of rel.split(path15.sep)) {
5012
5693
  if (ignoredSegments.has(seg)) return true;
5013
5694
  }
5014
5695
  return false;
@@ -5109,7 +5790,7 @@ async function createServer(inlineConfig = {}) {
5109
5790
  const readyIn = Math.ceil(performance.now() - startTime);
5110
5791
  logger.info(
5111
5792
  `
5112
- ${pc8.cyan(pc8.bold("NASTI"))} ${pc8.cyan(`v${"2.3.1"}`)} ${pc8.dim("ready in")} ${pc8.bold(readyIn)} ${pc8.dim("ms")}
5793
+ ${pc8.cyan(pc8.bold("NASTI"))} ${pc8.cyan(`v${"2.4.0"}`)} ${pc8.dim("ready in")} ${pc8.bold(readyIn)} ${pc8.dim("ms")}
5113
5794
  `
5114
5795
  );
5115
5796
  printServerUrls(
@@ -5137,7 +5818,12 @@ async function createServer(inlineConfig = {}) {
5137
5818
  },
5138
5819
  async transformRequest(url) {
5139
5820
  const { transformRequest: transformRequest2 } = await Promise.resolve().then(() => (init_middleware(), middleware_exports));
5140
- return transformRequest2(url, { config: configWithPlugins, pluginContainer, moduleGraph });
5821
+ return transformRequest2(url, {
5822
+ config: configWithPlugins,
5823
+ pluginContainer,
5824
+ moduleGraph,
5825
+ onPrune: (paths) => ws.send({ type: "prune", paths })
5826
+ });
5141
5827
  },
5142
5828
  async ssrLoadModule(url) {
5143
5829
  const runner = await getSsrRunner();
@@ -5197,9 +5883,10 @@ async function createServer(inlineConfig = {}) {
5197
5883
  app.use(transformMiddleware({
5198
5884
  config: configWithPlugins,
5199
5885
  pluginContainer,
5200
- moduleGraph
5886
+ moduleGraph,
5887
+ onPrune: (paths) => ws.send({ type: "prune", paths })
5201
5888
  }));
5202
- const publicDir = path14.resolve(config.root, "public");
5889
+ const publicDir = path15.resolve(config.root, "public");
5203
5890
  app.use(sirv(publicDir, { dev: true, etag: true }));
5204
5891
  app.use(sirv(config.root, { dev: true, etag: true }));
5205
5892
  const postMiddlewares = [];
@@ -5287,24 +5974,24 @@ __export(electron_exports, {
5287
5974
  detectInstalledElectron: () => detectInstalledElectron,
5288
5975
  normalizePreload: () => normalizePreload
5289
5976
  });
5290
- import path15 from "path";
5291
- import fs10 from "fs";
5977
+ import path16 from "path";
5978
+ import fs11 from "fs";
5292
5979
  import { rolldown as rolldown2 } from "rolldown";
5293
5980
  import pc9 from "picocolors";
5294
5981
  async function buildElectron(inlineConfig = {}) {
5295
5982
  const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
5296
5983
  const startTime = performance.now();
5297
5984
  assertElectronVersion(config);
5298
- console.log(pc9.cyan("\n\u26A1 nasti build (electron)") + pc9.dim(` v${"2.3.1"}`));
5985
+ console.log(pc9.cyan("\n\u26A1 nasti build (electron)") + pc9.dim(` v${"2.4.0"}`));
5299
5986
  console.log(pc9.dim(` root: ${config.root}`));
5300
5987
  console.log(pc9.dim(` mode: ${config.mode}`));
5301
5988
  console.log(pc9.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
5302
- const outDir = path15.resolve(config.root, config.build.outDir);
5303
- if (config.build.emptyOutDir && fs10.existsSync(outDir)) {
5304
- fs10.rmSync(outDir, { recursive: true, force: true });
5989
+ const outDir = path16.resolve(config.root, config.build.outDir);
5990
+ if (config.build.emptyOutDir && fs11.existsSync(outDir)) {
5991
+ fs11.rmSync(outDir, { recursive: true, force: true });
5305
5992
  }
5306
- fs10.mkdirSync(outDir, { recursive: true });
5307
- const rendererOutDir = path15.join(outDir, "renderer");
5993
+ fs11.mkdirSync(outDir, { recursive: true });
5994
+ const rendererOutDir = path16.join(outDir, "renderer");
5308
5995
  const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
5309
5996
  await build2(createElectronRendererConfig(config, inlineConfig, {
5310
5997
  build: {
@@ -5313,8 +6000,8 @@ async function buildElectron(inlineConfig = {}) {
5313
6000
  emptyOutDir: false
5314
6001
  }
5315
6002
  }));
5316
- const mainEntry = path15.resolve(config.root, config.electron.main);
5317
- if (!fs10.existsSync(mainEntry)) {
6003
+ const mainEntry = path16.resolve(config.root, config.electron.main);
6004
+ if (!fs11.existsSync(mainEntry)) {
5318
6005
  throw new Error(
5319
6006
  `Electron main entry not found: ${config.electron.main}
5320
6007
  \u5728 nasti.config.ts \u7684 electron.main \u6307\u5B9A\u4E3B\u8FDB\u7A0B\u5165\u53E3\u6587\u4EF6\u3002`
@@ -5328,11 +6015,11 @@ async function buildElectron(inlineConfig = {}) {
5328
6015
  const preloadEntries = normalizePreload(config.electron.preload, config.root);
5329
6016
  const preloadFiles = [];
5330
6017
  for (const entry of preloadEntries) {
5331
- if (!fs10.existsSync(entry)) {
6018
+ if (!fs11.existsSync(entry)) {
5332
6019
  console.warn(pc9.yellow(` \u26A0 preload entry not found, skipped: ${entry}`));
5333
6020
  continue;
5334
6021
  }
5335
- const base = path15.basename(entry).replace(/\.[^.]+$/, "");
6022
+ const base = path16.basename(entry).replace(/\.[^.]+$/, "");
5336
6023
  const out = outFileName(outDir, base, config.electron.preloadFormat);
5337
6024
  await bundleNode(config, entry, {
5338
6025
  outFile: out,
@@ -5344,10 +6031,10 @@ async function buildElectron(inlineConfig = {}) {
5344
6031
  const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
5345
6032
  console.log(pc9.green(`
5346
6033
  \u2713 Electron build complete in ${elapsed}s`));
5347
- console.log(pc9.dim(` renderer: ${path15.relative(config.root, rendererOutDir)}/`));
5348
- console.log(pc9.dim(` main: ${path15.relative(config.root, mainFile)}`));
6034
+ console.log(pc9.dim(` renderer: ${path16.relative(config.root, rendererOutDir)}/`));
6035
+ console.log(pc9.dim(` main: ${path16.relative(config.root, mainFile)}`));
5349
6036
  for (const pf of preloadFiles) {
5350
- console.log(pc9.dim(` preload: ${path15.relative(config.root, pf)}`));
6037
+ console.log(pc9.dim(` preload: ${path16.relative(config.root, pf)}`));
5351
6038
  }
5352
6039
  console.log();
5353
6040
  return { rendererOutDir, mainFile, preloadFiles };
@@ -5385,7 +6072,7 @@ async function bundleNode(config, entry, opts) {
5385
6072
  },
5386
6073
  plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
5387
6074
  });
5388
- fs10.mkdirSync(path15.dirname(opts.outFile), { recursive: true });
6075
+ fs11.mkdirSync(path16.dirname(opts.outFile), { recursive: true });
5389
6076
  await bundle2.write({
5390
6077
  sourcemap: !!config.build.sourcemap,
5391
6078
  minify: !!config.build.minify,
@@ -5396,7 +6083,7 @@ async function bundleNode(config, entry, opts) {
5396
6083
  codeSplitting: false
5397
6084
  });
5398
6085
  await bundle2.close();
5399
- console.log(pc9.dim(` \u2713 ${opts.label} \u2192 ${path15.relative(config.root, opts.outFile)}`));
6086
+ console.log(pc9.dim(` \u2713 ${opts.label} \u2192 ${path16.relative(config.root, opts.outFile)}`));
5400
6087
  return opts.outFile;
5401
6088
  }
5402
6089
  function createElectronRendererConfig(config, inlineConfig = {}, overrides = {}) {
@@ -5420,11 +6107,11 @@ function createElectronRendererConfig(config, inlineConfig = {}, overrides = {})
5420
6107
  }
5421
6108
  function outFileName(outDir, base, format) {
5422
6109
  const ext = format === "cjs" ? ".cjs" : ".mjs";
5423
- return path15.join(outDir, base + ext);
6110
+ return path16.join(outDir, base + ext);
5424
6111
  }
5425
6112
  function normalizePreload(preload, root) {
5426
6113
  const list = Array.isArray(preload) ? preload : preload ? [preload] : [];
5427
- return list.map((p) => path15.resolve(root, p));
6114
+ return list.map((p) => path16.resolve(root, p));
5428
6115
  }
5429
6116
  function assertElectronVersion(config) {
5430
6117
  const min = config.electron.minVersion;
@@ -5439,9 +6126,9 @@ function assertElectronVersion(config) {
5439
6126
  }
5440
6127
  function detectInstalledElectron(root) {
5441
6128
  try {
5442
- const pkgPath = path15.resolve(root, "node_modules/electron/package.json");
5443
- if (!fs10.existsSync(pkgPath)) return null;
5444
- const pkg = JSON.parse(fs10.readFileSync(pkgPath, "utf-8"));
6129
+ const pkgPath = path16.resolve(root, "node_modules/electron/package.json");
6130
+ if (!fs11.existsSync(pkgPath)) return null;
6131
+ const pkg = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
5445
6132
  const major = parseInt(String(pkg.version).split(".")[0], 10);
5446
6133
  return Number.isFinite(major) ? major : null;
5447
6134
  } catch {
@@ -5465,8 +6152,8 @@ __export(electron_dev_exports, {
5465
6152
  electronRendererDevPath: () => electronRendererDevPath,
5466
6153
  startElectronDev: () => startElectronDev
5467
6154
  });
5468
- import path16 from "path";
5469
- import fs11 from "fs";
6155
+ import path17 from "path";
6156
+ import fs12 from "fs";
5470
6157
  import { createRequire as createRequire5 } from "module";
5471
6158
  import { spawn } from "child_process";
5472
6159
  import chokidar from "chokidar";
@@ -5476,7 +6163,7 @@ async function startElectronDev(inlineConfig = {}) {
5476
6163
  const { noSpawn, ...rest } = inlineConfig;
5477
6164
  const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
5478
6165
  warnElectronVersion(config);
5479
- console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.3.1"}`));
6166
+ console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.4.0"}`));
5480
6167
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
5481
6168
  const server = await createServer2({
5482
6169
  ...rest,
@@ -5486,11 +6173,11 @@ async function startElectronDev(inlineConfig = {}) {
5486
6173
  await server.listen();
5487
6174
  const devUrl = `http://localhost:${server.config.server.port}` + electronRendererDevPath(config.electron.renderer);
5488
6175
  console.log(pc10.dim(` renderer: ${devUrl}`));
5489
- const stageDir = path16.resolve(config.root, ".nasti");
5490
- fs11.mkdirSync(stageDir, { recursive: true });
5491
- const mainEntry = path16.resolve(config.root, config.electron.main);
6176
+ const stageDir = path17.resolve(config.root, ".nasti");
6177
+ fs12.mkdirSync(stageDir, { recursive: true });
6178
+ const mainEntry = path17.resolve(config.root, config.electron.main);
5492
6179
  const preloadEntries = normalizePreload(config.electron.preload, config.root);
5493
- const builtMainFile = path16.join(stageDir, "main" + extFor(config.electron.mainFormat));
6180
+ const builtMainFile = path17.join(stageDir, "main" + extFor(config.electron.mainFormat));
5494
6181
  const builtPreloadFiles = [];
5495
6182
  const compileAll = async () => {
5496
6183
  await compileNode(config, mainEntry, {
@@ -5500,9 +6187,9 @@ async function startElectronDev(inlineConfig = {}) {
5500
6187
  });
5501
6188
  builtPreloadFiles.length = 0;
5502
6189
  for (const entry of preloadEntries) {
5503
- if (!fs11.existsSync(entry)) continue;
5504
- const base = path16.basename(entry).replace(/\.[^.]+$/, "");
5505
- const out = path16.join(stageDir, base + extFor(config.electron.preloadFormat));
6190
+ if (!fs12.existsSync(entry)) continue;
6191
+ const base = path17.basename(entry).replace(/\.[^.]+$/, "");
6192
+ const out = path17.join(stageDir, base + extFor(config.electron.preloadFormat));
5506
6193
  await compileNode(config, entry, {
5507
6194
  outFile: out,
5508
6195
  format: config.electron.preloadFormat,
@@ -5541,7 +6228,7 @@ async function startElectronDev(inlineConfig = {}) {
5541
6228
  };
5542
6229
  spawnElectron();
5543
6230
  if (config.electron.autoRestart) {
5544
- const watchTargets = [mainEntry, ...preloadEntries].filter(fs11.existsSync);
6231
+ const watchTargets = [mainEntry, ...preloadEntries].filter(fs12.existsSync);
5545
6232
  const watcher = chokidar.watch(watchTargets, { ignoreInitial: true });
5546
6233
  let restarting = null;
5547
6234
  let pending = false;
@@ -5621,7 +6308,7 @@ async function compileNode(config, entry, opts) {
5621
6308
  platform: "node",
5622
6309
  plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
5623
6310
  });
5624
- fs11.mkdirSync(path16.dirname(opts.outFile), { recursive: true });
6311
+ fs12.mkdirSync(path17.dirname(opts.outFile), { recursive: true });
5625
6312
  await bundle2.write({
5626
6313
  file: opts.outFile,
5627
6314
  format: opts.format === "cjs" ? "cjs" : "esm",
@@ -5634,18 +6321,18 @@ async function compileNode(config, entry, opts) {
5634
6321
  await bundle2.close();
5635
6322
  }
5636
6323
  function electronRendererDevPath(renderer) {
5637
- const normalized = renderer.split(path16.sep).join("/").replace(/^\.?\//, "");
6324
+ const normalized = renderer.split(path17.sep).join("/").replace(/^\.?\//, "");
5638
6325
  return normalized === "index.html" ? "/" : `/${normalized}`;
5639
6326
  }
5640
6327
  function resolveElectronBinary(config) {
5641
- if (config.electron.electronPath && fs11.existsSync(config.electron.electronPath)) {
6328
+ if (config.electron.electronPath && fs12.existsSync(config.electron.electronPath)) {
5642
6329
  return config.electron.electronPath;
5643
6330
  }
5644
6331
  try {
5645
- const require2 = createRequire5(path16.resolve(config.root, "package.json"));
6332
+ const require2 = createRequire5(path17.resolve(config.root, "package.json"));
5646
6333
  const pathFile = require2.resolve("electron");
5647
6334
  const electronModule = require2(pathFile);
5648
- if (typeof electronModule === "string" && fs11.existsSync(electronModule)) {
6335
+ if (typeof electronModule === "string" && fs12.existsSync(electronModule)) {
5649
6336
  return electronModule;
5650
6337
  }
5651
6338
  } catch {
@@ -5820,20 +6507,20 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
5820
6507
  const logger = createCliLogger(options);
5821
6508
  try {
5822
6509
  const http2 = await import("http");
5823
- const path17 = await import("path");
6510
+ const path18 = await import("path");
5824
6511
  const os2 = await import("os");
5825
6512
  const sirv2 = (await import("sirv")).default;
5826
6513
  const connect2 = (await import("connect")).default;
5827
6514
  const { printServerUrls: printServerUrls2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
5828
- const resolvedRoot = path17.resolve(root ?? ".");
5829
- const outDir = path17.resolve(resolvedRoot, options.outDir);
6515
+ const resolvedRoot = path18.resolve(root ?? ".");
6516
+ const outDir = path18.resolve(resolvedRoot, options.outDir);
5830
6517
  const app = connect2();
5831
6518
  app.use(sirv2(outDir, { single: true, etag: true, gzip: true, brotli: true }));
5832
6519
  const port = options.port;
5833
6520
  const host = options.host === true ? "0.0.0.0" : options.host ?? "localhost";
5834
6521
  http2.createServer(app).listen(port, host, () => {
5835
6522
  logger.info(`
5836
- ${pc11.cyan(pc11.bold("NASTI"))} ${pc11.cyan(`v${"2.3.1"}`)} ${pc11.dim("preview")}
6523
+ ${pc11.cyan(pc11.bold("NASTI"))} ${pc11.cyan(`v${"2.4.0"}`)} ${pc11.dim("preview")}
5837
6524
  `);
5838
6525
  printServerUrls2(
5839
6526
  {
@@ -5850,6 +6537,6 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
5850
6537
  }
5851
6538
  });
5852
6539
  cli.help();
5853
- cli.version("2.3.1");
6540
+ cli.version("2.4.0");
5854
6541
  cli.parse();
5855
6542
  //# sourceMappingURL=cli.js.map