@nasti-toolchain/nasti 1.2.1 → 1.2.3

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
@@ -113,6 +113,11 @@ function transformCode(filename, code, options = {}) {
113
113
  } : void 0,
114
114
  sourcemap: options.sourcemap ?? true
115
115
  });
116
+ if (result.errors && result.errors.length > 0) {
117
+ const msg = result.errors.map((e) => e.message ?? String(e)).join("\n");
118
+ throw new Error(`OXC transform failed for ${filename}:
119
+ ${msg}`);
120
+ }
116
121
  return {
117
122
  code: result.code,
118
123
  map: result.map ? JSON.stringify(result.map) : null
@@ -129,6 +134,68 @@ var init_transformer = __esm({
129
134
  }
130
135
  });
131
136
 
137
+ // src/core/env.ts
138
+ function loadEnv(mode, root, prefixes) {
139
+ const envFiles = [
140
+ ".env",
141
+ `.env.${mode}`,
142
+ ".env.local",
143
+ `.env.${mode}.local`
144
+ ];
145
+ const raw = {};
146
+ for (const file of envFiles) {
147
+ const filePath = import_node_path6.default.resolve(root, file);
148
+ if (!import_node_fs5.default.existsSync(filePath)) continue;
149
+ const content = import_node_fs5.default.readFileSync(filePath, "utf-8");
150
+ for (const line of content.split("\n")) {
151
+ const trimmed = line.trim();
152
+ if (!trimmed || trimmed.startsWith("#")) continue;
153
+ const eqIdx = trimmed.indexOf("=");
154
+ if (eqIdx === -1) continue;
155
+ const key = trimmed.slice(0, eqIdx).trim();
156
+ let value = trimmed.slice(eqIdx + 1).trim();
157
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
158
+ value = value.slice(1, -1);
159
+ }
160
+ raw[key] = value;
161
+ }
162
+ }
163
+ const filtered = {};
164
+ for (const [key, value] of Object.entries(raw)) {
165
+ if (prefixes.some((prefix) => key.startsWith(prefix))) {
166
+ filtered[key] = value;
167
+ }
168
+ }
169
+ return filtered;
170
+ }
171
+ function buildEnvDefine(env, mode) {
172
+ const define = {};
173
+ for (const [key, value] of Object.entries(env)) {
174
+ define[`import.meta.env.${key}`] = JSON.stringify(value);
175
+ }
176
+ define["import.meta.env.MODE"] = JSON.stringify(mode);
177
+ define["import.meta.env.DEV"] = mode !== "production" ? "true" : "false";
178
+ define["import.meta.env.PROD"] = mode === "production" ? "true" : "false";
179
+ define["import.meta.env.SSR"] = "false";
180
+ return define;
181
+ }
182
+ function replaceEnvInCode(code, define) {
183
+ let result = code;
184
+ for (const [key, value] of Object.entries(define)) {
185
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
186
+ result = result.replace(new RegExp(escaped, "g"), value);
187
+ }
188
+ return result;
189
+ }
190
+ var import_node_path6, import_node_fs5;
191
+ var init_env = __esm({
192
+ "src/core/env.ts"() {
193
+ "use strict";
194
+ import_node_path6 = __toESM(require("path"), 1);
195
+ import_node_fs5 = __toESM(require("fs"), 1);
196
+ }
197
+ });
198
+
132
199
  // src/server/middleware.ts
133
200
  var middleware_exports = {};
134
201
  __export(middleware_exports, {
@@ -138,6 +205,17 @@ __export(middleware_exports, {
138
205
  function transformMiddleware(ctx) {
139
206
  return async (req, res, next) => {
140
207
  const url = req.url ?? "/";
208
+ if (ctx.config.server.cors) {
209
+ const origin = req.headers.origin ?? "*";
210
+ res.setHeader("Access-Control-Allow-Origin", origin);
211
+ res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
212
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
213
+ if (req.method === "OPTIONS") {
214
+ res.statusCode = 204;
215
+ res.end();
216
+ return;
217
+ }
218
+ }
141
219
  if (req.method !== "GET") return next();
142
220
  if (url === "/@nasti/client") {
143
221
  res.setHeader("Content-Type", "application/javascript");
@@ -160,10 +238,12 @@ function transformMiddleware(ctx) {
160
238
  }
161
239
  }
162
240
  }
163
- processedHtml = processedHtml.replace(
164
- "<head>",
165
- '<head>\n <script type="module" src="/@nasti/client"></script>'
166
- );
241
+ if (ctx.config.server.hmr !== false) {
242
+ processedHtml = processedHtml.replace(
243
+ "<head>",
244
+ '<head>\n <script type="module" src="/@nasti/client"></script>'
245
+ );
246
+ }
167
247
  res.setHeader("Content-Type", "text/html");
168
248
  res.end(processedHtml);
169
249
  return;
@@ -196,10 +276,10 @@ async function transformRequest(url, ctx) {
196
276
  return cached.transformResult;
197
277
  }
198
278
  const filePath = resolveUrlToFile(url, config.root);
199
- if (!filePath || !import_node_fs6.default.existsSync(filePath)) return null;
279
+ if (!filePath || !import_node_fs7.default.existsSync(filePath)) return null;
200
280
  const mod = await moduleGraph.ensureEntryFromUrl(url);
201
281
  moduleGraph.registerModule(mod, filePath);
202
- let code = import_node_fs6.default.readFileSync(filePath, "utf-8");
282
+ let code = import_node_fs7.default.readFileSync(filePath, "utf-8");
203
283
  const pluginResult = await pluginContainer.transform(code, filePath);
204
284
  if (pluginResult) {
205
285
  code = typeof pluginResult === "string" ? pluginResult : pluginResult.code;
@@ -213,23 +293,31 @@ async function transformRequest(url, ctx) {
213
293
  });
214
294
  code = result.code;
215
295
  }
296
+ const env = loadEnv(config.mode, config.root, config.envPrefix);
297
+ const envDefine = buildEnvDefine(env, config.mode);
298
+ code = replaceEnvInCode(code, envDefine);
216
299
  code = rewriteImports(code, config);
217
300
  const transformResult = { code };
218
301
  mod.transformResult = transformResult;
219
302
  return transformResult;
220
303
  }
221
- function rewriteImports(code, config) {
304
+ function rewriteImports(code, _config) {
222
305
  return code.replace(
223
- /from\s+['"]([^'"./][^'"]*)['"]/g,
224
- (match, specifier) => {
225
- if (specifier.startsWith("/") || specifier.startsWith(".")) return match;
226
- return `from "/@modules/${specifier}"`;
306
+ /\bfrom\s+(['"])([^'"./][^'"]*)\1/g,
307
+ (match, quote, specifier) => {
308
+ return `from ${quote}/@modules/${specifier}${quote}`;
309
+ }
310
+ ).replace(
311
+ // 处理纯副作用导入: import 'bare-specifier'
312
+ /\bimport\s+(['"])([^'"./][^'"]*)\1/g,
313
+ (match, quote, specifier) => {
314
+ return `import ${quote}/@modules/${specifier}${quote}`;
227
315
  }
228
316
  ).replace(
229
- /import\s+['"]([^'"./][^'"]*)['"]/g,
230
- (match, specifier) => {
231
- if (specifier.startsWith("/") || specifier.startsWith(".")) return match;
232
- return `import "/@modules/${specifier}"`;
317
+ // 处理动态导入: import('bare-specifier')
318
+ /\bimport\s*\(\s*(['"])([^'"./][^'"]*)\1\s*\)/g,
319
+ (match, quote, specifier) => {
320
+ return `import(${quote}/@modules/${specifier}${quote})`;
233
321
  }
234
322
  );
235
323
  }
@@ -238,14 +326,13 @@ function resolveUrlToFile(url, root) {
238
326
  if (cleanUrl.startsWith("/@modules/")) {
239
327
  const moduleName = cleanUrl.slice("/@modules/".length);
240
328
  try {
241
- const { createRequire: createRequire2 } = require("module");
242
- const req = createRequire2(import_node_path7.default.resolve(root, "package.json"));
329
+ const req = (0, import_node_module2.createRequire)(import_node_path8.default.resolve(root, "package.json"));
243
330
  return req.resolve(moduleName);
244
331
  } catch {
245
332
  return null;
246
333
  }
247
334
  }
248
- return import_node_path7.default.resolve(root, cleanUrl.replace(/^\//, ""));
335
+ return import_node_path8.default.resolve(root, cleanUrl.replace(/^\//, ""));
249
336
  }
250
337
  function isModuleRequest(url) {
251
338
  const cleanUrl = url.split("?")[0];
@@ -307,7 +394,18 @@ function showErrorOverlay(err) {
307
394
  const overlay = document.createElement('div');
308
395
  overlay.id = 'nasti-error-overlay';
309
396
  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;';
310
- overlay.innerHTML = \`<h2 style="color:#ff5555">Build Error</h2><pre>\${err.message}\\n\${err.stack || ''}</pre><button onclick="this.parentElement.remove()" style="margin-top:1rem;padding:0.5rem 1rem;cursor:pointer">Close</button>\`;
397
+ const title = document.createElement('h2');
398
+ title.style.color = '#ff5555';
399
+ title.textContent = 'Build Error';
400
+ const pre = document.createElement('pre');
401
+ pre.textContent = err.message + '\\n' + (err.stack || '');
402
+ const btn = document.createElement('button');
403
+ btn.style.cssText = 'margin-top:1rem;padding:0.5rem 1rem;cursor:pointer';
404
+ btn.textContent = 'Close';
405
+ btn.onclick = () => overlay.remove();
406
+ overlay.appendChild(title);
407
+ overlay.appendChild(pre);
408
+ overlay.appendChild(btn);
311
409
  document.body.appendChild(overlay);
312
410
  }
313
411
 
@@ -338,14 +436,16 @@ if (!window.__nasti_hot_map) window.__nasti_hot_map = new Map();
338
436
  window.__NASTI_HMR__ = { createHotContext };
339
437
  `;
340
438
  }
341
- var import_node_path7, import_node_fs6;
439
+ var import_node_path8, import_node_fs7, import_node_module2;
342
440
  var init_middleware = __esm({
343
441
  "src/server/middleware.ts"() {
344
442
  "use strict";
345
- import_node_path7 = __toESM(require("path"), 1);
346
- import_node_fs6 = __toESM(require("fs"), 1);
443
+ import_node_path8 = __toESM(require("path"), 1);
444
+ import_node_fs7 = __toESM(require("fs"), 1);
445
+ import_node_module2 = require("module");
347
446
  init_transformer();
348
447
  init_html();
448
+ init_env();
349
449
  }
350
450
  });
351
451
 
@@ -403,6 +503,27 @@ var defaults = {
403
503
  };
404
504
 
405
505
  // src/config/index.ts
506
+ function loadTsconfigPaths(root) {
507
+ const tsconfigPath = import_node_path.default.resolve(root, "tsconfig.json");
508
+ if (!import_node_fs.default.existsSync(tsconfigPath)) return {};
509
+ try {
510
+ const content = import_node_fs.default.readFileSync(tsconfigPath, "utf-8");
511
+ const stripped = content.replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
512
+ const tsconfig = JSON.parse(stripped);
513
+ const paths = tsconfig?.compilerOptions?.paths ?? {};
514
+ const baseUrl = tsconfig?.compilerOptions?.baseUrl ?? ".";
515
+ const alias = {};
516
+ for (const [pattern, targets] of Object.entries(paths)) {
517
+ if (!targets.length) continue;
518
+ const cleanKey = pattern.replace(/\/\*$/, "");
519
+ const cleanTarget = targets[0].replace(/\/\*$/, "");
520
+ alias[cleanKey] = import_node_path.default.resolve(root, baseUrl, cleanTarget);
521
+ }
522
+ return alias;
523
+ } catch {
524
+ return {};
525
+ }
526
+ }
406
527
  function defineConfig(config) {
407
528
  return config;
408
529
  }
@@ -454,11 +575,6 @@ async function resolveConfig(inlineConfig = {}, command) {
454
575
  if (result) Object.assign(merged, result);
455
576
  }
456
577
  }
457
- const filteredPlugins = rawPlugins.filter((p) => {
458
- if (!p.apply) return true;
459
- if (typeof p.apply === "function") return p.apply(resolved, env);
460
- return p.apply === command;
461
- });
462
578
  const resolved = {
463
579
  root,
464
580
  base: merged.base ?? defaults.base,
@@ -466,17 +582,24 @@ async function resolveConfig(inlineConfig = {}, command) {
466
582
  framework: merged.framework ?? defaults.framework,
467
583
  command,
468
584
  resolve: {
469
- alias: { ...defaults.resolve.alias, ...merged.resolve?.alias },
585
+ // tsconfig paths 优先级最低:tsconfig < defaults < user config
586
+ alias: { ...loadTsconfigPaths(root), ...defaults.resolve.alias, ...merged.resolve?.alias },
470
587
  extensions: merged.resolve?.extensions ?? defaults.resolve.extensions,
471
588
  conditions: merged.resolve?.conditions ?? defaults.resolve.conditions,
472
589
  mainFields: merged.resolve?.mainFields ?? defaults.resolve.mainFields
473
590
  },
474
- plugins: filteredPlugins,
591
+ plugins: [],
475
592
  server: { ...defaults.server, ...merged.server },
476
593
  build: { ...defaults.build, ...merged.build },
477
594
  envPrefix: Array.isArray(merged.envPrefix) ? merged.envPrefix : merged.envPrefix ? [merged.envPrefix] : [...defaults.envPrefix],
478
595
  logLevel: merged.logLevel ?? defaults.logLevel
479
596
  };
597
+ const filteredPlugins = rawPlugins.filter((p) => {
598
+ if (!p.apply) return true;
599
+ if (typeof p.apply === "function") return p.apply(resolved, env);
600
+ return p.apply === command;
601
+ });
602
+ resolved.plugins = filteredPlugins;
480
603
  for (const plugin of resolved.plugins) {
481
604
  if (plugin.configResolved) {
482
605
  await plugin.configResolved(resolved);
@@ -501,8 +624,8 @@ function deepMerge(target, source) {
501
624
  }
502
625
 
503
626
  // src/build/index.ts
504
- var import_node_path6 = __toESM(require("path"), 1);
505
- var import_node_fs5 = __toESM(require("fs"), 1);
627
+ var import_node_path7 = __toESM(require("path"), 1);
628
+ var import_node_fs6 = __toESM(require("fs"), 1);
506
629
  var import_rolldown = require("rolldown");
507
630
 
508
631
  // src/plugins/resolve.ts
@@ -548,10 +671,12 @@ function resolvePlugin(config) {
548
671
  return null;
549
672
  },
550
673
  load(id) {
551
- if (import_node_fs2.default.existsSync(id)) {
552
- return import_node_fs2.default.readFileSync(id, "utf-8");
674
+ if (!import_node_fs2.default.existsSync(id)) return null;
675
+ if (id.endsWith(".json")) {
676
+ const content = import_node_fs2.default.readFileSync(id, "utf-8");
677
+ return `export default ${content}`;
553
678
  }
554
- return null;
679
+ return import_node_fs2.default.readFileSync(id, "utf-8");
555
680
  }
556
681
  };
557
682
  }
@@ -587,13 +712,17 @@ function cssPlugin(config) {
587
712
  },
588
713
  transform(code, id) {
589
714
  if (!id.endsWith(".css")) return null;
715
+ const rewritten = rewriteCssUrls(code, id, config.root);
590
716
  if (config.command === "serve") {
591
- const escaped = JSON.stringify(code);
717
+ const escaped = JSON.stringify(rewritten);
592
718
  return {
593
719
  code: `
594
720
  const css = ${escaped};
721
+ const __nasti_css_id__ = ${JSON.stringify(id)};
722
+ const __nasti_existing__ = document.querySelector('style[data-nasti-css=' + JSON.stringify(__nasti_css_id__) + ']');
723
+ if (__nasti_existing__) __nasti_existing__.remove();
595
724
  const style = document.createElement('style');
596
- style.setAttribute('data-nasti-css', ${JSON.stringify(id)});
725
+ style.setAttribute('data-nasti-css', __nasti_css_id__);
597
726
  style.textContent = css;
598
727
  document.head.appendChild(style);
599
728
 
@@ -609,10 +738,20 @@ export default css;
609
738
  `
610
739
  };
611
740
  }
612
- return null;
741
+ return rewritten !== code ? { code: rewritten } : null;
613
742
  }
614
743
  };
615
744
  }
745
+ function rewriteCssUrls(css, from, root) {
746
+ return css.replace(/url\(\s*['"]?([^'")\s]+)['"]?\s*\)/g, (match, url) => {
747
+ if (url.startsWith("/") || url.startsWith("data:") || url.startsWith("http")) {
748
+ return match;
749
+ }
750
+ const resolved = import_node_path3.default.resolve(import_node_path3.default.dirname(from), url);
751
+ const relative = "/" + import_node_path3.default.relative(root, resolved);
752
+ return `url(${relative})`;
753
+ });
754
+ }
616
755
 
617
756
  // src/plugins/assets.ts
618
757
  var import_node_path4 = __toESM(require("path"), 1);
@@ -681,6 +820,123 @@ function assetsPlugin(config) {
681
820
  // src/build/index.ts
682
821
  init_html();
683
822
  init_transformer();
823
+ init_env();
824
+
825
+ // src/core/plugin-container.ts
826
+ var PluginContainer = class {
827
+ plugins;
828
+ config;
829
+ ctx;
830
+ emittedFiles = /* @__PURE__ */ new Map();
831
+ constructor(config) {
832
+ this.config = config;
833
+ this.plugins = sortPlugins(config.plugins);
834
+ this.ctx = this.createContext();
835
+ }
836
+ createContext() {
837
+ const container = this;
838
+ return {
839
+ async resolve(source, importer) {
840
+ return container.resolveId(source, importer);
841
+ },
842
+ emitFile(file) {
843
+ const fileName = file.fileName ?? file.name ?? `asset-${container.emittedFiles.size}`;
844
+ const id = `emitted:${fileName}`;
845
+ container.emittedFiles.set(id, {
846
+ fileName,
847
+ source: file.source ?? ""
848
+ });
849
+ return id;
850
+ },
851
+ getModuleInfo(_id) {
852
+ return null;
853
+ }
854
+ };
855
+ }
856
+ /** 返回所有通过 emitFile() 输出的文件 */
857
+ getEmittedFiles() {
858
+ return Array.from(this.emittedFiles.values());
859
+ }
860
+ async buildStart() {
861
+ for (const plugin of this.plugins) {
862
+ if (plugin.buildStart) {
863
+ await plugin.buildStart.call(this.ctx);
864
+ }
865
+ }
866
+ }
867
+ async buildEnd(error) {
868
+ for (const plugin of this.plugins) {
869
+ if (plugin.buildEnd) {
870
+ await plugin.buildEnd.call(this.ctx, error);
871
+ }
872
+ }
873
+ }
874
+ async resolveId(source, importer, options = {}) {
875
+ for (const plugin of this.plugins) {
876
+ if (!plugin.resolveId) continue;
877
+ const result = await plugin.resolveId.call(
878
+ this.ctx,
879
+ source,
880
+ importer ?? void 0,
881
+ { isEntry: options.isEntry ?? false, ssr: false }
882
+ );
883
+ if (result != null) return result;
884
+ }
885
+ return null;
886
+ }
887
+ async load(id) {
888
+ for (const plugin of this.plugins) {
889
+ if (!plugin.load) continue;
890
+ const result = await plugin.load.call(this.ctx, id);
891
+ if (result != null) return result;
892
+ }
893
+ return null;
894
+ }
895
+ async transform(code, id) {
896
+ let currentCode = code;
897
+ for (const plugin of this.plugins) {
898
+ if (!plugin.transform) continue;
899
+ const result = await plugin.transform.call(this.ctx, currentCode, id);
900
+ if (result == null) continue;
901
+ if (typeof result === "string") {
902
+ currentCode = result;
903
+ } else {
904
+ currentCode = result.code;
905
+ }
906
+ }
907
+ return currentCode === code ? null : { code: currentCode };
908
+ }
909
+ /** 完整的模块处理管道: resolveId → load → transform */
910
+ async processModule(source, importer) {
911
+ const resolveResult = await this.resolveId(source, importer, {
912
+ isEntry: !importer
913
+ });
914
+ if (resolveResult == null) return null;
915
+ const id = typeof resolveResult === "string" ? resolveResult : resolveResult.id;
916
+ const loadResult = await this.load(id);
917
+ if (loadResult == null) return null;
918
+ const loadedCode = typeof loadResult === "string" ? loadResult : loadResult.code;
919
+ const transformResult = await this.transform(loadedCode, id);
920
+ const finalCode = transformResult == null ? loadedCode : typeof transformResult === "string" ? transformResult : transformResult.code;
921
+ return { id, code: finalCode };
922
+ }
923
+ getPlugins() {
924
+ return this.plugins;
925
+ }
926
+ };
927
+ function sortPlugins(plugins) {
928
+ const pre = [];
929
+ const normal = [];
930
+ const post = [];
931
+ for (const plugin of plugins) {
932
+ if (plugin.enforce === "pre") pre.push(plugin);
933
+ else if (plugin.enforce === "post") post.push(plugin);
934
+ else normal.push(plugin);
935
+ }
936
+ return [...pre, ...normal, ...post];
937
+ }
938
+
939
+ // src/build/index.ts
684
940
  var import_picocolors = __toESM(require("picocolors"), 1);
685
941
  async function build(inlineConfig = {}) {
686
942
  const config = await resolveConfig(inlineConfig, "build");
@@ -688,11 +944,11 @@ async function build(inlineConfig = {}) {
688
944
  console.log(import_picocolors.default.cyan("\n\u{1F528} nasti build") + import_picocolors.default.dim(` v${process.env.npm_package_version ?? "0.0.1"}`));
689
945
  console.log(import_picocolors.default.dim(` root: ${config.root}`));
690
946
  console.log(import_picocolors.default.dim(` mode: ${config.mode}`));
691
- const outDir = import_node_path6.default.resolve(config.root, config.build.outDir);
692
- if (config.build.emptyOutDir && import_node_fs5.default.existsSync(outDir)) {
693
- import_node_fs5.default.rmSync(outDir, { recursive: true, force: true });
947
+ const outDir = import_node_path7.default.resolve(config.root, config.build.outDir);
948
+ if (config.build.emptyOutDir && import_node_fs6.default.existsSync(outDir)) {
949
+ import_node_fs6.default.rmSync(outDir, { recursive: true, force: true });
694
950
  }
695
- import_node_fs5.default.mkdirSync(outDir, { recursive: true });
951
+ import_node_fs6.default.mkdirSync(outDir, { recursive: true });
696
952
  const html = await readHtmlFile(config.root);
697
953
  let entryPoints = [];
698
954
  if (html) {
@@ -700,15 +956,15 @@ async function build(inlineConfig = {}) {
700
956
  for (const match of scriptMatches) {
701
957
  const src = match[1];
702
958
  if (src && !src.startsWith("http")) {
703
- entryPoints.push(import_node_path6.default.resolve(config.root, src.replace(/^\//, "")));
959
+ entryPoints.push(import_node_path7.default.resolve(config.root, src.replace(/^\//, "")));
704
960
  }
705
961
  }
706
962
  }
707
963
  if (entryPoints.length === 0) {
708
964
  const fallbackEntries = ["src/main.ts", "src/main.tsx", "src/main.js", "src/index.ts", "src/index.tsx", "src/index.js"];
709
965
  for (const entry of fallbackEntries) {
710
- const fullPath = import_node_path6.default.resolve(config.root, entry);
711
- if (import_node_fs5.default.existsSync(fullPath)) {
966
+ const fullPath = import_node_path7.default.resolve(config.root, entry);
967
+ if (import_node_fs6.default.existsSync(fullPath)) {
712
968
  entryPoints.push(fullPath);
713
969
  break;
714
970
  }
@@ -723,6 +979,8 @@ async function build(inlineConfig = {}) {
723
979
  assetsPlugin(config)
724
980
  ];
725
981
  const allPlugins = [...builtinPlugins, ...config.plugins];
982
+ const pluginContainer = new PluginContainer(config);
983
+ await pluginContainer.buildStart();
726
984
  const oxcTransformPlugin = {
727
985
  name: "nasti:oxc-transform",
728
986
  transform(code, id) {
@@ -735,8 +993,11 @@ async function build(inlineConfig = {}) {
735
993
  return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
736
994
  }
737
995
  };
996
+ const env = loadEnv(config.mode, config.root, config.envPrefix);
997
+ const envDefine = buildEnvDefine(env, config.mode);
738
998
  const bundle = await (0, import_rolldown.rolldown)({
739
999
  input: entryPoints,
1000
+ define: envDefine,
740
1001
  plugins: [
741
1002
  oxcTransformPlugin,
742
1003
  // 转换 Nasti 插件为 Rolldown 插件格式
@@ -755,11 +1016,18 @@ async function build(inlineConfig = {}) {
755
1016
  dir: outDir,
756
1017
  format: "esm",
757
1018
  sourcemap: !!config.build.sourcemap,
1019
+ minify: !!config.build.minify,
758
1020
  entryFileNames: "assets/[name].[hash].js",
759
1021
  chunkFileNames: "assets/[name].[hash].js",
760
1022
  assetFileNames: "assets/[name].[hash][extname]"
761
1023
  });
762
1024
  await bundle.close();
1025
+ await pluginContainer.buildEnd();
1026
+ for (const ef of pluginContainer.getEmittedFiles()) {
1027
+ const dest = import_node_path7.default.resolve(outDir, ef.fileName);
1028
+ import_node_fs6.default.mkdirSync(import_node_path7.default.dirname(dest), { recursive: true });
1029
+ import_node_fs6.default.writeFileSync(dest, ef.source);
1030
+ }
763
1031
  if (html) {
764
1032
  let processedHtml = html;
765
1033
  const htmlPlugin_ = htmlPlugin(config);
@@ -774,15 +1042,15 @@ async function build(inlineConfig = {}) {
774
1042
  }
775
1043
  }
776
1044
  for (const chunk of output) {
777
- if (chunk.type === "chunk" && chunk.isEntry) {
778
- const originalEntry = import_node_path6.default.relative(config.root, entryPoints[0]);
1045
+ if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
1046
+ const originalEntry = import_node_path7.default.relative(config.root, chunk.facadeModuleId);
779
1047
  processedHtml = processedHtml.replace(
780
1048
  new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
781
1049
  `$1${config.base}${chunk.fileName}$3`
782
1050
  );
783
1051
  }
784
1052
  }
785
- import_node_fs5.default.writeFileSync(import_node_path6.default.resolve(outDir, "index.html"), processedHtml);
1053
+ import_node_fs6.default.writeFileSync(import_node_path7.default.resolve(outDir, "index.html"), processedHtml);
786
1054
  }
787
1055
  const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
788
1056
  const totalSize = output.reduce((sum, chunk) => {
@@ -807,115 +1075,13 @@ function escapeRegExp(string) {
807
1075
 
808
1076
  // src/server/index.ts
809
1077
  var import_node_http = __toESM(require("http"), 1);
810
- var import_node_path9 = __toESM(require("path"), 1);
1078
+ var import_node_path10 = __toESM(require("path"), 1);
1079
+ var import_node_os = __toESM(require("os"), 1);
811
1080
  var import_connect = __toESM(require("connect"), 1);
812
1081
  var import_sirv = __toESM(require("sirv"), 1);
813
1082
  var import_chokidar = require("chokidar");
814
1083
  var import_picocolors2 = __toESM(require("picocolors"), 1);
815
1084
 
816
- // src/core/plugin-container.ts
817
- var PluginContainer = class {
818
- plugins;
819
- config;
820
- ctx;
821
- constructor(config) {
822
- this.config = config;
823
- this.plugins = sortPlugins(config.plugins);
824
- this.ctx = this.createContext();
825
- }
826
- createContext() {
827
- const container = this;
828
- return {
829
- async resolve(source, importer) {
830
- return container.resolveId(source, importer);
831
- },
832
- emitFile(_file) {
833
- return "";
834
- },
835
- getModuleInfo(_id) {
836
- return null;
837
- }
838
- };
839
- }
840
- async buildStart() {
841
- for (const plugin of this.plugins) {
842
- if (plugin.buildStart) {
843
- await plugin.buildStart.call(this.ctx);
844
- }
845
- }
846
- }
847
- async buildEnd(error) {
848
- for (const plugin of this.plugins) {
849
- if (plugin.buildEnd) {
850
- await plugin.buildEnd.call(this.ctx, error);
851
- }
852
- }
853
- }
854
- async resolveId(source, importer, options = {}) {
855
- for (const plugin of this.plugins) {
856
- if (!plugin.resolveId) continue;
857
- const result = await plugin.resolveId.call(
858
- this.ctx,
859
- source,
860
- importer ?? void 0,
861
- { isEntry: options.isEntry ?? false, ssr: false }
862
- );
863
- if (result != null) return result;
864
- }
865
- return null;
866
- }
867
- async load(id) {
868
- for (const plugin of this.plugins) {
869
- if (!plugin.load) continue;
870
- const result = await plugin.load.call(this.ctx, id);
871
- if (result != null) return result;
872
- }
873
- return null;
874
- }
875
- async transform(code, id) {
876
- let currentCode = code;
877
- for (const plugin of this.plugins) {
878
- if (!plugin.transform) continue;
879
- const result = await plugin.transform.call(this.ctx, currentCode, id);
880
- if (result == null) continue;
881
- if (typeof result === "string") {
882
- currentCode = result;
883
- } else {
884
- currentCode = result.code;
885
- }
886
- }
887
- return currentCode === code ? null : { code: currentCode };
888
- }
889
- /** 完整的模块处理管道: resolveId → load → transform */
890
- async processModule(source, importer) {
891
- const resolveResult = await this.resolveId(source, importer, {
892
- isEntry: !importer
893
- });
894
- if (resolveResult == null) return null;
895
- const id = typeof resolveResult === "string" ? resolveResult : resolveResult.id;
896
- const loadResult = await this.load(id);
897
- if (loadResult == null) return null;
898
- const loadedCode = typeof loadResult === "string" ? loadResult : loadResult.code;
899
- const transformResult = await this.transform(loadedCode, id);
900
- const finalCode = transformResult == null ? loadedCode : typeof transformResult === "string" ? transformResult : transformResult.code;
901
- return { id, code: finalCode };
902
- }
903
- getPlugins() {
904
- return this.plugins;
905
- }
906
- };
907
- function sortPlugins(plugins) {
908
- const pre = [];
909
- const normal = [];
910
- const post = [];
911
- for (const plugin of plugins) {
912
- if (plugin.enforce === "pre") pre.push(plugin);
913
- else if (plugin.enforce === "post") post.push(plugin);
914
- else normal.push(plugin);
915
- }
916
- return [...pre, ...normal, ...post];
917
- }
918
-
919
1085
  // src/core/module-graph.ts
920
1086
  var ModuleGraph = class {
921
1087
  urlToModuleMap = /* @__PURE__ */ new Map();
@@ -1065,11 +1231,11 @@ function createWebSocketServer(server) {
1065
1231
  init_middleware();
1066
1232
 
1067
1233
  // src/server/hmr.ts
1068
- var import_node_path8 = __toESM(require("path"), 1);
1069
- var import_node_fs7 = __toESM(require("fs"), 1);
1234
+ var import_node_path9 = __toESM(require("path"), 1);
1235
+ var import_node_fs8 = __toESM(require("fs"), 1);
1070
1236
  async function handleFileChange(file, server) {
1071
1237
  const { moduleGraph, ws, config } = server;
1072
- const relativePath = "/" + import_node_path8.default.relative(config.root, file);
1238
+ const relativePath = "/" + import_node_path9.default.relative(config.root, file);
1073
1239
  const mods = moduleGraph.getModulesByFile(file);
1074
1240
  if (!mods || mods.size === 0) {
1075
1241
  return;
@@ -1082,7 +1248,7 @@ async function handleFileChange(file, server) {
1082
1248
  file,
1083
1249
  timestamp,
1084
1250
  modules: [mod],
1085
- read: () => import_node_fs7.default.readFileSync(file, "utf-8"),
1251
+ read: () => import_node_fs8.default.readFileSync(file, "utf-8"),
1086
1252
  server
1087
1253
  };
1088
1254
  let affectedModules = [mod];
@@ -1135,7 +1301,7 @@ async function createServer(inlineConfig = {}) {
1135
1301
  pluginContainer,
1136
1302
  moduleGraph
1137
1303
  }));
1138
- const publicDir = import_node_path9.default.resolve(config.root, "public");
1304
+ const publicDir = import_node_path10.default.resolve(config.root, "public");
1139
1305
  app.use((0, import_sirv.default)(publicDir, { dev: true, etag: true }));
1140
1306
  app.use((0, import_sirv.default)(config.root, { dev: true, etag: true }));
1141
1307
  const httpServer = import_node_http.default.createServer(app);
@@ -1212,8 +1378,7 @@ async function createServer(inlineConfig = {}) {
1212
1378
  return server;
1213
1379
  }
1214
1380
  function getNetworkAddress() {
1215
- const os = require("os");
1216
- const interfaces = os.networkInterfaces();
1381
+ const interfaces = import_node_os.default.networkInterfaces();
1217
1382
  for (const name of Object.keys(interfaces)) {
1218
1383
  for (const iface of interfaces[name] ?? []) {
1219
1384
  if (iface.family === "IPv4" && !iface.internal) {