@nasti-toolchain/nasti 1.4.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -183,6 +183,14 @@ async function resolveConfig(inlineConfig = {}, command) {
183
183
  return p.apply === command;
184
184
  });
185
185
  resolved.plugins = filteredPlugins;
186
+ if (resolved.target === "electron") {
187
+ const autoExternal = detectNativeDeps(root);
188
+ if (autoExternal.length > 0) {
189
+ const current = new Set(resolved.electron.external ?? []);
190
+ for (const dep of autoExternal) current.add(dep);
191
+ resolved.electron.external = [...current];
192
+ }
193
+ }
186
194
  for (const plugin of resolved.plugins) {
187
195
  if (plugin.configResolved) {
188
196
  await plugin.configResolved(resolved);
@@ -190,6 +198,73 @@ async function resolveConfig(inlineConfig = {}, command) {
190
198
  }
191
199
  return resolved;
192
200
  }
201
+ function detectNativeDeps(root) {
202
+ const result = /* @__PURE__ */ new Set();
203
+ const pkgJsonPath = import_node_path.default.resolve(root, "package.json");
204
+ if (!import_node_fs.default.existsSync(pkgJsonPath)) return [];
205
+ let pkg;
206
+ try {
207
+ pkg = JSON.parse(import_node_fs.default.readFileSync(pkgJsonPath, "utf-8"));
208
+ } catch {
209
+ return [];
210
+ }
211
+ const deps = {
212
+ ...pkg.dependencies ?? {},
213
+ ...pkg.optionalDependencies ?? {}
214
+ };
215
+ const KNOWN_NATIVE = /* @__PURE__ */ new Set([
216
+ "node-pty",
217
+ "better-sqlite3",
218
+ "sharp",
219
+ "serialport",
220
+ "@vscode/tree-sitter-wasm",
221
+ "keytar",
222
+ "nodegit",
223
+ "sqlite3",
224
+ "fsevents"
225
+ ]);
226
+ for (const dep of Object.keys(deps)) {
227
+ if (KNOWN_NATIVE.has(dep)) {
228
+ result.add(dep);
229
+ continue;
230
+ }
231
+ const depDir = import_node_path.default.resolve(root, "node_modules", dep);
232
+ if (!import_node_fs.default.existsSync(depDir)) continue;
233
+ if (import_node_fs.default.existsSync(import_node_path.default.join(depDir, "binding.gyp"))) {
234
+ result.add(dep);
235
+ continue;
236
+ }
237
+ const subPkg = import_node_path.default.join(depDir, "package.json");
238
+ if (import_node_fs.default.existsSync(subPkg)) {
239
+ try {
240
+ const sub = JSON.parse(import_node_fs.default.readFileSync(subPkg, "utf-8"));
241
+ if (sub.gypfile === true || sub.binary?.module_name) {
242
+ result.add(dep);
243
+ continue;
244
+ }
245
+ } catch {
246
+ }
247
+ }
248
+ if (hasDotNodeFile(depDir)) {
249
+ result.add(dep);
250
+ }
251
+ }
252
+ return [...result];
253
+ }
254
+ function hasDotNodeFile(dir, depth = 0) {
255
+ if (depth > 3) return false;
256
+ try {
257
+ const entries = import_node_fs.default.readdirSync(dir, { withFileTypes: true });
258
+ for (const e of entries) {
259
+ if (e.isFile() && e.name.endsWith(".node")) return true;
260
+ if (e.isDirectory() && (e.name === "build" || e.name === "prebuilds" || e.name === "Release")) {
261
+ if (hasDotNodeFile(import_node_path.default.join(dir, e.name), depth + 1)) return true;
262
+ }
263
+ }
264
+ } catch {
265
+ }
266
+ return false;
267
+ }
193
268
  function deepMerge(target, source) {
194
269
  const result = { ...target };
195
270
  for (const key of Object.keys(source)) {
@@ -437,6 +512,15 @@ function htmlPlugin(config) {
437
512
  transformIndexHtml(html) {
438
513
  const tags = [];
439
514
  if (config.command === "serve") {
515
+ const isReactLike = config.framework === "react" || config.framework === "auto";
516
+ if (isReactLike) {
517
+ tags.push({
518
+ tag: "script",
519
+ attrs: { type: "module" },
520
+ children: REACT_REFRESH_HTML_PREAMBLE,
521
+ injectTo: "head-prepend"
522
+ });
523
+ }
440
524
  tags.push({
441
525
  tag: "script",
442
526
  attrs: { type: "module", src: "/@nasti/client" },
@@ -487,12 +571,19 @@ async function readHtmlFile(root) {
487
571
  if (!import_node_fs4.default.existsSync(htmlPath)) return null;
488
572
  return import_node_fs4.default.readFileSync(htmlPath, "utf-8");
489
573
  }
490
- var import_node_path5, import_node_fs4;
574
+ var import_node_path5, import_node_fs4, REACT_REFRESH_HTML_PREAMBLE;
491
575
  var init_html = __esm({
492
576
  "src/plugins/html.ts"() {
493
577
  "use strict";
494
578
  import_node_path5 = __toESM(require("path"), 1);
495
579
  import_node_fs4 = __toESM(require("fs"), 1);
580
+ REACT_REFRESH_HTML_PREAMBLE = `
581
+ import RefreshRuntime from "/@react-refresh";
582
+ RefreshRuntime.injectIntoGlobalHook(window);
583
+ window.$RefreshReg$ = () => {};
584
+ window.$RefreshSig$ = () => (type) => type;
585
+ window.__vite_plugin_react_preamble_installed__ = true;
586
+ `.trim();
496
587
  }
497
588
  });
498
589
 
@@ -723,7 +814,7 @@ __export(build_exports, {
723
814
  async function build(inlineConfig = {}) {
724
815
  const config = await resolveConfig(inlineConfig, "build");
725
816
  const startTime = performance.now();
726
- console.log(import_picocolors.default.cyan("\n\u{1F528} nasti build") + import_picocolors.default.dim(` v${"1.4.1"}`));
817
+ console.log(import_picocolors.default.cyan("\n\u{1F528} nasti build") + import_picocolors.default.dim(` v${"1.5.1"}`));
727
818
  console.log(import_picocolors.default.dim(` root: ${config.root}`));
728
819
  console.log(import_picocolors.default.dim(` mode: ${config.mode}`));
729
820
  const outDir = import_node_path7.default.resolve(config.root, config.build.outDir);
@@ -1033,9 +1124,81 @@ var init_ws = __esm({
1033
1124
  // src/server/middleware.ts
1034
1125
  var middleware_exports = {};
1035
1126
  __export(middleware_exports, {
1127
+ REACT_REFRESH_GLOBAL_PREAMBLE: () => REACT_REFRESH_GLOBAL_PREAMBLE,
1036
1128
  transformMiddleware: () => transformMiddleware,
1037
1129
  transformRequest: () => transformRequest
1038
1130
  });
1131
+ function getReactRefreshRuntimeEsm() {
1132
+ if (__refreshRuntimeCache) return __refreshRuntimeCache;
1133
+ let cjsPath;
1134
+ try {
1135
+ cjsPath = __require.resolve("react-refresh/cjs/react-refresh-runtime.development.js");
1136
+ } catch {
1137
+ cjsPath = import_node_path9.default.resolve(__dirname_esm, "../../node_modules/react-refresh/cjs/react-refresh-runtime.development.js");
1138
+ }
1139
+ const cjsSource = import_node_fs8.default.readFileSync(cjsPath, "utf-8");
1140
+ __refreshRuntimeCache = `// Wrapped react-refresh runtime -> ESM
1141
+ const exports = {};
1142
+ const module = { exports };
1143
+ const process = { env: { NODE_ENV: 'development' } };
1144
+ ${cjsSource}
1145
+ const __rt = module.exports;
1146
+ export const injectIntoGlobalHook = __rt.injectIntoGlobalHook;
1147
+ export const register = __rt.register;
1148
+ export const createSignatureFunctionForTransform = __rt.createSignatureFunctionForTransform;
1149
+ export const performReactRefresh = __rt.performReactRefresh;
1150
+ export const isLikelyComponentType = __rt.isLikelyComponentType;
1151
+ export const hasUnrecoverableErrors = __rt.hasUnrecoverableErrors;
1152
+ export const setSignature = __rt.setSignature;
1153
+ export const getFamilyByID = __rt.getFamilyByID;
1154
+ export const getFamilyByType = __rt.getFamilyByType;
1155
+ export const findAffectedHostInstances = __rt.findAffectedHostInstances;
1156
+ export const collectCustomHooksForSignature = __rt.collectCustomHooksForSignature;
1157
+ export default __rt;
1158
+ `;
1159
+ return __refreshRuntimeCache;
1160
+ }
1161
+ function buildReactRefreshWrapper(moduleUrl, transformedCode) {
1162
+ const urlLit = JSON.stringify(moduleUrl);
1163
+ const userCode = transformedCode.replace(/\bimport\.meta\.hot\b/g, "__nasti_hot__");
1164
+ return `import * as RefreshRuntime from "/@react-refresh";
1165
+ import { createHotContext as __nasti_createHotContext__ } from "/@nasti/client";
1166
+ const __nasti_hot__ = __nasti_createHotContext__(${urlLit});
1167
+
1168
+ if (!window.__vite_plugin_react_preamble_installed__) {
1169
+ throw new Error("[nasti] React Fast Refresh preamble missing. Make sure nasti:html is wired with framework: 'react'.");
1170
+ }
1171
+
1172
+ const prevRefreshReg = window.$RefreshReg$;
1173
+ const prevRefreshSig = window.$RefreshSig$;
1174
+ window.$RefreshReg$ = (type, id) => {
1175
+ RefreshRuntime.register(type, ${urlLit} + " " + id);
1176
+ };
1177
+ window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
1178
+
1179
+ ${userCode}
1180
+
1181
+ window.$RefreshReg$ = prevRefreshReg;
1182
+ window.$RefreshSig$ = prevRefreshSig;
1183
+
1184
+ if (__nasti_hot__) {
1185
+ __nasti_hot__.accept(() => {
1186
+ clearTimeout(window.__nasti_refresh_timer__);
1187
+ window.__nasti_refresh_timer__ = setTimeout(() => {
1188
+ RefreshRuntime.performReactRefresh();
1189
+ }, 30);
1190
+ });
1191
+ }
1192
+ `;
1193
+ }
1194
+ function injectImportMetaHot(code, moduleUrl) {
1195
+ if (!/\bimport\.meta\.hot\b/.test(code)) return code;
1196
+ const urlLit = JSON.stringify(moduleUrl);
1197
+ const header = `import { createHotContext as __nasti_createHotContext__ } from "/@nasti/client";
1198
+ const __nasti_hot__ = __nasti_createHotContext__(${urlLit});
1199
+ `;
1200
+ return header + code.replace(/\bimport\.meta\.hot\b/g, "__nasti_hot__");
1201
+ }
1039
1202
  function transformMiddleware(ctx) {
1040
1203
  ctx.envDefine = buildEnvDefine(
1041
1204
  loadEnv(ctx.config.mode, ctx.config.root, ctx.config.envPrefix),
@@ -1109,7 +1272,7 @@ async function transformRequest(url, ctx) {
1109
1272
  return cached.transformResult;
1110
1273
  }
1111
1274
  if (cleanReqUrl === "/@react-refresh") {
1112
- return { code: REACT_REFRESH_RUNTIME };
1275
+ return { code: getReactRefreshRuntimeEsm() };
1113
1276
  }
1114
1277
  const filePath = resolveUrlToFile(url, config.root);
1115
1278
  if (!filePath || !import_node_fs8.default.existsSync(filePath)) return null;
@@ -1126,6 +1289,8 @@ async function transformRequest(url, ctx) {
1126
1289
  if (pluginResult) {
1127
1290
  code = typeof pluginResult === "string" ? pluginResult : pluginResult.code;
1128
1291
  }
1292
+ const stableUrl = cleanReqUrl;
1293
+ let wrappedWithRefresh = false;
1129
1294
  if (shouldTransform(filePath)) {
1130
1295
  const isJsx = /\.[jt]sx$/.test(filePath);
1131
1296
  const useRefresh = isJsx && config.framework !== "vue";
@@ -1137,9 +1302,14 @@ async function transformRequest(url, ctx) {
1137
1302
  });
1138
1303
  code = result.code;
1139
1304
  if (useRefresh) {
1140
- code = REACT_REFRESH_PREAMBLE + code + REACT_REFRESH_FOOTER;
1305
+ code = buildReactRefreshWrapper(stableUrl, code);
1306
+ wrappedWithRefresh = true;
1307
+ mod.isSelfAccepting = true;
1141
1308
  }
1142
1309
  }
1310
+ if (!wrappedWithRefresh) {
1311
+ code = injectImportMetaHot(code, stableUrl);
1312
+ }
1143
1313
  const envDefine = ctx.envDefine ?? buildEnvDefine(
1144
1314
  loadEnv(config.mode, config.root, config.envPrefix),
1145
1315
  config.mode
@@ -1207,8 +1377,8 @@ function rewriteExternalRequires(code) {
1207
1377
  }
1208
1378
  async function injectCjsNamedExports(code, entryFile) {
1209
1379
  try {
1210
- const { createRequire: createRequire3 } = await import("module");
1211
- const req = createRequire3(entryFile);
1380
+ const { createRequire: createRequire5 } = await import("module");
1381
+ const req = createRequire5(entryFile);
1212
1382
  const cjsExports = req(entryFile);
1213
1383
  if (!cjsExports || typeof cjsExports !== "object" && typeof cjsExports !== "function" || Array.isArray(cjsExports)) return code;
1214
1384
  const namedKeys = Object.keys(cjsExports).filter(
@@ -1371,12 +1541,15 @@ function getHmrClientCode() {
1371
1541
  // Nasti HMR Client
1372
1542
  const socket = new WebSocket(\`ws://\${location.host}\`, 'nasti-hmr');
1373
1543
  const hotModulesMap = new Map();
1544
+ const disposeMap = new Map();
1545
+ const pruneMap = new Map();
1374
1546
 
1375
1547
  socket.addEventListener('message', ({ data }) => {
1376
1548
  const payload = JSON.parse(data);
1377
1549
  switch (payload.type) {
1378
1550
  case 'connected':
1379
1551
  console.log('[nasti] connected.');
1552
+ clearErrorOverlay();
1380
1553
  break;
1381
1554
  case 'update':
1382
1555
  payload.updates.forEach((update) => {
@@ -1386,11 +1559,18 @@ socket.addEventListener('message', ({ data }) => {
1386
1559
  updateCss(update.path);
1387
1560
  }
1388
1561
  });
1562
+ clearErrorOverlay();
1389
1563
  break;
1390
1564
  case 'full-reload':
1391
1565
  console.log('[nasti] full reload');
1392
1566
  location.reload();
1393
1567
  break;
1568
+ case 'prune':
1569
+ payload.paths.forEach((p) => {
1570
+ const cb = pruneMap.get(p);
1571
+ if (cb) cb();
1572
+ });
1573
+ break;
1394
1574
  case 'error':
1395
1575
  console.error('[nasti] error:', payload.err.message);
1396
1576
  showErrorOverlay(payload.err);
@@ -1398,14 +1578,23 @@ socket.addEventListener('message', ({ data }) => {
1398
1578
  }
1399
1579
  });
1400
1580
 
1581
+ // \u81EA\u52A8\u91CD\u8FDE\uFF08\u65AD\u7EBF\u65F6\u6307\u6570\u9000\u907F\uFF09
1582
+ let reconnectTimer = 0;
1583
+ socket.addEventListener('close', () => {
1584
+ clearTimeout(reconnectTimer);
1585
+ reconnectTimer = setTimeout(() => location.reload(), 1000);
1586
+ });
1587
+
1401
1588
  async function fetchUpdate(update) {
1402
1589
  const mod = hotModulesMap.get(update.path);
1590
+ // \u5148\u8DD1 dispose\uFF08\u7ED9\u6A21\u5757\u673A\u4F1A\u6E05\u7406\u526F\u4F5C\u7528\uFF09
1591
+ const dispose = disposeMap.get(update.path);
1592
+ if (dispose) dispose();
1593
+
1594
+ const newMod = await import(update.acceptedPath + '?t=' + update.timestamp);
1403
1595
  if (mod) {
1404
- const newMod = await import(update.acceptedPath + '?t=' + update.timestamp);
1405
- mod.callbacks.forEach((cb) => cb(newMod));
1406
- } else {
1407
- // \u6CA1\u6709\u6CE8\u518C hot \u56DE\u8C03\uFF0C\u5C1D\u8BD5\u91CD\u65B0 import
1408
- await import(update.path + '?t=' + update.timestamp);
1596
+ // \u590D\u5236\u56DE\u8C03\u6570\u7EC4\u907F\u514D\u56DE\u8C03\u5185\u90E8\u53C8\u4FEE\u6539 hotModulesMap \u9020\u6210\u8FED\u4EE3\u5F02\u5E38
1597
+ [...mod.callbacks].forEach((cb) => cb(newMod));
1409
1598
  }
1410
1599
  }
1411
1600
 
@@ -1418,7 +1607,13 @@ function updateCss(path) {
1418
1607
  }
1419
1608
  }
1420
1609
 
1610
+ function clearErrorOverlay() {
1611
+ const el = document.getElementById('nasti-error-overlay');
1612
+ if (el) el.remove();
1613
+ }
1614
+
1421
1615
  function showErrorOverlay(err) {
1616
+ clearErrorOverlay();
1422
1617
  const overlay = document.createElement('div');
1423
1618
  overlay.id = 'nasti-error-overlay';
1424
1619
  overlay.style.cssText = 'position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,0.85);color:#fff;font-family:monospace;padding:2rem;overflow:auto;';
@@ -1437,61 +1632,60 @@ function showErrorOverlay(err) {
1437
1632
  document.body.appendChild(overlay);
1438
1633
  }
1439
1634
 
1440
- // import.meta.hot API
1441
- const createHotContext = (ownerPath) => ({
1442
- accept(deps, callback) {
1443
- if (typeof deps === 'function' || !deps) {
1444
- // self-accepting
1445
- const callbacks = hotModulesMap.get(ownerPath)?.callbacks || [];
1446
- callbacks.push(deps || (() => {}));
1447
- hotModulesMap.set(ownerPath, { callbacks });
1448
- }
1449
- },
1450
- prune(callback) {
1451
- // \u6A21\u5757\u88AB\u79FB\u9664\u65F6\u6267\u884C
1452
- },
1453
- dispose(callback) {
1454
- // \u6A21\u5757\u66F4\u65B0\u524D\u6267\u884C\u6E05\u7406
1455
- },
1456
- invalidate() {
1457
- location.reload();
1458
- },
1459
- data: {},
1460
- });
1461
-
1462
- // \u66B4\u9732\u7ED9\u6A21\u5757\u4F7F\u7528
1463
- if (!window.__nasti_hot_map) window.__nasti_hot_map = new Map();
1464
- window.__NASTI_HMR__ = { createHotContext };
1635
+ /**
1636
+ * \u751F\u6210 import.meta.hot \u7684 hot context\u3002
1637
+ * \u5173\u952E\u7EA6\u675F\uFF1A\u540C\u4E00 ownerPath \u7684 accept \u56DE\u8C03\u5FC5\u987B\u66FF\u6362\uFF08\u4E0D\u662F append\uFF09\u3002
1638
+ * \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
1639
+ * \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
1640
+ */
1641
+ export function createHotContext(ownerPath) {
1642
+ return {
1643
+ accept(deps, callback) {
1644
+ // \u81EA\u63A5\u53D7: hot.accept() \u6216 hot.accept(callback)
1645
+ if (typeof deps === 'function' || deps === undefined) {
1646
+ hotModulesMap.set(ownerPath, { callbacks: [deps || (() => {})] });
1647
+ return;
1648
+ }
1649
+ // \u4F9D\u8D56\u63A5\u53D7: hot.accept(deps, callback)\uFF0C\u591A\u6B21\u8C03\u7528\u8FFD\u52A0
1650
+ const existing = hotModulesMap.get(ownerPath)?.callbacks ?? [];
1651
+ hotModulesMap.set(ownerPath, { callbacks: [...existing, callback] });
1652
+ },
1653
+ prune(callback) {
1654
+ pruneMap.set(ownerPath, callback);
1655
+ },
1656
+ dispose(callback) {
1657
+ disposeMap.set(ownerPath, callback);
1658
+ },
1659
+ invalidate() {
1660
+ location.reload();
1661
+ },
1662
+ data: {},
1663
+ };
1664
+ }
1465
1665
  `;
1466
1666
  }
1467
- var import_node_path9, import_node_fs8, REACT_REFRESH_RUNTIME, REACT_REFRESH_PREAMBLE, REACT_REFRESH_FOOTER, esmBundleCache, VALID_IDENT, RESOLVE_EXTENSIONS, ESM_CONDITIONS;
1667
+ var import_node_path9, import_node_fs8, import_node_module3, import_node_url2, import_meta, __dirname_esm, __require, __refreshRuntimeCache, REACT_REFRESH_GLOBAL_PREAMBLE, esmBundleCache, VALID_IDENT, RESOLVE_EXTENSIONS, ESM_CONDITIONS;
1468
1668
  var init_middleware = __esm({
1469
1669
  "src/server/middleware.ts"() {
1470
1670
  "use strict";
1471
1671
  import_node_path9 = __toESM(require("path"), 1);
1472
1672
  import_node_fs8 = __toESM(require("fs"), 1);
1673
+ import_node_module3 = require("module");
1674
+ import_node_url2 = require("url");
1473
1675
  init_transformer();
1474
1676
  init_html();
1475
1677
  init_env();
1476
- REACT_REFRESH_RUNTIME = `
1477
- export function createSignatureFunctionForTransform() {
1478
- return function(type, key, forceReset, getCustomHooks) { return type; };
1479
- }
1480
- export function register(type, id) {}
1481
- export default { createSignatureFunctionForTransform, register };
1482
- `;
1483
- REACT_REFRESH_PREAMBLE = `
1484
- import RefreshRuntime from '/@react-refresh';
1485
- if (!window.$RefreshReg$) {
1486
- window.$RefreshReg$ = (type, id) => RefreshRuntime.register(type, import.meta.url + ' ' + id);
1487
- window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
1488
- }
1489
- `;
1490
- REACT_REFRESH_FOOTER = `
1491
- if (import.meta.hot) {
1492
- import.meta.hot.accept();
1493
- }
1494
- `;
1678
+ import_meta = {};
1679
+ __dirname_esm = import_node_path9.default.dirname((0, import_node_url2.fileURLToPath)(import_meta.url));
1680
+ __require = (0, import_node_module3.createRequire)(import_meta.url);
1681
+ __refreshRuntimeCache = null;
1682
+ REACT_REFRESH_GLOBAL_PREAMBLE = `
1683
+ import RefreshRuntime from "/@react-refresh";
1684
+ RefreshRuntime.injectIntoGlobalHook(window);
1685
+ window.$RefreshReg$ = () => {};
1686
+ window.$RefreshSig$ = () => (type) => type;
1687
+ window.__vite_plugin_react_preamble_installed__ = true;
1688
+ `.trim();
1495
1689
  esmBundleCache = /* @__PURE__ */ new Map();
1496
1690
  VALID_IDENT = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
1497
1691
  RESOLVE_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".json", ".vue"];
@@ -1599,15 +1793,6 @@ async function createServer(inlineConfig = {}) {
1599
1793
  watcher.on("add", (file) => {
1600
1794
  handleFileChange(file, server);
1601
1795
  });
1602
- const postMiddlewares = [];
1603
- for (const plugin of allPlugins) {
1604
- if (plugin.configureServer) {
1605
- const result = await plugin.configureServer(server);
1606
- if (typeof result === "function") {
1607
- postMiddlewares.push(result);
1608
- }
1609
- }
1610
- }
1611
1796
  server = {
1612
1797
  config: configWithPlugins,
1613
1798
  middlewares: app,
@@ -1626,7 +1811,7 @@ async function createServer(inlineConfig = {}) {
1626
1811
  const localUrl = `http://localhost:${actualPort}`;
1627
1812
  const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${actualPort}` : null;
1628
1813
  console.log();
1629
- console.log(import_picocolors3.default.cyan(" nasti dev server") + import_picocolors3.default.dim(` v${"1.4.1"}`));
1814
+ console.log(import_picocolors3.default.cyan(" nasti dev server") + import_picocolors3.default.dim(` v${"1.5.1"}`));
1630
1815
  console.log();
1631
1816
  console.log(` ${import_picocolors3.default.green(">")} Local: ${import_picocolors3.default.cyan(localUrl)}`);
1632
1817
  if (networkUrl) {
@@ -1659,6 +1844,16 @@ async function createServer(inlineConfig = {}) {
1659
1844
  httpServer.close();
1660
1845
  }
1661
1846
  };
1847
+ const postMiddlewares = [];
1848
+ for (const plugin of allPlugins) {
1849
+ if (plugin.configureServer) {
1850
+ const result = await plugin.configureServer(server);
1851
+ if (typeof result === "function") {
1852
+ postMiddlewares.push(result);
1853
+ }
1854
+ }
1855
+ }
1856
+ for (const run of postMiddlewares) run();
1662
1857
  return server;
1663
1858
  }
1664
1859
  function getNetworkAddress() {
@@ -1704,6 +1899,7 @@ __export(src_exports, {
1704
1899
  createServer: () => createServer,
1705
1900
  defineConfig: () => defineConfig,
1706
1901
  electronPlugin: () => electronPlugin,
1902
+ monacoEditorPlugin: () => monacoEditorPlugin,
1707
1903
  resolveConfig: () => resolveConfig,
1708
1904
  startElectronDev: () => startElectronDev
1709
1905
  });
@@ -1759,7 +1955,7 @@ async function buildElectron(inlineConfig = {}) {
1759
1955
  const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
1760
1956
  const startTime = performance.now();
1761
1957
  assertElectronVersion(config);
1762
- console.log(import_picocolors2.default.cyan("\n\u26A1 nasti build (electron)") + import_picocolors2.default.dim(` v${"1.4.1"}`));
1958
+ console.log(import_picocolors2.default.cyan("\n\u26A1 nasti build (electron)") + import_picocolors2.default.dim(` v${"1.5.1"}`));
1763
1959
  console.log(import_picocolors2.default.dim(` root: ${config.root}`));
1764
1960
  console.log(import_picocolors2.default.dim(` mode: ${config.mode}`));
1765
1961
  console.log(import_picocolors2.default.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
@@ -1893,7 +2089,7 @@ init_server();
1893
2089
  // src/server/electron-dev.ts
1894
2090
  var import_node_path12 = __toESM(require("path"), 1);
1895
2091
  var import_node_fs10 = __toESM(require("fs"), 1);
1896
- var import_node_module3 = require("module");
2092
+ var import_node_module4 = require("module");
1897
2093
  var import_node_child_process = require("child_process");
1898
2094
  var import_chokidar2 = __toESM(require("chokidar"), 1);
1899
2095
  var import_picocolors4 = __toESM(require("picocolors"), 1);
@@ -1906,7 +2102,7 @@ async function startElectronDev(inlineConfig = {}) {
1906
2102
  const { noSpawn, ...rest } = inlineConfig;
1907
2103
  const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
1908
2104
  warnElectronVersion(config);
1909
- console.log(import_picocolors4.default.cyan("\n\u26A1 nasti electron dev") + import_picocolors4.default.dim(` v${"1.4.1"}`));
2105
+ console.log(import_picocolors4.default.cyan("\n\u26A1 nasti electron dev") + import_picocolors4.default.dim(` v${"1.5.1"}`));
1910
2106
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
1911
2107
  const server = await createServer2({ ...rest, target: "electron" });
1912
2108
  await server.listen();
@@ -1970,11 +2166,16 @@ async function startElectronDev(inlineConfig = {}) {
1970
2166
  const watchTargets = [mainEntry, ...preloadEntries].filter(import_node_fs10.default.existsSync);
1971
2167
  const watcher = import_chokidar2.default.watch(watchTargets, { ignoreInitial: true });
1972
2168
  let restarting = null;
1973
- watcher.on("all", async () => {
1974
- if (restarting) return;
2169
+ let pending = false;
2170
+ const restart = async () => {
2171
+ if (restarting) {
2172
+ pending = true;
2173
+ return;
2174
+ }
1975
2175
  restarting = (async () => {
1976
- console.log(import_picocolors4.default.cyan("\n \u267B \u4E3B/preload \u53D8\u66F4\uFF0C\u91CD\u542F Electron..."));
1977
- try {
2176
+ do {
2177
+ pending = false;
2178
+ console.log(import_picocolors4.default.cyan("\n \u267B \u4E3B/preload \u53D8\u66F4\uFF0C\u91CD\u542F Electron..."));
1978
2179
  if (child && !child.killed) {
1979
2180
  ;
1980
2181
  child.__nastiKilled = true;
@@ -1988,12 +2189,24 @@ async function startElectronDev(inlineConfig = {}) {
1988
2189
  dying.kill();
1989
2190
  });
1990
2191
  }
1991
- await compileAll();
1992
- spawnElectron();
1993
- } finally {
1994
- restarting = null;
1995
- }
2192
+ try {
2193
+ await compileAll();
2194
+ spawnElectron();
2195
+ } catch (e) {
2196
+ console.warn(import_picocolors4.default.yellow(` \u26A0 \u91CD\u542F\u7F16\u8BD1\u5931\u8D25\uFF0C\u4FDD\u7559\u4E0A\u4E00\u6B21\u8FDB\u7A0B: ${e.message}`));
2197
+ }
2198
+ } while (pending);
2199
+ restarting = null;
1996
2200
  })();
2201
+ return restarting;
2202
+ };
2203
+ let debounceTimer = null;
2204
+ watcher.on("all", () => {
2205
+ if (debounceTimer) clearTimeout(debounceTimer);
2206
+ debounceTimer = setTimeout(() => {
2207
+ debounceTimer = null;
2208
+ void restart();
2209
+ }, 80);
1997
2210
  });
1998
2211
  }
1999
2212
  }
@@ -2041,7 +2254,7 @@ function resolveElectronBinary(config) {
2041
2254
  return config.electron.electronPath;
2042
2255
  }
2043
2256
  try {
2044
- const require2 = (0, import_node_module3.createRequire)(import_node_path12.default.resolve(config.root, "package.json"));
2257
+ const require2 = (0, import_node_module4.createRequire)(import_node_path12.default.resolve(config.root, "package.json"));
2045
2258
  const pathFile = require2.resolve("electron");
2046
2259
  const electronModule = require2(pathFile);
2047
2260
  if (typeof electronModule === "string" && import_node_fs10.default.existsSync(electronModule)) {
@@ -2069,6 +2282,215 @@ function warnElectronVersion(config) {
2069
2282
  );
2070
2283
  }
2071
2284
  }
2285
+
2286
+ // src/plugins/monaco-editor.ts
2287
+ var import_node_path13 = __toESM(require("path"), 1);
2288
+ var import_node_fs11 = __toESM(require("fs"), 1);
2289
+ var import_node_crypto2 = __toESM(require("crypto"), 1);
2290
+ var import_node_module5 = require("module");
2291
+ var DEFAULT_WORKERS = {
2292
+ editorWorkerService: "monaco-editor/esm/vs/editor/editor.worker",
2293
+ css: "monaco-editor/esm/vs/language/css/css.worker",
2294
+ html: "monaco-editor/esm/vs/language/html/html.worker",
2295
+ json: "monaco-editor/esm/vs/language/json/json.worker",
2296
+ typescript: "monaco-editor/esm/vs/language/typescript/ts.worker"
2297
+ };
2298
+ var DEFAULT_PUBLIC_PATH = "monacoeditorwork";
2299
+ function isCDN(p) {
2300
+ return /^((https?:)?\/\/|file:)/.test(p);
2301
+ }
2302
+ function normalizePublicPath(p) {
2303
+ if (isCDN(p)) return p.replace(/\/+$/, "");
2304
+ const withLead = p.startsWith("/") ? p : "/" + p;
2305
+ return withLead.replace(/\/+$/, "") || "/";
2306
+ }
2307
+ function readMonacoVersion(root) {
2308
+ try {
2309
+ const require2 = (0, import_node_module5.createRequire)(import_node_path13.default.resolve(root, "package.json"));
2310
+ const pkgJsonPath = require2.resolve("monaco-editor/package.json", { paths: [root] });
2311
+ const pkg = JSON.parse(import_node_fs11.default.readFileSync(pkgJsonPath, "utf-8"));
2312
+ return typeof pkg.version === "string" ? pkg.version : "unknown";
2313
+ } catch {
2314
+ return "unknown";
2315
+ }
2316
+ }
2317
+ function monacoEditorPlugin(options = {}) {
2318
+ const languageWorkers = options.languageWorkers ?? Object.keys(DEFAULT_WORKERS);
2319
+ const customWorkers = options.customWorkers ?? [];
2320
+ const publicPath = normalizePublicPath(options.publicPath ?? DEFAULT_PUBLIC_PATH);
2321
+ const globalAPI = !!options.globalAPI;
2322
+ const forceBuildCDN = !!options.forceBuildCDN;
2323
+ const workers = [
2324
+ ...languageWorkers.map((label) => ({ label, entry: DEFAULT_WORKERS[label] })),
2325
+ ...customWorkers
2326
+ ];
2327
+ let resolvedConfig;
2328
+ let cacheDir = "";
2329
+ const building = /* @__PURE__ */ new Map();
2330
+ async function buildWorker(worker) {
2331
+ const cacheFile = import_node_path13.default.join(cacheDir, `${worker.label}.worker.js`);
2332
+ if (import_node_fs11.default.existsSync(cacheFile)) return cacheFile;
2333
+ const existing = building.get(worker.label);
2334
+ if (existing) return existing;
2335
+ const task = (async () => {
2336
+ const { rolldown: rolldown4 } = await import("rolldown");
2337
+ const require2 = (0, import_node_module5.createRequire)(import_node_path13.default.resolve(resolvedConfig.root, "package.json"));
2338
+ let entry;
2339
+ try {
2340
+ entry = require2.resolve(worker.entry, { paths: [resolvedConfig.root] });
2341
+ } catch {
2342
+ entry = require2.resolve(worker.entry + ".js", { paths: [resolvedConfig.root] });
2343
+ }
2344
+ import_node_fs11.default.mkdirSync(cacheDir, { recursive: true });
2345
+ const bundle = await rolldown4({
2346
+ input: entry,
2347
+ platform: "browser"
2348
+ });
2349
+ await bundle.write({
2350
+ file: cacheFile,
2351
+ format: "iife",
2352
+ sourcemap: false,
2353
+ minify: true,
2354
+ inlineDynamicImports: true
2355
+ });
2356
+ await bundle.close();
2357
+ return cacheFile;
2358
+ })();
2359
+ building.set(worker.label, task);
2360
+ try {
2361
+ return await task;
2362
+ } finally {
2363
+ building.delete(worker.label);
2364
+ }
2365
+ }
2366
+ function runtimeInitScript() {
2367
+ let normalizedPrefix = publicPath;
2368
+ if (!isCDN(publicPath)) {
2369
+ const base = resolvedConfig.base.replace(/\/+$/, "") || "";
2370
+ const pub = publicPath.replace(/^\/+/, "");
2371
+ normalizedPrefix = base ? `${base}/${pub}` : `/${pub}`;
2372
+ }
2373
+ const map = {};
2374
+ for (const w of workers) {
2375
+ map[w.label] = `${normalizedPrefix}/${w.label}.worker.js`;
2376
+ }
2377
+ return `;(function () {
2378
+ var map = ${JSON.stringify(map)};
2379
+ function getWorkerUrl(_moduleId, label) {
2380
+ var url = map[label] || map['editorWorkerService'];
2381
+ if (/^(https?:)?\\/\\//.test(url)) {
2382
+ // \u8DE8\u57DF Worker \u9700\u7528 Blob + importScripts \u7ED5\u8FC7\u540C\u6E90\u9650\u5236
2383
+ var blob = new Blob(
2384
+ ['importScripts(' + JSON.stringify(url) + ');'],
2385
+ { type: 'application/javascript' }
2386
+ );
2387
+ return URL.createObjectURL(blob);
2388
+ }
2389
+ return url;
2390
+ }
2391
+ self.MonacoEnvironment = self.MonacoEnvironment || {};
2392
+ if (!self.MonacoEnvironment.getWorker && !self.MonacoEnvironment.getWorkerUrl) {
2393
+ self.MonacoEnvironment.getWorkerUrl = getWorkerUrl;
2394
+ }
2395
+ })();`;
2396
+ }
2397
+ return {
2398
+ name: "nasti:monaco-editor",
2399
+ enforce: "pre",
2400
+ configResolved(config) {
2401
+ resolvedConfig = config;
2402
+ const version = readMonacoVersion(config.root);
2403
+ const key = import_node_crypto2.default.createHash("sha1").update(version + "|" + publicPath).digest("hex").slice(0, 8);
2404
+ cacheDir = import_node_path13.default.resolve(config.root, "node_modules/.nasti/monaco", key);
2405
+ },
2406
+ async configureServer(server) {
2407
+ const shouldBuild = !isCDN(publicPath) || forceBuildCDN;
2408
+ const watcher = server.watcher;
2409
+ const monacoDir = import_node_path13.default.resolve(resolvedConfig.root, "node_modules/monaco-editor");
2410
+ try {
2411
+ watcher?.unwatch?.(monacoDir);
2412
+ } catch {
2413
+ }
2414
+ if (!shouldBuild) return;
2415
+ void Promise.all(
2416
+ workers.map(
2417
+ (w) => buildWorker(w).catch((e) => {
2418
+ console.warn(
2419
+ `[nasti:monaco-editor] worker build failed for "${w.label}": ${e.message}`
2420
+ );
2421
+ })
2422
+ )
2423
+ );
2424
+ const base = resolvedConfig.base.replace(/\/+$/, "") || "";
2425
+ const pub = publicPath.replace(/^\/+/, "");
2426
+ const prefix = (base ? `${base}/${pub}` : `/${pub}`) + "/";
2427
+ server.middlewares.use(
2428
+ async (req, res, next) => {
2429
+ if (req.method !== "GET") return next();
2430
+ const url = (req.url ?? "").split("?")[0];
2431
+ if (!url.startsWith(prefix)) return next();
2432
+ const name = url.slice(prefix.length).replace(/\.worker\.js$/, "");
2433
+ const worker = workers.find((w) => w.label === name);
2434
+ if (!worker) return next();
2435
+ try {
2436
+ const file = await buildWorker(worker);
2437
+ res.setHeader("Content-Type", "application/javascript; charset=utf-8");
2438
+ res.setHeader("Cache-Control", "public, max-age=604800, immutable");
2439
+ import_node_fs11.default.createReadStream(file).pipe(res);
2440
+ } catch (e) {
2441
+ res.statusCode = 500;
2442
+ res.end(`Monaco worker build failed: ${e.message}`);
2443
+ }
2444
+ }
2445
+ );
2446
+ },
2447
+ transformIndexHtml(html) {
2448
+ const tags = [
2449
+ {
2450
+ tag: "script",
2451
+ children: runtimeInitScript(),
2452
+ injectTo: "head-prepend"
2453
+ }
2454
+ ];
2455
+ if (globalAPI) {
2456
+ tags.push({
2457
+ tag: "script",
2458
+ attrs: { type: "module" },
2459
+ children: `import * as monaco from 'monaco-editor';
2460
+ self.monaco = monaco;`,
2461
+ injectTo: "head"
2462
+ });
2463
+ }
2464
+ return { html, tags };
2465
+ },
2466
+ // 生产构建:把所有 Worker 预打包并拷到 outDir/<publicPath>/ 下
2467
+ async buildStart() {
2468
+ if (resolvedConfig.command !== "build") return;
2469
+ if (isCDN(publicPath) && !forceBuildCDN) return;
2470
+ const outDir = options.customDistPath ? options.customDistPath(
2471
+ resolvedConfig.root,
2472
+ resolvedConfig.build.outDir,
2473
+ resolvedConfig.base
2474
+ ) : isCDN(publicPath) ? import_node_path13.default.resolve(resolvedConfig.root, resolvedConfig.build.outDir, "monaco") : import_node_path13.default.resolve(
2475
+ resolvedConfig.root,
2476
+ resolvedConfig.build.outDir,
2477
+ publicPath.replace(/^\//, "")
2478
+ );
2479
+ import_node_fs11.default.mkdirSync(outDir, { recursive: true });
2480
+ for (const worker of workers) {
2481
+ try {
2482
+ const cacheFile = await buildWorker(worker);
2483
+ import_node_fs11.default.copyFileSync(cacheFile, import_node_path13.default.join(outDir, `${worker.label}.worker.js`));
2484
+ } catch (e) {
2485
+ throw new Error(
2486
+ `[nasti:monaco-editor] worker build failed for "${worker.label}": ${e.message}
2487
+ ${e.stack || ""}`
2488
+ );
2489
+ }
2490
+ }
2491
+ }
2492
+ };
2493
+ }
2072
2494
  // Annotate the CommonJS export names for ESM import in node:
2073
2495
  0 && (module.exports = {
2074
2496
  build,
@@ -2076,6 +2498,7 @@ function warnElectronVersion(config) {
2076
2498
  createServer,
2077
2499
  defineConfig,
2078
2500
  electronPlugin,
2501
+ monacoEditorPlugin,
2079
2502
  resolveConfig,
2080
2503
  startElectronDev
2081
2504
  });