@tanstack/start-plugin-core 1.166.12 → 1.166.13

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.
Files changed (87) hide show
  1. package/dist/esm/build-sitemap.js +94 -123
  2. package/dist/esm/build-sitemap.js.map +1 -1
  3. package/dist/esm/constants.js +15 -20
  4. package/dist/esm/constants.js.map +1 -1
  5. package/dist/esm/dev-server-plugin/dev-styles.js +137 -150
  6. package/dist/esm/dev-server-plugin/dev-styles.js.map +1 -1
  7. package/dist/esm/dev-server-plugin/extract-html-scripts.js +16 -15
  8. package/dist/esm/dev-server-plugin/extract-html-scripts.js.map +1 -1
  9. package/dist/esm/dev-server-plugin/plugin.js +125 -195
  10. package/dist/esm/dev-server-plugin/plugin.js.map +1 -1
  11. package/dist/esm/import-protection-plugin/ast.js +6 -5
  12. package/dist/esm/import-protection-plugin/ast.js.map +1 -1
  13. package/dist/esm/import-protection-plugin/constants.js +20 -22
  14. package/dist/esm/import-protection-plugin/constants.js.map +1 -1
  15. package/dist/esm/import-protection-plugin/defaults.js +35 -25
  16. package/dist/esm/import-protection-plugin/defaults.js.map +1 -1
  17. package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js +93 -92
  18. package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js.map +1 -1
  19. package/dist/esm/import-protection-plugin/matchers.js +23 -24
  20. package/dist/esm/import-protection-plugin/matchers.js.map +1 -1
  21. package/dist/esm/import-protection-plugin/plugin.js +1045 -1361
  22. package/dist/esm/import-protection-plugin/plugin.js.map +1 -1
  23. package/dist/esm/import-protection-plugin/postCompileUsage.js +58 -55
  24. package/dist/esm/import-protection-plugin/postCompileUsage.js.map +1 -1
  25. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js +187 -259
  26. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js.map +1 -1
  27. package/dist/esm/import-protection-plugin/sourceLocation.js +238 -248
  28. package/dist/esm/import-protection-plugin/sourceLocation.js.map +1 -1
  29. package/dist/esm/import-protection-plugin/trace.js +173 -184
  30. package/dist/esm/import-protection-plugin/trace.js.map +1 -1
  31. package/dist/esm/import-protection-plugin/utils.js +132 -111
  32. package/dist/esm/import-protection-plugin/utils.js.map +1 -1
  33. package/dist/esm/import-protection-plugin/virtualModules.js +216 -196
  34. package/dist/esm/import-protection-plugin/virtualModules.js.map +1 -1
  35. package/dist/esm/index.js +2 -7
  36. package/dist/esm/load-env-plugin/plugin.js +12 -11
  37. package/dist/esm/load-env-plugin/plugin.js.map +1 -1
  38. package/dist/esm/output-directory.js +10 -10
  39. package/dist/esm/output-directory.js.map +1 -1
  40. package/dist/esm/plugin.js +275 -355
  41. package/dist/esm/plugin.js.map +1 -1
  42. package/dist/esm/post-server-build.js +39 -53
  43. package/dist/esm/post-server-build.js.map +1 -1
  44. package/dist/esm/prerender.js +177 -239
  45. package/dist/esm/prerender.js.map +1 -1
  46. package/dist/esm/preview-server-plugin/plugin.js +41 -44
  47. package/dist/esm/preview-server-plugin/plugin.js.map +1 -1
  48. package/dist/esm/queue.js +115 -126
  49. package/dist/esm/queue.js.map +1 -1
  50. package/dist/esm/resolve-entries.js +31 -32
  51. package/dist/esm/resolve-entries.js.map +1 -1
  52. package/dist/esm/schema.js +156 -179
  53. package/dist/esm/schema.js.map +1 -1
  54. package/dist/esm/start-compiler-plugin/compiler.js +655 -812
  55. package/dist/esm/start-compiler-plugin/compiler.js.map +1 -1
  56. package/dist/esm/start-compiler-plugin/handleClientOnlyJSX.js +25 -8
  57. package/dist/esm/start-compiler-plugin/handleClientOnlyJSX.js.map +1 -1
  58. package/dist/esm/start-compiler-plugin/handleCreateIsomorphicFn.js +22 -19
  59. package/dist/esm/start-compiler-plugin/handleCreateIsomorphicFn.js.map +1 -1
  60. package/dist/esm/start-compiler-plugin/handleCreateMiddleware.js +20 -22
  61. package/dist/esm/start-compiler-plugin/handleCreateMiddleware.js.map +1 -1
  62. package/dist/esm/start-compiler-plugin/handleCreateServerFn.js +187 -255
  63. package/dist/esm/start-compiler-plugin/handleCreateServerFn.js.map +1 -1
  64. package/dist/esm/start-compiler-plugin/handleEnvOnly.js +23 -33
  65. package/dist/esm/start-compiler-plugin/handleEnvOnly.js.map +1 -1
  66. package/dist/esm/start-compiler-plugin/plugin.js +247 -291
  67. package/dist/esm/start-compiler-plugin/plugin.js.map +1 -1
  68. package/dist/esm/start-compiler-plugin/utils.js +27 -27
  69. package/dist/esm/start-compiler-plugin/utils.js.map +1 -1
  70. package/dist/esm/start-manifest-plugin/manifestBuilder.js +272 -378
  71. package/dist/esm/start-manifest-plugin/manifestBuilder.js.map +1 -1
  72. package/dist/esm/start-manifest-plugin/plugin.js +35 -44
  73. package/dist/esm/start-manifest-plugin/plugin.js.map +1 -1
  74. package/dist/esm/start-router-plugin/constants.js +6 -5
  75. package/dist/esm/start-router-plugin/constants.js.map +1 -1
  76. package/dist/esm/start-router-plugin/generator-plugins/prerender-routes-plugin.js +24 -19
  77. package/dist/esm/start-router-plugin/generator-plugins/prerender-routes-plugin.js.map +1 -1
  78. package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js +28 -29
  79. package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js.map +1 -1
  80. package/dist/esm/start-router-plugin/plugin.js +146 -199
  81. package/dist/esm/start-router-plugin/plugin.js.map +1 -1
  82. package/dist/esm/start-router-plugin/pruneServerOnlySubtrees.js +32 -31
  83. package/dist/esm/start-router-plugin/pruneServerOnlySubtrees.js.map +1 -1
  84. package/dist/esm/utils.js +14 -14
  85. package/dist/esm/utils.js.map +1 -1
  86. package/package.json +7 -7
  87. package/dist/esm/index.js.map +0 -1
@@ -1,841 +1,684 @@
1
- import crypto from "node:crypto";
2
- import * as t from "@babel/types";
3
- import { parseAst, findReferencedIdentifiers, deadCodeElimination, generateFromAst } from "@tanstack/router-utils";
4
- import babel from "@babel/core";
5
1
  import { handleCreateServerFn } from "./handleCreateServerFn.js";
6
2
  import { handleCreateMiddleware } from "./handleCreateMiddleware.js";
7
3
  import { handleCreateIsomorphicFn } from "./handleCreateIsomorphicFn.js";
8
4
  import { handleEnvOnlyFn } from "./handleEnvOnly.js";
9
5
  import { handleClientOnlyJSX } from "./handleClientOnlyJSX.js";
6
+ import crypto from "node:crypto";
7
+ import * as t from "@babel/types";
8
+ import { deadCodeElimination, findReferencedIdentifiers, generateFromAst, parseAst } from "@tanstack/router-utils";
9
+ import babel from "@babel/core";
10
+ //#region src/start-compiler-plugin/compiler.ts
10
11
  function isLookupKind(kind) {
11
- return kind in LookupSetup;
12
+ return kind in LookupSetup;
12
13
  }
13
- const LookupSetup = {
14
- ServerFn: {
15
- type: "methodChain",
16
- candidateCallIdentifier: /* @__PURE__ */ new Set(["handler"])
17
- },
18
- Middleware: {
19
- type: "methodChain",
20
- candidateCallIdentifier: /* @__PURE__ */ new Set(["server", "client", "createMiddlewares"])
21
- },
22
- IsomorphicFn: {
23
- type: "methodChain",
24
- candidateCallIdentifier: /* @__PURE__ */ new Set(["server", "client"])
25
- },
26
- ServerOnlyFn: { type: "directCall", factoryName: "createServerOnlyFn" },
27
- ClientOnlyFn: { type: "directCall", factoryName: "createClientOnlyFn" },
28
- ClientOnlyJSX: { type: "jsx", componentName: "ClientOnly" }
14
+ var LookupSetup = {
15
+ ServerFn: {
16
+ type: "methodChain",
17
+ candidateCallIdentifier: new Set(["handler"])
18
+ },
19
+ Middleware: {
20
+ type: "methodChain",
21
+ candidateCallIdentifier: new Set([
22
+ "server",
23
+ "client",
24
+ "createMiddlewares"
25
+ ])
26
+ },
27
+ IsomorphicFn: {
28
+ type: "methodChain",
29
+ candidateCallIdentifier: new Set(["server", "client"])
30
+ },
31
+ ServerOnlyFn: {
32
+ type: "directCall",
33
+ factoryName: "createServerOnlyFn"
34
+ },
35
+ ClientOnlyFn: {
36
+ type: "directCall",
37
+ factoryName: "createClientOnlyFn"
38
+ },
39
+ ClientOnlyJSX: {
40
+ type: "jsx",
41
+ componentName: "ClientOnly"
42
+ }
29
43
  };
30
- const KindDetectionPatterns = {
31
- ServerFn: /\bcreateServerFn\b|\.\s*handler\s*\(/,
32
- Middleware: /createMiddleware/,
33
- IsomorphicFn: /createIsomorphicFn/,
34
- ServerOnlyFn: /createServerOnlyFn/,
35
- ClientOnlyFn: /createClientOnlyFn/,
36
- ClientOnlyJSX: /<ClientOnly|import\s*\{[^}]*\bClientOnly\b/
44
+ var KindDetectionPatterns = {
45
+ ServerFn: /\bcreateServerFn\b|\.\s*handler\s*\(/,
46
+ Middleware: /createMiddleware/,
47
+ IsomorphicFn: /createIsomorphicFn/,
48
+ ServerOnlyFn: /createServerOnlyFn/,
49
+ ClientOnlyFn: /createClientOnlyFn/,
50
+ ClientOnlyJSX: /<ClientOnly|import\s*\{[^}]*\bClientOnly\b/
37
51
  };
38
- const LookupKindsPerEnv = {
39
- client: /* @__PURE__ */ new Set([
40
- "Middleware",
41
- "ServerFn",
42
- "IsomorphicFn",
43
- "ServerOnlyFn",
44
- "ClientOnlyFn"
45
- ]),
46
- server: /* @__PURE__ */ new Set([
47
- "ServerFn",
48
- "IsomorphicFn",
49
- "ServerOnlyFn",
50
- "ClientOnlyFn",
51
- "ClientOnlyJSX"
52
- // Only transform on server to remove children
53
- ])
52
+ var LookupKindsPerEnv = {
53
+ client: new Set([
54
+ "Middleware",
55
+ "ServerFn",
56
+ "IsomorphicFn",
57
+ "ServerOnlyFn",
58
+ "ClientOnlyFn"
59
+ ]),
60
+ server: new Set([
61
+ "ServerFn",
62
+ "IsomorphicFn",
63
+ "ServerOnlyFn",
64
+ "ClientOnlyFn",
65
+ "ClientOnlyJSX"
66
+ ])
54
67
  };
55
- const KindHandlers = {
56
- ServerFn: handleCreateServerFn,
57
- Middleware: handleCreateMiddleware,
58
- IsomorphicFn: handleCreateIsomorphicFn,
59
- ServerOnlyFn: handleEnvOnlyFn,
60
- ClientOnlyFn: handleEnvOnlyFn
61
- // ClientOnlyJSX is handled separately via JSX traversal, not here
68
+ /**
69
+ * Registry mapping each LookupKind to its handler function.
70
+ * When adding a new kind, add its handler here.
71
+ */
72
+ var KindHandlers = {
73
+ ServerFn: handleCreateServerFn,
74
+ Middleware: handleCreateMiddleware,
75
+ IsomorphicFn: handleCreateIsomorphicFn,
76
+ ServerOnlyFn: handleEnvOnlyFn,
77
+ ClientOnlyFn: handleEnvOnlyFn
62
78
  };
63
- const AllLookupKinds = Object.keys(LookupSetup);
79
+ var AllLookupKinds = Object.keys(LookupSetup);
80
+ /**
81
+ * Detects which LookupKinds are present in the code using string matching.
82
+ * This is a fast pre-scan before AST parsing to limit the work done during compilation.
83
+ */
64
84
  function detectKindsInCode(code, env) {
65
- const detected = /* @__PURE__ */ new Set();
66
- const validForEnv = LookupKindsPerEnv[env];
67
- for (const kind of AllLookupKinds) {
68
- if (validForEnv.has(kind) && KindDetectionPatterns[kind].test(code)) {
69
- detected.add(kind);
70
- }
71
- }
72
- return detected;
85
+ const detected = /* @__PURE__ */ new Set();
86
+ const validForEnv = LookupKindsPerEnv[env];
87
+ for (const kind of AllLookupKinds) if (validForEnv.has(kind) && KindDetectionPatterns[kind].test(code)) detected.add(kind);
88
+ return detected;
73
89
  }
74
- const IdentifierToKinds = /* @__PURE__ */ new Map();
90
+ var IdentifierToKinds = /* @__PURE__ */ new Map();
75
91
  for (const kind of AllLookupKinds) {
76
- const setup = LookupSetup[kind];
77
- if (setup.type === "methodChain") {
78
- for (const id of setup.candidateCallIdentifier) {
79
- let kinds = IdentifierToKinds.get(id);
80
- if (!kinds) {
81
- kinds = /* @__PURE__ */ new Set();
82
- IdentifierToKinds.set(id, kinds);
83
- }
84
- kinds.add(kind);
85
- }
86
- }
92
+ const setup = LookupSetup[kind];
93
+ if (setup.type === "methodChain") for (const id of setup.candidateCallIdentifier) {
94
+ let kinds = IdentifierToKinds.get(id);
95
+ if (!kinds) {
96
+ kinds = /* @__PURE__ */ new Set();
97
+ IdentifierToKinds.set(id, kinds);
98
+ }
99
+ kinds.add(kind);
100
+ }
87
101
  }
88
- const DirectCallFactoryNames = /* @__PURE__ */ new Set();
102
+ var DirectCallFactoryNames = /* @__PURE__ */ new Set();
89
103
  for (const kind of AllLookupKinds) {
90
- const setup = LookupSetup[kind];
91
- if (setup.type === "directCall") {
92
- DirectCallFactoryNames.add(setup.factoryName);
93
- }
104
+ const setup = LookupSetup[kind];
105
+ if (setup.type === "directCall") DirectCallFactoryNames.add(setup.factoryName);
94
106
  }
107
+ /**
108
+ * Computes whether any file kinds need direct-call candidate detection.
109
+ * This applies to directCall types (ServerOnlyFn, ClientOnlyFn).
110
+ */
95
111
  function needsDirectCallDetection(kinds) {
96
- for (const kind of kinds) {
97
- if (LookupSetup[kind].type === "directCall") {
98
- return true;
99
- }
100
- }
101
- return false;
112
+ for (const kind of kinds) if (LookupSetup[kind].type === "directCall") return true;
113
+ return false;
102
114
  }
115
+ /**
116
+ * Checks if all kinds in the set are guaranteed to be top-level only.
117
+ * Only ServerFn is always declared at module level (must be assigned to a variable).
118
+ * Middleware, IsomorphicFn, ServerOnlyFn, ClientOnlyFn can be nested inside functions.
119
+ * When all kinds are top-level-only, we can use a fast scan instead of full traversal.
120
+ */
103
121
  function areAllKindsTopLevelOnly(kinds) {
104
- return kinds.size === 1 && kinds.has("ServerFn");
122
+ return kinds.size === 1 && kinds.has("ServerFn");
105
123
  }
124
+ /**
125
+ * Checks if we need to detect JSX elements (e.g., <ClientOnly>).
126
+ */
106
127
  function needsJSXDetection(kinds) {
107
- for (const kind of kinds) {
108
- if (LookupSetup[kind].type === "jsx") {
109
- return true;
110
- }
111
- }
112
- return false;
128
+ for (const kind of kinds) if (LookupSetup[kind].type === "jsx") return true;
129
+ return false;
113
130
  }
131
+ /**
132
+ * Checks if a CallExpression is a direct-call candidate for NESTED detection.
133
+ * Returns true if the callee is a known factory function name.
134
+ * This is stricter than top-level detection because we need to filter out
135
+ * invocations of existing server functions (e.g., `myServerFn()`).
136
+ */
114
137
  function isNestedDirectCallCandidate(node) {
115
- let calleeName;
116
- if (t.isIdentifier(node.callee)) {
117
- calleeName = node.callee.name;
118
- } else if (t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.property)) {
119
- calleeName = node.callee.property.name;
120
- }
121
- return calleeName !== void 0 && DirectCallFactoryNames.has(calleeName);
138
+ let calleeName;
139
+ if (t.isIdentifier(node.callee)) calleeName = node.callee.name;
140
+ else if (t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.property)) calleeName = node.callee.property.name;
141
+ return calleeName !== void 0 && DirectCallFactoryNames.has(calleeName);
122
142
  }
143
+ /**
144
+ * Checks if a CallExpression path is a top-level direct-call candidate.
145
+ * Top-level means the call is the init of a VariableDeclarator at program level.
146
+ * We accept any simple identifier call or namespace call at top level
147
+ * (e.g., `createServerOnlyFn()`, `TanStackStart.createServerOnlyFn()`) and let
148
+ * resolution verify it. This handles renamed imports.
149
+ */
123
150
  function isTopLevelDirectCallCandidate(path) {
124
- const node = path.node;
125
- const isSimpleCall = t.isIdentifier(node.callee) || t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object) && t.isIdentifier(node.callee.property);
126
- if (!isSimpleCall) {
127
- return false;
128
- }
129
- const parent = path.parent;
130
- if (!t.isVariableDeclarator(parent) || parent.init !== node) {
131
- return false;
132
- }
133
- const grandParent = path.parentPath.parent;
134
- if (!t.isVariableDeclaration(grandParent)) {
135
- return false;
136
- }
137
- return t.isProgram(path.parentPath.parentPath?.parent);
138
- }
139
- class StartCompiler {
140
- constructor(options) {
141
- this.options = options;
142
- this.validLookupKinds = options.lookupKinds;
143
- }
144
- moduleCache = /* @__PURE__ */ new Map();
145
- initialized = false;
146
- validLookupKinds;
147
- resolveIdCache = /* @__PURE__ */ new Map();
148
- exportResolutionCache = /* @__PURE__ */ new Map();
149
- // Fast lookup for direct imports from known libraries (e.g., '@tanstack/react-start')
150
- // Maps: libName → (exportName → Kind)
151
- // This allows O(1) resolution for the common case without async resolveId calls
152
- knownRootImports = /* @__PURE__ */ new Map();
153
- // For generating unique function IDs in production builds
154
- entryIdToFunctionId = /* @__PURE__ */ new Map();
155
- functionIds = /* @__PURE__ */ new Set();
156
- // Cached root path with trailing slash for dev mode function ID generation
157
- _rootWithTrailingSlash;
158
- /**
159
- * Generates a unique function ID for a server function.
160
- * In dev mode, uses a base64-encoded JSON with file path and export name.
161
- * In build mode, uses SHA256 hash or custom generator.
162
- */
163
- generateFunctionId(opts) {
164
- if (this.mode === "dev") {
165
- let file = opts.extractedFilename;
166
- if (opts.extractedFilename.startsWith(this.rootWithTrailingSlash)) {
167
- file = opts.extractedFilename.slice(this.rootWithTrailingSlash.length);
168
- }
169
- file = `/@id/${file}`;
170
- const serverFn = {
171
- file,
172
- export: opts.functionName
173
- };
174
- return Buffer.from(JSON.stringify(serverFn), "utf8").toString("base64url");
175
- }
176
- const entryId = `${opts.filename}--${opts.functionName}`;
177
- let functionId = this.entryIdToFunctionId.get(entryId);
178
- if (functionId === void 0) {
179
- if (this.options.generateFunctionId) {
180
- functionId = this.options.generateFunctionId({
181
- filename: opts.filename,
182
- functionName: opts.functionName
183
- });
184
- }
185
- if (!functionId) {
186
- functionId = crypto.createHash("sha256").update(entryId).digest("hex");
187
- }
188
- if (this.functionIds.has(functionId)) {
189
- let deduplicatedId;
190
- let iteration = 0;
191
- do {
192
- deduplicatedId = `${functionId}_${++iteration}`;
193
- } while (this.functionIds.has(deduplicatedId));
194
- functionId = deduplicatedId;
195
- }
196
- this.entryIdToFunctionId.set(entryId, functionId);
197
- this.functionIds.add(functionId);
198
- }
199
- return functionId;
200
- }
201
- get mode() {
202
- return this.options.mode ?? "dev";
203
- }
204
- get rootWithTrailingSlash() {
205
- if (this._rootWithTrailingSlash === void 0) {
206
- this._rootWithTrailingSlash = this.options.root.endsWith("/") ? this.options.root : `${this.options.root}/`;
207
- }
208
- return this._rootWithTrailingSlash;
209
- }
210
- async resolveIdCached(id, importer) {
211
- if (this.mode === "dev") {
212
- return this.options.resolveId(id, importer);
213
- }
214
- const cacheKey = importer ? `${importer}::${id}` : id;
215
- const cached = this.resolveIdCache.get(cacheKey);
216
- if (cached !== void 0) {
217
- return cached;
218
- }
219
- const resolved = await this.options.resolveId(id, importer);
220
- this.resolveIdCache.set(cacheKey, resolved);
221
- return resolved;
222
- }
223
- getExportResolutionCache(moduleId) {
224
- let cache = this.exportResolutionCache.get(moduleId);
225
- if (!cache) {
226
- cache = /* @__PURE__ */ new Map();
227
- this.exportResolutionCache.set(moduleId, cache);
228
- }
229
- return cache;
230
- }
231
- async init() {
232
- this.knownRootImports.set(
233
- "@tanstack/start-fn-stubs",
234
- /* @__PURE__ */ new Map([
235
- ["createIsomorphicFn", "IsomorphicFn"],
236
- ["createServerOnlyFn", "ServerOnlyFn"],
237
- ["createClientOnlyFn", "ClientOnlyFn"]
238
- ])
239
- );
240
- await Promise.all(
241
- this.options.lookupConfigurations.map(async (config) => {
242
- let libExports = this.knownRootImports.get(config.libName);
243
- if (!libExports) {
244
- libExports = /* @__PURE__ */ new Map();
245
- this.knownRootImports.set(config.libName, libExports);
246
- }
247
- libExports.set(config.rootExport, config.kind);
248
- if (config.kind !== "Root") {
249
- const setup = LookupSetup[config.kind];
250
- if (setup.type === "jsx") {
251
- return;
252
- }
253
- }
254
- const libId = await this.resolveIdCached(config.libName);
255
- if (!libId) {
256
- throw new Error(`could not resolve "${config.libName}"`);
257
- }
258
- let rootModule = this.moduleCache.get(libId);
259
- if (!rootModule) {
260
- rootModule = {
261
- bindings: /* @__PURE__ */ new Map(),
262
- exports: /* @__PURE__ */ new Map(),
263
- id: libId,
264
- reExportAllSources: []
265
- };
266
- this.moduleCache.set(libId, rootModule);
267
- }
268
- rootModule.exports.set(config.rootExport, config.rootExport);
269
- rootModule.exports.set("*", config.rootExport);
270
- rootModule.bindings.set(config.rootExport, {
271
- type: "var",
272
- init: null,
273
- // Not needed since resolvedKind is set
274
- resolvedKind: config.kind
275
- });
276
- this.moduleCache.set(libId, rootModule);
277
- })
278
- );
279
- this.initialized = true;
280
- }
281
- /**
282
- * Extracts bindings and exports from an already-parsed AST.
283
- * This is the core logic shared by ingestModule and ingestModuleFromAst.
284
- */
285
- extractModuleInfo(ast, id) {
286
- const bindings = /* @__PURE__ */ new Map();
287
- const exports$1 = /* @__PURE__ */ new Map();
288
- const reExportAllSources = [];
289
- for (const node of ast.program.body) {
290
- if (t.isImportDeclaration(node)) {
291
- const source = node.source.value;
292
- for (const s of node.specifiers) {
293
- if (t.isImportSpecifier(s)) {
294
- const importedName = t.isIdentifier(s.imported) ? s.imported.name : s.imported.value;
295
- bindings.set(s.local.name, { type: "import", source, importedName });
296
- } else if (t.isImportDefaultSpecifier(s)) {
297
- bindings.set(s.local.name, {
298
- type: "import",
299
- source,
300
- importedName: "default"
301
- });
302
- } else if (t.isImportNamespaceSpecifier(s)) {
303
- bindings.set(s.local.name, {
304
- type: "import",
305
- source,
306
- importedName: "*"
307
- });
308
- }
309
- }
310
- } else if (t.isVariableDeclaration(node)) {
311
- for (const decl of node.declarations) {
312
- if (t.isIdentifier(decl.id)) {
313
- bindings.set(decl.id.name, {
314
- type: "var",
315
- init: decl.init ?? null
316
- });
317
- }
318
- }
319
- } else if (t.isExportNamedDeclaration(node)) {
320
- if (node.declaration) {
321
- if (t.isVariableDeclaration(node.declaration)) {
322
- for (const d of node.declaration.declarations) {
323
- if (t.isIdentifier(d.id)) {
324
- exports$1.set(d.id.name, d.id.name);
325
- bindings.set(d.id.name, { type: "var", init: d.init ?? null });
326
- }
327
- }
328
- }
329
- }
330
- for (const sp of node.specifiers) {
331
- if (t.isExportNamespaceSpecifier(sp)) {
332
- exports$1.set(sp.exported.name, sp.exported.name);
333
- } else if (t.isExportSpecifier(sp)) {
334
- const local = sp.local.name;
335
- const exported = t.isIdentifier(sp.exported) ? sp.exported.name : sp.exported.value;
336
- exports$1.set(exported, local);
337
- if (node.source) {
338
- bindings.set(local, {
339
- type: "import",
340
- source: node.source.value,
341
- importedName: local
342
- });
343
- }
344
- }
345
- }
346
- } else if (t.isExportDefaultDeclaration(node)) {
347
- const d = node.declaration;
348
- if (t.isIdentifier(d)) {
349
- exports$1.set("default", d.name);
350
- } else {
351
- const synth = "__default_export__";
352
- bindings.set(synth, { type: "var", init: d });
353
- exports$1.set("default", synth);
354
- }
355
- } else if (t.isExportAllDeclaration(node)) {
356
- reExportAllSources.push(node.source.value);
357
- }
358
- }
359
- const info = {
360
- id,
361
- bindings,
362
- exports: exports$1,
363
- reExportAllSources
364
- };
365
- this.moduleCache.set(id, info);
366
- return info;
367
- }
368
- ingestModule({ code, id }) {
369
- const ast = parseAst({ code });
370
- const info = this.extractModuleInfo(ast, id);
371
- return { info, ast };
372
- }
373
- invalidateModule(id) {
374
- return this.moduleCache.delete(id);
375
- }
376
- async compile({
377
- code,
378
- id,
379
- detectedKinds
380
- }) {
381
- if (!this.initialized) {
382
- await this.init();
383
- }
384
- const fileKinds = detectedKinds ? new Set([...detectedKinds].filter((k) => this.validLookupKinds.has(k))) : this.validLookupKinds;
385
- if (fileKinds.size === 0) {
386
- return null;
387
- }
388
- const checkDirectCalls = needsDirectCallDetection(fileKinds);
389
- const canUseFastPath = areAllKindsTopLevelOnly(fileKinds);
390
- const { ast } = this.ingestModule({ code, id });
391
- const candidatePaths = [];
392
- const chainCallPaths = /* @__PURE__ */ new Map();
393
- const jsxCandidatePaths = [];
394
- const checkJSX = needsJSXDetection(fileKinds);
395
- const moduleInfo = this.moduleCache.get(id);
396
- if (canUseFastPath) {
397
- const candidateIndices = [];
398
- for (let i = 0; i < ast.program.body.length; i++) {
399
- const node = ast.program.body[i];
400
- let declarations;
401
- if (t.isVariableDeclaration(node)) {
402
- declarations = node.declarations;
403
- } else if (t.isExportNamedDeclaration(node) && node.declaration) {
404
- if (t.isVariableDeclaration(node.declaration)) {
405
- declarations = node.declaration.declarations;
406
- }
407
- }
408
- if (declarations) {
409
- for (const decl of declarations) {
410
- if (decl.init && t.isCallExpression(decl.init)) {
411
- if (isMethodChainCandidate(decl.init, fileKinds)) {
412
- candidateIndices.push(i);
413
- break;
414
- }
415
- }
416
- }
417
- }
418
- }
419
- if (candidateIndices.length === 0) {
420
- return null;
421
- }
422
- babel.traverse(ast, {
423
- Program(programPath) {
424
- const bodyPaths = programPath.get("body");
425
- for (const idx of candidateIndices) {
426
- const stmtPath = bodyPaths[idx];
427
- if (!stmtPath) continue;
428
- stmtPath.traverse({
429
- CallExpression(path) {
430
- const node = path.node;
431
- const parent = path.parent;
432
- if (t.isMemberExpression(parent) && t.isCallExpression(path.parentPath.parent)) {
433
- chainCallPaths.set(node, path);
434
- return;
435
- }
436
- if (isMethodChainCandidate(node, fileKinds)) {
437
- candidatePaths.push(path);
438
- }
439
- }
440
- });
441
- }
442
- programPath.stop();
443
- }
444
- });
445
- } else {
446
- babel.traverse(ast, {
447
- CallExpression: (path) => {
448
- const node = path.node;
449
- const parent = path.parent;
450
- if (t.isMemberExpression(parent) && t.isCallExpression(path.parentPath.parent)) {
451
- chainCallPaths.set(node, path);
452
- return;
453
- }
454
- if (isMethodChainCandidate(node, fileKinds)) {
455
- candidatePaths.push(path);
456
- return;
457
- }
458
- if (checkDirectCalls) {
459
- if (isTopLevelDirectCallCandidate(path)) {
460
- candidatePaths.push(path);
461
- } else if (isNestedDirectCallCandidate(node)) {
462
- candidatePaths.push(path);
463
- }
464
- }
465
- },
466
- // Pattern 3: JSX element pattern (e.g., <ClientOnly>)
467
- // Collect JSX elements where the component is imported from a known package
468
- // and resolves to a JSX kind (e.g., ClientOnly from @tanstack/react-router)
469
- JSXElement: (path) => {
470
- if (!checkJSX) return;
471
- const openingElement = path.node.openingElement;
472
- const nameNode = openingElement.name;
473
- if (!t.isJSXIdentifier(nameNode)) return;
474
- const componentName = nameNode.name;
475
- const binding = moduleInfo.bindings.get(componentName);
476
- if (!binding || binding.type !== "import") return;
477
- const knownExports = this.knownRootImports.get(binding.source);
478
- if (!knownExports) return;
479
- const kind = knownExports.get(binding.importedName);
480
- if (kind !== "ClientOnlyJSX") return;
481
- jsxCandidatePaths.push(path);
482
- }
483
- });
484
- }
485
- if (candidatePaths.length === 0 && jsxCandidatePaths.length === 0) {
486
- return null;
487
- }
488
- const resolvedCandidates = await Promise.all(
489
- candidatePaths.map(async (path) => ({
490
- path,
491
- kind: await this.resolveExprKind(path.node, id)
492
- }))
493
- );
494
- const validCandidates = resolvedCandidates.filter(
495
- ({ kind }) => this.validLookupKinds.has(kind)
496
- );
497
- if (validCandidates.length === 0 && jsxCandidatePaths.length === 0) {
498
- return null;
499
- }
500
- const pathsToRewrite = [];
501
- for (const { path, kind } of validCandidates) {
502
- const node = path.node;
503
- const methodChain = {
504
- middleware: null,
505
- inputValidator: null,
506
- handler: null,
507
- server: null,
508
- client: null
509
- };
510
- let currentNode = node;
511
- let currentPath = path;
512
- while (true) {
513
- const callee = currentNode.callee;
514
- if (!t.isMemberExpression(callee)) {
515
- break;
516
- }
517
- if (t.isIdentifier(callee.property)) {
518
- const name = callee.property.name;
519
- if (name in methodChain) {
520
- const args = currentPath.get("arguments");
521
- const firstArgPath = Array.isArray(args) && args.length > 0 ? args[0] ?? null : null;
522
- methodChain[name] = {
523
- callPath: currentPath,
524
- firstArgPath
525
- };
526
- }
527
- }
528
- if (!t.isCallExpression(callee.object)) {
529
- break;
530
- }
531
- currentNode = callee.object;
532
- const nextPath = chainCallPaths.get(currentNode);
533
- if (!nextPath) {
534
- break;
535
- }
536
- currentPath = nextPath;
537
- }
538
- pathsToRewrite.push({ path, kind, methodChain });
539
- }
540
- const refIdents = findReferencedIdentifiers(ast);
541
- const context = {
542
- ast,
543
- id,
544
- code,
545
- env: this.options.env,
546
- envName: this.options.envName,
547
- root: this.options.root,
548
- framework: this.options.framework,
549
- providerEnvName: this.options.providerEnvName,
550
- generateFunctionId: (opts) => this.generateFunctionId(opts),
551
- getKnownServerFns: () => this.options.getKnownServerFns?.() ?? {},
552
- onServerFnsById: this.options.onServerFnsById
553
- };
554
- const candidatesByKind = /* @__PURE__ */ new Map();
555
- for (const { path: candidatePath, kind, methodChain } of pathsToRewrite) {
556
- const candidate = { path: candidatePath, methodChain };
557
- const existing = candidatesByKind.get(kind);
558
- if (existing) {
559
- existing.push(candidate);
560
- } else {
561
- candidatesByKind.set(kind, [candidate]);
562
- }
563
- }
564
- for (const [kind, candidates] of candidatesByKind) {
565
- const handler = KindHandlers[kind];
566
- handler(candidates, context, kind);
567
- }
568
- for (const jsxPath of jsxCandidatePaths) {
569
- handleClientOnlyJSX(jsxPath);
570
- }
571
- deadCodeElimination(ast, refIdents);
572
- const result = generateFromAst(ast, {
573
- sourceMaps: true,
574
- sourceFileName: id,
575
- filename: id
576
- });
577
- if (result.map) {
578
- result.map.sourcesContent = [code];
579
- }
580
- return result;
581
- }
582
- async resolveIdentifierKind(ident, id, visited = /* @__PURE__ */ new Set()) {
583
- const info = await this.getModuleInfo(id);
584
- const binding = info.bindings.get(ident);
585
- if (!binding) {
586
- return "None";
587
- }
588
- if (binding.resolvedKind) {
589
- return binding.resolvedKind;
590
- }
591
- const vKey = `${id}:${ident}`;
592
- if (visited.has(vKey)) {
593
- return "None";
594
- }
595
- visited.add(vKey);
596
- const resolvedKind = await this.resolveBindingKind(binding, id, visited);
597
- binding.resolvedKind = resolvedKind;
598
- return resolvedKind;
599
- }
600
- /**
601
- * Recursively find an export in a module, following `export * from` chains.
602
- * Returns the module info and binding if found, or undefined if not found.
603
- */
604
- async findExportInModule(moduleInfo, exportName, visitedModules = /* @__PURE__ */ new Set()) {
605
- const isBuildMode = this.mode === "build";
606
- if (isBuildMode && visitedModules.size === 0) {
607
- const moduleCache = this.exportResolutionCache.get(moduleInfo.id);
608
- if (moduleCache) {
609
- const cached = moduleCache.get(exportName);
610
- if (cached !== void 0) {
611
- return cached ?? void 0;
612
- }
613
- }
614
- }
615
- if (visitedModules.has(moduleInfo.id)) {
616
- return void 0;
617
- }
618
- visitedModules.add(moduleInfo.id);
619
- const localBindingName = moduleInfo.exports.get(exportName);
620
- if (localBindingName) {
621
- const binding = moduleInfo.bindings.get(localBindingName);
622
- if (binding) {
623
- const result = { moduleInfo, binding };
624
- if (isBuildMode) {
625
- this.getExportResolutionCache(moduleInfo.id).set(exportName, result);
626
- }
627
- return result;
628
- }
629
- }
630
- if (moduleInfo.reExportAllSources.length > 0) {
631
- const results = await Promise.all(
632
- moduleInfo.reExportAllSources.map(async (reExportSource) => {
633
- const reExportTarget = await this.resolveIdCached(
634
- reExportSource,
635
- moduleInfo.id
636
- );
637
- if (reExportTarget) {
638
- const reExportModule = await this.getModuleInfo(reExportTarget);
639
- return this.findExportInModule(
640
- reExportModule,
641
- exportName,
642
- visitedModules
643
- );
644
- }
645
- return void 0;
646
- })
647
- );
648
- for (const result of results) {
649
- if (result) {
650
- if (isBuildMode) {
651
- this.getExportResolutionCache(moduleInfo.id).set(exportName, result);
652
- }
653
- return result;
654
- }
655
- }
656
- }
657
- if (isBuildMode) {
658
- this.getExportResolutionCache(moduleInfo.id).set(exportName, null);
659
- }
660
- return void 0;
661
- }
662
- async resolveBindingKind(binding, fileId, visited = /* @__PURE__ */ new Set()) {
663
- if (binding.resolvedKind) {
664
- return binding.resolvedKind;
665
- }
666
- if (binding.type === "import") {
667
- const knownExports = this.knownRootImports.get(binding.source);
668
- if (knownExports) {
669
- const kind = knownExports.get(binding.importedName);
670
- if (kind) {
671
- binding.resolvedKind = kind;
672
- return kind;
673
- }
674
- }
675
- const target = await this.resolveIdCached(binding.source, fileId);
676
- if (!target) {
677
- return "None";
678
- }
679
- const importedModule = await this.getModuleInfo(target);
680
- const found = await this.findExportInModule(
681
- importedModule,
682
- binding.importedName
683
- );
684
- if (!found) {
685
- return "None";
686
- }
687
- const { moduleInfo: foundModule, binding: foundBinding } = found;
688
- if (foundBinding.resolvedKind) {
689
- return foundBinding.resolvedKind;
690
- }
691
- const resolvedKind2 = await this.resolveBindingKind(
692
- foundBinding,
693
- foundModule.id,
694
- visited
695
- );
696
- foundBinding.resolvedKind = resolvedKind2;
697
- return resolvedKind2;
698
- }
699
- const resolvedKind = await this.resolveExprKind(
700
- binding.init,
701
- fileId,
702
- visited
703
- );
704
- if (isLookupKind(resolvedKind) && LookupSetup[resolvedKind].type === "directCall" && binding.init && t.isCallExpression(binding.init)) {
705
- binding.resolvedKind = "None";
706
- return "None";
707
- }
708
- binding.resolvedKind = resolvedKind;
709
- return resolvedKind;
710
- }
711
- async resolveExprKind(expr, fileId, visited = /* @__PURE__ */ new Set()) {
712
- if (!expr) {
713
- return "None";
714
- }
715
- while (t.isTSAsExpression(expr) || t.isTSNonNullExpression(expr) || t.isParenthesizedExpression(expr)) {
716
- expr = expr.expression;
717
- }
718
- let result = "None";
719
- if (t.isCallExpression(expr)) {
720
- if (!t.isExpression(expr.callee)) {
721
- return "None";
722
- }
723
- const calleeKind = await this.resolveCalleeKind(
724
- expr.callee,
725
- fileId,
726
- visited
727
- );
728
- if (calleeKind === "Root" || calleeKind === "Builder") {
729
- return "Builder";
730
- }
731
- if (t.isMemberExpression(expr.callee)) {
732
- if (this.validLookupKinds.has(calleeKind)) {
733
- return calleeKind;
734
- }
735
- }
736
- if (t.isIdentifier(expr.callee)) {
737
- if (this.validLookupKinds.has(calleeKind)) {
738
- return calleeKind;
739
- }
740
- }
741
- } else if (t.isMemberExpression(expr) && t.isIdentifier(expr.property)) {
742
- result = await this.resolveCalleeKind(expr.object, fileId, visited);
743
- }
744
- if (result === "None" && t.isIdentifier(expr)) {
745
- result = await this.resolveIdentifierKind(expr.name, fileId, visited);
746
- }
747
- return result;
748
- }
749
- async resolveCalleeKind(callee, fileId, visited = /* @__PURE__ */ new Set()) {
750
- if (t.isIdentifier(callee)) {
751
- return this.resolveIdentifierKind(callee.name, fileId, visited);
752
- }
753
- if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
754
- const prop = callee.property.name;
755
- const possibleKinds = IdentifierToKinds.get(prop);
756
- if (possibleKinds) {
757
- const base = await this.resolveExprKind(callee.object, fileId, visited);
758
- for (const kind of possibleKinds) {
759
- if (!this.validLookupKinds.has(kind)) continue;
760
- if (kind === "ServerFn") {
761
- if (base === "Root" || base === "Builder") {
762
- return "ServerFn";
763
- }
764
- } else if (kind === "Middleware") {
765
- if (base === "Root" || base === "Builder" || base === "Middleware") {
766
- return "Middleware";
767
- }
768
- } else if (kind === "IsomorphicFn") {
769
- if (base === "Root" || base === "Builder" || base === "IsomorphicFn") {
770
- return "IsomorphicFn";
771
- }
772
- }
773
- }
774
- }
775
- if (t.isIdentifier(callee.object)) {
776
- const info = await this.getModuleInfo(fileId);
777
- const binding = info.bindings.get(callee.object.name);
778
- if (binding && binding.type === "import" && binding.importedName === "*") {
779
- const targetModuleId = await this.resolveIdCached(
780
- binding.source,
781
- fileId
782
- );
783
- if (targetModuleId) {
784
- const targetModule = await this.getModuleInfo(targetModuleId);
785
- const localBindingName = targetModule.exports.get(
786
- callee.property.name
787
- );
788
- if (localBindingName) {
789
- const exportedBinding = targetModule.bindings.get(localBindingName);
790
- if (exportedBinding) {
791
- return await this.resolveBindingKind(
792
- exportedBinding,
793
- targetModule.id,
794
- visited
795
- );
796
- }
797
- }
798
- } else {
799
- return "None";
800
- }
801
- }
802
- }
803
- return this.resolveExprKind(callee.object, fileId, visited);
804
- }
805
- return this.resolveExprKind(callee, fileId, visited);
806
- }
807
- async getModuleInfo(id) {
808
- let cached = this.moduleCache.get(id);
809
- if (cached) {
810
- return cached;
811
- }
812
- await this.options.loadModule(id);
813
- cached = this.moduleCache.get(id);
814
- if (!cached) {
815
- throw new Error(`could not load module info for ${id}`);
816
- }
817
- return cached;
818
- }
151
+ const node = path.node;
152
+ if (!(t.isIdentifier(node.callee) || t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object) && t.isIdentifier(node.callee.property))) return false;
153
+ const parent = path.parent;
154
+ if (!t.isVariableDeclarator(parent) || parent.init !== node) return false;
155
+ const grandParent = path.parentPath.parent;
156
+ if (!t.isVariableDeclaration(grandParent)) return false;
157
+ return t.isProgram(path.parentPath.parentPath?.parent);
819
158
  }
159
+ var StartCompiler = class {
160
+ moduleCache = /* @__PURE__ */ new Map();
161
+ initialized = false;
162
+ validLookupKinds;
163
+ resolveIdCache = /* @__PURE__ */ new Map();
164
+ exportResolutionCache = /* @__PURE__ */ new Map();
165
+ knownRootImports = /* @__PURE__ */ new Map();
166
+ entryIdToFunctionId = /* @__PURE__ */ new Map();
167
+ functionIds = /* @__PURE__ */ new Set();
168
+ _rootWithTrailingSlash;
169
+ constructor(options) {
170
+ this.options = options;
171
+ this.validLookupKinds = options.lookupKinds;
172
+ }
173
+ /**
174
+ * Generates a unique function ID for a server function.
175
+ * In dev mode, uses a base64-encoded JSON with file path and export name.
176
+ * In build mode, uses SHA256 hash or custom generator.
177
+ */
178
+ generateFunctionId(opts) {
179
+ if (this.mode === "dev") {
180
+ let file = opts.extractedFilename;
181
+ if (opts.extractedFilename.startsWith(this.rootWithTrailingSlash)) file = opts.extractedFilename.slice(this.rootWithTrailingSlash.length);
182
+ file = `/@id/${file}`;
183
+ const serverFn = {
184
+ file,
185
+ export: opts.functionName
186
+ };
187
+ return Buffer.from(JSON.stringify(serverFn), "utf8").toString("base64url");
188
+ }
189
+ const entryId = `${opts.filename}--${opts.functionName}`;
190
+ let functionId = this.entryIdToFunctionId.get(entryId);
191
+ if (functionId === void 0) {
192
+ if (this.options.generateFunctionId) functionId = this.options.generateFunctionId({
193
+ filename: opts.filename,
194
+ functionName: opts.functionName
195
+ });
196
+ if (!functionId) functionId = crypto.createHash("sha256").update(entryId).digest("hex");
197
+ if (this.functionIds.has(functionId)) {
198
+ let deduplicatedId;
199
+ let iteration = 0;
200
+ do
201
+ deduplicatedId = `${functionId}_${++iteration}`;
202
+ while (this.functionIds.has(deduplicatedId));
203
+ functionId = deduplicatedId;
204
+ }
205
+ this.entryIdToFunctionId.set(entryId, functionId);
206
+ this.functionIds.add(functionId);
207
+ }
208
+ return functionId;
209
+ }
210
+ get mode() {
211
+ return this.options.mode ?? "dev";
212
+ }
213
+ get rootWithTrailingSlash() {
214
+ if (this._rootWithTrailingSlash === void 0) this._rootWithTrailingSlash = this.options.root.endsWith("/") ? this.options.root : `${this.options.root}/`;
215
+ return this._rootWithTrailingSlash;
216
+ }
217
+ async resolveIdCached(id, importer) {
218
+ if (this.mode === "dev") return this.options.resolveId(id, importer);
219
+ const cacheKey = importer ? `${importer}::${id}` : id;
220
+ const cached = this.resolveIdCache.get(cacheKey);
221
+ if (cached !== void 0) return cached;
222
+ const resolved = await this.options.resolveId(id, importer);
223
+ this.resolveIdCache.set(cacheKey, resolved);
224
+ return resolved;
225
+ }
226
+ getExportResolutionCache(moduleId) {
227
+ let cache = this.exportResolutionCache.get(moduleId);
228
+ if (!cache) {
229
+ cache = /* @__PURE__ */ new Map();
230
+ this.exportResolutionCache.set(moduleId, cache);
231
+ }
232
+ return cache;
233
+ }
234
+ async init() {
235
+ this.knownRootImports.set("@tanstack/start-fn-stubs", new Map([
236
+ ["createIsomorphicFn", "IsomorphicFn"],
237
+ ["createServerOnlyFn", "ServerOnlyFn"],
238
+ ["createClientOnlyFn", "ClientOnlyFn"]
239
+ ]));
240
+ await Promise.all(this.options.lookupConfigurations.map(async (config) => {
241
+ let libExports = this.knownRootImports.get(config.libName);
242
+ if (!libExports) {
243
+ libExports = /* @__PURE__ */ new Map();
244
+ this.knownRootImports.set(config.libName, libExports);
245
+ }
246
+ libExports.set(config.rootExport, config.kind);
247
+ if (config.kind !== "Root") {
248
+ if (LookupSetup[config.kind].type === "jsx") return;
249
+ }
250
+ const libId = await this.resolveIdCached(config.libName);
251
+ if (!libId) throw new Error(`could not resolve "${config.libName}"`);
252
+ let rootModule = this.moduleCache.get(libId);
253
+ if (!rootModule) {
254
+ rootModule = {
255
+ bindings: /* @__PURE__ */ new Map(),
256
+ exports: /* @__PURE__ */ new Map(),
257
+ id: libId,
258
+ reExportAllSources: []
259
+ };
260
+ this.moduleCache.set(libId, rootModule);
261
+ }
262
+ rootModule.exports.set(config.rootExport, config.rootExport);
263
+ rootModule.exports.set("*", config.rootExport);
264
+ rootModule.bindings.set(config.rootExport, {
265
+ type: "var",
266
+ init: null,
267
+ resolvedKind: config.kind
268
+ });
269
+ this.moduleCache.set(libId, rootModule);
270
+ }));
271
+ this.initialized = true;
272
+ }
273
+ /**
274
+ * Extracts bindings and exports from an already-parsed AST.
275
+ * This is the core logic shared by ingestModule and ingestModuleFromAst.
276
+ */
277
+ extractModuleInfo(ast, id) {
278
+ const bindings = /* @__PURE__ */ new Map();
279
+ const exports = /* @__PURE__ */ new Map();
280
+ const reExportAllSources = [];
281
+ for (const node of ast.program.body) if (t.isImportDeclaration(node)) {
282
+ const source = node.source.value;
283
+ for (const s of node.specifiers) if (t.isImportSpecifier(s)) {
284
+ const importedName = t.isIdentifier(s.imported) ? s.imported.name : s.imported.value;
285
+ bindings.set(s.local.name, {
286
+ type: "import",
287
+ source,
288
+ importedName
289
+ });
290
+ } else if (t.isImportDefaultSpecifier(s)) bindings.set(s.local.name, {
291
+ type: "import",
292
+ source,
293
+ importedName: "default"
294
+ });
295
+ else if (t.isImportNamespaceSpecifier(s)) bindings.set(s.local.name, {
296
+ type: "import",
297
+ source,
298
+ importedName: "*"
299
+ });
300
+ } else if (t.isVariableDeclaration(node)) {
301
+ for (const decl of node.declarations) if (t.isIdentifier(decl.id)) bindings.set(decl.id.name, {
302
+ type: "var",
303
+ init: decl.init ?? null
304
+ });
305
+ } else if (t.isExportNamedDeclaration(node)) {
306
+ if (node.declaration) {
307
+ if (t.isVariableDeclaration(node.declaration)) {
308
+ for (const d of node.declaration.declarations) if (t.isIdentifier(d.id)) {
309
+ exports.set(d.id.name, d.id.name);
310
+ bindings.set(d.id.name, {
311
+ type: "var",
312
+ init: d.init ?? null
313
+ });
314
+ }
315
+ }
316
+ }
317
+ for (const sp of node.specifiers) if (t.isExportNamespaceSpecifier(sp)) exports.set(sp.exported.name, sp.exported.name);
318
+ else if (t.isExportSpecifier(sp)) {
319
+ const local = sp.local.name;
320
+ const exported = t.isIdentifier(sp.exported) ? sp.exported.name : sp.exported.value;
321
+ exports.set(exported, local);
322
+ if (node.source) bindings.set(local, {
323
+ type: "import",
324
+ source: node.source.value,
325
+ importedName: local
326
+ });
327
+ }
328
+ } else if (t.isExportDefaultDeclaration(node)) {
329
+ const d = node.declaration;
330
+ if (t.isIdentifier(d)) exports.set("default", d.name);
331
+ else {
332
+ const synth = "__default_export__";
333
+ bindings.set(synth, {
334
+ type: "var",
335
+ init: d
336
+ });
337
+ exports.set("default", synth);
338
+ }
339
+ } else if (t.isExportAllDeclaration(node)) reExportAllSources.push(node.source.value);
340
+ const info = {
341
+ id,
342
+ bindings,
343
+ exports,
344
+ reExportAllSources
345
+ };
346
+ this.moduleCache.set(id, info);
347
+ return info;
348
+ }
349
+ ingestModule({ code, id }) {
350
+ const ast = parseAst({ code });
351
+ return {
352
+ info: this.extractModuleInfo(ast, id),
353
+ ast
354
+ };
355
+ }
356
+ invalidateModule(id) {
357
+ return this.moduleCache.delete(id);
358
+ }
359
+ async compile({ code, id, detectedKinds }) {
360
+ if (!this.initialized) await this.init();
361
+ const fileKinds = detectedKinds ? new Set([...detectedKinds].filter((k) => this.validLookupKinds.has(k))) : this.validLookupKinds;
362
+ if (fileKinds.size === 0) return null;
363
+ const checkDirectCalls = needsDirectCallDetection(fileKinds);
364
+ const canUseFastPath = areAllKindsTopLevelOnly(fileKinds);
365
+ const { ast } = this.ingestModule({
366
+ code,
367
+ id
368
+ });
369
+ const candidatePaths = [];
370
+ const chainCallPaths = /* @__PURE__ */ new Map();
371
+ const jsxCandidatePaths = [];
372
+ const checkJSX = needsJSXDetection(fileKinds);
373
+ const moduleInfo = this.moduleCache.get(id);
374
+ if (canUseFastPath) {
375
+ const candidateIndices = [];
376
+ for (let i = 0; i < ast.program.body.length; i++) {
377
+ const node = ast.program.body[i];
378
+ let declarations;
379
+ if (t.isVariableDeclaration(node)) declarations = node.declarations;
380
+ else if (t.isExportNamedDeclaration(node) && node.declaration) {
381
+ if (t.isVariableDeclaration(node.declaration)) declarations = node.declaration.declarations;
382
+ }
383
+ if (declarations) {
384
+ for (const decl of declarations) if (decl.init && t.isCallExpression(decl.init)) {
385
+ if (isMethodChainCandidate(decl.init, fileKinds)) {
386
+ candidateIndices.push(i);
387
+ break;
388
+ }
389
+ }
390
+ }
391
+ }
392
+ if (candidateIndices.length === 0) return null;
393
+ babel.traverse(ast, { Program(programPath) {
394
+ const bodyPaths = programPath.get("body");
395
+ for (const idx of candidateIndices) {
396
+ const stmtPath = bodyPaths[idx];
397
+ if (!stmtPath) continue;
398
+ stmtPath.traverse({ CallExpression(path) {
399
+ const node = path.node;
400
+ const parent = path.parent;
401
+ if (t.isMemberExpression(parent) && t.isCallExpression(path.parentPath.parent)) {
402
+ chainCallPaths.set(node, path);
403
+ return;
404
+ }
405
+ if (isMethodChainCandidate(node, fileKinds)) candidatePaths.push(path);
406
+ } });
407
+ }
408
+ programPath.stop();
409
+ } });
410
+ } else babel.traverse(ast, {
411
+ CallExpression: (path) => {
412
+ const node = path.node;
413
+ const parent = path.parent;
414
+ if (t.isMemberExpression(parent) && t.isCallExpression(path.parentPath.parent)) {
415
+ chainCallPaths.set(node, path);
416
+ return;
417
+ }
418
+ if (isMethodChainCandidate(node, fileKinds)) {
419
+ candidatePaths.push(path);
420
+ return;
421
+ }
422
+ if (checkDirectCalls) {
423
+ if (isTopLevelDirectCallCandidate(path)) candidatePaths.push(path);
424
+ else if (isNestedDirectCallCandidate(node)) candidatePaths.push(path);
425
+ }
426
+ },
427
+ JSXElement: (path) => {
428
+ if (!checkJSX) return;
429
+ const nameNode = path.node.openingElement.name;
430
+ if (!t.isJSXIdentifier(nameNode)) return;
431
+ const componentName = nameNode.name;
432
+ const binding = moduleInfo.bindings.get(componentName);
433
+ if (!binding || binding.type !== "import") return;
434
+ const knownExports = this.knownRootImports.get(binding.source);
435
+ if (!knownExports) return;
436
+ if (knownExports.get(binding.importedName) !== "ClientOnlyJSX") return;
437
+ jsxCandidatePaths.push(path);
438
+ }
439
+ });
440
+ if (candidatePaths.length === 0 && jsxCandidatePaths.length === 0) return null;
441
+ const validCandidates = (await Promise.all(candidatePaths.map(async (path) => ({
442
+ path,
443
+ kind: await this.resolveExprKind(path.node, id)
444
+ })))).filter(({ kind }) => this.validLookupKinds.has(kind));
445
+ if (validCandidates.length === 0 && jsxCandidatePaths.length === 0) return null;
446
+ const pathsToRewrite = [];
447
+ for (const { path, kind } of validCandidates) {
448
+ const node = path.node;
449
+ const methodChain = {
450
+ middleware: null,
451
+ inputValidator: null,
452
+ handler: null,
453
+ server: null,
454
+ client: null
455
+ };
456
+ let currentNode = node;
457
+ let currentPath = path;
458
+ while (true) {
459
+ const callee = currentNode.callee;
460
+ if (!t.isMemberExpression(callee)) break;
461
+ if (t.isIdentifier(callee.property)) {
462
+ const name = callee.property.name;
463
+ if (name in methodChain) {
464
+ const args = currentPath.get("arguments");
465
+ const firstArgPath = Array.isArray(args) && args.length > 0 ? args[0] ?? null : null;
466
+ methodChain[name] = {
467
+ callPath: currentPath,
468
+ firstArgPath
469
+ };
470
+ }
471
+ }
472
+ if (!t.isCallExpression(callee.object)) break;
473
+ currentNode = callee.object;
474
+ const nextPath = chainCallPaths.get(currentNode);
475
+ if (!nextPath) break;
476
+ currentPath = nextPath;
477
+ }
478
+ pathsToRewrite.push({
479
+ path,
480
+ kind,
481
+ methodChain
482
+ });
483
+ }
484
+ const refIdents = findReferencedIdentifiers(ast);
485
+ const context = {
486
+ ast,
487
+ id,
488
+ code,
489
+ env: this.options.env,
490
+ envName: this.options.envName,
491
+ root: this.options.root,
492
+ framework: this.options.framework,
493
+ providerEnvName: this.options.providerEnvName,
494
+ generateFunctionId: (opts) => this.generateFunctionId(opts),
495
+ getKnownServerFns: () => this.options.getKnownServerFns?.() ?? {},
496
+ onServerFnsById: this.options.onServerFnsById
497
+ };
498
+ const candidatesByKind = /* @__PURE__ */ new Map();
499
+ for (const { path: candidatePath, kind, methodChain } of pathsToRewrite) {
500
+ const candidate = {
501
+ path: candidatePath,
502
+ methodChain
503
+ };
504
+ const existing = candidatesByKind.get(kind);
505
+ if (existing) existing.push(candidate);
506
+ else candidatesByKind.set(kind, [candidate]);
507
+ }
508
+ for (const [kind, candidates] of candidatesByKind) {
509
+ const handler = KindHandlers[kind];
510
+ handler(candidates, context, kind);
511
+ }
512
+ for (const jsxPath of jsxCandidatePaths) handleClientOnlyJSX(jsxPath, { env: "server" });
513
+ deadCodeElimination(ast, refIdents);
514
+ const result = generateFromAst(ast, {
515
+ sourceMaps: true,
516
+ sourceFileName: id,
517
+ filename: id
518
+ });
519
+ if (result.map) result.map.sourcesContent = [code];
520
+ return result;
521
+ }
522
+ async resolveIdentifierKind(ident, id, visited = /* @__PURE__ */ new Set()) {
523
+ const binding = (await this.getModuleInfo(id)).bindings.get(ident);
524
+ if (!binding) return "None";
525
+ if (binding.resolvedKind) return binding.resolvedKind;
526
+ const vKey = `${id}:${ident}`;
527
+ if (visited.has(vKey)) return "None";
528
+ visited.add(vKey);
529
+ const resolvedKind = await this.resolveBindingKind(binding, id, visited);
530
+ binding.resolvedKind = resolvedKind;
531
+ return resolvedKind;
532
+ }
533
+ /**
534
+ * Recursively find an export in a module, following `export * from` chains.
535
+ * Returns the module info and binding if found, or undefined if not found.
536
+ */
537
+ async findExportInModule(moduleInfo, exportName, visitedModules = /* @__PURE__ */ new Set()) {
538
+ const isBuildMode = this.mode === "build";
539
+ if (isBuildMode && visitedModules.size === 0) {
540
+ const moduleCache = this.exportResolutionCache.get(moduleInfo.id);
541
+ if (moduleCache) {
542
+ const cached = moduleCache.get(exportName);
543
+ if (cached !== void 0) return cached ?? void 0;
544
+ }
545
+ }
546
+ if (visitedModules.has(moduleInfo.id)) return;
547
+ visitedModules.add(moduleInfo.id);
548
+ const localBindingName = moduleInfo.exports.get(exportName);
549
+ if (localBindingName) {
550
+ const binding = moduleInfo.bindings.get(localBindingName);
551
+ if (binding) {
552
+ const result = {
553
+ moduleInfo,
554
+ binding
555
+ };
556
+ if (isBuildMode) this.getExportResolutionCache(moduleInfo.id).set(exportName, result);
557
+ return result;
558
+ }
559
+ }
560
+ if (moduleInfo.reExportAllSources.length > 0) {
561
+ const results = await Promise.all(moduleInfo.reExportAllSources.map(async (reExportSource) => {
562
+ const reExportTarget = await this.resolveIdCached(reExportSource, moduleInfo.id);
563
+ if (reExportTarget) {
564
+ const reExportModule = await this.getModuleInfo(reExportTarget);
565
+ return this.findExportInModule(reExportModule, exportName, visitedModules);
566
+ }
567
+ }));
568
+ for (const result of results) if (result) {
569
+ if (isBuildMode) this.getExportResolutionCache(moduleInfo.id).set(exportName, result);
570
+ return result;
571
+ }
572
+ }
573
+ if (isBuildMode) this.getExportResolutionCache(moduleInfo.id).set(exportName, null);
574
+ }
575
+ async resolveBindingKind(binding, fileId, visited = /* @__PURE__ */ new Set()) {
576
+ if (binding.resolvedKind) return binding.resolvedKind;
577
+ if (binding.type === "import") {
578
+ const knownExports = this.knownRootImports.get(binding.source);
579
+ if (knownExports) {
580
+ const kind = knownExports.get(binding.importedName);
581
+ if (kind) {
582
+ binding.resolvedKind = kind;
583
+ return kind;
584
+ }
585
+ }
586
+ const target = await this.resolveIdCached(binding.source, fileId);
587
+ if (!target) return "None";
588
+ const importedModule = await this.getModuleInfo(target);
589
+ const found = await this.findExportInModule(importedModule, binding.importedName);
590
+ if (!found) return "None";
591
+ const { moduleInfo: foundModule, binding: foundBinding } = found;
592
+ if (foundBinding.resolvedKind) return foundBinding.resolvedKind;
593
+ const resolvedKind = await this.resolveBindingKind(foundBinding, foundModule.id, visited);
594
+ foundBinding.resolvedKind = resolvedKind;
595
+ return resolvedKind;
596
+ }
597
+ const resolvedKind = await this.resolveExprKind(binding.init, fileId, visited);
598
+ if (isLookupKind(resolvedKind) && LookupSetup[resolvedKind].type === "directCall" && binding.init && t.isCallExpression(binding.init)) {
599
+ binding.resolvedKind = "None";
600
+ return "None";
601
+ }
602
+ binding.resolvedKind = resolvedKind;
603
+ return resolvedKind;
604
+ }
605
+ async resolveExprKind(expr, fileId, visited = /* @__PURE__ */ new Set()) {
606
+ if (!expr) return "None";
607
+ while (t.isTSAsExpression(expr) || t.isTSNonNullExpression(expr) || t.isParenthesizedExpression(expr)) expr = expr.expression;
608
+ let result = "None";
609
+ if (t.isCallExpression(expr)) {
610
+ if (!t.isExpression(expr.callee)) return "None";
611
+ const calleeKind = await this.resolveCalleeKind(expr.callee, fileId, visited);
612
+ if (calleeKind === "Root" || calleeKind === "Builder") return "Builder";
613
+ if (t.isMemberExpression(expr.callee)) {
614
+ if (this.validLookupKinds.has(calleeKind)) return calleeKind;
615
+ }
616
+ if (t.isIdentifier(expr.callee)) {
617
+ if (this.validLookupKinds.has(calleeKind)) return calleeKind;
618
+ }
619
+ } else if (t.isMemberExpression(expr) && t.isIdentifier(expr.property)) result = await this.resolveCalleeKind(expr.object, fileId, visited);
620
+ if (result === "None" && t.isIdentifier(expr)) result = await this.resolveIdentifierKind(expr.name, fileId, visited);
621
+ return result;
622
+ }
623
+ async resolveCalleeKind(callee, fileId, visited = /* @__PURE__ */ new Set()) {
624
+ if (t.isIdentifier(callee)) return this.resolveIdentifierKind(callee.name, fileId, visited);
625
+ if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
626
+ const prop = callee.property.name;
627
+ const possibleKinds = IdentifierToKinds.get(prop);
628
+ if (possibleKinds) {
629
+ const base = await this.resolveExprKind(callee.object, fileId, visited);
630
+ for (const kind of possibleKinds) {
631
+ if (!this.validLookupKinds.has(kind)) continue;
632
+ if (kind === "ServerFn") {
633
+ if (base === "Root" || base === "Builder") return "ServerFn";
634
+ } else if (kind === "Middleware") {
635
+ if (base === "Root" || base === "Builder" || base === "Middleware") return "Middleware";
636
+ } else if (kind === "IsomorphicFn") {
637
+ if (base === "Root" || base === "Builder" || base === "IsomorphicFn") return "IsomorphicFn";
638
+ }
639
+ }
640
+ }
641
+ if (t.isIdentifier(callee.object)) {
642
+ const binding = (await this.getModuleInfo(fileId)).bindings.get(callee.object.name);
643
+ if (binding && binding.type === "import" && binding.importedName === "*") {
644
+ const targetModuleId = await this.resolveIdCached(binding.source, fileId);
645
+ if (targetModuleId) {
646
+ const targetModule = await this.getModuleInfo(targetModuleId);
647
+ const localBindingName = targetModule.exports.get(callee.property.name);
648
+ if (localBindingName) {
649
+ const exportedBinding = targetModule.bindings.get(localBindingName);
650
+ if (exportedBinding) return await this.resolveBindingKind(exportedBinding, targetModule.id, visited);
651
+ }
652
+ } else return "None";
653
+ }
654
+ }
655
+ return this.resolveExprKind(callee.object, fileId, visited);
656
+ }
657
+ return this.resolveExprKind(callee, fileId, visited);
658
+ }
659
+ async getModuleInfo(id) {
660
+ let cached = this.moduleCache.get(id);
661
+ if (cached) return cached;
662
+ await this.options.loadModule(id);
663
+ cached = this.moduleCache.get(id);
664
+ if (!cached) throw new Error(`could not load module info for ${id}`);
665
+ return cached;
666
+ }
667
+ };
668
+ /**
669
+ * Checks if a CallExpression has a method chain pattern that matches any of the lookup kinds.
670
+ * E.g., `.handler()`, `.server()`, `.client()`, `.createMiddlewares()`
671
+ */
820
672
  function isMethodChainCandidate(node, lookupKinds) {
821
- const callee = node.callee;
822
- if (!t.isMemberExpression(callee) || !t.isIdentifier(callee.property)) {
823
- return false;
824
- }
825
- const possibleKinds = IdentifierToKinds.get(callee.property.name);
826
- if (possibleKinds) {
827
- for (const kind of possibleKinds) {
828
- if (lookupKinds.has(kind)) {
829
- return true;
830
- }
831
- }
832
- }
833
- return false;
673
+ const callee = node.callee;
674
+ if (!t.isMemberExpression(callee) || !t.isIdentifier(callee.property)) return false;
675
+ const possibleKinds = IdentifierToKinds.get(callee.property.name);
676
+ if (possibleKinds) {
677
+ for (const kind of possibleKinds) if (lookupKinds.has(kind)) return true;
678
+ }
679
+ return false;
834
680
  }
835
- export {
836
- KindDetectionPatterns,
837
- LookupKindsPerEnv,
838
- StartCompiler,
839
- detectKindsInCode
840
- };
841
- //# sourceMappingURL=compiler.js.map
681
+ //#endregion
682
+ export { KindDetectionPatterns, LookupKindsPerEnv, StartCompiler, detectKindsInCode };
683
+
684
+ //# sourceMappingURL=compiler.js.map