@octanejs/tanstack-router 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -11
- package/package.json +26 -11
- package/src/Asset.tsrx +122 -0
- package/src/Asset.tsrx.d.ts +9 -0
- package/src/Await.tsrx +2 -1
- package/src/Await.tsrx.d.ts +8 -6
- package/src/Body.ts +31 -0
- package/src/CatchBoundary.tsrx +1 -3
- package/src/ClientOnly.tsrx +4 -2
- package/src/Head.ts +22 -0
- package/src/HeadContent.tsrx +41 -0
- package/src/HeadContent.tsrx.d.ts +8 -0
- package/src/Html.ts +19 -0
- package/src/Link.tsrx +25 -7
- package/src/Link.tsrx.d.ts +3 -4
- package/src/Match.tsrx +40 -13
- package/src/Matches.tsrx +7 -7
- package/src/Outlet.tsrx +3 -3
- package/src/RouteNotFound.tsrx +1 -1
- package/src/RouterProvider.tsrx +2 -1
- package/src/ScriptOnce.tsrx +23 -0
- package/src/ScriptOnce.tsrx.d.ts +3 -0
- package/src/Scripts.tsrx +42 -0
- package/src/Scripts.tsrx.d.ts +3 -0
- package/src/Transitioner.tsrx +15 -13
- package/src/assetKeys.ts +11 -0
- package/src/context.ts +6 -3
- package/src/externalHydration.ts +77 -0
- package/src/fileRoute.ts +277 -0
- package/src/frameworkTypes.ts +42 -0
- package/src/generator-plugin.d.ts +20 -0
- package/src/generator-plugin.js +108 -0
- package/src/headContentUtils.ts +172 -0
- package/src/hooks.ts +151 -0
- package/src/index.ts +84 -4
- package/src/lazyRouteComponent.ts +2 -1
- package/src/link.ts +25 -4
- package/src/linkTypes.ts +95 -0
- package/src/not-found.tsrx +2 -2
- package/src/octane-compiler.d.ts +12 -0
- package/src/route.ts +438 -42
- package/src/routeHookTypes.ts +228 -0
- package/src/router.ts +31 -6
- package/src/scriptContentUtils.ts +64 -0
- package/src/scroll-restoration.tsrx +16 -0
- package/src/scroll-restoration.tsrx.d.ts +3 -0
- package/src/ssr/RouterClient.tsrx +36 -0
- package/src/ssr/RouterClient.tsrx.d.ts +4 -0
- package/src/ssr/RouterServer.tsrx +6 -0
- package/src/ssr/RouterServer.tsrx.d.ts +4 -0
- package/src/ssr/client.ts +4 -0
- package/src/ssr/defaultRenderHandler.ts +11 -0
- package/src/ssr/defaultStreamHandler.ts +12 -0
- package/src/ssr/renderRouterToStream.ts +195 -0
- package/src/ssr/renderRouterToString.ts +58 -0
- package/src/ssr/server.ts +8 -0
- package/src/structuralSharing.ts +41 -0
- package/src/typePrimitives.ts +77 -0
- package/src/useAwaited.ts +7 -2
- package/src/useBlocker.tsrx +2 -1
- package/src/useRouterState.ts +20 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { compileToVolarMappings } from 'octane/compiler/volar';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {object} AstNode
|
|
5
|
+
* @property {AstNode | Array<AstNode>} [body]
|
|
6
|
+
* @property {number} [start]
|
|
7
|
+
* @property {number} [end]
|
|
8
|
+
* @property {{ native_tsrx_body?: boolean }} [metadata]
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Makes TSRX route modules parseable by the router generator without changing
|
|
13
|
+
* source offsets. The generator applies edits to the original source, so the
|
|
14
|
+
* authored Octane template bodies remain byte-for-byte intact.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} source
|
|
17
|
+
* @param {string} [filename]
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
export function maskOctaneRouteSource(source, filename = 'route.tsrx') {
|
|
21
|
+
// Only .tsrx carries the native template dialect. Plain .ts/.tsx route
|
|
22
|
+
// files (robots.txt.ts-style server routes, shared route helpers) must
|
|
23
|
+
// pass through untouched — the TSRX parser is not a general TS parser and
|
|
24
|
+
// rejects valid TS it was never meant to see.
|
|
25
|
+
if (!filename.endsWith('.tsrx')) {
|
|
26
|
+
return source;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { sourceAst } = compileToVolarMappings(source, filename);
|
|
30
|
+
const output = source.split('');
|
|
31
|
+
|
|
32
|
+
for (const body of findNativeTemplateBodies(/** @type {AstNode} */ (sourceAst))) {
|
|
33
|
+
const { start, end } = body;
|
|
34
|
+
output[start] = ' ';
|
|
35
|
+
output[start + 1] = '{';
|
|
36
|
+
for (let index = start + 2; index < end - 1; index++) {
|
|
37
|
+
if (source[index] !== '\n' && source[index] !== '\r') {
|
|
38
|
+
output[index] = ' ';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
output[end - 1] = '}';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return output.join('');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @returns {{
|
|
49
|
+
* name: string
|
|
50
|
+
* transformRouteSource: (options: { source: string, filename: string }) => string
|
|
51
|
+
* formatRoute: (options: { source: string }) => string
|
|
52
|
+
* }}
|
|
53
|
+
*/
|
|
54
|
+
export function octaneRouteGeneratorPlugin() {
|
|
55
|
+
return {
|
|
56
|
+
name: 'octane-route-source',
|
|
57
|
+
transformRouteSource: ({ source, filename }) => maskOctaneRouteSource(source, filename),
|
|
58
|
+
// Router scaffolds are already formatted. Returning them unchanged avoids
|
|
59
|
+
// passing TSRX's `@{}` syntax through a TypeScript-only formatter.
|
|
60
|
+
formatRoute: ({ source }) => source,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {AstNode} root
|
|
66
|
+
* @returns {Array<{ start: number, end: number }>}
|
|
67
|
+
*/
|
|
68
|
+
function findNativeTemplateBodies(root) {
|
|
69
|
+
/** @type {Array<{ start: number, end: number }>} */
|
|
70
|
+
const bodies = [];
|
|
71
|
+
const visited = new WeakSet();
|
|
72
|
+
|
|
73
|
+
/** @param {unknown} value */
|
|
74
|
+
const visit = (value) => {
|
|
75
|
+
if (!value || typeof value !== 'object' || visited.has(value)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
visited.add(value);
|
|
79
|
+
|
|
80
|
+
if (Array.isArray(value)) {
|
|
81
|
+
for (const item of value) {
|
|
82
|
+
visit(item);
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const node = /** @type {AstNode} */ (value);
|
|
88
|
+
if (
|
|
89
|
+
node.metadata?.native_tsrx_body === true &&
|
|
90
|
+
node.body &&
|
|
91
|
+
!Array.isArray(node.body) &&
|
|
92
|
+
typeof node.body.start === 'number' &&
|
|
93
|
+
typeof node.body.end === 'number'
|
|
94
|
+
) {
|
|
95
|
+
bodies.push({ start: node.body.start, end: node.body.end });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (const [key, child] of Object.entries(node)) {
|
|
100
|
+
if (key !== 'metadata' && key !== 'loc') {
|
|
101
|
+
visit(child);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
visit(root);
|
|
107
|
+
return bodies;
|
|
108
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import {
|
|
2
|
+
appendUniqueUserTags,
|
|
3
|
+
deepEqual,
|
|
4
|
+
escapeHtml,
|
|
5
|
+
getAssetCrossOrigin,
|
|
6
|
+
getScriptPreloadAttrs,
|
|
7
|
+
resolveManifestCssLink,
|
|
8
|
+
} from '@tanstack/router-core';
|
|
9
|
+
import { isServer } from '@tanstack/router-core/isServer';
|
|
10
|
+
import { useRouter } from './context';
|
|
11
|
+
import { splitSlot, subSlot } from './internal';
|
|
12
|
+
import { useStore } from './useStore';
|
|
13
|
+
import type {
|
|
14
|
+
AnyRouteMatch,
|
|
15
|
+
AnyRouter,
|
|
16
|
+
AssetCrossOriginConfig,
|
|
17
|
+
RouterManagedTag,
|
|
18
|
+
} from '@tanstack/router-core';
|
|
19
|
+
|
|
20
|
+
function buildTagsFromMatches(
|
|
21
|
+
router: AnyRouter,
|
|
22
|
+
nonce: string | undefined,
|
|
23
|
+
matches: Array<AnyRouteMatch>,
|
|
24
|
+
assetCrossOrigin?: AssetCrossOriginConfig,
|
|
25
|
+
): Array<RouterManagedTag> {
|
|
26
|
+
const routeMeta = matches.map((match) => match.meta).filter((meta) => meta !== undefined);
|
|
27
|
+
|
|
28
|
+
const resultMeta: Array<RouterManagedTag> = [];
|
|
29
|
+
const metaByAttribute: Record<string, true> = {};
|
|
30
|
+
let title: RouterManagedTag | undefined;
|
|
31
|
+
for (let i = routeMeta.length - 1; i >= 0; i--) {
|
|
32
|
+
const metas = routeMeta[i]!;
|
|
33
|
+
for (let j = metas.length - 1; j >= 0; j--) {
|
|
34
|
+
const meta = metas[j];
|
|
35
|
+
if (!meta) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if ('title' in meta && typeof meta.title === 'string') {
|
|
40
|
+
title ??= { tag: 'title', children: meta.title };
|
|
41
|
+
} else if ('script:ld+json' in meta) {
|
|
42
|
+
try {
|
|
43
|
+
resultMeta.push({
|
|
44
|
+
tag: 'script',
|
|
45
|
+
attrs: { type: 'application/ld+json' },
|
|
46
|
+
children: escapeHtml(JSON.stringify(meta['script:ld+json'])),
|
|
47
|
+
});
|
|
48
|
+
} catch {
|
|
49
|
+
// Ignore values that cannot be serialized as JSON-LD.
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
const attribute =
|
|
53
|
+
('name' in meta && typeof meta.name === 'string' ? meta.name : undefined) ??
|
|
54
|
+
('property' in meta && typeof meta.property === 'string' ? meta.property : undefined);
|
|
55
|
+
if (attribute && metaByAttribute[attribute]) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (attribute) {
|
|
59
|
+
metaByAttribute[attribute] = true;
|
|
60
|
+
}
|
|
61
|
+
resultMeta.push({ tag: 'meta', attrs: { ...meta, nonce } });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (title) {
|
|
67
|
+
resultMeta.push(title);
|
|
68
|
+
}
|
|
69
|
+
if (nonce) {
|
|
70
|
+
resultMeta.push({
|
|
71
|
+
tag: 'meta',
|
|
72
|
+
attrs: { property: 'csp-nonce', content: nonce },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
resultMeta.reverse();
|
|
76
|
+
|
|
77
|
+
const links = matches
|
|
78
|
+
.flatMap((match) => match.links ?? [])
|
|
79
|
+
.filter((link) => link !== undefined)
|
|
80
|
+
.map((link) => ({ tag: 'link', attrs: { ...link, nonce } }) satisfies RouterManagedTag);
|
|
81
|
+
|
|
82
|
+
const manifestTags: Array<RouterManagedTag> = [];
|
|
83
|
+
const preloadTags: Array<RouterManagedTag> = [];
|
|
84
|
+
const manifest = router.ssr?.manifest;
|
|
85
|
+
if (manifest) {
|
|
86
|
+
for (const match of matches) {
|
|
87
|
+
for (const link of manifest.routes[match.routeId]?.css ?? []) {
|
|
88
|
+
const resolvedLink = resolveManifestCssLink(link);
|
|
89
|
+
manifestTags.push({
|
|
90
|
+
tag: 'link',
|
|
91
|
+
attrs: {
|
|
92
|
+
rel: 'stylesheet',
|
|
93
|
+
...resolvedLink,
|
|
94
|
+
crossOrigin:
|
|
95
|
+
getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ?? resolvedLink.crossOrigin,
|
|
96
|
+
nonce,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
for (const preload of manifest.routes[match.routeId]?.preloads ?? []) {
|
|
101
|
+
preloadTags.push({
|
|
102
|
+
tag: 'link',
|
|
103
|
+
attrs: {
|
|
104
|
+
...getScriptPreloadAttrs(manifest, preload, assetCrossOrigin),
|
|
105
|
+
nonce,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (manifest.inlineStyle) {
|
|
112
|
+
manifestTags.push({
|
|
113
|
+
tag: 'style',
|
|
114
|
+
attrs: { ...manifest.inlineStyle.attrs, nonce },
|
|
115
|
+
children: manifest.inlineStyle.children,
|
|
116
|
+
inlineCss: true,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const styles = matches
|
|
122
|
+
.flatMap((match) => match.styles ?? [])
|
|
123
|
+
.filter((style) => style !== undefined)
|
|
124
|
+
.map(
|
|
125
|
+
({ children, ...attrs }) =>
|
|
126
|
+
({
|
|
127
|
+
tag: 'style',
|
|
128
|
+
attrs: { ...attrs, nonce },
|
|
129
|
+
children: children,
|
|
130
|
+
}) satisfies RouterManagedTag,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const headScripts = matches
|
|
134
|
+
.flatMap((match) => match.headScripts ?? [])
|
|
135
|
+
.filter((script) => script !== undefined)
|
|
136
|
+
.map(
|
|
137
|
+
({ children, ...attrs }) =>
|
|
138
|
+
({
|
|
139
|
+
tag: 'script',
|
|
140
|
+
attrs: { ...attrs, nonce },
|
|
141
|
+
children: children,
|
|
142
|
+
}) satisfies RouterManagedTag,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const tags: Array<RouterManagedTag> = [];
|
|
146
|
+
appendUniqueUserTags(tags, resultMeta);
|
|
147
|
+
tags.push(...preloadTags);
|
|
148
|
+
appendUniqueUserTags(tags, links);
|
|
149
|
+
tags.push(...manifestTags);
|
|
150
|
+
appendUniqueUserTags(tags, styles);
|
|
151
|
+
appendUniqueUserTags(tags, headScripts);
|
|
152
|
+
return tags;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function useTags(...args: Array<unknown>): Array<RouterManagedTag> {
|
|
156
|
+
const [userArgs, slot] = splitSlot(args);
|
|
157
|
+
const assetCrossOrigin = userArgs[0] as AssetCrossOriginConfig | undefined;
|
|
158
|
+
const router = useRouter();
|
|
159
|
+
const nonce = router.options.ssr?.nonce;
|
|
160
|
+
|
|
161
|
+
if (isServer ?? router.isServer) {
|
|
162
|
+
return buildTagsFromMatches(router, nonce, router.stores.matches.get(), assetCrossOrigin);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return useStore(
|
|
166
|
+
router.stores.matches,
|
|
167
|
+
(matches: Array<AnyRouteMatch>) =>
|
|
168
|
+
buildTagsFromMatches(router, nonce, matches, assetCrossOrigin),
|
|
169
|
+
deepEqual,
|
|
170
|
+
subSlot(slot, 'head:tags'),
|
|
171
|
+
);
|
|
172
|
+
}
|
package/src/hooks.ts
CHANGED
|
@@ -14,6 +14,33 @@ import { replaceEqualDeep } from '@tanstack/router-core';
|
|
|
14
14
|
import { useRouter, matchContext } from './context';
|
|
15
15
|
import { useStore } from './useStore';
|
|
16
16
|
import { splitSlot, subSlot } from './internal';
|
|
17
|
+
import type {
|
|
18
|
+
AnyRouter,
|
|
19
|
+
FromPathOption,
|
|
20
|
+
RegisteredRouter,
|
|
21
|
+
ThrowConstraint,
|
|
22
|
+
ThrowOrOptional,
|
|
23
|
+
UseLoaderDataResult,
|
|
24
|
+
UseLoaderDepsResult,
|
|
25
|
+
UseNavigateResult,
|
|
26
|
+
UseParamsResult,
|
|
27
|
+
UseRouteContextOptions,
|
|
28
|
+
UseRouteContextResult,
|
|
29
|
+
UseSearchResult,
|
|
30
|
+
} from '@tanstack/router-core';
|
|
31
|
+
import type {
|
|
32
|
+
UseLoaderDataOptions,
|
|
33
|
+
UseLoaderDepsOptions,
|
|
34
|
+
UseLocationBaseOptions,
|
|
35
|
+
UseLocationResult,
|
|
36
|
+
UseMatchOptions,
|
|
37
|
+
UseMatchResult,
|
|
38
|
+
UseMatchesBaseOptions,
|
|
39
|
+
UseMatchesResult,
|
|
40
|
+
UseParamsOptions,
|
|
41
|
+
UseSearchOptions,
|
|
42
|
+
} from './routeHookTypes';
|
|
43
|
+
import type { StructuralSharingOption } from './structuralSharing';
|
|
17
44
|
|
|
18
45
|
// Sentinel store + selection for "no match at this id" (upstream's dummyStore).
|
|
19
46
|
const dummyStore = {
|
|
@@ -37,6 +64,24 @@ function useStructuralSharing(opts: any, router: any, slot: symbol | undefined)
|
|
|
37
64
|
};
|
|
38
65
|
}
|
|
39
66
|
|
|
67
|
+
export function useMatch<
|
|
68
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
69
|
+
const TFrom extends string | undefined = undefined,
|
|
70
|
+
TStrict extends boolean = true,
|
|
71
|
+
TThrow extends boolean = true,
|
|
72
|
+
TSelected = unknown,
|
|
73
|
+
TStructuralSharing extends boolean = boolean,
|
|
74
|
+
>(
|
|
75
|
+
opts: UseMatchOptions<
|
|
76
|
+
TRouter,
|
|
77
|
+
TFrom,
|
|
78
|
+
TStrict,
|
|
79
|
+
ThrowConstraint<TStrict, TThrow>,
|
|
80
|
+
TSelected,
|
|
81
|
+
TStructuralSharing
|
|
82
|
+
>,
|
|
83
|
+
): ThrowOrOptional<UseMatchResult<TRouter, TFrom, TStrict, TSelected>, TThrow>;
|
|
84
|
+
export function useMatch(opts: any, slot: symbol | undefined): any;
|
|
40
85
|
export function useMatch(...args: any[]): any {
|
|
41
86
|
const [user, slot] = splitSlot(args);
|
|
42
87
|
const opts = user[0] ?? {};
|
|
@@ -67,6 +112,24 @@ export function useMatch(...args: any[]): any {
|
|
|
67
112
|
return undefined;
|
|
68
113
|
}
|
|
69
114
|
|
|
115
|
+
export function useParams<
|
|
116
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
117
|
+
const TFrom extends string | undefined = undefined,
|
|
118
|
+
TStrict extends boolean = true,
|
|
119
|
+
TThrow extends boolean = true,
|
|
120
|
+
TSelected = unknown,
|
|
121
|
+
TStructuralSharing extends boolean = boolean,
|
|
122
|
+
>(
|
|
123
|
+
opts: UseParamsOptions<
|
|
124
|
+
TRouter,
|
|
125
|
+
TFrom,
|
|
126
|
+
TStrict,
|
|
127
|
+
ThrowConstraint<TStrict, TThrow>,
|
|
128
|
+
TSelected,
|
|
129
|
+
TStructuralSharing
|
|
130
|
+
>,
|
|
131
|
+
): ThrowOrOptional<UseParamsResult<TRouter, TFrom, TStrict, TSelected>, TThrow>;
|
|
132
|
+
export function useParams(opts: any, slot: symbol | undefined): any;
|
|
70
133
|
export function useParams(...args: any[]): any {
|
|
71
134
|
const [user, slot] = splitSlot(args);
|
|
72
135
|
const opts = user[0] ?? {};
|
|
@@ -85,6 +148,24 @@ export function useParams(...args: any[]): any {
|
|
|
85
148
|
);
|
|
86
149
|
}
|
|
87
150
|
|
|
151
|
+
export function useSearch<
|
|
152
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
153
|
+
const TFrom extends string | undefined = undefined,
|
|
154
|
+
TStrict extends boolean = true,
|
|
155
|
+
TThrow extends boolean = true,
|
|
156
|
+
TSelected = unknown,
|
|
157
|
+
TStructuralSharing extends boolean = boolean,
|
|
158
|
+
>(
|
|
159
|
+
opts: UseSearchOptions<
|
|
160
|
+
TRouter,
|
|
161
|
+
TFrom,
|
|
162
|
+
TStrict,
|
|
163
|
+
ThrowConstraint<TStrict, TThrow>,
|
|
164
|
+
TSelected,
|
|
165
|
+
TStructuralSharing
|
|
166
|
+
>,
|
|
167
|
+
): ThrowOrOptional<UseSearchResult<TRouter, TFrom, TStrict, TSelected>, TThrow>;
|
|
168
|
+
export function useSearch(opts: any, slot: symbol | undefined): any;
|
|
88
169
|
export function useSearch(...args: any[]): any {
|
|
89
170
|
const [user, slot] = splitSlot(args);
|
|
90
171
|
const opts = user[0] ?? {};
|
|
@@ -100,6 +181,16 @@ export function useSearch(...args: any[]): any {
|
|
|
100
181
|
);
|
|
101
182
|
}
|
|
102
183
|
|
|
184
|
+
export function useLoaderData<
|
|
185
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
186
|
+
const TFrom extends string | undefined = undefined,
|
|
187
|
+
TStrict extends boolean = true,
|
|
188
|
+
TSelected = unknown,
|
|
189
|
+
TStructuralSharing extends boolean = boolean,
|
|
190
|
+
>(
|
|
191
|
+
opts: UseLoaderDataOptions<TRouter, TFrom, TStrict, TSelected, TStructuralSharing>,
|
|
192
|
+
): UseLoaderDataResult<TRouter, TFrom, TStrict, TSelected>;
|
|
193
|
+
export function useLoaderData(opts: any, slot: symbol | undefined): any;
|
|
103
194
|
export function useLoaderData(...args: any[]): any {
|
|
104
195
|
const [user, slot] = splitSlot(args);
|
|
105
196
|
const opts = user[0] ?? {};
|
|
@@ -114,6 +205,15 @@ export function useLoaderData(...args: any[]): any {
|
|
|
114
205
|
);
|
|
115
206
|
}
|
|
116
207
|
|
|
208
|
+
export function useLoaderDeps<
|
|
209
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
210
|
+
const TFrom extends string | undefined = undefined,
|
|
211
|
+
TSelected = unknown,
|
|
212
|
+
TStructuralSharing extends boolean = boolean,
|
|
213
|
+
>(
|
|
214
|
+
opts: UseLoaderDepsOptions<TRouter, TFrom, TSelected, TStructuralSharing>,
|
|
215
|
+
): UseLoaderDepsResult<TRouter, TFrom, TSelected>;
|
|
216
|
+
export function useLoaderDeps(opts: any, slot: symbol | undefined): any;
|
|
117
217
|
export function useLoaderDeps(...args: any[]): any {
|
|
118
218
|
const [user, slot] = splitSlot(args);
|
|
119
219
|
const opts = user[0] ?? {};
|
|
@@ -127,6 +227,15 @@ export function useLoaderDeps(...args: any[]): any {
|
|
|
127
227
|
);
|
|
128
228
|
}
|
|
129
229
|
|
|
230
|
+
export function useRouteContext<
|
|
231
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
232
|
+
const TFrom extends string | undefined = undefined,
|
|
233
|
+
TStrict extends boolean = true,
|
|
234
|
+
TSelected = unknown,
|
|
235
|
+
>(
|
|
236
|
+
opts: UseRouteContextOptions<TRouter, TFrom, TStrict, TSelected>,
|
|
237
|
+
): UseRouteContextResult<TRouter, TFrom, TStrict, TSelected>;
|
|
238
|
+
export function useRouteContext(opts: any, slot: symbol | undefined): any;
|
|
130
239
|
export function useRouteContext(...args: any[]): any {
|
|
131
240
|
const [user, slot] = splitSlot(args);
|
|
132
241
|
const opts = user[0] ?? {};
|
|
@@ -139,6 +248,15 @@ export function useRouteContext(...args: any[]): any {
|
|
|
139
248
|
);
|
|
140
249
|
}
|
|
141
250
|
|
|
251
|
+
export function useLocation<
|
|
252
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
253
|
+
TSelected = unknown,
|
|
254
|
+
TStructuralSharing extends boolean = boolean,
|
|
255
|
+
>(
|
|
256
|
+
opts?: UseLocationBaseOptions<TRouter, TSelected, TStructuralSharing> &
|
|
257
|
+
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
|
|
258
|
+
): UseLocationResult<TRouter, TSelected>;
|
|
259
|
+
export function useLocation(opts: any, slot: symbol | undefined): any;
|
|
142
260
|
export function useLocation(...args: any[]): any {
|
|
143
261
|
const [user, slot] = splitSlot(args);
|
|
144
262
|
const opts = user[0] ?? {};
|
|
@@ -151,6 +269,15 @@ export function useLocation(...args: any[]): any {
|
|
|
151
269
|
);
|
|
152
270
|
}
|
|
153
271
|
|
|
272
|
+
export function useMatches<
|
|
273
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
274
|
+
TSelected = unknown,
|
|
275
|
+
TStructuralSharing extends boolean = boolean,
|
|
276
|
+
>(
|
|
277
|
+
opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
|
|
278
|
+
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
|
|
279
|
+
): UseMatchesResult<TRouter, TSelected>;
|
|
280
|
+
export function useMatches(opts: any, slot: symbol | undefined): any;
|
|
154
281
|
export function useMatches(...args: any[]): any {
|
|
155
282
|
const [user, slot] = splitSlot(args);
|
|
156
283
|
const opts = user[0] ?? {};
|
|
@@ -163,6 +290,15 @@ export function useMatches(...args: any[]): any {
|
|
|
163
290
|
);
|
|
164
291
|
}
|
|
165
292
|
|
|
293
|
+
export function useParentMatches<
|
|
294
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
295
|
+
TSelected = unknown,
|
|
296
|
+
TStructuralSharing extends boolean = boolean,
|
|
297
|
+
>(
|
|
298
|
+
opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
|
|
299
|
+
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
|
|
300
|
+
): UseMatchesResult<TRouter, TSelected>;
|
|
301
|
+
export function useParentMatches(opts: any, slot: symbol | undefined): any;
|
|
166
302
|
export function useParentMatches(...args: any[]): any {
|
|
167
303
|
const [user, slot] = splitSlot(args);
|
|
168
304
|
const opts = user[0] ?? {};
|
|
@@ -182,6 +318,15 @@ export function useParentMatches(...args: any[]): any {
|
|
|
182
318
|
);
|
|
183
319
|
}
|
|
184
320
|
|
|
321
|
+
export function useChildMatches<
|
|
322
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
323
|
+
TSelected = unknown,
|
|
324
|
+
TStructuralSharing extends boolean = boolean,
|
|
325
|
+
>(
|
|
326
|
+
opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
|
|
327
|
+
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
|
|
328
|
+
): UseMatchesResult<TRouter, TSelected>;
|
|
329
|
+
export function useChildMatches(opts: any, slot: symbol | undefined): any;
|
|
185
330
|
export function useChildMatches(...args: any[]): any {
|
|
186
331
|
const [user, slot] = splitSlot(args);
|
|
187
332
|
const opts = user[0] ?? {};
|
|
@@ -200,6 +345,11 @@ export function useChildMatches(...args: any[]): any {
|
|
|
200
345
|
|
|
201
346
|
// Returns a STABLE navigate function (upstream useCallback([from, router])) that
|
|
202
347
|
// forwards to `router.navigate`, defaulting `from` to the hook's option.
|
|
348
|
+
export function useNavigate<
|
|
349
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
350
|
+
TDefaultFrom extends string = string,
|
|
351
|
+
>(options?: { from?: FromPathOption<TRouter, TDefaultFrom> }): UseNavigateResult<TDefaultFrom>;
|
|
352
|
+
export function useNavigate(options: any, slot: symbol | undefined): (to: any) => any;
|
|
203
353
|
export function useNavigate(...args: any[]): (to: any) => any {
|
|
204
354
|
const [user, slot] = splitSlot(args);
|
|
205
355
|
const opts = user[0] ?? {};
|
|
@@ -213,6 +363,7 @@ export function useNavigate(...args: any[]): (to: any) => any {
|
|
|
213
363
|
|
|
214
364
|
// True when the current history entry isn't the first (there is somewhere to go
|
|
215
365
|
// back to) — per upstream useCanGoBack (location.state.__TSR_index !== 0).
|
|
366
|
+
export function useCanGoBack(): boolean;
|
|
216
367
|
export function useCanGoBack(...args: any[]): boolean {
|
|
217
368
|
const [, slot] = splitSlot(args);
|
|
218
369
|
const router = useRouter();
|
package/src/index.ts
CHANGED
|
@@ -19,9 +19,11 @@
|
|
|
19
19
|
// createLink/useLinkProps, navigation blocking (useBlocker/Block), the full
|
|
20
20
|
// read-hook set (useMatch and friends, nearest-match resolution via
|
|
21
21
|
// matchContext), Route/getRouteApi hook accessors, Await/useAwaited, lazy
|
|
22
|
-
// routes,
|
|
23
|
-
//
|
|
24
|
-
//
|
|
22
|
+
// routes, search validation/middleware from core, generated file routes,
|
|
23
|
+
// document/head assets, and Start-compatible SSR/hydration. Devtools remain
|
|
24
|
+
// separate.
|
|
25
|
+
import './frameworkTypes';
|
|
26
|
+
|
|
25
27
|
export * from '@tanstack/router-core';
|
|
26
28
|
export {
|
|
27
29
|
createHistory,
|
|
@@ -48,25 +50,39 @@ export {
|
|
|
48
50
|
createRoute,
|
|
49
51
|
createRootRoute,
|
|
50
52
|
createRootRouteWithContext,
|
|
53
|
+
rootRouteWithContext,
|
|
51
54
|
createRouteMask,
|
|
52
55
|
getRouteApi,
|
|
53
56
|
Route,
|
|
54
57
|
RootRoute,
|
|
55
58
|
RouteApi,
|
|
59
|
+
NotFoundRoute,
|
|
56
60
|
} from './route';
|
|
61
|
+
export {
|
|
62
|
+
FileRoute,
|
|
63
|
+
createFileRoute,
|
|
64
|
+
FileRouteLoader,
|
|
65
|
+
LazyRoute,
|
|
66
|
+
createLazyRoute,
|
|
67
|
+
createLazyFileRoute,
|
|
68
|
+
} from './fileRoute';
|
|
57
69
|
// Framework-facing component types (react-router parity, on octane renderables).
|
|
58
70
|
// route.ts also narrows router-core's *Extensions interfaces to these via module
|
|
59
71
|
// augmentation — mirroring upstream's route.tsx/router.tsx `declare module`.
|
|
60
72
|
export type {
|
|
73
|
+
DefaultRouteTypes,
|
|
61
74
|
SyncRouteComponent,
|
|
62
75
|
AsyncRouteComponent,
|
|
63
76
|
RouteComponent,
|
|
77
|
+
RouteTypes,
|
|
64
78
|
ErrorRouteComponent,
|
|
65
79
|
NotFoundRouteComponent,
|
|
80
|
+
AnyRootRoute,
|
|
66
81
|
} from './route';
|
|
67
82
|
export { routerContext, getRouterContext, matchContext, useRouter } from './context';
|
|
68
83
|
export { useStore } from './useStore';
|
|
69
84
|
export { useRouterState } from './useRouterState';
|
|
85
|
+
export type { UseRouterStateOptions, UseRouterStateResult } from './useRouterState';
|
|
70
86
|
export {
|
|
71
87
|
useMatch,
|
|
72
88
|
useLocation,
|
|
@@ -81,10 +97,53 @@ export {
|
|
|
81
97
|
useNavigate,
|
|
82
98
|
useCanGoBack,
|
|
83
99
|
} from './hooks';
|
|
100
|
+
export type {
|
|
101
|
+
UseLoaderDataBaseOptions,
|
|
102
|
+
UseLoaderDataOptions,
|
|
103
|
+
UseLoaderDataRoute,
|
|
104
|
+
UseLoaderDepsBaseOptions,
|
|
105
|
+
UseLoaderDepsOptions,
|
|
106
|
+
UseLoaderDepsRoute,
|
|
107
|
+
UseLocationBaseOptions,
|
|
108
|
+
UseLocationResult,
|
|
109
|
+
UseMatchBaseOptions,
|
|
110
|
+
UseMatchOptions,
|
|
111
|
+
UseMatchResult,
|
|
112
|
+
UseMatchRoute,
|
|
113
|
+
UseMatchesBaseOptions,
|
|
114
|
+
UseMatchesResult,
|
|
115
|
+
UseParamsBaseOptions,
|
|
116
|
+
UseParamsOptions,
|
|
117
|
+
UseParamsRoute,
|
|
118
|
+
UseRouteContextRoute,
|
|
119
|
+
UseSearchBaseOptions,
|
|
120
|
+
UseSearchOptions,
|
|
121
|
+
UseSearchRoute,
|
|
122
|
+
} from './routeHookTypes';
|
|
84
123
|
export { useAwaited } from './useAwaited';
|
|
124
|
+
export type { AwaitOptions } from './useAwaited';
|
|
85
125
|
export { useLinkProps, createLink, linkOptions } from './link';
|
|
126
|
+
export type { LinkOptionsFn, LinkOptionsFnOptions } from './link';
|
|
127
|
+
export type {
|
|
128
|
+
ActiveLinkOptionProps,
|
|
129
|
+
ActiveLinkOptions,
|
|
130
|
+
CreateLinkProps,
|
|
131
|
+
LinkComponent,
|
|
132
|
+
LinkComponentProps,
|
|
133
|
+
LinkComponentRoute,
|
|
134
|
+
LinkProps,
|
|
135
|
+
LinkPropsChildren,
|
|
136
|
+
OctaneAnchorProps,
|
|
137
|
+
OctaneRenderable,
|
|
138
|
+
UseLinkPropsOptions,
|
|
139
|
+
} from './linkTypes';
|
|
86
140
|
export { useBlocker, Block } from './useBlocker.tsrx';
|
|
87
|
-
export type {
|
|
141
|
+
export type {
|
|
142
|
+
BlockerResolver,
|
|
143
|
+
ShouldBlockFn,
|
|
144
|
+
ShouldBlockFnArgs,
|
|
145
|
+
UseBlockerOpts,
|
|
146
|
+
} from './useBlocker.tsrx';
|
|
88
147
|
export { useMatchRoute, MatchRoute } from './MatchRoute.tsrx';
|
|
89
148
|
export { useElementScrollRestoration } from './useElementScrollRestoration';
|
|
90
149
|
export { lazyRouteComponent } from './lazyRouteComponent';
|
|
@@ -101,3 +160,24 @@ export { Match } from './Match.tsrx';
|
|
|
101
160
|
export { CatchBoundary, ErrorComponent } from './CatchBoundary.tsrx';
|
|
102
161
|
export { CatchNotFound, DefaultGlobalNotFound } from './not-found.tsrx';
|
|
103
162
|
export { ClientOnly, useHydrated } from './ClientOnly.tsrx';
|
|
163
|
+
export { HeadContent } from './HeadContent.tsrx';
|
|
164
|
+
export type { HeadContentProps } from './HeadContent.tsrx';
|
|
165
|
+
export { Scripts } from './Scripts.tsrx';
|
|
166
|
+
export { ScriptOnce } from './ScriptOnce.tsrx';
|
|
167
|
+
export { Asset } from './Asset.tsrx';
|
|
168
|
+
export type { AssetProps } from './Asset.tsrx';
|
|
169
|
+
export { useTags } from './headContentUtils';
|
|
170
|
+
export { Html } from './Html';
|
|
171
|
+
export type { HtmlProps } from './Html';
|
|
172
|
+
export { Head } from './Head';
|
|
173
|
+
export type { HeadProps } from './Head';
|
|
174
|
+
export { Body } from './Body';
|
|
175
|
+
export type { BodyProps } from './Body';
|
|
176
|
+
export type { OctaneElementAttributes, OctaneScriptAttributes } from './frameworkTypes';
|
|
177
|
+
export type {
|
|
178
|
+
InferStructuralSharing,
|
|
179
|
+
ValidateLinkOptions,
|
|
180
|
+
ValidateLinkOptionsArray,
|
|
181
|
+
ValidateUseParamsOptions,
|
|
182
|
+
ValidateUseSearchOptions,
|
|
183
|
+
} from './typePrimitives';
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// createRoute({ path: 'item/$id', component: lazyRouteComponent(() => import('./Item')) })
|
|
7
7
|
import { use, createElement } from 'octane';
|
|
8
8
|
import { isModuleNotFoundError } from '@tanstack/router-core';
|
|
9
|
+
import { toExternalHydrationThenable } from './externalHydration';
|
|
9
10
|
|
|
10
11
|
export function lazyRouteComponent(
|
|
11
12
|
importer: () => Promise<any>,
|
|
@@ -48,7 +49,7 @@ export function lazyRouteComponent(
|
|
|
48
49
|
throw new Promise(() => {});
|
|
49
50
|
}
|
|
50
51
|
if (error) throw error;
|
|
51
|
-
if (!comp) use(load());
|
|
52
|
+
if (!comp) use(toExternalHydrationThenable(load()));
|
|
52
53
|
return createElement(comp, props);
|
|
53
54
|
};
|
|
54
55
|
Lazy.preload = load;
|