@octanejs/tanstack-router 0.2.7 → 0.2.9
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 +68 -0
- package/package.json +3 -2
- package/src/Asset.tsrx +3 -135
- package/src/ClientOnly.tsrx +2 -0
- package/src/Link.tsrx +2 -53
- package/src/Matches.tsrx +6 -4
- package/src/Scripts.tsrx +2 -19
- package/src/Transitioner.tsrx +8 -7
- package/src/assetComponent.tsrx +146 -0
- package/src/compiled/Document.tsrx +101 -0
- package/src/compiled/Link.tsrx +18 -0
- package/src/compiled/Matches.tsrx +275 -0
- package/src/compiled/bindings.ts +42 -0
- package/src/compiled/route.tsrx +71 -0
- package/src/compiled.ts +67 -0
- package/src/fileRoute.ts +10 -60
- package/src/frameworkTypes.ts +11 -8
- package/src/headContentBinding.ts +178 -0
- package/src/headContentUtils.ts +2 -170
- package/src/hookBindings.ts +396 -0
- package/src/hooks.ts +17 -374
- package/src/index.ts +2 -0
- package/src/internal.ts +2 -0
- package/src/lazyRouteComponent.ts +6 -50
- package/src/lazyRouteLoader.ts +49 -0
- package/src/link.ts +5 -379
- package/src/linkBinding.ts +391 -0
- package/src/linkComponent.tsrx +61 -0
- package/src/route.ts +187 -51
- package/src/routeHookBindings.ts +146 -0
- package/src/routerStateBinding.ts +41 -0
- package/src/scriptComponent.tsrx +27 -0
- package/src/scriptContentBinding.ts +43 -0
- package/src/scriptContentUtils.ts +2 -35
- package/src/useRouterState.ts +3 -35
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
'use strong';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
appendUniqueUserTags,
|
|
5
|
+
deepEqual,
|
|
6
|
+
escapeHtml,
|
|
7
|
+
getAssetCrossOrigin,
|
|
8
|
+
getScriptPreloadAttrs,
|
|
9
|
+
resolveManifestCssLink,
|
|
10
|
+
} from '@tanstack/router-core';
|
|
11
|
+
import { isServer } from '@tanstack/router-core/isServer';
|
|
12
|
+
import type { useRouter as UseRouter } from './context';
|
|
13
|
+
import { splitSlot, subSlot } from './internal';
|
|
14
|
+
import { useStore } from './useStore';
|
|
15
|
+
import type {
|
|
16
|
+
AnyRouteMatch,
|
|
17
|
+
AnyRouter,
|
|
18
|
+
AssetCrossOriginConfig,
|
|
19
|
+
RouterManagedTag,
|
|
20
|
+
} from '@tanstack/router-core';
|
|
21
|
+
|
|
22
|
+
function buildTagsFromMatches(
|
|
23
|
+
router: AnyRouter,
|
|
24
|
+
nonce: string | undefined,
|
|
25
|
+
matches: Array<AnyRouteMatch>,
|
|
26
|
+
assetCrossOrigin?: AssetCrossOriginConfig,
|
|
27
|
+
): Array<RouterManagedTag> {
|
|
28
|
+
const routeMeta = matches.map((match) => match.meta).filter((meta) => meta !== undefined);
|
|
29
|
+
|
|
30
|
+
const resultMeta: Array<RouterManagedTag> = [];
|
|
31
|
+
const metaByAttribute: Record<string, true> = {};
|
|
32
|
+
let title: RouterManagedTag | undefined;
|
|
33
|
+
for (let i = routeMeta.length - 1; i >= 0; i--) {
|
|
34
|
+
const metas = routeMeta[i]!;
|
|
35
|
+
for (let j = metas.length - 1; j >= 0; j--) {
|
|
36
|
+
const meta = metas[j];
|
|
37
|
+
if (!meta) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if ('title' in meta && typeof meta.title === 'string') {
|
|
42
|
+
title ??= { tag: 'title', children: meta.title };
|
|
43
|
+
} else if ('script:ld+json' in meta) {
|
|
44
|
+
try {
|
|
45
|
+
resultMeta.push({
|
|
46
|
+
tag: 'script',
|
|
47
|
+
attrs: { type: 'application/ld+json' },
|
|
48
|
+
children: escapeHtml(JSON.stringify(meta['script:ld+json'])),
|
|
49
|
+
});
|
|
50
|
+
} catch {
|
|
51
|
+
// Ignore values that cannot be serialized as JSON-LD.
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
const attribute =
|
|
55
|
+
('name' in meta && typeof meta.name === 'string' ? meta.name : undefined) ??
|
|
56
|
+
('property' in meta && typeof meta.property === 'string' ? meta.property : undefined);
|
|
57
|
+
if (attribute && metaByAttribute[attribute]) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (attribute) {
|
|
61
|
+
metaByAttribute[attribute] = true;
|
|
62
|
+
}
|
|
63
|
+
resultMeta.push({ tag: 'meta', attrs: { ...meta, nonce } });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (title) {
|
|
69
|
+
resultMeta.push(title);
|
|
70
|
+
}
|
|
71
|
+
if (nonce) {
|
|
72
|
+
resultMeta.push({
|
|
73
|
+
tag: 'meta',
|
|
74
|
+
attrs: { property: 'csp-nonce', content: nonce },
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
resultMeta.reverse();
|
|
78
|
+
|
|
79
|
+
const links = matches
|
|
80
|
+
.flatMap((match) => match.links ?? [])
|
|
81
|
+
.filter((link) => link !== undefined)
|
|
82
|
+
.map((link) => ({ tag: 'link', attrs: { ...link, nonce } }) satisfies RouterManagedTag);
|
|
83
|
+
|
|
84
|
+
const manifestTags: Array<RouterManagedTag> = [];
|
|
85
|
+
const preloadTags: Array<RouterManagedTag> = [];
|
|
86
|
+
const manifest = router.ssr?.manifest;
|
|
87
|
+
if (manifest) {
|
|
88
|
+
for (const match of matches) {
|
|
89
|
+
for (const link of manifest.routes[match.routeId]?.css ?? []) {
|
|
90
|
+
const resolvedLink = resolveManifestCssLink(link);
|
|
91
|
+
manifestTags.push({
|
|
92
|
+
tag: 'link',
|
|
93
|
+
attrs: {
|
|
94
|
+
rel: 'stylesheet',
|
|
95
|
+
...resolvedLink,
|
|
96
|
+
crossOrigin:
|
|
97
|
+
getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ?? resolvedLink.crossOrigin,
|
|
98
|
+
nonce,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
for (const preload of manifest.routes[match.routeId]?.preloads ?? []) {
|
|
103
|
+
preloadTags.push({
|
|
104
|
+
tag: 'link',
|
|
105
|
+
attrs: {
|
|
106
|
+
...getScriptPreloadAttrs(manifest, preload, assetCrossOrigin),
|
|
107
|
+
nonce,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (manifest.inlineStyle) {
|
|
114
|
+
manifestTags.push({
|
|
115
|
+
tag: 'style',
|
|
116
|
+
attrs: { ...manifest.inlineStyle.attrs, nonce },
|
|
117
|
+
children: manifest.inlineStyle.children,
|
|
118
|
+
inlineCss: true,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const styles = matches
|
|
124
|
+
.flatMap((match) => match.styles ?? [])
|
|
125
|
+
.filter((style) => style !== undefined)
|
|
126
|
+
.map(
|
|
127
|
+
({ children, ...attrs }) =>
|
|
128
|
+
({
|
|
129
|
+
tag: 'style',
|
|
130
|
+
attrs: { ...attrs, nonce },
|
|
131
|
+
children: children,
|
|
132
|
+
}) satisfies RouterManagedTag,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const headScripts = matches
|
|
136
|
+
.flatMap((match) => match.headScripts ?? [])
|
|
137
|
+
.filter((script) => script !== undefined)
|
|
138
|
+
.map(
|
|
139
|
+
({ children, ...attrs }) =>
|
|
140
|
+
({
|
|
141
|
+
tag: 'script',
|
|
142
|
+
attrs: { ...attrs, nonce },
|
|
143
|
+
children: children,
|
|
144
|
+
}) satisfies RouterManagedTag,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const tags: Array<RouterManagedTag> = [];
|
|
148
|
+
appendUniqueUserTags(tags, resultMeta);
|
|
149
|
+
tags.push(...preloadTags);
|
|
150
|
+
appendUniqueUserTags(tags, links);
|
|
151
|
+
tags.push(...manifestTags);
|
|
152
|
+
appendUniqueUserTags(tags, styles);
|
|
153
|
+
appendUniqueUserTags(tags, headScripts);
|
|
154
|
+
return tags;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function createUseTags(useRouter: typeof UseRouter) {
|
|
158
|
+
function useTags(...args: Array<unknown>): Array<RouterManagedTag> {
|
|
159
|
+
const [userArgs, slot] = splitSlot(args);
|
|
160
|
+
const assetCrossOrigin = userArgs[0] as AssetCrossOriginConfig | undefined;
|
|
161
|
+
const router = useRouter();
|
|
162
|
+
const nonce = router.options.ssr?.nonce;
|
|
163
|
+
|
|
164
|
+
if (isServer ?? router.isServer) {
|
|
165
|
+
return buildTagsFromMatches(router, nonce, router.stores.matches.get(), assetCrossOrigin);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return useStore(
|
|
169
|
+
router.stores.matches,
|
|
170
|
+
(matches: Array<AnyRouteMatch>) =>
|
|
171
|
+
buildTagsFromMatches(router, nonce, matches, assetCrossOrigin),
|
|
172
|
+
deepEqual,
|
|
173
|
+
subSlot(slot, 'head:tags'),
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return useTags;
|
|
178
|
+
}
|
package/src/headContentUtils.ts
CHANGED
|
@@ -1,172 +1,4 @@
|
|
|
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
1
|
import { useRouter } from './context';
|
|
11
|
-
import {
|
|
12
|
-
import { useStore } from './useStore';
|
|
13
|
-
import type {
|
|
14
|
-
AnyRouteMatch,
|
|
15
|
-
AnyRouter,
|
|
16
|
-
AssetCrossOriginConfig,
|
|
17
|
-
RouterManagedTag,
|
|
18
|
-
} from '@tanstack/router-core';
|
|
2
|
+
import { createUseTags } from './headContentBinding';
|
|
19
3
|
|
|
20
|
-
|
|
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
|
-
}
|
|
4
|
+
export const useTags = createUseTags(useRouter);
|