@tanstack/solid-router 1.147.1 → 1.147.3

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 (58) hide show
  1. package/dist/cjs/HeadContent.cjs +5 -196
  2. package/dist/cjs/HeadContent.cjs.map +1 -1
  3. package/dist/cjs/HeadContent.d.cts +1 -4
  4. package/dist/cjs/HeadContent.dev.cjs +36 -0
  5. package/dist/cjs/HeadContent.dev.cjs.map +1 -0
  6. package/dist/cjs/HeadContent.dev.d.cts +10 -0
  7. package/dist/cjs/headContentUtils.cjs +174 -0
  8. package/dist/cjs/headContentUtils.cjs.map +1 -0
  9. package/dist/cjs/headContentUtils.d.cts +7 -0
  10. package/dist/cjs/index.cjs +2 -1
  11. package/dist/cjs/index.cjs.map +1 -1
  12. package/dist/cjs/index.d.cts +2 -1
  13. package/dist/cjs/index.dev.cjs +259 -0
  14. package/dist/cjs/index.dev.cjs.map +1 -0
  15. package/dist/cjs/index.dev.d.cts +2 -0
  16. package/dist/cjs/ssr/RouterServer.cjs +2 -2
  17. package/dist/cjs/ssr/RouterServer.cjs.map +1 -1
  18. package/dist/esm/HeadContent.d.ts +1 -4
  19. package/dist/esm/HeadContent.dev.d.ts +10 -0
  20. package/dist/esm/HeadContent.dev.js +36 -0
  21. package/dist/esm/HeadContent.dev.js.map +1 -0
  22. package/dist/esm/HeadContent.js +6 -181
  23. package/dist/esm/HeadContent.js.map +1 -1
  24. package/dist/esm/headContentUtils.d.ts +7 -0
  25. package/dist/esm/headContentUtils.js +157 -0
  26. package/dist/esm/headContentUtils.js.map +1 -0
  27. package/dist/esm/index.d.ts +2 -1
  28. package/dist/esm/index.dev.d.ts +2 -0
  29. package/dist/esm/index.dev.js +142 -0
  30. package/dist/esm/index.dev.js.map +1 -0
  31. package/dist/esm/index.js +2 -1
  32. package/dist/esm/index.js.map +1 -1
  33. package/dist/esm/ssr/RouterServer.js +1 -1
  34. package/dist/esm/ssr/RouterServer.js.map +1 -1
  35. package/dist/source/HeadContent.d.ts +1 -4
  36. package/dist/source/HeadContent.dev.d.ts +10 -0
  37. package/dist/source/HeadContent.dev.jsx +39 -0
  38. package/dist/source/HeadContent.dev.jsx.map +1 -0
  39. package/dist/source/HeadContent.jsx +2 -202
  40. package/dist/source/HeadContent.jsx.map +1 -1
  41. package/dist/source/headContentUtils.d.ts +7 -0
  42. package/dist/source/headContentUtils.jsx +181 -0
  43. package/dist/source/headContentUtils.jsx.map +1 -0
  44. package/dist/source/index.d.ts +2 -1
  45. package/dist/source/index.dev.d.ts +2 -0
  46. package/dist/source/index.dev.jsx +6 -0
  47. package/dist/source/index.dev.jsx.map +1 -0
  48. package/dist/source/index.jsx +2 -1
  49. package/dist/source/index.jsx.map +1 -1
  50. package/dist/source/ssr/RouterServer.jsx +1 -1
  51. package/dist/source/ssr/RouterServer.jsx.map +1 -1
  52. package/package.json +4 -1
  53. package/src/HeadContent.dev.tsx +45 -0
  54. package/src/HeadContent.tsx +2 -235
  55. package/src/headContentUtils.tsx +209 -0
  56. package/src/index.dev.tsx +6 -0
  57. package/src/index.tsx +2 -1
  58. package/src/ssr/RouterServer.tsx +1 -1
@@ -0,0 +1,209 @@
1
+ import * as Solid from 'solid-js'
2
+ import { escapeHtml } from '@tanstack/router-core'
3
+ import { useRouter } from './useRouter'
4
+ import { useRouterState } from './useRouterState'
5
+ import type { RouterManagedTag } from '@tanstack/router-core'
6
+
7
+ /**
8
+ * Build the list of head/link/meta/script tags to render for active matches.
9
+ * Used internally by `HeadContent`.
10
+ */
11
+ export const useTags = () => {
12
+ const router = useRouter()
13
+ const nonce = router.options.ssr?.nonce
14
+ const routeMeta = useRouterState({
15
+ select: (state) => {
16
+ return state.matches.map((match) => match.meta!).filter(Boolean)
17
+ },
18
+ })
19
+
20
+ const meta: Solid.Accessor<Array<RouterManagedTag>> = Solid.createMemo(() => {
21
+ const resultMeta: Array<RouterManagedTag> = []
22
+ const metaByAttribute: Record<string, true> = {}
23
+ let title: RouterManagedTag | undefined
24
+ const routeMetasArray = routeMeta()
25
+ for (let i = routeMetasArray.length - 1; i >= 0; i--) {
26
+ const metas = routeMetasArray[i]!
27
+ for (let j = metas.length - 1; j >= 0; j--) {
28
+ const m = metas[j]
29
+ if (!m) continue
30
+
31
+ if (m.title) {
32
+ if (!title) {
33
+ title = {
34
+ tag: 'title',
35
+ children: m.title,
36
+ }
37
+ }
38
+ } else if ('script:ld+json' in m) {
39
+ // Handle JSON-LD structured data
40
+ // Content is HTML-escaped to prevent XSS when injected via innerHTML
41
+ try {
42
+ const json = JSON.stringify(m['script:ld+json'])
43
+ resultMeta.push({
44
+ tag: 'script',
45
+ attrs: {
46
+ type: 'application/ld+json',
47
+ },
48
+ children: escapeHtml(json),
49
+ })
50
+ } catch {
51
+ // Skip invalid JSON-LD objects
52
+ }
53
+ } else {
54
+ const attribute = m.name ?? m.property
55
+ if (attribute) {
56
+ if (metaByAttribute[attribute]) {
57
+ continue
58
+ } else {
59
+ metaByAttribute[attribute] = true
60
+ }
61
+ }
62
+
63
+ resultMeta.push({
64
+ tag: 'meta',
65
+ attrs: {
66
+ ...m,
67
+ nonce,
68
+ },
69
+ })
70
+ }
71
+ }
72
+ }
73
+
74
+ if (title) {
75
+ resultMeta.push(title)
76
+ }
77
+
78
+ if (router.options.ssr?.nonce) {
79
+ resultMeta.push({
80
+ tag: 'meta',
81
+ attrs: {
82
+ property: 'csp-nonce',
83
+ content: router.options.ssr.nonce,
84
+ },
85
+ })
86
+ }
87
+ resultMeta.reverse()
88
+
89
+ return resultMeta
90
+ })
91
+
92
+ const links = useRouterState({
93
+ select: (state) => {
94
+ const constructed = state.matches
95
+ .map((match) => match.links!)
96
+ .filter(Boolean)
97
+ .flat(1)
98
+ .map((link) => ({
99
+ tag: 'link',
100
+ attrs: {
101
+ ...link,
102
+ nonce,
103
+ },
104
+ })) satisfies Array<RouterManagedTag>
105
+
106
+ const manifest = router.ssr?.manifest
107
+
108
+ const assets = state.matches
109
+ .map((match) => manifest?.routes[match.routeId]?.assets ?? [])
110
+ .filter(Boolean)
111
+ .flat(1)
112
+ .filter((asset) => asset.tag === 'link')
113
+ .map(
114
+ (asset) =>
115
+ ({
116
+ tag: 'link',
117
+ attrs: { ...asset.attrs, nonce },
118
+ }) satisfies RouterManagedTag,
119
+ )
120
+
121
+ return [...constructed, ...assets]
122
+ },
123
+ })
124
+
125
+ const preloadLinks = useRouterState({
126
+ select: (state) => {
127
+ const preloadLinks: Array<RouterManagedTag> = []
128
+
129
+ state.matches
130
+ .map((match) => router.looseRoutesById[match.routeId]!)
131
+ .forEach((route) =>
132
+ router.ssr?.manifest?.routes[route.id]?.preloads
133
+ ?.filter(Boolean)
134
+ .forEach((preload) => {
135
+ preloadLinks.push({
136
+ tag: 'link',
137
+ attrs: {
138
+ rel: 'modulepreload',
139
+ href: preload,
140
+ nonce,
141
+ },
142
+ })
143
+ }),
144
+ )
145
+
146
+ return preloadLinks
147
+ },
148
+ })
149
+
150
+ const styles = useRouterState({
151
+ select: (state) =>
152
+ (
153
+ state.matches
154
+ .map((match) => match.styles!)
155
+ .flat(1)
156
+ .filter(Boolean) as Array<RouterManagedTag>
157
+ ).map(({ children, ...style }) => ({
158
+ tag: 'style',
159
+ attrs: {
160
+ ...style,
161
+ nonce,
162
+ },
163
+ children,
164
+ })),
165
+ })
166
+
167
+ const headScripts = useRouterState({
168
+ select: (state) =>
169
+ (
170
+ state.matches
171
+ .map((match) => match.headScripts!)
172
+ .flat(1)
173
+ .filter(Boolean) as Array<RouterManagedTag>
174
+ ).map(({ children, ...script }) => ({
175
+ tag: 'script',
176
+ attrs: {
177
+ ...script,
178
+ nonce,
179
+ },
180
+ children,
181
+ })),
182
+ })
183
+
184
+ return () =>
185
+ uniqBy(
186
+ [
187
+ ...meta(),
188
+ ...preloadLinks(),
189
+ ...links(),
190
+ ...styles(),
191
+ ...headScripts(),
192
+ ] as Array<RouterManagedTag>,
193
+ (d) => {
194
+ return JSON.stringify(d)
195
+ },
196
+ )
197
+ }
198
+
199
+ export function uniqBy<T>(arr: Array<T>, fn: (item: T) => string) {
200
+ const seen = new Set<string>()
201
+ return arr.filter((item) => {
202
+ const key = fn(item)
203
+ if (seen.has(key)) {
204
+ return false
205
+ }
206
+ seen.add(key)
207
+ return true
208
+ })
209
+ }
@@ -0,0 +1,6 @@
1
+ // Development entry point - re-exports everything from index.tsx
2
+ // but overrides HeadContent with the dev version that handles
3
+ // dev styles cleanup after hydration
4
+
5
+ export * from './index'
6
+ export { HeadContent } from './HeadContent.dev'
package/src/index.tsx CHANGED
@@ -346,7 +346,8 @@ export type {
346
346
  export { ScriptOnce } from './ScriptOnce'
347
347
 
348
348
  export { Asset } from './Asset'
349
- export { HeadContent, useTags } from './HeadContent'
349
+ export { HeadContent } from './HeadContent'
350
+ export { useTags } from './headContentUtils'
350
351
  export { Scripts } from './Scripts'
351
352
  export { composeRewrites } from '@tanstack/router-core'
352
353
  export type {
@@ -7,7 +7,7 @@ import {
7
7
  } from 'solid-js/web'
8
8
  import { MetaProvider } from '@solidjs/meta'
9
9
  import { Asset } from '../Asset'
10
- import { useTags } from '../HeadContent'
10
+ import { useTags } from '../headContentUtils'
11
11
  import { RouterProvider } from '../RouterProvider'
12
12
  import { Scripts } from '../Scripts'
13
13
  import type { AnyRouter } from '@tanstack/router-core'