@reckona/mreact-router 0.0.54 → 0.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +8 -0
- package/dist/build.js.map +1 -1
- package/dist/client.d.ts +9 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +282 -26
- package/dist/client.js.map +1 -1
- package/dist/vite.d.ts +3 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +10 -2
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
package/dist/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readFile, stat } from "node:fs/promises";
|
|
3
3
|
import { builtinModules } from "node:module";
|
|
4
|
-
import { dirname, join, relative, sep } from "node:path";
|
|
4
|
+
import { dirname, extname, join, relative, sep } from "node:path";
|
|
5
5
|
import { collectClientRouteModuleAnalysis, formatDiagnostic, } from "@reckona/mreact-compiler";
|
|
6
6
|
import { collectClientRouteModuleAnalysisFromContext, createCompilerModuleContext, transformCompilerModuleContext, } from "@reckona/mreact-compiler/internal";
|
|
7
7
|
import { assetPath } from "./assets.js";
|
|
@@ -40,6 +40,7 @@ export function createClientRouteInferenceCache() {
|
|
|
40
40
|
moduleContextByFile: new Map(),
|
|
41
41
|
resolvedByImport: new Map(),
|
|
42
42
|
sourceByFile: new Map(),
|
|
43
|
+
transformedSourceByFile: new Map(),
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
export async function isClientRouteModule(options) {
|
|
@@ -47,14 +48,21 @@ export async function isClientRouteModule(options) {
|
|
|
47
48
|
}
|
|
48
49
|
export async function inferClientRouteModule(options) {
|
|
49
50
|
const cache = options.cache ?? createClientRouteInferenceCache();
|
|
51
|
+
const sourceTransform = clientRouteSourceTransformForVitePlugins(options.vitePlugins);
|
|
52
|
+
const code = await transformClientRouteSource({
|
|
53
|
+
code: options.code,
|
|
54
|
+
filename: options.filename,
|
|
55
|
+
sourceTransform,
|
|
56
|
+
});
|
|
50
57
|
try {
|
|
51
58
|
const routeInference = await inferClientRouteModuleSource({
|
|
52
59
|
cache,
|
|
53
|
-
code
|
|
60
|
+
code,
|
|
54
61
|
filename: options.filename,
|
|
55
|
-
moduleContext: options.moduleContext,
|
|
62
|
+
...(sourceTransform === undefined ? { moduleContext: options.moduleContext } : {}),
|
|
56
63
|
root: true,
|
|
57
64
|
seen: new Set(),
|
|
65
|
+
sourceTransform,
|
|
58
66
|
});
|
|
59
67
|
if (options.appDir === undefined) {
|
|
60
68
|
return routeInference;
|
|
@@ -63,6 +71,7 @@ export async function inferClientRouteModule(options) {
|
|
|
63
71
|
appDir: options.appDir,
|
|
64
72
|
cache,
|
|
65
73
|
filename: options.filename,
|
|
74
|
+
sourceTransform,
|
|
66
75
|
});
|
|
67
76
|
return {
|
|
68
77
|
client: routeInference.client || shellInferences.some((inference) => inference.client),
|
|
@@ -79,18 +88,25 @@ export async function inferClientRouteModule(options) {
|
|
|
79
88
|
}
|
|
80
89
|
export async function collectClientRouteReferences(options) {
|
|
81
90
|
const cache = options.cache ?? createClientRouteInferenceCache();
|
|
91
|
+
const sourceTransform = clientRouteSourceTransformForVitePlugins(options.vitePlugins);
|
|
92
|
+
const code = await transformClientRouteSource({
|
|
93
|
+
code: options.code,
|
|
94
|
+
filename: options.filename,
|
|
95
|
+
sourceTransform,
|
|
96
|
+
});
|
|
82
97
|
const routeModuleContext = await compilerModuleContextForSource({
|
|
83
98
|
cache,
|
|
84
|
-
code
|
|
99
|
+
code,
|
|
85
100
|
filename: options.filename,
|
|
86
101
|
});
|
|
87
102
|
const routeInference = await inferClientRouteModuleSource({
|
|
88
103
|
cache,
|
|
89
|
-
code
|
|
104
|
+
code,
|
|
90
105
|
filename: options.filename,
|
|
91
106
|
moduleContext: routeModuleContext,
|
|
92
107
|
root: true,
|
|
93
108
|
seen: new Set(),
|
|
109
|
+
sourceTransform,
|
|
94
110
|
});
|
|
95
111
|
const sources = [];
|
|
96
112
|
const seenSourceFiles = new Set();
|
|
@@ -113,6 +129,7 @@ export async function collectClientRouteReferences(options) {
|
|
|
113
129
|
moduleContext,
|
|
114
130
|
root: true,
|
|
115
131
|
seen: new Set(),
|
|
132
|
+
sourceTransform,
|
|
116
133
|
}));
|
|
117
134
|
sources.push({
|
|
118
135
|
code: sourceOptions.code,
|
|
@@ -121,19 +138,19 @@ export async function collectClientRouteReferences(options) {
|
|
|
121
138
|
moduleContext,
|
|
122
139
|
});
|
|
123
140
|
for (const referenceFile of inference.clientReferenceSourceFiles) {
|
|
124
|
-
const code = stripRouteClientOnlyExports(await
|
|
141
|
+
const code = stripRouteClientOnlyExports(await readClientRouteSource({ cache, filename: referenceFile, sourceTransform }));
|
|
125
142
|
await addSource({ code, filename: referenceFile });
|
|
126
143
|
}
|
|
127
144
|
};
|
|
128
145
|
await addSource({
|
|
129
|
-
code
|
|
146
|
+
code,
|
|
130
147
|
filename: options.filename,
|
|
131
148
|
inference: routeInference,
|
|
132
149
|
moduleContext: routeModuleContext,
|
|
133
150
|
});
|
|
134
151
|
if (options.appDir !== undefined) {
|
|
135
152
|
for (const shell of await clientShellFilesForPage(options.appDir, options.filename)) {
|
|
136
|
-
const code = stripRouteClientOnlyExports(await
|
|
153
|
+
const code = stripRouteClientOnlyExports(await readClientRouteSource({ cache, filename: shell, sourceTransform }));
|
|
137
154
|
const moduleContext = await compilerModuleContextForSource({
|
|
138
155
|
cache,
|
|
139
156
|
code,
|
|
@@ -149,6 +166,7 @@ export async function collectClientRouteReferences(options) {
|
|
|
149
166
|
moduleContext,
|
|
150
167
|
root: true,
|
|
151
168
|
seen: new Set(),
|
|
169
|
+
sourceTransform,
|
|
152
170
|
}),
|
|
153
171
|
moduleContext,
|
|
154
172
|
});
|
|
@@ -202,13 +220,18 @@ export async function collectClientRouteReferences(options) {
|
|
|
202
220
|
}
|
|
203
221
|
async function inferClientRouteShellModules(options) {
|
|
204
222
|
return Promise.all((await clientShellFilesForPage(options.appDir, options.filename)).map(async (shell) => {
|
|
205
|
-
const code = stripRouteClientOnlyExports(await
|
|
223
|
+
const code = stripRouteClientOnlyExports(await readClientRouteSource({
|
|
224
|
+
cache: options.cache,
|
|
225
|
+
filename: shell,
|
|
226
|
+
sourceTransform: options.sourceTransform,
|
|
227
|
+
}));
|
|
206
228
|
return await inferClientRouteModuleSource({
|
|
207
229
|
cache: options.cache,
|
|
208
230
|
code,
|
|
209
231
|
filename: shell,
|
|
210
232
|
root: true,
|
|
211
233
|
seen: new Set(),
|
|
234
|
+
sourceTransform: options.sourceTransform,
|
|
212
235
|
});
|
|
213
236
|
}));
|
|
214
237
|
}
|
|
@@ -232,7 +255,7 @@ function isExplicitClientRouteSource(analysis, filename) {
|
|
|
232
255
|
return analysis.hasUseClientDirective || isClientBoundaryFilename(filename);
|
|
233
256
|
}
|
|
234
257
|
function isClientBoundaryFilename(filename) {
|
|
235
|
-
return /\.client(?:\.mreact)?\.[cm]?[jt]sx?$/.test(filename);
|
|
258
|
+
return /\.(?:client|compat)(?:\.mreact)?\.[cm]?[jt]sx?$/.test(filename);
|
|
236
259
|
}
|
|
237
260
|
function isServerOnlyClientRouteSource(analysis) {
|
|
238
261
|
return analysis.hasUseServerDirective;
|
|
@@ -297,6 +320,7 @@ async function inferClientRouteModuleSource(options) {
|
|
|
297
320
|
continue;
|
|
298
321
|
}
|
|
299
322
|
const resolved = await resolveAppLocalModule({
|
|
323
|
+
allowExplicitNonSource: options.sourceTransform !== undefined,
|
|
300
324
|
cache: options.cache,
|
|
301
325
|
importer: options.filename,
|
|
302
326
|
specifier: reference.source,
|
|
@@ -304,7 +328,11 @@ async function inferClientRouteModuleSource(options) {
|
|
|
304
328
|
if (resolved === undefined) {
|
|
305
329
|
continue;
|
|
306
330
|
}
|
|
307
|
-
const source = await
|
|
331
|
+
const source = await readClientRouteSource({
|
|
332
|
+
cache: options.cache,
|
|
333
|
+
filename: resolved,
|
|
334
|
+
sourceTransform: options.sourceTransform,
|
|
335
|
+
});
|
|
308
336
|
const imported = await inferClientRouteModuleSource({
|
|
309
337
|
cache: options.cache,
|
|
310
338
|
code: source,
|
|
@@ -316,6 +344,7 @@ async function inferClientRouteModuleSource(options) {
|
|
|
316
344
|
}),
|
|
317
345
|
root: false,
|
|
318
346
|
seen: options.seen,
|
|
347
|
+
sourceTransform: options.sourceTransform,
|
|
319
348
|
});
|
|
320
349
|
diagnostics.push(...imported.diagnostics);
|
|
321
350
|
if (!imported.client) {
|
|
@@ -364,6 +393,7 @@ async function inferClientRouteModuleSource(options) {
|
|
|
364
393
|
if (!options.root) {
|
|
365
394
|
for (const reference of analysis.staticExports) {
|
|
366
395
|
const resolved = await resolveAppLocalModule({
|
|
396
|
+
allowExplicitNonSource: options.sourceTransform !== undefined,
|
|
367
397
|
cache: options.cache,
|
|
368
398
|
importer: options.filename,
|
|
369
399
|
specifier: reference.source,
|
|
@@ -371,7 +401,11 @@ async function inferClientRouteModuleSource(options) {
|
|
|
371
401
|
if (resolved === undefined) {
|
|
372
402
|
continue;
|
|
373
403
|
}
|
|
374
|
-
const source = await
|
|
404
|
+
const source = await readClientRouteSource({
|
|
405
|
+
cache: options.cache,
|
|
406
|
+
filename: resolved,
|
|
407
|
+
sourceTransform: options.sourceTransform,
|
|
408
|
+
});
|
|
375
409
|
const exported = await inferClientRouteModuleSource({
|
|
376
410
|
cache: options.cache,
|
|
377
411
|
code: source,
|
|
@@ -383,6 +417,7 @@ async function inferClientRouteModuleSource(options) {
|
|
|
383
417
|
}),
|
|
384
418
|
root: false,
|
|
385
419
|
seen: options.seen,
|
|
420
|
+
sourceTransform: options.sourceTransform,
|
|
386
421
|
});
|
|
387
422
|
diagnostics.push(...exported.diagnostics);
|
|
388
423
|
if (exported.clientBoundaryModule) {
|
|
@@ -562,19 +597,27 @@ async function resolveAppLocalModule(options) {
|
|
|
562
597
|
if (!options.specifier.startsWith(".")) {
|
|
563
598
|
return undefined;
|
|
564
599
|
}
|
|
565
|
-
const cacheKey = `${options.importer}\0${options.specifier}`;
|
|
600
|
+
const cacheKey = `${options.importer}\0${options.specifier}\0${options.allowExplicitNonSource === true ? "explicit" : "source"}`;
|
|
566
601
|
const cached = options.cache.resolvedByImport.get(cacheKey);
|
|
567
602
|
if (cached !== undefined) {
|
|
568
603
|
return cached;
|
|
569
604
|
}
|
|
570
|
-
const resolved = resolveAppLocalModuleUncached(
|
|
605
|
+
const resolved = resolveAppLocalModuleUncached({
|
|
606
|
+
allowExplicitNonSource: options.allowExplicitNonSource === true,
|
|
607
|
+
importer: options.importer,
|
|
608
|
+
specifier: options.specifier,
|
|
609
|
+
});
|
|
571
610
|
options.cache.resolvedByImport.set(cacheKey, resolved);
|
|
572
611
|
return resolved;
|
|
573
612
|
}
|
|
574
|
-
async function resolveAppLocalModuleUncached(
|
|
613
|
+
async function resolveAppLocalModuleUncached(options) {
|
|
614
|
+
const { importer, specifier } = options;
|
|
575
615
|
const base = join(dirname(importer), specifier);
|
|
576
616
|
const candidates = sourceModuleCandidates(base);
|
|
577
617
|
if (candidates.length === 0) {
|
|
618
|
+
if (options.allowExplicitNonSource && extname(base) !== "" && (await isFile(base))) {
|
|
619
|
+
return base;
|
|
620
|
+
}
|
|
578
621
|
return undefined;
|
|
579
622
|
}
|
|
580
623
|
for (const candidate of candidates) {
|
|
@@ -608,6 +651,126 @@ async function readCachedFile(cache, filename) {
|
|
|
608
651
|
cache.sourceByFile.set(filename, source);
|
|
609
652
|
return (await source).source;
|
|
610
653
|
}
|
|
654
|
+
async function readClientRouteSource(options) {
|
|
655
|
+
if (options.sourceTransform === undefined) {
|
|
656
|
+
return readCachedFile(options.cache, options.filename);
|
|
657
|
+
}
|
|
658
|
+
const signature = await sourceFileSignature(options.filename);
|
|
659
|
+
const cacheKey = `${options.filename}\0${options.sourceTransform.cacheKey}`;
|
|
660
|
+
const cached = options.cache.transformedSourceByFile.get(cacheKey);
|
|
661
|
+
if (cached !== undefined) {
|
|
662
|
+
const source = await cached;
|
|
663
|
+
if (source.signature === signature) {
|
|
664
|
+
return source.source;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
const source = readCachedFile(options.cache, options.filename).then(async (code) => ({
|
|
668
|
+
signature,
|
|
669
|
+
source: await options.sourceTransform.transform(options.filename, code),
|
|
670
|
+
}));
|
|
671
|
+
options.cache.transformedSourceByFile.set(cacheKey, source);
|
|
672
|
+
return (await source).source;
|
|
673
|
+
}
|
|
674
|
+
async function transformClientRouteSource(options) {
|
|
675
|
+
return options.sourceTransform === undefined
|
|
676
|
+
? options.code
|
|
677
|
+
: await options.sourceTransform.transform(options.filename, options.code);
|
|
678
|
+
}
|
|
679
|
+
function clientRouteSourceTransformForVitePlugins(pluginOptions) {
|
|
680
|
+
const plugins = orderVitePlugins(flattenVitePlugins(pluginOptions)).filter((plugin) => hasViteTransformHook(plugin));
|
|
681
|
+
if (plugins.length === 0) {
|
|
682
|
+
return undefined;
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
cacheKey: plugins.map((plugin, index) => `${index}:${plugin.name}`).join("\0"),
|
|
686
|
+
async transform(filename, code) {
|
|
687
|
+
let nextCode = code;
|
|
688
|
+
for (const plugin of plugins) {
|
|
689
|
+
const handler = viteTransformHookHandler(plugin);
|
|
690
|
+
if (handler === undefined) {
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
const result = await handler.call(createClientRouteViteTransformContext(plugin.name), nextCode, filename, { ssr: false });
|
|
694
|
+
if (typeof result === "string") {
|
|
695
|
+
nextCode = result;
|
|
696
|
+
}
|
|
697
|
+
else if (result !== null && result !== undefined && typeof result === "object") {
|
|
698
|
+
const codeResult = result.code;
|
|
699
|
+
if (typeof codeResult === "string") {
|
|
700
|
+
nextCode = codeResult;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return nextCode;
|
|
705
|
+
},
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
function flattenVitePlugins(pluginOptions) {
|
|
709
|
+
const plugins = [];
|
|
710
|
+
const visit = (option) => {
|
|
711
|
+
if (option === false || option === null || option === undefined) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (Array.isArray(option)) {
|
|
715
|
+
for (const child of option) {
|
|
716
|
+
visit(child);
|
|
717
|
+
}
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
if (typeof option === "object" && "then" in option) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
plugins.push(option);
|
|
724
|
+
};
|
|
725
|
+
for (const option of pluginOptions ?? []) {
|
|
726
|
+
visit(option);
|
|
727
|
+
}
|
|
728
|
+
return plugins;
|
|
729
|
+
}
|
|
730
|
+
function orderVitePlugins(plugins) {
|
|
731
|
+
return [
|
|
732
|
+
...plugins.filter((plugin) => plugin.enforce === "pre"),
|
|
733
|
+
...plugins.filter((plugin) => plugin.enforce !== "pre" && plugin.enforce !== "post"),
|
|
734
|
+
...plugins.filter((plugin) => plugin.enforce === "post"),
|
|
735
|
+
];
|
|
736
|
+
}
|
|
737
|
+
function hasViteTransformHook(plugin) {
|
|
738
|
+
return viteTransformHookHandler(plugin) !== undefined;
|
|
739
|
+
}
|
|
740
|
+
function viteTransformHookHandler(plugin) {
|
|
741
|
+
const transform = plugin.transform;
|
|
742
|
+
if (typeof transform === "function") {
|
|
743
|
+
return transform;
|
|
744
|
+
}
|
|
745
|
+
if (transform !== null && typeof transform === "object") {
|
|
746
|
+
const handler = transform.handler;
|
|
747
|
+
if (typeof handler === "function") {
|
|
748
|
+
return handler;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return undefined;
|
|
752
|
+
}
|
|
753
|
+
function createClientRouteViteTransformContext(pluginName) {
|
|
754
|
+
return {
|
|
755
|
+
addWatchFile() { },
|
|
756
|
+
async resolve() {
|
|
757
|
+
return null;
|
|
758
|
+
},
|
|
759
|
+
error(error) {
|
|
760
|
+
if (error instanceof Error) {
|
|
761
|
+
throw error;
|
|
762
|
+
}
|
|
763
|
+
throw new Error(String(error));
|
|
764
|
+
},
|
|
765
|
+
warn() { },
|
|
766
|
+
getCombinedSourcemap() {
|
|
767
|
+
return null;
|
|
768
|
+
},
|
|
769
|
+
parse() {
|
|
770
|
+
throw new Error(`${pluginName}: client route import analysis cannot provide Rollup parser context.`);
|
|
771
|
+
},
|
|
772
|
+
};
|
|
773
|
+
}
|
|
611
774
|
async function sourceFileSignature(filename) {
|
|
612
775
|
const stats = await stat(filename);
|
|
613
776
|
return `${stats.mtimeMs}\0${stats.size}`;
|
|
@@ -685,6 +848,7 @@ export async function buildClientRouteOutput(options) {
|
|
|
685
848
|
});
|
|
686
849
|
const compiled = transformCompilerModuleContext({
|
|
687
850
|
code: options.code,
|
|
851
|
+
clientBoundaryImports: options.clientBoundaryImports ?? [],
|
|
688
852
|
filename: options.filename,
|
|
689
853
|
moduleContext,
|
|
690
854
|
target: "client",
|
|
@@ -697,13 +861,16 @@ export async function buildClientRouteOutput(options) {
|
|
|
697
861
|
}
|
|
698
862
|
const clientNavigation = options.clientNavigation ?? detectClientNavigationHint(options.code);
|
|
699
863
|
const clientReferenceManifest = options.clientReferenceManifest ?? (await inferClientReferenceManifestForBundle(options));
|
|
864
|
+
const compatClientReferenceNames = compatClientReferenceComponentNames(clientReferenceManifest);
|
|
700
865
|
const clientReferenceImportBlock = emitClientReferenceImportBlock(options.clientReferenceImports ?? []);
|
|
701
|
-
const clientReferenceRegistry = emitClientReferenceRegistry(clientReferenceManifest, options.clientReferenceImports ?? []);
|
|
866
|
+
const clientReferenceRegistry = emitClientReferenceRegistry(clientReferenceManifest, options.clientReferenceImports ?? [], compatClientReferenceNames);
|
|
702
867
|
const routeComponentExpression = routeComponentExpressionForComponents(compiled.metadata.components);
|
|
703
868
|
const routeId = routeIdForPath(options.routePath);
|
|
704
869
|
const routeUsesCells = detectRouteCellStateHint(compiled.code);
|
|
870
|
+
const routeUsesReactiveEffect = detectRouteReactiveEffectHint(compiled.code);
|
|
871
|
+
const routeUsesCleanupScope = routeUsesCells || routeUsesReactiveEffect;
|
|
705
872
|
const routeHasEventBindings = (compiled.metadata.eventHydrationManifest?.events.length ?? 0) > 0;
|
|
706
|
-
const routeRequiresFullHydration = routeUsesCells || routeHasEventBindings;
|
|
873
|
+
const routeRequiresFullHydration = routeUsesCells || routeUsesReactiveEffect || routeHasEventBindings;
|
|
707
874
|
const routeUsesOnlyClientReferenceBoundaries = !routeRequiresFullHydration &&
|
|
708
875
|
clientReferenceManifest.length > 0 &&
|
|
709
876
|
(options.clientReferenceImports?.length ?? 0) > 0;
|
|
@@ -715,6 +882,9 @@ export async function buildClientRouteOutput(options) {
|
|
|
715
882
|
const routeCellEffectImport = routeUsesCells
|
|
716
883
|
? `import { effect as __mreactRouteEffect } from "@reckona/mreact-reactive-core";\n`
|
|
717
884
|
: "";
|
|
885
|
+
const routeCleanupScopeImport = routeUsesCleanupScope
|
|
886
|
+
? `import { withCleanupScope as __mreactWithCleanupScope } from "@reckona/mreact-reactive-core/internal";\n`
|
|
887
|
+
: "";
|
|
718
888
|
const navigationStateDeclaration = clientNavigation
|
|
719
889
|
? `const __mreactNavigationState = __mreactGlobal.__mreactNavigationState ??= {
|
|
720
890
|
cache: new Map(),
|
|
@@ -737,6 +907,9 @@ export async function buildClientRouteOutput(options) {
|
|
|
737
907
|
let __mreactActiveCellRecords = undefined;
|
|
738
908
|
let __mreactActiveCellIndex = 0;`
|
|
739
909
|
: "";
|
|
910
|
+
const routeCleanupStateDeclaration = routeUsesCleanupScope
|
|
911
|
+
? `const __mreactRouteDisposers = __mreactGlobal.__mreactRouteDisposers ??= new Map();`
|
|
912
|
+
: "";
|
|
740
913
|
const routeCellHook = routeUsesCells
|
|
741
914
|
? `
|
|
742
915
|
__mreactGlobal.__mreactRouteCell = (nativeCell, initial) => {
|
|
@@ -778,8 +951,10 @@ __mreactGlobal.__mreactRouteCell = (nativeCell, initial) => {
|
|
|
778
951
|
__mreactState.dispose?.();
|
|
779
952
|
|
|
780
953
|
__mreactState.dispose = __mreactRouteEffect(() => {
|
|
954
|
+
const __mreactRouteEffectDisposers = new Set();
|
|
781
955
|
__mreactActiveCellRecords = __mreactState.cells;
|
|
782
956
|
__mreactActiveCellIndex = 0;
|
|
957
|
+
__mreactRouteDisposers.set(__mreactRouteId, () => __mreactState.dispose?.());
|
|
783
958
|
|
|
784
959
|
try {
|
|
785
960
|
`
|
|
@@ -789,10 +964,27 @@ __mreactGlobal.__mreactRouteCell = (nativeCell, initial) => {
|
|
|
789
964
|
__mreactActiveCellRecords = undefined;
|
|
790
965
|
__mreactActiveCellIndex = 0;
|
|
791
966
|
}
|
|
967
|
+
return () => {
|
|
968
|
+
for (const __mreactDispose of Array.from(__mreactRouteEffectDisposers)) {
|
|
969
|
+
__mreactDispose();
|
|
970
|
+
}
|
|
971
|
+
__mreactRouteEffectDisposers.clear();
|
|
972
|
+
};
|
|
792
973
|
});
|
|
793
974
|
`
|
|
794
975
|
: "";
|
|
795
976
|
const routeCellHydrationIndent = routeUsesCells ? " " : " ";
|
|
977
|
+
const routeCleanupHydrationStart = routeUsesCleanupScope && !routeUsesCells
|
|
978
|
+
? ` __mreactDisposeRoute(__mreactRouteId);
|
|
979
|
+
const __mreactRouteEffectDisposers = new Set();
|
|
980
|
+
__mreactRouteDisposers.set(__mreactRouteId, () => {
|
|
981
|
+
for (const __mreactDispose of Array.from(__mreactRouteEffectDisposers)) {
|
|
982
|
+
__mreactDispose();
|
|
983
|
+
}
|
|
984
|
+
__mreactRouteEffectDisposers.clear();
|
|
985
|
+
});
|
|
986
|
+
`
|
|
987
|
+
: "";
|
|
796
988
|
const routeCellDropFunction = routeUsesCells
|
|
797
989
|
? `
|
|
798
990
|
function __mreactDropMismatchedRouteState(previousState, nextState) {
|
|
@@ -804,6 +996,24 @@ function __mreactDropMismatchedRouteState(previousState, nextState) {
|
|
|
804
996
|
console.warn("mreact: dropping stale route state after route cell signature changed");
|
|
805
997
|
}
|
|
806
998
|
}
|
|
999
|
+
`
|
|
1000
|
+
: "";
|
|
1001
|
+
const routeCleanupFunction = routeUsesCleanupScope
|
|
1002
|
+
? `
|
|
1003
|
+
function __mreactDisposeRoute(routeId) {
|
|
1004
|
+
const __mreactDispose = __mreactRouteDisposers.get(routeId);
|
|
1005
|
+
if (__mreactDispose === undefined) {
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
__mreactRouteDisposers.delete(routeId);
|
|
1009
|
+
__mreactDispose();
|
|
1010
|
+
}
|
|
1011
|
+
`
|
|
1012
|
+
: "";
|
|
1013
|
+
const routeCleanupNavigationDispose = routeUsesCleanupScope
|
|
1014
|
+
? ` if (currentRouteId !== nextRouteId) {
|
|
1015
|
+
__mreactDisposeRoute(currentRouteId);
|
|
1016
|
+
}
|
|
807
1017
|
`
|
|
808
1018
|
: "";
|
|
809
1019
|
const routeNodeResolver = routeUsesCells
|
|
@@ -830,9 +1040,12 @@ function __mreactResolveRouteNode(value) {
|
|
|
830
1040
|
}
|
|
831
1041
|
`
|
|
832
1042
|
: "";
|
|
833
|
-
const
|
|
834
|
-
? "
|
|
1043
|
+
const routeComponentCallExpression = routeUsesCleanupScope
|
|
1044
|
+
? "__mreactWithCleanupScope((__mreactDispose) => __mreactRouteEffectDisposers.add(__mreactDispose), () => __mreactComponent(__mreactProps))"
|
|
835
1045
|
: "__mreactComponent(__mreactProps)";
|
|
1046
|
+
const routeNodeExpression = routeUsesCells
|
|
1047
|
+
? `__mreactResolveRouteNode(${routeComponentCallExpression})`
|
|
1048
|
+
: routeComponentCallExpression;
|
|
836
1049
|
const boundaryOnlyHydrationBlock = routeRequiresFullHydration
|
|
837
1050
|
? ""
|
|
838
1051
|
: `${routeCellHydrationIndent}if (!__mreactHasNonSerializableClientBoundaries(__mreactMarker) && __mreactHydrateClientBoundaries(document, __mreactClientReferences, __mreactClientReferenceComponents)) {
|
|
@@ -844,13 +1057,14 @@ ${routeCellHydrationIndent}}
|
|
|
844
1057
|
${routeCellHydrationIndent} return;
|
|
845
1058
|
${routeCellHydrationIndent}}
|
|
846
1059
|
`;
|
|
847
|
-
const entry = `${routeCellEffectImport}${clientReferenceImportBlock}${routeHydrationCode}
|
|
1060
|
+
const entry = `${routeCellEffectImport}${routeCleanupScopeImport}${emitCompatClientReferenceImportBlock(compatClientReferenceNames)}${clientReferenceImportBlock}${routeHydrationCode}
|
|
848
1061
|
|
|
849
1062
|
const __mreactRouteId = ${JSON.stringify(routeId)};
|
|
850
1063
|
const __mreactRouteStateSignature = ${JSON.stringify(routeStateSignature)};
|
|
851
1064
|
const __mreactGlobal = globalThis;
|
|
852
1065
|
${navigationStateDeclaration}
|
|
853
1066
|
${routeCellStateDeclaration}
|
|
1067
|
+
${routeCleanupStateDeclaration}
|
|
854
1068
|
${routeCellHook}
|
|
855
1069
|
${clientReferenceRegistry}
|
|
856
1070
|
${routeNodeResolver}
|
|
@@ -873,11 +1087,13 @@ export function __mreactHydrateRoute() {
|
|
|
873
1087
|
if (__mreactMarker === null) {
|
|
874
1088
|
return;
|
|
875
1089
|
}
|
|
876
|
-
${routeCellHydrationStart}${boundaryOnlyHydrationBlock}${routeComponentGuard}${routeCellHydrationIndent}const __mreactNode = ${routeNodeExpression};
|
|
1090
|
+
${routeCellHydrationStart}${routeCleanupHydrationStart}${boundaryOnlyHydrationBlock}${routeComponentGuard}${routeCellHydrationIndent}const __mreactNode = ${routeNodeExpression};
|
|
877
1091
|
${routeCellHydrationIndent}__mreactResumeRoute(__mreactMarker, __mreactNode);
|
|
1092
|
+
${routeCellHydrationIndent}__mreactHydrateClientBoundaries(__mreactMarker, __mreactClientReferences, __mreactClientReferenceComponents);
|
|
878
1093
|
${routeCellHydrationIndent}__mreactMarker.setAttribute("data-mreact-hydrated", "true");
|
|
879
1094
|
${routeCellHydrationEnd}}
|
|
880
1095
|
${routeCellDropFunction}
|
|
1096
|
+
${routeCleanupFunction}
|
|
881
1097
|
|
|
882
1098
|
__mreactHydrateRoute();
|
|
883
1099
|
${clientNavigation ? "__mreactInstallNavigation();" : ""}
|
|
@@ -1272,7 +1488,7 @@ function __mreactApplyNavigationHtml(html, url) {
|
|
|
1272
1488
|
|
|
1273
1489
|
__mreactSyncHeadMetadata(template.content, html);
|
|
1274
1490
|
__mreactResumeNode(currentMarker, nextMarker);
|
|
1275
|
-
__mreactSyncRouteDataScripts(template.content, currentRouteId, nextRouteId);
|
|
1491
|
+
${routeCleanupNavigationDispose} __mreactSyncRouteDataScripts(template.content, currentRouteId, nextRouteId);
|
|
1276
1492
|
|
|
1277
1493
|
const script = template.content.querySelector('script[type="module"][src]')?.getAttribute("src");
|
|
1278
1494
|
if (script !== null && script !== undefined) {
|
|
@@ -1748,7 +1964,9 @@ function __mreactHydrateClientBoundaries(marker, references, components) {
|
|
|
1748
1964
|
|
|
1749
1965
|
for (const placeholder of placeholders) {
|
|
1750
1966
|
const name = placeholder.getAttribute("data-mreact-client-boundary");
|
|
1751
|
-
const
|
|
1967
|
+
const entry = name === null ? undefined : components.get(name);
|
|
1968
|
+
const component = typeof entry === "function" ? entry : entry?.component;
|
|
1969
|
+
const compat = entry?.compat === true;
|
|
1752
1970
|
|
|
1753
1971
|
if (typeof component !== "function") {
|
|
1754
1972
|
return false;
|
|
@@ -1758,6 +1976,17 @@ function __mreactHydrateClientBoundaries(marker, references, components) {
|
|
|
1758
1976
|
const props = propsElement?.textContent === undefined || propsElement.textContent === ""
|
|
1759
1977
|
? {}
|
|
1760
1978
|
: JSON.parse(propsElement.textContent);
|
|
1979
|
+
|
|
1980
|
+
if (compat) {
|
|
1981
|
+
const container = document.createElement("span");
|
|
1982
|
+
container.setAttribute("data-mreact-compat-boundary", name ?? "");
|
|
1983
|
+
container.style.display = "contents";
|
|
1984
|
+
placeholder.replaceWith(container);
|
|
1985
|
+
__mreactCompatCreateRoot(container).render(__mreactCompatCreateElement(component, props));
|
|
1986
|
+
propsElement?.remove();
|
|
1987
|
+
continue;
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1761
1990
|
const node = component(props);
|
|
1762
1991
|
|
|
1763
1992
|
placeholder.replaceWith(node);
|
|
@@ -2180,6 +2409,7 @@ function __mreactResumeChildren(current, next) {
|
|
|
2180
2409
|
preserveExports: true,
|
|
2181
2410
|
plugins: [workspaceRuntimePlugin({ routeFile: options.filename })],
|
|
2182
2411
|
sourceMap: options.sourceMap,
|
|
2412
|
+
vitePlugins: options.vitePlugins,
|
|
2183
2413
|
});
|
|
2184
2414
|
return {
|
|
2185
2415
|
code: bundled.code,
|
|
@@ -2196,6 +2426,10 @@ function workspaceRuntimePlugin(options) {
|
|
|
2196
2426
|
const reactiveCorePath = packageFile("reactive-core", "@reckona/mreact-reactive-core", "index");
|
|
2197
2427
|
const reactiveCoreDir = dirname(reactiveCorePath);
|
|
2198
2428
|
const runtimePaths = new Map([
|
|
2429
|
+
[
|
|
2430
|
+
"@reckona/mreact-reactive-core/internal",
|
|
2431
|
+
packageFile("reactive-core", "@reckona/mreact-reactive-core", "internal"),
|
|
2432
|
+
],
|
|
2199
2433
|
["@reckona/mreact-compat", packageFile("react-compat", "@reckona/mreact-compat", "index")],
|
|
2200
2434
|
[
|
|
2201
2435
|
"@reckona/mreact-compat/event-priority",
|
|
@@ -2237,7 +2471,7 @@ function workspaceRuntimePlugin(options) {
|
|
|
2237
2471
|
path: "reactive-core",
|
|
2238
2472
|
}));
|
|
2239
2473
|
buildApi.onResolve({
|
|
2240
|
-
filter: /^@reckona\/mreact-(?:compat|reactive-dom)(?:\/(?:event-priority|flight|internal|jsx-dev-runtime|jsx-runtime|scheduler))?$/,
|
|
2474
|
+
filter: /^@reckona\/mreact-(?:compat|reactive-core|reactive-dom)(?:\/(?:event-priority|flight|internal|jsx-dev-runtime|jsx-runtime|scheduler))?$/,
|
|
2241
2475
|
}, (args) => {
|
|
2242
2476
|
const path = runtimePaths.get(args.path);
|
|
2243
2477
|
return path === undefined ? undefined : { path };
|
|
@@ -2271,6 +2505,7 @@ export function currentDevtoolsEmitter() { return undefined; }`,
|
|
|
2271
2505
|
code: source,
|
|
2272
2506
|
dev: true,
|
|
2273
2507
|
filename: args.path,
|
|
2508
|
+
mode: isCompatSourcePath(args.path) ? "compat" : "reactive",
|
|
2274
2509
|
moduleContext,
|
|
2275
2510
|
target: "client",
|
|
2276
2511
|
});
|
|
@@ -2291,6 +2526,9 @@ export function currentDevtoolsEmitter() { return undefined; }`,
|
|
|
2291
2526
|
function isRouteClientDependencySourcePath(path, routeFile) {
|
|
2292
2527
|
return path !== routeFile && !path.includes(`${sep}node_modules${sep}`);
|
|
2293
2528
|
}
|
|
2529
|
+
function isCompatSourcePath(path) {
|
|
2530
|
+
return /\.compat(?:\.mreact)?(?:\.[cm]?[jt]sx?)?$/.test(path);
|
|
2531
|
+
}
|
|
2294
2532
|
/**
|
|
2295
2533
|
* Detects the `export const clientNavigation = false` hint in a page module
|
|
2296
2534
|
* source. Returns the hinted value, or `true` when no hint is present (i.e.,
|
|
@@ -2316,6 +2554,10 @@ function detectRouteCellStateHint(code) {
|
|
|
2316
2554
|
? /\bcell\d*\s*\(/.test(code)
|
|
2317
2555
|
: new RegExp(`(?:${callExpression})\\s*\\(`).test(code);
|
|
2318
2556
|
}
|
|
2557
|
+
function detectRouteReactiveEffectHint(code) {
|
|
2558
|
+
return (/from\s+["']@reckona\/mreact-reactive-core["']/.test(code) &&
|
|
2559
|
+
/\beffect\s*\(/.test(code));
|
|
2560
|
+
}
|
|
2319
2561
|
async function inferClientReferenceManifestForBundle(options) {
|
|
2320
2562
|
const cache = createClientRouteInferenceCache();
|
|
2321
2563
|
const moduleContext = await compilerModuleContextForSource({
|
|
@@ -2356,7 +2598,7 @@ function emitClientReferenceImportBlock(imports) {
|
|
|
2356
2598
|
})
|
|
2357
2599
|
.join("\n")}\n`;
|
|
2358
2600
|
}
|
|
2359
|
-
function emitClientReferenceRegistry(manifest, imports) {
|
|
2601
|
+
function emitClientReferenceRegistry(manifest, imports, compatNames) {
|
|
2360
2602
|
const importedExpressions = new Map(imports.map((reference, index) => [
|
|
2361
2603
|
reference.name,
|
|
2362
2604
|
isIdentifierName(reference.exportName)
|
|
@@ -2367,10 +2609,24 @@ function emitClientReferenceRegistry(manifest, imports) {
|
|
|
2367
2609
|
const expression = importedExpressions.get(reference.name) ?? clientReferenceExpression(reference.name);
|
|
2368
2610
|
return expression === undefined
|
|
2369
2611
|
? []
|
|
2370
|
-
: [
|
|
2612
|
+
: [
|
|
2613
|
+
compatNames.has(reference.name)
|
|
2614
|
+
? ` [${JSON.stringify(reference.name)}, { component: ${expression}, compat: true }],`
|
|
2615
|
+
: ` [${JSON.stringify(reference.name)}, ${expression}],`,
|
|
2616
|
+
];
|
|
2371
2617
|
});
|
|
2372
2618
|
return ["const __mreactClientReferenceComponents = new Map([", ...entries, "]);"].join("\n");
|
|
2373
2619
|
}
|
|
2620
|
+
function compatClientReferenceComponentNames(manifest) {
|
|
2621
|
+
return new Set(manifest
|
|
2622
|
+
.filter((reference) => isCompatSourcePath(reference.moduleId))
|
|
2623
|
+
.map((reference) => reference.name));
|
|
2624
|
+
}
|
|
2625
|
+
function emitCompatClientReferenceImportBlock(compatNames) {
|
|
2626
|
+
return compatNames.size === 0
|
|
2627
|
+
? ""
|
|
2628
|
+
: 'import { createElement as __mreactCompatCreateElement, createRoot as __mreactCompatCreateRoot } from "@reckona/mreact-compat";\n';
|
|
2629
|
+
}
|
|
2374
2630
|
function clientReferenceLocalName(index) {
|
|
2375
2631
|
return `__mreactClientReference${index}`;
|
|
2376
2632
|
}
|