@nasti-toolchain/nasti 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -74,6 +74,27 @@ var init_defaults = __esm({
74
74
  });
75
75
 
76
76
  // src/config/index.ts
77
+ function loadTsconfigPaths(root) {
78
+ const tsconfigPath = import_node_path.default.resolve(root, "tsconfig.json");
79
+ if (!import_node_fs.default.existsSync(tsconfigPath)) return {};
80
+ try {
81
+ const content = import_node_fs.default.readFileSync(tsconfigPath, "utf-8");
82
+ const stripped = content.replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
83
+ const tsconfig = JSON.parse(stripped);
84
+ const paths = tsconfig?.compilerOptions?.paths ?? {};
85
+ const baseUrl = tsconfig?.compilerOptions?.baseUrl ?? ".";
86
+ const alias = {};
87
+ for (const [pattern, targets] of Object.entries(paths)) {
88
+ if (!targets.length) continue;
89
+ const cleanKey = pattern.replace(/\/\*$/, "");
90
+ const cleanTarget = targets[0].replace(/\/\*$/, "");
91
+ alias[cleanKey] = import_node_path.default.resolve(root, baseUrl, cleanTarget);
92
+ }
93
+ return alias;
94
+ } catch {
95
+ return {};
96
+ }
97
+ }
77
98
  async function loadConfigFromFile(root) {
78
99
  for (const file of CONFIG_FILES) {
79
100
  const filePath = import_node_path.default.resolve(root, file);
@@ -116,11 +137,6 @@ async function resolveConfig(inlineConfig = {}, command) {
116
137
  if (result) Object.assign(merged, result);
117
138
  }
118
139
  }
119
- const filteredPlugins = rawPlugins.filter((p) => {
120
- if (!p.apply) return true;
121
- if (typeof p.apply === "function") return p.apply(resolved, env);
122
- return p.apply === command;
123
- });
124
140
  const resolved = {
125
141
  root,
126
142
  base: merged.base ?? defaults.base,
@@ -128,17 +144,24 @@ async function resolveConfig(inlineConfig = {}, command) {
128
144
  framework: merged.framework ?? defaults.framework,
129
145
  command,
130
146
  resolve: {
131
- alias: { ...defaults.resolve.alias, ...merged.resolve?.alias },
147
+ // tsconfig paths 优先级最低:tsconfig < defaults < user config
148
+ alias: { ...loadTsconfigPaths(root), ...defaults.resolve.alias, ...merged.resolve?.alias },
132
149
  extensions: merged.resolve?.extensions ?? defaults.resolve.extensions,
133
150
  conditions: merged.resolve?.conditions ?? defaults.resolve.conditions,
134
151
  mainFields: merged.resolve?.mainFields ?? defaults.resolve.mainFields
135
152
  },
136
- plugins: filteredPlugins,
153
+ plugins: [],
137
154
  server: { ...defaults.server, ...merged.server },
138
155
  build: { ...defaults.build, ...merged.build },
139
156
  envPrefix: Array.isArray(merged.envPrefix) ? merged.envPrefix : merged.envPrefix ? [merged.envPrefix] : [...defaults.envPrefix],
140
157
  logLevel: merged.logLevel ?? defaults.logLevel
141
158
  };
159
+ const filteredPlugins = rawPlugins.filter((p) => {
160
+ if (!p.apply) return true;
161
+ if (typeof p.apply === "function") return p.apply(resolved, env);
162
+ return p.apply === command;
163
+ });
164
+ resolved.plugins = filteredPlugins;
142
165
  for (const plugin of resolved.plugins) {
143
166
  if (plugin.configResolved) {
144
167
  await plugin.configResolved(resolved);
@@ -198,6 +221,7 @@ var init_plugin_container = __esm({
198
221
  plugins;
199
222
  config;
200
223
  ctx;
224
+ emittedFiles = /* @__PURE__ */ new Map();
201
225
  constructor(config) {
202
226
  this.config = config;
203
227
  this.plugins = sortPlugins(config.plugins);
@@ -209,14 +233,24 @@ var init_plugin_container = __esm({
209
233
  async resolve(source, importer) {
210
234
  return container.resolveId(source, importer);
211
235
  },
212
- emitFile(_file) {
213
- return "";
236
+ emitFile(file) {
237
+ const fileName = file.fileName ?? file.name ?? `asset-${container.emittedFiles.size}`;
238
+ const id = `emitted:${fileName}`;
239
+ container.emittedFiles.set(id, {
240
+ fileName,
241
+ source: file.source ?? ""
242
+ });
243
+ return id;
214
244
  },
215
245
  getModuleInfo(_id) {
216
246
  return null;
217
247
  }
218
248
  };
219
249
  }
250
+ /** 返回所有通过 emitFile() 输出的文件 */
251
+ getEmittedFiles() {
252
+ return Array.from(this.emittedFiles.values());
253
+ }
220
254
  async buildStart() {
221
255
  for (const plugin of this.plugins) {
222
256
  if (plugin.buildStart) {
@@ -460,6 +494,11 @@ function transformCode(filename, code, options = {}) {
460
494
  } : void 0,
461
495
  sourcemap: options.sourcemap ?? true
462
496
  });
497
+ if (result.errors && result.errors.length > 0) {
498
+ const msg = result.errors.map((e) => e.message ?? String(e)).join("\n");
499
+ throw new Error(`OXC transform failed for ${filename}:
500
+ ${msg}`);
501
+ }
463
502
  return {
464
503
  code: result.code,
465
504
  map: result.map ? JSON.stringify(result.map) : null
@@ -543,6 +582,68 @@ var init_html = __esm({
543
582
  }
544
583
  });
545
584
 
585
+ // src/core/env.ts
586
+ function loadEnv(mode, root, prefixes) {
587
+ const envFiles = [
588
+ ".env",
589
+ `.env.${mode}`,
590
+ ".env.local",
591
+ `.env.${mode}.local`
592
+ ];
593
+ const raw = {};
594
+ for (const file of envFiles) {
595
+ const filePath = import_node_path3.default.resolve(root, file);
596
+ if (!import_node_fs3.default.existsSync(filePath)) continue;
597
+ const content = import_node_fs3.default.readFileSync(filePath, "utf-8");
598
+ for (const line of content.split("\n")) {
599
+ const trimmed = line.trim();
600
+ if (!trimmed || trimmed.startsWith("#")) continue;
601
+ const eqIdx = trimmed.indexOf("=");
602
+ if (eqIdx === -1) continue;
603
+ const key = trimmed.slice(0, eqIdx).trim();
604
+ let value = trimmed.slice(eqIdx + 1).trim();
605
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
606
+ value = value.slice(1, -1);
607
+ }
608
+ raw[key] = value;
609
+ }
610
+ }
611
+ const filtered = {};
612
+ for (const [key, value] of Object.entries(raw)) {
613
+ if (prefixes.some((prefix) => key.startsWith(prefix))) {
614
+ filtered[key] = value;
615
+ }
616
+ }
617
+ return filtered;
618
+ }
619
+ function buildEnvDefine(env, mode) {
620
+ const define = {};
621
+ for (const [key, value] of Object.entries(env)) {
622
+ define[`import.meta.env.${key}`] = JSON.stringify(value);
623
+ }
624
+ define["import.meta.env.MODE"] = JSON.stringify(mode);
625
+ define["import.meta.env.DEV"] = mode !== "production" ? "true" : "false";
626
+ define["import.meta.env.PROD"] = mode === "production" ? "true" : "false";
627
+ define["import.meta.env.SSR"] = "false";
628
+ return define;
629
+ }
630
+ function replaceEnvInCode(code, define) {
631
+ let result = code;
632
+ for (const [key, value] of Object.entries(define)) {
633
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
634
+ result = result.replace(new RegExp(escaped, "g"), value);
635
+ }
636
+ return result;
637
+ }
638
+ var import_node_path3, import_node_fs3;
639
+ var init_env = __esm({
640
+ "src/core/env.ts"() {
641
+ "use strict";
642
+ import_node_path3 = __toESM(require("path"), 1);
643
+ import_node_fs3 = __toESM(require("fs"), 1);
644
+ }
645
+ });
646
+
546
647
  // src/server/middleware.ts
547
648
  var middleware_exports = {};
548
649
  __export(middleware_exports, {
@@ -550,8 +651,23 @@ __export(middleware_exports, {
550
651
  transformRequest: () => transformRequest
551
652
  });
552
653
  function transformMiddleware(ctx) {
654
+ ctx.envDefine = buildEnvDefine(
655
+ loadEnv(ctx.config.mode, ctx.config.root, ctx.config.envPrefix),
656
+ ctx.config.mode
657
+ );
553
658
  return async (req, res, next) => {
554
659
  const url = req.url ?? "/";
660
+ if (ctx.config.server.cors) {
661
+ const origin = req.headers.origin ?? "*";
662
+ res.setHeader("Access-Control-Allow-Origin", origin);
663
+ res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
664
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
665
+ if (req.method === "OPTIONS") {
666
+ res.statusCode = 204;
667
+ res.end();
668
+ return;
669
+ }
670
+ }
555
671
  if (req.method !== "GET") return next();
556
672
  if (url === "/@nasti/client") {
557
673
  res.setHeader("Content-Type", "application/javascript");
@@ -574,10 +690,6 @@ function transformMiddleware(ctx) {
574
690
  }
575
691
  }
576
692
  }
577
- processedHtml = processedHtml.replace(
578
- "<head>",
579
- '<head>\n <script type="module" src="/@nasti/client"></script>'
580
- );
581
693
  res.setHeader("Content-Type", "text/html");
582
694
  res.end(processedHtml);
583
695
  return;
@@ -610,10 +722,10 @@ async function transformRequest(url, ctx) {
610
722
  return cached.transformResult;
611
723
  }
612
724
  const filePath = resolveUrlToFile(url, config.root);
613
- if (!filePath || !import_node_fs3.default.existsSync(filePath)) return null;
725
+ if (!filePath || !import_node_fs4.default.existsSync(filePath)) return null;
614
726
  const mod = await moduleGraph.ensureEntryFromUrl(url);
615
727
  moduleGraph.registerModule(mod, filePath);
616
- let code = import_node_fs3.default.readFileSync(filePath, "utf-8");
728
+ let code = import_node_fs4.default.readFileSync(filePath, "utf-8");
617
729
  const pluginResult = await pluginContainer.transform(code, filePath);
618
730
  if (pluginResult) {
619
731
  code = typeof pluginResult === "string" ? pluginResult : pluginResult.code;
@@ -627,23 +739,33 @@ async function transformRequest(url, ctx) {
627
739
  });
628
740
  code = result.code;
629
741
  }
742
+ const envDefine = ctx.envDefine ?? buildEnvDefine(
743
+ loadEnv(config.mode, config.root, config.envPrefix),
744
+ config.mode
745
+ );
746
+ code = replaceEnvInCode(code, envDefine);
630
747
  code = rewriteImports(code, config);
631
748
  const transformResult = { code };
632
749
  mod.transformResult = transformResult;
633
750
  return transformResult;
634
751
  }
635
- function rewriteImports(code, config) {
752
+ function rewriteImports(code, _config) {
636
753
  return code.replace(
637
- /from\s+['"]([^'"./][^'"]*)['"]/g,
638
- (match, specifier) => {
639
- if (specifier.startsWith("/") || specifier.startsWith(".")) return match;
640
- return `from "/@modules/${specifier}"`;
754
+ /\bfrom\s+(['"])([^'"./][^'"]*)\1/g,
755
+ (match, quote, specifier) => {
756
+ return `from ${quote}/@modules/${specifier}${quote}`;
757
+ }
758
+ ).replace(
759
+ // 处理纯副作用导入: import 'bare-specifier'
760
+ /\bimport\s+(['"])([^'"./][^'"]*)\1/g,
761
+ (match, quote, specifier) => {
762
+ return `import ${quote}/@modules/${specifier}${quote}`;
641
763
  }
642
764
  ).replace(
643
- /import\s+['"]([^'"./][^'"]*)['"]/g,
644
- (match, specifier) => {
645
- if (specifier.startsWith("/") || specifier.startsWith(".")) return match;
646
- return `import "/@modules/${specifier}"`;
765
+ // 处理动态导入: import('bare-specifier')
766
+ /\bimport\s*\(\s*(['"])([^'"./][^'"]*)\1\s*\)/g,
767
+ (match, quote, specifier) => {
768
+ return `import(${quote}/@modules/${specifier}${quote})`;
647
769
  }
648
770
  );
649
771
  }
@@ -652,14 +774,13 @@ function resolveUrlToFile(url, root) {
652
774
  if (cleanUrl.startsWith("/@modules/")) {
653
775
  const moduleName = cleanUrl.slice("/@modules/".length);
654
776
  try {
655
- const { createRequire: createRequire2 } = require("module");
656
- const req = createRequire2(import_node_path3.default.resolve(root, "package.json"));
777
+ const req = (0, import_node_module.createRequire)(import_node_path4.default.resolve(root, "package.json"));
657
778
  return req.resolve(moduleName);
658
779
  } catch {
659
780
  return null;
660
781
  }
661
782
  }
662
- return import_node_path3.default.resolve(root, cleanUrl.replace(/^\//, ""));
783
+ return import_node_path4.default.resolve(root, cleanUrl.replace(/^\//, ""));
663
784
  }
664
785
  function isModuleRequest(url) {
665
786
  const cleanUrl = url.split("?")[0];
@@ -721,7 +842,18 @@ function showErrorOverlay(err) {
721
842
  const overlay = document.createElement('div');
722
843
  overlay.id = 'nasti-error-overlay';
723
844
  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;';
724
- 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>\`;
845
+ const title = document.createElement('h2');
846
+ title.style.color = '#ff5555';
847
+ title.textContent = 'Build Error';
848
+ const pre = document.createElement('pre');
849
+ pre.textContent = err.message + '\\n' + (err.stack || '');
850
+ const btn = document.createElement('button');
851
+ btn.style.cssText = 'margin-top:1rem;padding:0.5rem 1rem;cursor:pointer';
852
+ btn.textContent = 'Close';
853
+ btn.onclick = () => overlay.remove();
854
+ overlay.appendChild(title);
855
+ overlay.appendChild(pre);
856
+ overlay.appendChild(btn);
725
857
  document.body.appendChild(overlay);
726
858
  }
727
859
 
@@ -752,21 +884,23 @@ if (!window.__nasti_hot_map) window.__nasti_hot_map = new Map();
752
884
  window.__NASTI_HMR__ = { createHotContext };
753
885
  `;
754
886
  }
755
- var import_node_path3, import_node_fs3;
887
+ var import_node_path4, import_node_fs4, import_node_module;
756
888
  var init_middleware = __esm({
757
889
  "src/server/middleware.ts"() {
758
890
  "use strict";
759
- import_node_path3 = __toESM(require("path"), 1);
760
- import_node_fs3 = __toESM(require("fs"), 1);
891
+ import_node_path4 = __toESM(require("path"), 1);
892
+ import_node_fs4 = __toESM(require("fs"), 1);
893
+ import_node_module = require("module");
761
894
  init_transformer();
762
895
  init_html();
896
+ init_env();
763
897
  }
764
898
  });
765
899
 
766
900
  // src/server/hmr.ts
767
901
  async function handleFileChange(file, server) {
768
902
  const { moduleGraph, ws, config } = server;
769
- const relativePath = "/" + import_node_path4.default.relative(config.root, file);
903
+ const relativePath = "/" + import_node_path5.default.relative(config.root, file);
770
904
  const mods = moduleGraph.getModulesByFile(file);
771
905
  if (!mods || mods.size === 0) {
772
906
  return;
@@ -779,7 +913,7 @@ async function handleFileChange(file, server) {
779
913
  file,
780
914
  timestamp,
781
915
  modules: [mod],
782
- read: () => import_node_fs4.default.readFileSync(file, "utf-8"),
916
+ read: () => import_node_fs5.default.readFileSync(file, "utf-8"),
783
917
  server
784
918
  };
785
919
  let affectedModules = [mod];
@@ -811,19 +945,19 @@ async function handleFileChange(file, server) {
811
945
  ws.send({ type: "update", updates });
812
946
  }
813
947
  }
814
- var import_node_path4, import_node_fs4;
948
+ var import_node_path5, import_node_fs5;
815
949
  var init_hmr = __esm({
816
950
  "src/server/hmr.ts"() {
817
951
  "use strict";
818
- import_node_path4 = __toESM(require("path"), 1);
819
- import_node_fs4 = __toESM(require("fs"), 1);
952
+ import_node_path5 = __toESM(require("path"), 1);
953
+ import_node_fs5 = __toESM(require("fs"), 1);
820
954
  }
821
955
  });
822
956
 
823
957
  // src/plugins/resolve.ts
824
958
  function resolvePlugin(config) {
825
959
  const { alias, extensions } = config.resolve;
826
- const require2 = (0, import_node_module.createRequire)(import_node_path5.default.resolve(config.root, "package.json"));
960
+ const require2 = (0, import_node_module2.createRequire)(import_node_path6.default.resolve(config.root, "package.json"));
827
961
  return {
828
962
  name: "nasti:resolve",
829
963
  enforce: "pre",
@@ -831,26 +965,26 @@ function resolvePlugin(config) {
831
965
  for (const [key, value] of Object.entries(alias)) {
832
966
  if (source === key || source.startsWith(key + "/")) {
833
967
  source = source.replace(key, value);
834
- if (!import_node_path5.default.isAbsolute(source)) {
835
- source = import_node_path5.default.resolve(config.root, source);
968
+ if (!import_node_path6.default.isAbsolute(source)) {
969
+ source = import_node_path6.default.resolve(config.root, source);
836
970
  }
837
971
  break;
838
972
  }
839
973
  }
840
- if (import_node_path5.default.isAbsolute(source)) {
974
+ if (import_node_path6.default.isAbsolute(source)) {
841
975
  const resolved = tryResolveFile(source, extensions);
842
976
  if (resolved) return resolved;
843
977
  }
844
978
  if (source.startsWith(".")) {
845
- const dir = importer ? import_node_path5.default.dirname(importer) : config.root;
846
- const absolute = import_node_path5.default.resolve(dir, source);
979
+ const dir = importer ? import_node_path6.default.dirname(importer) : config.root;
980
+ const absolute = import_node_path6.default.resolve(dir, source);
847
981
  const resolved = tryResolveFile(absolute, extensions);
848
982
  if (resolved) return resolved;
849
983
  }
850
984
  if (!source.startsWith("/") && !source.startsWith(".")) {
851
985
  try {
852
986
  const resolved = require2.resolve(source, {
853
- paths: [importer ? import_node_path5.default.dirname(importer) : config.root]
987
+ paths: [importer ? import_node_path6.default.dirname(importer) : config.root]
854
988
  });
855
989
  return resolved;
856
990
  } catch {
@@ -860,40 +994,42 @@ function resolvePlugin(config) {
860
994
  return null;
861
995
  },
862
996
  load(id) {
863
- if (import_node_fs5.default.existsSync(id)) {
864
- return import_node_fs5.default.readFileSync(id, "utf-8");
997
+ if (!import_node_fs6.default.existsSync(id)) return null;
998
+ if (id.endsWith(".json")) {
999
+ const content = import_node_fs6.default.readFileSync(id, "utf-8");
1000
+ return `export default ${content}`;
865
1001
  }
866
- return null;
1002
+ return import_node_fs6.default.readFileSync(id, "utf-8");
867
1003
  }
868
1004
  };
869
1005
  }
870
1006
  function tryResolveFile(file, extensions) {
871
- if (import_node_fs5.default.existsSync(file) && import_node_fs5.default.statSync(file).isFile()) {
1007
+ if (import_node_fs6.default.existsSync(file) && import_node_fs6.default.statSync(file).isFile()) {
872
1008
  return file;
873
1009
  }
874
1010
  for (const ext of extensions) {
875
1011
  const withExt = file + ext;
876
- if (import_node_fs5.default.existsSync(withExt) && import_node_fs5.default.statSync(withExt).isFile()) {
1012
+ if (import_node_fs6.default.existsSync(withExt) && import_node_fs6.default.statSync(withExt).isFile()) {
877
1013
  return withExt;
878
1014
  }
879
1015
  }
880
- if (import_node_fs5.default.existsSync(file) && import_node_fs5.default.statSync(file).isDirectory()) {
1016
+ if (import_node_fs6.default.existsSync(file) && import_node_fs6.default.statSync(file).isDirectory()) {
881
1017
  for (const ext of extensions) {
882
- const indexFile = import_node_path5.default.join(file, "index" + ext);
883
- if (import_node_fs5.default.existsSync(indexFile)) {
1018
+ const indexFile = import_node_path6.default.join(file, "index" + ext);
1019
+ if (import_node_fs6.default.existsSync(indexFile)) {
884
1020
  return indexFile;
885
1021
  }
886
1022
  }
887
1023
  }
888
1024
  return null;
889
1025
  }
890
- var import_node_path5, import_node_fs5, import_node_module;
1026
+ var import_node_path6, import_node_fs6, import_node_module2;
891
1027
  var init_resolve = __esm({
892
1028
  "src/plugins/resolve.ts"() {
893
1029
  "use strict";
894
- import_node_path5 = __toESM(require("path"), 1);
895
- import_node_fs5 = __toESM(require("fs"), 1);
896
- import_node_module = require("module");
1030
+ import_node_path6 = __toESM(require("path"), 1);
1031
+ import_node_fs6 = __toESM(require("fs"), 1);
1032
+ import_node_module2 = require("module");
897
1033
  }
898
1034
  });
899
1035
 
@@ -907,13 +1043,17 @@ function cssPlugin(config) {
907
1043
  },
908
1044
  transform(code, id) {
909
1045
  if (!id.endsWith(".css")) return null;
1046
+ const rewritten = rewriteCssUrls(code, id, config.root);
910
1047
  if (config.command === "serve") {
911
- const escaped = JSON.stringify(code);
1048
+ const escaped = JSON.stringify(rewritten);
912
1049
  return {
913
1050
  code: `
914
1051
  const css = ${escaped};
1052
+ const __nasti_css_id__ = ${JSON.stringify(id)};
1053
+ const __nasti_existing__ = document.querySelector('style[data-nasti-css=' + JSON.stringify(__nasti_css_id__) + ']');
1054
+ if (__nasti_existing__) __nasti_existing__.remove();
915
1055
  const style = document.createElement('style');
916
- style.setAttribute('data-nasti-css', ${JSON.stringify(id)});
1056
+ style.setAttribute('data-nasti-css', __nasti_css_id__);
917
1057
  style.textContent = css;
918
1058
  document.head.appendChild(style);
919
1059
 
@@ -929,15 +1069,25 @@ export default css;
929
1069
  `
930
1070
  };
931
1071
  }
932
- return null;
1072
+ return rewritten !== code ? { code: rewritten } : null;
933
1073
  }
934
1074
  };
935
1075
  }
936
- var import_node_path6;
1076
+ function rewriteCssUrls(css, from, root) {
1077
+ return css.replace(/url\(\s*['"]?([^'")\s]+)['"]?\s*\)/g, (match, url) => {
1078
+ if (url.startsWith("/") || url.startsWith("data:") || url.startsWith("http")) {
1079
+ return match;
1080
+ }
1081
+ const resolved = import_node_path7.default.resolve(import_node_path7.default.dirname(from), url);
1082
+ const relative = "/" + import_node_path7.default.relative(root, resolved);
1083
+ return `url(${relative})`;
1084
+ });
1085
+ }
1086
+ var import_node_path7;
937
1087
  var init_css = __esm({
938
1088
  "src/plugins/css.ts"() {
939
1089
  "use strict";
940
- import_node_path6 = __toESM(require("path"), 1);
1090
+ import_node_path7 = __toESM(require("path"), 1);
941
1091
  }
942
1092
  });
943
1093
 
@@ -952,24 +1102,24 @@ function assetsPlugin(config) {
952
1102
  return null;
953
1103
  },
954
1104
  load(id) {
955
- const ext = import_node_path7.default.extname(id.replace(/\?.*$/, ""));
1105
+ const ext = import_node_path8.default.extname(id.replace(/\?.*$/, ""));
956
1106
  if (id.endsWith("?raw")) {
957
1107
  const file = id.slice(0, -4);
958
- if (import_node_fs6.default.existsSync(file)) {
959
- const content = import_node_fs6.default.readFileSync(file, "utf-8");
1108
+ if (import_node_fs7.default.existsSync(file)) {
1109
+ const content = import_node_fs7.default.readFileSync(file, "utf-8");
960
1110
  return `export default ${JSON.stringify(content)}`;
961
1111
  }
962
1112
  }
963
1113
  if (id.endsWith("?url") || ASSET_EXTENSIONS.has(ext)) {
964
1114
  const file = id.replace(/\?.*$/, "");
965
- if (!import_node_fs6.default.existsSync(file)) return null;
1115
+ if (!import_node_fs7.default.existsSync(file)) return null;
966
1116
  if (config.command === "serve") {
967
- const url = "/" + import_node_path7.default.relative(config.root, file);
1117
+ const url = "/" + import_node_path8.default.relative(config.root, file);
968
1118
  return `export default ${JSON.stringify(url)}`;
969
1119
  }
970
- const content = import_node_fs6.default.readFileSync(file);
1120
+ const content = import_node_fs7.default.readFileSync(file);
971
1121
  const hash = import_node_crypto.default.createHash("sha256").update(content).digest("hex").slice(0, 8);
972
- const basename = import_node_path7.default.basename(file, ext);
1122
+ const basename = import_node_path8.default.basename(file, ext);
973
1123
  const hashedName = `${config.build.assetsDir}/${basename}.${hash}${ext}`;
974
1124
  return `export default ${JSON.stringify(config.base + hashedName)}`;
975
1125
  }
@@ -977,12 +1127,12 @@ function assetsPlugin(config) {
977
1127
  }
978
1128
  };
979
1129
  }
980
- var import_node_path7, import_node_fs6, import_node_crypto, ASSET_EXTENSIONS;
1130
+ var import_node_path8, import_node_fs7, import_node_crypto, ASSET_EXTENSIONS;
981
1131
  var init_assets = __esm({
982
1132
  "src/plugins/assets.ts"() {
983
1133
  "use strict";
984
- import_node_path7 = __toESM(require("path"), 1);
985
- import_node_fs6 = __toESM(require("fs"), 1);
1134
+ import_node_path8 = __toESM(require("path"), 1);
1135
+ import_node_fs7 = __toESM(require("fs"), 1);
986
1136
  import_node_crypto = __toESM(require("crypto"), 1);
987
1137
  ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
988
1138
  ".png",
@@ -1034,7 +1184,7 @@ async function createServer(inlineConfig = {}) {
1034
1184
  pluginContainer,
1035
1185
  moduleGraph
1036
1186
  }));
1037
- const publicDir = import_node_path8.default.resolve(config.root, "public");
1187
+ const publicDir = import_node_path9.default.resolve(config.root, "public");
1038
1188
  app.use((0, import_sirv.default)(publicDir, { dev: true, etag: true }));
1039
1189
  app.use((0, import_sirv.default)(config.root, { dev: true, etag: true }));
1040
1190
  const httpServer = import_node_http.default.createServer(app);
@@ -1074,11 +1224,13 @@ async function createServer(inlineConfig = {}) {
1074
1224
  const host = config.server.host === true ? "0.0.0.0" : config.server.host;
1075
1225
  await pluginContainer.buildStart();
1076
1226
  return new Promise((resolve, reject) => {
1077
- httpServer.listen(finalPort, host, () => {
1078
- const localUrl = `http://localhost:${finalPort}`;
1079
- const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${finalPort}` : null;
1227
+ let currentPort = finalPort;
1228
+ const onListening = () => {
1229
+ const actualPort = httpServer.address()?.port ?? currentPort;
1230
+ const localUrl = `http://localhost:${actualPort}`;
1231
+ const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${actualPort}` : null;
1080
1232
  console.log();
1081
- console.log(import_picocolors.default.cyan(" nasti dev server") + import_picocolors.default.dim(` v0.0.1`));
1233
+ console.log(import_picocolors.default.cyan(" nasti dev server") + import_picocolors.default.dim(` v${"1.3.0"}`));
1082
1234
  console.log();
1083
1235
  console.log(` ${import_picocolors.default.green(">")} Local: ${import_picocolors.default.cyan(localUrl)}`);
1084
1236
  if (networkUrl) {
@@ -1086,15 +1238,18 @@ async function createServer(inlineConfig = {}) {
1086
1238
  }
1087
1239
  console.log();
1088
1240
  resolve(server);
1089
- });
1241
+ };
1242
+ httpServer.on("listening", onListening);
1090
1243
  httpServer.on("error", (err) => {
1091
1244
  if (err.code === "EADDRINUSE") {
1092
- console.log(import_picocolors.default.yellow(`Port ${finalPort} is in use, trying ${finalPort + 1}...`));
1093
- httpServer.listen(finalPort + 1, host);
1245
+ currentPort++;
1246
+ console.log(import_picocolors.default.yellow(`Port ${currentPort - 1} is in use, trying ${currentPort}...`));
1247
+ httpServer.listen(currentPort, host);
1094
1248
  } else {
1095
1249
  reject(err);
1096
1250
  }
1097
1251
  });
1252
+ httpServer.listen(currentPort, host);
1098
1253
  });
1099
1254
  },
1100
1255
  async transformRequest(url) {
@@ -1111,8 +1266,7 @@ async function createServer(inlineConfig = {}) {
1111
1266
  return server;
1112
1267
  }
1113
1268
  function getNetworkAddress() {
1114
- const os = require("os");
1115
- const interfaces = os.networkInterfaces();
1269
+ const interfaces = import_node_os.default.networkInterfaces();
1116
1270
  for (const name of Object.keys(interfaces)) {
1117
1271
  for (const iface of interfaces[name] ?? []) {
1118
1272
  if (iface.family === "IPv4" && !iface.internal) {
@@ -1122,12 +1276,13 @@ function getNetworkAddress() {
1122
1276
  }
1123
1277
  return "localhost";
1124
1278
  }
1125
- var import_node_http, import_node_path8, import_connect, import_sirv, import_chokidar, import_picocolors;
1279
+ var import_node_http, import_node_path9, import_node_os, import_connect, import_sirv, import_chokidar, import_picocolors;
1126
1280
  var init_server = __esm({
1127
1281
  "src/server/index.ts"() {
1128
1282
  "use strict";
1129
1283
  import_node_http = __toESM(require("http"), 1);
1130
- import_node_path8 = __toESM(require("path"), 1);
1284
+ import_node_path9 = __toESM(require("path"), 1);
1285
+ import_node_os = __toESM(require("os"), 1);
1131
1286
  import_connect = __toESM(require("connect"), 1);
1132
1287
  import_sirv = __toESM(require("sirv"), 1);
1133
1288
  import_chokidar = require("chokidar");
@@ -1153,14 +1308,14 @@ __export(build_exports, {
1153
1308
  async function build(inlineConfig = {}) {
1154
1309
  const config = await resolveConfig(inlineConfig, "build");
1155
1310
  const startTime = performance.now();
1156
- console.log(import_picocolors2.default.cyan("\n\u{1F528} nasti build") + import_picocolors2.default.dim(` v${process.env.npm_package_version ?? "0.0.1"}`));
1311
+ console.log(import_picocolors2.default.cyan("\n\u{1F528} nasti build") + import_picocolors2.default.dim(` v${"1.3.0"}`));
1157
1312
  console.log(import_picocolors2.default.dim(` root: ${config.root}`));
1158
1313
  console.log(import_picocolors2.default.dim(` mode: ${config.mode}`));
1159
- const outDir = import_node_path9.default.resolve(config.root, config.build.outDir);
1160
- if (config.build.emptyOutDir && import_node_fs7.default.existsSync(outDir)) {
1161
- import_node_fs7.default.rmSync(outDir, { recursive: true, force: true });
1314
+ const outDir = import_node_path10.default.resolve(config.root, config.build.outDir);
1315
+ if (config.build.emptyOutDir && import_node_fs8.default.existsSync(outDir)) {
1316
+ import_node_fs8.default.rmSync(outDir, { recursive: true, force: true });
1162
1317
  }
1163
- import_node_fs7.default.mkdirSync(outDir, { recursive: true });
1318
+ import_node_fs8.default.mkdirSync(outDir, { recursive: true });
1164
1319
  const html = await readHtmlFile(config.root);
1165
1320
  let entryPoints = [];
1166
1321
  if (html) {
@@ -1168,15 +1323,15 @@ async function build(inlineConfig = {}) {
1168
1323
  for (const match of scriptMatches) {
1169
1324
  const src = match[1];
1170
1325
  if (src && !src.startsWith("http")) {
1171
- entryPoints.push(import_node_path9.default.resolve(config.root, src.replace(/^\//, "")));
1326
+ entryPoints.push(import_node_path10.default.resolve(config.root, src.replace(/^\//, "")));
1172
1327
  }
1173
1328
  }
1174
1329
  }
1175
1330
  if (entryPoints.length === 0) {
1176
1331
  const fallbackEntries = ["src/main.ts", "src/main.tsx", "src/main.js", "src/index.ts", "src/index.tsx", "src/index.js"];
1177
1332
  for (const entry of fallbackEntries) {
1178
- const fullPath = import_node_path9.default.resolve(config.root, entry);
1179
- if (import_node_fs7.default.existsSync(fullPath)) {
1333
+ const fullPath = import_node_path10.default.resolve(config.root, entry);
1334
+ if (import_node_fs8.default.existsSync(fullPath)) {
1180
1335
  entryPoints.push(fullPath);
1181
1336
  break;
1182
1337
  }
@@ -1191,6 +1346,8 @@ async function build(inlineConfig = {}) {
1191
1346
  assetsPlugin(config)
1192
1347
  ];
1193
1348
  const allPlugins = [...builtinPlugins, ...config.plugins];
1349
+ const pluginContainer = new PluginContainer(config);
1350
+ await pluginContainer.buildStart();
1194
1351
  const oxcTransformPlugin = {
1195
1352
  name: "nasti:oxc-transform",
1196
1353
  transform(code, id) {
@@ -1203,8 +1360,11 @@ async function build(inlineConfig = {}) {
1203
1360
  return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
1204
1361
  }
1205
1362
  };
1363
+ const env = loadEnv(config.mode, config.root, config.envPrefix);
1364
+ const envDefine = buildEnvDefine(env, config.mode);
1206
1365
  const bundle = await (0, import_rolldown.rolldown)({
1207
1366
  input: entryPoints,
1367
+ define: envDefine,
1208
1368
  plugins: [
1209
1369
  oxcTransformPlugin,
1210
1370
  // 转换 Nasti 插件为 Rolldown 插件格式
@@ -1223,11 +1383,18 @@ async function build(inlineConfig = {}) {
1223
1383
  dir: outDir,
1224
1384
  format: "esm",
1225
1385
  sourcemap: !!config.build.sourcemap,
1386
+ minify: !!config.build.minify,
1226
1387
  entryFileNames: "assets/[name].[hash].js",
1227
1388
  chunkFileNames: "assets/[name].[hash].js",
1228
1389
  assetFileNames: "assets/[name].[hash][extname]"
1229
1390
  });
1230
1391
  await bundle.close();
1392
+ await pluginContainer.buildEnd();
1393
+ for (const ef of pluginContainer.getEmittedFiles()) {
1394
+ const dest = import_node_path10.default.resolve(outDir, ef.fileName);
1395
+ import_node_fs8.default.mkdirSync(import_node_path10.default.dirname(dest), { recursive: true });
1396
+ import_node_fs8.default.writeFileSync(dest, ef.source);
1397
+ }
1231
1398
  if (html) {
1232
1399
  let processedHtml = html;
1233
1400
  const htmlPlugin_ = htmlPlugin(config);
@@ -1242,15 +1409,15 @@ async function build(inlineConfig = {}) {
1242
1409
  }
1243
1410
  }
1244
1411
  for (const chunk of output) {
1245
- if (chunk.type === "chunk" && chunk.isEntry) {
1246
- const originalEntry = import_node_path9.default.relative(config.root, entryPoints[0]);
1412
+ if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
1413
+ const originalEntry = import_node_path10.default.relative(config.root, chunk.facadeModuleId);
1247
1414
  processedHtml = processedHtml.replace(
1248
1415
  new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
1249
1416
  `$1${config.base}${chunk.fileName}$3`
1250
1417
  );
1251
1418
  }
1252
1419
  }
1253
- import_node_fs7.default.writeFileSync(import_node_path9.default.resolve(outDir, "index.html"), processedHtml);
1420
+ import_node_fs8.default.writeFileSync(import_node_path10.default.resolve(outDir, "index.html"), processedHtml);
1254
1421
  }
1255
1422
  const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
1256
1423
  const totalSize = output.reduce((sum, chunk) => {
@@ -1272,12 +1439,12 @@ function formatSize(bytes) {
1272
1439
  function escapeRegExp(string) {
1273
1440
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1274
1441
  }
1275
- var import_node_path9, import_node_fs7, import_rolldown, import_picocolors2;
1442
+ var import_node_path10, import_node_fs8, import_rolldown, import_picocolors2;
1276
1443
  var init_build = __esm({
1277
1444
  "src/build/index.ts"() {
1278
1445
  "use strict";
1279
- import_node_path9 = __toESM(require("path"), 1);
1280
- import_node_fs7 = __toESM(require("fs"), 1);
1446
+ import_node_path10 = __toESM(require("path"), 1);
1447
+ import_node_fs8 = __toESM(require("fs"), 1);
1281
1448
  import_rolldown = require("rolldown");
1282
1449
  init_config();
1283
1450
  init_resolve();
@@ -1285,6 +1452,8 @@ var init_build = __esm({
1285
1452
  init_assets();
1286
1453
  init_html();
1287
1454
  init_transformer();
1455
+ init_env();
1456
+ init_plugin_container();
1288
1457
  import_picocolors2 = __toESM(require("picocolors"), 1);
1289
1458
  }
1290
1459
  });
@@ -1339,11 +1508,11 @@ cli.command("build [root]", "Build for production").option("--outDir <dir>", "Ou
1339
1508
  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) => {
1340
1509
  try {
1341
1510
  const http2 = await import("http");
1342
- const path10 = await import("path");
1511
+ const path11 = await import("path");
1343
1512
  const sirv2 = (await import("sirv")).default;
1344
1513
  const connect2 = (await import("connect")).default;
1345
- const resolvedRoot = path10.resolve(root ?? ".");
1346
- const outDir = path10.resolve(resolvedRoot, options.outDir);
1514
+ const resolvedRoot = path11.resolve(root ?? ".");
1515
+ const outDir = path11.resolve(resolvedRoot, options.outDir);
1347
1516
  const app = connect2();
1348
1517
  app.use(sirv2(outDir, { single: true, etag: true, gzip: true, brotli: true }));
1349
1518
  const port = options.port;
@@ -1364,6 +1533,6 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
1364
1533
  }
1365
1534
  });
1366
1535
  cli.help();
1367
- cli.version("0.0.1");
1536
+ cli.version("1.3.0");
1368
1537
  cli.parse();
1369
1538
  //# sourceMappingURL=cli.cjs.map