@tanstack/start-server-core 1.168.0 → 1.168.1
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/esm/createStartHandler.d.ts +2 -133
- package/dist/esm/createStartHandler.js +27 -154
- package/dist/esm/createStartHandler.js.map +1 -1
- package/dist/esm/early-hints.d.ts +12 -0
- package/dist/esm/early-hints.js +87 -1
- package/dist/esm/early-hints.js.map +1 -1
- package/dist/esm/finalManifest.d.ts +42 -0
- package/dist/esm/finalManifest.js +126 -0
- package/dist/esm/finalManifest.js.map +1 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/inlineCss.d.ts +10 -0
- package/dist/esm/inlineCss.js +14 -0
- package/dist/esm/inlineCss.js.map +1 -0
- package/dist/esm/request-handler.d.ts +11 -1
- package/dist/esm/transformAssetUrls.d.ts +25 -48
- package/dist/esm/transformAssetUrls.js +41 -34
- package/dist/esm/transformAssetUrls.js.map +1 -1
- package/package.json +4 -4
- package/skills/start-server-core/SKILL.md +1 -1
- package/src/createStartHandler.ts +43 -465
- package/src/early-hints.ts +159 -0
- package/src/finalManifest.ts +319 -0
- package/src/index.tsx +1 -5
- package/src/inlineCss.ts +31 -0
- package/src/request-handler.ts +12 -0
- package/src/transformAssetUrls.ts +118 -121
package/src/early-hints.ts
CHANGED
|
@@ -47,6 +47,15 @@ export type ResponseLinkHeaderOptions = {
|
|
|
47
47
|
filter?: ResponseLinkHeaderFilter
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
export interface EarlyHintsCollector {
|
|
51
|
+
collectStatic: (opts: {
|
|
52
|
+
manifest: Manifest
|
|
53
|
+
matchedRoutes?: ReadonlyArray<AnyRoute>
|
|
54
|
+
}) => void
|
|
55
|
+
collectDynamic: (matches: ReadonlyArray<AnyRouteMatch>) => void
|
|
56
|
+
appendResponseHeaders: (headers: Headers) => void
|
|
57
|
+
}
|
|
58
|
+
|
|
50
59
|
const LINK_PARAM_TOKEN_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/
|
|
51
60
|
const PRELOAD_AS_VALUES = new Set<EarlyHint['as']>([
|
|
52
61
|
'fetch',
|
|
@@ -293,3 +302,153 @@ export function getResponseLinkHeaderEntries(opts: {
|
|
|
293
302
|
return []
|
|
294
303
|
}
|
|
295
304
|
}
|
|
305
|
+
|
|
306
|
+
function notifyEarlyHints(
|
|
307
|
+
phase: EarlyHintsPhase,
|
|
308
|
+
event: EarlyHintsEvent,
|
|
309
|
+
onEarlyHints: OnEarlyHints,
|
|
310
|
+
) {
|
|
311
|
+
try {
|
|
312
|
+
const result = onEarlyHints(event)
|
|
313
|
+
if (result) {
|
|
314
|
+
void Promise.resolve(result).catch((err) => {
|
|
315
|
+
console.error(`Error sending ${phase} early hints:`, err)
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
} catch (err) {
|
|
319
|
+
console.error(`Error sending ${phase} early hints:`, err)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function getResponseLinkHeaderFilter(
|
|
324
|
+
responseLinkHeader: boolean | ResponseLinkHeaderOptions | undefined,
|
|
325
|
+
): ResponseLinkHeaderFilter | undefined {
|
|
326
|
+
if (typeof responseLinkHeader !== 'object') {
|
|
327
|
+
return undefined
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return responseLinkHeader.filter
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function appendResponseLinkHeaders(opts: {
|
|
334
|
+
responseHeaders: Headers
|
|
335
|
+
entries: ReadonlyArray<ResponseLinkHeaderEntry>
|
|
336
|
+
filter?: ResponseLinkHeaderFilter
|
|
337
|
+
}) {
|
|
338
|
+
for (const link of getResponseLinkHeaderEntries(opts)) {
|
|
339
|
+
opts.responseHeaders.append('Link', link)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function collectResponseLinkHeaderEntries(opts: {
|
|
344
|
+
phase: EarlyHintsPhase
|
|
345
|
+
event: EarlyHintsEvent
|
|
346
|
+
entries: Array<ResponseLinkHeaderEntry>
|
|
347
|
+
}) {
|
|
348
|
+
for (let index = 0; index < opts.event.hints.length; index++) {
|
|
349
|
+
opts.entries.push({
|
|
350
|
+
phase: opts.phase,
|
|
351
|
+
hint: opts.event.hints[index]!,
|
|
352
|
+
link: opts.event.links[index]!,
|
|
353
|
+
})
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function collectEarlyHintsPhase(opts: {
|
|
358
|
+
phase: EarlyHintsPhase
|
|
359
|
+
hints: ReadonlyArray<EarlyHint>
|
|
360
|
+
sentLinks: Set<string>
|
|
361
|
+
sentHints?: Array<EarlyHint>
|
|
362
|
+
onEarlyHints?: OnEarlyHints
|
|
363
|
+
responseLinkHeaderEntries?: Array<ResponseLinkHeaderEntry>
|
|
364
|
+
}) {
|
|
365
|
+
const event = opts.onEarlyHints
|
|
366
|
+
? createEarlyHintsEvent({
|
|
367
|
+
phase: opts.phase,
|
|
368
|
+
hints: opts.hints,
|
|
369
|
+
sentLinks: opts.sentLinks,
|
|
370
|
+
sentHints: opts.sentHints!,
|
|
371
|
+
})
|
|
372
|
+
: undefined
|
|
373
|
+
|
|
374
|
+
if (event) {
|
|
375
|
+
notifyEarlyHints(opts.phase, event, opts.onEarlyHints!)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (!opts.responseLinkHeaderEntries) return
|
|
379
|
+
|
|
380
|
+
if (event) {
|
|
381
|
+
collectResponseLinkHeaderEntries({
|
|
382
|
+
phase: opts.phase,
|
|
383
|
+
event,
|
|
384
|
+
entries: opts.responseLinkHeaderEntries,
|
|
385
|
+
})
|
|
386
|
+
return
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
createResponseLinkHeaderEntries({
|
|
390
|
+
phase: opts.phase,
|
|
391
|
+
hints: opts.hints,
|
|
392
|
+
sentLinks: opts.sentLinks,
|
|
393
|
+
entries: opts.responseLinkHeaderEntries,
|
|
394
|
+
})
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function createEarlyHintsCollector(
|
|
398
|
+
opts:
|
|
399
|
+
| {
|
|
400
|
+
onEarlyHints?: OnEarlyHints
|
|
401
|
+
responseLinkHeader?: boolean | ResponseLinkHeaderOptions
|
|
402
|
+
}
|
|
403
|
+
| undefined,
|
|
404
|
+
): EarlyHintsCollector | undefined {
|
|
405
|
+
if (
|
|
406
|
+
process.env.TSS_DEV_SERVER === 'true' ||
|
|
407
|
+
(!opts?.onEarlyHints && !opts?.responseLinkHeader)
|
|
408
|
+
) {
|
|
409
|
+
return undefined
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const sentLinks = new Set<string>()
|
|
413
|
+
const sentHints = opts.onEarlyHints ? new Array<EarlyHint>() : undefined
|
|
414
|
+
const responseLinkHeaderEntries = opts.responseLinkHeader
|
|
415
|
+
? new Array<ResponseLinkHeaderEntry>()
|
|
416
|
+
: undefined
|
|
417
|
+
const responseLinkHeaderFilter = getResponseLinkHeaderFilter(
|
|
418
|
+
opts.responseLinkHeader,
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
collectStatic: ({ manifest, matchedRoutes }) => {
|
|
423
|
+
if (!matchedRoutes?.length) return
|
|
424
|
+
|
|
425
|
+
collectEarlyHintsPhase({
|
|
426
|
+
phase: 'static',
|
|
427
|
+
hints: collectStaticHintsFromManifest(manifest, matchedRoutes),
|
|
428
|
+
sentLinks,
|
|
429
|
+
sentHints,
|
|
430
|
+
onEarlyHints: opts.onEarlyHints,
|
|
431
|
+
responseLinkHeaderEntries,
|
|
432
|
+
})
|
|
433
|
+
},
|
|
434
|
+
collectDynamic: (matches) => {
|
|
435
|
+
collectEarlyHintsPhase({
|
|
436
|
+
phase: 'dynamic',
|
|
437
|
+
hints: collectDynamicHintsFromMatches(matches),
|
|
438
|
+
sentLinks,
|
|
439
|
+
sentHints,
|
|
440
|
+
onEarlyHints: opts.onEarlyHints,
|
|
441
|
+
responseLinkHeaderEntries,
|
|
442
|
+
})
|
|
443
|
+
},
|
|
444
|
+
appendResponseHeaders: (headers) => {
|
|
445
|
+
if (!responseLinkHeaderEntries?.length) return
|
|
446
|
+
|
|
447
|
+
appendResponseLinkHeaders({
|
|
448
|
+
responseHeaders: headers,
|
|
449
|
+
entries: responseLinkHeaderEntries,
|
|
450
|
+
filter: responseLinkHeaderFilter,
|
|
451
|
+
})
|
|
452
|
+
},
|
|
453
|
+
}
|
|
454
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildManifestWithClientEntry,
|
|
3
|
+
resolveTransformAssetsConfig,
|
|
4
|
+
transformManifestAssets,
|
|
5
|
+
} from './transformAssetUrls'
|
|
6
|
+
import {
|
|
7
|
+
getStaticHandlerInlineCssDefault,
|
|
8
|
+
resolveInlineCssForRequest,
|
|
9
|
+
} from './inlineCss'
|
|
10
|
+
import type { Manifest } from '@tanstack/router-core'
|
|
11
|
+
import type { HandlerInlineCssOption } from './inlineCss'
|
|
12
|
+
import type {
|
|
13
|
+
CreateTransformAssetsContext,
|
|
14
|
+
StartManifestWithClientEntry,
|
|
15
|
+
TransformAssets,
|
|
16
|
+
TransformAssetsFn,
|
|
17
|
+
} from './transformAssetUrls'
|
|
18
|
+
|
|
19
|
+
export type {
|
|
20
|
+
HandlerInlineCssOption,
|
|
21
|
+
StartManifestWithClientEntry,
|
|
22
|
+
TransformAssets,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface FinalManifestOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Controls whether Start inlines build-collected CSS by default at runtime.
|
|
28
|
+
*
|
|
29
|
+
* This only has an effect when the build was created with
|
|
30
|
+
* `server.build.inlineCss` enabled. Pass a callback to decide per request.
|
|
31
|
+
* `handler(request, { inlineCss })` overrides this value for that request.
|
|
32
|
+
*
|
|
33
|
+
* @default true
|
|
34
|
+
*/
|
|
35
|
+
inlineCss?: HandlerInlineCssOption
|
|
36
|
+
/**
|
|
37
|
+
* Transform manifest-managed asset URLs and attributes at runtime, e.g. to
|
|
38
|
+
* prepend a CDN prefix.
|
|
39
|
+
*
|
|
40
|
+
* This covers JS preloads, CSS links, the client entry script, and URLs
|
|
41
|
+
* inside build-collected inline CSS. Asset imports used directly in
|
|
42
|
+
* components should be handled by the bundler instead.
|
|
43
|
+
*/
|
|
44
|
+
transformAssets?: TransformAssets
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type FinalManifestCacheKey = 'inline-css' | 'linked-css'
|
|
48
|
+
type FinalManifestCache = Map<FinalManifestCacheKey, Promise<Manifest>>
|
|
49
|
+
export type GetBaseManifest = () => Promise<StartManifestWithClientEntry>
|
|
50
|
+
|
|
51
|
+
export interface FinalManifestRequestOptions {
|
|
52
|
+
request: Request
|
|
53
|
+
requestInlineCss: boolean | undefined
|
|
54
|
+
getBaseManifest: GetBaseManifest
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface FinalManifestTransformResolver {
|
|
58
|
+
cache: boolean
|
|
59
|
+
warmup: boolean
|
|
60
|
+
getTransformFn: (
|
|
61
|
+
ctx: CreateTransformAssetsContext,
|
|
62
|
+
) => Promise<TransformAssetsFn | undefined>
|
|
63
|
+
clearCachedCreateTransform: () => void
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface FinalManifestResolver {
|
|
67
|
+
warmup: (opts: {
|
|
68
|
+
getBaseManifest: GetBaseManifest
|
|
69
|
+
}) => Promise<Manifest> | undefined
|
|
70
|
+
resolveCached: (opts: FinalManifestRequestOptions) => Promise<Manifest>
|
|
71
|
+
resolveUncached: (opts: FinalManifestRequestOptions) => Promise<Manifest>
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function createCachedBaseManifestLoader(
|
|
75
|
+
loadBaseManifest: GetBaseManifest,
|
|
76
|
+
): GetBaseManifest {
|
|
77
|
+
let baseManifestPromise: Promise<StartManifestWithClientEntry> | undefined
|
|
78
|
+
|
|
79
|
+
return () => {
|
|
80
|
+
if (!baseManifestPromise) {
|
|
81
|
+
baseManifestPromise = loadBaseManifest().catch((error) => {
|
|
82
|
+
baseManifestPromise = undefined
|
|
83
|
+
throw error
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return baseManifestPromise
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function createFinalManifestTransformResolver(
|
|
92
|
+
transformAssets: TransformAssets | undefined,
|
|
93
|
+
opts: { cacheCreateTransform: boolean },
|
|
94
|
+
): FinalManifestTransformResolver {
|
|
95
|
+
const transformConfig =
|
|
96
|
+
transformAssets !== undefined
|
|
97
|
+
? resolveTransformAssetsConfig(transformAssets)
|
|
98
|
+
: undefined
|
|
99
|
+
const cache = transformConfig ? transformConfig.cache : true
|
|
100
|
+
const warmup =
|
|
101
|
+
!!transformAssets &&
|
|
102
|
+
typeof transformAssets === 'object' &&
|
|
103
|
+
'warmup' in transformAssets &&
|
|
104
|
+
transformAssets.warmup === true
|
|
105
|
+
|
|
106
|
+
let cachedCreateTransformPromise: Promise<TransformAssetsFn> | undefined
|
|
107
|
+
|
|
108
|
+
const clearCachedCreateTransform = () => {
|
|
109
|
+
cachedCreateTransformPromise = undefined
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
cache,
|
|
114
|
+
warmup,
|
|
115
|
+
clearCachedCreateTransform,
|
|
116
|
+
getTransformFn: async (ctx) => {
|
|
117
|
+
if (!transformConfig) return undefined
|
|
118
|
+
|
|
119
|
+
if (transformConfig.type !== 'createTransform') {
|
|
120
|
+
return transformConfig.transformFn
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!cache || !opts.cacheCreateTransform) {
|
|
124
|
+
return transformConfig.createTransform(ctx)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!cachedCreateTransformPromise) {
|
|
128
|
+
cachedCreateTransformPromise = Promise.resolve(
|
|
129
|
+
transformConfig.createTransform(ctx),
|
|
130
|
+
).catch((error) => {
|
|
131
|
+
clearCachedCreateTransform()
|
|
132
|
+
throw error
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return cachedCreateTransformPromise
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function createFinalManifestResolver(
|
|
142
|
+
opts: FinalManifestOptions & { cacheCreateTransform: boolean },
|
|
143
|
+
): FinalManifestResolver {
|
|
144
|
+
const finalManifestCache: FinalManifestCache = new Map()
|
|
145
|
+
const transformResolver = createFinalManifestTransformResolver(
|
|
146
|
+
opts.transformAssets,
|
|
147
|
+
{ cacheCreateTransform: opts.cacheCreateTransform },
|
|
148
|
+
)
|
|
149
|
+
const handlerDefaultInlineCss = getStaticHandlerInlineCssDefault(
|
|
150
|
+
opts.inlineCss,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
const getRequestManifestOptions = async (
|
|
154
|
+
requestOpts: FinalManifestRequestOptions,
|
|
155
|
+
) => {
|
|
156
|
+
const transformFn = await transformResolver.getTransformFn({
|
|
157
|
+
warmup: false,
|
|
158
|
+
request: requestOpts.request,
|
|
159
|
+
})
|
|
160
|
+
const inlineCss = await resolveInlineCssForRequest({
|
|
161
|
+
request: requestOpts.request,
|
|
162
|
+
handlerInlineCss: opts.inlineCss,
|
|
163
|
+
requestInlineCss: requestOpts.requestInlineCss,
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
getBaseManifest: requestOpts.getBaseManifest,
|
|
168
|
+
transformFn,
|
|
169
|
+
cache: transformResolver.cache,
|
|
170
|
+
inlineCss,
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const resolveRequest = async (
|
|
175
|
+
requestOpts: FinalManifestRequestOptions,
|
|
176
|
+
cache: FinalManifestCache | undefined,
|
|
177
|
+
) => {
|
|
178
|
+
return resolveFinalManifest({
|
|
179
|
+
...(await getRequestManifestOptions(requestOpts)),
|
|
180
|
+
finalManifestCache: cache,
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
warmup: ({ getBaseManifest }) =>
|
|
186
|
+
warmupFinalManifest({
|
|
187
|
+
enabled: transformResolver.warmup,
|
|
188
|
+
handlerDefaultInlineCss,
|
|
189
|
+
cache: transformResolver.cache,
|
|
190
|
+
finalManifestCache,
|
|
191
|
+
getBaseManifest,
|
|
192
|
+
getTransformFn: () =>
|
|
193
|
+
transformResolver.getTransformFn({ warmup: true }),
|
|
194
|
+
onError: transformResolver.clearCachedCreateTransform,
|
|
195
|
+
}),
|
|
196
|
+
resolveCached: (requestOpts) =>
|
|
197
|
+
resolveRequest(requestOpts, finalManifestCache),
|
|
198
|
+
resolveUncached: (requestOpts) => resolveRequest(requestOpts, undefined),
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function getFinalManifestCacheKey(inlineCss: boolean): FinalManifestCacheKey {
|
|
203
|
+
return inlineCss ? 'inline-css' : 'linked-css'
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function cacheFinalManifestPromise(
|
|
207
|
+
cachedFinalManifestPromises: FinalManifestCache,
|
|
208
|
+
cacheKey: FinalManifestCacheKey,
|
|
209
|
+
promise: Promise<Manifest>,
|
|
210
|
+
): Promise<Manifest> {
|
|
211
|
+
const cachedFinalManifestPromise = promise.catch((error) => {
|
|
212
|
+
if (
|
|
213
|
+
cachedFinalManifestPromises.get(cacheKey) === cachedFinalManifestPromise
|
|
214
|
+
) {
|
|
215
|
+
cachedFinalManifestPromises.delete(cacheKey)
|
|
216
|
+
}
|
|
217
|
+
throw error
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
cachedFinalManifestPromises.set(cacheKey, cachedFinalManifestPromise)
|
|
221
|
+
return cachedFinalManifestPromise
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function getOrCreateCachedFinalManifestPromise(
|
|
225
|
+
cachedFinalManifestPromises: FinalManifestCache,
|
|
226
|
+
cacheKey: FinalManifestCacheKey,
|
|
227
|
+
computeFinalManifest: () => Promise<Manifest>,
|
|
228
|
+
): Promise<Manifest> {
|
|
229
|
+
const cachedFinalManifestPromise = cachedFinalManifestPromises.get(cacheKey)
|
|
230
|
+
if (cachedFinalManifestPromise) {
|
|
231
|
+
return cachedFinalManifestPromise
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return cacheFinalManifestPromise(
|
|
235
|
+
cachedFinalManifestPromises,
|
|
236
|
+
cacheKey,
|
|
237
|
+
Promise.resolve().then(computeFinalManifest),
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function buildFinalManifest(opts: {
|
|
242
|
+
base: StartManifestWithClientEntry
|
|
243
|
+
transformFn: TransformAssetsFn | undefined
|
|
244
|
+
inlineCss: boolean
|
|
245
|
+
}): Promise<Manifest> {
|
|
246
|
+
return opts.transformFn
|
|
247
|
+
? await transformManifestAssets(opts.base, opts.transformFn, {
|
|
248
|
+
inlineCss: opts.inlineCss,
|
|
249
|
+
})
|
|
250
|
+
: buildManifestWithClientEntry(opts.base, { inlineCss: opts.inlineCss })
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function resolveFinalManifest(opts: {
|
|
254
|
+
getBaseManifest: () => Promise<StartManifestWithClientEntry>
|
|
255
|
+
transformFn: TransformAssetsFn | undefined
|
|
256
|
+
cache: boolean
|
|
257
|
+
inlineCss: boolean
|
|
258
|
+
finalManifestCache?: FinalManifestCache
|
|
259
|
+
}): Promise<Manifest> {
|
|
260
|
+
const computeFinalManifest = async () => {
|
|
261
|
+
return buildFinalManifest({
|
|
262
|
+
base: await opts.getBaseManifest(),
|
|
263
|
+
transformFn: opts.transformFn,
|
|
264
|
+
inlineCss: opts.inlineCss,
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (opts.finalManifestCache && (!opts.transformFn || opts.cache)) {
|
|
269
|
+
return getOrCreateCachedFinalManifestPromise(
|
|
270
|
+
opts.finalManifestCache,
|
|
271
|
+
getFinalManifestCacheKey(opts.inlineCss),
|
|
272
|
+
computeFinalManifest,
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return computeFinalManifest()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function warmupFinalManifest(opts: {
|
|
280
|
+
enabled: boolean
|
|
281
|
+
handlerDefaultInlineCss: boolean | undefined
|
|
282
|
+
cache: boolean
|
|
283
|
+
finalManifestCache: FinalManifestCache
|
|
284
|
+
getBaseManifest: () => Promise<StartManifestWithClientEntry>
|
|
285
|
+
getTransformFn: () => Promise<TransformAssetsFn | undefined>
|
|
286
|
+
onError?: () => void
|
|
287
|
+
}): Promise<Manifest> | undefined {
|
|
288
|
+
if (
|
|
289
|
+
!opts.enabled ||
|
|
290
|
+
opts.handlerDefaultInlineCss === undefined ||
|
|
291
|
+
!opts.cache
|
|
292
|
+
) {
|
|
293
|
+
return undefined
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const inlineCss = opts.handlerDefaultInlineCss
|
|
297
|
+
const warmupPromise = getOrCreateCachedFinalManifestPromise(
|
|
298
|
+
opts.finalManifestCache,
|
|
299
|
+
getFinalManifestCacheKey(inlineCss),
|
|
300
|
+
async () => {
|
|
301
|
+
const [base, transformFn] = await Promise.all([
|
|
302
|
+
opts.getBaseManifest(),
|
|
303
|
+
opts.getTransformFn(),
|
|
304
|
+
])
|
|
305
|
+
|
|
306
|
+
return buildFinalManifest({
|
|
307
|
+
base,
|
|
308
|
+
transformFn,
|
|
309
|
+
inlineCss,
|
|
310
|
+
})
|
|
311
|
+
},
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
if (opts.onError) {
|
|
315
|
+
void warmupPromise.catch(opts.onError)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return warmupPromise
|
|
319
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -9,12 +9,8 @@ export type {
|
|
|
9
9
|
TransformAssetsObjectShorthand,
|
|
10
10
|
TransformAssetsCrossOriginConfig,
|
|
11
11
|
TransformAssetResult,
|
|
12
|
-
TransformAssetUrls,
|
|
13
|
-
TransformAssetUrlsFn,
|
|
14
|
-
TransformAssetUrlsContext,
|
|
15
|
-
TransformAssetUrlsOptions,
|
|
16
|
-
AssetUrlType,
|
|
17
12
|
TransformAssetKind,
|
|
13
|
+
CreateTransformAssetsContext,
|
|
18
14
|
} from './transformAssetUrls'
|
|
19
15
|
|
|
20
16
|
export {
|
package/src/inlineCss.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Awaitable } from '@tanstack/router-core'
|
|
2
|
+
|
|
3
|
+
export type HandlerInlineCssOption =
|
|
4
|
+
| boolean
|
|
5
|
+
| ((ctx: { request: Request }) => Awaitable<boolean>)
|
|
6
|
+
|
|
7
|
+
export function getStaticHandlerInlineCssDefault(
|
|
8
|
+
handlerInlineCss: HandlerInlineCssOption | undefined,
|
|
9
|
+
) {
|
|
10
|
+
if (typeof handlerInlineCss === 'function') {
|
|
11
|
+
return undefined
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return handlerInlineCss ?? true
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function resolveInlineCssForRequest(opts: {
|
|
18
|
+
request: Request
|
|
19
|
+
handlerInlineCss: HandlerInlineCssOption | undefined
|
|
20
|
+
requestInlineCss: boolean | undefined
|
|
21
|
+
}) {
|
|
22
|
+
if (opts.requestInlineCss !== undefined) {
|
|
23
|
+
return opts.requestInlineCss
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (typeof opts.handlerInlineCss === 'function') {
|
|
27
|
+
return await opts.handlerInlineCss({ request: opts.request })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return opts.handlerInlineCss ?? true
|
|
31
|
+
}
|
package/src/request-handler.ts
CHANGED
|
@@ -45,7 +45,19 @@ type EarlyHintsOptions = {
|
|
|
45
45
|
responseLinkHeader?: boolean | ResponseLinkHeaderOptions
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
type InlineCssOptions = {
|
|
49
|
+
/**
|
|
50
|
+
* Controls whether Start inlines build-collected CSS for this request.
|
|
51
|
+
*
|
|
52
|
+
* This only has an effect when the build was created with
|
|
53
|
+
* `server.build.inlineCss` enabled. Defaults to `true` so builds with inline
|
|
54
|
+
* CSS enabled continue to inline CSS unless a request opts out.
|
|
55
|
+
*/
|
|
56
|
+
inlineCss?: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
48
59
|
export type RequestOptions<TRegister> = EarlyHintsOptions &
|
|
60
|
+
InlineCssOptions &
|
|
49
61
|
(TRegister extends {
|
|
50
62
|
server: { requestContext: infer TRequestContext }
|
|
51
63
|
}
|