Skip to content

Commit b04c703

Browse files
fix
1 parent 7bb0309 commit b04c703

5 files changed

Lines changed: 103 additions & 37 deletions

File tree

packages/react-router/src/Match.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export const Match = React.memo(function MatchImpl({
6464
// Subscribe directly to the match store from the pool.
6565
// The matchId prop is stable for this component's lifetime (set by Outlet),
6666
// and reconcileMatchPool reuses stores for the same matchId.
67-
67+
6868
const matchStore = router.stores.activeMatchStoresById.get(matchId)
6969
if (!matchStore) {
7070
if (process.env.NODE_ENV !== 'production') {
@@ -338,7 +338,6 @@ export const MatchInner = React.memo(function MatchInnerImpl({
338338
return out
339339
}
340340

341-
342341
const matchStore = router.stores.activeMatchStoresById.get(matchId)
343342
if (!matchStore) {
344343
if (process.env.NODE_ENV !== 'production') {

packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/* eslint-disable @typescript-eslint/prefer-for-of */
22
import { joinURL } from 'ufo'
3-
import { rootRouteId } from '@tanstack/router-core'
3+
import { resolveManifestAssetLink, rootRouteId } from '@tanstack/router-core'
44
import { tsrSplit } from '@tanstack/router-plugin'
5-
import type { RouterManagedTag } from '@tanstack/router-core'
5+
import type { ManifestAssetLink, RouterManagedTag } from '@tanstack/router-core'
66
import type { Rollup } from 'vite'
77

88
const ROUTER_MANAGED_MODE = 1
@@ -31,6 +31,11 @@ interface ManifestAssetResolvers {
3131
getStylesheetAsset: (cssFile: string) => RouterManagedTag
3232
}
3333

34+
type DedupePreloadRoute = {
35+
preloads?: Array<ManifestAssetLink>
36+
children?: Array<string>
37+
}
38+
3439
export function appendUniqueStrings(
3540
target: Array<string> | undefined,
3641
source: Array<string>,
@@ -506,25 +511,27 @@ export function buildRouteManifestRoutes(options: {
506511
}
507512

508513
export function dedupeNestedRoutePreloads(
509-
route: { preloads?: Array<string>; children?: Array<string> },
510-
routesById: Record<string, RouteTreeRoute>,
514+
route: DedupePreloadRoute,
515+
routesById: Record<string, DedupePreloadRoute>,
511516
seenPreloads = new Set<string>(),
512517
) {
513518
let routePreloads = route.preloads
514519

515520
if (routePreloads && routePreloads.length > 0) {
516-
let dedupedPreloads: Array<string> | undefined
521+
let dedupedPreloads: Array<ManifestAssetLink> | undefined
517522

518523
for (let i = 0; i < routePreloads.length; i++) {
519524
const preload = routePreloads[i]!
520-
if (seenPreloads.has(preload)) {
525+
const preloadHref = resolveManifestAssetLink(preload).href
526+
527+
if (seenPreloads.has(preloadHref)) {
521528
if (dedupedPreloads === undefined) {
522529
dedupedPreloads = routePreloads.slice(0, i)
523530
}
524531
continue
525532
}
526533

527-
seenPreloads.add(preload)
534+
seenPreloads.add(preloadHref)
528535

529536
if (dedupedPreloads) {
530537
dedupedPreloads.push(preload)
@@ -549,7 +556,7 @@ export function dedupeNestedRoutePreloads(
549556

550557
if (routePreloads) {
551558
for (let i = routePreloads.length - 1; i >= 0; i--) {
552-
seenPreloads.delete(routePreloads[i]!)
559+
seenPreloads.delete(resolveManifestAssetLink(routePreloads[i]!).href)
553560
}
554561
}
555562
}

packages/start-server-core/src/createStartHandler.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -445,15 +445,16 @@ export function createStartHandler<TRegister = Register>(
445445
? undefined
446446
: cbOrOptions.transformAssetUrls
447447

448-
const transformOption = transformAssetsOption
449-
? resolveTransformAssetsConfig(transformAssetsOption)
450-
: transformAssetUrlsOption
451-
? resolveTransformAssetsConfig(
452-
adaptTransformAssetUrlsConfigToTransformAssets(
453-
transformAssetUrlsOption,
454-
),
455-
)
456-
: undefined
448+
const transformOption =
449+
transformAssetsOption !== undefined
450+
? resolveTransformAssetsConfig(transformAssetsOption)
451+
: transformAssetUrlsOption !== undefined
452+
? resolveTransformAssetsConfig(
453+
adaptTransformAssetUrlsConfigToTransformAssets(
454+
transformAssetUrlsOption,
455+
),
456+
)
457+
: undefined
457458

458459
const warmupTransformManifest =
459460
(!!transformAssetsOption &&
@@ -467,25 +468,35 @@ export function createStartHandler<TRegister = Register>(
467468
// Pre-resolve the transform function and cache flag
468469
const resolvedTransformConfig = transformOption
469470
const cache = resolvedTransformConfig ? resolvedTransformConfig.cache : true
471+
const shouldCacheCreateTransform =
472+
cache && process.env.TSS_DEV_SERVER !== 'true'
470473

471-
// Memoize a single createTransform() result when caching is enabled.
474+
// Memoize a single createTransform() result when caching is enabled outside
475+
// of the dev server.
472476
let cachedCreateTransformPromise: Promise<TransformAssetsFn> | undefined
473477

474478
const getTransformFn = async (
475479
opts: { warmup: true } | { warmup: false; request: Request },
476480
): Promise<TransformAssetsFn | undefined> => {
477481
if (!resolvedTransformConfig) return undefined
482+
478483
if (resolvedTransformConfig.type === 'createTransform') {
479-
if (cache) {
484+
if (shouldCacheCreateTransform) {
480485
if (!cachedCreateTransformPromise) {
481486
cachedCreateTransformPromise = Promise.resolve(
482487
resolvedTransformConfig.createTransform(opts),
483-
)
488+
).catch((error) => {
489+
cachedCreateTransformPromise = undefined
490+
throw error
491+
})
484492
}
493+
485494
return cachedCreateTransformPromise
486495
}
496+
487497
return resolvedTransformConfig.createTransform(opts)
488498
}
499+
489500
return resolvedTransformConfig.transformFn
490501
}
491502

packages/start-server-core/src/transformAssetUrls.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ export type { AssetCrossOrigin }
1212

1313
export type TransformAssetKind = 'modulepreload' | 'stylesheet' | 'clientEntry'
1414

15+
type TransformAssetsShorthandCrossOriginKind = Exclude<
16+
TransformAssetKind,
17+
'clientEntry'
18+
>
19+
1520
export type AssetUrlType = TransformAssetKind
1621

1722
export interface TransformAssetsContext {
@@ -148,7 +153,7 @@ export type TransformAssetUrls =
148153
*/
149154
export type TransformAssetsCrossOriginConfig =
150155
| AssetCrossOrigin
151-
| Partial<Record<TransformAssetKind, AssetCrossOrigin>>
156+
| Partial<Record<TransformAssetsShorthandCrossOriginKind, AssetCrossOrigin>>
152157

153158
/**
154159
* Object shorthand for `transformAssets`. Combines a URL prefix with optional
@@ -217,10 +222,11 @@ function normalizeTransformAssetResult(
217222

218223
function resolveTransformAssetsCrossOrigin(
219224
config: TransformAssetsCrossOriginConfig | undefined,
220-
kind: TransformAssetKind,
225+
kind: TransformAssetsShorthandCrossOriginKind,
221226
): AssetCrossOrigin | undefined {
222227
if (!config) return undefined
223228
if (typeof config === 'string') return config
229+
224230
return config[kind]
225231
}
226232

@@ -253,17 +259,18 @@ export function resolveTransformAssetsConfig(
253259
// Object shorthand: { prefix, crossOrigin? }
254260
if (isObjectShorthand(transform)) {
255261
const { prefix, crossOrigin } = transform
262+
256263
return {
257264
type: 'transform',
258265
transformFn: ({ url, kind }) => {
259-
const co = resolveTransformAssetsCrossOrigin(crossOrigin, kind)
260-
const result: { href: string; crossOrigin?: AssetCrossOrigin } = {
261-
href: `${prefix}${url}`,
262-
}
263-
if (co) {
264-
result.crossOrigin = co
266+
const href = `${prefix}${url}`
267+
268+
if (kind === 'clientEntry') {
269+
return { href }
265270
}
266-
return result
271+
272+
const co = resolveTransformAssetsCrossOrigin(crossOrigin, kind)
273+
return co ? { href, crossOrigin: co } : { href }
267274
},
268275
cache: true,
269276
}
@@ -377,13 +384,11 @@ function assignManifestAssetLink(
377384
export async function transformManifestAssets(
378385
source: StartManifestWithClientEntry,
379386
transformFn: TransformAssetsFn,
380-
opts?: {
387+
_opts?: {
381388
clone?: boolean
382389
},
383390
): Promise<Manifest> {
384-
const manifest = opts?.clone
385-
? structuredClone(source.manifest)
386-
: source.manifest
391+
const manifest = structuredClone(source.manifest)
387392

388393
for (const route of Object.values(manifest.routes)) {
389394
if (route.preloads) {

packages/start-server-core/tests/transformAssets.test.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,42 @@ describe('transformAssets', () => {
101101
)
102102
})
103103

104+
it('does not mutate the source manifest when clone is false', async () => {
105+
const source = {
106+
manifest: {
107+
routes: {
108+
__root__: {
109+
preloads: ['/assets/app.js'],
110+
assets: [
111+
{
112+
tag: 'link' as const,
113+
attrs: { rel: 'stylesheet', href: '/assets/app.css' },
114+
},
115+
],
116+
},
117+
},
118+
},
119+
clientEntry: '/assets/entry.js',
120+
}
121+
122+
const manifest = await transformManifestAssets(
123+
source,
124+
({ url }) => ({ href: `https://cdn.example.com${url}` }),
125+
{ clone: false },
126+
)
127+
128+
expect(manifest.routes.__root__?.preloads?.[0]).toBe(
129+
'https://cdn.example.com/assets/app.js',
130+
)
131+
expect(source.manifest.routes.__root__?.preloads?.[0]).toBe(
132+
'/assets/app.js',
133+
)
134+
expect(source.manifest.routes.__root__?.assets?.[0]).toEqual({
135+
tag: 'link',
136+
attrs: { rel: 'stylesheet', href: '/assets/app.css' },
137+
})
138+
})
139+
104140
describe('object shorthand', () => {
105141
it('supports { prefix } — same as string shorthand', () => {
106142
const config = resolveTransformAssetsConfig({
@@ -138,12 +174,10 @@ describe('transformAssets', () => {
138174
crossOrigin: 'anonymous',
139175
})
140176

141-
// clientEntry gets crossOrigin too (though only href matters for script)
142177
expect(
143178
config.transformFn({ kind: 'clientEntry', url: '/assets/entry.js' }),
144179
).toEqual({
145180
href: 'https://cdn.example.com/assets/entry.js',
146-
crossOrigin: 'anonymous',
147181
})
148182
})
149183

@@ -180,6 +214,16 @@ describe('transformAssets', () => {
180214
})
181215
})
182216

217+
it('supports empty-string prefix shorthand', () => {
218+
const config = resolveTransformAssetsConfig('')
219+
220+
if (config.type !== 'transform') throw new Error('expected transform')
221+
222+
expect(
223+
config.transformFn({ kind: 'modulepreload', url: '/assets/app.js' }),
224+
).toEqual({ href: '/assets/app.js' })
225+
})
226+
183227
it('applies object shorthand crossOrigin to manifest assets', async () => {
184228
const config = resolveTransformAssetsConfig({
185229
prefix: 'https://cdn.example.com',

0 commit comments

Comments
 (0)