@tanstack/redact 0.0.21 → 0.1.0
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/dist/core/attributes.js +2 -1
- package/dist/core/attributes.js.map +2 -2
- package/dist/core/browser.d.ts +9 -0
- package/dist/core/browser.js +35 -0
- package/dist/core/browser.js.map +7 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +3 -3
- package/dist/core/internal.d.ts +31 -8
- package/dist/core/internal.js +3 -1
- package/dist/core/internal.js.map +2 -2
- package/dist/core/resource-hints.d.ts +7 -0
- package/dist/core/resource-hints.js +19 -0
- package/dist/core/resource-hints.js.map +7 -0
- package/dist/core/types.js +1 -1
- package/dist/dom/_all.d.ts +11 -16
- package/dist/dom/_all.js +9 -20
- package/dist/dom/_all.js.map +2 -2
- package/dist/dom/client.d.ts +4 -4
- package/dist/dom/client.js +4 -4
- package/dist/dom/commit.d.ts +23 -0
- package/dist/dom/commit.js +191 -0
- package/dist/dom/commit.js.map +7 -0
- package/dist/dom/dispatcher.d.ts +4 -22
- package/dist/dom/dispatcher.js +184 -126
- package/dist/dom/dispatcher.js.map +3 -3
- package/dist/dom/dom.d.ts +2 -0
- package/dist/dom/dom.js +72 -52
- package/dist/dom/dom.js.map +2 -2
- package/dist/dom/error-info.d.ts +2 -0
- package/dist/dom/error-info.js +16 -0
- package/dist/dom/error-info.js.map +7 -0
- package/dist/dom/event-replay.js +1 -1
- package/dist/dom/features/activity/full.d.ts +1 -0
- package/dist/dom/features/activity/full.js +162 -0
- package/dist/dom/features/activity/full.js.map +7 -0
- package/dist/dom/features/activity/index.d.ts +1 -0
- package/dist/dom/features/activity/index.js +3 -0
- package/dist/dom/features/activity/index.js.map +7 -0
- package/dist/dom/features/activity/stub.d.ts +1 -0
- package/dist/dom/features/activity/stub.js +12 -0
- package/dist/dom/features/activity/stub.js.map +7 -0
- package/dist/dom/features/class/full.js +87 -27
- package/dist/dom/features/class/full.js.map +2 -2
- package/dist/dom/features/class/index.d.ts +1 -1
- package/dist/dom/features/class/index.js +2 -2
- package/dist/dom/features/class/stub.js +3 -3
- package/dist/dom/features/context/full.d.ts +4 -1
- package/dist/dom/features/context/full.js +69 -9
- package/dist/dom/features/context/full.js.map +2 -2
- package/dist/dom/features/context/index.d.ts +1 -1
- package/dist/dom/features/context/index.js +2 -2
- package/dist/dom/features/context/stub.d.ts +4 -1
- package/dist/dom/features/context/stub.js +15 -4
- package/dist/dom/features/context/stub.js.map +2 -2
- package/dist/dom/features/forward-ref/full.js +12 -21
- package/dist/dom/features/forward-ref/full.js.map +2 -2
- package/dist/dom/features/forward-ref/index.d.ts +1 -1
- package/dist/dom/features/forward-ref/index.js +2 -2
- package/dist/dom/features/forward-ref/stub.js +4 -4
- package/dist/dom/features/fragment-refs/index.d.ts +1 -0
- package/dist/dom/features/fragment-refs/index.js +8 -0
- package/dist/dom/features/fragment-refs/index.js.map +7 -0
- package/dist/dom/features/fragment-refs/stub.d.ts +3 -0
- package/dist/dom/features/fragment-refs/stub.js +14 -0
- package/dist/dom/features/fragment-refs/stub.js.map +7 -0
- package/dist/dom/features/hydration/full.d.ts +11 -6
- package/dist/dom/features/hydration/full.js +76 -77
- package/dist/dom/features/hydration/full.js.map +2 -2
- package/dist/dom/features/hydration/index.d.ts +1 -1
- package/dist/dom/features/hydration/index.js +2 -2
- package/dist/dom/features/hydration/stub.d.ts +7 -2
- package/dist/dom/features/hydration/stub.js +1 -1
- package/dist/dom/features/hydration/stub.js.map +1 -1
- package/dist/dom/features/index.d.ts +8 -7
- package/dist/dom/features/index.js +9 -8
- package/dist/dom/features/index.js.map +2 -2
- package/dist/dom/features/lazy/full.js +4 -4
- package/dist/dom/features/lazy/index.d.ts +1 -1
- package/dist/dom/features/lazy/index.js +2 -2
- package/dist/dom/features/lazy/stub.js +4 -4
- package/dist/dom/features/memo/full.js +8 -6
- package/dist/dom/features/memo/full.js.map +2 -2
- package/dist/dom/features/memo/index.d.ts +1 -1
- package/dist/dom/features/memo/index.js +2 -2
- package/dist/dom/features/memo/stub.js +4 -4
- package/dist/dom/features/portal/full.js +4 -4
- package/dist/dom/features/portal/index.d.ts +1 -1
- package/dist/dom/features/portal/index.js +2 -2
- package/dist/dom/features/portal/stub.js +4 -4
- package/dist/dom/features/suspense/full.js +197 -52
- package/dist/dom/features/suspense/full.js.map +2 -2
- package/dist/dom/features/suspense/index.d.ts +1 -1
- package/dist/dom/features/suspense/index.js +2 -2
- package/dist/dom/features/suspense/stub.js +4 -4
- package/dist/dom/features/view-transition/full.d.ts +6 -0
- package/dist/dom/features/view-transition/full.js +280 -0
- package/dist/dom/features/view-transition/full.js.map +7 -0
- package/dist/dom/features/view-transition/index.d.ts +1 -0
- package/dist/dom/features/view-transition/index.js +3 -0
- package/dist/dom/features/view-transition/index.js.map +7 -0
- package/dist/dom/features/view-transition/resources.d.ts +11 -0
- package/dist/dom/features/view-transition/resources.js +108 -0
- package/dist/dom/features/view-transition/resources.js.map +7 -0
- package/dist/dom/features/view-transition/stub.d.ts +6 -0
- package/dist/dom/features/view-transition/stub.js +22 -0
- package/dist/dom/features/view-transition/stub.js.map +7 -0
- package/dist/dom/features/view-transition/visual.d.ts +14 -0
- package/dist/dom/features/view-transition/visual.js +356 -0
- package/dist/dom/features/view-transition/visual.js.map +7 -0
- package/dist/dom/fragment-instance.d.ts +40 -0
- package/dist/dom/fragment-instance.js +324 -0
- package/dist/dom/fragment-instance.js.map +7 -0
- package/dist/dom/index.d.ts +11 -11
- package/dist/dom/index.js +11 -18
- package/dist/dom/index.js.map +3 -3
- package/dist/dom/portal.d.ts +2 -2
- package/dist/dom/portal.js +2 -2
- package/dist/dom/portal.js.map +1 -1
- package/dist/dom/reconcile.d.ts +32 -3
- package/dist/dom/reconcile.js +689 -221
- package/dist/dom/reconcile.js.map +2 -2
- package/dist/dom/resource-hints.d.ts +6 -0
- package/dist/dom/resource-hints.js +115 -0
- package/dist/dom/resource-hints.js.map +7 -0
- package/dist/dom/resource-store.d.ts +13 -0
- package/dist/dom/resource-store.js +43 -0
- package/dist/dom/resource-store.js.map +7 -0
- package/dist/dom/resources.d.ts +1 -0
- package/dist/dom/resources.js +44 -0
- package/dist/dom/resources.js.map +7 -0
- package/dist/dom/retained-effects.d.ts +6 -0
- package/dist/dom/retained-effects.js +49 -0
- package/dist/dom/retained-effects.js.map +7 -0
- package/dist/dom/root-internal.d.ts +4 -4
- package/dist/dom/root-internal.js +2 -2
- package/dist/dom/root-internal.js.map +2 -2
- package/dist/dom/root.d.ts +5 -5
- package/dist/dom/root.js +11 -11
- package/dist/dom/root.js.map +2 -2
- package/dist/dom/test-utils.js +1 -1
- package/dist/react/activity.d.ts +7 -0
- package/dist/react/activity.js +8 -0
- package/dist/react/activity.js.map +7 -0
- package/dist/react/children.d.ts +1 -1
- package/dist/react/children.js +3 -3
- package/dist/react/children.js.map +1 -1
- package/dist/react/class.d.ts +1 -1
- package/dist/react/class.js +3 -3
- package/dist/react/class.js.map +1 -1
- package/dist/react/compiler-runtime.js +2 -2
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js +3 -3
- package/dist/react/context.js.map +1 -1
- package/dist/react/element.d.ts +6 -3
- package/dist/react/element.js +2 -2
- package/dist/react/element.js.map +2 -2
- package/dist/react/hooks.d.ts +26 -7
- package/dist/react/hooks.js +13 -3
- package/dist/react/hooks.js.map +2 -2
- package/dist/react/index.d.ts +39 -24
- package/dist/react/index.js +39 -19
- package/dist/react/index.js.map +3 -3
- package/dist/react/jsx-runtime.d.ts +2 -2
- package/dist/react/jsx-runtime.js +3 -3
- package/dist/react/memo.d.ts +1 -1
- package/dist/react/memo.js +1 -1
- package/dist/react/portal.js +1 -1
- package/dist/react/shared-internals.d.ts +15 -5
- package/dist/react/shared-internals.js +2 -3
- package/dist/react/shared-internals.js.map +2 -2
- package/dist/react/suspense.js +1 -1
- package/dist/react/view-transition.d.ts +32 -0
- package/dist/react/view-transition.js +13 -0
- package/dist/react/view-transition.js.map +7 -0
- package/dist/scheduler/index.js +1 -1
- package/dist/server/bootstrap-script.d.ts +1 -23
- package/dist/server/bootstrap-script.js +7 -6
- package/dist/server/bootstrap-script.js.map +2 -2
- package/dist/server/dispatcher.d.ts +28 -6
- package/dist/server/dispatcher.js +33 -15
- package/dist/server/dispatcher.js.map +2 -2
- package/dist/server/escape.js +16 -15
- package/dist/server/escape.js.map +2 -2
- package/dist/server/index.d.ts +6 -6
- package/dist/server/index.js +7 -7
- package/dist/server/renderToString.d.ts +1 -1
- package/dist/server/renderToString.js +12 -8
- package/dist/server/renderToString.js.map +2 -2
- package/dist/server/resource-hints.d.ts +22 -0
- package/dist/server/resource-hints.js +148 -0
- package/dist/server/resource-hints.js.map +7 -0
- package/dist/server/stream.d.ts +15 -2
- package/dist/server/stream.js +155 -63
- package/dist/server/stream.js.map +2 -2
- package/dist/server/walk.d.ts +15 -2
- package/dist/server/walk.js +143 -96
- package/dist/server/walk.js.map +2 -2
- package/dist/vite/index.d.ts +6 -0
- package/dist/vite/index.js +22 -6
- package/dist/vite/index.js.map +2 -2
- package/package.json +1 -1
- package/src/core/attributes.ts +1 -0
- package/src/core/browser.ts +33 -0
- package/src/core/internal.ts +24 -6
- package/src/core/resource-hints.ts +36 -0
- package/src/dom/_all.ts +1 -7
- package/src/dom/commit.ts +186 -0
- package/src/dom/dispatcher.ts +190 -134
- package/src/dom/dom.ts +70 -54
- package/src/dom/error-info.ts +15 -0
- package/src/dom/features/activity/full.ts +166 -0
- package/src/dom/features/activity/index.ts +1 -0
- package/src/dom/features/activity/stub.ts +14 -0
- package/src/dom/features/class/full.ts +72 -28
- package/src/dom/features/context/full.ts +66 -4
- package/src/dom/features/context/stub.ts +6 -0
- package/src/dom/features/forward-ref/full.ts +9 -19
- package/src/dom/features/fragment-refs/index.ts +1 -0
- package/src/dom/features/fragment-refs/stub.ts +3 -0
- package/src/dom/features/hydration/full.ts +84 -85
- package/src/dom/features/hydration/stub.ts +1 -1
- package/src/dom/features/index.ts +1 -0
- package/src/dom/features/memo/full.ts +4 -3
- package/src/dom/features/suspense/full.ts +192 -55
- package/src/dom/features/view-transition/full.ts +288 -0
- package/src/dom/features/view-transition/index.ts +1 -0
- package/src/dom/features/view-transition/resources.ts +107 -0
- package/src/dom/features/view-transition/stub.ts +7 -0
- package/src/dom/features/view-transition/visual.ts +406 -0
- package/src/dom/fragment-instance.ts +337 -0
- package/src/dom/index.ts +6 -7
- package/src/dom/portal.ts +1 -1
- package/src/dom/reconcile.ts +695 -242
- package/src/dom/resource-hints.ts +115 -0
- package/src/dom/resource-store.ts +52 -0
- package/src/dom/resources.ts +39 -0
- package/src/dom/retained-effects.ts +38 -0
- package/src/dom/root-internal.ts +4 -4
- package/src/dom/root.ts +12 -14
- package/src/react/activity.ts +10 -0
- package/src/react/element.ts +6 -2
- package/src/react/hooks.ts +12 -3
- package/src/react/index.ts +18 -0
- package/src/react/shared-internals.ts +3 -3
- package/src/react/view-transition.ts +41 -0
- package/src/server/bootstrap-script.ts +36 -5
- package/src/server/dispatcher.ts +35 -13
- package/src/server/escape.ts +15 -12
- package/src/server/renderToString.ts +7 -5
- package/src/server/resource-hints.ts +165 -0
- package/src/server/stream.ts +154 -62
- package/src/server/walk.ts +72 -11
- package/src/vite/index.ts +30 -13
package/src/server/dispatcher.ts
CHANGED
|
@@ -1,18 +1,41 @@
|
|
|
1
1
|
import { ReactSharedInternals, REACT_CONTEXT_TYPE } from '../react'
|
|
2
|
+
import { isBrowserToken, browserError } from '../core/browser'
|
|
3
|
+
import { resourceHintDispatcher } from '../core/resource-hints'
|
|
4
|
+
import { createResourceHints, type ResourceScope } from './resource-hints'
|
|
2
5
|
|
|
3
|
-
interface SSRFrame {
|
|
6
|
+
export interface SSRFrame {
|
|
4
7
|
idCounter: number
|
|
5
8
|
identifierPrefix: string
|
|
6
9
|
contextStack: Array<{ context: any; prev: any }>
|
|
10
|
+
resources?: ReturnType<typeof createResourceHints>
|
|
11
|
+
shellFlushed?: boolean
|
|
12
|
+
styleNonce?: string | undefined
|
|
7
13
|
}
|
|
8
14
|
|
|
9
15
|
let frame: SSRFrame | null = null
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
frame
|
|
17
|
+
resourceHintDispatcher.emit = (kind, href, options) => {
|
|
18
|
+
if (!frame) return false
|
|
19
|
+
;(frame.resources ||= createResourceHints(!!frame.shellFlushed, frame.styleNonce)).add(kind, href, options)
|
|
20
|
+
return true
|
|
13
21
|
}
|
|
14
22
|
|
|
15
|
-
export function
|
|
23
|
+
export function beginSSR(identifierPrefix = ':R', nextFrame?: SSRFrame) {
|
|
24
|
+
const previous = { frame, dispatcher: ReactSharedInternals.H }
|
|
25
|
+
frame = nextFrame ?? { idCounter: 0, identifierPrefix, contextStack: [] }
|
|
26
|
+
ReactSharedInternals.H = ssrDispatcher as any
|
|
27
|
+
return previous
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function currentSSRFrame(): SSRFrame {
|
|
31
|
+
return frame!
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function recordHostResource(type: string, props: any, scope?: ResourceScope): boolean {
|
|
35
|
+
return !!frame && (frame.resources ||= createResourceHints(!!frame.shellFlushed, frame.styleNonce)).addHost(type, props, scope)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function endSSR(previous?: ReturnType<typeof beginSSR>): void {
|
|
16
39
|
// Restore any remaining pushed contexts (defensive)
|
|
17
40
|
if (frame) {
|
|
18
41
|
for (let i = frame.contextStack.length - 1; i >= 0; i--) {
|
|
@@ -20,7 +43,10 @@ export function endSSR(): void {
|
|
|
20
43
|
context._currentValue = prev
|
|
21
44
|
}
|
|
22
45
|
}
|
|
23
|
-
|
|
46
|
+
// Only synchronous renders restore their caller. A stream can finish
|
|
47
|
+
// after its caller has returned, so it must not reinstall that dispatcher.
|
|
48
|
+
ReactSharedInternals.H = previous?.dispatcher ?? null
|
|
49
|
+
frame = previous?.frame ?? null
|
|
24
50
|
}
|
|
25
51
|
|
|
26
52
|
export function pushContext(context: any, value: any): void {
|
|
@@ -59,6 +85,9 @@ export function applyContextSnapshot(snapshot: ContextSnapshot): () => void {
|
|
|
59
85
|
}
|
|
60
86
|
|
|
61
87
|
export const ssrDispatcher = {
|
|
88
|
+
useCacheRefresh() {
|
|
89
|
+
return () => { throw new Error('Refreshing the cache is not supported during server rendering.') }
|
|
90
|
+
},
|
|
62
91
|
useState<S>(initial: S | (() => S)) {
|
|
63
92
|
const v = typeof initial === 'function' ? (initial as () => S)() : initial
|
|
64
93
|
return [v, (() => {}) as any] as [S, any]
|
|
@@ -113,6 +142,7 @@ export const ssrDispatcher = {
|
|
|
113
142
|
},
|
|
114
143
|
use<T>(resource: any): T {
|
|
115
144
|
if (resource == null) throw new Error('use() received null or undefined')
|
|
145
|
+
if (isBrowserToken(resource)) throw browserError(resource)
|
|
116
146
|
if (resource.$$typeof === REACT_CONTEXT_TYPE) {
|
|
117
147
|
return resource._currentValue
|
|
118
148
|
}
|
|
@@ -148,11 +178,3 @@ export const ssrDispatcher = {
|
|
|
148
178
|
throw new Error('use() expected a Promise or Context')
|
|
149
179
|
},
|
|
150
180
|
}
|
|
151
|
-
|
|
152
|
-
export function installSSRDispatcher(): void {
|
|
153
|
-
ReactSharedInternals.H = ssrDispatcher as any
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export function uninstallSSRDispatcher(): void {
|
|
157
|
-
ReactSharedInternals.H = null
|
|
158
|
-
}
|
package/src/server/escape.ts
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import { attributeName, STRING_BOOLEAN_ATTRS } from '../core/attributes'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
function escape(value: string, pattern: RegExp): string {
|
|
4
|
+
let out = ''
|
|
5
|
+
let start = 0
|
|
6
|
+
while (pattern.test(value)) {
|
|
7
|
+
const end = pattern.lastIndex
|
|
8
|
+
const char = value[end - 1]
|
|
9
|
+
out += value.slice(start, end - 1) + (
|
|
10
|
+
char === '&' ? '&' : char === '<' ? '<' : char === '>' ? '>' : '"'
|
|
11
|
+
)
|
|
12
|
+
start = end
|
|
13
|
+
}
|
|
14
|
+
return out + value.slice(start)
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export function escapeAttr(value: string): string {
|
|
16
|
-
return value
|
|
18
|
+
return /[&"<>]/.test(value) ? escape(value, /[&"<>]/g) : value
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export function escapeText(value: string): string {
|
|
20
|
-
return value
|
|
22
|
+
return /[&<>]/.test(value) ? escape(value, /[&<>]/g) : value
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
// Raw-text element body escaping: prevent any closing tag for the raw-text
|
|
@@ -57,6 +59,7 @@ const BOOLEAN_ATTRS = new Set([
|
|
|
57
59
|
'autoplay',
|
|
58
60
|
'checked',
|
|
59
61
|
'controls',
|
|
62
|
+
'credentialless',
|
|
60
63
|
'default',
|
|
61
64
|
'defer',
|
|
62
65
|
'disabled',
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import type { ReactNode } from '../core'
|
|
2
|
-
import { beginSSR,
|
|
2
|
+
import { beginSSR, currentSSRFrame, endSSR } from './dispatcher'
|
|
3
3
|
import { walk } from './walk'
|
|
4
4
|
|
|
5
5
|
export function renderToString(
|
|
6
6
|
children: ReactNode,
|
|
7
7
|
options: { identifierPrefix?: string } = {},
|
|
8
8
|
): string {
|
|
9
|
-
|
|
10
|
-
beginSSR(options.identifierPrefix)
|
|
9
|
+
const previous = beginSSR(options.identifierPrefix)
|
|
11
10
|
let output = ''
|
|
12
11
|
let id = 0
|
|
12
|
+
let head = 0
|
|
13
13
|
try {
|
|
14
14
|
walk(children, {
|
|
15
15
|
emit: (s) => {
|
|
16
16
|
output += s
|
|
17
17
|
},
|
|
18
18
|
nextBoundaryId: () => id++,
|
|
19
|
+
onHead: () => { head = output.length },
|
|
19
20
|
})
|
|
21
|
+
const hints = currentSSRFrame().resources?.drain()
|
|
22
|
+
if (hints) output = output.slice(0, head) + hints + output.slice(head)
|
|
20
23
|
} finally {
|
|
21
|
-
endSSR()
|
|
22
|
-
uninstallSSRDispatcher()
|
|
24
|
+
endSSR(previous)
|
|
23
25
|
}
|
|
24
26
|
return output
|
|
25
27
|
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { resourceKind, resourceURL, type ResourceHintKind, type ResourceHintOptions } from '../core/resource-hints'
|
|
2
|
+
import { attrToHtml, escapeAttr } from './escape'
|
|
3
|
+
import { attributeName } from '../core/attributes'
|
|
4
|
+
|
|
5
|
+
interface Resource {
|
|
6
|
+
kind: ResourceHintKind | 'style'
|
|
7
|
+
href: string
|
|
8
|
+
options: Record<string, any>
|
|
9
|
+
sent?: boolean
|
|
10
|
+
cancelled?: boolean
|
|
11
|
+
preloaded?: boolean
|
|
12
|
+
css?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ResourceScope {
|
|
16
|
+
stylesheets?: Map<string, string[]>
|
|
17
|
+
inline?: Set<string>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function styleText(props: any): string {
|
|
21
|
+
const html = props.dangerouslySetInnerHTML
|
|
22
|
+
if (html != null) {
|
|
23
|
+
if (typeof html !== 'object' || !('__html' in html)) throw new Error('dangerouslySetInnerHTML must be an object with an __html field.')
|
|
24
|
+
if (props.children != null) throw new Error('Can only set one of children or dangerouslySetInnerHTML.')
|
|
25
|
+
return html.__html == null ? '' : '' + html.__html
|
|
26
|
+
}
|
|
27
|
+
const child = Array.isArray(props.children) ? props.children.length < 2 ? props.children[0] : null : props.children
|
|
28
|
+
return child == null || typeof child === 'function' || typeof child === 'symbol' ? ''
|
|
29
|
+
: ('' + child).replace(/(<\/?)(s)(tyle)/gi, (_, prefix: string, s: string, suffix: string) => prefix + '\\' + s.charCodeAt(0).toString(16) + ' ' + suffix)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function markup(resource: Resource, late: boolean): string {
|
|
33
|
+
const { kind, href, options } = resource
|
|
34
|
+
const script = kind === 'script' || kind === 'module-script'
|
|
35
|
+
const style = kind === 'stylesheet'
|
|
36
|
+
const props: Record<string, any> = script
|
|
37
|
+
? { src: href, ...(kind === 'module-script' ? { type: 'module' } : {}), async: true, ...options }
|
|
38
|
+
: style
|
|
39
|
+
? late
|
|
40
|
+
? { rel: 'preload', as: 'style', href, crossOrigin: options.crossOrigin, fetchPriority: options.fetchPriority, integrity: options.integrity, media: options.media, hrefLang: options.hrefLang, referrerPolicy: options.referrerPolicy }
|
|
41
|
+
: { rel: 'stylesheet', href, 'data-precedence': options.precedence!, ...options }
|
|
42
|
+
: { rel: kind, ...(options.as === 'image' && options.imageSrcSet ? {} : { href }), ...options }
|
|
43
|
+
let output = script ? '<script' : '<link'
|
|
44
|
+
for (const name in props) {
|
|
45
|
+
if (name === 'precedence' || late && name === 'data-precedence') continue
|
|
46
|
+
const value = name === 'href' || name === 'src' ? resourceURL(props[name]!) : props[name]!
|
|
47
|
+
output += attrToHtml(name, value).replace(/'/g, ''')
|
|
48
|
+
}
|
|
49
|
+
return output + (script ? '></script>' : '/>')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createResourceHints(shellFlushed = false, styleNonce?: string) {
|
|
53
|
+
const resources = new Map<string, Resource>()
|
|
54
|
+
const groups: Resource[][] = [[], [], [], [], []]
|
|
55
|
+
const styles = new Map<string, Resource[]>()
|
|
56
|
+
const dependencies = new Map<string, string[]>()
|
|
57
|
+
const visibleInline = new Set<string>()
|
|
58
|
+
const collector = {
|
|
59
|
+
add(kind: ResourceHintKind | 'style', href: string, options: ResourceHintOptions): Resource {
|
|
60
|
+
let type: string = kind
|
|
61
|
+
let source = href
|
|
62
|
+
const init = kind === 'style' || kind === 'stylesheet' || kind === 'script' || kind === 'module-script'
|
|
63
|
+
if (kind === 'preload') {
|
|
64
|
+
type = options.as!
|
|
65
|
+
if (type === 'image' && options.imageSrcSet) source = options.imageSrcSet + '\n' + (options.imageSizes || '')
|
|
66
|
+
} else if (kind === 'modulepreload') type = 'module-' + (options.as || 'script')
|
|
67
|
+
else if (kind === 'stylesheet') type = 'style'
|
|
68
|
+
else if (kind === 'preconnect') type += ':' + (options.crossOrigin ?? 'default')
|
|
69
|
+
const key = JSON.stringify([type, source])
|
|
70
|
+
const prior = resources.get(key)
|
|
71
|
+
if (prior && (!init || prior.kind !== 'preload' && prior.kind !== 'modulepreload')) return prior
|
|
72
|
+
|
|
73
|
+
const resource: Resource = { kind, href, options: { ...options } }
|
|
74
|
+
if (kind === 'style' && (!styleNonce || options.nonce === styleNonce)) resource.css = styleText(options)
|
|
75
|
+
if (prior) {
|
|
76
|
+
prior.cancelled = true
|
|
77
|
+
resource.preloaded = !!prior.sent
|
|
78
|
+
for (const name of ['crossOrigin', 'integrity']) {
|
|
79
|
+
if (resource.options[name] == null && prior.options[name] != null) resource.options[name] = prior.options[name]!
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
resources.set(key, resource)
|
|
83
|
+
if (kind === 'stylesheet' || kind === 'style') {
|
|
84
|
+
const precedence = options.precedence!
|
|
85
|
+
let group = styles.get(precedence)
|
|
86
|
+
if (!group) styles.set(precedence, group = [])
|
|
87
|
+
group.push(resource)
|
|
88
|
+
} else {
|
|
89
|
+
const group = kind === 'dns-prefetch' || kind === 'preconnect' ? 0
|
|
90
|
+
: kind === 'preload' && options.as === 'font' ? 1
|
|
91
|
+
: kind === 'preload' && options.as === 'image' && options.fetchPriority === 'high' ? 2
|
|
92
|
+
: init ? 3 : 4
|
|
93
|
+
groups[group]!.push(resource)
|
|
94
|
+
}
|
|
95
|
+
return resource
|
|
96
|
+
},
|
|
97
|
+
addHost(type: string, props: any, scope?: ResourceScope): boolean {
|
|
98
|
+
const kind = resourceKind(type, props)
|
|
99
|
+
if (!kind) return false
|
|
100
|
+
const resource = collector.add(kind === 'script' && props.type === 'module' ? 'module-script' : kind, kind === 'script' ? props.src : props.href, props)
|
|
101
|
+
if (shellFlushed && kind === 'stylesheet') {
|
|
102
|
+
const descriptor = [resourceURL(resource.href), resource.options.precedence]
|
|
103
|
+
for (const name in resource.options) {
|
|
104
|
+
if (name === 'href' || name === 'rel' || name === 'precedence' || name === 'data-precedence' || name === 'children' || name === 'dangerouslySetInnerHTML' || name === 'innerHTML' || name === 'style' || name === 'suppressHydrationWarning' || name === 'suppressContentEditableWarning' || name === 'ref' || name === 'key' || /^on/i.test(name) || !/^[a-zA-Z_:][a-zA-Z0-9:_.-]*$/.test(name)) continue
|
|
105
|
+
const value = resource.options[name]
|
|
106
|
+
if (value == null || typeof value === 'function' || typeof value === 'symbol' || name === 'hidden' && value === false) continue
|
|
107
|
+
descriptor.push(attributeName(name), name === 'hidden' ? '' : name === 'src' ? resourceURL('' + value) : '' + value)
|
|
108
|
+
}
|
|
109
|
+
;(scope ? scope.stylesheets ||= new Map() : dependencies).set(resource.href, descriptor)
|
|
110
|
+
} else if (shellFlushed && kind === 'style') {
|
|
111
|
+
;(scope ? scope.inline ||= new Set() : visibleInline).add(resource.options.precedence)
|
|
112
|
+
}
|
|
113
|
+
return true
|
|
114
|
+
},
|
|
115
|
+
mergeScope(scope: ResourceScope, parent?: ResourceScope): void {
|
|
116
|
+
if (scope.stylesheets) for (const [href, descriptor] of scope.stylesheets) {
|
|
117
|
+
;(parent ? parent.stylesheets ||= new Map() : dependencies).set(href, descriptor)
|
|
118
|
+
}
|
|
119
|
+
if (scope.inline) for (const precedence of scope.inline) {
|
|
120
|
+
;(parent ? parent.inline ||= new Set() : visibleInline).add(precedence)
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
takeStylesheets(): string[][] {
|
|
124
|
+
const result = Array.from(dependencies.values())
|
|
125
|
+
dependencies.clear()
|
|
126
|
+
return result
|
|
127
|
+
},
|
|
128
|
+
drain(): string {
|
|
129
|
+
let output = ''
|
|
130
|
+
function flush(group: Resource[], precedence?: string) {
|
|
131
|
+
let remaining = 0
|
|
132
|
+
let inline = false, hasStylesheets = false
|
|
133
|
+
let hrefs = '', rules = ''
|
|
134
|
+
for (const resource of group) {
|
|
135
|
+
if (!resource.sent && !resource.cancelled) {
|
|
136
|
+
if (resource.kind === 'style') {
|
|
137
|
+
if (shellFlushed && !visibleInline.has(precedence!)) { group[remaining++] = resource; continue }
|
|
138
|
+
inline = true
|
|
139
|
+
if (resource.css !== undefined) {
|
|
140
|
+
hrefs += (hrefs ? ' ' : '') + resource.href
|
|
141
|
+
rules += resource.css
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
if (resource.kind === 'stylesheet') hasStylesheets = true
|
|
145
|
+
if (!(shellFlushed && resource.kind === 'stylesheet' && resource.preloaded)) output += markup(resource, shellFlushed)
|
|
146
|
+
}
|
|
147
|
+
resource.sent = true
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
group.length = remaining
|
|
151
|
+
if (hrefs || inline && !shellFlushed && !hasStylesheets) {
|
|
152
|
+
output += `<style${styleNonce !== undefined ? ` nonce="${escapeAttr(styleNonce)}"` : ''}${shellFlushed ? ' media="not all"' : ''} data-precedence="${escapeAttr(precedence!)}"${hrefs ? ` data-href="${escapeAttr(hrefs)}"` : ''}>${rules}</style>`
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
for (let index = 0; index < groups.length; index++) {
|
|
156
|
+
if (index === 3) for (const [precedence, group] of styles) flush(group, precedence)
|
|
157
|
+
flush(groups[index]!)
|
|
158
|
+
}
|
|
159
|
+
shellFlushed = true
|
|
160
|
+
visibleInline.clear()
|
|
161
|
+
return output
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
return collector
|
|
165
|
+
}
|