@octanejs/tanstack-router 0.2.8 → 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.
@@ -36,15 +36,23 @@ export function CompiledRouterProvider(props: RouterProps) @{
36
36
  </CompiledContext>
37
37
  }
38
38
 
39
+ function CompiledWrap(props: { component?: any; children?: unknown }) @{
40
+ @if (props.component) {
41
+ <CompiledComponent component={props.component} props={{ children: props.children }} />
42
+ } @else {
43
+ <>{props.children}</>
44
+ }
45
+ }
46
+
39
47
  function CompiledMatches() @{
40
48
  const router = useRouter();
41
49
  const rootRoute = (router.routesById as any)[rootRouteId];
42
50
  useLayoutEffect(() => {
43
51
  if (router.options.scrollRestoration) setupScrollRestoration(router);
44
52
  }, [router]);
45
- useTransitioner(router);
53
+ const expectedMatches = useTransitioner(router);
46
54
  @try {
47
- <CompiledMatchesInner />
55
+ <CompiledMatchesInner expectedMatches={expectedMatches} />
48
56
  } @pending {
49
57
  <CompiledPending route={rootRoute} />
50
58
  } @catch (error, reset) {
@@ -53,16 +61,20 @@ function CompiledMatches() @{
53
61
  }
54
62
  }
55
63
 
56
- function CompiledMatchesInner() @{
64
+ function CompiledMatchesInner(props: { expectedMatches: unknown }) @{
57
65
  const router = useRouter();
58
66
  const acknowledgement: Array<any> = ((router as any)._rendered ??= []);
59
- const matches = useStore(router.stores.matches, (m: any) => acknowledgement[0] ?? m);
67
+ const matches = useStore(router.stores.matches, (m: any) =>
68
+ acknowledgement[0] === props.expectedMatches ? (props.expectedMatches ?? m) : m,
69
+ ) as any;
60
70
  const rootMatch = matches[0];
61
71
  useLayoutEffect(() => {
62
72
  if (acknowledgement[0] === matches) settleOwner(acknowledgement, true);
63
73
  }, [acknowledgement, matches]);
64
74
  @if (rootMatch) {
65
- <CompiledMatch routeId={rootMatch.routeId} />
75
+ <CompiledContext context={matchContext} value={rootMatch.routeId}>
76
+ <CompiledMatch routeId={rootMatch.routeId} />
77
+ </CompiledContext>
66
78
  }
67
79
  }
68
80
 
@@ -71,31 +83,28 @@ function CompiledMatch(props: { routeId: string }) @{
71
83
  const store = router.stores.getMatchStore(props.routeId);
72
84
  const ssr = useStore(store, (match: any) => match.ssr);
73
85
  const route = (router.routesById as Record<string, any>)[props.routeId];
74
- if (route.isRoot && route.options.shellComponent) {
75
- throw new Error(
76
- 'CompiledRouterProvider does not accept shellComponent. Use RouterProvider for renderable document shells.',
77
- );
78
- }
79
86
  const pending = route.options.pendingComponent ?? router.options.defaultPendingComponent;
80
87
  const errorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent;
81
88
  const noSsr = ssr === false || ssr === 'data-only';
82
89
  const hasSuspense =
83
90
  (!route.isRoot || route.options.wrapInSuspense || noSsr) &&
84
91
  ((route.options.wrapInSuspense ?? pending ?? errorComponent?.preload) || noSsr);
85
- <CompiledContext context={matchContext} value={props.routeId}>
86
- @if (hasSuspense) {
87
- @try {
92
+ <CompiledWrap component={route.isRoot ? route.options.shellComponent : undefined}>
93
+ <CompiledContext context={matchContext} value={props.routeId}>
94
+ @if (hasSuspense) {
95
+ @try {
96
+ <CompiledMatchContent route={route} routeId={props.routeId} noSsr={noSsr} />
97
+ } @pending {
98
+ <CompiledPending route={route} />
99
+ }
100
+ } @else {
88
101
  <CompiledMatchContent route={route} routeId={props.routeId} noSsr={noSsr} />
89
- } @pending {
90
- <CompiledPending route={route} />
91
102
  }
92
- } @else {
93
- <CompiledMatchContent route={route} routeId={props.routeId} noSsr={noSsr} />
94
- }
103
+ </CompiledContext>
95
104
  @if (route.parentRoute?.id === rootRouteId && router.options.scrollRestoration) {
96
105
  <CompiledScrollRestoration />
97
106
  }
98
- </CompiledContext>
107
+ </CompiledWrap>
99
108
  }
100
109
 
101
110
  // Without a configured Suspense boundary, both lazy imports and loader promises
@@ -1,3 +1,5 @@
1
+ 'use strong';
2
+
1
3
  import { createCompiledContext, useContext } from 'octane';
2
4
  import type { AnyRouter, RegisteredRouter } from '@tanstack/router-core';
3
5
  import { createRouterHooks } from '../hookBindings';
@@ -0,0 +1,71 @@
1
+ 'use strong';
2
+
3
+ import { BaseRoute, BaseRootRoute, BaseRouteApi, notFound } from '@tanstack/router-core';
4
+ import { createRouteHookBindings } from '../routeHookBindings';
5
+ import * as hooks from './bindings';
6
+ import { Link } from './Link.tsrx';
7
+ import { splitSlot, subSlot } from '../internal';
8
+ import type * as RouteFactories from '../route';
9
+ import type * as FileFactories from '../fileRoute';
10
+
11
+ const { attachRouteHooks, attachRouteNavigation, attachLazyRouteHooks } = createRouteHookBindings(
12
+ hooks,
13
+ hooks.useRouter,
14
+ );
15
+
16
+ function attachRoute(route: any) {
17
+ attachRouteHooks(route, true);
18
+ attachRouteNavigation(route);
19
+ function RouteLink(props: any) @{
20
+ <Link from={route.fullPath} {...props} />
21
+ }
22
+ route.Link = RouteLink;
23
+ return route;
24
+ }
25
+
26
+ // These factories preserve the binding's precise route types, while constructing
27
+ // core instances directly: no ordinary-context hooks or descriptor Link are made.
28
+ export const createRoute: typeof RouteFactories.createRoute = (options: any) =>
29
+ attachRoute(new BaseRoute(options));
30
+ export const createRootRoute: typeof RouteFactories.createRootRoute = (options?: any) =>
31
+ attachRoute(new BaseRootRoute(options));
32
+ export const createRootRouteWithContext: typeof RouteFactories.createRootRouteWithContext = () =>
33
+ createRootRoute;
34
+ export const rootRouteWithContext = createRootRouteWithContext;
35
+
36
+ export const getRouteApi: typeof RouteFactories.getRouteApi = (id: any) => {
37
+ const route = new BaseRouteApi({ id }) as any;
38
+ attachRouteHooks(route, false);
39
+ route.useNavigate = (...args: any[]) => {
40
+ const [, slot] = splitSlot(args);
41
+ const router = hooks.useRouter();
42
+ return hooks.useNavigate(
43
+ { from: (router.routesById as any)[id].fullPath },
44
+ subSlot(slot, 'r:n'),
45
+ );
46
+ };
47
+ function RouteLink(props: any) @{
48
+ const router = hooks.useRouter();
49
+ <Link from={(router.routesById as any)[id].fullPath} {...props} />
50
+ }
51
+ route.Link = RouteLink;
52
+ route.notFound = (options: any) => notFound({ routeId: id, ...options });
53
+ return route;
54
+ };
55
+
56
+ export const createFileRoute: typeof FileFactories.createFileRoute = () => (options: any) => {
57
+ const route = createRoute(options) as any;
58
+ route.isRoot = false;
59
+ return route;
60
+ };
61
+
62
+ function lazyRoute(options: any) {
63
+ const route = { options } as any;
64
+ attachLazyRouteHooks(route);
65
+ return route;
66
+ }
67
+
68
+ export const createLazyRoute: typeof FileFactories.createLazyRoute = (id: any) => (options: any) =>
69
+ lazyRoute({ id, ...options });
70
+ export const createLazyFileRoute: typeof FileFactories.createLazyFileRoute = (id: any) =>
71
+ typeof id === 'object' ? lazyRoute(id) : (options: any) => lazyRoute({ id, ...options });
package/src/compiled.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Opt-in source-owned renderer for code routes with compiled bodies only.
1
+ // Opt-in source-owned renderer for route trees with compiled bodies.
2
2
  import './frameworkTypes';
3
3
  export * from '@tanstack/router-core';
4
4
  export { Router, createRouter } from './router';
@@ -8,6 +8,17 @@ export {
8
8
  createHashHistory,
9
9
  createMemoryHistory,
10
10
  } from './history';
11
+ export type {
12
+ RouterHistory,
13
+ HistoryLocation,
14
+ ParsedPath,
15
+ HistoryState,
16
+ ParsedHistoryState,
17
+ HistoryAction,
18
+ BlockerFnArgs,
19
+ BlockerFn,
20
+ NavigationBlocker,
21
+ } from '@tanstack/history';
11
22
  export {
12
23
  useRouter,
13
24
  useRouterState,
@@ -27,6 +38,30 @@ export {
27
38
  export type { UseRouterStateOptions, UseRouterStateResult } from './routerStateBinding';
28
39
  export {
29
40
  CompiledRouterProvider,
41
+ CompiledRouterProvider as RouterProvider,
30
42
  CompiledOutlet,
43
+ CompiledOutlet as Outlet,
31
44
  lazyCompiledRouteComponent,
45
+ lazyCompiledRouteComponent as lazyRouteComponent,
32
46
  } from './compiled/Matches.tsrx';
47
+ export {
48
+ createRoute,
49
+ createRootRoute,
50
+ createRootRouteWithContext,
51
+ rootRouteWithContext,
52
+ getRouteApi,
53
+ createFileRoute,
54
+ createLazyRoute,
55
+ createLazyFileRoute,
56
+ } from './compiled/route.tsrx';
57
+ export { Link, useLinkProps, createLink } from './compiled/Link.tsrx';
58
+ export { ClientOnly, useHydrated } from './ClientOnly.tsrx';
59
+ export { getAssetKeys } from './assetKeys';
60
+ export { Html, Head, Body, Asset, HeadContent, Scripts, useTags } from './compiled/Document.tsrx';
61
+ export type { AssetKeys } from './assetKeys';
62
+ export type {
63
+ LinkComponent,
64
+ LinkComponentProps,
65
+ LinkProps,
66
+ UseLinkPropsOptions,
67
+ } from './linkTypes';
package/src/fileRoute.ts CHANGED
@@ -10,7 +10,12 @@ import {
10
10
  useSearch,
11
11
  } from './hooks';
12
12
  import { useRouter } from './context';
13
- import { splitSlot, subSlot } from './internal';
13
+ import { createRouteHookBindings } from './routeHookBindings';
14
+
15
+ const { attachLazyRouteHooks } = createRouteHookBindings(
16
+ { useMatch, useParams, useSearch, useLoaderData, useLoaderDeps, useRouteContext, useNavigate },
17
+ useRouter,
18
+ );
14
19
  import type {
15
20
  AnyContext,
16
21
  AnyRoute,
@@ -198,63 +203,7 @@ export class LazyRoute<TRoute extends AnyRoute> {
198
203
 
199
204
  constructor(opts: { id: string } & LazyRouteOptions) {
200
205
  this.options = opts;
201
- const id = this.options.id;
202
- this.useMatch = ((...args: Array<any>) => {
203
- const [user, slot] = splitSlot(args);
204
- const options = user[0] ?? {};
205
- return useMatch(
206
- {
207
- select: options.select,
208
- from: id,
209
- structuralSharing: options.structuralSharing,
210
- },
211
- subSlot(slot, 'lr:m'),
212
- );
213
- }) as typeof this.useMatch;
214
- this.useRouteContext = ((...args: Array<any>) => {
215
- const [user, slot] = splitSlot(args);
216
- return useRouteContext({ ...(user[0] ?? {}), from: id }, subSlot(slot, 'lr:c'));
217
- }) as typeof this.useRouteContext;
218
- this.useSearch = ((...args: Array<any>) => {
219
- const [user, slot] = splitSlot(args);
220
- const options = user[0] ?? {};
221
- return useSearch(
222
- {
223
- select: options.select,
224
- from: id,
225
- structuralSharing: options.structuralSharing,
226
- },
227
- subSlot(slot, 'lr:s'),
228
- );
229
- }) as typeof this.useSearch;
230
- this.useParams = ((...args: Array<any>) => {
231
- const [user, slot] = splitSlot(args);
232
- const options = user[0] ?? {};
233
- return useParams(
234
- {
235
- select: options.select,
236
- from: id,
237
- structuralSharing: options.structuralSharing,
238
- },
239
- subSlot(slot, 'lr:p'),
240
- );
241
- }) as typeof this.useParams;
242
- this.useLoaderDeps = ((...args: Array<any>) => {
243
- const [user, slot] = splitSlot(args);
244
- return useLoaderDeps({ ...(user[0] ?? {}), from: id }, subSlot(slot, 'lr:d'));
245
- }) as typeof this.useLoaderDeps;
246
- this.useLoaderData = ((...args: Array<any>) => {
247
- const [user, slot] = splitSlot(args);
248
- return useLoaderData({ ...(user[0] ?? {}), from: id }, subSlot(slot, 'lr:l'));
249
- }) as typeof this.useLoaderData;
250
- this.useNavigate = ((...args: Array<any>) => {
251
- const [, slot] = splitSlot(args);
252
- const router = useRouter();
253
- return useNavigate(
254
- { from: (router.routesById as Record<string, any>)[id].fullPath },
255
- subSlot(slot, 'lr:n'),
256
- );
257
- }) as typeof this.useNavigate;
206
+ attachLazyRouteHooks(this);
258
207
  }
259
208
  }
260
209
 
@@ -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
+ }
@@ -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 { 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';
2
+ import { createUseTags } from './headContentBinding';
19
3
 
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
- }
4
+ export const useTags = createUseTags(useRouter);
package/src/internal.ts CHANGED
@@ -1,3 +1,5 @@
1
+ 'use strong';
2
+
1
3
  // Slot mechanics shared by the binding's plain-`.ts` hooks (copied from
2
4
  // @octanejs/tanstack-query). The octane compiler injects a per-call-site Symbol slot into
3
5
  // every hook call in `.tsrx`/`.tsx`, but these binding files are NOT compiled — so