@nasti-toolchain/nasti 1.1.0 → 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/cli.js CHANGED
@@ -1,11 +1,5 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
9
3
  var __esm = (fn, res) => function __init() {
10
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
5
  };
@@ -62,6 +56,27 @@ var init_defaults = __esm({
62
56
  import { pathToFileURL } from "url";
63
57
  import path from "path";
64
58
  import fs from "fs";
59
+ function loadTsconfigPaths(root) {
60
+ const tsconfigPath = path.resolve(root, "tsconfig.json");
61
+ if (!fs.existsSync(tsconfigPath)) return {};
62
+ try {
63
+ const content = fs.readFileSync(tsconfigPath, "utf-8");
64
+ const stripped = content.replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
65
+ const tsconfig = JSON.parse(stripped);
66
+ const paths = tsconfig?.compilerOptions?.paths ?? {};
67
+ const baseUrl = tsconfig?.compilerOptions?.baseUrl ?? ".";
68
+ const alias = {};
69
+ for (const [pattern, targets] of Object.entries(paths)) {
70
+ if (!targets.length) continue;
71
+ const cleanKey = pattern.replace(/\/\*$/, "");
72
+ const cleanTarget = targets[0].replace(/\/\*$/, "");
73
+ alias[cleanKey] = path.resolve(root, baseUrl, cleanTarget);
74
+ }
75
+ return alias;
76
+ } catch {
77
+ return {};
78
+ }
79
+ }
65
80
  async function loadConfigFromFile(root) {
66
81
  for (const file of CONFIG_FILES) {
67
82
  const filePath = path.resolve(root, file);
@@ -104,11 +119,6 @@ async function resolveConfig(inlineConfig = {}, command) {
104
119
  if (result) Object.assign(merged, result);
105
120
  }
106
121
  }
107
- const filteredPlugins = rawPlugins.filter((p) => {
108
- if (!p.apply) return true;
109
- if (typeof p.apply === "function") return p.apply(resolved, env);
110
- return p.apply === command;
111
- });
112
122
  const resolved = {
113
123
  root,
114
124
  base: merged.base ?? defaults.base,
@@ -116,17 +126,24 @@ async function resolveConfig(inlineConfig = {}, command) {
116
126
  framework: merged.framework ?? defaults.framework,
117
127
  command,
118
128
  resolve: {
119
- alias: { ...defaults.resolve.alias, ...merged.resolve?.alias },
129
+ // tsconfig paths 优先级最低:tsconfig < defaults < user config
130
+ alias: { ...loadTsconfigPaths(root), ...defaults.resolve.alias, ...merged.resolve?.alias },
120
131
  extensions: merged.resolve?.extensions ?? defaults.resolve.extensions,
121
132
  conditions: merged.resolve?.conditions ?? defaults.resolve.conditions,
122
133
  mainFields: merged.resolve?.mainFields ?? defaults.resolve.mainFields
123
134
  },
124
- plugins: filteredPlugins,
135
+ plugins: [],
125
136
  server: { ...defaults.server, ...merged.server },
126
137
  build: { ...defaults.build, ...merged.build },
127
138
  envPrefix: Array.isArray(merged.envPrefix) ? merged.envPrefix : merged.envPrefix ? [merged.envPrefix] : [...defaults.envPrefix],
128
139
  logLevel: merged.logLevel ?? defaults.logLevel
129
140
  };
141
+ const filteredPlugins = rawPlugins.filter((p) => {
142
+ if (!p.apply) return true;
143
+ if (typeof p.apply === "function") return p.apply(resolved, env);
144
+ return p.apply === command;
145
+ });
146
+ resolved.plugins = filteredPlugins;
130
147
  for (const plugin of resolved.plugins) {
131
148
  if (plugin.configResolved) {
132
149
  await plugin.configResolved(resolved);
@@ -183,6 +200,7 @@ var init_plugin_container = __esm({
183
200
  plugins;
184
201
  config;
185
202
  ctx;
203
+ emittedFiles = /* @__PURE__ */ new Map();
186
204
  constructor(config) {
187
205
  this.config = config;
188
206
  this.plugins = sortPlugins(config.plugins);
@@ -194,14 +212,24 @@ var init_plugin_container = __esm({
194
212
  async resolve(source, importer) {
195
213
  return container.resolveId(source, importer);
196
214
  },
197
- emitFile(_file) {
198
- return "";
215
+ emitFile(file) {
216
+ const fileName = file.fileName ?? file.name ?? `asset-${container.emittedFiles.size}`;
217
+ const id = `emitted:${fileName}`;
218
+ container.emittedFiles.set(id, {
219
+ fileName,
220
+ source: file.source ?? ""
221
+ });
222
+ return id;
199
223
  },
200
224
  getModuleInfo(_id) {
201
225
  return null;
202
226
  }
203
227
  };
204
228
  }
229
+ /** 返回所有通过 emitFile() 输出的文件 */
230
+ getEmittedFiles() {
231
+ return Array.from(this.emittedFiles.values());
232
+ }
205
233
  async buildStart() {
206
234
  for (const plugin of this.plugins) {
207
235
  if (plugin.buildStart) {
@@ -445,6 +473,11 @@ function transformCode(filename, code, options = {}) {
445
473
  } : void 0,
446
474
  sourcemap: options.sourcemap ?? true
447
475
  });
476
+ if (result.errors && result.errors.length > 0) {
477
+ const msg = result.errors.map((e) => e.message ?? String(e)).join("\n");
478
+ throw new Error(`OXC transform failed for ${filename}:
479
+ ${msg}`);
480
+ }
448
481
  return {
449
482
  code: result.code,
450
483
  map: result.map ? JSON.stringify(result.map) : null
@@ -526,17 +559,90 @@ var init_html = __esm({
526
559
  }
527
560
  });
528
561
 
562
+ // src/core/env.ts
563
+ import path3 from "path";
564
+ import fs3 from "fs";
565
+ function loadEnv(mode, root, prefixes) {
566
+ const envFiles = [
567
+ ".env",
568
+ `.env.${mode}`,
569
+ ".env.local",
570
+ `.env.${mode}.local`
571
+ ];
572
+ const raw = {};
573
+ for (const file of envFiles) {
574
+ const filePath = path3.resolve(root, file);
575
+ if (!fs3.existsSync(filePath)) continue;
576
+ const content = fs3.readFileSync(filePath, "utf-8");
577
+ for (const line of content.split("\n")) {
578
+ const trimmed = line.trim();
579
+ if (!trimmed || trimmed.startsWith("#")) continue;
580
+ const eqIdx = trimmed.indexOf("=");
581
+ if (eqIdx === -1) continue;
582
+ const key = trimmed.slice(0, eqIdx).trim();
583
+ let value = trimmed.slice(eqIdx + 1).trim();
584
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
585
+ value = value.slice(1, -1);
586
+ }
587
+ raw[key] = value;
588
+ }
589
+ }
590
+ const filtered = {};
591
+ for (const [key, value] of Object.entries(raw)) {
592
+ if (prefixes.some((prefix) => key.startsWith(prefix))) {
593
+ filtered[key] = value;
594
+ }
595
+ }
596
+ return filtered;
597
+ }
598
+ function buildEnvDefine(env, mode) {
599
+ const define = {};
600
+ for (const [key, value] of Object.entries(env)) {
601
+ define[`import.meta.env.${key}`] = JSON.stringify(value);
602
+ }
603
+ define["import.meta.env.MODE"] = JSON.stringify(mode);
604
+ define["import.meta.env.DEV"] = mode !== "production" ? "true" : "false";
605
+ define["import.meta.env.PROD"] = mode === "production" ? "true" : "false";
606
+ define["import.meta.env.SSR"] = "false";
607
+ return define;
608
+ }
609
+ function replaceEnvInCode(code, define) {
610
+ let result = code;
611
+ for (const [key, value] of Object.entries(define)) {
612
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
613
+ result = result.replace(new RegExp(escaped, "g"), value);
614
+ }
615
+ return result;
616
+ }
617
+ var init_env = __esm({
618
+ "src/core/env.ts"() {
619
+ "use strict";
620
+ }
621
+ });
622
+
529
623
  // src/server/middleware.ts
530
624
  var middleware_exports = {};
531
625
  __export(middleware_exports, {
532
626
  transformMiddleware: () => transformMiddleware,
533
627
  transformRequest: () => transformRequest
534
628
  });
535
- import path3 from "path";
536
- import fs3 from "fs";
629
+ import path4 from "path";
630
+ import fs4 from "fs";
631
+ import { createRequire } from "module";
537
632
  function transformMiddleware(ctx) {
538
633
  return async (req, res, next) => {
539
634
  const url = req.url ?? "/";
635
+ if (ctx.config.server.cors) {
636
+ const origin = req.headers.origin ?? "*";
637
+ res.setHeader("Access-Control-Allow-Origin", origin);
638
+ res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
639
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
640
+ if (req.method === "OPTIONS") {
641
+ res.statusCode = 204;
642
+ res.end();
643
+ return;
644
+ }
645
+ }
540
646
  if (req.method !== "GET") return next();
541
647
  if (url === "/@nasti/client") {
542
648
  res.setHeader("Content-Type", "application/javascript");
@@ -559,10 +665,12 @@ function transformMiddleware(ctx) {
559
665
  }
560
666
  }
561
667
  }
562
- processedHtml = processedHtml.replace(
563
- "<head>",
564
- '<head>\n <script type="module" src="/@nasti/client"></script>'
565
- );
668
+ if (ctx.config.server.hmr !== false) {
669
+ processedHtml = processedHtml.replace(
670
+ "<head>",
671
+ '<head>\n <script type="module" src="/@nasti/client"></script>'
672
+ );
673
+ }
566
674
  res.setHeader("Content-Type", "text/html");
567
675
  res.end(processedHtml);
568
676
  return;
@@ -595,10 +703,10 @@ async function transformRequest(url, ctx) {
595
703
  return cached.transformResult;
596
704
  }
597
705
  const filePath = resolveUrlToFile(url, config.root);
598
- if (!filePath || !fs3.existsSync(filePath)) return null;
706
+ if (!filePath || !fs4.existsSync(filePath)) return null;
599
707
  const mod = await moduleGraph.ensureEntryFromUrl(url);
600
708
  moduleGraph.registerModule(mod, filePath);
601
- let code = fs3.readFileSync(filePath, "utf-8");
709
+ let code = fs4.readFileSync(filePath, "utf-8");
602
710
  const pluginResult = await pluginContainer.transform(code, filePath);
603
711
  if (pluginResult) {
604
712
  code = typeof pluginResult === "string" ? pluginResult : pluginResult.code;
@@ -612,23 +720,31 @@ async function transformRequest(url, ctx) {
612
720
  });
613
721
  code = result.code;
614
722
  }
723
+ const env = loadEnv(config.mode, config.root, config.envPrefix);
724
+ const envDefine = buildEnvDefine(env, config.mode);
725
+ code = replaceEnvInCode(code, envDefine);
615
726
  code = rewriteImports(code, config);
616
727
  const transformResult = { code };
617
728
  mod.transformResult = transformResult;
618
729
  return transformResult;
619
730
  }
620
- function rewriteImports(code, config) {
731
+ function rewriteImports(code, _config) {
621
732
  return code.replace(
622
- /from\s+['"]([^'"./][^'"]*)['"]/g,
623
- (match, specifier) => {
624
- if (specifier.startsWith("/") || specifier.startsWith(".")) return match;
625
- return `from "/@modules/${specifier}"`;
733
+ /\bfrom\s+(['"])([^'"./][^'"]*)\1/g,
734
+ (match, quote, specifier) => {
735
+ return `from ${quote}/@modules/${specifier}${quote}`;
626
736
  }
627
737
  ).replace(
628
- /import\s+['"]([^'"./][^'"]*)['"]/g,
629
- (match, specifier) => {
630
- if (specifier.startsWith("/") || specifier.startsWith(".")) return match;
631
- return `import "/@modules/${specifier}"`;
738
+ // 处理纯副作用导入: import 'bare-specifier'
739
+ /\bimport\s+(['"])([^'"./][^'"]*)\1/g,
740
+ (match, quote, specifier) => {
741
+ return `import ${quote}/@modules/${specifier}${quote}`;
742
+ }
743
+ ).replace(
744
+ // 处理动态导入: import('bare-specifier')
745
+ /\bimport\s*\(\s*(['"])([^'"./][^'"]*)\1\s*\)/g,
746
+ (match, quote, specifier) => {
747
+ return `import(${quote}/@modules/${specifier}${quote})`;
632
748
  }
633
749
  );
634
750
  }
@@ -637,14 +753,13 @@ function resolveUrlToFile(url, root) {
637
753
  if (cleanUrl.startsWith("/@modules/")) {
638
754
  const moduleName = cleanUrl.slice("/@modules/".length);
639
755
  try {
640
- const { createRequire: createRequire2 } = __require("module");
641
- const req = createRequire2(path3.resolve(root, "package.json"));
756
+ const req = createRequire(path4.resolve(root, "package.json"));
642
757
  return req.resolve(moduleName);
643
758
  } catch {
644
759
  return null;
645
760
  }
646
761
  }
647
- return path3.resolve(root, cleanUrl.replace(/^\//, ""));
762
+ return path4.resolve(root, cleanUrl.replace(/^\//, ""));
648
763
  }
649
764
  function isModuleRequest(url) {
650
765
  const cleanUrl = url.split("?")[0];
@@ -706,7 +821,18 @@ function showErrorOverlay(err) {
706
821
  const overlay = document.createElement('div');
707
822
  overlay.id = 'nasti-error-overlay';
708
823
  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;';
709
- 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>\`;
824
+ const title = document.createElement('h2');
825
+ title.style.color = '#ff5555';
826
+ title.textContent = 'Build Error';
827
+ const pre = document.createElement('pre');
828
+ pre.textContent = err.message + '\\n' + (err.stack || '');
829
+ const btn = document.createElement('button');
830
+ btn.style.cssText = 'margin-top:1rem;padding:0.5rem 1rem;cursor:pointer';
831
+ btn.textContent = 'Close';
832
+ btn.onclick = () => overlay.remove();
833
+ overlay.appendChild(title);
834
+ overlay.appendChild(pre);
835
+ overlay.appendChild(btn);
710
836
  document.body.appendChild(overlay);
711
837
  }
712
838
 
@@ -742,15 +868,16 @@ var init_middleware = __esm({
742
868
  "use strict";
743
869
  init_transformer();
744
870
  init_html();
871
+ init_env();
745
872
  }
746
873
  });
747
874
 
748
875
  // src/server/hmr.ts
749
- import path4 from "path";
750
- import fs4 from "fs";
876
+ import path5 from "path";
877
+ import fs5 from "fs";
751
878
  async function handleFileChange(file, server) {
752
879
  const { moduleGraph, ws, config } = server;
753
- const relativePath = "/" + path4.relative(config.root, file);
880
+ const relativePath = "/" + path5.relative(config.root, file);
754
881
  const mods = moduleGraph.getModulesByFile(file);
755
882
  if (!mods || mods.size === 0) {
756
883
  return;
@@ -763,7 +890,7 @@ async function handleFileChange(file, server) {
763
890
  file,
764
891
  timestamp,
765
892
  modules: [mod],
766
- read: () => fs4.readFileSync(file, "utf-8"),
893
+ read: () => fs5.readFileSync(file, "utf-8"),
767
894
  server
768
895
  };
769
896
  let affectedModules = [mod];
@@ -802,12 +929,12 @@ var init_hmr = __esm({
802
929
  });
803
930
 
804
931
  // src/plugins/resolve.ts
805
- import path5 from "path";
806
- import fs5 from "fs";
807
- import { createRequire } from "module";
932
+ import path6 from "path";
933
+ import fs6 from "fs";
934
+ import { createRequire as createRequire2 } from "module";
808
935
  function resolvePlugin(config) {
809
936
  const { alias, extensions } = config.resolve;
810
- const require2 = createRequire(path5.resolve(config.root, "package.json"));
937
+ const require2 = createRequire2(path6.resolve(config.root, "package.json"));
811
938
  return {
812
939
  name: "nasti:resolve",
813
940
  enforce: "pre",
@@ -815,26 +942,26 @@ function resolvePlugin(config) {
815
942
  for (const [key, value] of Object.entries(alias)) {
816
943
  if (source === key || source.startsWith(key + "/")) {
817
944
  source = source.replace(key, value);
818
- if (!path5.isAbsolute(source)) {
819
- source = path5.resolve(config.root, source);
945
+ if (!path6.isAbsolute(source)) {
946
+ source = path6.resolve(config.root, source);
820
947
  }
821
948
  break;
822
949
  }
823
950
  }
824
- if (path5.isAbsolute(source)) {
951
+ if (path6.isAbsolute(source)) {
825
952
  const resolved = tryResolveFile(source, extensions);
826
953
  if (resolved) return resolved;
827
954
  }
828
955
  if (source.startsWith(".")) {
829
- const dir = importer ? path5.dirname(importer) : config.root;
830
- const absolute = path5.resolve(dir, source);
956
+ const dir = importer ? path6.dirname(importer) : config.root;
957
+ const absolute = path6.resolve(dir, source);
831
958
  const resolved = tryResolveFile(absolute, extensions);
832
959
  if (resolved) return resolved;
833
960
  }
834
961
  if (!source.startsWith("/") && !source.startsWith(".")) {
835
962
  try {
836
963
  const resolved = require2.resolve(source, {
837
- paths: [importer ? path5.dirname(importer) : config.root]
964
+ paths: [importer ? path6.dirname(importer) : config.root]
838
965
  });
839
966
  return resolved;
840
967
  } catch {
@@ -844,27 +971,29 @@ function resolvePlugin(config) {
844
971
  return null;
845
972
  },
846
973
  load(id) {
847
- if (fs5.existsSync(id)) {
848
- return fs5.readFileSync(id, "utf-8");
974
+ if (!fs6.existsSync(id)) return null;
975
+ if (id.endsWith(".json")) {
976
+ const content = fs6.readFileSync(id, "utf-8");
977
+ return `export default ${content}`;
849
978
  }
850
- return null;
979
+ return fs6.readFileSync(id, "utf-8");
851
980
  }
852
981
  };
853
982
  }
854
983
  function tryResolveFile(file, extensions) {
855
- if (fs5.existsSync(file) && fs5.statSync(file).isFile()) {
984
+ if (fs6.existsSync(file) && fs6.statSync(file).isFile()) {
856
985
  return file;
857
986
  }
858
987
  for (const ext of extensions) {
859
988
  const withExt = file + ext;
860
- if (fs5.existsSync(withExt) && fs5.statSync(withExt).isFile()) {
989
+ if (fs6.existsSync(withExt) && fs6.statSync(withExt).isFile()) {
861
990
  return withExt;
862
991
  }
863
992
  }
864
- if (fs5.existsSync(file) && fs5.statSync(file).isDirectory()) {
993
+ if (fs6.existsSync(file) && fs6.statSync(file).isDirectory()) {
865
994
  for (const ext of extensions) {
866
- const indexFile = path5.join(file, "index" + ext);
867
- if (fs5.existsSync(indexFile)) {
995
+ const indexFile = path6.join(file, "index" + ext);
996
+ if (fs6.existsSync(indexFile)) {
868
997
  return indexFile;
869
998
  }
870
999
  }
@@ -878,7 +1007,7 @@ var init_resolve = __esm({
878
1007
  });
879
1008
 
880
1009
  // src/plugins/css.ts
881
- import path6 from "path";
1010
+ import path7 from "path";
882
1011
  function cssPlugin(config) {
883
1012
  return {
884
1013
  name: "nasti:css",
@@ -888,13 +1017,17 @@ function cssPlugin(config) {
888
1017
  },
889
1018
  transform(code, id) {
890
1019
  if (!id.endsWith(".css")) return null;
1020
+ const rewritten = rewriteCssUrls(code, id, config.root);
891
1021
  if (config.command === "serve") {
892
- const escaped = JSON.stringify(code);
1022
+ const escaped = JSON.stringify(rewritten);
893
1023
  return {
894
1024
  code: `
895
1025
  const css = ${escaped};
1026
+ const __nasti_css_id__ = ${JSON.stringify(id)};
1027
+ const __nasti_existing__ = document.querySelector('style[data-nasti-css=' + JSON.stringify(__nasti_css_id__) + ']');
1028
+ if (__nasti_existing__) __nasti_existing__.remove();
896
1029
  const style = document.createElement('style');
897
- style.setAttribute('data-nasti-css', ${JSON.stringify(id)});
1030
+ style.setAttribute('data-nasti-css', __nasti_css_id__);
898
1031
  style.textContent = css;
899
1032
  document.head.appendChild(style);
900
1033
 
@@ -910,10 +1043,20 @@ export default css;
910
1043
  `
911
1044
  };
912
1045
  }
913
- return null;
1046
+ return rewritten !== code ? { code: rewritten } : null;
914
1047
  }
915
1048
  };
916
1049
  }
1050
+ function rewriteCssUrls(css, from, root) {
1051
+ return css.replace(/url\(\s*['"]?([^'")\s]+)['"]?\s*\)/g, (match, url) => {
1052
+ if (url.startsWith("/") || url.startsWith("data:") || url.startsWith("http")) {
1053
+ return match;
1054
+ }
1055
+ const resolved = path7.resolve(path7.dirname(from), url);
1056
+ const relative = "/" + path7.relative(root, resolved);
1057
+ return `url(${relative})`;
1058
+ });
1059
+ }
917
1060
  var init_css = __esm({
918
1061
  "src/plugins/css.ts"() {
919
1062
  "use strict";
@@ -921,8 +1064,8 @@ var init_css = __esm({
921
1064
  });
922
1065
 
923
1066
  // src/plugins/assets.ts
924
- import path7 from "path";
925
- import fs6 from "fs";
1067
+ import path8 from "path";
1068
+ import fs7 from "fs";
926
1069
  import crypto from "crypto";
927
1070
  function assetsPlugin(config) {
928
1071
  return {
@@ -934,24 +1077,24 @@ function assetsPlugin(config) {
934
1077
  return null;
935
1078
  },
936
1079
  load(id) {
937
- const ext = path7.extname(id.replace(/\?.*$/, ""));
1080
+ const ext = path8.extname(id.replace(/\?.*$/, ""));
938
1081
  if (id.endsWith("?raw")) {
939
1082
  const file = id.slice(0, -4);
940
- if (fs6.existsSync(file)) {
941
- const content = fs6.readFileSync(file, "utf-8");
1083
+ if (fs7.existsSync(file)) {
1084
+ const content = fs7.readFileSync(file, "utf-8");
942
1085
  return `export default ${JSON.stringify(content)}`;
943
1086
  }
944
1087
  }
945
1088
  if (id.endsWith("?url") || ASSET_EXTENSIONS.has(ext)) {
946
1089
  const file = id.replace(/\?.*$/, "");
947
- if (!fs6.existsSync(file)) return null;
1090
+ if (!fs7.existsSync(file)) return null;
948
1091
  if (config.command === "serve") {
949
- const url = "/" + path7.relative(config.root, file);
1092
+ const url = "/" + path8.relative(config.root, file);
950
1093
  return `export default ${JSON.stringify(url)}`;
951
1094
  }
952
- const content = fs6.readFileSync(file);
1095
+ const content = fs7.readFileSync(file);
953
1096
  const hash = crypto.createHash("sha256").update(content).digest("hex").slice(0, 8);
954
- const basename = path7.basename(file, ext);
1097
+ const basename = path8.basename(file, ext);
955
1098
  const hashedName = `${config.build.assetsDir}/${basename}.${hash}${ext}`;
956
1099
  return `export default ${JSON.stringify(config.base + hashedName)}`;
957
1100
  }
@@ -996,7 +1139,8 @@ __export(server_exports, {
996
1139
  createServer: () => createServer
997
1140
  });
998
1141
  import http from "http";
999
- import path8 from "path";
1142
+ import path9 from "path";
1143
+ import os from "os";
1000
1144
  import connect from "connect";
1001
1145
  import sirv from "sirv";
1002
1146
  import { watch } from "chokidar";
@@ -1019,7 +1163,7 @@ async function createServer(inlineConfig = {}) {
1019
1163
  pluginContainer,
1020
1164
  moduleGraph
1021
1165
  }));
1022
- const publicDir = path8.resolve(config.root, "public");
1166
+ const publicDir = path9.resolve(config.root, "public");
1023
1167
  app.use(sirv(publicDir, { dev: true, etag: true }));
1024
1168
  app.use(sirv(config.root, { dev: true, etag: true }));
1025
1169
  const httpServer = http.createServer(app);
@@ -1096,7 +1240,6 @@ async function createServer(inlineConfig = {}) {
1096
1240
  return server;
1097
1241
  }
1098
1242
  function getNetworkAddress() {
1099
- const os = __require("os");
1100
1243
  const interfaces = os.networkInterfaces();
1101
1244
  for (const name of Object.keys(interfaces)) {
1102
1245
  for (const iface of interfaces[name] ?? []) {
@@ -1128,8 +1271,8 @@ var build_exports = {};
1128
1271
  __export(build_exports, {
1129
1272
  build: () => build
1130
1273
  });
1131
- import path9 from "path";
1132
- import fs7 from "fs";
1274
+ import path10 from "path";
1275
+ import fs8 from "fs";
1133
1276
  import { rolldown } from "rolldown";
1134
1277
  import pc2 from "picocolors";
1135
1278
  async function build(inlineConfig = {}) {
@@ -1138,11 +1281,11 @@ async function build(inlineConfig = {}) {
1138
1281
  console.log(pc2.cyan("\n\u{1F528} nasti build") + pc2.dim(` v${process.env.npm_package_version ?? "0.0.1"}`));
1139
1282
  console.log(pc2.dim(` root: ${config.root}`));
1140
1283
  console.log(pc2.dim(` mode: ${config.mode}`));
1141
- const outDir = path9.resolve(config.root, config.build.outDir);
1142
- if (config.build.emptyOutDir && fs7.existsSync(outDir)) {
1143
- fs7.rmSync(outDir, { recursive: true, force: true });
1284
+ const outDir = path10.resolve(config.root, config.build.outDir);
1285
+ if (config.build.emptyOutDir && fs8.existsSync(outDir)) {
1286
+ fs8.rmSync(outDir, { recursive: true, force: true });
1144
1287
  }
1145
- fs7.mkdirSync(outDir, { recursive: true });
1288
+ fs8.mkdirSync(outDir, { recursive: true });
1146
1289
  const html = await readHtmlFile(config.root);
1147
1290
  let entryPoints = [];
1148
1291
  if (html) {
@@ -1150,15 +1293,15 @@ async function build(inlineConfig = {}) {
1150
1293
  for (const match of scriptMatches) {
1151
1294
  const src = match[1];
1152
1295
  if (src && !src.startsWith("http")) {
1153
- entryPoints.push(path9.resolve(config.root, src.replace(/^\//, "")));
1296
+ entryPoints.push(path10.resolve(config.root, src.replace(/^\//, "")));
1154
1297
  }
1155
1298
  }
1156
1299
  }
1157
1300
  if (entryPoints.length === 0) {
1158
1301
  const fallbackEntries = ["src/main.ts", "src/main.tsx", "src/main.js", "src/index.ts", "src/index.tsx", "src/index.js"];
1159
1302
  for (const entry of fallbackEntries) {
1160
- const fullPath = path9.resolve(config.root, entry);
1161
- if (fs7.existsSync(fullPath)) {
1303
+ const fullPath = path10.resolve(config.root, entry);
1304
+ if (fs8.existsSync(fullPath)) {
1162
1305
  entryPoints.push(fullPath);
1163
1306
  break;
1164
1307
  }
@@ -1173,6 +1316,8 @@ async function build(inlineConfig = {}) {
1173
1316
  assetsPlugin(config)
1174
1317
  ];
1175
1318
  const allPlugins = [...builtinPlugins, ...config.plugins];
1319
+ const pluginContainer = new PluginContainer(config);
1320
+ await pluginContainer.buildStart();
1176
1321
  const oxcTransformPlugin = {
1177
1322
  name: "nasti:oxc-transform",
1178
1323
  transform(code, id) {
@@ -1185,8 +1330,11 @@ async function build(inlineConfig = {}) {
1185
1330
  return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
1186
1331
  }
1187
1332
  };
1333
+ const env = loadEnv(config.mode, config.root, config.envPrefix);
1334
+ const envDefine = buildEnvDefine(env, config.mode);
1188
1335
  const bundle = await rolldown({
1189
1336
  input: entryPoints,
1337
+ define: envDefine,
1190
1338
  plugins: [
1191
1339
  oxcTransformPlugin,
1192
1340
  // 转换 Nasti 插件为 Rolldown 插件格式
@@ -1205,11 +1353,18 @@ async function build(inlineConfig = {}) {
1205
1353
  dir: outDir,
1206
1354
  format: "esm",
1207
1355
  sourcemap: !!config.build.sourcemap,
1356
+ minify: !!config.build.minify,
1208
1357
  entryFileNames: "assets/[name].[hash].js",
1209
1358
  chunkFileNames: "assets/[name].[hash].js",
1210
1359
  assetFileNames: "assets/[name].[hash][extname]"
1211
1360
  });
1212
1361
  await bundle.close();
1362
+ await pluginContainer.buildEnd();
1363
+ for (const ef of pluginContainer.getEmittedFiles()) {
1364
+ const dest = path10.resolve(outDir, ef.fileName);
1365
+ fs8.mkdirSync(path10.dirname(dest), { recursive: true });
1366
+ fs8.writeFileSync(dest, ef.source);
1367
+ }
1213
1368
  if (html) {
1214
1369
  let processedHtml = html;
1215
1370
  const htmlPlugin_ = htmlPlugin(config);
@@ -1224,15 +1379,15 @@ async function build(inlineConfig = {}) {
1224
1379
  }
1225
1380
  }
1226
1381
  for (const chunk of output) {
1227
- if (chunk.type === "chunk" && chunk.isEntry) {
1228
- const originalEntry = path9.relative(config.root, entryPoints[0]);
1382
+ if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
1383
+ const originalEntry = path10.relative(config.root, chunk.facadeModuleId);
1229
1384
  processedHtml = processedHtml.replace(
1230
1385
  new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
1231
1386
  `$1${config.base}${chunk.fileName}$3`
1232
1387
  );
1233
1388
  }
1234
1389
  }
1235
- fs7.writeFileSync(path9.resolve(outDir, "index.html"), processedHtml);
1390
+ fs8.writeFileSync(path10.resolve(outDir, "index.html"), processedHtml);
1236
1391
  }
1237
1392
  const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
1238
1393
  const totalSize = output.reduce((sum, chunk) => {
@@ -1263,6 +1418,8 @@ var init_build = __esm({
1263
1418
  init_assets();
1264
1419
  init_html();
1265
1420
  init_transformer();
1421
+ init_env();
1422
+ init_plugin_container();
1266
1423
  }
1267
1424
  });
1268
1425
 
@@ -1316,11 +1473,11 @@ cli.command("build [root]", "Build for production").option("--outDir <dir>", "Ou
1316
1473
  cli.command("preview [root]", "Preview production build").option("--port <port>", "Port number", { default: 4173 }).option("--host [host]", "Hostname").option("--outDir <dir>", "Output directory to serve", { default: "dist" }).action(async (root, options) => {
1317
1474
  try {
1318
1475
  const http2 = await import("http");
1319
- const path10 = await import("path");
1476
+ const path11 = await import("path");
1320
1477
  const sirv2 = (await import("sirv")).default;
1321
1478
  const connect2 = (await import("connect")).default;
1322
- const resolvedRoot = path10.resolve(root ?? ".");
1323
- const outDir = path10.resolve(resolvedRoot, options.outDir);
1479
+ const resolvedRoot = path11.resolve(root ?? ".");
1480
+ const outDir = path11.resolve(resolvedRoot, options.outDir);
1324
1481
  const app = connect2();
1325
1482
  app.use(sirv2(outDir, { single: true, etag: true, gzip: true, brotli: true }));
1326
1483
  const port = options.port;