@pracht/vite-plugin 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.mts +20 -5
- package/dist/index.mjs +1123 -171
- package/dist/pages-router.mjs +1 -1
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,944 @@
|
|
|
1
1
|
import { generatePagesManifestSource, scanPagesDirectory } from "./pages-router.mjs";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
2
|
import preact from "@preact/preset-vite";
|
|
5
|
-
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { parseAst } from "vite";
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
//#region src/client-module-query.ts
|
|
7
|
+
const CLIENT_MODULE_QUERY = "pracht-client";
|
|
8
|
+
const PRACHT_CLIENT_MODULE_QUERY = `?${CLIENT_MODULE_QUERY}`;
|
|
9
|
+
function isPrachtClientModuleId(id) {
|
|
10
|
+
const queryStart = id.indexOf("?");
|
|
11
|
+
if (queryStart === -1) return false;
|
|
12
|
+
return id.slice(queryStart + 1).split("&").includes(CLIENT_MODULE_QUERY);
|
|
13
|
+
}
|
|
14
|
+
function stripPrachtClientModuleQuery(id) {
|
|
15
|
+
const queryStart = id.indexOf("?");
|
|
16
|
+
if (queryStart === -1) return id;
|
|
17
|
+
const path = id.slice(0, queryStart);
|
|
18
|
+
const query = id.slice(queryStart + 1).split("&").filter((part) => part !== CLIENT_MODULE_QUERY);
|
|
19
|
+
return query.length > 0 ? `${path}?${query.join("&")}` : path;
|
|
20
|
+
}
|
|
21
|
+
function getRolldownLang(id) {
|
|
22
|
+
const path = stripPrachtClientModuleQuery(id).split("?")[0];
|
|
23
|
+
if (/\.(c|m)?tsx$/i.test(path)) return "tsx";
|
|
24
|
+
if (/\.(c|m)?ts$/i.test(path)) return "ts";
|
|
25
|
+
if (/\.(c|m)?jsx$/i.test(path)) return "jsx";
|
|
26
|
+
if (/\.mdx?$/i.test(path)) return "jsx";
|
|
27
|
+
if (/\.(c|m)?js$/i.test(path)) return "js";
|
|
28
|
+
return "tsx";
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/scope-analysis-types.ts
|
|
32
|
+
const JSX_COMPONENT_RE = /^[A-Z]/;
|
|
33
|
+
const SKIPPED_KEYS = new Set([
|
|
34
|
+
"attributes",
|
|
35
|
+
"decorators",
|
|
36
|
+
"end",
|
|
37
|
+
"exportKind",
|
|
38
|
+
"importKind",
|
|
39
|
+
"optional",
|
|
40
|
+
"phase",
|
|
41
|
+
"raw",
|
|
42
|
+
"returnType",
|
|
43
|
+
"start",
|
|
44
|
+
"superTypeArguments",
|
|
45
|
+
"type",
|
|
46
|
+
"typeAnnotation",
|
|
47
|
+
"typeArguments",
|
|
48
|
+
"typeParameters",
|
|
49
|
+
"value"
|
|
50
|
+
]);
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/scope-analysis-helpers.ts
|
|
53
|
+
function getStatementDeclaration(statement) {
|
|
54
|
+
if (statement.type === "ExportNamedDeclaration") return statement.declaration ?? null;
|
|
55
|
+
if (statement.type === "ExportDefaultDeclaration" && (statement.declaration.type === "FunctionDeclaration" || statement.declaration.type === "ClassDeclaration")) return statement.declaration;
|
|
56
|
+
if (statement.type === "FunctionDeclaration" || statement.type === "ClassDeclaration" || statement.type === "VariableDeclaration") return statement;
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function collectBindingNamesFromPattern(pattern) {
|
|
60
|
+
if (!pattern) return [];
|
|
61
|
+
switch (pattern.type) {
|
|
62
|
+
case "Identifier": return [pattern.name];
|
|
63
|
+
case "AssignmentPattern": return collectBindingNamesFromPattern(pattern.left);
|
|
64
|
+
case "RestElement": return collectBindingNamesFromPattern(pattern.argument);
|
|
65
|
+
case "ObjectPattern": return pattern.properties.flatMap((property) => {
|
|
66
|
+
if (property.type === "Property") return collectBindingNamesFromPattern(property.value);
|
|
67
|
+
return collectBindingNamesFromPattern(property.argument);
|
|
68
|
+
});
|
|
69
|
+
case "ArrayPattern": return pattern.elements.flatMap((element) => collectBindingNamesFromPattern(element));
|
|
70
|
+
default: return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function getIdentifierName(node) {
|
|
74
|
+
if (!node) return null;
|
|
75
|
+
if (node.type === "Identifier" || node.type === "JSXIdentifier") return node.name;
|
|
76
|
+
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function isNode(value) {
|
|
80
|
+
return !!value && typeof value === "object" && "type" in value;
|
|
81
|
+
}
|
|
82
|
+
function getTsRuntimeChildren(node) {
|
|
83
|
+
switch (node.type) {
|
|
84
|
+
case "TSAsExpression":
|
|
85
|
+
case "TSInstantiationExpression":
|
|
86
|
+
case "TSNonNullExpression":
|
|
87
|
+
case "TSSatisfiesExpression":
|
|
88
|
+
case "TSTypeAssertion": return [node.expression];
|
|
89
|
+
default: return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function collectFunctionScopedVarBindings(node) {
|
|
93
|
+
const names = /* @__PURE__ */ new Set();
|
|
94
|
+
collectFunctionScopedVarBindingsInto(node, names);
|
|
95
|
+
return names;
|
|
96
|
+
}
|
|
97
|
+
function collectFunctionScopedVarBindingsInto(node, names) {
|
|
98
|
+
if (!node) return;
|
|
99
|
+
if (node.type.startsWith("TS")) {
|
|
100
|
+
for (const child of getTsRuntimeChildren(node)) collectFunctionScopedVarBindingsInto(child, names);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
switch (node.type) {
|
|
104
|
+
case "ArrowFunctionExpression":
|
|
105
|
+
case "FunctionDeclaration":
|
|
106
|
+
case "FunctionExpression":
|
|
107
|
+
case "ClassDeclaration":
|
|
108
|
+
case "ClassExpression": return;
|
|
109
|
+
case "VariableDeclaration":
|
|
110
|
+
if (node.kind === "var") for (const declarator of node.declarations) for (const name of collectBindingNamesFromPattern(declarator.id)) names.add(name);
|
|
111
|
+
return;
|
|
112
|
+
default: for (const [key, value] of Object.entries(node)) {
|
|
113
|
+
if (SKIPPED_KEYS.has(key)) continue;
|
|
114
|
+
if (key === "id" || key === "implements" || key === "superTypeArguments") continue;
|
|
115
|
+
collectFunctionScopedVarBindingsFromUnknown(value, names);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function collectFunctionScopedVarBindingsFromUnknown(value, names) {
|
|
120
|
+
if (Array.isArray(value)) {
|
|
121
|
+
for (const item of value) collectFunctionScopedVarBindingsFromUnknown(item, names);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (!isNode(value)) return;
|
|
125
|
+
collectFunctionScopedVarBindingsInto(value, names);
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/client-module-transform-state.ts
|
|
129
|
+
function createStatementStates(program) {
|
|
130
|
+
return program.body.map((node) => ({
|
|
131
|
+
node,
|
|
132
|
+
removed: false,
|
|
133
|
+
removedDeclarators: /* @__PURE__ */ new Set(),
|
|
134
|
+
removedSpecifiers: /* @__PURE__ */ new Set()
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
function getRemainingDeclaratorIndices(state) {
|
|
138
|
+
const declaration = getStatementDeclaration(state.node);
|
|
139
|
+
if (!declaration || declaration.type !== "VariableDeclaration") return [];
|
|
140
|
+
return declaration.declarations.map((_item, index) => index).filter((index) => !state.removedDeclarators.has(index));
|
|
141
|
+
}
|
|
142
|
+
function getRemainingSpecifierIndices(state) {
|
|
143
|
+
const statement = state.node;
|
|
144
|
+
if (!("specifiers" in statement) || !Array.isArray(statement.specifiers)) return [];
|
|
145
|
+
return statement.specifiers.map((_item, index) => index).filter((index) => !state.removedSpecifiers.has(index));
|
|
146
|
+
}
|
|
147
|
+
function collectBindingNamesFromDeclaration(declaration) {
|
|
148
|
+
if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") return declaration.id ? [declaration.id.name] : [];
|
|
149
|
+
if (declaration.type === "VariableDeclaration") return declaration.declarations.flatMap((declarator) => collectBindingNamesFromPattern(declarator.id));
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
function normalizeRetainedStatements(states) {
|
|
153
|
+
return states.map((state) => normalizeRetainedStatement(state)).filter((state) => state !== null);
|
|
154
|
+
}
|
|
155
|
+
function normalizeRetainedStatement(state) {
|
|
156
|
+
if (state.removed) return null;
|
|
157
|
+
const statement = state.node;
|
|
158
|
+
if (statement.type === "ImportDeclaration" && state.removedSpecifiers.size > 0) return { node: {
|
|
159
|
+
...statement,
|
|
160
|
+
specifiers: getRemainingSpecifierIndices(state).map((index) => statement.specifiers[index])
|
|
161
|
+
} };
|
|
162
|
+
if (statement.type === "ExportNamedDeclaration" && !statement.declaration && state.removedSpecifiers.size > 0) return { node: {
|
|
163
|
+
...statement,
|
|
164
|
+
specifiers: getRemainingSpecifierIndices(state).map((index) => statement.specifiers[index])
|
|
165
|
+
} };
|
|
166
|
+
const declaration = getStatementDeclaration(statement);
|
|
167
|
+
if (declaration?.type === "VariableDeclaration" && state.removedDeclarators.size > 0) {
|
|
168
|
+
const retainedDeclaration = {
|
|
169
|
+
...declaration,
|
|
170
|
+
declarations: getRemainingDeclaratorIndices(state).map((index) => declaration.declarations[index])
|
|
171
|
+
};
|
|
172
|
+
if (statement.type === "ExportNamedDeclaration") return { node: {
|
|
173
|
+
...statement,
|
|
174
|
+
declaration: retainedDeclaration
|
|
175
|
+
} };
|
|
176
|
+
return { node: retainedDeclaration };
|
|
177
|
+
}
|
|
178
|
+
return { node: statement };
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/client-module-transform-render.ts
|
|
182
|
+
function renderProgram(code, states) {
|
|
183
|
+
let cursor = 0;
|
|
184
|
+
let out = "";
|
|
185
|
+
for (const state of states) {
|
|
186
|
+
const statement = state.node;
|
|
187
|
+
out += code.slice(cursor, statement.start);
|
|
188
|
+
out += renderStatement(code, state);
|
|
189
|
+
cursor = statement.end;
|
|
190
|
+
}
|
|
191
|
+
out += code.slice(cursor);
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
194
|
+
function renderStatement(code, state) {
|
|
195
|
+
if (state.removed) return "";
|
|
196
|
+
const statement = state.node;
|
|
197
|
+
const declaration = getStatementDeclaration(statement);
|
|
198
|
+
if (statement.type === "ImportDeclaration" && state.removedSpecifiers.size > 0) return renderImportDeclaration(code, statement, state);
|
|
199
|
+
if (statement.type === "ExportNamedDeclaration" && !statement.declaration && state.removedSpecifiers.size > 0) return renderExportSpecifiers(code, statement, state);
|
|
200
|
+
if (declaration?.type === "VariableDeclaration" && state.removedDeclarators.size > 0) return renderVariableDeclaration(code, statement, declaration, state);
|
|
201
|
+
return code.slice(statement.start, statement.end);
|
|
202
|
+
}
|
|
203
|
+
function renderImportDeclaration(code, statement, state) {
|
|
204
|
+
const remaining = getRemainingSpecifierIndices(state).map((index) => statement.specifiers[index]);
|
|
205
|
+
if (remaining.length === 0) return "";
|
|
206
|
+
const defaultSpecifier = remaining.find((specifier) => specifier.type === "ImportDefaultSpecifier");
|
|
207
|
+
const namespaceSpecifier = remaining.find((specifier) => specifier.type === "ImportNamespaceSpecifier");
|
|
208
|
+
const namedSpecifiers = remaining.filter((specifier) => specifier.type === "ImportSpecifier");
|
|
209
|
+
const clauseParts = [];
|
|
210
|
+
if (defaultSpecifier) clauseParts.push(code.slice(defaultSpecifier.start, defaultSpecifier.end));
|
|
211
|
+
if (namespaceSpecifier) clauseParts.push(code.slice(namespaceSpecifier.start, namespaceSpecifier.end));
|
|
212
|
+
if (namedSpecifiers.length > 0) clauseParts.push(`{ ${namedSpecifiers.map((specifier) => code.slice(specifier.start, specifier.end)).join(", ")} }`);
|
|
213
|
+
const importPrefix = ["import"];
|
|
214
|
+
if (statement.importKind === "type") importPrefix.push("type");
|
|
215
|
+
if (typeof statement.phase === "string" && statement.phase.length > 0) importPrefix.push(statement.phase);
|
|
216
|
+
return `${importPrefix.join(" ")} ${clauseParts.join(", ")} from ${code.slice(statement.source.start, statement.end)}`;
|
|
217
|
+
}
|
|
218
|
+
function renderExportSpecifiers(code, statement, state) {
|
|
219
|
+
const remaining = getRemainingSpecifierIndices(state).map((index) => statement.specifiers[index]);
|
|
220
|
+
if (remaining.length === 0) return "";
|
|
221
|
+
const exportPrefix = statement.exportKind === "type" ? "export type" : "export";
|
|
222
|
+
const sourceSuffix = statement.source ? ` from ${code.slice(statement.source.start, statement.end)}` : ";";
|
|
223
|
+
return `${exportPrefix} { ${remaining.map((specifier) => code.slice(specifier.start, specifier.end)).join(", ")} }${sourceSuffix}`;
|
|
224
|
+
}
|
|
225
|
+
function renderVariableDeclaration(code, statement, declaration, state) {
|
|
226
|
+
const remaining = getRemainingDeclaratorIndices(state).map((index) => declaration.declarations[index]);
|
|
227
|
+
if (remaining.length === 0) return "";
|
|
228
|
+
return `${statement.type === "ExportNamedDeclaration" ? "export " : ""}${declaration.kind} ${remaining.map((item) => code.slice(item.start, item.end)).join(", ")};`;
|
|
229
|
+
}
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/scope-analysis-declare.ts
|
|
232
|
+
function createScope(type, parent, node) {
|
|
233
|
+
return {
|
|
234
|
+
bindings: /* @__PURE__ */ new Map(),
|
|
235
|
+
node,
|
|
236
|
+
parent,
|
|
237
|
+
type
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function declareBinding(scope, name, kind, node) {
|
|
241
|
+
const binding = {
|
|
242
|
+
kind,
|
|
243
|
+
name,
|
|
244
|
+
node,
|
|
245
|
+
scope
|
|
246
|
+
};
|
|
247
|
+
scope.bindings.set(name, binding);
|
|
248
|
+
return binding;
|
|
249
|
+
}
|
|
250
|
+
function declareProgramScopes(statements, programScope, scopesByNode) {
|
|
251
|
+
for (const statement of statements) declareTopLevelStatement(statement.node, programScope);
|
|
252
|
+
for (const statement of statements) declareNodeScopes(statement.node, programScope, scopesByNode);
|
|
253
|
+
}
|
|
254
|
+
function declareTopLevelStatement(statement, programScope) {
|
|
255
|
+
if (statement.type === "ImportDeclaration") {
|
|
256
|
+
if (statement.importKind === "type") return;
|
|
257
|
+
for (const specifier of statement.specifiers) {
|
|
258
|
+
if (specifier.type === "ImportSpecifier" && specifier.importKind === "type") continue;
|
|
259
|
+
const localName = getIdentifierName(specifier.local);
|
|
260
|
+
if (localName) declareBinding(programScope, localName, "import", specifier);
|
|
261
|
+
}
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const declaration = getStatementDeclaration(statement);
|
|
265
|
+
if (!declaration) return;
|
|
266
|
+
declareDeclarationBindings(programScope, declaration);
|
|
267
|
+
}
|
|
268
|
+
function declareNodeScopes(node, currentScope, scopesByNode) {
|
|
269
|
+
if (!node) return;
|
|
270
|
+
if (node.type.startsWith("TS")) {
|
|
271
|
+
for (const child of getTsRuntimeChildren(node)) declareNodeScopes(child, currentScope, scopesByNode);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
switch (node.type) {
|
|
275
|
+
case "ImportDeclaration": return;
|
|
276
|
+
case "ArrowFunctionExpression":
|
|
277
|
+
case "FunctionDeclaration":
|
|
278
|
+
case "FunctionExpression": {
|
|
279
|
+
const functionScope = createScope("function", currentScope, node);
|
|
280
|
+
scopesByNode.set(node, functionScope);
|
|
281
|
+
declareFunctionBindings(node, functionScope);
|
|
282
|
+
for (const param of node.params) declareNodeScopes(param, functionScope, scopesByNode);
|
|
283
|
+
declareNodeScopes(node.body, functionScope, scopesByNode);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
case "BlockStatement": {
|
|
287
|
+
const blockScope = createScope("block", currentScope, node);
|
|
288
|
+
scopesByNode.set(node, blockScope);
|
|
289
|
+
declareBlockBindings(node.body, blockScope);
|
|
290
|
+
for (const statement of node.body) declareNodeScopes(statement, blockScope, scopesByNode);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
case "CatchClause": {
|
|
294
|
+
const catchScope = createScope("catch", currentScope, node);
|
|
295
|
+
scopesByNode.set(node, catchScope);
|
|
296
|
+
declareCatchBindings(node, catchScope);
|
|
297
|
+
if (node.param) declareNodeScopes(node.param, catchScope, scopesByNode);
|
|
298
|
+
declareNodeScopes(node.body, catchScope, scopesByNode);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
case "ForStatement": {
|
|
302
|
+
const init = node.init;
|
|
303
|
+
if (init?.type === "VariableDeclaration" && init.kind !== "var") {
|
|
304
|
+
const loopScope = createScope("for", currentScope, node);
|
|
305
|
+
scopesByNode.set(node, loopScope);
|
|
306
|
+
declareDeclarationBindings(loopScope, init);
|
|
307
|
+
declareNodeScopes(init, loopScope, scopesByNode);
|
|
308
|
+
declareNodeScopes(node.test, loopScope, scopesByNode);
|
|
309
|
+
declareNodeScopes(node.update, loopScope, scopesByNode);
|
|
310
|
+
declareNodeScopes(node.body, loopScope, scopesByNode);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
declareNodeScopes(init, currentScope, scopesByNode);
|
|
314
|
+
declareNodeScopes(node.test, currentScope, scopesByNode);
|
|
315
|
+
declareNodeScopes(node.update, currentScope, scopesByNode);
|
|
316
|
+
declareNodeScopes(node.body, currentScope, scopesByNode);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
case "ForInStatement":
|
|
320
|
+
case "ForOfStatement": {
|
|
321
|
+
const left = node.left;
|
|
322
|
+
if (left?.type === "VariableDeclaration" && left.kind !== "var") {
|
|
323
|
+
const loopScope = createScope("for", currentScope, node);
|
|
324
|
+
scopesByNode.set(node, loopScope);
|
|
325
|
+
declareDeclarationBindings(loopScope, left);
|
|
326
|
+
declareNodeScopes(left, loopScope, scopesByNode);
|
|
327
|
+
declareNodeScopes(node.right, loopScope, scopesByNode);
|
|
328
|
+
declareNodeScopes(node.body, loopScope, scopesByNode);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
declareNodeScopes(left, currentScope, scopesByNode);
|
|
332
|
+
declareNodeScopes(node.right, currentScope, scopesByNode);
|
|
333
|
+
declareNodeScopes(node.body, currentScope, scopesByNode);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
case "SwitchStatement": {
|
|
337
|
+
declareNodeScopes(node.discriminant, currentScope, scopesByNode);
|
|
338
|
+
const switchScope = createScope("switch", currentScope, node);
|
|
339
|
+
scopesByNode.set(node, switchScope);
|
|
340
|
+
declareSwitchBindings(node.cases, switchScope);
|
|
341
|
+
for (const switchCase of node.cases) declareNodeScopes(switchCase, switchScope, scopesByNode);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
case "ClassDeclaration":
|
|
345
|
+
case "ClassExpression": {
|
|
346
|
+
declareNodeScopes(node.superClass, currentScope, scopesByNode);
|
|
347
|
+
const classScope = createScope("class", currentScope, node);
|
|
348
|
+
scopesByNode.set(node, classScope);
|
|
349
|
+
const name = getIdentifierName(node.id);
|
|
350
|
+
if (name) declareBinding(classScope, name, "class", node);
|
|
351
|
+
declareNodeScopes(node.body, classScope, scopesByNode);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
case "ExportNamedDeclaration":
|
|
355
|
+
if (node.declaration) declareNodeScopes(node.declaration, currentScope, scopesByNode);
|
|
356
|
+
return;
|
|
357
|
+
case "ExportDefaultDeclaration":
|
|
358
|
+
if (node.declaration.type !== "Identifier") declareNodeScopes(node.declaration, currentScope, scopesByNode);
|
|
359
|
+
return;
|
|
360
|
+
default: for (const [key, value] of Object.entries(node)) {
|
|
361
|
+
if (SKIPPED_KEYS.has(key)) continue;
|
|
362
|
+
if (key === "id" || key === "implements" || key === "superTypeArguments") continue;
|
|
363
|
+
declareUnknownValue(value, currentScope, scopesByNode);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function declareUnknownValue(value, currentScope, scopesByNode) {
|
|
368
|
+
if (Array.isArray(value)) {
|
|
369
|
+
for (const item of value) declareUnknownValue(item, currentScope, scopesByNode);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (!isNode(value)) return;
|
|
373
|
+
declareNodeScopes(value, currentScope, scopesByNode);
|
|
374
|
+
}
|
|
375
|
+
function declareFunctionBindings(node, scope) {
|
|
376
|
+
const functionName = getIdentifierName(node.id);
|
|
377
|
+
if (functionName) declareBinding(scope, functionName, "function", node);
|
|
378
|
+
for (const param of node.params) for (const name of collectBindingNamesFromPattern(param)) declareBinding(scope, name, "param", param);
|
|
379
|
+
for (const name of collectFunctionScopedVarBindings(node.body)) declareBinding(scope, name, "var", node.body);
|
|
380
|
+
}
|
|
381
|
+
function declareBlockBindings(statements, scope) {
|
|
382
|
+
for (const statement of statements) {
|
|
383
|
+
const declaration = getStatementDeclaration(statement);
|
|
384
|
+
if (!declaration) continue;
|
|
385
|
+
if (declaration.type === "VariableDeclaration" && declaration.kind === "var") continue;
|
|
386
|
+
declareDeclarationBindings(scope, declaration);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function declareCatchBindings(node, scope) {
|
|
390
|
+
if (!node.param) return;
|
|
391
|
+
for (const name of collectBindingNamesFromPattern(node.param)) declareBinding(scope, name, "catch", node.param);
|
|
392
|
+
}
|
|
393
|
+
function declareSwitchBindings(cases, scope) {
|
|
394
|
+
const statements = [];
|
|
395
|
+
for (const switchCase of cases) for (const statement of switchCase.consequent) statements.push(statement);
|
|
396
|
+
declareBlockBindings(statements, scope);
|
|
397
|
+
}
|
|
398
|
+
function declareDeclarationBindings(scope, declaration) {
|
|
399
|
+
if (declaration.type === "FunctionDeclaration") {
|
|
400
|
+
const name = getIdentifierName(declaration.id);
|
|
401
|
+
if (name) declareBinding(scope, name, "function", declaration);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (declaration.type === "ClassDeclaration") {
|
|
405
|
+
const name = getIdentifierName(declaration.id);
|
|
406
|
+
if (name) declareBinding(scope, name, "class", declaration);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (declaration.type !== "VariableDeclaration") return;
|
|
410
|
+
for (const declarator of declaration.declarations) for (const name of collectBindingNamesFromPattern(declarator.id)) declareBinding(scope, name, declaration.kind, declarator);
|
|
411
|
+
}
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region src/scope-analysis-references.ts
|
|
414
|
+
function collectStatementReferences(statement, currentScope, scopesByNode, result, excludedNames) {
|
|
415
|
+
if (statement.type === "ImportDeclaration") return;
|
|
416
|
+
if (statement.type === "ExportNamedDeclaration") {
|
|
417
|
+
const declaration = statement.declaration;
|
|
418
|
+
if (declaration) collectNodeReferences(declaration, currentScope, scopesByNode, result, excludedNames);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (statement.type === "ExportDefaultDeclaration") {
|
|
422
|
+
const declaration = statement.declaration;
|
|
423
|
+
if (declaration.type !== "Identifier") collectNodeReferences(declaration, currentScope, scopesByNode, result, excludedNames);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
collectNodeReferences(statement, currentScope, scopesByNode, result, excludedNames);
|
|
427
|
+
}
|
|
428
|
+
function collectNodeReferences(node, currentScope, scopesByNode, result, excludedNames) {
|
|
429
|
+
if (!node) return;
|
|
430
|
+
if (node.type.startsWith("TS")) {
|
|
431
|
+
for (const child of getTsRuntimeChildren(node)) collectNodeReferences(child, currentScope, scopesByNode, result, excludedNames);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
switch (node.type) {
|
|
435
|
+
case "Identifier":
|
|
436
|
+
recordReference(node.name, node, currentScope, result, excludedNames);
|
|
437
|
+
return;
|
|
438
|
+
case "JSXIdentifier":
|
|
439
|
+
if (JSX_COMPONENT_RE.test(node.name)) recordReference(node.name, node, currentScope, result, excludedNames);
|
|
440
|
+
return;
|
|
441
|
+
case "ArrowFunctionExpression":
|
|
442
|
+
case "FunctionDeclaration":
|
|
443
|
+
case "FunctionExpression": {
|
|
444
|
+
const functionScope = scopesByNode.get(node) ?? currentScope;
|
|
445
|
+
for (const param of node.params) collectPatternReferences(param, functionScope, scopesByNode, result, excludedNames);
|
|
446
|
+
collectNodeReferences(node.body, functionScope, scopesByNode, result, excludedNames);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
case "BlockStatement": {
|
|
450
|
+
const blockScope = scopesByNode.get(node) ?? currentScope;
|
|
451
|
+
for (const statement of node.body) collectStatementReferences(statement, blockScope, scopesByNode, result, excludedNames);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
case "CatchClause": {
|
|
455
|
+
const catchScope = scopesByNode.get(node) ?? currentScope;
|
|
456
|
+
if (node.param) collectPatternReferences(node.param, catchScope, scopesByNode, result, excludedNames);
|
|
457
|
+
collectNodeReferences(node.body, catchScope, scopesByNode, result, excludedNames);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
case "ForStatement": {
|
|
461
|
+
const loopScope = scopesByNode.get(node) ?? currentScope;
|
|
462
|
+
collectNodeReferences(node.init, loopScope, scopesByNode, result, excludedNames);
|
|
463
|
+
collectNodeReferences(node.test, loopScope, scopesByNode, result, excludedNames);
|
|
464
|
+
collectNodeReferences(node.update, loopScope, scopesByNode, result, excludedNames);
|
|
465
|
+
collectNodeReferences(node.body, loopScope, scopesByNode, result, excludedNames);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
case "ForInStatement":
|
|
469
|
+
case "ForOfStatement": {
|
|
470
|
+
const loopScope = scopesByNode.get(node) ?? currentScope;
|
|
471
|
+
collectNodeReferences(node.left, loopScope, scopesByNode, result, excludedNames);
|
|
472
|
+
collectNodeReferences(node.right, loopScope, scopesByNode, result, excludedNames);
|
|
473
|
+
collectNodeReferences(node.body, loopScope, scopesByNode, result, excludedNames);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
case "SwitchStatement": {
|
|
477
|
+
collectNodeReferences(node.discriminant, currentScope, scopesByNode, result, excludedNames);
|
|
478
|
+
const switchScope = scopesByNode.get(node) ?? currentScope;
|
|
479
|
+
for (const switchCase of node.cases) {
|
|
480
|
+
collectNodeReferences(switchCase.test, switchScope, scopesByNode, result, excludedNames);
|
|
481
|
+
for (const statement of switchCase.consequent) collectStatementReferences(statement, switchScope, scopesByNode, result, excludedNames);
|
|
482
|
+
}
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
case "ClassDeclaration":
|
|
486
|
+
case "ClassExpression": {
|
|
487
|
+
collectNodeReferences(node.superClass, currentScope, scopesByNode, result, excludedNames);
|
|
488
|
+
const classScope = scopesByNode.get(node) ?? currentScope;
|
|
489
|
+
collectNodeReferences(node.body, classScope, scopesByNode, result, excludedNames);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
case "VariableDeclaration":
|
|
493
|
+
for (const declarator of node.declarations) collectVariableDeclaratorReferences(declarator, currentScope, scopesByNode, result, excludedNames);
|
|
494
|
+
return;
|
|
495
|
+
case "MemberExpression":
|
|
496
|
+
collectNodeReferences(node.object, currentScope, scopesByNode, result, excludedNames);
|
|
497
|
+
if (node.computed) collectNodeReferences(node.property, currentScope, scopesByNode, result, excludedNames);
|
|
498
|
+
return;
|
|
499
|
+
case "MetaProperty": return;
|
|
500
|
+
case "LabeledStatement":
|
|
501
|
+
collectNodeReferences(node.body, currentScope, scopesByNode, result, excludedNames);
|
|
502
|
+
return;
|
|
503
|
+
case "BreakStatement":
|
|
504
|
+
case "ContinueStatement": return;
|
|
505
|
+
case "Property":
|
|
506
|
+
if (node.computed) collectNodeReferences(node.key, currentScope, scopesByNode, result, excludedNames);
|
|
507
|
+
collectNodeReferences(node.value, currentScope, scopesByNode, result, excludedNames);
|
|
508
|
+
return;
|
|
509
|
+
case "ObjectPattern":
|
|
510
|
+
case "ArrayPattern":
|
|
511
|
+
case "AssignmentPattern":
|
|
512
|
+
case "RestElement":
|
|
513
|
+
collectPatternReferences(node, currentScope, scopesByNode, result, excludedNames);
|
|
514
|
+
return;
|
|
515
|
+
case "JSXElement":
|
|
516
|
+
collectNodeReferences(node.openingElement, currentScope, scopesByNode, result, excludedNames);
|
|
517
|
+
for (const child of node.children) collectNodeReferences(child, currentScope, scopesByNode, result, excludedNames);
|
|
518
|
+
return;
|
|
519
|
+
case "JSXFragment":
|
|
520
|
+
for (const child of node.children) collectNodeReferences(child, currentScope, scopesByNode, result, excludedNames);
|
|
521
|
+
return;
|
|
522
|
+
case "JSXOpeningElement":
|
|
523
|
+
collectNodeReferences(node.name, currentScope, scopesByNode, result, excludedNames);
|
|
524
|
+
for (const attribute of node.attributes) collectNodeReferences(attribute, currentScope, scopesByNode, result, excludedNames);
|
|
525
|
+
return;
|
|
526
|
+
case "JSXClosingElement":
|
|
527
|
+
collectNodeReferences(node.name, currentScope, scopesByNode, result, excludedNames);
|
|
528
|
+
return;
|
|
529
|
+
case "JSXAttribute":
|
|
530
|
+
collectNodeReferences(node.value, currentScope, scopesByNode, result, excludedNames);
|
|
531
|
+
return;
|
|
532
|
+
case "JSXExpressionContainer":
|
|
533
|
+
collectNodeReferences(node.expression, currentScope, scopesByNode, result, excludedNames);
|
|
534
|
+
return;
|
|
535
|
+
case "JSXMemberExpression":
|
|
536
|
+
collectNodeReferences(node.object, currentScope, scopesByNode, result, excludedNames);
|
|
537
|
+
return;
|
|
538
|
+
case "MethodDefinition":
|
|
539
|
+
case "PropertyDefinition":
|
|
540
|
+
if (node.computed) collectNodeReferences(node.key, currentScope, scopesByNode, result, excludedNames);
|
|
541
|
+
collectNodeReferences(node.value, currentScope, scopesByNode, result, excludedNames);
|
|
542
|
+
return;
|
|
543
|
+
case "ImportDeclaration": return;
|
|
544
|
+
case "ExportNamedDeclaration":
|
|
545
|
+
if (node.declaration) collectNodeReferences(node.declaration, currentScope, scopesByNode, result, excludedNames);
|
|
546
|
+
return;
|
|
547
|
+
case "ExportDefaultDeclaration":
|
|
548
|
+
if (node.declaration.type !== "Identifier") collectNodeReferences(node.declaration, currentScope, scopesByNode, result, excludedNames);
|
|
549
|
+
return;
|
|
550
|
+
default: for (const [key, value] of Object.entries(node)) {
|
|
551
|
+
if (SKIPPED_KEYS.has(key)) continue;
|
|
552
|
+
if (key === "id" || key === "implements" || key === "superTypeArguments") continue;
|
|
553
|
+
collectUnknownValueReferences(value, currentScope, scopesByNode, result, excludedNames);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
function collectUnknownValueReferences(value, currentScope, scopesByNode, result, excludedNames) {
|
|
558
|
+
if (Array.isArray(value)) {
|
|
559
|
+
for (const item of value) collectUnknownValueReferences(item, currentScope, scopesByNode, result, excludedNames);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (!isNode(value)) return;
|
|
563
|
+
collectNodeReferences(value, currentScope, scopesByNode, result, excludedNames);
|
|
564
|
+
}
|
|
565
|
+
function collectVariableDeclaratorReferences(declarator, currentScope, scopesByNode, result, excludedNames) {
|
|
566
|
+
collectPatternReferences(declarator.id, currentScope, scopesByNode, result, excludedNames);
|
|
567
|
+
collectNodeReferences(declarator.init, currentScope, scopesByNode, result, excludedNames);
|
|
568
|
+
}
|
|
569
|
+
function collectPatternReferences(node, currentScope, scopesByNode, result, excludedNames) {
|
|
570
|
+
if (!node) return;
|
|
571
|
+
if (node.type.startsWith("TS")) {
|
|
572
|
+
for (const child of getTsRuntimeChildren(node)) collectNodeReferences(child, currentScope, scopesByNode, result, excludedNames);
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
switch (node.type) {
|
|
576
|
+
case "AssignmentPattern":
|
|
577
|
+
collectNodeReferences(node.right, currentScope, scopesByNode, result, excludedNames);
|
|
578
|
+
collectPatternReferences(node.left, currentScope, scopesByNode, result, excludedNames);
|
|
579
|
+
return;
|
|
580
|
+
case "ObjectPattern":
|
|
581
|
+
for (const property of node.properties) {
|
|
582
|
+
if (property.type === "Property") {
|
|
583
|
+
if (property.computed) collectNodeReferences(property.key, currentScope, scopesByNode, result, excludedNames);
|
|
584
|
+
collectPatternReferences(property.value, currentScope, scopesByNode, result, excludedNames);
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
collectPatternReferences(property.argument, currentScope, scopesByNode, result, excludedNames);
|
|
588
|
+
}
|
|
589
|
+
return;
|
|
590
|
+
case "ArrayPattern":
|
|
591
|
+
for (const element of node.elements) collectPatternReferences(element, currentScope, scopesByNode, result, excludedNames);
|
|
592
|
+
return;
|
|
593
|
+
case "RestElement":
|
|
594
|
+
collectPatternReferences(node.argument, currentScope, scopesByNode, result, excludedNames);
|
|
595
|
+
return;
|
|
596
|
+
default: return;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function recordReference(name, node, currentScope, result, excludedNames) {
|
|
600
|
+
const resolvedBinding = resolveBinding(name, currentScope);
|
|
601
|
+
result.references.push({
|
|
602
|
+
name,
|
|
603
|
+
node,
|
|
604
|
+
resolvedBinding
|
|
605
|
+
});
|
|
606
|
+
if (!resolvedBinding) return;
|
|
607
|
+
if (resolvedBinding.scope.type !== "program") return;
|
|
608
|
+
if (excludedNames.has(name)) return;
|
|
609
|
+
result.referencedTopLevelNames.add(name);
|
|
610
|
+
}
|
|
611
|
+
function resolveBinding(name, currentScope) {
|
|
612
|
+
let scope = currentScope;
|
|
613
|
+
while (scope) {
|
|
614
|
+
const binding = scope.bindings.get(name);
|
|
615
|
+
if (binding) return binding;
|
|
616
|
+
scope = scope.parent;
|
|
617
|
+
}
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
//#endregion
|
|
621
|
+
//#region src/client-module-scope-analysis.ts
|
|
622
|
+
function analyzeRetainedStatements(statements, options = {}) {
|
|
623
|
+
const programScope = createScope("program", null, null);
|
|
624
|
+
for (const name of options.knownTopLevelNames ?? []) declareBinding(programScope, name, "placeholder", null);
|
|
625
|
+
const scopesByNode = /* @__PURE__ */ new WeakMap();
|
|
626
|
+
declareProgramScopes(statements, programScope, scopesByNode);
|
|
627
|
+
const result = {
|
|
628
|
+
programScope,
|
|
629
|
+
referencedTopLevelNames: /* @__PURE__ */ new Set(),
|
|
630
|
+
references: []
|
|
631
|
+
};
|
|
632
|
+
const excludedNames = new Set(options.excludedNames);
|
|
633
|
+
for (const statement of statements) collectStatementReferences(statement.node, programScope, scopesByNode, result, excludedNames);
|
|
634
|
+
return result;
|
|
635
|
+
}
|
|
636
|
+
//#endregion
|
|
637
|
+
//#region src/client-module-transform.ts
|
|
638
|
+
const SERVER_ONLY_EXPORTS = new Set([
|
|
639
|
+
"loader",
|
|
640
|
+
"head",
|
|
641
|
+
"headers",
|
|
642
|
+
"getStaticPaths"
|
|
643
|
+
]);
|
|
644
|
+
function stripServerOnlyExportsForClient(code, id = "pracht-client-route.tsx") {
|
|
645
|
+
const states = createStatementStates(parseAst(code, { lang: getRolldownLang(id) }));
|
|
646
|
+
const initialBindingNames = collectCurrentTopLevelBindingNames(states);
|
|
647
|
+
const { changed, candidates } = removeServerOnlyExports(states, initialBindingNames);
|
|
648
|
+
if (!changed) return code;
|
|
649
|
+
pruneDeadBindings(states, initialBindingNames, candidates);
|
|
650
|
+
return renderProgram(code, states);
|
|
651
|
+
}
|
|
652
|
+
function removeServerOnlyExports(states, initialBindingNames) {
|
|
653
|
+
let changed = false;
|
|
654
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
655
|
+
for (const state of states) {
|
|
656
|
+
const statement = state.node;
|
|
657
|
+
if (statement.type !== "ExportNamedDeclaration" || statement.exportKind === "type") continue;
|
|
658
|
+
const declaration = statement.declaration;
|
|
659
|
+
if (declaration?.type === "FunctionDeclaration") {
|
|
660
|
+
const name = declaration.id?.name;
|
|
661
|
+
if (!name || !SERVER_ONLY_EXPORTS.has(name)) continue;
|
|
662
|
+
changed = true;
|
|
663
|
+
state.removed = true;
|
|
664
|
+
enqueueDependencies(candidates, collectTopLevelReferences(declaration, initialBindingNames, new Set([name])));
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
if (declaration?.type === "VariableDeclaration") {
|
|
668
|
+
const removable = getRemainingDeclaratorIndices(state).filter((index) => collectBindingNamesFromPattern(declaration.declarations[index].id).some((name) => SERVER_ONLY_EXPORTS.has(name)));
|
|
669
|
+
if (removable.length === 0) continue;
|
|
670
|
+
changed = true;
|
|
671
|
+
for (const index of removable) {
|
|
672
|
+
const declarator = declaration.declarations[index];
|
|
673
|
+
const declaredNames = new Set(collectBindingNamesFromPattern(declarator.id));
|
|
674
|
+
enqueueDependencies(candidates, collectVariableDeclaratorDependencies(declarator, declaration.kind, initialBindingNames, declaredNames));
|
|
675
|
+
state.removedDeclarators.add(index);
|
|
676
|
+
}
|
|
677
|
+
if (getRemainingDeclaratorIndices(state).length === 0) state.removed = true;
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
const removableSpecifiers = getRemainingSpecifierIndices(state).filter((index) => {
|
|
681
|
+
const specifier = statement.specifiers[index];
|
|
682
|
+
if (specifier.type !== "ExportSpecifier" || specifier.exportKind === "type") return false;
|
|
683
|
+
const localName = getIdentifierName(specifier.local);
|
|
684
|
+
const exportedName = getIdentifierName(specifier.exported);
|
|
685
|
+
return SERVER_ONLY_EXPORTS.has(localName ?? "") || SERVER_ONLY_EXPORTS.has(exportedName ?? "");
|
|
686
|
+
});
|
|
687
|
+
if (removableSpecifiers.length === 0) continue;
|
|
688
|
+
changed = true;
|
|
689
|
+
for (const index of removableSpecifiers) {
|
|
690
|
+
const specifier = statement.specifiers[index];
|
|
691
|
+
if (!statement.source) {
|
|
692
|
+
const localName = getIdentifierName(specifier.local);
|
|
693
|
+
if (localName) candidates.add(localName);
|
|
694
|
+
}
|
|
695
|
+
state.removedSpecifiers.add(index);
|
|
696
|
+
}
|
|
697
|
+
if (getRemainingSpecifierIndices(state).length === 0) state.removed = true;
|
|
698
|
+
}
|
|
699
|
+
return {
|
|
700
|
+
changed,
|
|
701
|
+
candidates
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
function pruneDeadBindings(states, initialBindingNames, candidates) {
|
|
705
|
+
let changed = true;
|
|
706
|
+
while (changed) {
|
|
707
|
+
changed = false;
|
|
708
|
+
const bindings = collectTopLevelBindings(states, initialBindingNames);
|
|
709
|
+
const exportedNames = collectExportedBindingNames(states);
|
|
710
|
+
const referencedNames = collectProgramReferences(states);
|
|
711
|
+
const pendingNames = Array.from(candidates);
|
|
712
|
+
for (const name of pendingNames) {
|
|
713
|
+
const binding = bindings.get(name);
|
|
714
|
+
if (!binding) continue;
|
|
715
|
+
if (exportedNames.has(name) || referencedNames.has(name)) continue;
|
|
716
|
+
removeBinding(states, binding);
|
|
717
|
+
enqueueDependencies(candidates, binding.dependencies);
|
|
718
|
+
changed = true;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
function collectTopLevelBindings(states, dependencyBindingNames) {
|
|
723
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
724
|
+
for (const [statementIndex, state] of states.entries()) {
|
|
725
|
+
if (state.removed) continue;
|
|
726
|
+
const statement = state.node;
|
|
727
|
+
if (statement.type === "ImportDeclaration") {
|
|
728
|
+
if (statement.importKind === "type") continue;
|
|
729
|
+
for (const index of getRemainingSpecifierIndices(state)) {
|
|
730
|
+
const specifier = statement.specifiers[index];
|
|
731
|
+
if (specifier.type === "ImportSpecifier" && specifier.importKind === "type") continue;
|
|
732
|
+
const local = specifier.local;
|
|
733
|
+
const name = getIdentifierName(local);
|
|
734
|
+
if (!name) continue;
|
|
735
|
+
const info = {
|
|
736
|
+
dependencies: /* @__PURE__ */ new Set(),
|
|
737
|
+
kind: "import",
|
|
738
|
+
names: new Set([name]),
|
|
739
|
+
node: specifier,
|
|
740
|
+
specifierIndex: index,
|
|
741
|
+
statementIndex
|
|
742
|
+
};
|
|
743
|
+
bindings.set(name, info);
|
|
744
|
+
}
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
const declaration = getStatementDeclaration(statement);
|
|
748
|
+
if (!declaration) continue;
|
|
749
|
+
if (declaration.type === "FunctionDeclaration") {
|
|
750
|
+
const name = getIdentifierName(declaration.id);
|
|
751
|
+
if (!name) continue;
|
|
752
|
+
const info = {
|
|
753
|
+
dependencies: collectTopLevelReferences(declaration, dependencyBindingNames, new Set([name])),
|
|
754
|
+
kind: "function",
|
|
755
|
+
names: new Set([name]),
|
|
756
|
+
node: declaration,
|
|
757
|
+
statementIndex
|
|
758
|
+
};
|
|
759
|
+
bindings.set(name, info);
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (declaration.type === "ClassDeclaration") {
|
|
763
|
+
const name = getIdentifierName(declaration.id);
|
|
764
|
+
if (!name) continue;
|
|
765
|
+
const info = {
|
|
766
|
+
dependencies: collectTopLevelReferences(declaration, dependencyBindingNames, new Set([name])),
|
|
767
|
+
kind: "class",
|
|
768
|
+
names: new Set([name]),
|
|
769
|
+
node: declaration,
|
|
770
|
+
statementIndex
|
|
771
|
+
};
|
|
772
|
+
bindings.set(name, info);
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
if (declaration.type !== "VariableDeclaration") continue;
|
|
776
|
+
for (const index of getRemainingDeclaratorIndices(state)) {
|
|
777
|
+
const declarator = declaration.declarations[index];
|
|
778
|
+
const names = new Set(collectBindingNamesFromPattern(declarator.id));
|
|
779
|
+
if (names.size === 0) continue;
|
|
780
|
+
const info = {
|
|
781
|
+
declaratorIndex: index,
|
|
782
|
+
dependencies: collectVariableDeclaratorDependencies(declarator, declaration.kind, dependencyBindingNames, names),
|
|
783
|
+
kind: "variable",
|
|
784
|
+
names,
|
|
785
|
+
node: declarator,
|
|
786
|
+
statementIndex
|
|
787
|
+
};
|
|
788
|
+
for (const name of names) bindings.set(name, info);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return bindings;
|
|
792
|
+
}
|
|
793
|
+
function collectCurrentTopLevelBindingNames(states) {
|
|
794
|
+
const names = /* @__PURE__ */ new Set();
|
|
795
|
+
for (const state of states) {
|
|
796
|
+
if (state.removed) continue;
|
|
797
|
+
const statement = state.node;
|
|
798
|
+
if (statement.type === "ImportDeclaration") {
|
|
799
|
+
if (statement.importKind === "type") continue;
|
|
800
|
+
for (const index of getRemainingSpecifierIndices(state)) {
|
|
801
|
+
const specifier = statement.specifiers[index];
|
|
802
|
+
if (specifier.type === "ImportSpecifier" && specifier.importKind === "type") continue;
|
|
803
|
+
const localName = getIdentifierName(specifier.local);
|
|
804
|
+
if (localName) names.add(localName);
|
|
805
|
+
}
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
const declaration = getStatementDeclaration(statement);
|
|
809
|
+
if (!declaration) continue;
|
|
810
|
+
if (declaration.type === "VariableDeclaration") {
|
|
811
|
+
for (const index of getRemainingDeclaratorIndices(state)) {
|
|
812
|
+
const declarator = declaration.declarations[index];
|
|
813
|
+
for (const name of collectBindingNamesFromPattern(declarator.id)) names.add(name);
|
|
814
|
+
}
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
for (const name of collectBindingNamesFromDeclaration(declaration)) names.add(name);
|
|
818
|
+
}
|
|
819
|
+
return names;
|
|
820
|
+
}
|
|
821
|
+
function collectExportedBindingNames(states) {
|
|
822
|
+
const names = /* @__PURE__ */ new Set();
|
|
823
|
+
for (const state of states) {
|
|
824
|
+
if (state.removed) continue;
|
|
825
|
+
const statement = state.node;
|
|
826
|
+
if (statement.type === "ExportNamedDeclaration") {
|
|
827
|
+
const declaration = statement.declaration;
|
|
828
|
+
if (declaration) if (declaration.type === "VariableDeclaration") for (const index of getRemainingDeclaratorIndices(state)) {
|
|
829
|
+
const declarator = declaration.declarations[index];
|
|
830
|
+
for (const name of collectBindingNamesFromPattern(declarator.id)) names.add(name);
|
|
831
|
+
}
|
|
832
|
+
else for (const name of collectBindingNamesFromDeclaration(declaration)) names.add(name);
|
|
833
|
+
for (const index of getRemainingSpecifierIndices(state)) {
|
|
834
|
+
const specifier = statement.specifiers[index];
|
|
835
|
+
if (specifier.type !== "ExportSpecifier" || specifier.exportKind === "type") continue;
|
|
836
|
+
const localName = getIdentifierName(specifier.local);
|
|
837
|
+
if (localName) names.add(localName);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
if (statement.type !== "ExportDefaultDeclaration") continue;
|
|
841
|
+
const declaration = statement.declaration;
|
|
842
|
+
if (declaration.type === "Identifier") {
|
|
843
|
+
names.add(declaration.name);
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
if ((declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") && declaration.id) names.add(declaration.id.name);
|
|
847
|
+
}
|
|
848
|
+
return names;
|
|
849
|
+
}
|
|
850
|
+
function collectProgramReferences(states) {
|
|
851
|
+
return analyzeRetainedStatements(normalizeRetainedStatements(states)).referencedTopLevelNames;
|
|
852
|
+
}
|
|
853
|
+
function removeBinding(states, binding) {
|
|
854
|
+
const state = states[binding.statementIndex];
|
|
855
|
+
if (binding.kind === "import" && binding.specifierIndex !== void 0) {
|
|
856
|
+
state.removedSpecifiers.add(binding.specifierIndex);
|
|
857
|
+
if (getRemainingSpecifierIndices(state).length === 0) state.removed = true;
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
if (binding.kind === "variable" && binding.declaratorIndex !== void 0) {
|
|
861
|
+
state.removedDeclarators.add(binding.declaratorIndex);
|
|
862
|
+
if (getRemainingDeclaratorIndices(state).length === 0) state.removed = true;
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
state.removed = true;
|
|
866
|
+
}
|
|
867
|
+
function collectVariableDeclaratorDependencies(declarator, declarationKind, topLevelBindingNames, excludedNames) {
|
|
868
|
+
return collectTopLevelReferences({
|
|
869
|
+
declarations: [declarator],
|
|
870
|
+
end: declarator.end,
|
|
871
|
+
kind: declarationKind,
|
|
872
|
+
start: declarator.start,
|
|
873
|
+
type: "VariableDeclaration"
|
|
874
|
+
}, topLevelBindingNames, excludedNames);
|
|
875
|
+
}
|
|
876
|
+
function collectTopLevelReferences(node, topLevelBindingNames, excludedNames) {
|
|
877
|
+
return analyzeRetainedStatements([{ node }], {
|
|
878
|
+
excludedNames,
|
|
879
|
+
knownTopLevelNames: topLevelBindingNames
|
|
880
|
+
}).referencedTopLevelNames;
|
|
881
|
+
}
|
|
882
|
+
function enqueueDependencies(target, dependencies) {
|
|
883
|
+
for (const name of dependencies) target.add(name);
|
|
884
|
+
}
|
|
885
|
+
//#endregion
|
|
886
|
+
//#region src/plugin-assets.ts
|
|
6
887
|
const PRACHT_CLIENT_MODULE_ID = "virtual:pracht/client";
|
|
7
888
|
const PRACHT_SERVER_MODULE_ID = "virtual:pracht/server";
|
|
8
889
|
const CLIENT_BROWSER_PATH = "/@pracht/client.js";
|
|
890
|
+
function readClientBuildAssets(root = process.cwd()) {
|
|
891
|
+
const manifestPath = ["dist/client/.vite/manifest.json", "dist/.vite/manifest.json"].map((candidate) => resolve(root, candidate)).find((candidate) => existsSync(candidate));
|
|
892
|
+
if (!manifestPath) return {
|
|
893
|
+
clientEntryUrl: null,
|
|
894
|
+
cssManifest: {},
|
|
895
|
+
jsManifest: {}
|
|
896
|
+
};
|
|
897
|
+
const rawManifest = readFileSync(manifestPath, "utf-8");
|
|
898
|
+
const manifest = JSON.parse(rawManifest);
|
|
899
|
+
const clientEntry = manifest[PRACHT_CLIENT_MODULE_ID];
|
|
900
|
+
const cssManifest = {};
|
|
901
|
+
const jsManifest = {};
|
|
902
|
+
for (const [key, entry] of Object.entries(manifest)) {
|
|
903
|
+
if (!entry.src) continue;
|
|
904
|
+
const deps = collectTransitiveDeps(manifest, key);
|
|
905
|
+
const manifestKey = stripPrachtClientModuleQuery(entry.src);
|
|
906
|
+
if (deps.css.length > 0) cssManifest[manifestKey] = deps.css.map((f) => `/${f}`);
|
|
907
|
+
if (deps.js.length > 0) jsManifest[manifestKey] = deps.js.map((f) => `/${f}`);
|
|
908
|
+
}
|
|
909
|
+
return {
|
|
910
|
+
clientEntryUrl: clientEntry ? `/${clientEntry.file}` : null,
|
|
911
|
+
cssManifest,
|
|
912
|
+
jsManifest
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
function collectTransitiveDeps(manifest, key) {
|
|
916
|
+
const css = /* @__PURE__ */ new Set();
|
|
917
|
+
const js = /* @__PURE__ */ new Set();
|
|
918
|
+
const visited = /* @__PURE__ */ new Set();
|
|
919
|
+
function collect(k) {
|
|
920
|
+
if (visited.has(k)) return;
|
|
921
|
+
visited.add(k);
|
|
922
|
+
const entry = manifest[k];
|
|
923
|
+
if (!entry) return;
|
|
924
|
+
for (const c of entry.css ?? []) css.add(c);
|
|
925
|
+
js.add(entry.file);
|
|
926
|
+
for (const imp of entry.imports ?? []) collect(imp);
|
|
927
|
+
}
|
|
928
|
+
collect(key);
|
|
929
|
+
return {
|
|
930
|
+
css: [...css],
|
|
931
|
+
js: [...js]
|
|
932
|
+
};
|
|
933
|
+
}
|
|
9
934
|
function isClientModule(id) {
|
|
10
|
-
return id === "virtual:pracht/client" || id ===
|
|
935
|
+
return id === "virtual:pracht/client" || id === "/@pracht/client.js" || id.endsWith("virtual:pracht/client");
|
|
11
936
|
}
|
|
12
937
|
function isServerModule(id) {
|
|
13
938
|
return id === "virtual:pracht/server" || id.endsWith("virtual:pracht/server");
|
|
14
939
|
}
|
|
940
|
+
//#endregion
|
|
941
|
+
//#region src/plugin-adapter.ts
|
|
15
942
|
function createDefaultNodeAdapter() {
|
|
16
943
|
return {
|
|
17
944
|
id: "node",
|
|
@@ -55,6 +982,8 @@ function createDefaultNodeAdapter() {
|
|
|
55
982
|
}
|
|
56
983
|
};
|
|
57
984
|
}
|
|
985
|
+
//#endregion
|
|
986
|
+
//#region src/plugin-options.ts
|
|
58
987
|
const DEFAULTS = {
|
|
59
988
|
appFile: "/src/routes.ts",
|
|
60
989
|
middlewareDir: "/src/middleware",
|
|
@@ -66,98 +995,14 @@ const DEFAULTS = {
|
|
|
66
995
|
pagesDir: "",
|
|
67
996
|
pagesDefaultRender: "ssr"
|
|
68
997
|
};
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (isPagesMode && options.appFile) console.warn("[pracht] Both `pagesDir` and `appFile` are set. `pagesDir` takes precedence — `appFile` will be ignored.");
|
|
74
|
-
let isBuild = false;
|
|
75
|
-
const prachtPlugin = {
|
|
76
|
-
name: "pracht",
|
|
77
|
-
enforce: "pre",
|
|
78
|
-
config(_config, env) {
|
|
79
|
-
const isEdge = resolved.adapter.id === "vercel" || resolved.adapter.id === "cloudflare";
|
|
80
|
-
const isSSRBuild = env.isSsrBuild;
|
|
81
|
-
return {
|
|
82
|
-
appType: "custom",
|
|
83
|
-
build: { rollupOptions: { output: { manualChunks(id) {
|
|
84
|
-
if (id.includes("node_modules/preact") || id.includes("node_modules/preact-suspense")) return "vendor";
|
|
85
|
-
} } } },
|
|
86
|
-
...isEdge && isSSRBuild ? { ssr: { noExternal: true } } : {}
|
|
87
|
-
};
|
|
88
|
-
},
|
|
89
|
-
configResolved(config) {
|
|
90
|
-
root = config.root;
|
|
91
|
-
isBuild = config.command === "build";
|
|
92
|
-
},
|
|
93
|
-
resolveId(id) {
|
|
94
|
-
if (isClientModule(id)) return PRACHT_CLIENT_MODULE_ID;
|
|
95
|
-
if (isServerModule(id)) return PRACHT_SERVER_MODULE_ID;
|
|
96
|
-
return null;
|
|
97
|
-
},
|
|
98
|
-
load(id) {
|
|
99
|
-
if (isClientModule(id)) return createPrachtClientModuleSource(resolved, { root });
|
|
100
|
-
if (isServerModule(id)) return createPrachtServerModuleSource(resolved, {
|
|
101
|
-
root,
|
|
102
|
-
isBuild
|
|
103
|
-
});
|
|
104
|
-
return null;
|
|
105
|
-
},
|
|
106
|
-
transform(code, id) {
|
|
107
|
-
if (id !== resolve(root, resolved.appFile.slice(1))) return null;
|
|
108
|
-
const transformed = code.replace(/\(\)\s*=>\s*import\(\s*(['"])([^'"]+)\1\s*\)/g, "$1$2$1");
|
|
109
|
-
if (transformed === code) return null;
|
|
110
|
-
return {
|
|
111
|
-
code: transformed,
|
|
112
|
-
map: null
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
|
-
configureServer(server) {
|
|
116
|
-
if (isPagesMode) {
|
|
117
|
-
const abs = resolve(root, resolved.pagesDir.slice(1));
|
|
118
|
-
server.watcher.on("add", (f) => {
|
|
119
|
-
if (f.startsWith(abs)) server.restart();
|
|
120
|
-
});
|
|
121
|
-
server.watcher.on("unlink", (f) => {
|
|
122
|
-
if (f.startsWith(abs)) server.restart();
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
if (resolved.adapter.ownsDevServer) return;
|
|
126
|
-
return () => {
|
|
127
|
-
server.middlewares.use(createDevSSRMiddleware(server, resolved));
|
|
128
|
-
};
|
|
129
|
-
},
|
|
130
|
-
handleHotUpdate({ file, server }) {
|
|
131
|
-
const root = server.config.root;
|
|
132
|
-
const relative = file.startsWith(root) ? file.slice(root.length) : file;
|
|
133
|
-
if (isPagesMode && relative.startsWith(resolved.pagesDir)) {
|
|
134
|
-
const clientMod = server.moduleGraph.getModuleById(PRACHT_CLIENT_MODULE_ID);
|
|
135
|
-
const serverMod = server.moduleGraph.getModuleById(PRACHT_SERVER_MODULE_ID);
|
|
136
|
-
if (clientMod) server.moduleGraph.invalidateModule(clientMod);
|
|
137
|
-
if (serverMod) server.moduleGraph.invalidateModule(serverMod);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
if (!isPagesMode && relative === resolved.appFile) {
|
|
141
|
-
server.restart();
|
|
142
|
-
return [];
|
|
143
|
-
}
|
|
144
|
-
if ([
|
|
145
|
-
resolved.routesDir,
|
|
146
|
-
resolved.shellsDir,
|
|
147
|
-
resolved.middlewareDir,
|
|
148
|
-
resolved.apiDir,
|
|
149
|
-
resolved.serverDir
|
|
150
|
-
].some((dir) => relative.startsWith(dir))) {
|
|
151
|
-
const serverMod = server.moduleGraph.getModuleById(PRACHT_SERVER_MODULE_ID);
|
|
152
|
-
if (serverMod) server.moduleGraph.invalidateModule(serverMod);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
998
|
+
function resolveOptions(options) {
|
|
999
|
+
return {
|
|
1000
|
+
...DEFAULTS,
|
|
1001
|
+
...options
|
|
155
1002
|
};
|
|
156
|
-
const plugins = [...preact(), prachtPlugin];
|
|
157
|
-
const adapterPlugins = await resolved.adapter.vitePlugins?.();
|
|
158
|
-
if (adapterPlugins?.length) plugins.push(...adapterPlugins);
|
|
159
|
-
return plugins;
|
|
160
1003
|
}
|
|
1004
|
+
//#endregion
|
|
1005
|
+
//#region src/plugin-codegen.ts
|
|
161
1006
|
function createPrachtClientModuleSource(options = {}, buildOptions = {}) {
|
|
162
1007
|
const resolved = resolveOptions(options);
|
|
163
1008
|
const isPagesMode = !!resolved.pagesDir;
|
|
@@ -168,16 +1013,21 @@ function createPrachtClientModuleSource(options = {}, buildOptions = {}) {
|
|
|
168
1013
|
"import { resolveApp, initClientRouter, readHydrationState } from \"@pracht/core\";",
|
|
169
1014
|
appImport,
|
|
170
1015
|
"",
|
|
171
|
-
`const routeModules = import.meta.glob(${JSON.stringify(routeGlob)});`,
|
|
172
|
-
`const shellModules = import.meta.glob(${JSON.stringify(shellGlob)});`,
|
|
1016
|
+
`const routeModules = import.meta.glob(${JSON.stringify(routeGlob)}, { query: ${JSON.stringify(PRACHT_CLIENT_MODULE_QUERY)} });`,
|
|
1017
|
+
`const shellModules = import.meta.glob(${JSON.stringify(shellGlob)}, { query: ${JSON.stringify(PRACHT_CLIENT_MODULE_QUERY)} });`,
|
|
173
1018
|
"",
|
|
174
1019
|
"const resolvedApp = resolveApp(app);",
|
|
175
1020
|
"",
|
|
1021
|
+
"function normalizeModuleKey(key) {",
|
|
1022
|
+
" return key.split(\"?\")[0];",
|
|
1023
|
+
"}",
|
|
1024
|
+
"",
|
|
176
1025
|
"function findModuleKey(modules, file) {",
|
|
177
1026
|
" if (file in modules) return file;",
|
|
178
1027
|
" const suffix = file.replace(/^\\.\\//,\"\");",
|
|
179
1028
|
" for (const key of Object.keys(modules)) {",
|
|
180
|
-
"
|
|
1029
|
+
" const normalizedKey = normalizeModuleKey(key);",
|
|
1030
|
+
" if (normalizedKey.endsWith(\"/\" + suffix) || normalizedKey.endsWith(suffix)) return key;",
|
|
181
1031
|
" }",
|
|
182
1032
|
" return null;",
|
|
183
1033
|
"}",
|
|
@@ -216,7 +1066,7 @@ function createPrachtServerModuleSource(options = {}, buildOptions = {}) {
|
|
|
216
1066
|
"export const resolvedApp = resolveApp(app);",
|
|
217
1067
|
`export const apiRoutes = resolveApiRoutes(Object.keys(apiModules), ${JSON.stringify(resolved.apiDir)});`,
|
|
218
1068
|
`export const buildTarget = ${JSON.stringify(adapter?.id ?? "node")};`,
|
|
219
|
-
`export const clientEntryUrl = ${JSON.stringify(clientBuild.clientEntryUrl ??
|
|
1069
|
+
`export const clientEntryUrl = ${JSON.stringify(clientBuild.clientEntryUrl ?? "/@pracht/client.js")};`,
|
|
220
1070
|
`export const cssManifest = ${JSON.stringify(clientBuild.cssManifest)};`,
|
|
221
1071
|
`export const jsManifest = ${JSON.stringify(clientBuild.jsManifest)};`,
|
|
222
1072
|
"export { prerenderApp };",
|
|
@@ -254,7 +1104,11 @@ function generatePagesAppInlineSource(options, root = process.cwd()) {
|
|
|
254
1104
|
pagesDirPrefix: options.pagesDir
|
|
255
1105
|
});
|
|
256
1106
|
}
|
|
257
|
-
|
|
1107
|
+
//#endregion
|
|
1108
|
+
//#region src/plugin-dev-ssr.ts
|
|
1109
|
+
const BODYLESS_METHODS = new Set(["GET", "HEAD"]);
|
|
1110
|
+
const MAX_BODY_SIZE = 1024 * 1024;
|
|
1111
|
+
function createDevSSRMiddleware(server) {
|
|
258
1112
|
return async (req, res, next) => {
|
|
259
1113
|
const url = req.url ?? "/";
|
|
260
1114
|
const pathname = new URL(url, "http://localhost").pathname;
|
|
@@ -290,34 +1144,36 @@ function createDevSSRMiddleware(server, _pluginOptions) {
|
|
|
290
1144
|
});
|
|
291
1145
|
res.end(body);
|
|
292
1146
|
} catch (error) {
|
|
293
|
-
|
|
294
|
-
if (req.headers["x-pracht-route-state-request"] === "1") {
|
|
295
|
-
res.statusCode = 500;
|
|
296
|
-
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
297
|
-
res.end(JSON.stringify({ error: {
|
|
298
|
-
message: error instanceof Error ? error.message : String(error),
|
|
299
|
-
name: error instanceof Error ? error.name : "Error",
|
|
300
|
-
status: 500
|
|
301
|
-
} }));
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
try {
|
|
305
|
-
const { buildErrorOverlayHtml } = await server.ssrLoadModule("pracht/error-overlay");
|
|
306
|
-
let html = buildErrorOverlayHtml({
|
|
307
|
-
message: error instanceof Error ? error.message : String(error),
|
|
308
|
-
stack: error instanceof Error ? error.stack : void 0
|
|
309
|
-
});
|
|
310
|
-
html = await server.transformIndexHtml(url, html);
|
|
311
|
-
res.statusCode = 500;
|
|
312
|
-
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
313
|
-
res.end(html);
|
|
314
|
-
} catch {
|
|
315
|
-
next(error);
|
|
316
|
-
}
|
|
1147
|
+
await handleDevError(server, req, res, next, url, error);
|
|
317
1148
|
}
|
|
318
1149
|
};
|
|
319
1150
|
}
|
|
320
|
-
|
|
1151
|
+
async function handleDevError(server, req, res, next, url, error) {
|
|
1152
|
+
if (error instanceof Error) server.ssrFixStacktrace(error);
|
|
1153
|
+
if (req.headers["x-pracht-route-state-request"] === "1") {
|
|
1154
|
+
res.statusCode = 500;
|
|
1155
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
1156
|
+
res.end(JSON.stringify({ error: {
|
|
1157
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1158
|
+
name: error instanceof Error ? error.name : "Error",
|
|
1159
|
+
status: 500
|
|
1160
|
+
} }));
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
try {
|
|
1164
|
+
const { buildErrorOverlayHtml } = await server.ssrLoadModule("pracht/error-overlay");
|
|
1165
|
+
let html = buildErrorOverlayHtml({
|
|
1166
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1167
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
1168
|
+
});
|
|
1169
|
+
html = await server.transformIndexHtml(url, html);
|
|
1170
|
+
res.statusCode = 500;
|
|
1171
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
1172
|
+
res.end(html);
|
|
1173
|
+
} catch {
|
|
1174
|
+
next(error);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
321
1177
|
async function nodeToWebRequest(req) {
|
|
322
1178
|
const protocol = "http";
|
|
323
1179
|
const host = req.headers.host ?? "localhost";
|
|
@@ -334,7 +1190,6 @@ async function nodeToWebRequest(req) {
|
|
|
334
1190
|
headers
|
|
335
1191
|
};
|
|
336
1192
|
if (!BODYLESS_METHODS.has(method.toUpperCase())) {
|
|
337
|
-
const MAX_BODY_SIZE = 1024 * 1024;
|
|
338
1193
|
const chunks = [];
|
|
339
1194
|
let totalSize = 0;
|
|
340
1195
|
for await (const chunk of req) {
|
|
@@ -351,54 +1206,151 @@ async function nodeToWebRequest(req) {
|
|
|
351
1206
|
}
|
|
352
1207
|
return new Request(url, init);
|
|
353
1208
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
1209
|
+
//#endregion
|
|
1210
|
+
//#region src/index.ts
|
|
1211
|
+
async function pracht(options = {}) {
|
|
1212
|
+
const resolved = resolveOptions(options);
|
|
1213
|
+
const isPagesMode = !!resolved.pagesDir;
|
|
1214
|
+
let root = process.cwd();
|
|
1215
|
+
let routeFileDirs = [];
|
|
1216
|
+
if (isPagesMode && options.appFile) console.warn("[pracht] Both `pagesDir` and `appFile` are set. `pagesDir` takes precedence — `appFile` will be ignored.");
|
|
1217
|
+
let isBuild = false;
|
|
1218
|
+
const prachtPlugin = {
|
|
1219
|
+
name: "pracht",
|
|
1220
|
+
enforce: "pre",
|
|
1221
|
+
config(_config, env) {
|
|
1222
|
+
const isEdge = resolved.adapter.edge === true;
|
|
1223
|
+
const isSSRBuild = env.isSsrBuild;
|
|
1224
|
+
return {
|
|
1225
|
+
appType: "custom",
|
|
1226
|
+
build: { rollupOptions: { output: { manualChunks(id) {
|
|
1227
|
+
if (id.includes("node_modules/preact") || id.includes("node_modules/preact-suspense")) return "vendor";
|
|
1228
|
+
} } } },
|
|
1229
|
+
...isEdge && isSSRBuild ? { ssr: { noExternal: true } } : {}
|
|
1230
|
+
};
|
|
1231
|
+
},
|
|
1232
|
+
configResolved(config) {
|
|
1233
|
+
root = config.root;
|
|
1234
|
+
isBuild = config.command === "build";
|
|
1235
|
+
routeFileDirs = computeRouteFileDirs(root, resolved);
|
|
1236
|
+
},
|
|
1237
|
+
resolveId(id) {
|
|
1238
|
+
if (isClientModule(id)) return PRACHT_CLIENT_MODULE_ID;
|
|
1239
|
+
if (isServerModule(id)) return PRACHT_SERVER_MODULE_ID;
|
|
1240
|
+
return null;
|
|
1241
|
+
},
|
|
1242
|
+
load(id) {
|
|
1243
|
+
if (isClientModule(id)) return createPrachtClientModuleSource(resolved, { root });
|
|
1244
|
+
if (isServerModule(id)) return createPrachtServerModuleSource(resolved, {
|
|
1245
|
+
root,
|
|
1246
|
+
isBuild
|
|
1247
|
+
});
|
|
1248
|
+
return null;
|
|
1249
|
+
},
|
|
1250
|
+
transform(code, id) {
|
|
1251
|
+
if (id !== resolve(root, resolved.appFile.slice(1))) return null;
|
|
1252
|
+
const transformed = code.replace(/\(\)\s*=>\s*import\(\s*(['"])([^'"]+)\1\s*\)/g, "$1$2$1");
|
|
1253
|
+
if (transformed === code) return null;
|
|
1254
|
+
return {
|
|
1255
|
+
code: transformed,
|
|
1256
|
+
map: null
|
|
1257
|
+
};
|
|
1258
|
+
},
|
|
1259
|
+
configureServer(server) {
|
|
1260
|
+
if (isPagesMode) watchPagesDirectory(server, resolved, root);
|
|
1261
|
+
if (resolved.adapter.ownsDevServer) return;
|
|
1262
|
+
return () => {
|
|
1263
|
+
server.middlewares.use(createDevSSRMiddleware(server));
|
|
1264
|
+
};
|
|
1265
|
+
},
|
|
1266
|
+
handleHotUpdate({ file, server }) {
|
|
1267
|
+
const serverRoot = server.config.root;
|
|
1268
|
+
const relative = file.startsWith(serverRoot) ? file.slice(serverRoot.length) : file;
|
|
1269
|
+
if (isPagesMode && relative.startsWith(resolved.pagesDir)) {
|
|
1270
|
+
invalidateVirtualModules(server);
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
if (!isPagesMode && relative === resolved.appFile) {
|
|
1274
|
+
server.restart();
|
|
1275
|
+
return [];
|
|
1276
|
+
}
|
|
1277
|
+
if ([
|
|
1278
|
+
resolved.routesDir,
|
|
1279
|
+
resolved.shellsDir,
|
|
1280
|
+
resolved.middlewareDir,
|
|
1281
|
+
resolved.apiDir,
|
|
1282
|
+
resolved.serverDir
|
|
1283
|
+
].some((dir) => relative.startsWith(dir))) {
|
|
1284
|
+
const serverMod = server.moduleGraph.getModuleById(PRACHT_SERVER_MODULE_ID);
|
|
1285
|
+
if (serverMod) server.moduleGraph.invalidateModule(serverMod);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
366
1288
|
};
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
for (const c of entry.css ?? []) css.add(c);
|
|
380
|
-
js.add(entry.file);
|
|
381
|
-
for (const imp of entry.imports ?? []) collect(imp);
|
|
382
|
-
}
|
|
383
|
-
collect(key);
|
|
384
|
-
return {
|
|
385
|
-
css: [...css],
|
|
386
|
-
js: [...js]
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
const cssManifest = {};
|
|
390
|
-
const jsManifest = {};
|
|
391
|
-
for (const [key, entry] of Object.entries(manifest)) {
|
|
392
|
-
if (!entry.src) continue;
|
|
393
|
-
const deps = collectTransitiveDeps(key);
|
|
394
|
-
if (deps.css.length > 0) cssManifest[key] = deps.css.map((f) => `/${f}`);
|
|
395
|
-
if (deps.js.length > 0) jsManifest[key] = deps.js.map((f) => `/${f}`);
|
|
396
|
-
}
|
|
397
|
-
return {
|
|
398
|
-
clientEntryUrl: clientEntry ? `/${clientEntry.file}` : null,
|
|
399
|
-
cssManifest,
|
|
400
|
-
jsManifest
|
|
1289
|
+
const clientModuleTransformPlugin = {
|
|
1290
|
+
name: "pracht:client-module-transform",
|
|
1291
|
+
enforce: "post",
|
|
1292
|
+
transform(code, id, transformOptions) {
|
|
1293
|
+
if (!(isPrachtClientModuleId(id) || !transformOptions?.ssr && isRouteOrShellFile(id, routeFileDirs))) return null;
|
|
1294
|
+
const transformed = stripServerOnlyExportsForClient(code, id);
|
|
1295
|
+
if (transformed === code) return null;
|
|
1296
|
+
return {
|
|
1297
|
+
code: transformed,
|
|
1298
|
+
map: null
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
401
1301
|
};
|
|
1302
|
+
const plugins = [
|
|
1303
|
+
...preact(),
|
|
1304
|
+
prachtPlugin,
|
|
1305
|
+
clientModuleTransformPlugin
|
|
1306
|
+
];
|
|
1307
|
+
const adapterPlugins = await resolved.adapter.vitePlugins?.();
|
|
1308
|
+
if (adapterPlugins?.length) plugins.push(...adapterPlugins);
|
|
1309
|
+
return plugins;
|
|
1310
|
+
}
|
|
1311
|
+
function watchPagesDirectory(server, resolved, root) {
|
|
1312
|
+
const abs = resolve(root, resolved.pagesDir.slice(1));
|
|
1313
|
+
server.watcher.on("add", (f) => {
|
|
1314
|
+
if (f.startsWith(abs)) server.restart();
|
|
1315
|
+
});
|
|
1316
|
+
server.watcher.on("unlink", (f) => {
|
|
1317
|
+
if (f.startsWith(abs)) server.restart();
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
function invalidateVirtualModules(server) {
|
|
1321
|
+
const clientMod = server.moduleGraph.getModuleById(PRACHT_CLIENT_MODULE_ID);
|
|
1322
|
+
const serverMod = server.moduleGraph.getModuleById(PRACHT_SERVER_MODULE_ID);
|
|
1323
|
+
if (clientMod) server.moduleGraph.invalidateModule(clientMod);
|
|
1324
|
+
if (serverMod) server.moduleGraph.invalidateModule(serverMod);
|
|
1325
|
+
}
|
|
1326
|
+
const ROUTE_FILE_EXTENSIONS = new Set([
|
|
1327
|
+
".ts",
|
|
1328
|
+
".tsx",
|
|
1329
|
+
".js",
|
|
1330
|
+
".jsx",
|
|
1331
|
+
".md",
|
|
1332
|
+
".mdx"
|
|
1333
|
+
]);
|
|
1334
|
+
function computeRouteFileDirs(root, resolved) {
|
|
1335
|
+
return (resolved.pagesDir ? [resolved.pagesDir] : [resolved.routesDir, resolved.shellsDir]).map((dir) => toPosixPath(resolve(root, dir.replace(/^\//, "")))).map(withTrailingSep);
|
|
1336
|
+
}
|
|
1337
|
+
function isRouteOrShellFile(id, dirs) {
|
|
1338
|
+
if (dirs.length === 0) return false;
|
|
1339
|
+
const queryStart = id.indexOf("?");
|
|
1340
|
+
const path = queryStart === -1 ? id : id.slice(0, queryStart);
|
|
1341
|
+
if (path.startsWith("\0") || path.startsWith("virtual:")) return false;
|
|
1342
|
+
const extIndex = path.lastIndexOf(".");
|
|
1343
|
+
if (extIndex === -1) return false;
|
|
1344
|
+
const ext = path.slice(extIndex);
|
|
1345
|
+
if (!ROUTE_FILE_EXTENSIONS.has(ext)) return false;
|
|
1346
|
+
const normalized = toPosixPath(path);
|
|
1347
|
+
return dirs.some((dir) => normalized.startsWith(dir));
|
|
1348
|
+
}
|
|
1349
|
+
function toPosixPath(p) {
|
|
1350
|
+
return p.replace(/\\/g, "/");
|
|
1351
|
+
}
|
|
1352
|
+
function withTrailingSep(p) {
|
|
1353
|
+
return p.endsWith("/") ? p : `${p}/`;
|
|
402
1354
|
}
|
|
403
1355
|
//#endregion
|
|
404
1356
|
export { PRACHT_CLIENT_MODULE_ID, PRACHT_SERVER_MODULE_ID, createPrachtClientModuleSource, createPrachtRegistryModuleSource, createPrachtServerModuleSource, pracht };
|