@tanstack/start-plugin-core 1.166.11 → 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.
- package/dist/esm/build-sitemap.js +94 -123
- package/dist/esm/build-sitemap.js.map +1 -1
- package/dist/esm/constants.js +15 -20
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/dev-server-plugin/dev-styles.js +137 -150
- package/dist/esm/dev-server-plugin/dev-styles.js.map +1 -1
- package/dist/esm/dev-server-plugin/extract-html-scripts.js +16 -15
- package/dist/esm/dev-server-plugin/extract-html-scripts.js.map +1 -1
- package/dist/esm/dev-server-plugin/plugin.js +125 -195
- package/dist/esm/dev-server-plugin/plugin.js.map +1 -1
- package/dist/esm/import-protection-plugin/ast.js +6 -5
- package/dist/esm/import-protection-plugin/ast.js.map +1 -1
- package/dist/esm/import-protection-plugin/constants.js +20 -22
- package/dist/esm/import-protection-plugin/constants.js.map +1 -1
- package/dist/esm/import-protection-plugin/defaults.js +35 -25
- package/dist/esm/import-protection-plugin/defaults.js.map +1 -1
- package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js +93 -92
- package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js.map +1 -1
- package/dist/esm/import-protection-plugin/matchers.js +23 -24
- package/dist/esm/import-protection-plugin/matchers.js.map +1 -1
- package/dist/esm/import-protection-plugin/plugin.js +1045 -1361
- package/dist/esm/import-protection-plugin/plugin.js.map +1 -1
- package/dist/esm/import-protection-plugin/postCompileUsage.js +58 -55
- package/dist/esm/import-protection-plugin/postCompileUsage.js.map +1 -1
- package/dist/esm/import-protection-plugin/rewriteDeniedImports.js +187 -259
- package/dist/esm/import-protection-plugin/rewriteDeniedImports.js.map +1 -1
- package/dist/esm/import-protection-plugin/sourceLocation.js +238 -248
- package/dist/esm/import-protection-plugin/sourceLocation.js.map +1 -1
- package/dist/esm/import-protection-plugin/trace.js +173 -184
- package/dist/esm/import-protection-plugin/trace.js.map +1 -1
- package/dist/esm/import-protection-plugin/utils.js +132 -111
- package/dist/esm/import-protection-plugin/utils.js.map +1 -1
- package/dist/esm/import-protection-plugin/virtualModules.js +214 -194
- package/dist/esm/import-protection-plugin/virtualModules.js.map +1 -1
- package/dist/esm/index.js +2 -7
- package/dist/esm/load-env-plugin/plugin.js +12 -11
- package/dist/esm/load-env-plugin/plugin.js.map +1 -1
- package/dist/esm/output-directory.js +10 -10
- package/dist/esm/output-directory.js.map +1 -1
- package/dist/esm/plugin.js +275 -355
- package/dist/esm/plugin.js.map +1 -1
- package/dist/esm/post-server-build.js +39 -53
- package/dist/esm/post-server-build.js.map +1 -1
- package/dist/esm/prerender.js +177 -239
- package/dist/esm/prerender.js.map +1 -1
- package/dist/esm/preview-server-plugin/plugin.js +41 -44
- package/dist/esm/preview-server-plugin/plugin.js.map +1 -1
- package/dist/esm/queue.js +115 -126
- package/dist/esm/queue.js.map +1 -1
- package/dist/esm/resolve-entries.js +31 -32
- package/dist/esm/resolve-entries.js.map +1 -1
- package/dist/esm/schema.js +156 -179
- package/dist/esm/schema.js.map +1 -1
- package/dist/esm/start-compiler-plugin/compiler.js +655 -812
- package/dist/esm/start-compiler-plugin/compiler.js.map +1 -1
- package/dist/esm/start-compiler-plugin/handleClientOnlyJSX.js +25 -8
- package/dist/esm/start-compiler-plugin/handleClientOnlyJSX.js.map +1 -1
- package/dist/esm/start-compiler-plugin/handleCreateIsomorphicFn.js +22 -19
- package/dist/esm/start-compiler-plugin/handleCreateIsomorphicFn.js.map +1 -1
- package/dist/esm/start-compiler-plugin/handleCreateMiddleware.js +20 -22
- package/dist/esm/start-compiler-plugin/handleCreateMiddleware.js.map +1 -1
- package/dist/esm/start-compiler-plugin/handleCreateServerFn.js +187 -255
- package/dist/esm/start-compiler-plugin/handleCreateServerFn.js.map +1 -1
- package/dist/esm/start-compiler-plugin/handleEnvOnly.js +23 -33
- package/dist/esm/start-compiler-plugin/handleEnvOnly.js.map +1 -1
- package/dist/esm/start-compiler-plugin/plugin.js +247 -291
- package/dist/esm/start-compiler-plugin/plugin.js.map +1 -1
- package/dist/esm/start-compiler-plugin/utils.js +27 -27
- package/dist/esm/start-compiler-plugin/utils.js.map +1 -1
- package/dist/esm/start-manifest-plugin/manifestBuilder.js +272 -378
- package/dist/esm/start-manifest-plugin/manifestBuilder.js.map +1 -1
- package/dist/esm/start-manifest-plugin/plugin.js +35 -44
- package/dist/esm/start-manifest-plugin/plugin.js.map +1 -1
- package/dist/esm/start-router-plugin/constants.js +6 -5
- package/dist/esm/start-router-plugin/constants.js.map +1 -1
- package/dist/esm/start-router-plugin/generator-plugins/prerender-routes-plugin.js +24 -19
- package/dist/esm/start-router-plugin/generator-plugins/prerender-routes-plugin.js.map +1 -1
- package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js +28 -29
- package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js.map +1 -1
- package/dist/esm/start-router-plugin/plugin.js +146 -199
- package/dist/esm/start-router-plugin/plugin.js.map +1 -1
- package/dist/esm/start-router-plugin/pruneServerOnlySubtrees.js +32 -31
- package/dist/esm/start-router-plugin/pruneServerOnlySubtrees.js.map +1 -1
- package/dist/esm/utils.js +14 -14
- package/dist/esm/utils.js.map +1 -1
- package/package.json +7 -7
- 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
|
-
|
|
12
|
+
return kind in LookupSetup;
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
90
|
+
var IdentifierToKinds = /* @__PURE__ */ new Map();
|
|
75
91
|
for (const kind of AllLookupKinds) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
102
|
+
var DirectCallFactoryNames = /* @__PURE__ */ new Set();
|
|
89
103
|
for (const kind of AllLookupKinds) {
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
|
|
108
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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 = /* @__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.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.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.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.set("default", d.name);
|
|
350
|
-
} else {
|
|
351
|
-
const synth = "__default_export__";
|
|
352
|
-
bindings.set(synth, { type: "var", init: d });
|
|
353
|
-
exports.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,
|
|
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
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
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
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
detectKindsInCode
|
|
840
|
-
};
|
|
841
|
-
//# sourceMappingURL=compiler.js.map
|
|
681
|
+
//#endregion
|
|
682
|
+
export { KindDetectionPatterns, LookupKindsPerEnv, StartCompiler, detectKindsInCode };
|
|
683
|
+
|
|
684
|
+
//# sourceMappingURL=compiler.js.map
|