@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.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,94 @@ 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) {
633
+ ctx.envDefine = buildEnvDefine(
634
+ loadEnv(ctx.config.mode, ctx.config.root, ctx.config.envPrefix),
635
+ ctx.config.mode
636
+ );
538
637
  return async (req, res, next) => {
539
638
  const url = req.url ?? "/";
639
+ if (ctx.config.server.cors) {
640
+ const origin = req.headers.origin ?? "*";
641
+ res.setHeader("Access-Control-Allow-Origin", origin);
642
+ res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
643
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
644
+ if (req.method === "OPTIONS") {
645
+ res.statusCode = 204;
646
+ res.end();
647
+ return;
648
+ }
649
+ }
540
650
  if (req.method !== "GET") return next();
541
651
  if (url === "/@nasti/client") {
542
652
  res.setHeader("Content-Type", "application/javascript");
@@ -559,10 +669,6 @@ function transformMiddleware(ctx) {
559
669
  }
560
670
  }
561
671
  }
562
- processedHtml = processedHtml.replace(
563
- "<head>",
564
- '<head>\n <script type="module" src="/@nasti/client"></script>'
565
- );
566
672
  res.setHeader("Content-Type", "text/html");
567
673
  res.end(processedHtml);
568
674
  return;
@@ -595,10 +701,10 @@ async function transformRequest(url, ctx) {
595
701
  return cached.transformResult;
596
702
  }
597
703
  const filePath = resolveUrlToFile(url, config.root);
598
- if (!filePath || !fs3.existsSync(filePath)) return null;
704
+ if (!filePath || !fs4.existsSync(filePath)) return null;
599
705
  const mod = await moduleGraph.ensureEntryFromUrl(url);
600
706
  moduleGraph.registerModule(mod, filePath);
601
- let code = fs3.readFileSync(filePath, "utf-8");
707
+ let code = fs4.readFileSync(filePath, "utf-8");
602
708
  const pluginResult = await pluginContainer.transform(code, filePath);
603
709
  if (pluginResult) {
604
710
  code = typeof pluginResult === "string" ? pluginResult : pluginResult.code;
@@ -612,23 +718,33 @@ async function transformRequest(url, ctx) {
612
718
  });
613
719
  code = result.code;
614
720
  }
721
+ const envDefine = ctx.envDefine ?? buildEnvDefine(
722
+ loadEnv(config.mode, config.root, config.envPrefix),
723
+ config.mode
724
+ );
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}`;
736
+ }
737
+ ).replace(
738
+ // 处理纯副作用导入: import 'bare-specifier'
739
+ /\bimport\s+(['"])([^'"./][^'"]*)\1/g,
740
+ (match, quote, specifier) => {
741
+ return `import ${quote}/@modules/${specifier}${quote}`;
626
742
  }
627
743
  ).replace(
628
- /import\s+['"]([^'"./][^'"]*)['"]/g,
629
- (match, specifier) => {
630
- if (specifier.startsWith("/") || specifier.startsWith(".")) return match;
631
- return `import "/@modules/${specifier}"`;
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);
@@ -1059,11 +1203,13 @@ async function createServer(inlineConfig = {}) {
1059
1203
  const host = config.server.host === true ? "0.0.0.0" : config.server.host;
1060
1204
  await pluginContainer.buildStart();
1061
1205
  return new Promise((resolve, reject) => {
1062
- httpServer.listen(finalPort, host, () => {
1063
- const localUrl = `http://localhost:${finalPort}`;
1064
- const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${finalPort}` : null;
1206
+ let currentPort = finalPort;
1207
+ const onListening = () => {
1208
+ const actualPort = httpServer.address()?.port ?? currentPort;
1209
+ const localUrl = `http://localhost:${actualPort}`;
1210
+ const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${actualPort}` : null;
1065
1211
  console.log();
1066
- console.log(pc.cyan(" nasti dev server") + pc.dim(` v0.0.1`));
1212
+ console.log(pc.cyan(" nasti dev server") + pc.dim(` v${"1.3.0"}`));
1067
1213
  console.log();
1068
1214
  console.log(` ${pc.green(">")} Local: ${pc.cyan(localUrl)}`);
1069
1215
  if (networkUrl) {
@@ -1071,15 +1217,18 @@ async function createServer(inlineConfig = {}) {
1071
1217
  }
1072
1218
  console.log();
1073
1219
  resolve(server);
1074
- });
1220
+ };
1221
+ httpServer.on("listening", onListening);
1075
1222
  httpServer.on("error", (err) => {
1076
1223
  if (err.code === "EADDRINUSE") {
1077
- console.log(pc.yellow(`Port ${finalPort} is in use, trying ${finalPort + 1}...`));
1078
- httpServer.listen(finalPort + 1, host);
1224
+ currentPort++;
1225
+ console.log(pc.yellow(`Port ${currentPort - 1} is in use, trying ${currentPort}...`));
1226
+ httpServer.listen(currentPort, host);
1079
1227
  } else {
1080
1228
  reject(err);
1081
1229
  }
1082
1230
  });
1231
+ httpServer.listen(currentPort, host);
1083
1232
  });
1084
1233
  },
1085
1234
  async transformRequest(url) {
@@ -1096,7 +1245,6 @@ async function createServer(inlineConfig = {}) {
1096
1245
  return server;
1097
1246
  }
1098
1247
  function getNetworkAddress() {
1099
- const os = __require("os");
1100
1248
  const interfaces = os.networkInterfaces();
1101
1249
  for (const name of Object.keys(interfaces)) {
1102
1250
  for (const iface of interfaces[name] ?? []) {
@@ -1128,21 +1276,21 @@ var build_exports = {};
1128
1276
  __export(build_exports, {
1129
1277
  build: () => build
1130
1278
  });
1131
- import path9 from "path";
1132
- import fs7 from "fs";
1279
+ import path10 from "path";
1280
+ import fs8 from "fs";
1133
1281
  import { rolldown } from "rolldown";
1134
1282
  import pc2 from "picocolors";
1135
1283
  async function build(inlineConfig = {}) {
1136
1284
  const config = await resolveConfig(inlineConfig, "build");
1137
1285
  const startTime = performance.now();
1138
- console.log(pc2.cyan("\n\u{1F528} nasti build") + pc2.dim(` v${process.env.npm_package_version ?? "0.0.1"}`));
1286
+ console.log(pc2.cyan("\n\u{1F528} nasti build") + pc2.dim(` v${"1.3.0"}`));
1139
1287
  console.log(pc2.dim(` root: ${config.root}`));
1140
1288
  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 });
1289
+ const outDir = path10.resolve(config.root, config.build.outDir);
1290
+ if (config.build.emptyOutDir && fs8.existsSync(outDir)) {
1291
+ fs8.rmSync(outDir, { recursive: true, force: true });
1144
1292
  }
1145
- fs7.mkdirSync(outDir, { recursive: true });
1293
+ fs8.mkdirSync(outDir, { recursive: true });
1146
1294
  const html = await readHtmlFile(config.root);
1147
1295
  let entryPoints = [];
1148
1296
  if (html) {
@@ -1150,15 +1298,15 @@ async function build(inlineConfig = {}) {
1150
1298
  for (const match of scriptMatches) {
1151
1299
  const src = match[1];
1152
1300
  if (src && !src.startsWith("http")) {
1153
- entryPoints.push(path9.resolve(config.root, src.replace(/^\//, "")));
1301
+ entryPoints.push(path10.resolve(config.root, src.replace(/^\//, "")));
1154
1302
  }
1155
1303
  }
1156
1304
  }
1157
1305
  if (entryPoints.length === 0) {
1158
1306
  const fallbackEntries = ["src/main.ts", "src/main.tsx", "src/main.js", "src/index.ts", "src/index.tsx", "src/index.js"];
1159
1307
  for (const entry of fallbackEntries) {
1160
- const fullPath = path9.resolve(config.root, entry);
1161
- if (fs7.existsSync(fullPath)) {
1308
+ const fullPath = path10.resolve(config.root, entry);
1309
+ if (fs8.existsSync(fullPath)) {
1162
1310
  entryPoints.push(fullPath);
1163
1311
  break;
1164
1312
  }
@@ -1173,6 +1321,8 @@ async function build(inlineConfig = {}) {
1173
1321
  assetsPlugin(config)
1174
1322
  ];
1175
1323
  const allPlugins = [...builtinPlugins, ...config.plugins];
1324
+ const pluginContainer = new PluginContainer(config);
1325
+ await pluginContainer.buildStart();
1176
1326
  const oxcTransformPlugin = {
1177
1327
  name: "nasti:oxc-transform",
1178
1328
  transform(code, id) {
@@ -1185,8 +1335,11 @@ async function build(inlineConfig = {}) {
1185
1335
  return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
1186
1336
  }
1187
1337
  };
1338
+ const env = loadEnv(config.mode, config.root, config.envPrefix);
1339
+ const envDefine = buildEnvDefine(env, config.mode);
1188
1340
  const bundle = await rolldown({
1189
1341
  input: entryPoints,
1342
+ define: envDefine,
1190
1343
  plugins: [
1191
1344
  oxcTransformPlugin,
1192
1345
  // 转换 Nasti 插件为 Rolldown 插件格式
@@ -1205,11 +1358,18 @@ async function build(inlineConfig = {}) {
1205
1358
  dir: outDir,
1206
1359
  format: "esm",
1207
1360
  sourcemap: !!config.build.sourcemap,
1361
+ minify: !!config.build.minify,
1208
1362
  entryFileNames: "assets/[name].[hash].js",
1209
1363
  chunkFileNames: "assets/[name].[hash].js",
1210
1364
  assetFileNames: "assets/[name].[hash][extname]"
1211
1365
  });
1212
1366
  await bundle.close();
1367
+ await pluginContainer.buildEnd();
1368
+ for (const ef of pluginContainer.getEmittedFiles()) {
1369
+ const dest = path10.resolve(outDir, ef.fileName);
1370
+ fs8.mkdirSync(path10.dirname(dest), { recursive: true });
1371
+ fs8.writeFileSync(dest, ef.source);
1372
+ }
1213
1373
  if (html) {
1214
1374
  let processedHtml = html;
1215
1375
  const htmlPlugin_ = htmlPlugin(config);
@@ -1224,15 +1384,15 @@ async function build(inlineConfig = {}) {
1224
1384
  }
1225
1385
  }
1226
1386
  for (const chunk of output) {
1227
- if (chunk.type === "chunk" && chunk.isEntry) {
1228
- const originalEntry = path9.relative(config.root, entryPoints[0]);
1387
+ if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
1388
+ const originalEntry = path10.relative(config.root, chunk.facadeModuleId);
1229
1389
  processedHtml = processedHtml.replace(
1230
1390
  new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
1231
1391
  `$1${config.base}${chunk.fileName}$3`
1232
1392
  );
1233
1393
  }
1234
1394
  }
1235
- fs7.writeFileSync(path9.resolve(outDir, "index.html"), processedHtml);
1395
+ fs8.writeFileSync(path10.resolve(outDir, "index.html"), processedHtml);
1236
1396
  }
1237
1397
  const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
1238
1398
  const totalSize = output.reduce((sum, chunk) => {
@@ -1263,6 +1423,8 @@ var init_build = __esm({
1263
1423
  init_assets();
1264
1424
  init_html();
1265
1425
  init_transformer();
1426
+ init_env();
1427
+ init_plugin_container();
1266
1428
  }
1267
1429
  });
1268
1430
 
@@ -1316,11 +1478,11 @@ cli.command("build [root]", "Build for production").option("--outDir <dir>", "Ou
1316
1478
  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
1479
  try {
1318
1480
  const http2 = await import("http");
1319
- const path10 = await import("path");
1481
+ const path11 = await import("path");
1320
1482
  const sirv2 = (await import("sirv")).default;
1321
1483
  const connect2 = (await import("connect")).default;
1322
- const resolvedRoot = path10.resolve(root ?? ".");
1323
- const outDir = path10.resolve(resolvedRoot, options.outDir);
1484
+ const resolvedRoot = path11.resolve(root ?? ".");
1485
+ const outDir = path11.resolve(resolvedRoot, options.outDir);
1324
1486
  const app = connect2();
1325
1487
  app.use(sirv2(outDir, { single: true, etag: true, gzip: true, brotli: true }));
1326
1488
  const port = options.port;
@@ -1341,6 +1503,6 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
1341
1503
  }
1342
1504
  });
1343
1505
  cli.help();
1344
- cli.version("0.0.1");
1506
+ cli.version("1.3.0");
1345
1507
  cli.parse();
1346
1508
  //# sourceMappingURL=cli.js.map