@nasti-toolchain/nasti 2.4.4 → 2.5.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/index.js CHANGED
@@ -10,10 +10,10 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
10
10
  if (typeof require !== "undefined") return require.apply(this, arguments);
11
11
  throw Error('Dynamic require of "' + x + '" is not supported');
12
12
  });
13
- var __glob = (map) => (path20) => {
14
- var fn = map[path20];
13
+ var __glob = (map) => (path21) => {
14
+ var fn = map[path21];
15
15
  if (fn) return fn();
16
- throw new Error("Module not found in bundle: " + path20);
16
+ throw new Error("Module not found in bundle: " + path21);
17
17
  };
18
18
  var __esm = (fn, res) => function __init() {
19
19
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
@@ -43,7 +43,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
43
43
  ));
44
44
 
45
45
  // src/config/defaults.ts
46
- var defaultResolve, defaultServer, defaultBuild, defaultElectron, defaultExperimental, defaults;
46
+ var defaultResolve, defaultServer, defaultBuild, defaultElectron, defaultExperimental, defaultReact, defaults;
47
47
  var init_defaults = __esm({
48
48
  "src/config/defaults.ts"() {
49
49
  "use strict";
@@ -96,12 +96,20 @@ var init_defaults = __esm({
96
96
  defaultExperimental = {
97
97
  bundledDev: false
98
98
  };
99
+ defaultReact = {
100
+ include: /\.[tj]sx?$/,
101
+ exclude: /node_modules/,
102
+ jsxImportSource: "react",
103
+ jsxRuntime: "automatic",
104
+ compiler: false
105
+ };
99
106
  defaults = {
100
107
  root: ".",
101
108
  base: "/",
102
109
  mode: "development",
103
110
  target: "web",
104
111
  framework: "auto",
112
+ react: defaultReact,
105
113
  resolve: defaultResolve,
106
114
  server: defaultServer,
107
115
  build: defaultBuild,
@@ -442,6 +450,13 @@ async function resolveConfig(inlineConfig = {}, command) {
442
450
  mode,
443
451
  target: merged.target ?? defaults.target,
444
452
  framework: (merged.framework ?? defaults.framework) === "auto" ? detectFramework(root) : merged.framework,
453
+ react: {
454
+ include: merged.react?.include ?? defaultReact.include,
455
+ exclude: merged.react?.exclude ?? defaultReact.exclude,
456
+ jsxImportSource: merged.react?.jsxImportSource ?? defaultReact.jsxImportSource,
457
+ jsxRuntime: merged.react?.jsxRuntime ?? defaultReact.jsxRuntime,
458
+ compiler: merged.react?.compiler === true ? {} : merged.react?.compiler ?? defaultReact.compiler
459
+ },
445
460
  command,
446
461
  resolve: {
447
462
  // tsconfig paths 优先级最低:tsconfig < defaults < user config
@@ -876,27 +891,27 @@ var require_process = __commonJS({
876
891
  var require_filesystem = __commonJS({
877
892
  "node_modules/detect-libc/lib/filesystem.js"(exports, module) {
878
893
  "use strict";
879
- var fs15 = __require("fs");
894
+ var fs16 = __require("fs");
880
895
  var LDD_PATH = "/usr/bin/ldd";
881
896
  var SELF_PATH = "/proc/self/exe";
882
897
  var MAX_LENGTH = 2048;
883
- var readFileSync = (path20) => {
884
- const fd = fs15.openSync(path20, "r");
898
+ var readFileSync = (path21) => {
899
+ const fd = fs16.openSync(path21, "r");
885
900
  const buffer = Buffer.alloc(MAX_LENGTH);
886
- const bytesRead = fs15.readSync(fd, buffer, 0, MAX_LENGTH, 0);
887
- fs15.close(fd, () => {
901
+ const bytesRead = fs16.readSync(fd, buffer, 0, MAX_LENGTH, 0);
902
+ fs16.close(fd, () => {
888
903
  });
889
904
  return buffer.subarray(0, bytesRead);
890
905
  };
891
- var readFile = (path20) => new Promise((resolve, reject) => {
892
- fs15.open(path20, "r", (err, fd) => {
906
+ var readFile = (path21) => new Promise((resolve, reject) => {
907
+ fs16.open(path21, "r", (err, fd) => {
893
908
  if (err) {
894
909
  reject(err);
895
910
  } else {
896
911
  const buffer = Buffer.alloc(MAX_LENGTH);
897
- fs15.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
912
+ fs16.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
898
913
  resolve(buffer.subarray(0, bytesRead));
899
- fs15.close(fd, () => {
914
+ fs16.close(fd, () => {
900
915
  });
901
916
  });
902
917
  }
@@ -1008,11 +1023,11 @@ var require_detect_libc = __commonJS({
1008
1023
  }
1009
1024
  return null;
1010
1025
  };
1011
- var familyFromInterpreterPath = (path20) => {
1012
- if (path20) {
1013
- if (path20.includes("/ld-musl-")) {
1026
+ var familyFromInterpreterPath = (path21) => {
1027
+ if (path21) {
1028
+ if (path21.includes("/ld-musl-")) {
1014
1029
  return MUSL;
1015
- } else if (path20.includes("/ld-linux-")) {
1030
+ } else if (path21.includes("/ld-linux-")) {
1016
1031
  return GLIBC;
1017
1032
  }
1018
1033
  }
@@ -1059,8 +1074,8 @@ var require_detect_libc = __commonJS({
1059
1074
  cachedFamilyInterpreter = null;
1060
1075
  try {
1061
1076
  const selfContent = await readFile(SELF_PATH);
1062
- const path20 = interpreterPath(selfContent);
1063
- cachedFamilyInterpreter = familyFromInterpreterPath(path20);
1077
+ const path21 = interpreterPath(selfContent);
1078
+ cachedFamilyInterpreter = familyFromInterpreterPath(path21);
1064
1079
  } catch (e) {
1065
1080
  }
1066
1081
  return cachedFamilyInterpreter;
@@ -1072,8 +1087,8 @@ var require_detect_libc = __commonJS({
1072
1087
  cachedFamilyInterpreter = null;
1073
1088
  try {
1074
1089
  const selfContent = readFileSync(SELF_PATH);
1075
- const path20 = interpreterPath(selfContent);
1076
- cachedFamilyInterpreter = familyFromInterpreterPath(path20);
1090
+ const path21 = interpreterPath(selfContent);
1091
+ cachedFamilyInterpreter = familyFromInterpreterPath(path21);
1077
1092
  } catch (e) {
1078
1093
  }
1079
1094
  return cachedFamilyInterpreter;
@@ -2174,13 +2189,88 @@ ${msg}`);
2174
2189
  map: result.map ? JSON.stringify(result.map) : null
2175
2190
  };
2176
2191
  }
2177
- var JS_EXTENSIONS, TS_EXTENSIONS, JSX_EXTENSIONS;
2192
+ async function transformReactCode(filename, code, options) {
2193
+ if (!matchesReactFilter(filename, options.react.include, options.react.exclude)) {
2194
+ return null;
2195
+ }
2196
+ if (!options.react.compiler) {
2197
+ if (!shouldTransform(filename)) return null;
2198
+ return transformCode(filename, code, {
2199
+ sourcemap: options.sourcemap,
2200
+ jsxRuntime: options.react.jsxRuntime,
2201
+ jsxImportSource: options.react.jsxImportSource,
2202
+ reactRefresh: options.reactRefresh,
2203
+ target: options.target
2204
+ });
2205
+ }
2206
+ if (!shouldTransform(filename)) return null;
2207
+ const compiler2 = await loadReactCompiler();
2208
+ const compilerOptions = options.react.compiler;
2209
+ const shouldCompile = options.consumer === "client" && (compilerOptions.compilationMode === "annotation" ? /['"]use memo['"]/.test(code) : defaultReactCompilerCodeFilter.test(code));
2210
+ const result = await compiler2.transform(cleanTransformId(filename), code, {
2211
+ jsx: {
2212
+ runtime: options.react.jsxRuntime,
2213
+ development: options.development,
2214
+ importSource: options.react.jsxImportSource,
2215
+ refresh: options.consumer === "client" && !!options.reactRefresh
2216
+ },
2217
+ reactCompiler: shouldCompile ? compilerOptions : false,
2218
+ sourcemap: options.sourcemap ?? true
2219
+ });
2220
+ const diagnostics = result.errors.map(
2221
+ (error) => `${error.message}${error.codeframe ? `
2222
+ ${error.codeframe}` : ""}`
2223
+ );
2224
+ if (result.fatal) {
2225
+ throw new Error(
2226
+ diagnostics.join("\n\n") || `React Compiler transform failed for ${filename}`
2227
+ );
2228
+ }
2229
+ for (const diagnostic of diagnostics) options.onWarning?.(diagnostic);
2230
+ return {
2231
+ code: result.code,
2232
+ map: result.map ? JSON.stringify(result.map) : null
2233
+ };
2234
+ }
2235
+ function matchesReactFilter(id, include, exclude) {
2236
+ const cleanId = cleanTransformId(id);
2237
+ return matchesFilter(cleanId, include) && !matchesFilter(cleanId, exclude);
2238
+ }
2239
+ function matchesFilter(id, filter2) {
2240
+ const patterns = Array.isArray(filter2) ? filter2 : [filter2];
2241
+ return patterns.some((pattern) => {
2242
+ if (pattern instanceof RegExp) {
2243
+ pattern.lastIndex = 0;
2244
+ return pattern.test(id);
2245
+ }
2246
+ if (!pattern.includes("*")) return id.includes(pattern);
2247
+ const expression = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, "[^/]*").replace(/\0/g, ".*");
2248
+ return new RegExp(`^${expression}$`).test(id);
2249
+ });
2250
+ }
2251
+ function cleanTransformId(id) {
2252
+ return id.split(/[?#]/, 1)[0];
2253
+ }
2254
+ async function loadReactCompiler() {
2255
+ if (reactCompilerImplementation) return reactCompilerImplementation;
2256
+ try {
2257
+ reactCompilerImplementation = await import("oxc-transform-react");
2258
+ return reactCompilerImplementation;
2259
+ } catch (error) {
2260
+ throw new Error(
2261
+ '[nasti] React Compiler requires the optional "oxc-transform-react" package. Install it before setting react.compiler.' + (error instanceof Error ? `
2262
+ ${error.message}` : "")
2263
+ );
2264
+ }
2265
+ }
2266
+ var JS_EXTENSIONS, TS_EXTENSIONS, JSX_EXTENSIONS, defaultReactCompilerCodeFilter, reactCompilerImplementation;
2178
2267
  var init_transformer = __esm({
2179
2268
  "src/core/transformer.ts"() {
2180
2269
  "use strict";
2181
2270
  JS_EXTENSIONS = /\.(js|mjs|cjs)$/;
2182
2271
  TS_EXTENSIONS = /\.(ts|mts|cts)$/;
2183
2272
  JSX_EXTENSIONS = /\.(jsx|tsx)$/;
2273
+ defaultReactCompilerCodeFilter = /forwardRef|memo|\b(?:[A-Z]|use[A-Z0-9])/;
2184
2274
  }
2185
2275
  });
2186
2276
 
@@ -2226,8 +2316,8 @@ function vuePlugin(config, environmentName = "client") {
2226
2316
  let cached2 = descriptorCache.get(filePath);
2227
2317
  if (!cached2) {
2228
2318
  try {
2229
- const fs15 = await import("fs");
2230
- const rawSource = fs15.readFileSync(filePath, "utf-8");
2319
+ const fs16 = await import("fs");
2320
+ const rawSource = fs16.readFileSync(filePath, "utf-8");
2231
2321
  const transformedSfc = await applySourceTransform(
2232
2322
  vueOptions.transformSfc,
2233
2323
  rawSource,
@@ -3281,6 +3371,44 @@ var init_environment = __esm({
3281
3371
  }
3282
3372
  });
3283
3373
 
3374
+ // src/plugins/react.ts
3375
+ function reactPlugin(config, environment) {
3376
+ return {
3377
+ name: "nasti:oxc-transform",
3378
+ async transform(code, id) {
3379
+ const result = await transformReactCode(id, code, {
3380
+ react: config.react,
3381
+ consumer: environment.consumer,
3382
+ development: config.mode === "development",
3383
+ sourcemap: !!environment.options.build.sourcemap,
3384
+ target: environment.options.build.target,
3385
+ onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`)
3386
+ });
3387
+ if (!result) return null;
3388
+ return {
3389
+ code: result.code,
3390
+ map: result.map ? JSON.parse(result.map) : void 0
3391
+ };
3392
+ },
3393
+ handleHotUpdate(ctx) {
3394
+ for (const mod of ctx.modules) {
3395
+ if (REACT_FILE_RE.test(mod.url) && matchesReactFilter(mod.url, config.react.include, config.react.exclude)) {
3396
+ mod.isSelfAccepting = true;
3397
+ }
3398
+ }
3399
+ return ctx.modules;
3400
+ }
3401
+ };
3402
+ }
3403
+ var REACT_FILE_RE;
3404
+ var init_react = __esm({
3405
+ "src/plugins/react.ts"() {
3406
+ "use strict";
3407
+ init_transformer();
3408
+ REACT_FILE_RE = /\.[jt]sx(?:[?#].*)?$/;
3409
+ }
3410
+ });
3411
+
3284
3412
  // src/core/env.ts
3285
3413
  import path7 from "path";
3286
3414
  import fs5 from "fs";
@@ -3641,7 +3769,7 @@ function getRolldownOptions(environment, entryPoints, rolldownPlugins) {
3641
3769
  // 相对/绝对/虚拟模块照常打包。需要内联依赖时经 rolldownOptions.external 覆盖。
3642
3770
  external: restInputOptions.external ?? ((id) => {
3643
3771
  if (NODE_BUILTINS.has(id)) return true;
3644
- return !id.startsWith(".") && !path10.isAbsolute(id) && !id.startsWith("\0");
3772
+ return !id.startsWith(".") && !path10.isAbsolute(id) && !id.startsWith("\0") && !id.startsWith("virtual:");
3645
3773
  })
3646
3774
  } : {}
3647
3775
  };
@@ -3864,6 +3992,7 @@ function resolveClientEntries(config, html) {
3864
3992
  return entryPoints;
3865
3993
  }
3866
3994
  function createOxcTransformPlugin(config, environment) {
3995
+ if (config.framework === "react") return reactPlugin(config, environment);
3867
3996
  return {
3868
3997
  name: "nasti:oxc-transform",
3869
3998
  transform(code, id) {
@@ -3884,7 +4013,7 @@ async function build(inlineConfig = {}) {
3884
4013
  const startTime = performance.now();
3885
4014
  logger.info(
3886
4015
  pc4.cyan(`
3887
- nasti v${"2.4.4"} `) + pc4.green(`building for ${config.mode}...`)
4016
+ nasti v${"2.5.0"} `) + pc4.green(`building for ${config.mode}...`)
3888
4017
  );
3889
4018
  debug5?.(`root: ${config.root}`);
3890
4019
  const buildableNames = Object.keys(config.environments).filter((name) => {
@@ -4166,6 +4295,7 @@ var init_build = __esm({
4166
4295
  init_environment();
4167
4296
  init_css_engine();
4168
4297
  init_html();
4298
+ init_react();
4169
4299
  init_transformer();
4170
4300
  init_env();
4171
4301
  init_reporter();
@@ -4618,22 +4748,35 @@ async function transformRequest(url, ctx) {
4618
4748
  }
4619
4749
  const stableUrl = cleanReqUrl;
4620
4750
  let wrappedWithRefresh = false;
4621
- if (shouldTransform(filePath)) {
4622
- const isJsx = /\.[jt]sx$/.test(filePath);
4623
- const useRefresh = isJsx && config.framework !== "vue";
4751
+ if (config.framework === "react") {
4752
+ const refreshEnabled = (ctx.environment?.consumer ?? "client") === "client" && config.server.hmr !== false;
4753
+ const useRefresh = refreshEnabled && (!!config.react.compiler || /\.[jt]sx$/.test(filePath));
4754
+ const result = await transformReactCode(filePath, code, {
4755
+ react: config.react,
4756
+ consumer: ctx.environment?.consumer ?? "client",
4757
+ development: true,
4758
+ reactRefresh: useRefresh,
4759
+ sourcemap: true,
4760
+ target: ctx.environment?.options.build.target ?? config.build.target,
4761
+ onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`)
4762
+ });
4763
+ if (result) {
4764
+ code = result.code;
4765
+ if (result.map) map = JSON.parse(result.map);
4766
+ if (useRefresh) {
4767
+ code = buildReactRefreshWrapper(stableUrl, code);
4768
+ wrappedWithRefresh = true;
4769
+ }
4770
+ }
4771
+ } else if (shouldTransform(filePath)) {
4624
4772
  const result = transformCode(filePath, code, {
4625
4773
  sourcemap: true,
4626
4774
  jsxRuntime: "automatic",
4627
- jsxImportSource: config.framework === "vue" ? "vue" : "react",
4628
- reactRefresh: useRefresh,
4775
+ jsxImportSource: "vue",
4629
4776
  target: ctx.environment?.options.build.target ?? config.build.target
4630
4777
  });
4631
4778
  code = result.code;
4632
4779
  if (result.map) map = JSON.parse(result.map);
4633
- if (useRefresh) {
4634
- code = buildReactRefreshWrapper(stableUrl, code);
4635
- wrappedWithRefresh = true;
4636
- }
4637
4780
  }
4638
4781
  const hotInfo = rewriteHotAcceptDeps(code, config, filePath);
4639
4782
  code = hotInfo.code;
@@ -5796,12 +5939,32 @@ var init_runnable_environment = __esm({
5796
5939
  if (transformed != null) {
5797
5940
  code = typeof transformed === "string" ? transformed : transformed.code;
5798
5941
  }
5799
- if (shouldTransform(cleanId)) {
5942
+ if (this.config.framework === "react") {
5943
+ const result = await transformReactCode(cleanId, code, {
5944
+ react: this.config.react,
5945
+ consumer: this.environment.consumer,
5946
+ development: true,
5947
+ sourcemap: false,
5948
+ target: this.environment.options.build.target,
5949
+ onWarning: (message) => this.config.logger.warn(`[nasti:react] ${message}`)
5950
+ });
5951
+ if (result) {
5952
+ code = result.code;
5953
+ } else if (shouldTransform(cleanId)) {
5954
+ const fallback = transformCode(cleanId, code, {
5955
+ sourcemap: false,
5956
+ jsxRuntime: this.config.react.jsxRuntime,
5957
+ jsxImportSource: this.config.react.jsxImportSource,
5958
+ target: this.environment.options.build.target
5959
+ });
5960
+ code = fallback.code;
5961
+ }
5962
+ } else if (shouldTransform(cleanId)) {
5800
5963
  const result = transformCode(cleanId, code, {
5801
5964
  sourcemap: false,
5802
5965
  target: this.environment.options.build.target,
5803
5966
  jsxRuntime: "automatic",
5804
- jsxImportSource: this.config.framework === "vue" ? "vue" : "react"
5967
+ jsxImportSource: "vue"
5805
5968
  });
5806
5969
  code = result.code;
5807
5970
  }
@@ -5952,11 +6115,11 @@ async function createBundledDevServer(opts) {
5952
6115
  const patches = new MemoryFiles();
5953
6116
  const entryFileNames = /* @__PURE__ */ new Map();
5954
6117
  const bundledClients = /* @__PURE__ */ new Map();
5955
- const useReactRefresh = config.framework !== "vue" && refreshWrapperFn != null;
6118
+ const useReactRefresh = config.framework === "react" && config.server.hmr !== false && refreshWrapperFn != null;
5956
6119
  const rolldownPlugins = [
5957
6120
  ...useReactRefresh ? [
5958
6121
  createReactRefreshRuntimePlugin(entryPoints),
5959
- createBundledOxcRefreshPlugin()
6122
+ createBundledOxcRefreshPlugin(config)
5960
6123
  ] : [],
5961
6124
  ...stripCatchAllLoad(toRolldownPlugins(clientEnv.plugins, clientEnv)),
5962
6125
  ...useReactRefresh ? [
@@ -6227,18 +6390,21 @@ ${code}`, map: null };
6227
6390
  }
6228
6391
  };
6229
6392
  }
6230
- function createBundledOxcRefreshPlugin() {
6393
+ function createBundledOxcRefreshPlugin(config) {
6231
6394
  return {
6232
6395
  name: "nasti:bundled-oxc-refresh",
6233
- transform(code, id) {
6396
+ async transform(code, id) {
6234
6397
  const clean = id.split("?")[0];
6235
- if (!/\.[jt]sx$/.test(clean) || clean.includes("/node_modules/")) return null;
6236
- const result = transformCode(clean, code, {
6398
+ const result = await transformReactCode(clean, code, {
6399
+ react: config.react,
6400
+ consumer: "client",
6401
+ development: true,
6402
+ reactRefresh: true,
6237
6403
  sourcemap: true,
6238
- jsxRuntime: "automatic",
6239
- jsxImportSource: "react",
6240
- reactRefresh: true
6404
+ target: config.build.target,
6405
+ onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`)
6241
6406
  });
6407
+ if (!result) return null;
6242
6408
  return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
6243
6409
  }
6244
6410
  };
@@ -6677,7 +6843,7 @@ async function createServer(inlineConfig = {}) {
6677
6843
  const readyIn = Math.ceil(performance.now() - startTime);
6678
6844
  logger.info(
6679
6845
  `
6680
- ${pc9.cyan(pc9.bold("NASTI"))} ${pc9.cyan(`v${"2.4.4"}`)} ${pc9.dim("ready in")} ${pc9.bold(readyIn)} ${pc9.dim("ms")}
6846
+ ${pc9.cyan(pc9.bold("NASTI"))} ${pc9.cyan(`v${"2.5.0"}`)} ${pc9.dim("ready in")} ${pc9.bold(readyIn)} ${pc9.dim("ms")}
6681
6847
  `
6682
6848
  );
6683
6849
  printServerUrls(
@@ -6870,7 +7036,7 @@ async function buildElectron(inlineConfig = {}) {
6870
7036
  const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
6871
7037
  const startTime = performance.now();
6872
7038
  assertElectronVersion(config);
6873
- console.log(pc5.cyan("\n\u26A1 nasti build (electron)") + pc5.dim(` v${"2.4.4"}`));
7039
+ console.log(pc5.cyan("\n\u26A1 nasti build (electron)") + pc5.dim(` v${"2.5.0"}`));
6874
7040
  console.log(pc5.dim(` root: ${config.root}`));
6875
7041
  console.log(pc5.dim(` mode: ${config.mode}`));
6876
7042
  console.log(pc5.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
@@ -6936,14 +7102,21 @@ async function bundleNode(config, entry, opts) {
6936
7102
  };
6937
7103
  const oxcTransformPlugin = {
6938
7104
  name: "nasti:oxc-transform",
6939
- transform(code, id) {
6940
- if (!shouldTransform(id)) return null;
6941
- const result = transformCode(id, code, {
7105
+ async transform(code, id) {
7106
+ const result = config.framework === "react" ? await transformReactCode(id, code, {
7107
+ react: config.react,
7108
+ consumer: "server",
7109
+ development: config.mode === "development",
7110
+ sourcemap: !!config.build.sourcemap,
7111
+ target: config.electron.nodeTarget,
7112
+ onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`)
7113
+ }) : shouldTransform(id) ? transformCode(id, code, {
6942
7114
  sourcemap: !!config.build.sourcemap,
6943
7115
  jsxRuntime: "automatic",
6944
- jsxImportSource: config.framework === "vue" ? "vue" : "react",
7116
+ jsxImportSource: "vue",
6945
7117
  target: config.electron.nodeTarget
6946
- });
7118
+ }) : null;
7119
+ if (!result) return null;
6947
7120
  return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
6948
7121
  }
6949
7122
  };
@@ -7051,7 +7224,7 @@ async function startElectronDev(inlineConfig = {}) {
7051
7224
  const { noSpawn, ...rest } = inlineConfig;
7052
7225
  const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
7053
7226
  warnElectronVersion(config);
7054
- console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.4.4"}`));
7227
+ console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.5.0"}`));
7055
7228
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
7056
7229
  const server = await createServer2({
7057
7230
  ...rest,
@@ -7176,14 +7349,21 @@ async function compileNode(config, entry, opts) {
7176
7349
  };
7177
7350
  const oxcTransformPlugin = {
7178
7351
  name: "nasti:oxc-transform",
7179
- transform(code, id) {
7180
- if (!shouldTransform(id)) return null;
7181
- const result = transformCode(id, code, {
7352
+ async transform(code, id) {
7353
+ const result = config.framework === "react" ? await transformReactCode(id, code, {
7354
+ react: config.react,
7355
+ consumer: "server",
7356
+ development: true,
7357
+ sourcemap: true,
7358
+ target: config.electron.nodeTarget,
7359
+ onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`)
7360
+ }) : shouldTransform(id) ? transformCode(id, code, {
7182
7361
  sourcemap: true,
7183
7362
  jsxRuntime: "automatic",
7184
- jsxImportSource: config.framework === "vue" ? "vue" : "react",
7363
+ jsxImportSource: "vue",
7185
7364
  target: config.electron.nodeTarget
7186
- });
7365
+ }) : null;
7366
+ if (!result) return null;
7187
7367
  return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
7188
7368
  }
7189
7369
  };
@@ -7462,6 +7642,345 @@ ${e.stack || ""}`
7462
7642
  };
7463
7643
  }
7464
7644
 
7645
+ // src/plugins/rsc.ts
7646
+ init_transformer();
7647
+ import fs15 from "fs";
7648
+ import path20 from "path";
7649
+ import { parseSync } from "oxc-parser";
7650
+ var SERVER_RUNTIME_ID = "virtual:nasti-rsc/server-runtime";
7651
+ var CLIENT_RUNTIME_ID = "virtual:nasti-rsc/client-runtime";
7652
+ var RESOLVED_SERVER_RUNTIME_ID = `\0${SERVER_RUNTIME_ID}`;
7653
+ var RESOLVED_CLIENT_RUNTIME_ID = `\0${CLIENT_RUNTIME_ID}`;
7654
+ function rsc(options = {}) {
7655
+ const names = {
7656
+ client: options.environment?.client ?? "client",
7657
+ ssr: options.environment?.ssr ?? "ssr",
7658
+ rsc: options.environment?.rsc ?? "rsc"
7659
+ };
7660
+ const clientReferences = /* @__PURE__ */ new Map();
7661
+ const serverReferences = /* @__PURE__ */ new Map();
7662
+ let resolvedConfig;
7663
+ return {
7664
+ name: "nasti:rsc",
7665
+ enforce: "post",
7666
+ config(config, env) {
7667
+ const root = path20.resolve(config.root ?? ".");
7668
+ const include = options.include ?? config.react?.include ?? /\.[tj]sx?$/;
7669
+ const exclude = options.exclude ?? config.react?.exclude ?? /node_modules/;
7670
+ const discoveredClients = env.command === "build" ? discoverDirectiveModules(root, options.scanDirectories, include, exclude) : [];
7671
+ for (const item of discoveredClients) {
7672
+ if (item.module.useClient) {
7673
+ clientReferences.set(
7674
+ item.id,
7675
+ new Set(item.module.exports.map((entry) => entry.exported))
7676
+ );
7677
+ }
7678
+ if (item.module.useServer) {
7679
+ serverReferences.set(
7680
+ item.id,
7681
+ new Set(item.module.exports.map((entry) => entry.exported))
7682
+ );
7683
+ }
7684
+ }
7685
+ const environments = { ...config.environments ?? {} };
7686
+ if (names.client !== "client") {
7687
+ environments.client = {
7688
+ ...environments.client ?? {},
7689
+ buildEnabled: false
7690
+ };
7691
+ }
7692
+ const client = { ...environments[names.client] ?? {} };
7693
+ const clientSeeds = collectClientSeeds(root, config, client, options.entries?.client);
7694
+ const generatedClientEntries = discoveredClients.filter((item) => item.module.useClient).map((item) => item.id);
7695
+ client.consumer = "client";
7696
+ client.entry = uniqueEntries([...clientSeeds, ...generatedClientEntries]);
7697
+ environments[names.client] = client;
7698
+ const rscEnvironment = { ...environments[names.rsc] ?? {} };
7699
+ rscEnvironment.consumer = "server";
7700
+ const rscEntries = normalizeEntries(options.entries?.rsc);
7701
+ if (rscEntries.length > 0) rscEnvironment.entry = rscEntries;
7702
+ environments[names.rsc] = rscEnvironment;
7703
+ const ssrEntries = normalizeEntries(options.entries?.ssr);
7704
+ if (ssrEntries.length > 0 || environments[names.ssr]) {
7705
+ environments[names.ssr] = {
7706
+ ...environments[names.ssr] ?? {},
7707
+ consumer: "server",
7708
+ ...ssrEntries.length > 0 ? { entry: ssrEntries } : {}
7709
+ };
7710
+ }
7711
+ return { environments };
7712
+ },
7713
+ configEnvironment(name, environment) {
7714
+ if (name !== names.rsc) return;
7715
+ const conditions = environment.resolve?.conditions ?? ["node", "import", "module", "default"];
7716
+ return {
7717
+ consumer: "server",
7718
+ resolve: {
7719
+ ...environment.resolve,
7720
+ conditions: ["react-server", ...conditions.filter((item) => item !== "react-server")]
7721
+ }
7722
+ };
7723
+ },
7724
+ configResolved(config) {
7725
+ if (config.framework !== "react") {
7726
+ throw new Error('[nasti:rsc] the RSC generator requires framework: "react"');
7727
+ }
7728
+ resolvedConfig = config;
7729
+ },
7730
+ resolveId(source) {
7731
+ if (source === SERVER_RUNTIME_ID) return RESOLVED_SERVER_RUNTIME_ID;
7732
+ if (source === CLIENT_RUNTIME_ID) return RESOLVED_CLIENT_RUNTIME_ID;
7733
+ return null;
7734
+ },
7735
+ load(id) {
7736
+ if (id === RESOLVED_SERVER_RUNTIME_ID) return createServerRuntimeModule();
7737
+ if (id === RESOLVED_CLIENT_RUNTIME_ID) {
7738
+ const browser = this.environment?.name === names.client;
7739
+ return createClientRuntimeModule(browser);
7740
+ }
7741
+ return null;
7742
+ },
7743
+ transform(code, id) {
7744
+ if (!resolvedConfig || !isSourceModule(id, resolvedConfig, options)) return null;
7745
+ const cleanId = cleanModuleId(id);
7746
+ const module = scanModule(cleanId, code);
7747
+ if (!module) return null;
7748
+ if (module.useClient && module.useServer) {
7749
+ throw new Error(`[nasti:rsc] ${displayId(resolvedConfig.root, cleanId)} cannot contain both "use client" and "use server"`);
7750
+ }
7751
+ const moduleId = referenceModuleId(resolvedConfig.root, cleanId);
7752
+ if (module.useClient) {
7753
+ clientReferences.set(cleanId, new Set(module.exports.map((entry) => entry.exported)));
7754
+ if (this.environment?.name === names.rsc) {
7755
+ return {
7756
+ code: createClientReferenceProxy(moduleId, module.exports),
7757
+ map: { mappings: "" }
7758
+ };
7759
+ }
7760
+ }
7761
+ if (module.useServer) {
7762
+ serverReferences.set(cleanId, new Set(module.exports.map((entry) => entry.exported)));
7763
+ if (this.environment?.name === names.rsc) {
7764
+ return {
7765
+ code: registerServerReferences(code, moduleId, module.exports, cleanId),
7766
+ map: { mappings: "" }
7767
+ };
7768
+ }
7769
+ if (this.environment?.consumer === "client" || this.environment?.name === names.client || this.environment?.name === names.ssr) {
7770
+ return {
7771
+ code: createServerReferenceProxy(moduleId, module.exports),
7772
+ map: { mappings: "" }
7773
+ };
7774
+ }
7775
+ }
7776
+ return null;
7777
+ },
7778
+ afterBuildApp(results, _api, context) {
7779
+ if (options.manifestFile === false) return;
7780
+ const manifest = createManifest(
7781
+ names,
7782
+ resolvedConfig?.root ?? "",
7783
+ results,
7784
+ clientReferences,
7785
+ serverReferences
7786
+ );
7787
+ const output = {
7788
+ type: "asset",
7789
+ fileName: options.manifestFile ?? "rsc-manifest.json",
7790
+ source: JSON.stringify(manifest, null, 2) + "\n"
7791
+ };
7792
+ context.emitFile(output);
7793
+ }
7794
+ };
7795
+ }
7796
+ function scanModule(filename, code) {
7797
+ if (!code.includes("use client") && !code.includes("use server")) return null;
7798
+ const parsed = parseSync(filename, code);
7799
+ if (parsed.errors.length > 0) return null;
7800
+ const directives = new Set(
7801
+ parsed.program.body.filter((node) => node.type === "ExpressionStatement" && typeof node.directive === "string").map((node) => node.directive)
7802
+ );
7803
+ const useClient = directives.has("use client");
7804
+ const useServer = directives.has("use server");
7805
+ if (!useClient && !useServer) return null;
7806
+ const exports = [];
7807
+ for (const statement of parsed.module.staticExports) {
7808
+ for (const entry of statement.entries) {
7809
+ if (entry.isType) continue;
7810
+ const exported = entry.exportName.kind === "Default" ? "default" : entry.exportName.name;
7811
+ if (!exported) {
7812
+ throw new Error(
7813
+ `[nasti:rsc] ${filename} uses export * inside an RSC directive module; replace it with explicit named exports so reference identities are stable`
7814
+ );
7815
+ }
7816
+ exports.push({
7817
+ exported,
7818
+ local: entry.localName.kind === "Name" ? entry.localName.name ?? void 0 : void 0,
7819
+ reexported: entry.moduleRequest !== null
7820
+ });
7821
+ }
7822
+ }
7823
+ return { useClient, useServer, exports: dedupeExports(exports) };
7824
+ }
7825
+ function createClientReferenceProxy(moduleId, exports) {
7826
+ const lines = [
7827
+ `import { registerClientReference as __nasti_register_client__ } from ${JSON.stringify(SERVER_RUNTIME_ID)};`
7828
+ ];
7829
+ exports.forEach((entry, index2) => {
7830
+ const local = `__nasti_client_reference_${index2}__`;
7831
+ const message = `Cannot call client export ${moduleId}#${entry.exported} from the RSC server graph.`;
7832
+ lines.push(
7833
+ `const ${local} = __nasti_register_client__(function () { throw new Error(${JSON.stringify(message)}); }, ${JSON.stringify(moduleId)}, ${JSON.stringify(entry.exported)});`,
7834
+ exportLocal(local, entry.exported)
7835
+ );
7836
+ });
7837
+ return lines.join("\n") + "\n";
7838
+ }
7839
+ function registerServerReferences(code, moduleId, exports, filename) {
7840
+ const registrations = [];
7841
+ for (const entry of exports) {
7842
+ if (entry.reexported || !entry.local) {
7843
+ throw new Error(
7844
+ `[nasti:rsc] ${filename} must use named local bindings for "use server" exports; ${entry.exported} cannot be registered safely`
7845
+ );
7846
+ }
7847
+ registrations.push(
7848
+ `__nasti_register_server__(${entry.local}, ${JSON.stringify(moduleId)}, ${JSON.stringify(entry.exported)});`
7849
+ );
7850
+ }
7851
+ return [
7852
+ code,
7853
+ `import { registerServerReference as __nasti_register_server__ } from ${JSON.stringify(SERVER_RUNTIME_ID)};`,
7854
+ ...registrations,
7855
+ ""
7856
+ ].join("\n");
7857
+ }
7858
+ function createServerReferenceProxy(moduleId, exports) {
7859
+ const lines = [
7860
+ `import { createServerReference as __nasti_create_server__ } from ${JSON.stringify(CLIENT_RUNTIME_ID)};`
7861
+ ];
7862
+ exports.forEach((entry, index2) => {
7863
+ const local = `__nasti_server_reference_${index2}__`;
7864
+ lines.push(
7865
+ `const ${local} = __nasti_create_server__(${JSON.stringify(moduleId)}, ${JSON.stringify(entry.exported)});`,
7866
+ exportLocal(local, entry.exported)
7867
+ );
7868
+ });
7869
+ return lines.join("\n") + "\n";
7870
+ }
7871
+ function exportLocal(local, exported) {
7872
+ if (exported === "default") return `export default ${local};`;
7873
+ const name = /^[A-Za-z_$][\w$]*$/.test(exported) ? exported : JSON.stringify(exported);
7874
+ return `export { ${local} as ${name} };`;
7875
+ }
7876
+ function createServerRuntimeModule() {
7877
+ return [
7878
+ `export { registerClientReference, registerServerReference } from "react-server-dom-webpack/server.edge";`,
7879
+ ""
7880
+ ].join("\n");
7881
+ }
7882
+ function createClientRuntimeModule(browser) {
7883
+ const source = browser ? "react-server-dom-webpack/client.browser" : "react-server-dom-webpack/client.edge";
7884
+ return `
7885
+ import { createServerReference as __createServerReference } from ${JSON.stringify(source)};
7886
+ const __callServer = (id, args) => {
7887
+ const handler = globalThis[Symbol.for("nasti.rsc.callServer")];
7888
+ if (typeof handler !== "function") {
7889
+ throw new Error("No RSC callServer handler is installed. Set globalThis[Symbol.for('nasti.rsc.callServer')].");
7890
+ }
7891
+ return handler(id, args);
7892
+ };
7893
+ export function createServerReference(moduleId, name) {
7894
+ return __createServerReference(moduleId + "#" + name, __callServer, undefined, undefined, name);
7895
+ }
7896
+ `.trimStart();
7897
+ }
7898
+ function createManifest(names, root, results, clients, servers) {
7899
+ return {
7900
+ version: 1,
7901
+ environments: names,
7902
+ clientReferences: manifestReferences(root, results[names.client], clients),
7903
+ serverReferences: manifestReferences(root, results[names.rsc], servers)
7904
+ };
7905
+ }
7906
+ function manifestReferences(root, result, references) {
7907
+ const output = {};
7908
+ for (const [id, names] of [...references].sort(([a], [b]) => a.localeCompare(b))) {
7909
+ const chunks = Object.values(result?.chunks ?? {}).filter((chunk) => chunk.moduleIds.some((moduleId) => cleanModuleId(moduleId) === id)).map((chunk) => chunk.fileName).sort();
7910
+ for (const name of [...names].sort()) {
7911
+ const moduleId = referenceModuleId(root, id);
7912
+ output[`${moduleId}#${name}`] = { id: moduleId, name, chunks };
7913
+ }
7914
+ }
7915
+ return output;
7916
+ }
7917
+ function discoverDirectiveModules(root, scanDirectories, include, exclude) {
7918
+ const requested = scanDirectories ?? ["app", "src"];
7919
+ const roots = requested.map((directory) => path20.resolve(root, directory)).filter((directory) => fs15.existsSync(directory));
7920
+ if (roots.length === 0) roots.push(root);
7921
+ const output = [];
7922
+ for (const directory of roots) walk(directory);
7923
+ return output;
7924
+ function walk(directory) {
7925
+ for (const entry of fs15.readdirSync(directory, { withFileTypes: true })) {
7926
+ if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".nasti") continue;
7927
+ const id = path20.join(directory, entry.name);
7928
+ if (entry.isDirectory()) {
7929
+ walk(id);
7930
+ continue;
7931
+ }
7932
+ if (!entry.isFile() || !matchesReactFilter(id, include, exclude)) continue;
7933
+ const module = scanModule(id, fs15.readFileSync(id, "utf-8"));
7934
+ if (module) output.push({ id, module });
7935
+ }
7936
+ }
7937
+ }
7938
+ function collectClientSeeds(root, config, client, configured) {
7939
+ const explicit = [
7940
+ ...normalizeEntries(client.entry),
7941
+ ...normalizeEntries(configured)
7942
+ ];
7943
+ if (explicit.length > 0) return explicit;
7944
+ const htmlFile = path20.resolve(root, client.html ?? "index.html");
7945
+ if (fs15.existsSync(htmlFile)) {
7946
+ const html = fs15.readFileSync(htmlFile, "utf-8");
7947
+ const htmlDir = path20.dirname(htmlFile);
7948
+ const entries = [...html.matchAll(/<script[^>]+src=["']([^"']+)["'][^>]*>/gi)].map((match) => match[1]?.split(/[?#]/, 1)[0]).filter((entry) => !!entry && !entry.startsWith("http")).map((entry) => entry.startsWith("/") ? path20.resolve(root, entry.slice(1)) : path20.resolve(htmlDir, entry));
7949
+ if (entries.length > 0) return entries;
7950
+ }
7951
+ for (const entry of ["src/main.ts", "src/main.tsx", "src/main.js", "src/index.ts", "src/index.tsx", "src/index.js"]) {
7952
+ if (fs15.existsSync(path20.resolve(root, entry))) return [entry];
7953
+ }
7954
+ return [];
7955
+ }
7956
+ function normalizeEntries(entries) {
7957
+ return entries == null ? [] : Array.isArray(entries) ? entries : [entries];
7958
+ }
7959
+ function uniqueEntries(entries) {
7960
+ return [...new Set(entries)];
7961
+ }
7962
+ function isSourceModule(id, config, options) {
7963
+ const cleanId = cleanModuleId(id);
7964
+ if (cleanId.startsWith("\0")) return false;
7965
+ const include = options.include ?? config.react.include;
7966
+ const exclude = options.exclude ?? config.react.exclude;
7967
+ return matchesReactFilter(cleanId, include, exclude);
7968
+ }
7969
+ function cleanModuleId(id) {
7970
+ return id.split(/[?#]/, 1)[0];
7971
+ }
7972
+ function referenceModuleId(root, id) {
7973
+ if (!root || !path20.isAbsolute(id)) return id.replaceAll(path20.sep, "/");
7974
+ const relative = path20.relative(root, id).replaceAll(path20.sep, "/");
7975
+ return relative.startsWith("../") ? id.replaceAll(path20.sep, "/") : `/${relative}`;
7976
+ }
7977
+ function displayId(root, id) {
7978
+ return referenceModuleId(root, id);
7979
+ }
7980
+ function dedupeExports(exports) {
7981
+ return [...new Map(exports.map((entry) => [entry.exported, entry])).values()];
7982
+ }
7983
+
7465
7984
  // src/index.ts
7466
7985
  init_environment();
7467
7986
  init_hot_channel();
@@ -7489,6 +8008,7 @@ export {
7489
8008
  printServerUrls,
7490
8009
  resolveConfig,
7491
8010
  resolveEnvironmentPlugins,
8011
+ rsc,
7492
8012
  ssrDefineOverrides,
7493
8013
  startElectronDev
7494
8014
  };