@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.9 → 3.2.0-ultramodern.90
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/cjs/cli/index.js +12 -0
- package/dist/cjs/cli/routeSplitting.js +83 -0
- package/dist/cjs/cli/tanstackTypes.js +146 -58
- package/dist/cjs/runtime/index.js +38 -6
- package/dist/cjs/runtime/plugin.js +6 -5
- package/dist/cjs/runtime/plugin.node.js +27 -10
- package/dist/cjs/runtime/plugin.worker.js +49 -0
- package/dist/cjs/runtime/routeTree.js +55 -4
- package/dist/cjs/runtime/types.js +27 -1
- package/dist/esm/cli/index.mjs +4 -1
- package/dist/esm/cli/routeSplitting.mjs +43 -0
- package/dist/esm/cli/tanstackTypes.mjs +146 -58
- package/dist/esm/runtime/index.mjs +2 -1
- package/dist/esm/runtime/plugin.mjs +10 -9
- package/dist/esm/runtime/plugin.node.mjs +28 -11
- package/dist/esm/runtime/plugin.worker.mjs +1 -0
- package/dist/esm/runtime/routeTree.mjs +55 -4
- package/dist/esm/runtime/types.mjs +7 -0
- package/dist/esm-node/cli/index.mjs +4 -1
- package/dist/esm-node/cli/routeSplitting.mjs +44 -0
- package/dist/esm-node/cli/tanstackTypes.mjs +146 -58
- package/dist/esm-node/runtime/index.mjs +2 -1
- package/dist/esm-node/runtime/plugin.mjs +10 -9
- package/dist/esm-node/runtime/plugin.node.mjs +28 -11
- package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
- package/dist/esm-node/runtime/routeTree.mjs +55 -4
- package/dist/esm-node/runtime/types.mjs +7 -0
- package/dist/types/cli/index.d.ts +4 -0
- package/dist/types/cli/routeSplitting.d.ts +29 -0
- package/dist/types/runtime/index.d.ts +3 -1
- package/dist/types/runtime/plugin.d.ts +1 -1
- package/dist/types/runtime/plugin.node.d.ts +1 -1
- package/dist/types/runtime/plugin.worker.d.ts +1 -0
- package/dist/types/runtime/types.d.ts +7 -0
- package/package.json +14 -14
- package/src/cli/index.ts +17 -0
- package/src/cli/routeSplitting.ts +81 -0
- package/src/cli/tanstackTypes.ts +216 -67
- package/src/runtime/index.tsx +13 -1
- package/src/runtime/plugin.node.tsx +54 -7
- package/src/runtime/plugin.tsx +8 -5
- package/src/runtime/plugin.worker.tsx +4 -0
- package/src/runtime/routeTree.ts +125 -8
- package/src/runtime/types.ts +13 -0
- package/tests/router/cli.test.ts +239 -0
- package/tests/router/fastDefaults.test.ts +25 -0
- package/tests/router/routeTree.test.ts +193 -1
- package/tests/router/tanstackTypes.test.ts +184 -0
|
@@ -55,6 +55,14 @@ function pickModernLoaderModule(route) {
|
|
|
55
55
|
inline
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
+
function pickRouteSearchContractModules(route) {
|
|
59
|
+
const validateSearchPath = route.validateSearch;
|
|
60
|
+
const loaderDepsPath = route.loaderDeps;
|
|
61
|
+
return {
|
|
62
|
+
validateSearchPath: 'string' == typeof validateSearchPath ? validateSearchPath : null,
|
|
63
|
+
loaderDepsPath: 'string' == typeof loaderDepsPath ? loaderDepsPath : null
|
|
64
|
+
};
|
|
65
|
+
}
|
|
58
66
|
function isPathlessLayout(route) {
|
|
59
67
|
return 'nested' === route.type && 'boolean' != typeof route.index && void 0 === route.path;
|
|
60
68
|
}
|
|
@@ -88,8 +96,21 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
88
96
|
const imports = [];
|
|
89
97
|
const statements = [];
|
|
90
98
|
const loaderImportMap = new Map();
|
|
99
|
+
const searchContractImportMap = new Map();
|
|
100
|
+
const usedRouteVarNames = new Set();
|
|
91
101
|
let loaderIndex = 0;
|
|
102
|
+
let validateSearchIndex = 0;
|
|
103
|
+
let loaderDepsIndex = 0;
|
|
92
104
|
let routeIndex = 0;
|
|
105
|
+
const resolveRouteModuleNoExt = async (aliasedNoExtPath)=>{
|
|
106
|
+
const prefix = `${appContext.internalSrcAlias}/`;
|
|
107
|
+
let absNoExt;
|
|
108
|
+
if (aliasedNoExtPath.startsWith(prefix)) {
|
|
109
|
+
const rel = aliasedNoExtPath.slice(prefix.length);
|
|
110
|
+
absNoExt = path.join(appContext.srcDirectory, rel);
|
|
111
|
+
} else absNoExt = path.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : path.join(appContext.srcDirectory, aliasedNoExtPath);
|
|
112
|
+
return resolveFileNoExt(absNoExt);
|
|
113
|
+
};
|
|
93
114
|
const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
|
|
94
115
|
const key = `${inline ? 'inline' : 'default'}:${hasAction ? 'action' : 'loader'}:${aliasedNoExtPath}`;
|
|
95
116
|
const existing = loaderImportMap.get(key);
|
|
@@ -97,13 +118,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
97
118
|
loaderName: existing,
|
|
98
119
|
actionName: hasAction ? existing.replace(/^loader_/, 'action_') : null
|
|
99
120
|
};
|
|
100
|
-
const
|
|
101
|
-
let absNoExt;
|
|
102
|
-
if (aliasedNoExtPath.startsWith(prefix)) {
|
|
103
|
-
const rel = aliasedNoExtPath.slice(prefix.length);
|
|
104
|
-
absNoExt = path.join(appContext.srcDirectory, rel);
|
|
105
|
-
} else absNoExt = path.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : path.join(appContext.srcDirectory, aliasedNoExtPath);
|
|
106
|
-
const resolvedNoExt = await resolveFileNoExt(absNoExt);
|
|
121
|
+
const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
|
|
107
122
|
if (!resolvedNoExt) return null;
|
|
108
123
|
const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
|
|
109
124
|
const importName = `loader_${loaderIndex++}`;
|
|
@@ -121,10 +136,29 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
121
136
|
actionName
|
|
122
137
|
};
|
|
123
138
|
};
|
|
139
|
+
const getImportNameForSearchContract = async (aliasedNoExtPath, exportName)=>{
|
|
140
|
+
const key = `${exportName}:${aliasedNoExtPath}`;
|
|
141
|
+
const existing = searchContractImportMap.get(key);
|
|
142
|
+
if (existing) return existing;
|
|
143
|
+
const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
|
|
144
|
+
if (!resolvedNoExt) return null;
|
|
145
|
+
const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
|
|
146
|
+
const importName = 'validateSearch' === exportName ? `validateSearch_${validateSearchIndex++}` : `loaderDeps_${loaderDepsIndex++}`;
|
|
147
|
+
imports.push(`import { ${exportName} as ${importName} } from ${quote(relImport)};`);
|
|
148
|
+
searchContractImportMap.set(key, importName);
|
|
149
|
+
return importName;
|
|
150
|
+
};
|
|
151
|
+
const reserveRouteVarName = (preferred)=>{
|
|
152
|
+
let candidate = preferred;
|
|
153
|
+
let suffix = 1;
|
|
154
|
+
while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
|
|
155
|
+
usedRouteVarNames.add(candidate);
|
|
156
|
+
return candidate;
|
|
157
|
+
};
|
|
124
158
|
const createRouteVarName = (route)=>{
|
|
125
159
|
const id = route.id;
|
|
126
160
|
const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
|
|
127
|
-
return `route_${base}
|
|
161
|
+
return reserveRouteVarName(`route_${base}`);
|
|
128
162
|
};
|
|
129
163
|
const buildRoute = async (opts)=>{
|
|
130
164
|
const { parentVar, route } = opts;
|
|
@@ -134,6 +168,9 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
134
168
|
const loaderImports = loaderInfo ? await getImportNamesForLoader(loaderInfo.loaderPath, loaderInfo.inline, Boolean(loaderInfo.inline && routeAction === loaderInfo.loaderPath)) : null;
|
|
135
169
|
const loaderName = loaderImports?.loaderName || null;
|
|
136
170
|
const actionName = loaderImports?.actionName || null;
|
|
171
|
+
const searchContractInfo = pickRouteSearchContractModules(route);
|
|
172
|
+
const validateSearchName = searchContractInfo.validateSearchPath ? await getImportNameForSearchContract(searchContractInfo.validateSearchPath, 'validateSearch') : null;
|
|
173
|
+
const loaderDepsName = searchContractInfo.loaderDepsPath ? await getImportNameForSearchContract(searchContractInfo.loaderDepsPath, 'loaderDeps') : null;
|
|
137
174
|
const rawPath = route.path;
|
|
138
175
|
const hasSplat = 'string' == typeof rawPath && rawPath.includes('*');
|
|
139
176
|
const routeOpts = [
|
|
@@ -147,20 +184,24 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
147
184
|
routeOpts.push(`path: ${quote(p)},`);
|
|
148
185
|
}
|
|
149
186
|
if (loaderName) routeOpts.push(`loader: modernLoaderToTanstack({ hasSplat: ${hasSplat} }, ${loaderName}),`);
|
|
187
|
+
if (validateSearchName) routeOpts.push(`validateSearch: ${validateSearchName},`);
|
|
188
|
+
if (loaderDepsName) routeOpts.push(`loaderDeps: ${loaderDepsName},`);
|
|
150
189
|
const staticDataSnippet = createRouteStaticDataSnippet({
|
|
151
190
|
modernRouteId: route.id,
|
|
152
191
|
loaderName,
|
|
153
192
|
actionName
|
|
154
193
|
});
|
|
155
194
|
if (staticDataSnippet) routeOpts.push(staticDataSnippet);
|
|
156
|
-
statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
|
|
157
195
|
const children = route.children;
|
|
196
|
+
const hasChildren = Boolean(children && children.length > 0);
|
|
197
|
+
const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
|
|
198
|
+
statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
|
|
158
199
|
if (children && children.length > 0) {
|
|
159
200
|
const childVars = await Promise.all(children.map((child)=>buildRoute({
|
|
160
|
-
parentVar:
|
|
201
|
+
parentVar: routeCtorVarName,
|
|
161
202
|
route: child
|
|
162
203
|
})));
|
|
163
|
-
statements.push(
|
|
204
|
+
statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
|
|
164
205
|
}
|
|
165
206
|
return varName;
|
|
166
207
|
};
|
|
@@ -169,17 +210,23 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
|
|
|
169
210
|
const rootLoaderImports = rootLoaderInfo?.loaderPath ? await getImportNamesForLoader(rootLoaderInfo.loaderPath, rootLoaderInfo.inline, Boolean(rootLoaderInfo.inline && rootAction === rootLoaderInfo.loaderPath)) : null;
|
|
170
211
|
const rootLoaderName = rootLoaderImports?.loaderName || null;
|
|
171
212
|
const rootActionName = rootLoaderImports?.actionName || null;
|
|
213
|
+
const rootSearchContractInfo = rootModern ? pickRouteSearchContractModules(rootModern) : null;
|
|
214
|
+
const rootValidateSearchName = rootSearchContractInfo?.validateSearchPath ? await getImportNameForSearchContract(rootSearchContractInfo.validateSearchPath, 'validateSearch') : null;
|
|
215
|
+
const rootLoaderDepsName = rootSearchContractInfo?.loaderDepsPath ? await getImportNameForSearchContract(rootSearchContractInfo.loaderDepsPath, 'loaderDeps') : null;
|
|
172
216
|
const topLevelVars = await Promise.all(topLevel.map((route)=>buildRoute({
|
|
173
217
|
parentVar: 'rootRoute',
|
|
174
218
|
route
|
|
175
219
|
})));
|
|
176
220
|
const rootOpts = [];
|
|
177
221
|
if (rootLoaderName) rootOpts.push(`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`);
|
|
222
|
+
if (rootValidateSearchName) rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
|
|
223
|
+
if (rootLoaderDepsName) rootOpts.push(`loaderDeps: ${rootLoaderDepsName},`);
|
|
178
224
|
const routerGenTs = `/* eslint-disable */
|
|
179
225
|
// This file is auto-generated by Modern.js. Do not edit manually.
|
|
180
226
|
|
|
181
227
|
import {
|
|
182
228
|
createMemoryHistory,
|
|
229
|
+
modernTanstackRouterFastDefaults,
|
|
183
230
|
createRootRouteWithContext,
|
|
184
231
|
createRoute,
|
|
185
232
|
createRouter,
|
|
@@ -207,7 +254,7 @@ function isRedirectResponse(res: Response) {
|
|
|
207
254
|
}
|
|
208
255
|
|
|
209
256
|
function throwTanstackRedirect(location: string) {
|
|
210
|
-
const target = location
|
|
257
|
+
const target = location.length > 0 ? location : '/';
|
|
211
258
|
try {
|
|
212
259
|
void new URL(target);
|
|
213
260
|
throw redirect({ href: target });
|
|
@@ -233,21 +280,87 @@ function createRouteStaticData(opts: {
|
|
|
233
280
|
modernRouteAction?: unknown;
|
|
234
281
|
modernRouteLoader?: unknown;
|
|
235
282
|
}) {
|
|
236
|
-
const staticData:
|
|
283
|
+
const staticData: {
|
|
284
|
+
modernRouteId?: string;
|
|
285
|
+
modernRouteAction?: unknown;
|
|
286
|
+
modernRouteLoader?: unknown;
|
|
287
|
+
} = {};
|
|
237
288
|
|
|
238
|
-
if (opts.modernRouteId) {
|
|
289
|
+
if (typeof opts.modernRouteId === 'string' && opts.modernRouteId.length > 0) {
|
|
239
290
|
staticData.modernRouteId = opts.modernRouteId;
|
|
240
291
|
}
|
|
241
292
|
|
|
242
|
-
if (opts.modernRouteLoader) {
|
|
293
|
+
if (typeof opts.modernRouteLoader !== 'undefined') {
|
|
243
294
|
staticData.modernRouteLoader = opts.modernRouteLoader;
|
|
244
295
|
}
|
|
245
296
|
|
|
246
|
-
if (opts.modernRouteAction) {
|
|
297
|
+
if (typeof opts.modernRouteAction !== 'undefined') {
|
|
247
298
|
staticData.modernRouteAction = opts.modernRouteAction;
|
|
248
299
|
}
|
|
249
300
|
|
|
250
|
-
return
|
|
301
|
+
return staticData;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function getLoaderSignal(ctx: any): AbortSignal {
|
|
305
|
+
const abortSignal = ctx?.abortController?.signal;
|
|
306
|
+
if (abortSignal instanceof AbortSignal) {
|
|
307
|
+
return abortSignal;
|
|
308
|
+
}
|
|
309
|
+
if (ctx?.signal instanceof AbortSignal) {
|
|
310
|
+
return ctx.signal;
|
|
311
|
+
}
|
|
312
|
+
return new AbortController().signal;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function getLoaderHref(ctx: any): string {
|
|
316
|
+
if (typeof ctx?.location === 'string') {
|
|
317
|
+
return ctx.location;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const publicHref = ctx?.location?.publicHref;
|
|
321
|
+
if (typeof publicHref === 'string') {
|
|
322
|
+
return publicHref;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const href = ctx?.location?.href;
|
|
326
|
+
if (typeof href === 'string') {
|
|
327
|
+
return href;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const urlHref = ctx?.location?.url?.href;
|
|
331
|
+
return typeof urlHref === 'string' ? urlHref : '';
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function getLoaderParams(ctx: any): Record<string, string> {
|
|
335
|
+
return typeof ctx?.params === 'object' && ctx.params !== null ? ctx.params : {};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function handleModernLoaderResult<LoaderResult>(result: LoaderResult): LoaderResult {
|
|
339
|
+
if (isResponse(result)) {
|
|
340
|
+
if (isRedirectResponse(result)) {
|
|
341
|
+
const location = result.headers.get('Location') ?? '/';
|
|
342
|
+
throwTanstackRedirect(location);
|
|
343
|
+
}
|
|
344
|
+
if (result.status === 404) {
|
|
345
|
+
throw notFound();
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return result;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function handleModernLoaderError(err: unknown): never {
|
|
353
|
+
if (isResponse(err)) {
|
|
354
|
+
if (isRedirectResponse(err)) {
|
|
355
|
+
const location = err.headers.get('Location') ?? '/';
|
|
356
|
+
throwTanstackRedirect(location);
|
|
357
|
+
}
|
|
358
|
+
if (err.status === 404) {
|
|
359
|
+
throw notFound();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
throw err;
|
|
251
364
|
}
|
|
252
365
|
|
|
253
366
|
function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
|
@@ -256,57 +369,31 @@ function modernLoaderToTanstack<TLoader extends (args: any) => any>(
|
|
|
256
369
|
) {
|
|
257
370
|
type LoaderResult = Awaited<ReturnType<TLoader>>;
|
|
258
371
|
|
|
259
|
-
return
|
|
372
|
+
return (ctx: any): Promise<LoaderResult> => {
|
|
260
373
|
try {
|
|
261
|
-
const signal
|
|
262
|
-
ctx?.abortController?.signal ||
|
|
263
|
-
ctx?.signal ||
|
|
264
|
-
new AbortController().signal;
|
|
374
|
+
const signal = getLoaderSignal(ctx);
|
|
265
375
|
const baseRequest: Request | undefined =
|
|
266
376
|
ctx?.context?.request instanceof Request ? ctx.context.request : undefined;
|
|
267
377
|
|
|
268
|
-
const href =
|
|
269
|
-
typeof ctx?.location === 'string'
|
|
270
|
-
? ctx.location
|
|
271
|
-
: ctx?.location?.publicHref ||
|
|
272
|
-
ctx?.location?.href ||
|
|
273
|
-
ctx?.location?.url?.href ||
|
|
274
|
-
'';
|
|
378
|
+
const href = getLoaderHref(ctx);
|
|
275
379
|
|
|
276
|
-
const request = baseRequest
|
|
380
|
+
const request = baseRequest !== undefined
|
|
277
381
|
? new Request(baseRequest, { signal })
|
|
278
382
|
: new Request(href, { signal });
|
|
279
383
|
|
|
280
|
-
const params = mapParamsForModernLoader(ctx
|
|
384
|
+
const params = mapParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
|
|
281
385
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
throwTanstackRedirect(location);
|
|
292
|
-
}
|
|
293
|
-
if (result.status === 404) {
|
|
294
|
-
throw notFound();
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
return result as LoaderResult;
|
|
386
|
+
return Promise.resolve(
|
|
387
|
+
(modernLoader as any)({
|
|
388
|
+
request,
|
|
389
|
+
params,
|
|
390
|
+
context: ctx?.context?.requestContext,
|
|
391
|
+
}),
|
|
392
|
+
)
|
|
393
|
+
.then((result: LoaderResult) => handleModernLoaderResult(result))
|
|
394
|
+
.catch(handleModernLoaderError);
|
|
299
395
|
} catch (err) {
|
|
300
|
-
|
|
301
|
-
if (isRedirectResponse(err)) {
|
|
302
|
-
const location = err.headers.get('Location') || '/';
|
|
303
|
-
throwTanstackRedirect(location);
|
|
304
|
-
}
|
|
305
|
-
if (err.status === 404) {
|
|
306
|
-
throw notFound();
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
throw err;
|
|
396
|
+
handleModernLoaderError(err);
|
|
310
397
|
}
|
|
311
398
|
};
|
|
312
399
|
}
|
|
@@ -327,6 +414,7 @@ ${statements.join('\n\n')}
|
|
|
327
414
|
export const routeTree = rootRoute.addChildren([${topLevelVars.join(', ')}]);
|
|
328
415
|
|
|
329
416
|
export const router = createRouter({
|
|
417
|
+
...modernTanstackRouterFastDefaults,
|
|
330
418
|
routeTree,
|
|
331
419
|
history: createMemoryHistory({
|
|
332
420
|
initialEntries: ['/'],
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from "@tanstack/react-router";
|
|
2
|
-
export { useMatch } from "@tanstack/react-router";
|
|
2
|
+
export { Outlet, useLocation, useMatch, useMatches, useNavigate, useRouter } from "@tanstack/react-router";
|
|
3
3
|
export { Form, RouteActionResponseError, useFetcher } from "./dataMutation.mjs";
|
|
4
4
|
export { tanstackRouterPlugin as default, tanstackRouterPlugin } from "./plugin.mjs";
|
|
5
5
|
export { Link, NavLink } from "./prefetchLink.mjs";
|
|
6
6
|
export { CompositeComponent } from "./rsc/client.mjs";
|
|
7
|
+
export { getModernTanstackRouterFastDefaults, modernTanstackRouterFastDefaults } from "./types.mjs";
|
|
@@ -4,13 +4,15 @@ import { merge } from "@modern-js/runtime-utils/merge";
|
|
|
4
4
|
import { normalizePathname } from "@modern-js/runtime-utils/url";
|
|
5
5
|
import { RouterProvider, createBrowserHistory, createHashHistory, createRouter, useLocation, useMatches, useNavigate, useRouter } from "@tanstack/react-router";
|
|
6
6
|
import { RouterClient } from "@tanstack/react-router/ssr/client";
|
|
7
|
+
import { useContext, useMemo } from "react";
|
|
7
8
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
8
9
|
import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
|
|
9
10
|
import { applyRouterRuntimeState } from "./lifecycle.mjs";
|
|
11
|
+
import { Link } from "./prefetchLink.mjs";
|
|
10
12
|
import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
|
|
11
13
|
import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
|
|
14
|
+
import { getModernTanstackRouterFastDefaults } from "./types.mjs";
|
|
12
15
|
import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
|
|
13
|
-
import * as __rspack_external_react from "react";
|
|
14
16
|
const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for('@modern-js/plugin-tanstack:blocking-subscribe');
|
|
15
17
|
const BLOCKING_STATE_SYMBOL = Symbol.for('@modern-js/plugin-tanstack:blocking-state');
|
|
16
18
|
function normalizeBase(b) {
|
|
@@ -73,6 +75,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
77
|
context.router = {
|
|
78
|
+
Link: Link,
|
|
76
79
|
useMatches: useMatches,
|
|
77
80
|
useLocation: useLocation,
|
|
78
81
|
useNavigate: useNavigate,
|
|
@@ -107,10 +110,10 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
107
110
|
let cachedRouter = null;
|
|
108
111
|
let cachedRouterBasepath = null;
|
|
109
112
|
const RouterWrapper = ()=>{
|
|
110
|
-
const runtimeContext =
|
|
113
|
+
const runtimeContext = useContext(InternalRuntimeContext);
|
|
111
114
|
const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
|
|
112
115
|
const _basename = '/' === baseUrl ? urlJoin(baseUrl, runtimeContext._internalRouterBaseName || basename || '') : baseUrl;
|
|
113
|
-
const routeTree =
|
|
116
|
+
const routeTree = useMemo(()=>{
|
|
114
117
|
if (cachedRouteTree) return cachedRouteTree;
|
|
115
118
|
const routeObjects = getRouteObjects();
|
|
116
119
|
if (!routeObjects.length) return null;
|
|
@@ -120,7 +123,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
120
123
|
return cachedRouteTree;
|
|
121
124
|
}, []);
|
|
122
125
|
if (!routeTree) return App ? /*#__PURE__*/ jsx(App, {}) : null;
|
|
123
|
-
const router =
|
|
126
|
+
const router = useMemo(()=>{
|
|
124
127
|
const lifecycleContext = {
|
|
125
128
|
framework: 'tanstack',
|
|
126
129
|
phase: 'client-create',
|
|
@@ -142,6 +145,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
142
145
|
const rewrite = createModernBasepathRewrite(_basename);
|
|
143
146
|
const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
|
|
144
147
|
cachedRouter = createRouter({
|
|
148
|
+
...getModernTanstackRouterFastDefaults(mergedConfig),
|
|
145
149
|
routeTree,
|
|
146
150
|
basepath: '/',
|
|
147
151
|
rewrite,
|
|
@@ -185,11 +189,8 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
185
189
|
router,
|
|
186
190
|
runtimeContext: runtimeState
|
|
187
191
|
});
|
|
188
|
-
const RouterContent = hasSSRBootstrap ? /*#__PURE__*/ jsx(
|
|
189
|
-
|
|
190
|
-
children: /*#__PURE__*/ jsx(RouterClient, {
|
|
191
|
-
router: router
|
|
192
|
-
})
|
|
192
|
+
const RouterContent = hasSSRBootstrap ? /*#__PURE__*/ jsx(RouterClient, {
|
|
193
|
+
router: router
|
|
193
194
|
}) : /*#__PURE__*/ jsx(RouterProvider, {
|
|
194
195
|
router: router
|
|
195
196
|
});
|
|
@@ -5,24 +5,41 @@ import { createRequestContext, storage } from "@modern-js/runtime-utils/node";
|
|
|
5
5
|
import { time } from "@modern-js/runtime-utils/time";
|
|
6
6
|
import { LOADER_REPORTER_NAME } from "@modern-js/utils/universal/constants";
|
|
7
7
|
import { RouterProvider, createMemoryHistory, createRouter } from "@tanstack/react-router";
|
|
8
|
-
import { attachRouterServerSsrUtils } from "@tanstack/
|
|
9
|
-
import {
|
|
8
|
+
import { attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server";
|
|
9
|
+
import { useContext } from "react";
|
|
10
10
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
11
11
|
import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
|
|
12
12
|
import { applyRouterServerPrepareResult, createRouterServerSnapshot } from "./lifecycle.mjs";
|
|
13
13
|
import { createRouteTreeFromRouteObjects, getModernRouteIdsFromMatches } from "./routeTree.mjs";
|
|
14
14
|
import { createTanstackRscServerPayload, handleTanstackRscRedirect } from "./rsc/payloadRouter.mjs";
|
|
15
|
+
import { getModernTanstackRouterFastDefaults } from "./types.mjs";
|
|
15
16
|
import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
|
|
16
17
|
const setTanstackRscServerPayload = (payload)=>{
|
|
17
18
|
const storageContext = storage.useContext?.();
|
|
18
19
|
if (storageContext) storageContext.serverPayload = payload;
|
|
19
20
|
};
|
|
21
|
+
function isPromiseLike(value) {
|
|
22
|
+
return Boolean(value && 'function' == typeof value.then);
|
|
23
|
+
}
|
|
20
24
|
function isPreloadableRouteComponent(component) {
|
|
21
25
|
if (!component || 'function' != typeof component) return false;
|
|
22
26
|
const preloadable = component;
|
|
23
27
|
return 'function' == typeof preloadable.load || 'function' == typeof preloadable.preload;
|
|
24
28
|
}
|
|
29
|
+
function isReactLazyRouteComponent(component) {
|
|
30
|
+
return Boolean(component) && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
|
|
31
|
+
}
|
|
32
|
+
async function preloadReactLazyRouteComponent(component) {
|
|
33
|
+
try {
|
|
34
|
+
component._init?.(component._payload);
|
|
35
|
+
} catch (thrown) {
|
|
36
|
+
if (!isPromiseLike(thrown)) throw thrown;
|
|
37
|
+
await thrown;
|
|
38
|
+
component._init?.(component._payload);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
25
41
|
async function preloadRouteComponent(component) {
|
|
42
|
+
if (isReactLazyRouteComponent(component)) return void await preloadReactLazyRouteComponent(component);
|
|
26
43
|
if (!isPreloadableRouteComponent(component)) return;
|
|
27
44
|
if ('function' == typeof component.load) return void await component.load({});
|
|
28
45
|
await component.preload?.({});
|
|
@@ -161,6 +178,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
161
178
|
};
|
|
162
179
|
hooks.onBeforeCreateRouter.call(routerLifecycleContext);
|
|
163
180
|
const tanstackRouter = createRouter({
|
|
181
|
+
...getModernTanstackRouterFastDefaults(mergedConfig),
|
|
164
182
|
routeTree,
|
|
165
183
|
history,
|
|
166
184
|
basepath: '/',
|
|
@@ -206,10 +224,12 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
206
224
|
await preloadMatchedRouteComponents(serverRouter);
|
|
207
225
|
context.ssrContext?.response.status(tanstackRouter.state.statusCode);
|
|
208
226
|
await serverRouter.serverSsr?.dehydrate?.();
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
227
|
+
if (isRSCNavigation) {
|
|
228
|
+
await waitForRouterSerialization(serverRouter);
|
|
229
|
+
setTanstackRscServerPayload(createTanstackRscServerPayload(serverRouter, {
|
|
230
|
+
omitClientLoaderData: true
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
213
233
|
const ssrScriptTags = serverRouter.serverSsr?.takeBufferedScripts?.();
|
|
214
234
|
const hydrationScripts = routerManagedTagsToHtml(ssrScriptTags);
|
|
215
235
|
const matchedRouteIds = getModernRouteIdsFromMatches(serverRouter);
|
|
@@ -247,11 +267,8 @@ const tanstackRouterPlugin = (userConfig = {})=>{
|
|
|
247
267
|
if (!router) return App ? /*#__PURE__*/ jsx(App, {
|
|
248
268
|
...props
|
|
249
269
|
}) : null;
|
|
250
|
-
const routerWrapper = /*#__PURE__*/ jsx(
|
|
251
|
-
|
|
252
|
-
children: /*#__PURE__*/ jsx(RouterProvider, {
|
|
253
|
-
router: router
|
|
254
|
-
})
|
|
270
|
+
const routerWrapper = /*#__PURE__*/ jsx(RouterProvider, {
|
|
271
|
+
router: router
|
|
255
272
|
});
|
|
256
273
|
return App ? /*#__PURE__*/ jsx(App, {
|
|
257
274
|
children: routerWrapper
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, tanstackRouterPlugin } from "./plugin.node.mjs";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRootRoute, createRoute, notFound, redirect } from "@tanstack/react-router";
|
|
2
|
+
import { createElement } from "react";
|
|
2
3
|
import { DefaultNotFound } from "./DefaultNotFound.mjs";
|
|
3
4
|
import { isTanstackRscPayloadNavigationEnabled, loadTanstackRscRouteData } from "./rsc/payloadRouter.mjs";
|
|
4
5
|
function createTanstackRoute(options) {
|
|
@@ -54,6 +55,40 @@ function normalizeModernLoaderResponse(result) {
|
|
|
54
55
|
}
|
|
55
56
|
return normalizeModernLoaderResult(result);
|
|
56
57
|
}
|
|
58
|
+
function pickRouteModuleComponent(routeModule, seen = new Set()) {
|
|
59
|
+
if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
|
|
60
|
+
if (!routeModule || 'object' != typeof routeModule) return;
|
|
61
|
+
if (seen.has(routeModule)) return;
|
|
62
|
+
seen.add(routeModule);
|
|
63
|
+
const module = routeModule;
|
|
64
|
+
for (const candidate of [
|
|
65
|
+
module.default,
|
|
66
|
+
module.Component
|
|
67
|
+
]){
|
|
68
|
+
const component = pickRouteModuleComponent(candidate, seen);
|
|
69
|
+
if (component) return component;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function createServerLazyImportComponent(lazyImport, fallbackComponent) {
|
|
73
|
+
if ("u" > typeof document) return fallbackComponent;
|
|
74
|
+
let resolvedComponent;
|
|
75
|
+
let pendingLoad;
|
|
76
|
+
const load = async ()=>{
|
|
77
|
+
if (resolvedComponent) return resolvedComponent;
|
|
78
|
+
const routeModule = await lazyImport();
|
|
79
|
+
const component = pickRouteModuleComponent(routeModule);
|
|
80
|
+
if (component) resolvedComponent = component;
|
|
81
|
+
return resolvedComponent;
|
|
82
|
+
};
|
|
83
|
+
const Component = (props)=>{
|
|
84
|
+
if (resolvedComponent) return createElement(resolvedComponent, props);
|
|
85
|
+
pendingLoad ||= load();
|
|
86
|
+
throw pendingLoad;
|
|
87
|
+
};
|
|
88
|
+
Component.load = load;
|
|
89
|
+
Component.preload = load;
|
|
90
|
+
return Component;
|
|
91
|
+
}
|
|
57
92
|
function isAbsoluteUrl(value) {
|
|
58
93
|
try {
|
|
59
94
|
new URL(value);
|
|
@@ -126,7 +161,7 @@ function wrapModernLoader(modernRoute, modernLoader, revalidationState, options
|
|
|
126
161
|
const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
|
|
127
162
|
const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
|
|
128
163
|
const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
|
|
129
|
-
const request = baseRequest ? new Request(baseRequest, {
|
|
164
|
+
const request = void 0 !== baseRequest ? new Request(baseRequest, {
|
|
130
165
|
signal
|
|
131
166
|
}) : createModernRequest(href, signal);
|
|
132
167
|
const params = mapParamsForModernLoader({
|
|
@@ -191,7 +226,7 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
|
|
|
191
226
|
const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
|
|
192
227
|
const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
|
|
193
228
|
const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
|
|
194
|
-
const request = baseRequest ? new Request(baseRequest, {
|
|
229
|
+
const request = void 0 !== baseRequest ? new Request(baseRequest, {
|
|
195
230
|
signal
|
|
196
231
|
}) : createModernRequest(href, signal);
|
|
197
232
|
const params = mapParamsForRouteObjectLoader({
|
|
@@ -228,10 +263,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
|
|
|
228
263
|
}
|
|
229
264
|
function toRouteComponent(routeObject) {
|
|
230
265
|
const route = routeObject;
|
|
266
|
+
const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
|
|
267
|
+
const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
|
|
268
|
+
if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
|
|
231
269
|
if (route.Component) return route.Component;
|
|
232
270
|
const element = route.element;
|
|
233
271
|
if (element) return ()=>element;
|
|
234
272
|
}
|
|
273
|
+
function toModernRouteComponent(route) {
|
|
274
|
+
const component = route.component || void 0;
|
|
275
|
+
if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
|
|
276
|
+
return component;
|
|
277
|
+
}
|
|
235
278
|
function toErrorComponent(routeObject) {
|
|
236
279
|
const route = routeObject;
|
|
237
280
|
if (route.ErrorBoundary) return route.ErrorBoundary;
|
|
@@ -274,6 +317,8 @@ function createRouteFromRouteObject(opts) {
|
|
|
274
317
|
component: toRouteComponent(routeObject),
|
|
275
318
|
pendingComponent: toPendingComponent(routeObject),
|
|
276
319
|
errorComponent: toErrorComponent(routeObject),
|
|
320
|
+
validateSearch: modernRouteObject.validateSearch,
|
|
321
|
+
loaderDeps: modernRouteObject.loaderDeps,
|
|
277
322
|
wrapInSuspense: true,
|
|
278
323
|
staticData: createRouteStaticData({
|
|
279
324
|
modernRouteId: routeObject.id,
|
|
@@ -312,7 +357,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
312
357
|
const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
|
|
313
358
|
const pendingComponent = route.loading || route.pendingComponent;
|
|
314
359
|
const errorComponent = route.error || route.errorComponent;
|
|
315
|
-
const component = route
|
|
360
|
+
const component = toModernRouteComponent(route);
|
|
316
361
|
const modernLoader = route.loader;
|
|
317
362
|
const modernAction = route.action;
|
|
318
363
|
const modernShouldRevalidate = route.shouldRevalidate;
|
|
@@ -324,6 +369,8 @@ function createRouteFromModernRoute(opts) {
|
|
|
324
369
|
component: component || void 0,
|
|
325
370
|
pendingComponent: pendingComponent || void 0,
|
|
326
371
|
errorComponent: errorComponent || void 0,
|
|
372
|
+
validateSearch: route.validateSearch,
|
|
373
|
+
loaderDeps: route.loaderDeps,
|
|
327
374
|
wrapInSuspense: true,
|
|
328
375
|
staticData: createRouteStaticData({
|
|
329
376
|
modernRouteId: modernId,
|
|
@@ -359,7 +406,7 @@ function createRouteFromModernRoute(opts) {
|
|
|
359
406
|
}
|
|
360
407
|
function createRouteTreeFromModernRoutes(routes, options = {}) {
|
|
361
408
|
const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
|
|
362
|
-
const rootComponent = rootModern
|
|
409
|
+
const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
|
|
363
410
|
const pendingComponent = rootModern?.loading;
|
|
364
411
|
const errorComponent = rootModern?.error;
|
|
365
412
|
const rootLoader = rootModern?.loader;
|
|
@@ -372,6 +419,8 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
|
|
|
372
419
|
component: rootComponent || void 0,
|
|
373
420
|
pendingComponent: pendingComponent || void 0,
|
|
374
421
|
errorComponent: errorComponent || void 0,
|
|
422
|
+
validateSearch: rootModern?.validateSearch,
|
|
423
|
+
loaderDeps: rootModern?.loaderDeps,
|
|
375
424
|
wrapInSuspense: true,
|
|
376
425
|
notFoundComponent: DefaultNotFound,
|
|
377
426
|
staticData: createRouteStaticData({
|
|
@@ -411,6 +460,8 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
|
|
|
411
460
|
component: rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0,
|
|
412
461
|
pendingComponent: rootLikeRoute ? toPendingComponent(rootLikeRoute) : void 0,
|
|
413
462
|
errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : void 0,
|
|
463
|
+
validateSearch: rootLikeRoute?.validateSearch,
|
|
464
|
+
loaderDeps: rootLikeRoute?.loaderDeps,
|
|
414
465
|
wrapInSuspense: true,
|
|
415
466
|
notFoundComponent: DefaultNotFound,
|
|
416
467
|
staticData: createRouteStaticData({
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const modernTanstackRouterFastDefaults = {
|
|
2
|
+
defaultStructuralSharing: true
|
|
3
|
+
};
|
|
4
|
+
const getModernTanstackRouterFastDefaults = (config = {})=>({
|
|
5
|
+
defaultStructuralSharing: config.defaultStructuralSharing ?? modernTanstackRouterFastDefaults.defaultStructuralSharing
|
|
6
|
+
});
|
|
7
|
+
export { getModernTanstackRouterFastDefaults, modernTanstackRouterFastDefaults };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import node_path from "node:path";
|
|
3
3
|
import { NESTED_ROUTE_SPEC_FILE, filterRoutesForServer, fs } from "@modern-js/utils";
|
|
4
|
+
import { createTanstackRsbuildRouteSplittingProfile, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled } from "./routeSplitting.mjs";
|
|
4
5
|
import { generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled } from "./tanstackTypes.mjs";
|
|
5
6
|
import { __webpack_require__ } from "../rslib-runtime.mjs";
|
|
6
7
|
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
@@ -82,6 +83,7 @@ async function writeTanstackRouterTypesForEntries(opts) {
|
|
|
82
83
|
function tanstackRouterPlugin(options = {}) {
|
|
83
84
|
const routesDir = options.routesDir || DEFAULT_ROUTES_DIR;
|
|
84
85
|
const generatedDirName = options.generatedDirName || DEFAULT_GENERATED_DIR_NAME;
|
|
86
|
+
const routeSplittingProfile = createTanstackRsbuildRouteSplittingProfile(options);
|
|
85
87
|
return {
|
|
86
88
|
name: '@modern-js/plugin-tanstack',
|
|
87
89
|
required: [
|
|
@@ -117,6 +119,7 @@ function tanstackRouterPlugin(options = {}) {
|
|
|
117
119
|
entry: entry || getRuntimeRouterCli().isRouteEntry(entryPath, routesDir)
|
|
118
120
|
}));
|
|
119
121
|
api.config(()=>({
|
|
122
|
+
...routeSplittingProfile.defaultConfig,
|
|
120
123
|
source: {
|
|
121
124
|
include: [
|
|
122
125
|
/[\\/]node_modules[\\/]@tanstack[\\/]react-router[\\/]/,
|
|
@@ -199,4 +202,4 @@ function tanstackRouterPlugin(options = {}) {
|
|
|
199
202
|
}
|
|
200
203
|
const src_cli = tanstackRouterPlugin;
|
|
201
204
|
export default src_cli;
|
|
202
|
-
export { generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
|
|
205
|
+
export { createTanstackRsbuildRouteSplittingProfile, generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
|