@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.120 → 3.2.0-ultramodern.121

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.
Files changed (94) hide show
  1. package/dist/cjs/cli/index.js +47 -27
  2. package/dist/cjs/cli/routeSplitting.js +0 -32
  3. package/dist/cjs/cli/tanstackTypes.js +34 -199
  4. package/dist/cjs/runtime/hooks.js +11 -14
  5. package/dist/cjs/runtime/index.js +107 -319
  6. package/dist/cjs/runtime/lifecycle.js +12 -86
  7. package/dist/cjs/runtime/loaderBridge.js +173 -0
  8. package/dist/cjs/runtime/plugin.js +6 -30
  9. package/dist/cjs/runtime/plugin.node.js +7 -29
  10. package/dist/cjs/runtime/pluginCore.js +55 -0
  11. package/dist/cjs/runtime/register.js +56 -0
  12. package/dist/cjs/runtime/routeTree.js +10 -207
  13. package/dist/cjs/runtime/{DefaultNotFound.js → router.js} +5 -15
  14. package/dist/cjs/runtime/rsc/payloadRouter.js +35 -1
  15. package/dist/cjs/runtime/state.js +45 -0
  16. package/dist/cjs/runtime/utils.js +0 -5
  17. package/dist/esm/cli/index.mjs +52 -26
  18. package/dist/esm/cli/routeSplitting.mjs +1 -30
  19. package/dist/esm/cli/tanstackTypes.mjs +32 -194
  20. package/dist/esm/runtime/hooks.mjs +1 -8
  21. package/dist/esm/runtime/index.mjs +4 -2
  22. package/dist/esm/runtime/lifecycle.mjs +1 -82
  23. package/dist/esm/runtime/loaderBridge.mjs +114 -0
  24. package/dist/esm/runtime/plugin.mjs +8 -32
  25. package/dist/esm/runtime/plugin.node.mjs +10 -32
  26. package/dist/esm/runtime/pluginCore.mjs +14 -0
  27. package/dist/esm/runtime/register.mjs +18 -0
  28. package/dist/esm/runtime/routeTree.mjs +4 -198
  29. package/dist/esm/runtime/router.mjs +2 -0
  30. package/dist/esm/runtime/rsc/payloadRouter.mjs +35 -1
  31. package/dist/esm/runtime/state.mjs +7 -0
  32. package/dist/esm/runtime/utils.mjs +0 -5
  33. package/dist/esm-node/cli/index.mjs +52 -26
  34. package/dist/esm-node/cli/routeSplitting.mjs +1 -30
  35. package/dist/esm-node/cli/tanstackTypes.mjs +32 -194
  36. package/dist/esm-node/runtime/hooks.mjs +1 -8
  37. package/dist/esm-node/runtime/index.mjs +4 -2
  38. package/dist/esm-node/runtime/lifecycle.mjs +1 -82
  39. package/dist/esm-node/runtime/loaderBridge.mjs +115 -0
  40. package/dist/esm-node/runtime/plugin.mjs +8 -32
  41. package/dist/esm-node/runtime/plugin.node.mjs +10 -32
  42. package/dist/esm-node/runtime/pluginCore.mjs +15 -0
  43. package/dist/esm-node/runtime/register.mjs +19 -0
  44. package/dist/esm-node/runtime/routeTree.mjs +4 -198
  45. package/dist/esm-node/runtime/router.mjs +3 -0
  46. package/dist/esm-node/runtime/rsc/payloadRouter.mjs +35 -1
  47. package/dist/esm-node/runtime/state.mjs +8 -0
  48. package/dist/esm-node/runtime/utils.mjs +0 -5
  49. package/dist/types/cli/index.d.ts +9 -2
  50. package/dist/types/cli/routeSplitting.d.ts +6 -15
  51. package/dist/types/cli/tanstackTypes.d.ts +13 -2
  52. package/dist/types/runtime/hooks.d.ts +8 -18
  53. package/dist/types/runtime/index.d.ts +6 -4
  54. package/dist/types/runtime/lifecycle.d.ts +7 -22
  55. package/dist/types/runtime/loaderBridge.d.ts +48 -0
  56. package/dist/types/runtime/plugin.d.ts +1 -14
  57. package/dist/types/runtime/plugin.node.d.ts +1 -14
  58. package/dist/types/runtime/pluginCore.d.ts +21 -0
  59. package/dist/types/runtime/register.d.ts +9 -0
  60. package/dist/types/runtime/routeTree.d.ts +0 -2
  61. package/dist/types/runtime/router.d.ts +14 -0
  62. package/dist/types/runtime/state.d.ts +16 -0
  63. package/dist/types/runtime/types.d.ts +7 -53
  64. package/package.json +30 -28
  65. package/rstest.config.mts +6 -0
  66. package/src/cli/index.ts +111 -29
  67. package/src/cli/routeSplitting.ts +6 -44
  68. package/src/cli/tanstackTypes.ts +78 -214
  69. package/src/runtime/hooks.ts +10 -27
  70. package/src/runtime/index.tsx +12 -107
  71. package/src/runtime/lifecycle.ts +16 -151
  72. package/src/runtime/loaderBridge.ts +257 -0
  73. package/src/runtime/plugin.node.tsx +14 -77
  74. package/src/runtime/plugin.tsx +12 -72
  75. package/src/runtime/pluginCore.ts +48 -0
  76. package/src/runtime/register.ts +58 -0
  77. package/src/runtime/routeTree.ts +8 -370
  78. package/src/runtime/router.ts +15 -0
  79. package/src/runtime/rsc/payloadRouter.ts +45 -2
  80. package/src/runtime/state.ts +29 -0
  81. package/src/runtime/types.ts +20 -67
  82. package/src/runtime/utils.tsx +3 -6
  83. package/tests/router/cli.test.ts +297 -31
  84. package/tests/router/hooks.test.ts +26 -0
  85. package/tests/router/loaderBridge.test.ts +211 -0
  86. package/tests/router/packageSurface.test.ts +24 -0
  87. package/tests/router/register.test.ts +46 -0
  88. package/tests/router/routeTree.test.ts +65 -180
  89. package/tests/router/rsc.test.tsx +70 -0
  90. package/tests/router/tanstackTypes.test.ts +164 -6
  91. package/dist/esm/runtime/DefaultNotFound.mjs +0 -13
  92. package/dist/esm-node/runtime/DefaultNotFound.mjs +0 -14
  93. package/dist/types/runtime/DefaultNotFound.d.ts +0 -2
  94. package/src/runtime/DefaultNotFound.tsx +0 -15
@@ -1,18 +1,7 @@
1
1
  import "node:module";
2
- import { findExists, formatImportPath, fs, slash } from "@modern-js/utils";
2
+ import { getPathWithoutExt, makeLegalIdentifier } from "@modern-js/runtime/cli";
3
+ import { findExists, formatImportPath, slash } from "@modern-js/utils";
3
4
  import path from "path";
4
- const reservedWords = 'break case class catch const continue debugger default delete do else export extends finally for function if import in instanceof let new return super switch this throw try typeof var void while with yield enum await implements package protected static interface private public';
5
- const builtins = 'arguments Infinity NaN undefined null true false eval uneval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Symbol Error EvalError InternalError RangeError ReferenceError SyntaxError TypeError URIError Number Math Date String RegExp Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Float32Array Float64Array Map Set WeakMap WeakSet SIMD ArrayBuffer DataView JSON Promise Generator GeneratorFunction Reflect Proxy Intl';
6
- const forbidList = new Set(`${reservedWords} ${builtins}`.split(' '));
7
- function makeLegalIdentifier(str) {
8
- const identifier = str.replace(/-(\w)/g, (_, letter)=>letter.toUpperCase()).replace(/[^$_a-zA-Z0-9]/g, '_');
9
- if (/\d/.test(identifier[0]) || forbidList.has(identifier)) return `_${identifier}`;
10
- return identifier || '_';
11
- }
12
- function getPathWithoutExt(filename) {
13
- const extname = path.extname(filename);
14
- return extname ? filename.slice(0, -extname.length) : filename;
15
- }
16
5
  const JS_OR_TS_EXTS = [
17
6
  '.js',
18
7
  '.jsx',
@@ -109,7 +98,8 @@ function paramsTypeForCanonicalPath(canonicalPath) {
109
98
  }
110
99
  return fields.length > 0 ? `{ ${fields.join('; ')} }` : 'Record<string, never>';
111
100
  }
112
- function collectCanonicalRoutesForEntry(routes) {
101
+ function collectCanonicalRoutesForEntry(routes, options = {}) {
102
+ const { localeParamHeuristic = true } = options;
113
103
  const canonicalParams = new Map();
114
104
  let hasI18nSurface = false;
115
105
  const normalizeJoined = (joined)=>{
@@ -129,7 +119,7 @@ function collectCanonicalRoutesForEntry(routes) {
129
119
  currentPath = normalizeJoined(route.modernCanonicalPath);
130
120
  } else if ('string' == typeof route.path && route.path.length > 0) {
131
121
  const segments = route.path.replace(/\[(.+?)\]/g, ':$1').split('/').filter(Boolean);
132
- if ('' === parentPath && LOCALE_PARAM_SEGMENTS.has(segments[0])) {
122
+ if (localeParamHeuristic && '' === parentPath && LOCALE_PARAM_SEGMENTS.has(segments[0])) {
133
123
  hasI18nSurface = true;
134
124
  segments.shift();
135
125
  }
@@ -150,17 +140,6 @@ function collectCanonicalRoutesForEntry(routes) {
150
140
  ...canonicalParams.entries()
151
141
  ].sort(([a], [b])=>a.localeCompare(b)));
152
142
  }
153
- async function isTanstackRouterFrameworkEnabled(appContext) {
154
- const runtimeConfigBase = path.join(appContext.srcDirectory, appContext.runtimeConfigFile);
155
- const runtimeConfigFile = findExists(JS_OR_TS_EXTS.map((ext)=>`${runtimeConfigBase}${ext}`));
156
- if (!runtimeConfigFile) return false;
157
- try {
158
- const content = await fs.readFile(runtimeConfigFile, 'utf-8');
159
- return /framework\s*:\s*['"]tanstack['"]/.test(content);
160
- } catch {
161
- return false;
162
- }
163
- }
164
143
  async function generateTanstackRouterTypesSourceForEntry(opts) {
165
144
  const { appContext, entryName, generatedDirName = 'modern-tanstack', routes } = opts;
166
145
  const outDir = path.join(appContext.srcDirectory, generatedDirName, entryName);
@@ -169,12 +148,30 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
169
148
  const imports = [];
170
149
  const statements = [];
171
150
  const loaderImportMap = new Map();
151
+ const componentImportMap = new Map();
172
152
  const searchContractImportMap = new Map();
173
153
  const usedRouteVarNames = new Set();
174
154
  let loaderIndex = 0;
155
+ let componentIndex = 0;
175
156
  let validateSearchIndex = 0;
176
157
  let loaderDepsIndex = 0;
177
158
  let routeIndex = 0;
159
+ const getImportNameForComponent = (componentPath)=>{
160
+ if ('string' != typeof componentPath || 0 === componentPath.length) return Promise.resolve(null);
161
+ let pendingImportName = componentImportMap.get(componentPath);
162
+ if (!pendingImportName) {
163
+ pendingImportName = (async ()=>{
164
+ const resolvedNoExt = await resolveRouteModuleNoExt(componentPath);
165
+ if (!resolvedNoExt) return null;
166
+ const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
167
+ const componentName = `component_${componentIndex++}`;
168
+ imports.push(`import ${componentName} from ${quote(relImport)};`);
169
+ return componentName;
170
+ })();
171
+ componentImportMap.set(componentPath, pendingImportName);
172
+ }
173
+ return pendingImportName;
174
+ };
178
175
  const resolveRouteModuleNoExt = async (aliasedNoExtPath)=>{
179
176
  const prefix = `${appContext.internalSrcAlias}/`;
180
177
  let absNoExt;
@@ -249,6 +246,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
249
246
  const routeOpts = [
250
247
  `getParentRoute: () => ${parentVar},`
251
248
  ];
249
+ const componentName = await getImportNameForComponent(route._component);
250
+ if (componentName) routeOpts.push(`component: ${componentName},`);
252
251
  if (isPathlessLayout(route)) {
253
252
  const id = route.id;
254
253
  routeOpts.push(`id: ${quote(id || 'pathless')},`);
@@ -291,6 +290,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
291
290
  route
292
291
  })));
293
292
  const rootOpts = [];
293
+ const rootComponentName = await getImportNameForComponent(rootModern?._component);
294
+ if (rootComponentName) rootOpts.push(`component: ${rootComponentName},`);
294
295
  if (rootLoaderName) rootOpts.push(`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`);
295
296
  if (rootValidateSearchName) rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
296
297
  if (rootLoaderDepsName) rootOpts.push(`loaderDeps: ${rootLoaderDepsName},`);
@@ -299,178 +300,15 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
299
300
 
300
301
  import {
301
302
  createMemoryHistory,
302
- modernTanstackRouterFastDefaults,
303
303
  createRootRouteWithContext,
304
304
  createRoute,
305
305
  createRouter,
306
- notFound,
307
- redirect,
306
+ createRouteStaticData,
307
+ type ModernRouterContext,
308
+ modernLoaderToTanstack,
309
+ modernTanstackRouterFastDefaults,
308
310
  } from '@modern-js/plugin-tanstack/runtime';
309
311
 
310
- type ModernRouterContext = {
311
- request?: Request;
312
- requestContext?: unknown;
313
- };
314
-
315
- function isResponse(value: unknown): value is Response {
316
- return (
317
- value != null &&
318
- typeof value === 'object' &&
319
- typeof (value as any).status === 'number' &&
320
- typeof (value as any).headers === 'object'
321
- );
322
- }
323
-
324
- const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
325
- function isRedirectResponse(res: Response) {
326
- return redirectStatusCodes.has(res.status);
327
- }
328
-
329
- function throwTanstackRedirect(location: string) {
330
- const target = location.length > 0 ? location : '/';
331
- try {
332
- void new URL(target);
333
- throw redirect({ href: target });
334
- } catch {
335
- throw redirect({ to: target });
336
- }
337
- }
338
-
339
- function mapParamsForModernLoader(params: Record<string, string>, hasSplat: boolean) {
340
- if (!hasSplat) {
341
- return params;
342
- }
343
-
344
- const { _splat, ...rest } = params as any;
345
- if (typeof _splat !== 'undefined') {
346
- return { ...rest, '*': _splat };
347
- }
348
- return rest;
349
- }
350
-
351
- function createRouteStaticData(opts: {
352
- modernRouteId?: string;
353
- modernRouteAction?: unknown;
354
- modernRouteLoader?: unknown;
355
- }) {
356
- const staticData: {
357
- modernRouteId?: string;
358
- modernRouteAction?: unknown;
359
- modernRouteLoader?: unknown;
360
- } = {};
361
-
362
- if (typeof opts.modernRouteId === 'string' && opts.modernRouteId.length > 0) {
363
- staticData.modernRouteId = opts.modernRouteId;
364
- }
365
-
366
- if (typeof opts.modernRouteLoader !== 'undefined') {
367
- staticData.modernRouteLoader = opts.modernRouteLoader;
368
- }
369
-
370
- if (typeof opts.modernRouteAction !== 'undefined') {
371
- staticData.modernRouteAction = opts.modernRouteAction;
372
- }
373
-
374
- return staticData;
375
- }
376
-
377
- function getLoaderSignal(ctx: any): AbortSignal {
378
- const abortSignal = ctx?.abortController?.signal;
379
- if (abortSignal instanceof AbortSignal) {
380
- return abortSignal;
381
- }
382
- if (ctx?.signal instanceof AbortSignal) {
383
- return ctx.signal;
384
- }
385
- return new AbortController().signal;
386
- }
387
-
388
- function getLoaderHref(ctx: any): string {
389
- if (typeof ctx?.location === 'string') {
390
- return ctx.location;
391
- }
392
-
393
- const publicHref = ctx?.location?.publicHref;
394
- if (typeof publicHref === 'string') {
395
- return publicHref;
396
- }
397
-
398
- const href = ctx?.location?.href;
399
- if (typeof href === 'string') {
400
- return href;
401
- }
402
-
403
- const urlHref = ctx?.location?.url?.href;
404
- return typeof urlHref === 'string' ? urlHref : '';
405
- }
406
-
407
- function getLoaderParams(ctx: any): Record<string, string> {
408
- return typeof ctx?.params === 'object' && ctx.params !== null ? ctx.params : {};
409
- }
410
-
411
- function handleModernLoaderResult<LoaderResult>(result: LoaderResult): LoaderResult {
412
- if (isResponse(result)) {
413
- if (isRedirectResponse(result)) {
414
- const location = result.headers.get('Location') ?? '/';
415
- throwTanstackRedirect(location);
416
- }
417
- if (result.status === 404) {
418
- throw notFound();
419
- }
420
- }
421
-
422
- return result;
423
- }
424
-
425
- function handleModernLoaderError(err: unknown): never {
426
- if (isResponse(err)) {
427
- if (isRedirectResponse(err)) {
428
- const location = err.headers.get('Location') ?? '/';
429
- throwTanstackRedirect(location);
430
- }
431
- if (err.status === 404) {
432
- throw notFound();
433
- }
434
- }
435
-
436
- throw err;
437
- }
438
-
439
- function modernLoaderToTanstack<TLoader extends (args: any) => any>(
440
- opts: { hasSplat: boolean },
441
- modernLoader: TLoader,
442
- ) {
443
- type LoaderResult = Awaited<ReturnType<TLoader>>;
444
-
445
- return (ctx: any): Promise<LoaderResult> => {
446
- try {
447
- const signal = getLoaderSignal(ctx);
448
- const baseRequest: Request | undefined =
449
- ctx?.context?.request instanceof Request ? ctx.context.request : undefined;
450
-
451
- const href = getLoaderHref(ctx);
452
-
453
- const request = baseRequest !== undefined
454
- ? new Request(baseRequest, { signal })
455
- : new Request(href, { signal });
456
-
457
- const params = mapParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
458
-
459
- return Promise.resolve(
460
- (modernLoader as any)({
461
- request,
462
- params,
463
- context: ctx?.context?.requestContext,
464
- }),
465
- )
466
- .then((result: LoaderResult) => handleModernLoaderResult(result))
467
- .catch(handleModernLoaderError);
468
- } catch (err) {
469
- handleModernLoaderError(err);
470
- }
471
- };
472
- }
473
-
474
312
  ${imports.join('\n')}
475
313
 
476
314
  export const rootRoute = createRootRouteWithContext<ModernRouterContext>()({
@@ -499,4 +337,4 @@ export const router = createRouter({
499
337
  routerGenTs
500
338
  };
501
339
  }
502
- export { collectCanonicalRoutesForEntry, generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled };
340
+ export { collectCanonicalRoutesForEntry, generateTanstackRouterTypesSourceForEntry };
@@ -1,9 +1,2 @@
1
1
  import "node:module";
2
- import { createSyncHook } from "@modern-js/plugin";
3
- const modifyRoutes = createSyncHook();
4
- const onBeforeCreateRoutes = createSyncHook();
5
- const onBeforeCreateRouter = createSyncHook();
6
- const onAfterCreateRouter = createSyncHook();
7
- const onBeforeHydrateRouter = createSyncHook();
8
- const onAfterHydrateRouter = createSyncHook();
9
- export { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter };
2
+ export { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter, routerProviderRegistryHooks } from "@modern-js/runtime/context";
@@ -1,8 +1,10 @@
1
1
  import "node:module";
2
- export { Asset, Await, Block, CatchBoundary, CatchNotFound, ClientOnly, DEFAULT_PROTOCOL_ALLOWLIST, DefaultGlobalNotFound, ErrorComponent, FileRoute, FileRouteLoader, HeadContent, LazyRoute, Match, MatchRoute, Matches, Navigate, NotFoundRoute, RootRoute, Route, RouteApi, Router, RouterContextProvider, RouterProvider, ScriptOnce, Scripts, ScrollRestoration, SearchParamError, cleanPath, composeRewrites, createBrowserHistory, createControlledPromise, createFileRoute, createHashHistory, createHistory, createLazyFileRoute, createLazyRoute, createLink, createMemoryHistory, createRootRoute, createRootRouteWithContext, createRoute, createRouteMask, createRouter, createRouterConfig, createSerializationAdapter, deepEqual, defaultParseSearch, defaultStringifySearch, defer, functionalUpdate, getRouteApi, interpolatePath, isMatch, isNotFound, isPlainArray, isPlainObject, isRedirect, joinPaths, lazyFn, lazyRouteComponent, linkOptions, notFound, parseSearchWith, reactUse, redirect, replaceEqualDeep, resolvePath, retainSearchParams, rootRouteId, rootRouteWithContext, stringifySearchWith, stripSearchParams, trimPath, trimPathLeft, trimPathRight, useAwaited, useBlocker, useCanGoBack, useChildMatches, useElementScrollRestoration, useHydrated, useLayoutEffect, useLinkProps, useLoaderData, useLoaderDeps, useLocation, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useParentMatches, useRouteContext, useRouter, useRouterState, useSearch, useTags } from "@tanstack/react-router";
2
+ import "./register.mjs";
3
+ export * from "@tanstack/react-router";
3
4
  export { Form, RouteActionResponseError, useFetcher } from "./dataMutation.mjs";
5
+ export { createRouteStaticData, isAbsoluteUrl, modernLoaderToTanstack, throwTanstackRedirect } from "./loaderBridge.mjs";
4
6
  export { Outlet } from "./outlet.mjs";
5
7
  export { tanstackRouterPlugin as default, tanstackRouterPlugin } from "./plugin.mjs";
6
8
  export { Link, NavLink } from "./prefetchLink.mjs";
7
- export { CompositeComponent } from "./rsc/client.mjs";
9
+ export { getTanstackRouterState } from "./state.mjs";
8
10
  export { getModernTanstackRouterFastDefaults, modernTanstackRouterFastDefaults } from "./types.mjs";
@@ -1,83 +1,2 @@
1
1
  import "node:module";
2
- function toHydrationScripts(state) {
3
- if (state.hydrationScripts?.length) return state.hydrationScripts;
4
- return state.hydrationScript ? [
5
- state.hydrationScript
6
- ] : void 0;
7
- }
8
- function getMatchedRouteIdsFromMatches(matches) {
9
- const routeIds = matches?.map((match)=>match.assetRouteId ?? match.routeId).filter((routeId)=>'string' == typeof routeId);
10
- return routeIds?.length ? routeIds : void 0;
11
- }
12
- function createRouterServerSnapshot(state) {
13
- const hydrationScripts = toHydrationScripts(state);
14
- const matchedRouteIds = state.matchedRouteIds ?? getMatchedRouteIdsFromMatches(state.matches);
15
- return {
16
- ...state,
17
- ...hydrationScripts?.length ? {
18
- hydrationScript: state.hydrationScript ?? hydrationScripts[0],
19
- hydrationScripts
20
- } : {},
21
- ...matchedRouteIds ? {
22
- matchedRouteIds
23
- } : {}
24
- };
25
- }
26
- function createRouterRuntimeState(state) {
27
- const hasSnapshotState = Boolean(state.serverSnapshot) || Boolean(state.hydrationScript) || Boolean(state.hydrationScripts?.length) || Boolean(state.matchedRouteIds?.length) || Boolean(state.matches?.length);
28
- const serverSnapshot = state.serverSnapshot ? createRouterServerSnapshot({
29
- ...state.serverSnapshot,
30
- framework: state.serverSnapshot.framework ?? state.framework,
31
- basename: state.serverSnapshot.basename ?? state.basename,
32
- hydrationScript: state.serverSnapshot.hydrationScript ?? state.hydrationScript,
33
- hydrationScripts: state.serverSnapshot.hydrationScripts ?? state.hydrationScripts,
34
- matchedRouteIds: state.serverSnapshot.matchedRouteIds ?? state.matchedRouteIds,
35
- matches: state.serverSnapshot.matches ?? state.matches
36
- }) : hasSnapshotState ? createRouterServerSnapshot({
37
- framework: state.framework,
38
- basename: state.basename,
39
- hydrationScript: state.hydrationScript,
40
- hydrationScripts: state.hydrationScripts,
41
- matchedRouteIds: state.matchedRouteIds,
42
- matches: state.matches
43
- }) : void 0;
44
- const hydrationScripts = toHydrationScripts({
45
- hydrationScript: state.hydrationScript ?? serverSnapshot?.hydrationScript,
46
- hydrationScripts: state.hydrationScripts ?? serverSnapshot?.hydrationScripts
47
- });
48
- const matchedRouteIds = state.matchedRouteIds ?? serverSnapshot?.matchedRouteIds ?? getMatchedRouteIdsFromMatches(state.matches);
49
- return {
50
- ...state,
51
- ...hydrationScripts?.length ? {
52
- hydrationScript: state.hydrationScript ?? hydrationScripts[0],
53
- hydrationScripts
54
- } : {},
55
- ...matchedRouteIds ? {
56
- matchedRouteIds
57
- } : {},
58
- ...serverSnapshot ? {
59
- serverSnapshot
60
- } : {}
61
- };
62
- }
63
- function applyRouterRuntimeState(runtimeContext, state) {
64
- const normalized = createRouterRuntimeState(state);
65
- const mutableRuntimeContext = runtimeContext;
66
- mutableRuntimeContext.routerFramework = normalized.framework;
67
- mutableRuntimeContext.routerInstance = normalized.instance;
68
- mutableRuntimeContext.routerHydrationScript = normalized.hydrationScript;
69
- mutableRuntimeContext.routerMatchedRouteIds = normalized.matchedRouteIds;
70
- mutableRuntimeContext.routerRuntime = normalized;
71
- if (normalized.serverSnapshot) mutableRuntimeContext.routerServerSnapshot = normalized.serverSnapshot;
72
- return runtimeContext;
73
- }
74
- function applyRouterServerPrepareResult(runtimeContext, result) {
75
- const state = createRouterRuntimeState({
76
- ...result.state,
77
- cleanup: result.cleanup ?? result.state.cleanup,
78
- serverSnapshot: result.snapshot ?? result.state.serverSnapshot
79
- });
80
- applyRouterRuntimeState(runtimeContext, state);
81
- return runtimeContext;
82
- }
83
- export { applyRouterRuntimeState, applyRouterServerPrepareResult, createRouterRuntimeState, createRouterServerSnapshot };
2
+ export { applyRouterRuntimeState, applyRouterServerPrepareResult, createRouterRuntimeState, createRouterServerSnapshot, getRouterRuntimeState, getRouterServerSnapshot } from "@modern-js/runtime/context";
@@ -0,0 +1,115 @@
1
+ import "node:module";
2
+ import { notFound, redirect } from "@tanstack/react-router";
3
+ function isResponse(value) {
4
+ const record = value;
5
+ return null != record && 'object' == typeof record && 'number' == typeof record.status && 'object' == typeof record.headers;
6
+ }
7
+ const redirectStatusCodes = new Set([
8
+ 301,
9
+ 302,
10
+ 303,
11
+ 307,
12
+ 308
13
+ ]);
14
+ function isRedirectResponse(res) {
15
+ return redirectStatusCodes.has(res.status);
16
+ }
17
+ function isTanstackRedirect(value) {
18
+ return isResponse(value) && 'object' == typeof value.options;
19
+ }
20
+ function isAbsoluteUrl(value) {
21
+ try {
22
+ new URL(value);
23
+ return true;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+ function throwTanstackRedirect(location) {
29
+ const target = location || '/';
30
+ if (isAbsoluteUrl(target)) throw redirect({
31
+ href: target
32
+ });
33
+ throw redirect({
34
+ to: target
35
+ });
36
+ }
37
+ function mapSplatParamsForModernLoader(params, hasSplat) {
38
+ if (!hasSplat) return params;
39
+ const { _splat, ...rest } = params;
40
+ if (void 0 !== _splat) return {
41
+ ...rest,
42
+ '*': _splat
43
+ };
44
+ return rest;
45
+ }
46
+ function createRouteStaticData(opts) {
47
+ const staticData = {};
48
+ if ('string' == typeof opts.modernRouteId && opts.modernRouteId.length > 0) staticData.modernRouteId = opts.modernRouteId;
49
+ if (void 0 !== opts.modernRouteLoader) staticData.modernRouteLoader = opts.modernRouteLoader;
50
+ if (void 0 !== opts.modernRouteAction) staticData.modernRouteAction = opts.modernRouteAction;
51
+ return staticData;
52
+ }
53
+ function getLoaderSignal(ctx) {
54
+ const abortSignal = ctx?.abortController?.signal;
55
+ if (abortSignal instanceof AbortSignal) return abortSignal;
56
+ if (ctx?.signal instanceof AbortSignal) return ctx.signal;
57
+ return new AbortController().signal;
58
+ }
59
+ function getLoaderHref(ctx) {
60
+ if ('string' == typeof ctx?.location) return ctx.location;
61
+ const publicHref = ctx?.location?.publicHref;
62
+ if ('string' == typeof publicHref) return publicHref;
63
+ const href = ctx?.location?.href;
64
+ if ('string' == typeof href) return href;
65
+ const urlHref = ctx?.location?.url?.href;
66
+ return 'string' == typeof urlHref ? urlHref : '';
67
+ }
68
+ function getLoaderParams(ctx) {
69
+ return 'object' == typeof ctx?.params && null !== ctx.params ? ctx.params : {};
70
+ }
71
+ function handleModernLoaderResult(result) {
72
+ if (isResponse(result)) {
73
+ if (isRedirectResponse(result)) {
74
+ const location = result.headers.get('Location') ?? '/';
75
+ throwTanstackRedirect(location);
76
+ }
77
+ if (404 === result.status) throw notFound();
78
+ }
79
+ return result;
80
+ }
81
+ function handleModernLoaderError(err) {
82
+ if (isResponse(err)) {
83
+ if (isTanstackRedirect(err)) throw err;
84
+ if (isRedirectResponse(err)) {
85
+ const location = err.headers.get('Location') ?? '/';
86
+ throwTanstackRedirect(location);
87
+ }
88
+ if (404 === err.status) throw notFound();
89
+ }
90
+ throw err;
91
+ }
92
+ function modernLoaderToTanstack(opts, modernLoader) {
93
+ return (rawCtx)=>{
94
+ const ctx = rawCtx;
95
+ try {
96
+ const signal = getLoaderSignal(ctx);
97
+ const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
98
+ const href = getLoaderHref(ctx);
99
+ const request = void 0 !== baseRequest ? new Request(baseRequest, {
100
+ signal
101
+ }) : new Request(href, {
102
+ signal
103
+ });
104
+ const params = mapSplatParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
105
+ return Promise.resolve(modernLoader({
106
+ request,
107
+ params,
108
+ context: ctx?.context?.requestContext
109
+ })).then((result)=>handleModernLoaderResult(result)).catch(handleModernLoaderError);
110
+ } catch (err) {
111
+ handleModernLoaderError(err);
112
+ }
113
+ };
114
+ }
115
+ export { createRouteStaticData, isAbsoluteUrl, isRedirectResponse, isResponse, isTanstackRedirect, mapSplatParamsForModernLoader, modernLoaderToTanstack, throwTanstackRedirect };
@@ -1,16 +1,16 @@
1
1
  import "node:module";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import { InternalRuntimeContext, getGlobalEnableRsc, getGlobalLayoutApp, getGlobalRoutes } from "@modern-js/runtime/context";
4
- import { merge } from "@modern-js/runtime-utils/merge";
3
+ import { InternalRuntimeContext, getGlobalEnableRsc } from "@modern-js/runtime/context";
5
4
  import { normalizePathname } from "@modern-js/runtime-utils/url";
6
5
  import { RouterProvider, createBrowserHistory, createHashHistory, createRouter, useLocation, useMatches, useNavigate, useRouter } from "@tanstack/react-router";
7
6
  import { hydrate } from "@tanstack/react-router/ssr/client";
8
7
  import { useContext, useMemo } from "react";
9
8
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
10
- import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
9
+ import { routerProviderRegistryHooks } from "./hooks.mjs";
11
10
  import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
12
11
  import { applyRouterRuntimeState } from "./lifecycle.mjs";
13
12
  import { withModernRouteMatchContext } from "./outlet.mjs";
13
+ import { getFinalRouteConfig, getMergedRouterConfig } from "./pluginCore.mjs";
14
14
  import { Link } from "./prefetchLink.mjs";
15
15
  import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
16
16
  import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
@@ -119,50 +119,26 @@ function ModernRouterClient({ router }) {
119
119
  router: router
120
120
  });
121
121
  }
122
- function stripSyntheticNotFoundRoute(routes) {
123
- return routes.filter((route)=>!('*' === route.path && !route.id && !route.loader)).map((route)=>{
124
- if (!route.children?.length) return route;
125
- return {
126
- ...route,
127
- children: stripSyntheticNotFoundRoute(route.children)
128
- };
129
- });
130
- }
131
122
  const tanstackRouterPlugin = (userConfig = {})=>{
132
123
  const plugin = {
133
124
  name: '@modern-js/plugin-router-tanstack',
134
- registryHooks: {
135
- modifyRoutes: modifyRoutes,
136
- onAfterCreateRouter: onAfterCreateRouter,
137
- onAfterHydrateRouter: onAfterHydrateRouter,
138
- onBeforeCreateRouter: onBeforeCreateRouter,
139
- onBeforeCreateRoutes: onBeforeCreateRoutes,
140
- onBeforeHydrateRouter: onBeforeHydrateRouter
141
- },
125
+ registryHooks: routerProviderRegistryHooks,
142
126
  setup: (api)=>{
143
127
  const hooks = api.getHooks();
144
128
  let cachedRouteObjects;
145
129
  let cachedRouteTree = null;
146
130
  let cachedRouter = null;
147
131
  let cachedRouterBasepath = null;
148
- const getMergedConfig = ()=>{
149
- const pluginConfig = api.getRuntimeConfig();
150
- return merge(pluginConfig.router || {}, userConfig);
151
- };
132
+ const getMergedConfig = ()=>getMergedRouterConfig(api, userConfig);
152
133
  const getRouteObjects = ()=>{
153
134
  if (void 0 !== cachedRouteObjects) return cachedRouteObjects;
154
135
  const mergedConfig = getMergedConfig();
155
- const { routesConfig, createRoutes } = mergedConfig;
156
- const finalRouteConfig = {
157
- routes: getGlobalRoutes(),
158
- globalApp: getGlobalLayoutApp(),
159
- ...routesConfig
160
- };
136
+ const { createRoutes } = mergedConfig;
137
+ const finalRouteConfig = getFinalRouteConfig(mergedConfig);
161
138
  const routeObjects = createRoutes ? createRoutes() : createRouteObjectsFromConfig({
162
139
  routesConfig: finalRouteConfig
163
140
  }) || [];
164
- const normalizedRouteObjects = createRoutes ? routeObjects : stripSyntheticNotFoundRoute(routeObjects);
165
- cachedRouteObjects = hooks.modifyRoutes.call(normalizedRouteObjects);
141
+ cachedRouteObjects = hooks.modifyRoutes.call(routeObjects);
166
142
  return cachedRouteObjects;
167
143
  };
168
144
  const getRouteTree = ()=>{