@reckona/mreact-router 0.0.1
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/LICENSE +21 -0
- package/README.md +101 -0
- package/dist/actions.d.ts +43 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +577 -0
- package/dist/actions.js.map +1 -0
- package/dist/adapters/aws-lambda.d.ts +45 -0
- package/dist/adapters/aws-lambda.d.ts.map +1 -0
- package/dist/adapters/aws-lambda.js +168 -0
- package/dist/adapters/aws-lambda.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +94 -0
- package/dist/adapters/cloudflare.d.ts.map +1 -0
- package/dist/adapters/cloudflare.js +390 -0
- package/dist/adapters/cloudflare.js.map +1 -0
- package/dist/adapters/devtools.d.ts +4 -0
- package/dist/adapters/devtools.d.ts.map +1 -0
- package/dist/adapters/devtools.js +5 -0
- package/dist/adapters/devtools.js.map +1 -0
- package/dist/adapters/edge.d.ts +9 -0
- package/dist/adapters/edge.d.ts.map +1 -0
- package/dist/adapters/edge.js +53 -0
- package/dist/adapters/edge.js.map +1 -0
- package/dist/adapters/node.d.ts +26 -0
- package/dist/adapters/node.d.ts.map +1 -0
- package/dist/adapters/node.js +64 -0
- package/dist/adapters/node.js.map +1 -0
- package/dist/adapters/static.d.ts +10 -0
- package/dist/adapters/static.d.ts.map +1 -0
- package/dist/adapters/static.js +34 -0
- package/dist/adapters/static.js.map +1 -0
- package/dist/assets.d.ts +18 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +67 -0
- package/dist/assets.js.map +1 -0
- package/dist/build.d.ts +36 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +322 -0
- package/dist/build.js.map +1 -0
- package/dist/cache.d.ts +54 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +221 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +37 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +105 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +1268 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -0
- package/dist/cookies.d.ts +14 -0
- package/dist/cookies.d.ts.map +1 -0
- package/dist/cookies.js +69 -0
- package/dist/cookies.js.map +1 -0
- package/dist/csp.d.ts +6 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +70 -0
- package/dist/csp.js.map +1 -0
- package/dist/dev-server.d.ts +16 -0
- package/dist/dev-server.d.ts.map +1 -0
- package/dist/dev-server.js +103 -0
- package/dist/dev-server.js.map +1 -0
- package/dist/http.d.ts +23 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +106 -0
- package/dist/http.js.map +1 -0
- package/dist/i18n.d.ts +15 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +61 -0
- package/dist/i18n.js.map +1 -0
- package/dist/import-policy.d.ts +30 -0
- package/dist/import-policy.d.ts.map +1 -0
- package/dist/import-policy.js +105 -0
- package/dist/import-policy.js.map +1 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +47 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +60 -0
- package/dist/logger.js.map +1 -0
- package/dist/module-runner.d.ts +9 -0
- package/dist/module-runner.d.ts.map +1 -0
- package/dist/module-runner.js +112 -0
- package/dist/module-runner.js.map +1 -0
- package/dist/native-escape.d.ts +2 -0
- package/dist/native-escape.d.ts.map +1 -0
- package/dist/native-escape.js +43 -0
- package/dist/native-escape.js.map +1 -0
- package/dist/native-route-matcher.d.ts +5 -0
- package/dist/native-route-matcher.d.ts.map +1 -0
- package/dist/native-route-matcher.js +91 -0
- package/dist/native-route-matcher.js.map +1 -0
- package/dist/navigation.d.ts +25 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +125 -0
- package/dist/navigation.js.map +1 -0
- package/dist/prerender-store.d.ts +37 -0
- package/dist/prerender-store.d.ts.map +1 -0
- package/dist/prerender-store.js +158 -0
- package/dist/prerender-store.js.map +1 -0
- package/dist/render.d.ts +26 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +1688 -0
- package/dist/render.js.map +1 -0
- package/dist/route-path.d.ts +2 -0
- package/dist/route-path.d.ts.map +1 -0
- package/dist/route-path.js +5 -0
- package/dist/route-path.js.map +1 -0
- package/dist/route-source.d.ts +9 -0
- package/dist/route-source.d.ts.map +1 -0
- package/dist/route-source.js +44 -0
- package/dist/route-source.js.map +1 -0
- package/dist/routes.d.ts +38 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +168 -0
- package/dist/routes.js.map +1 -0
- package/dist/serve.d.ts +63 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/serve.js +445 -0
- package/dist/serve.js.map +1 -0
- package/dist/session.d.ts +25 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +104 -0
- package/dist/session.js.map +1 -0
- package/dist/vite-config.d.ts +8 -0
- package/dist/vite-config.d.ts.map +1 -0
- package/dist/vite-config.js +17 -0
- package/dist/vite-config.js.map +1 -0
- package/dist/vite.d.ts +25 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +150 -0
- package/dist/vite.js.map +1 -0
- package/package.json +91 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,1268 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile, stat } from "node:fs/promises";
|
|
3
|
+
import { dirname, extname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { collectStaticModuleSpecifiers, transform, } from "@reckona/mreact-compiler";
|
|
6
|
+
import { build } from "esbuild";
|
|
7
|
+
import { assetPath } from "./assets.js";
|
|
8
|
+
import { stripRouteClientOnlyExports } from "./route-source.js";
|
|
9
|
+
import { escapeHtmlQuotedAttribute as escapeHtmlAttribute } from "@reckona/mreact-shared/html-escape";
|
|
10
|
+
export async function routeToClientManifestEntry(route) {
|
|
11
|
+
if (route.kind === "server") {
|
|
12
|
+
return { path: route.path, kind: route.kind, client: false };
|
|
13
|
+
}
|
|
14
|
+
const code = await readFile(route.file, "utf8");
|
|
15
|
+
const inference = await inferClientRouteModule({
|
|
16
|
+
code: stripRouteClientOnlyExports(code),
|
|
17
|
+
filename: route.file,
|
|
18
|
+
routePath: route.path,
|
|
19
|
+
});
|
|
20
|
+
return inference.client
|
|
21
|
+
? {
|
|
22
|
+
path: route.path,
|
|
23
|
+
kind: route.kind,
|
|
24
|
+
client: true,
|
|
25
|
+
routeId: routeIdForPath(route.path),
|
|
26
|
+
script: clientScriptForPath(route.path),
|
|
27
|
+
}
|
|
28
|
+
: { path: route.path, kind: route.kind, client: false };
|
|
29
|
+
}
|
|
30
|
+
export function createClientRouteInferenceCache() {
|
|
31
|
+
return {
|
|
32
|
+
importsByFile: new Map(),
|
|
33
|
+
resolvedByImport: new Map(),
|
|
34
|
+
sourceByFile: new Map(),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function isClientRouteModule(options) {
|
|
38
|
+
return (await inferClientRouteModule(options)).client;
|
|
39
|
+
}
|
|
40
|
+
export async function inferClientRouteModule(options) {
|
|
41
|
+
const cache = options.cache ?? createClientRouteInferenceCache();
|
|
42
|
+
try {
|
|
43
|
+
return await inferClientRouteModuleSource({
|
|
44
|
+
cache,
|
|
45
|
+
code: options.code,
|
|
46
|
+
filename: options.filename,
|
|
47
|
+
seen: new Set(),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new Error(`Failed to infer client route for ${options.routePath ?? "<unknown>"} (${options.filename}).\n${errorMessage(error)}`, { cause: error });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function isClientRouteSource(code) {
|
|
55
|
+
return /\bon[A-Z][A-Za-z0-9_]*=|\bcell\s*\(|\bwindow\b|\bdocument\b|\blocalStorage\b/.test(stripCommentsAndStringLiterals(code));
|
|
56
|
+
}
|
|
57
|
+
async function inferClientRouteModuleSource(options) {
|
|
58
|
+
if (isClientRouteSource(options.code)) {
|
|
59
|
+
return { client: true, clientBoundaryImports: [] };
|
|
60
|
+
}
|
|
61
|
+
if (options.seen.has(options.filename)) {
|
|
62
|
+
return { client: false, clientBoundaryImports: [] };
|
|
63
|
+
}
|
|
64
|
+
options.seen.add(options.filename);
|
|
65
|
+
const clientBoundaryImports = [];
|
|
66
|
+
for (const specifier of await staticImportSpecifiersForSource(options)) {
|
|
67
|
+
const resolved = await resolveAppLocalModule({
|
|
68
|
+
cache: options.cache,
|
|
69
|
+
importer: options.filename,
|
|
70
|
+
specifier,
|
|
71
|
+
});
|
|
72
|
+
if (resolved === undefined) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const source = await readCachedFile(options.cache, resolved);
|
|
76
|
+
const imported = await inferClientRouteModuleSource({
|
|
77
|
+
cache: options.cache,
|
|
78
|
+
code: source,
|
|
79
|
+
filename: resolved,
|
|
80
|
+
seen: options.seen,
|
|
81
|
+
});
|
|
82
|
+
if (imported.client) {
|
|
83
|
+
clientBoundaryImports.push(specifier);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
client: clientBoundaryImports.length > 0,
|
|
88
|
+
clientBoundaryImports,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async function staticImportSpecifiersForSource(options) {
|
|
92
|
+
const cached = options.cache.importsByFile.get(options.filename);
|
|
93
|
+
if (cached !== undefined) {
|
|
94
|
+
return cached;
|
|
95
|
+
}
|
|
96
|
+
const imports = Promise.resolve().then(() => collectStaticModuleSpecifiers({
|
|
97
|
+
code: options.code,
|
|
98
|
+
filename: options.filename,
|
|
99
|
+
}));
|
|
100
|
+
options.cache.importsByFile.set(options.filename, imports);
|
|
101
|
+
return imports;
|
|
102
|
+
}
|
|
103
|
+
async function resolveAppLocalModule(options) {
|
|
104
|
+
if (!options.specifier.startsWith(".")) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
const cacheKey = `${options.importer}\0${options.specifier}`;
|
|
108
|
+
const cached = options.cache.resolvedByImport.get(cacheKey);
|
|
109
|
+
if (cached !== undefined) {
|
|
110
|
+
return cached;
|
|
111
|
+
}
|
|
112
|
+
const resolved = resolveAppLocalModuleUncached(options.importer, options.specifier);
|
|
113
|
+
options.cache.resolvedByImport.set(cacheKey, resolved);
|
|
114
|
+
return resolved;
|
|
115
|
+
}
|
|
116
|
+
async function resolveAppLocalModuleUncached(importer, specifier) {
|
|
117
|
+
const base = join(dirname(importer), specifier);
|
|
118
|
+
const candidates = sourceModuleCandidates(base);
|
|
119
|
+
if (candidates.length === 0) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
for (const candidate of candidates) {
|
|
123
|
+
if (await isFile(candidate)) {
|
|
124
|
+
return candidate;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
throw new Error(`${importer}: could not resolve app-local import ${JSON.stringify(specifier)}.`);
|
|
128
|
+
}
|
|
129
|
+
function sourceModuleCandidates(base) {
|
|
130
|
+
if (hasSourceModuleExtension(base)) {
|
|
131
|
+
return [base, ...typescriptSourceModuleCandidates(base)];
|
|
132
|
+
}
|
|
133
|
+
if (extname(base) !== "") {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
return [
|
|
137
|
+
`${base}.ts`,
|
|
138
|
+
`${base}.tsx`,
|
|
139
|
+
`${base}.mreact.tsx`,
|
|
140
|
+
`${base}.js`,
|
|
141
|
+
`${base}.jsx`,
|
|
142
|
+
`${base}.mjs`,
|
|
143
|
+
`${base}.mts`,
|
|
144
|
+
`${base}.cjs`,
|
|
145
|
+
`${base}.cts`,
|
|
146
|
+
join(base, "index.ts"),
|
|
147
|
+
join(base, "index.tsx"),
|
|
148
|
+
join(base, "index.mreact.tsx"),
|
|
149
|
+
join(base, "index.js"),
|
|
150
|
+
join(base, "index.jsx"),
|
|
151
|
+
join(base, "index.mjs"),
|
|
152
|
+
join(base, "index.mts"),
|
|
153
|
+
join(base, "index.cjs"),
|
|
154
|
+
join(base, "index.cts"),
|
|
155
|
+
];
|
|
156
|
+
}
|
|
157
|
+
function hasSourceModuleExtension(path) {
|
|
158
|
+
return /\.(?:mreact\.tsx|tsx?|jsx?|mjs|mts|cjs|cts)$/.test(path);
|
|
159
|
+
}
|
|
160
|
+
function typescriptSourceModuleCandidates(path) {
|
|
161
|
+
if (path.endsWith(".js")) {
|
|
162
|
+
return [`${path.slice(0, -3)}.ts`, `${path.slice(0, -3)}.tsx`];
|
|
163
|
+
}
|
|
164
|
+
if (path.endsWith(".jsx")) {
|
|
165
|
+
return [`${path.slice(0, -4)}.tsx`];
|
|
166
|
+
}
|
|
167
|
+
if (path.endsWith(".mjs")) {
|
|
168
|
+
return [`${path.slice(0, -4)}.mts`];
|
|
169
|
+
}
|
|
170
|
+
if (path.endsWith(".cjs")) {
|
|
171
|
+
return [`${path.slice(0, -4)}.cts`];
|
|
172
|
+
}
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
async function isFile(path) {
|
|
176
|
+
try {
|
|
177
|
+
return (await stat(path)).isFile();
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function readCachedFile(cache, filename) {
|
|
184
|
+
const cached = cache.sourceByFile.get(filename);
|
|
185
|
+
if (cached !== undefined) {
|
|
186
|
+
return cached;
|
|
187
|
+
}
|
|
188
|
+
const source = readFile(filename, "utf8");
|
|
189
|
+
cache.sourceByFile.set(filename, source);
|
|
190
|
+
return source;
|
|
191
|
+
}
|
|
192
|
+
function stripCommentsAndStringLiterals(code) {
|
|
193
|
+
let output = "";
|
|
194
|
+
let index = 0;
|
|
195
|
+
while (index < code.length) {
|
|
196
|
+
index = appendCodeOrMaskedLiteral(code, index, (value) => {
|
|
197
|
+
output += value;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return output;
|
|
201
|
+
}
|
|
202
|
+
function appendCodeOrMaskedLiteral(code, index, append) {
|
|
203
|
+
const char = code[index];
|
|
204
|
+
const next = code[index + 1];
|
|
205
|
+
if ((char === "'" || char === '"') && char !== undefined) {
|
|
206
|
+
return appendMaskedQuotedString(code, index, char, append);
|
|
207
|
+
}
|
|
208
|
+
if (char === "`") {
|
|
209
|
+
return appendMaskedTemplateLiteral(code, index, append);
|
|
210
|
+
}
|
|
211
|
+
if (char === "/" && next === "/") {
|
|
212
|
+
return appendMaskedLineComment(code, index, append);
|
|
213
|
+
}
|
|
214
|
+
if (char === "/" && next === "*") {
|
|
215
|
+
return appendMaskedBlockComment(code, index, append);
|
|
216
|
+
}
|
|
217
|
+
append(char ?? "");
|
|
218
|
+
return index + 1;
|
|
219
|
+
}
|
|
220
|
+
function appendMaskedQuotedString(code, start, quote, append) {
|
|
221
|
+
append(maskedChar(code[start] ?? ""));
|
|
222
|
+
let index = start + 1;
|
|
223
|
+
while (index < code.length) {
|
|
224
|
+
const char = code[index] ?? "";
|
|
225
|
+
append(maskedChar(char));
|
|
226
|
+
index += 1;
|
|
227
|
+
if (char === "\\") {
|
|
228
|
+
append(maskedChar(code[index] ?? ""));
|
|
229
|
+
index += 1;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (char === quote) {
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return index;
|
|
237
|
+
}
|
|
238
|
+
function appendMaskedTemplateLiteral(code, start, append) {
|
|
239
|
+
append(maskedChar(code[start] ?? ""));
|
|
240
|
+
let index = start + 1;
|
|
241
|
+
while (index < code.length) {
|
|
242
|
+
const char = code[index] ?? "";
|
|
243
|
+
const next = code[index + 1];
|
|
244
|
+
if (char === "\\") {
|
|
245
|
+
append(maskedChar(char));
|
|
246
|
+
append(maskedChar(next ?? ""));
|
|
247
|
+
index += 2;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (char === "`") {
|
|
251
|
+
append(maskedChar(char));
|
|
252
|
+
return index + 1;
|
|
253
|
+
}
|
|
254
|
+
if (char === "$" && next === "{") {
|
|
255
|
+
append(maskedChar(char));
|
|
256
|
+
append(maskedChar(next));
|
|
257
|
+
index = appendTemplateExpression(code, index + 2, append);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
append(maskedChar(char));
|
|
261
|
+
index += 1;
|
|
262
|
+
}
|
|
263
|
+
return index;
|
|
264
|
+
}
|
|
265
|
+
function appendTemplateExpression(code, start, append) {
|
|
266
|
+
let index = start;
|
|
267
|
+
let depth = 1;
|
|
268
|
+
while (index < code.length) {
|
|
269
|
+
const char = code[index];
|
|
270
|
+
if (char === "{") {
|
|
271
|
+
depth += 1;
|
|
272
|
+
append(char);
|
|
273
|
+
index += 1;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (char === "}") {
|
|
277
|
+
depth -= 1;
|
|
278
|
+
append(depth === 0 ? " " : char);
|
|
279
|
+
index += 1;
|
|
280
|
+
if (depth === 0) {
|
|
281
|
+
return index;
|
|
282
|
+
}
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
index = appendCodeOrMaskedLiteral(code, index, append);
|
|
286
|
+
}
|
|
287
|
+
return index;
|
|
288
|
+
}
|
|
289
|
+
function appendMaskedLineComment(code, start, append) {
|
|
290
|
+
let index = start;
|
|
291
|
+
while (index < code.length) {
|
|
292
|
+
const char = code[index] ?? "";
|
|
293
|
+
append(maskedChar(char));
|
|
294
|
+
index += 1;
|
|
295
|
+
if (char === "\n") {
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return index;
|
|
300
|
+
}
|
|
301
|
+
function appendMaskedBlockComment(code, start, append) {
|
|
302
|
+
append(maskedChar(code[start] ?? ""));
|
|
303
|
+
append(maskedChar(code[start + 1] ?? ""));
|
|
304
|
+
let index = start + 2;
|
|
305
|
+
while (index < code.length) {
|
|
306
|
+
const char = code[index] ?? "";
|
|
307
|
+
const next = code[index + 1];
|
|
308
|
+
append(maskedChar(char));
|
|
309
|
+
index += 1;
|
|
310
|
+
if (char === "*" && next === "/") {
|
|
311
|
+
append(maskedChar(next));
|
|
312
|
+
index += 1;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return index;
|
|
317
|
+
}
|
|
318
|
+
function maskedChar(char) {
|
|
319
|
+
return char === "\n" || char === "\r" ? char : " ";
|
|
320
|
+
}
|
|
321
|
+
export function routeIdForPath(path) {
|
|
322
|
+
if (path === "/") {
|
|
323
|
+
return "index";
|
|
324
|
+
}
|
|
325
|
+
return path
|
|
326
|
+
.slice(1)
|
|
327
|
+
.replaceAll("/", "_")
|
|
328
|
+
.replaceAll(":", "_")
|
|
329
|
+
.replace(/[^A-Za-z0-9_$-]/g, "_");
|
|
330
|
+
}
|
|
331
|
+
export function clientScriptForPath(path) {
|
|
332
|
+
return `routes/${routeIdForPath(path)}.js`;
|
|
333
|
+
}
|
|
334
|
+
export function withHydrationMarkers(options) {
|
|
335
|
+
const marker = hydrationMarkerParts({
|
|
336
|
+
assetBaseUrl: options.assetBaseUrl,
|
|
337
|
+
clientReferenceManifest: options.clientReferenceManifest,
|
|
338
|
+
props: options.props,
|
|
339
|
+
routePath: options.routePath,
|
|
340
|
+
script: options.script,
|
|
341
|
+
});
|
|
342
|
+
return `${marker.prefix}${options.html}${marker.suffix}`;
|
|
343
|
+
}
|
|
344
|
+
export function withRouteMarkers(options) {
|
|
345
|
+
const routeId = routeIdForPath(options.routePath);
|
|
346
|
+
return `<div data-mreact-route-id="${escapeHtmlAttribute(routeId)}">${options.html}</div>`;
|
|
347
|
+
}
|
|
348
|
+
export function hydrationMarkerParts(options) {
|
|
349
|
+
const routeId = routeIdForPath(options.routePath);
|
|
350
|
+
const escapedRouteId = escapeHtmlAttribute(routeId);
|
|
351
|
+
const propsJson = escapeScriptJson(JSON.stringify(options.props));
|
|
352
|
+
const script = options.script ?? clientScriptForPath(options.routePath);
|
|
353
|
+
const scriptSrc = assetPath(script, options.assetBaseUrl ?? "/_mreact/client/");
|
|
354
|
+
const clientReferencesJson = options.clientReferenceManifest === undefined || options.clientReferenceManifest.length === 0
|
|
355
|
+
? undefined
|
|
356
|
+
: escapeScriptJson(JSON.stringify(options.clientReferenceManifest));
|
|
357
|
+
return {
|
|
358
|
+
prefix: `<div data-mreact-route-id="${escapedRouteId}">`,
|
|
359
|
+
suffix: [
|
|
360
|
+
"</div>",
|
|
361
|
+
`<script type="application/json" id="mreact-props-${escapedRouteId}">${propsJson}</script>`,
|
|
362
|
+
clientReferencesJson === undefined
|
|
363
|
+
? undefined
|
|
364
|
+
: `<script type="application/json" id="mreact-client-references-${escapedRouteId}">${clientReferencesJson}</script>`,
|
|
365
|
+
`<script type="module" src="${escapeHtmlAttribute(scriptSrc)}"></script>`,
|
|
366
|
+
].filter((part) => part !== undefined).join(""),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
export async function buildClientRouteBundle(options) {
|
|
370
|
+
return (await buildClientRouteOutput(options)).code;
|
|
371
|
+
}
|
|
372
|
+
export async function buildClientRouteOutput(options) {
|
|
373
|
+
const compiled = transform({
|
|
374
|
+
code: options.code,
|
|
375
|
+
filename: options.filename,
|
|
376
|
+
target: "client",
|
|
377
|
+
dev: options.minify !== true,
|
|
378
|
+
});
|
|
379
|
+
if (compiled.diagnostics.length > 0) {
|
|
380
|
+
throw new Error(`${options.filename}: ${compiled.diagnostics
|
|
381
|
+
.map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`)
|
|
382
|
+
.join("\n")}`);
|
|
383
|
+
}
|
|
384
|
+
const clientNavigation = options.clientNavigation ?? detectClientNavigationHint(options.code);
|
|
385
|
+
const clientReferenceManifest = options.clientReferenceManifest ?? await inferClientReferenceManifestForBundle(options);
|
|
386
|
+
const clientReferenceRegistry = emitClientReferenceRegistry(clientReferenceManifest);
|
|
387
|
+
const routeId = routeIdForPath(options.routePath);
|
|
388
|
+
const routeUsesCells = detectRouteCellStateHint(compiled.code);
|
|
389
|
+
const routeStateSignature = routeUsesCells ? routeStateSignatureForSource(compiled.code) : "";
|
|
390
|
+
const navigationStateDeclaration = clientNavigation
|
|
391
|
+
? `const __mreactNavigationState = __mreactGlobal.__mreactNavigationState ??= {
|
|
392
|
+
cache: new Map(),
|
|
393
|
+
installed: false,
|
|
394
|
+
};`
|
|
395
|
+
: "";
|
|
396
|
+
const routeCellStateDeclaration = routeUsesCells
|
|
397
|
+
? `const __mreactRouteStates = __mreactGlobal.__mreactRouteStates ??= new Map();
|
|
398
|
+
let __mreactActiveCellRecords = undefined;
|
|
399
|
+
let __mreactActiveCellIndex = 0;`
|
|
400
|
+
: "";
|
|
401
|
+
const routeCellHook = routeUsesCells
|
|
402
|
+
? `
|
|
403
|
+
__mreactGlobal.__mreactRouteCell = (nativeCell, initial) => {
|
|
404
|
+
if (__mreactActiveCellRecords === undefined) {
|
|
405
|
+
return nativeCell(initial);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const cellKey = String(__mreactActiveCellIndex);
|
|
409
|
+
__mreactActiveCellIndex += 1;
|
|
410
|
+
const existingRecord = __mreactActiveCellRecords.get(cellKey);
|
|
411
|
+
const record = existingRecord ?? { value: initial };
|
|
412
|
+
const stateCell = nativeCell(record.value);
|
|
413
|
+
const setStateCell = stateCell.set;
|
|
414
|
+
|
|
415
|
+
stateCell.set = (next) => {
|
|
416
|
+
setStateCell((previous) => {
|
|
417
|
+
const resolved = typeof next === "function" ? next(previous) : next;
|
|
418
|
+
record.value = resolved;
|
|
419
|
+
return resolved;
|
|
420
|
+
});
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
__mreactActiveCellRecords.set(cellKey, record);
|
|
424
|
+
return stateCell;
|
|
425
|
+
};`
|
|
426
|
+
: "";
|
|
427
|
+
const routeCellHydrationStart = routeUsesCells
|
|
428
|
+
? ` const __mreactPreviousState = __mreactRouteStates.get(__mreactRouteId);
|
|
429
|
+
const __mreactState = __mreactPreviousState?.marker === __mreactMarker &&
|
|
430
|
+
__mreactPreviousState?.signature === __mreactRouteStateSignature
|
|
431
|
+
? __mreactPreviousState
|
|
432
|
+
: {
|
|
433
|
+
cells: new Map(),
|
|
434
|
+
marker: __mreactMarker,
|
|
435
|
+
signature: __mreactRouteStateSignature,
|
|
436
|
+
};
|
|
437
|
+
__mreactDropMismatchedRouteState(__mreactPreviousState, __mreactState);
|
|
438
|
+
__mreactRouteStates.set(__mreactRouteId, __mreactState);
|
|
439
|
+
__mreactActiveCellRecords = __mreactState.cells;
|
|
440
|
+
__mreactActiveCellIndex = 0;
|
|
441
|
+
|
|
442
|
+
try {
|
|
443
|
+
`
|
|
444
|
+
: "";
|
|
445
|
+
const routeCellHydrationEnd = routeUsesCells
|
|
446
|
+
? ` } finally {
|
|
447
|
+
__mreactActiveCellRecords = undefined;
|
|
448
|
+
__mreactActiveCellIndex = 0;
|
|
449
|
+
}
|
|
450
|
+
`
|
|
451
|
+
: "";
|
|
452
|
+
const routeCellHydrationIndent = routeUsesCells ? " " : " ";
|
|
453
|
+
const routeCellDropFunction = routeUsesCells
|
|
454
|
+
? `
|
|
455
|
+
function __mreactDropMismatchedRouteState(previousState, nextState) {
|
|
456
|
+
if (previousState === undefined || previousState === nextState) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (previousState.signature !== nextState.signature && typeof console !== "undefined") {
|
|
461
|
+
console.warn("mreact: dropping stale route state after route cell signature changed");
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
`
|
|
465
|
+
: "";
|
|
466
|
+
const entry = `${compiled.code}
|
|
467
|
+
|
|
468
|
+
const __mreactRouteId = ${JSON.stringify(routeId)};
|
|
469
|
+
const __mreactRouteStateSignature = ${JSON.stringify(routeStateSignature)};
|
|
470
|
+
const __mreactGlobal = globalThis;
|
|
471
|
+
${navigationStateDeclaration}
|
|
472
|
+
${routeCellStateDeclaration}
|
|
473
|
+
${routeCellHook}
|
|
474
|
+
${clientReferenceRegistry}
|
|
475
|
+
|
|
476
|
+
export function __mreactHydrateRoute() {
|
|
477
|
+
__mreactApplyOutOfOrderFragments(document);
|
|
478
|
+
const __mreactMarker = document.querySelector(\`[data-mreact-route-id="\${__mreactRouteId}"]\`);
|
|
479
|
+
const __mreactPropsElement = document.getElementById(\`mreact-props-\${__mreactRouteId}\`);
|
|
480
|
+
const __mreactClientReferencesElement = document.getElementById(\`mreact-client-references-\${__mreactRouteId}\`);
|
|
481
|
+
const __mreactProps = __mreactPropsElement?.textContent === undefined
|
|
482
|
+
? {}
|
|
483
|
+
: JSON.parse(__mreactPropsElement.textContent);
|
|
484
|
+
const __mreactClientReferences = __mreactClientReferencesElement?.textContent === undefined
|
|
485
|
+
? []
|
|
486
|
+
: JSON.parse(__mreactClientReferencesElement.textContent);
|
|
487
|
+
const __mreactClientReferenceManifests = __mreactGlobal.__mreactClientReferenceManifests ??= new Map();
|
|
488
|
+
__mreactClientReferenceManifests.set(__mreactRouteId, __mreactClientReferences);
|
|
489
|
+
const __mreactComponent = typeof Page === "function"
|
|
490
|
+
? Page
|
|
491
|
+
: typeof DefaultExport === "function"
|
|
492
|
+
? DefaultExport
|
|
493
|
+
: undefined;
|
|
494
|
+
|
|
495
|
+
if (__mreactMarker === null || __mreactComponent === undefined) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
${routeCellHydrationStart}${routeCellHydrationIndent}if (__mreactHydrateClientBoundaries(__mreactMarker, __mreactClientReferences, __mreactClientReferenceComponents)) {
|
|
499
|
+
${routeCellHydrationIndent} __mreactMarker.setAttribute("data-mreact-hydrated", "true");
|
|
500
|
+
${routeCellHydrationIndent} return;
|
|
501
|
+
${routeCellHydrationIndent}}
|
|
502
|
+
${routeCellHydrationIndent}const __mreactNode = __mreactComponent(__mreactProps);
|
|
503
|
+
${routeCellHydrationIndent}__mreactResumeRoute(__mreactMarker, __mreactNode);
|
|
504
|
+
${routeCellHydrationIndent}__mreactMarker.setAttribute("data-mreact-hydrated", "true");
|
|
505
|
+
${routeCellHydrationEnd}}
|
|
506
|
+
${routeCellDropFunction}
|
|
507
|
+
|
|
508
|
+
__mreactHydrateRoute();
|
|
509
|
+
${clientNavigation ? "__mreactInstallNavigation();" : ""}
|
|
510
|
+
|
|
511
|
+
${clientNavigation
|
|
512
|
+
? `export function __mreactNavigateToHtml(html, url) {
|
|
513
|
+
__mreactSaveCurrentHistoryState();
|
|
514
|
+
const applied = __mreactApplyNavigationHtml(html, url);
|
|
515
|
+
|
|
516
|
+
if (!applied) {
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
__mreactPushHistoryState(url);
|
|
521
|
+
__mreactScrollTo(0, 0);
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export async function __mreactPrefetch(url) {
|
|
526
|
+
const href = __mreactNormalizeNavigationUrl(url);
|
|
527
|
+
|
|
528
|
+
if (href === undefined) {
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (__mreactNavigationState.cache.has(href)) {
|
|
533
|
+
return true;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const response = await fetch(href, {
|
|
537
|
+
headers: { "x-mreact-navigation": "1" },
|
|
538
|
+
});
|
|
539
|
+
__mreactApplyRevalidationHeader(response);
|
|
540
|
+
const html = await response.text();
|
|
541
|
+
__mreactNavigationState.cache.set(href, html);
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export async function __mreactNavigate(url) {
|
|
546
|
+
const href = __mreactNormalizeNavigationUrl(url);
|
|
547
|
+
|
|
548
|
+
if (href === undefined) {
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
document.documentElement.setAttribute("data-mreact-navigation-pending", "true");
|
|
553
|
+
|
|
554
|
+
try {
|
|
555
|
+
const cachedHtml = __mreactNavigationState.cache.get(href);
|
|
556
|
+
const html = cachedHtml ?? await __mreactFetchNavigationHtml(href);
|
|
557
|
+
|
|
558
|
+
__mreactNavigationState.cache.set(href, html);
|
|
559
|
+
return __mreactNavigateToHtml(html, href);
|
|
560
|
+
} finally {
|
|
561
|
+
document.documentElement.removeAttribute("data-mreact-navigation-pending");
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
export function __mreactInvalidateNavigationCache(path) {
|
|
566
|
+
const normalizedPath = __mreactNormalizeNavigationPath(path);
|
|
567
|
+
|
|
568
|
+
if (normalizedPath === undefined) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
for (const href of Array.from(__mreactNavigationState.cache.keys())) {
|
|
573
|
+
if (__mreactNormalizeNavigationPath(href) === normalizedPath) {
|
|
574
|
+
__mreactNavigationState.cache.delete(href);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function __mreactFetchNavigationHtml(href) {
|
|
580
|
+
return fetch(href, {
|
|
581
|
+
headers: { "x-mreact-navigation": "1" },
|
|
582
|
+
}).then((response) => {
|
|
583
|
+
__mreactApplyRevalidationHeader(response);
|
|
584
|
+
return response.text();
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function __mreactApplyRevalidationHeader(response) {
|
|
589
|
+
const header = response.headers.get("x-mreact-revalidate");
|
|
590
|
+
|
|
591
|
+
if (header === null || header.trim() === "") {
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
for (const path of header.split(",")) {
|
|
596
|
+
__mreactInvalidateNavigationCache(path.trim());
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function __mreactNormalizeNavigationPath(path) {
|
|
601
|
+
if (typeof location === "undefined") {
|
|
602
|
+
return typeof path === "string" && path.length > 0 ? path : undefined;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
try {
|
|
606
|
+
const url = new URL(path, location.href);
|
|
607
|
+
const pathname = url.pathname.replace(/\\/+$/, "");
|
|
608
|
+
|
|
609
|
+
return pathname === "" ? "/" : pathname;
|
|
610
|
+
} catch {
|
|
611
|
+
return undefined;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export function __mreactRestoreHistoryState(state) {
|
|
616
|
+
if (state === null || state === undefined || state.__mreact !== true || typeof state.html !== "string") {
|
|
617
|
+
return false;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const applied = __mreactApplyNavigationHtml(state.html, state.url);
|
|
621
|
+
|
|
622
|
+
if (!applied) {
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
__mreactScrollTo(Number(state.scrollX ?? 0), Number(state.scrollY ?? 0));
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function __mreactApplyNavigationHtml(html, url) {
|
|
631
|
+
const template = document.createElement("template");
|
|
632
|
+
template.innerHTML = html.replace(/^\\s*<!doctype html>/i, "");
|
|
633
|
+
__mreactApplyOutOfOrderFragments(template.content);
|
|
634
|
+
const nextMarker = template.content.querySelector("[data-mreact-route-id]");
|
|
635
|
+
const currentMarker = document.querySelector("[data-mreact-route-id]");
|
|
636
|
+
|
|
637
|
+
if (nextMarker === null || currentMarker === null) {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
__mreactResumeNode(currentMarker, nextMarker);
|
|
642
|
+
|
|
643
|
+
for (const propsElement of Array.from(document.querySelectorAll('script[type="application/json"][id^="mreact-props-"], script[type="application/json"][id^="mreact-client-references-"]'))) {
|
|
644
|
+
propsElement.remove();
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
for (const propsElement of Array.from(template.content.querySelectorAll('script[type="application/json"][id^="mreact-props-"], script[type="application/json"][id^="mreact-client-references-"]'))) {
|
|
648
|
+
document.body.appendChild(propsElement);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const script = template.content.querySelector('script[type="module"][src]')?.getAttribute("src");
|
|
652
|
+
if (script !== null && script !== undefined) {
|
|
653
|
+
void import(/* @vite-ignore */ script).then((module) => module.__mreactHydrateRoute?.());
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
__mreactApplyOutOfOrderFragments(document);
|
|
657
|
+
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function __mreactCurrentHistoryState(url) {
|
|
662
|
+
return {
|
|
663
|
+
__mreact: true,
|
|
664
|
+
html: document.body.innerHTML,
|
|
665
|
+
scrollX: Number(globalThis.scrollX ?? 0),
|
|
666
|
+
scrollY: Number(globalThis.scrollY ?? 0),
|
|
667
|
+
url,
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function __mreactPushHistoryState(url) {
|
|
672
|
+
if (typeof history === "undefined" || url === undefined) {
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
try {
|
|
677
|
+
history.pushState(__mreactCurrentHistoryState(url), "", url);
|
|
678
|
+
} catch {
|
|
679
|
+
// Ignore invalid URLs in non-browser test environments.
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function __mreactSaveCurrentHistoryState() {
|
|
684
|
+
if (typeof history === "undefined" || typeof location === "undefined") {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
try {
|
|
689
|
+
history.replaceState(__mreactCurrentHistoryState(location.href), "", location.href);
|
|
690
|
+
} catch {
|
|
691
|
+
// Ignore invalid URLs in non-browser test environments.
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function __mreactNormalizeNavigationUrl(url) {
|
|
696
|
+
if (typeof location === "undefined") {
|
|
697
|
+
return typeof url === "string" ? url : undefined;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
try {
|
|
701
|
+
return new URL(url, location.href).href;
|
|
702
|
+
} catch {
|
|
703
|
+
return undefined;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function __mreactScrollTo(x, y) {
|
|
708
|
+
if (typeof scrollTo === "function") {
|
|
709
|
+
scrollTo(x, y);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function __mreactInstallNavigation() {
|
|
714
|
+
if (__mreactNavigationState.installed || typeof document === "undefined") {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
__mreactNavigationState.installed = true;
|
|
719
|
+
__mreactSaveCurrentHistoryState();
|
|
720
|
+
addEventListener("popstate", (event) => {
|
|
721
|
+
if (!__mreactRestoreHistoryState(event.state)) {
|
|
722
|
+
location.reload();
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
document.addEventListener("pointerenter", (event) => {
|
|
726
|
+
const anchor = __mreactAnchorFromEvent(event);
|
|
727
|
+
|
|
728
|
+
if (anchor !== null && anchor.dataset.mreactPrefetch !== "false") {
|
|
729
|
+
void __mreactPrefetch(anchor.href);
|
|
730
|
+
}
|
|
731
|
+
}, true);
|
|
732
|
+
document.addEventListener("focusin", (event) => {
|
|
733
|
+
const anchor = __mreactAnchorFromEvent(event);
|
|
734
|
+
|
|
735
|
+
if (anchor !== null && anchor.dataset.mreactPrefetch !== "false") {
|
|
736
|
+
void __mreactPrefetch(anchor.href);
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
document.addEventListener("click", (event) => {
|
|
740
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
const anchor = __mreactAnchorFromEvent(event);
|
|
745
|
+
|
|
746
|
+
if (anchor === null) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const nextUrl = new URL(anchor.href, location.href);
|
|
751
|
+
|
|
752
|
+
if (nextUrl.origin !== location.origin) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
event.preventDefault();
|
|
757
|
+
void __mreactNavigate(nextUrl.href)
|
|
758
|
+
.then((navigated) => {
|
|
759
|
+
if (!navigated) {
|
|
760
|
+
location.href = nextUrl.href;
|
|
761
|
+
}
|
|
762
|
+
}).catch(() => {
|
|
763
|
+
location.href = nextUrl.href;
|
|
764
|
+
});
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function __mreactAnchorFromEvent(event) {
|
|
769
|
+
const target = event.target;
|
|
770
|
+
const anchor = target instanceof Element ? target.closest("a[href]") : null;
|
|
771
|
+
|
|
772
|
+
if (!(anchor instanceof HTMLAnchorElement) || anchor.target !== "" || anchor.hasAttribute("download")) {
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
return anchor;
|
|
777
|
+
}
|
|
778
|
+
`
|
|
779
|
+
: ""}
|
|
780
|
+
|
|
781
|
+
function __mreactApplyOutOfOrderFragments(root) {
|
|
782
|
+
const fragments = Array.from(root.querySelectorAll("template[data-mreact-oob-fragment]"));
|
|
783
|
+
|
|
784
|
+
for (const fragment of fragments) {
|
|
785
|
+
const id = fragment.getAttribute("data-mreact-oob-fragment");
|
|
786
|
+
|
|
787
|
+
if (id === null) {
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
const placeholder = Array.from(root.querySelectorAll("[data-mreact-oob-placeholder]"))
|
|
792
|
+
.find((candidate) => candidate.getAttribute("data-mreact-oob-placeholder") === id);
|
|
793
|
+
|
|
794
|
+
if (placeholder === undefined) {
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
placeholder.replaceWith(fragment.content.cloneNode(true));
|
|
799
|
+
fragment.remove();
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function __mreactHydrateClientBoundaries(marker, references, components) {
|
|
804
|
+
if (!Array.isArray(references) || references.length === 0) {
|
|
805
|
+
return false;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const placeholders = Array.from(marker.querySelectorAll("template[data-mreact-client-boundary]"));
|
|
809
|
+
|
|
810
|
+
if (placeholders.length === 0) {
|
|
811
|
+
return false;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
for (const placeholder of placeholders) {
|
|
815
|
+
const name = placeholder.getAttribute("data-mreact-client-boundary");
|
|
816
|
+
const component = name === null ? undefined : components.get(name);
|
|
817
|
+
|
|
818
|
+
if (typeof component !== "function") {
|
|
819
|
+
return false;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const propsElement = __mreactClientBoundaryPropsElement(placeholder, name);
|
|
823
|
+
const props = propsElement?.textContent === undefined || propsElement.textContent === ""
|
|
824
|
+
? {}
|
|
825
|
+
: JSON.parse(propsElement.textContent);
|
|
826
|
+
const node = component(props);
|
|
827
|
+
|
|
828
|
+
placeholder.replaceWith(node);
|
|
829
|
+
propsElement?.remove();
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
return true;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function __mreactClientBoundaryPropsElement(placeholder, name) {
|
|
836
|
+
let next = placeholder.nextSibling;
|
|
837
|
+
|
|
838
|
+
while (next !== null) {
|
|
839
|
+
if (
|
|
840
|
+
next.nodeType === Node.ELEMENT_NODE &&
|
|
841
|
+
next.tagName === "SCRIPT" &&
|
|
842
|
+
next.getAttribute("type") === "application/json" &&
|
|
843
|
+
next.getAttribute("data-mreact-client-boundary-props") === name
|
|
844
|
+
) {
|
|
845
|
+
return next;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
if (next.nodeType === Node.ELEMENT_NODE) {
|
|
849
|
+
return undefined;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
next = next.nextSibling;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
return undefined;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function __mreactResumeRoute(marker, nextNode) {
|
|
859
|
+
const current = __mreactRouteResumeTarget(marker, nextNode);
|
|
860
|
+
|
|
861
|
+
if (current === null) {
|
|
862
|
+
marker.appendChild(nextNode);
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
__mreactResumeNode(current, nextNode);
|
|
867
|
+
|
|
868
|
+
if (current.parentNode !== marker) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
while (marker.childNodes.length > 1) {
|
|
873
|
+
marker.lastChild?.remove();
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function __mreactRouteResumeTarget(marker, nextNode) {
|
|
878
|
+
const current = marker.firstChild;
|
|
879
|
+
|
|
880
|
+
if (
|
|
881
|
+
current === null ||
|
|
882
|
+
current.nodeType !== Node.ELEMENT_NODE ||
|
|
883
|
+
nextNode.nodeType !== Node.ELEMENT_NODE ||
|
|
884
|
+
current.tagName === nextNode.tagName ||
|
|
885
|
+
!current.hasAttribute("data-mreact-layout-boundary")
|
|
886
|
+
) {
|
|
887
|
+
return current;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
return __mreactFindLayoutPageTarget(current, nextNode) ?? current;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function __mreactFindLayoutPageTarget(current, nextNode) {
|
|
894
|
+
for (const child of Array.from(current.childNodes)) {
|
|
895
|
+
if (child.nodeType !== Node.ELEMENT_NODE) {
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
if (
|
|
900
|
+
child.tagName === nextNode.tagName &&
|
|
901
|
+
!child.hasAttribute("data-mreact-layout-boundary") &&
|
|
902
|
+
!child.hasAttribute("data-mreact-template-boundary")
|
|
903
|
+
) {
|
|
904
|
+
return child;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
if (child.hasAttribute("data-mreact-layout-boundary")) {
|
|
908
|
+
const nested = __mreactFindLayoutPageTarget(child, nextNode);
|
|
909
|
+
|
|
910
|
+
if (nested !== null) {
|
|
911
|
+
return nested;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
return null;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
function __mreactResumeNode(current, next) {
|
|
920
|
+
if (
|
|
921
|
+
next.nodeType === Node.COMMENT_NODE &&
|
|
922
|
+
next.nodeValue === "mreact-async-boundary"
|
|
923
|
+
) {
|
|
924
|
+
// Server stream emits the resolved <Await> content; preserve the existing
|
|
925
|
+
// DOM instead of replacing it with the client placeholder comment.
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
if (__mreactShouldReplaceNode(current, next)) {
|
|
930
|
+
current.replaceWith(next);
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
if (current.nodeType === Node.TEXT_NODE && next.nodeType === Node.TEXT_NODE) {
|
|
935
|
+
if (current.nodeValue !== next.nodeValue) {
|
|
936
|
+
current.nodeValue = next.nodeValue;
|
|
937
|
+
}
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
if (current.nodeType !== Node.ELEMENT_NODE || next.nodeType !== Node.ELEMENT_NODE) {
|
|
942
|
+
current.replaceWith(next);
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
__mreactSyncEventBindings(current, next);
|
|
947
|
+
__mreactSyncAttributes(current, next);
|
|
948
|
+
__mreactResumeChildren(current, next);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
function __mreactShouldReplaceNode(current, next) {
|
|
952
|
+
if (
|
|
953
|
+
next.nodeType === Node.ELEMENT_NODE &&
|
|
954
|
+
next.hasAttribute("data-mreact-template-boundary")
|
|
955
|
+
) {
|
|
956
|
+
return true;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if (current.nodeType !== next.nodeType) {
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
return current.nodeType === Node.ELEMENT_NODE &&
|
|
964
|
+
current.tagName !== next.tagName;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
function __mreactSyncEventBindings(current, next) {
|
|
968
|
+
const previousDisposers = current.__mreactEventDisposers;
|
|
969
|
+
|
|
970
|
+
if (Array.isArray(previousDisposers)) {
|
|
971
|
+
for (const dispose of previousDisposers) {
|
|
972
|
+
dispose();
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
const bindings = next.__mreactEventBindings;
|
|
977
|
+
|
|
978
|
+
if (!Array.isArray(bindings) || bindings.length === 0) {
|
|
979
|
+
current.__mreactEventDisposers = [];
|
|
980
|
+
current.__mreactHasEvents = false;
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
const disposers = [];
|
|
985
|
+
|
|
986
|
+
for (const binding of bindings) {
|
|
987
|
+
current.addEventListener(binding.type, binding.listener);
|
|
988
|
+
disposers.push(() => current.removeEventListener(binding.type, binding.listener));
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
current.__mreactEventDisposers = disposers;
|
|
992
|
+
current.__mreactHasEvents = true;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
function __mreactSyncAttributes(current, next) {
|
|
996
|
+
for (const attribute of Array.from(current.attributes)) {
|
|
997
|
+
if (!next.hasAttribute(attribute.name)) {
|
|
998
|
+
current.removeAttribute(attribute.name);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
for (const attribute of Array.from(next.attributes)) {
|
|
1003
|
+
if (current.getAttribute(attribute.name) !== attribute.value) {
|
|
1004
|
+
current.setAttribute(attribute.name, attribute.value);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
function __mreactResumeChildren(current, next) {
|
|
1010
|
+
const nextChildren = Array.from(next.childNodes);
|
|
1011
|
+
const refreshTextBindings = next.__mreactHasEvents === true;
|
|
1012
|
+
let index = 0;
|
|
1013
|
+
|
|
1014
|
+
while (index < nextChildren.length) {
|
|
1015
|
+
const currentChild = current.childNodes[index];
|
|
1016
|
+
const nextChild = nextChildren[index];
|
|
1017
|
+
|
|
1018
|
+
if (currentChild === undefined) {
|
|
1019
|
+
current.appendChild(nextChild);
|
|
1020
|
+
index += 1;
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// Text nodes that the client bound reactively must replace the
|
|
1025
|
+
// server's static text so subsequent updates land in the live DOM.
|
|
1026
|
+
const isReactiveText =
|
|
1027
|
+
nextChild.nodeType === Node.TEXT_NODE &&
|
|
1028
|
+
nextChild.__mreactReactiveText === true;
|
|
1029
|
+
|
|
1030
|
+
if (
|
|
1031
|
+
(refreshTextBindings || isReactiveText) &&
|
|
1032
|
+
currentChild.nodeType === Node.TEXT_NODE &&
|
|
1033
|
+
nextChild.nodeType === Node.TEXT_NODE
|
|
1034
|
+
) {
|
|
1035
|
+
currentChild.replaceWith(nextChild);
|
|
1036
|
+
} else {
|
|
1037
|
+
__mreactResumeNode(currentChild, nextChild);
|
|
1038
|
+
}
|
|
1039
|
+
index += 1;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
while (current.childNodes.length > nextChildren.length) {
|
|
1043
|
+
current.lastChild?.remove();
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
`;
|
|
1047
|
+
const bundled = await build({
|
|
1048
|
+
bundle: true,
|
|
1049
|
+
format: "esm",
|
|
1050
|
+
logLevel: "silent",
|
|
1051
|
+
minify: options.minify === true,
|
|
1052
|
+
define: {
|
|
1053
|
+
__MREACT_CLIENT_DEVTOOLS__: "false",
|
|
1054
|
+
},
|
|
1055
|
+
// issue 059: rename a strictly internal set of reactive-core / DOM scope
|
|
1056
|
+
// properties to single-character names. Each name in the allow-list is
|
|
1057
|
+
// reserved for cross-file internal state (not part of any public API
|
|
1058
|
+
// and not used by browser host objects), so global mangling is safe.
|
|
1059
|
+
// See packages/reactive-core/src/state.ts and reactive-dom/src/scope.ts.
|
|
1060
|
+
...(options.minify === true
|
|
1061
|
+
? {
|
|
1062
|
+
mangleProps: /^(singleSubscriber|subscribers|markDirty|pendingComputed|flushingComputed|nextComputationId|notificationDepth|batchDepth|activeTracker|deps|queued|disposed|disposers)$/,
|
|
1063
|
+
}
|
|
1064
|
+
: {}),
|
|
1065
|
+
outfile: "route.js",
|
|
1066
|
+
platform: "browser",
|
|
1067
|
+
plugins: [workspaceRuntimePlugin({ routeFile: options.filename })],
|
|
1068
|
+
sourcemap: options.sourceMap === true ? "external" : false,
|
|
1069
|
+
write: false,
|
|
1070
|
+
stdin: {
|
|
1071
|
+
contents: entry,
|
|
1072
|
+
loader: "tsx",
|
|
1073
|
+
resolveDir: dirname(options.filename),
|
|
1074
|
+
sourcefile: options.filename,
|
|
1075
|
+
},
|
|
1076
|
+
});
|
|
1077
|
+
const codeFile = bundled.outputFiles.find((file) => file.path.endsWith(".js"));
|
|
1078
|
+
const mapFile = bundled.outputFiles.find((file) => file.path.endsWith(".js.map"));
|
|
1079
|
+
return {
|
|
1080
|
+
code: codeFile?.text ?? bundled.outputFiles[0]?.text ?? "",
|
|
1081
|
+
...(mapFile === undefined ? {} : { map: mapFile.text }),
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
function workspaceRuntimePlugin(options) {
|
|
1085
|
+
const rootDir = join(dirname(fileURLToPath(import.meta.url)), "../../..");
|
|
1086
|
+
const routeDir = dirname(options.routeFile);
|
|
1087
|
+
const reactiveCorePath = join(rootDir, "packages/reactive-core/src/index.ts");
|
|
1088
|
+
const packageFile = (packageName, basename) => join(rootDir, "packages", packageName, "src", `${basename}.ts`);
|
|
1089
|
+
const runtimePaths = new Map([
|
|
1090
|
+
["@reckona/mreact-compat", packageFile("react-compat", "index")],
|
|
1091
|
+
["@reckona/mreact-compat/flight", packageFile("react-compat", "flight")],
|
|
1092
|
+
["@reckona/mreact-compat/internal", packageFile("react-compat", "internal")],
|
|
1093
|
+
["@reckona/mreact-compat/jsx-dev-runtime", packageFile("react-compat", "jsx-dev-runtime")],
|
|
1094
|
+
["@reckona/mreact-compat/jsx-runtime", packageFile("react-compat", "jsx-runtime")],
|
|
1095
|
+
["@reckona/mreact-compat/scheduler", packageFile("react-compat", "scheduler")],
|
|
1096
|
+
["@reckona/mreact-reactive-dom", join(rootDir, "packages/reactive-dom/src/index.ts")],
|
|
1097
|
+
]);
|
|
1098
|
+
return {
|
|
1099
|
+
name: "mreact-workspace-runtime",
|
|
1100
|
+
setup(buildApi) {
|
|
1101
|
+
buildApi.onResolve({ filter: /^\.\/devtools\.js$/ }, (args) => args.importer?.startsWith(join(rootDir, "packages/reactive-core/src/")) === true
|
|
1102
|
+
? { namespace: "mreact-devtools-stub", path: "devtools" }
|
|
1103
|
+
: undefined);
|
|
1104
|
+
buildApi.onResolve({ filter: /^@reckona\/mreact-reactive-core$/ }, () => ({
|
|
1105
|
+
namespace: "mreact-hot-runtime",
|
|
1106
|
+
path: "reactive-core",
|
|
1107
|
+
}));
|
|
1108
|
+
buildApi.onResolve({
|
|
1109
|
+
filter: /^@reckona\/mreact-(?:compat|reactive-dom)(?:\/(?:flight|internal|jsx-dev-runtime|jsx-runtime|scheduler))?$/,
|
|
1110
|
+
}, (args) => {
|
|
1111
|
+
const path = runtimePaths.get(args.path);
|
|
1112
|
+
return path === undefined ? undefined : { path };
|
|
1113
|
+
});
|
|
1114
|
+
buildApi.onLoad({ filter: /^reactive-core$/, namespace: "mreact-hot-runtime" }, () => ({
|
|
1115
|
+
contents: `import { cell as nativeCell } from ${JSON.stringify(reactiveCorePath)};
|
|
1116
|
+
export * from ${JSON.stringify(reactiveCorePath)};
|
|
1117
|
+
export function cell(initial) {
|
|
1118
|
+
const routeCell = globalThis.__mreactRouteCell;
|
|
1119
|
+
return typeof routeCell === "function" ? routeCell(nativeCell, initial) : nativeCell(initial);
|
|
1120
|
+
}`,
|
|
1121
|
+
loader: "ts",
|
|
1122
|
+
resolveDir: rootDir,
|
|
1123
|
+
}));
|
|
1124
|
+
buildApi.onLoad({ filter: /^devtools$/, namespace: "mreact-devtools-stub" }, () => ({
|
|
1125
|
+
contents: `export function emitReactiveDevtoolsEvent() {}
|
|
1126
|
+
export function hasReactiveDevtoolsEmitter() { return false; }
|
|
1127
|
+
export function currentDevtoolsEmitter() { return undefined; }`,
|
|
1128
|
+
loader: "ts",
|
|
1129
|
+
}));
|
|
1130
|
+
buildApi.onLoad({ filter: /\.(?:mreact\.)?[cm]?[jt]sx$/ }, async (args) => {
|
|
1131
|
+
if (!isAppLocalSourcePath(args.path, routeDir) || args.path === options.routeFile) {
|
|
1132
|
+
return undefined;
|
|
1133
|
+
}
|
|
1134
|
+
const source = await readFile(args.path, "utf8");
|
|
1135
|
+
const output = transform({
|
|
1136
|
+
code: source,
|
|
1137
|
+
dev: true,
|
|
1138
|
+
filename: args.path,
|
|
1139
|
+
target: "client",
|
|
1140
|
+
});
|
|
1141
|
+
if (output.diagnostics.length > 0) {
|
|
1142
|
+
throw new Error(`${args.path}: ${output.diagnostics
|
|
1143
|
+
.map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`)
|
|
1144
|
+
.join("\n")}`);
|
|
1145
|
+
}
|
|
1146
|
+
return {
|
|
1147
|
+
contents: output.code,
|
|
1148
|
+
loader: "tsx",
|
|
1149
|
+
resolveDir: dirname(args.path),
|
|
1150
|
+
};
|
|
1151
|
+
});
|
|
1152
|
+
},
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
function isAppLocalSourcePath(path, routeDir) {
|
|
1156
|
+
return path === routeDir || path.startsWith(`${routeDir}/`);
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Detects the `export const clientNavigation = false` hint in a page module
|
|
1160
|
+
* source. Returns the hinted value, or `true` when no hint is present (i.e.,
|
|
1161
|
+
* preserve the historical "navigation runtime is always present" behavior).
|
|
1162
|
+
*
|
|
1163
|
+
* Regex-based to avoid pulling the JS parser into the build path. The pattern
|
|
1164
|
+
* accepts the common formatting variants:
|
|
1165
|
+
* export const clientNavigation = false
|
|
1166
|
+
* export const clientNavigation = false ;
|
|
1167
|
+
* export const clientNavigation: boolean = false
|
|
1168
|
+
*/
|
|
1169
|
+
export function detectClientNavigationHint(source) {
|
|
1170
|
+
const match = source.match(/export\s+const\s+clientNavigation\s*(?::\s*[^=]+)?=\s*(true|false)\s*;?/);
|
|
1171
|
+
return match === null ? true : match[1] === "true";
|
|
1172
|
+
}
|
|
1173
|
+
function detectRouteCellStateHint(code) {
|
|
1174
|
+
const callExpression = routeCellCallExpressionSource(code);
|
|
1175
|
+
return callExpression === undefined
|
|
1176
|
+
? /\bcell\d*\s*\(/.test(code)
|
|
1177
|
+
: new RegExp(`(?:${callExpression})\\s*\\(`).test(code);
|
|
1178
|
+
}
|
|
1179
|
+
async function inferClientReferenceManifestForBundle(options) {
|
|
1180
|
+
const inference = await inferClientRouteModule({
|
|
1181
|
+
code: options.code,
|
|
1182
|
+
filename: options.filename,
|
|
1183
|
+
routePath: options.routePath,
|
|
1184
|
+
});
|
|
1185
|
+
if (inference.clientBoundaryImports.length === 0) {
|
|
1186
|
+
return [];
|
|
1187
|
+
}
|
|
1188
|
+
const output = transform({
|
|
1189
|
+
code: options.code,
|
|
1190
|
+
clientBoundaryImports: inference.clientBoundaryImports,
|
|
1191
|
+
dev: true,
|
|
1192
|
+
filename: options.filename,
|
|
1193
|
+
target: "server",
|
|
1194
|
+
});
|
|
1195
|
+
return output.metadata.clientReferenceManifest ?? [];
|
|
1196
|
+
}
|
|
1197
|
+
function emitClientReferenceRegistry(manifest) {
|
|
1198
|
+
const entries = manifest.flatMap((reference) => {
|
|
1199
|
+
const expression = clientReferenceExpression(reference.name);
|
|
1200
|
+
return expression === undefined
|
|
1201
|
+
? []
|
|
1202
|
+
: [` [${JSON.stringify(reference.name)}, ${expression}],`];
|
|
1203
|
+
});
|
|
1204
|
+
return [
|
|
1205
|
+
"const __mreactClientReferenceComponents = new Map([",
|
|
1206
|
+
...entries,
|
|
1207
|
+
"]);",
|
|
1208
|
+
].join("\n");
|
|
1209
|
+
}
|
|
1210
|
+
function clientReferenceExpression(name) {
|
|
1211
|
+
return /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(name) ? name : undefined;
|
|
1212
|
+
}
|
|
1213
|
+
function routeStateSignatureForSource(code) {
|
|
1214
|
+
const callExpression = routeCellCallExpressionSource(code);
|
|
1215
|
+
const callsitePattern = callExpression === undefined
|
|
1216
|
+
? /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(cell\d*|cell)\s*\(/g
|
|
1217
|
+
: new RegExp(`\\b(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*(${callExpression})\\s*\\(`, "g");
|
|
1218
|
+
const cellCallsites = Array.from(code.matchAll(callsitePattern), (match) => `${match[1]}:${match[2]}`);
|
|
1219
|
+
const countPattern = callExpression === undefined
|
|
1220
|
+
? /\bcell\d*\s*\(/g
|
|
1221
|
+
: new RegExp(`(?:${callExpression})\\s*\\(`, "g");
|
|
1222
|
+
const signature = cellCallsites.length > 0
|
|
1223
|
+
? cellCallsites.join("\n")
|
|
1224
|
+
: `cell-count:${(code.match(countPattern) ?? []).length}`;
|
|
1225
|
+
return createHash("sha256").update(signature).digest("hex").slice(0, 16);
|
|
1226
|
+
}
|
|
1227
|
+
function routeCellCallExpressionSource(code) {
|
|
1228
|
+
const namedImports = new Set();
|
|
1229
|
+
const namespaceImports = new Set();
|
|
1230
|
+
const namedImportPattern = /import\s+\{(?<imports>[^}]*)\}\s+from\s+["']@reckona\/mreact-reactive-core["']/g;
|
|
1231
|
+
for (const match of code.matchAll(namedImportPattern)) {
|
|
1232
|
+
const imports = match.groups?.imports;
|
|
1233
|
+
if (imports === undefined) {
|
|
1234
|
+
continue;
|
|
1235
|
+
}
|
|
1236
|
+
for (const part of imports.split(",")) {
|
|
1237
|
+
const specifier = part.trim();
|
|
1238
|
+
const alias = /^cell\s+as\s+([A-Za-z_$][\w$]*)$/.exec(specifier);
|
|
1239
|
+
if (specifier === "cell") {
|
|
1240
|
+
namedImports.add("cell");
|
|
1241
|
+
}
|
|
1242
|
+
else if (alias?.[1] !== undefined) {
|
|
1243
|
+
namedImports.add(alias[1]);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
const namespaceImportPattern = /import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s+["']@reckona\/mreact-reactive-core["']/g;
|
|
1248
|
+
for (const match of code.matchAll(namespaceImportPattern)) {
|
|
1249
|
+
if (match[1] !== undefined) {
|
|
1250
|
+
namespaceImports.add(match[1]);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
const alternatives = [
|
|
1254
|
+
...Array.from(namedImports, (name) => `\\b${escapeRegExp(name)}`),
|
|
1255
|
+
...Array.from(namespaceImports, (name) => `\\b${escapeRegExp(name)}\\.cell`),
|
|
1256
|
+
];
|
|
1257
|
+
return alternatives.length === 0 ? undefined : `(?:${alternatives.join("|")})`;
|
|
1258
|
+
}
|
|
1259
|
+
function escapeRegExp(value) {
|
|
1260
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1261
|
+
}
|
|
1262
|
+
function errorMessage(error) {
|
|
1263
|
+
return error instanceof Error ? error.message : String(error);
|
|
1264
|
+
}
|
|
1265
|
+
function escapeScriptJson(value) {
|
|
1266
|
+
return value.replaceAll("<", "\\u003c");
|
|
1267
|
+
}
|
|
1268
|
+
//# sourceMappingURL=client.js.map
|