@tanstack/start-server-core 1.169.1 → 1.169.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.
@@ -1,21 +1,18 @@
1
- import { AssetCrossOrigin, Awaitable, Manifest, RouterManagedTag } from '@tanstack/router-core';
1
+ import { AssetCrossOrigin, Awaitable, ManifestScript, ScriptFormat, ServerManifest } from '@tanstack/router-core';
2
2
  export type { AssetCrossOrigin };
3
3
  export type TransformAssetsContext = {
4
4
  url: string;
5
- kind: 'modulepreload';
5
+ kind: 'script';
6
6
  } | {
7
7
  url: string;
8
8
  kind: 'stylesheet';
9
- } | {
10
- url: string;
11
- kind: 'clientEntry';
12
9
  } | {
13
10
  url: string;
14
11
  kind: 'css-url';
15
12
  stylesheetHref: string;
16
13
  };
17
14
  export type TransformAssetKind = TransformAssetsContext['kind'];
18
- type TransformAssetsShorthandCrossOriginKind = Exclude<TransformAssetKind, 'clientEntry' | 'css-url'>;
15
+ type TransformAssetsShorthandCrossOriginKind = Exclude<TransformAssetKind, 'css-url'>;
19
16
  export type TransformAssetResult = string | {
20
17
  href: string;
21
18
  crossOrigin?: AssetCrossOrigin;
@@ -77,7 +74,7 @@ export type TransformAssetsOptions = (TransformAssetsOptionsBase & {
77
74
  * crossOrigin: 'anonymous'
78
75
  *
79
76
  * // Different values per kind
80
- * crossOrigin: { modulepreload: 'anonymous', stylesheet: 'use-credentials' }
77
+ * crossOrigin: { script: 'anonymous', stylesheet: 'use-credentials' }
81
78
  * ```
82
79
  */
83
80
  export type TransformAssetsCrossOriginConfig = AssetCrossOrigin | Partial<Record<TransformAssetsShorthandCrossOriginKind, AssetCrossOrigin>>;
@@ -96,7 +93,7 @@ export interface TransformAssetsObjectShorthand {
96
93
  /** URL prefix prepended to every asset URL. */
97
94
  prefix: string;
98
95
  /**
99
- * Optional crossOrigin attribute applied to manifest-managed `<link>` assets.
96
+ * Optional crossOrigin attribute applied to transformed script and stylesheet assets.
100
97
  *
101
98
  * Accepts a single value or a per-kind record.
102
99
  */
@@ -114,26 +111,24 @@ export type ResolvedTransformAssetsConfig = {
114
111
  };
115
112
  export declare function resolveTransformAssetsConfig(transform: TransformAssets): ResolvedTransformAssetsConfig;
116
113
  export interface StartManifestWithClientEntry {
117
- manifest: Manifest;
114
+ manifest: ServerManifest;
118
115
  clientEntry: string;
119
- /** Script content prepended before the client entry import (dev only) */
120
- injectedHeadScripts?: string;
121
116
  }
122
117
  /**
123
118
  * Builds the client entry `<script>` tag from a (possibly transformed) client
124
- * entry URL and optional injected head scripts.
119
+ * entry URL.
125
120
  */
126
- export declare function buildClientEntryScriptTag(clientEntry: string, injectedHeadScripts?: string): RouterManagedTag;
121
+ export declare function buildClientEntryScriptTag(clientEntry: string, scriptFormat?: ScriptFormat, crossOrigin?: AssetCrossOrigin): ManifestScript;
127
122
  export declare function transformManifestAssets(source: StartManifestWithClientEntry, transformFn: TransformAssetsFn, _opts?: {
128
123
  clone?: boolean;
129
124
  inlineCss?: boolean;
130
- }): Promise<Manifest>;
125
+ }): Promise<ServerManifest>;
131
126
  /**
132
- * Builds a final Manifest from a StartManifestWithClientEntry without any
127
+ * Builds a final ServerManifest from a StartManifestWithClientEntry without any
133
128
  * URL transforms. Used when no transformAssets option is provided.
134
129
  *
135
130
  * Returns a new manifest object so the cached base manifest is never mutated.
136
131
  */
137
132
  export declare function buildManifestWithClientEntry(source: StartManifestWithClientEntry, opts?: {
138
133
  inlineCss?: boolean;
139
- }): Manifest;
134
+ }): ServerManifest;
@@ -1,4 +1,4 @@
1
- import { resolveManifestAssetLink, rootRouteId } from "@tanstack/router-core";
1
+ import { getManifestScriptFormat, resolveManifestAssetLink, resolveManifestCssLink } from "@tanstack/router-core";
2
2
  //#region src/transformAssetUrls.ts
3
3
  function normalizeTransformAssetResult(result) {
4
4
  if (typeof result === "string") return { href: result };
@@ -65,7 +65,7 @@ function resolveTransformAssetsConfig(transform) {
65
65
  type: "transform",
66
66
  transformFn: ({ url, kind }) => {
67
67
  const href = `${prefix}${url}`;
68
- if (kind === "clientEntry" || kind === "css-url") return { href };
68
+ if (kind === "css-url") return { href };
69
69
  const co = resolveTransformAssetsCrossOrigin(crossOrigin, kind);
70
70
  return co ? {
71
71
  href,
@@ -88,82 +88,109 @@ function resolveTransformAssetsConfig(transform) {
88
88
  }
89
89
  /**
90
90
  * Builds the client entry `<script>` tag from a (possibly transformed) client
91
- * entry URL and optional injected head scripts.
91
+ * entry URL.
92
92
  */
93
- function buildClientEntryScriptTag(clientEntry, injectedHeadScripts) {
94
- let script = `import(${JSON.stringify(clientEntry)})`;
95
- if (injectedHeadScripts) script = `${injectedHeadScripts};${script}`;
96
- return {
97
- tag: "script",
98
- attrs: {
99
- type: "module",
100
- async: true
101
- },
102
- children: script
103
- };
93
+ function buildClientEntryScriptTag(clientEntry, scriptFormat = "module", crossOrigin) {
94
+ return { attrs: {
95
+ ...scriptFormat === "module" ? { type: "module" } : {},
96
+ async: true,
97
+ src: clientEntry,
98
+ ...crossOrigin ? { crossOrigin } : {}
99
+ } };
104
100
  }
105
- function assignManifestAssetLink(link, next) {
101
+ function assignManifestLink(link, next) {
106
102
  if (typeof link === "string") return next.crossOrigin ? next : next.href;
107
- return next.crossOrigin ? next : { href: next.href };
103
+ const nextLink = {
104
+ ...link,
105
+ href: next.href
106
+ };
107
+ if (next.crossOrigin) nextLink.crossOrigin = next.crossOrigin;
108
+ else delete nextLink.crossOrigin;
109
+ return nextLink;
110
+ }
111
+ function appendUniqueManifestAssetLink(target, link) {
112
+ const href = typeof link === "string" ? link : link.href;
113
+ if (target) {
114
+ for (const item of target) if ((typeof item === "string" ? item : item.href) === href) return target;
115
+ }
116
+ return [...target ?? [], link];
117
+ }
118
+ function addClientEntryToManifest(manifest, clientEntry) {
119
+ const rootRoute = manifest.routes.__root__ ?? {};
120
+ const rootScripts = rootRoute.scripts ?? [];
121
+ const scripts = rootScripts.some((script) => script.attrs?.src === clientEntry) ? rootScripts : [...rootScripts, buildClientEntryScriptTag(clientEntry, getManifestScriptFormat(manifest))];
122
+ manifest.routes = {
123
+ ...manifest.routes,
124
+ __root__: {
125
+ ...rootRoute,
126
+ preloads: appendUniqueManifestAssetLink(rootRoute.preloads, clientEntry),
127
+ scripts
128
+ }
129
+ };
108
130
  }
109
131
  async function transformManifestAssets(source, transformFn, _opts) {
110
132
  const manifest = structuredClone(source.manifest);
111
- if (!(_opts?.inlineCss !== false)) delete manifest.inlineCss;
133
+ const inlineCssEnabled = _opts?.inlineCss !== false;
134
+ const scriptTransforms = /* @__PURE__ */ new Map();
135
+ const transformScript = (url) => {
136
+ const cached = scriptTransforms.get(url);
137
+ if (cached) return cached;
138
+ const transformed = Promise.resolve(transformFn({
139
+ url,
140
+ kind: "script"
141
+ })).then(normalizeTransformAssetResult);
142
+ scriptTransforms.set(url, transformed);
143
+ return transformed;
144
+ };
145
+ if (!inlineCssEnabled) delete manifest.inlineCss;
112
146
  else if (manifest.inlineCss) manifest.inlineCss = await transformInlineCssStyles(manifest.inlineCss, transformFn);
147
+ addClientEntryToManifest(manifest, source.clientEntry);
113
148
  for (const route of Object.values(manifest.routes)) {
114
- if (route.preloads) route.preloads = await Promise.all(route.preloads.map(async (link) => {
149
+ if (route.preloads?.length) route.preloads = await Promise.all(route.preloads.map(async (link) => {
150
+ const result = await transformScript(resolveManifestAssetLink(link).href);
151
+ return assignManifestLink(link, {
152
+ href: result.href,
153
+ crossOrigin: result.crossOrigin
154
+ });
155
+ }));
156
+ if (route.css?.length && !manifest.inlineCss) route.css = await Promise.all(route.css.map(async (link) => {
115
157
  const result = normalizeTransformAssetResult(await transformFn({
116
- url: resolveManifestAssetLink(link).href,
117
- kind: "modulepreload"
158
+ url: resolveManifestCssLink(link).href,
159
+ kind: "stylesheet"
118
160
  }));
119
- return assignManifestAssetLink(link, {
161
+ return assignManifestLink(link, {
120
162
  href: result.href,
121
163
  crossOrigin: result.crossOrigin
122
164
  });
123
165
  }));
124
- if (route.assets && !manifest.inlineCss) {
125
- for (const asset of route.assets) if (asset.tag === "link" && asset.attrs?.href) {
126
- const rel = asset.attrs.rel;
127
- if (!(typeof rel === "string" ? rel.split(/\s+/) : []).includes("stylesheet")) continue;
128
- const result = normalizeTransformAssetResult(await transformFn({
129
- url: asset.attrs.href,
130
- kind: "stylesheet"
131
- }));
132
- asset.attrs.href = result.href;
133
- if (result.crossOrigin) asset.attrs.crossOrigin = result.crossOrigin;
134
- else delete asset.attrs.crossOrigin;
135
- }
166
+ if (route.scripts?.length) for (const script of route.scripts) {
167
+ const src = script.attrs?.src;
168
+ if (typeof src !== "string") continue;
169
+ const result = await transformScript(src);
170
+ script.attrs = {
171
+ ...script.attrs,
172
+ src: result.href
173
+ };
174
+ if (result.crossOrigin) script.attrs.crossOrigin = result.crossOrigin;
175
+ else delete script.attrs.crossOrigin;
136
176
  }
137
177
  }
138
- const transformedClientEntry = normalizeTransformAssetResult(await transformFn({
139
- url: source.clientEntry,
140
- kind: "clientEntry"
141
- }));
142
- const rootRoute = manifest.routes[rootRouteId] = manifest.routes[rootRouteId] || {};
143
- rootRoute.assets = rootRoute.assets || [];
144
- rootRoute.assets.push(buildClientEntryScriptTag(transformedClientEntry.href, source.injectedHeadScripts));
145
178
  return manifest;
146
179
  }
147
180
  /**
148
- * Builds a final Manifest from a StartManifestWithClientEntry without any
181
+ * Builds a final ServerManifest from a StartManifestWithClientEntry without any
149
182
  * URL transforms. Used when no transformAssets option is provided.
150
183
  *
151
184
  * Returns a new manifest object so the cached base manifest is never mutated.
152
185
  */
153
186
  function buildManifestWithClientEntry(source, opts) {
154
- const scriptTag = buildClientEntryScriptTag(source.clientEntry, source.injectedHeadScripts);
155
- const baseRootRoute = source.manifest.routes[rootRouteId];
156
- const routes = {
157
- ...source.manifest.routes,
158
- [rootRouteId]: {
159
- ...baseRootRoute,
160
- assets: [...baseRootRoute?.assets || [], scriptTag]
161
- }
162
- };
163
- return {
164
- ...opts?.inlineCss === false ? {} : { inlineCss: structuredClone(source.manifest.inlineCss) },
165
- routes
187
+ const manifest = {
188
+ ...source.manifest.scriptFormat ? { scriptFormat: source.manifest.scriptFormat } : {},
189
+ ...opts?.inlineCss !== false && source.manifest.inlineCss ? { inlineCss: structuredClone(source.manifest.inlineCss) } : {},
190
+ routes: { ...source.manifest.routes }
166
191
  };
192
+ addClientEntryToManifest(manifest, source.clientEntry);
193
+ return manifest;
167
194
  }
168
195
  //#endregion
169
196
  export { buildManifestWithClientEntry, resolveTransformAssetsConfig, transformManifestAssets };
@@ -1 +1 @@
1
- {"version":3,"file":"transformAssetUrls.js","names":[],"sources":["../../src/transformAssetUrls.ts"],"sourcesContent":["import { resolveManifestAssetLink, rootRouteId } from '@tanstack/router-core'\n\nimport type {\n AssetCrossOrigin,\n Awaitable,\n Manifest,\n ManifestAssetLink,\n RouterManagedTag,\n} from '@tanstack/router-core'\n\nexport type { AssetCrossOrigin }\n\nexport type TransformAssetsContext =\n | {\n url: string\n kind: 'modulepreload'\n }\n | {\n url: string\n kind: 'stylesheet'\n }\n | {\n url: string\n kind: 'clientEntry'\n }\n | {\n url: string\n kind: 'css-url'\n stylesheetHref: string\n }\n\nexport type TransformAssetKind = TransformAssetsContext['kind']\n\ntype TransformAssetsShorthandCrossOriginKind = Exclude<\n TransformAssetKind,\n 'clientEntry' | 'css-url'\n>\n\nexport type TransformAssetResult =\n | string\n | {\n href: string\n crossOrigin?: AssetCrossOrigin\n }\n\nexport type TransformAssetsFn = (\n context: TransformAssetsContext,\n) => Awaitable<TransformAssetResult>\n\nexport type CreateTransformAssetsContext =\n | {\n /** True when the server is computing the cached manifest during startup warmup. */\n warmup: true\n }\n | {\n /**\n * The current Request.\n *\n * Only available during request handling (i.e. when `warmup: false`).\n */\n request: Request\n /** False when transforming URLs as part of request handling. */\n warmup: false\n }\n\nexport type CreateTransformAssetsFn = (\n ctx: CreateTransformAssetsContext,\n) => Awaitable<TransformAssetsFn>\n\ntype TransformAssetsOptionsBase = {\n /**\n * Whether to cache the transformed manifest after the first request.\n *\n * When `true` (default), the transform runs once on the first request and\n * the resulting manifest is reused for all subsequent requests in production.\n *\n * Set to `false` for per-request transforms (e.g. geo-routing to different\n * CDNs based on request headers).\n *\n * @default true\n */\n cache?: boolean\n\n /**\n * When `true`, warms up the cached transformed manifest in the background when\n * the server starts (production only).\n *\n * This can reduce latency for the first request when `cache` is `true`.\n * Has no effect when `cache: false` (per-request transforms) or in dev mode.\n *\n * @default false\n */\n warmup?: boolean\n}\n\nexport type TransformAssetsOptions =\n | (TransformAssetsOptionsBase & {\n transform: string | TransformAssetsFn\n createTransform?: never\n })\n | (TransformAssetsOptionsBase & {\n createTransform: CreateTransformAssetsFn\n transform?: never\n })\n\n/**\n * Per-kind crossOrigin configuration for the object shorthand.\n *\n * Accepts either a single value applied to all asset kinds, or a per-kind\n * record (matching `HeadContent`'s `assetCrossOrigin` shape):\n *\n * ```ts\n * // All assets get the same value\n * crossOrigin: 'anonymous'\n *\n * // Different values per kind\n * crossOrigin: { modulepreload: 'anonymous', stylesheet: 'use-credentials' }\n * ```\n */\nexport type TransformAssetsCrossOriginConfig =\n | AssetCrossOrigin\n | Partial<Record<TransformAssetsShorthandCrossOriginKind, AssetCrossOrigin>>\n\n/**\n * Object shorthand for `transformAssets`. Combines a URL prefix with optional\n * per-asset `crossOrigin` without needing a callback:\n *\n * ```ts\n * transformAssets: {\n * prefix: 'https://cdn.example.com',\n * crossOrigin: 'anonymous',\n * }\n * ```\n */\nexport interface TransformAssetsObjectShorthand {\n /** URL prefix prepended to every asset URL. */\n prefix: string\n /**\n * Optional crossOrigin attribute applied to manifest-managed `<link>` assets.\n *\n * Accepts a single value or a per-kind record.\n */\n crossOrigin?: TransformAssetsCrossOriginConfig\n}\n\nexport type TransformAssets =\n | string\n | TransformAssetsFn\n | TransformAssetsObjectShorthand\n | TransformAssetsOptions\n\nexport type ResolvedTransformAssetsConfig =\n | {\n type: 'transform'\n transformFn: TransformAssetsFn\n cache: boolean\n }\n | {\n type: 'createTransform'\n createTransform: CreateTransformAssetsFn\n cache: boolean\n }\n\nfunction normalizeTransformAssetResult(\n result: TransformAssetResult,\n): Exclude<TransformAssetResult, string> {\n if (typeof result === 'string') {\n return { href: result }\n }\n\n return result\n}\n\nfunction escapeCssString(value: string) {\n return value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\a ')\n .replace(/\\r/g, '\\\\d ')\n .replace(/\\f/g, '\\\\c ')\n}\n\nasync function transformInlineCssTemplate(options: {\n stylesheetHref: string\n template: { strings: Array<string>; urls: Array<string> }\n transformFn: TransformAssetsFn\n}) {\n const { strings, urls } = options.template\n\n if (strings.length !== urls.length + 1) {\n throw new Error(\n `TanStack Start inlineCss template for ${options.stylesheetHref} is invalid`,\n )\n }\n\n let css = strings[0]!\n\n for (let index = 0; index < urls.length; index++) {\n const transformed = normalizeTransformAssetResult(\n await options.transformFn({\n kind: 'css-url',\n url: urls[index]!,\n stylesheetHref: options.stylesheetHref,\n }),\n )\n\n css += escapeCssString(transformed.href) + strings[index + 1]!\n }\n\n return css\n}\n\nasync function transformInlineCssStyles(\n inlineCss: NonNullable<Manifest['inlineCss']>,\n transformFn: TransformAssetsFn,\n) {\n const transformedStyles: Record<string, string> = {}\n\n const transformedEntries = await Promise.all(\n Object.entries(inlineCss.styles).map(async ([stylesheetHref, css]) => {\n const template = inlineCss.templates?.[stylesheetHref]\n return [\n stylesheetHref,\n template\n ? await transformInlineCssTemplate({\n stylesheetHref,\n template,\n transformFn,\n })\n : css,\n ] as const\n }),\n )\n\n for (const [stylesheetHref, css] of transformedEntries) {\n transformedStyles[stylesheetHref] = css\n }\n\n return {\n styles: transformedStyles,\n ...(inlineCss.templates ? { templates: inlineCss.templates } : {}),\n }\n}\n\nfunction resolveTransformAssetsCrossOrigin(\n config: TransformAssetsCrossOriginConfig | undefined,\n kind: TransformAssetsShorthandCrossOriginKind,\n): AssetCrossOrigin | undefined {\n if (!config) return undefined\n if (typeof config === 'string') return config\n\n return config[kind]\n}\n\nfunction isObjectShorthand(\n transform: TransformAssetsObjectShorthand | TransformAssetsOptions,\n): transform is TransformAssetsObjectShorthand {\n return 'prefix' in transform\n}\n\nexport function resolveTransformAssetsConfig(\n transform: TransformAssets,\n): ResolvedTransformAssetsConfig {\n if (typeof transform === 'string') {\n const prefix = transform\n return {\n type: 'transform',\n transformFn: ({ url }) => ({ href: `${prefix}${url}` }),\n cache: true,\n }\n }\n\n if (typeof transform === 'function') {\n return {\n type: 'transform',\n transformFn: transform,\n cache: true,\n }\n }\n\n // Object shorthand: { prefix, crossOrigin? }\n if (isObjectShorthand(transform)) {\n const { prefix, crossOrigin } = transform\n\n return {\n type: 'transform',\n transformFn: ({ url, kind }) => {\n const href = `${prefix}${url}`\n\n if (kind === 'clientEntry' || kind === 'css-url') {\n return { href }\n }\n\n const co = resolveTransformAssetsCrossOrigin(crossOrigin, kind)\n return co ? { href, crossOrigin: co } : { href }\n },\n cache: true,\n }\n }\n\n if ('createTransform' in transform && transform.createTransform) {\n return {\n type: 'createTransform',\n createTransform: transform.createTransform,\n cache: transform.cache !== false,\n }\n }\n\n const transformFn =\n typeof transform.transform === 'string'\n ? ((({ url }: TransformAssetsContext) => ({\n href: `${transform.transform}${url}`,\n })) as TransformAssetsFn)\n : transform.transform\n\n return {\n type: 'transform',\n transformFn,\n cache: transform.cache !== false,\n }\n}\n\nexport interface StartManifestWithClientEntry {\n manifest: Manifest\n clientEntry: string\n /** Script content prepended before the client entry import (dev only) */\n injectedHeadScripts?: string\n}\n\n/**\n * Builds the client entry `<script>` tag from a (possibly transformed) client\n * entry URL and optional injected head scripts.\n */\nexport function buildClientEntryScriptTag(\n clientEntry: string,\n injectedHeadScripts?: string,\n): RouterManagedTag {\n const clientEntryLiteral = JSON.stringify(clientEntry)\n let script = `import(${clientEntryLiteral})`\n if (injectedHeadScripts) {\n script = `${injectedHeadScripts};${script}`\n }\n return {\n tag: 'script',\n attrs: {\n type: 'module',\n async: true,\n },\n children: script,\n }\n}\n\nfunction assignManifestAssetLink(\n link: ManifestAssetLink,\n next: { href: string; crossOrigin?: AssetCrossOrigin },\n): ManifestAssetLink {\n if (typeof link === 'string') {\n return next.crossOrigin ? next : next.href\n }\n\n return next.crossOrigin ? next : { href: next.href }\n}\n\nexport async function transformManifestAssets(\n source: StartManifestWithClientEntry,\n transformFn: TransformAssetsFn,\n _opts?: {\n clone?: boolean\n inlineCss?: boolean\n },\n): Promise<Manifest> {\n const manifest = structuredClone(source.manifest)\n const inlineCssEnabled = _opts?.inlineCss !== false\n\n if (!inlineCssEnabled) {\n delete manifest.inlineCss\n } else if (manifest.inlineCss) {\n manifest.inlineCss = await transformInlineCssStyles(\n manifest.inlineCss,\n transformFn,\n )\n }\n\n for (const route of Object.values(manifest.routes)) {\n if (route.preloads) {\n route.preloads = await Promise.all(\n route.preloads.map(async (link) => {\n const resolved = resolveManifestAssetLink(link)\n const result = normalizeTransformAssetResult(\n await transformFn({\n url: resolved.href,\n kind: 'modulepreload',\n }),\n )\n\n return assignManifestAssetLink(link, {\n href: result.href,\n crossOrigin: result.crossOrigin,\n })\n }),\n )\n }\n\n if (route.assets && !manifest.inlineCss) {\n for (const asset of route.assets) {\n if (asset.tag === 'link' && asset.attrs?.href) {\n const rel = asset.attrs.rel\n const relTokens = typeof rel === 'string' ? rel.split(/\\s+/) : []\n\n if (!relTokens.includes('stylesheet')) {\n continue\n }\n\n const result = normalizeTransformAssetResult(\n await transformFn({\n url: asset.attrs.href,\n kind: 'stylesheet',\n }),\n )\n\n asset.attrs.href = result.href\n if (result.crossOrigin) {\n asset.attrs.crossOrigin = result.crossOrigin\n } else {\n delete asset.attrs.crossOrigin\n }\n }\n }\n }\n }\n\n const transformedClientEntry = normalizeTransformAssetResult(\n await transformFn({\n url: source.clientEntry,\n kind: 'clientEntry',\n }),\n )\n\n const rootRoute = (manifest.routes[rootRouteId] =\n manifest.routes[rootRouteId] || {})\n rootRoute.assets = rootRoute.assets || []\n rootRoute.assets.push(\n buildClientEntryScriptTag(\n transformedClientEntry.href,\n source.injectedHeadScripts,\n ),\n )\n\n return manifest\n}\n\n/**\n * Builds a final Manifest from a StartManifestWithClientEntry without any\n * URL transforms. Used when no transformAssets option is provided.\n *\n * Returns a new manifest object so the cached base manifest is never mutated.\n */\nexport function buildManifestWithClientEntry(\n source: StartManifestWithClientEntry,\n opts?: { inlineCss?: boolean },\n): Manifest {\n const scriptTag = buildClientEntryScriptTag(\n source.clientEntry,\n source.injectedHeadScripts,\n )\n\n const baseRootRoute = source.manifest.routes[rootRouteId]\n const routes = {\n ...source.manifest.routes,\n [rootRouteId]: {\n ...baseRootRoute,\n assets: [...(baseRootRoute?.assets || []), scriptTag],\n },\n }\n\n return {\n ...(opts?.inlineCss === false\n ? {}\n : { inlineCss: structuredClone(source.manifest.inlineCss) }),\n routes,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"transformAssetUrls.js","names":[],"sources":["../../src/transformAssetUrls.ts"],"sourcesContent":["import {\n getManifestScriptFormat,\n resolveManifestAssetLink,\n resolveManifestCssLink,\n} from '@tanstack/router-core'\n\nimport type {\n AssetCrossOrigin,\n Awaitable,\n ManifestAssetLink,\n ManifestCssLink,\n ManifestScript,\n ScriptFormat,\n ServerManifest,\n} from '@tanstack/router-core'\n\nexport type { AssetCrossOrigin }\n\nexport type TransformAssetsContext =\n | {\n url: string\n kind: 'script'\n }\n | {\n url: string\n kind: 'stylesheet'\n }\n | {\n url: string\n kind: 'css-url'\n stylesheetHref: string\n }\n\nexport type TransformAssetKind = TransformAssetsContext['kind']\n\ntype TransformAssetsShorthandCrossOriginKind = Exclude<\n TransformAssetKind,\n 'css-url'\n>\n\nexport type TransformAssetResult =\n | string\n | {\n href: string\n crossOrigin?: AssetCrossOrigin\n }\n\nexport type TransformAssetsFn = (\n context: TransformAssetsContext,\n) => Awaitable<TransformAssetResult>\n\nexport type CreateTransformAssetsContext =\n | {\n /** True when the server is computing the cached manifest during startup warmup. */\n warmup: true\n }\n | {\n /**\n * The current Request.\n *\n * Only available during request handling (i.e. when `warmup: false`).\n */\n request: Request\n /** False when transforming URLs as part of request handling. */\n warmup: false\n }\n\nexport type CreateTransformAssetsFn = (\n ctx: CreateTransformAssetsContext,\n) => Awaitable<TransformAssetsFn>\n\ntype TransformAssetsOptionsBase = {\n /**\n * Whether to cache the transformed manifest after the first request.\n *\n * When `true` (default), the transform runs once on the first request and\n * the resulting manifest is reused for all subsequent requests in production.\n *\n * Set to `false` for per-request transforms (e.g. geo-routing to different\n * CDNs based on request headers).\n *\n * @default true\n */\n cache?: boolean\n\n /**\n * When `true`, warms up the cached transformed manifest in the background when\n * the server starts (production only).\n *\n * This can reduce latency for the first request when `cache` is `true`.\n * Has no effect when `cache: false` (per-request transforms) or in dev mode.\n *\n * @default false\n */\n warmup?: boolean\n}\n\nexport type TransformAssetsOptions =\n | (TransformAssetsOptionsBase & {\n transform: string | TransformAssetsFn\n createTransform?: never\n })\n | (TransformAssetsOptionsBase & {\n createTransform: CreateTransformAssetsFn\n transform?: never\n })\n\n/**\n * Per-kind crossOrigin configuration for the object shorthand.\n *\n * Accepts either a single value applied to all asset kinds, or a per-kind\n * record (matching `HeadContent`'s `assetCrossOrigin` shape):\n *\n * ```ts\n * // All assets get the same value\n * crossOrigin: 'anonymous'\n *\n * // Different values per kind\n * crossOrigin: { script: 'anonymous', stylesheet: 'use-credentials' }\n * ```\n */\nexport type TransformAssetsCrossOriginConfig =\n | AssetCrossOrigin\n | Partial<Record<TransformAssetsShorthandCrossOriginKind, AssetCrossOrigin>>\n\n/**\n * Object shorthand for `transformAssets`. Combines a URL prefix with optional\n * per-asset `crossOrigin` without needing a callback:\n *\n * ```ts\n * transformAssets: {\n * prefix: 'https://cdn.example.com',\n * crossOrigin: 'anonymous',\n * }\n * ```\n */\nexport interface TransformAssetsObjectShorthand {\n /** URL prefix prepended to every asset URL. */\n prefix: string\n /**\n * Optional crossOrigin attribute applied to transformed script and stylesheet assets.\n *\n * Accepts a single value or a per-kind record.\n */\n crossOrigin?: TransformAssetsCrossOriginConfig\n}\n\nexport type TransformAssets =\n | string\n | TransformAssetsFn\n | TransformAssetsObjectShorthand\n | TransformAssetsOptions\n\nexport type ResolvedTransformAssetsConfig =\n | {\n type: 'transform'\n transformFn: TransformAssetsFn\n cache: boolean\n }\n | {\n type: 'createTransform'\n createTransform: CreateTransformAssetsFn\n cache: boolean\n }\n\nfunction normalizeTransformAssetResult(\n result: TransformAssetResult,\n): Exclude<TransformAssetResult, string> {\n if (typeof result === 'string') {\n return { href: result }\n }\n\n return result\n}\n\nfunction escapeCssString(value: string) {\n return value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\a ')\n .replace(/\\r/g, '\\\\d ')\n .replace(/\\f/g, '\\\\c ')\n}\n\nasync function transformInlineCssTemplate(options: {\n stylesheetHref: string\n template: { strings: Array<string>; urls: Array<string> }\n transformFn: TransformAssetsFn\n}) {\n const { strings, urls } = options.template\n\n if (strings.length !== urls.length + 1) {\n throw new Error(\n `TanStack Start inlineCss template for ${options.stylesheetHref} is invalid`,\n )\n }\n\n let css = strings[0]!\n\n for (let index = 0; index < urls.length; index++) {\n const transformed = normalizeTransformAssetResult(\n await options.transformFn({\n kind: 'css-url',\n url: urls[index]!,\n stylesheetHref: options.stylesheetHref,\n }),\n )\n\n css += escapeCssString(transformed.href) + strings[index + 1]!\n }\n\n return css\n}\n\nasync function transformInlineCssStyles(\n inlineCss: NonNullable<ServerManifest['inlineCss']>,\n transformFn: TransformAssetsFn,\n) {\n const transformedStyles: Record<string, string> = {}\n\n const transformedEntries = await Promise.all(\n Object.entries(inlineCss.styles).map(async ([stylesheetHref, css]) => {\n const template = inlineCss.templates?.[stylesheetHref]\n return [\n stylesheetHref,\n template\n ? await transformInlineCssTemplate({\n stylesheetHref,\n template,\n transformFn,\n })\n : css,\n ] as const\n }),\n )\n\n for (const [stylesheetHref, css] of transformedEntries) {\n transformedStyles[stylesheetHref] = css\n }\n\n return {\n styles: transformedStyles,\n ...(inlineCss.templates ? { templates: inlineCss.templates } : {}),\n }\n}\n\nfunction resolveTransformAssetsCrossOrigin(\n config: TransformAssetsCrossOriginConfig | undefined,\n kind: TransformAssetsShorthandCrossOriginKind,\n): AssetCrossOrigin | undefined {\n if (!config) return undefined\n if (typeof config === 'string') return config\n\n return config[kind]\n}\n\nfunction isObjectShorthand(\n transform: TransformAssetsObjectShorthand | TransformAssetsOptions,\n): transform is TransformAssetsObjectShorthand {\n return 'prefix' in transform\n}\n\nexport function resolveTransformAssetsConfig(\n transform: TransformAssets,\n): ResolvedTransformAssetsConfig {\n if (typeof transform === 'string') {\n const prefix = transform\n return {\n type: 'transform',\n transformFn: ({ url }) => ({ href: `${prefix}${url}` }),\n cache: true,\n }\n }\n\n if (typeof transform === 'function') {\n return {\n type: 'transform',\n transformFn: transform,\n cache: true,\n }\n }\n\n // Object shorthand: { prefix, crossOrigin? }\n if (isObjectShorthand(transform)) {\n const { prefix, crossOrigin } = transform\n\n return {\n type: 'transform',\n transformFn: ({ url, kind }) => {\n const href = `${prefix}${url}`\n\n if (kind === 'css-url') {\n return { href }\n }\n\n const co = resolveTransformAssetsCrossOrigin(crossOrigin, kind)\n return co ? { href, crossOrigin: co } : { href }\n },\n cache: true,\n }\n }\n\n if ('createTransform' in transform && transform.createTransform) {\n return {\n type: 'createTransform',\n createTransform: transform.createTransform,\n cache: transform.cache !== false,\n }\n }\n\n const transformFn =\n typeof transform.transform === 'string'\n ? ((({ url }: TransformAssetsContext) => ({\n href: `${transform.transform}${url}`,\n })) as TransformAssetsFn)\n : transform.transform\n\n return {\n type: 'transform',\n transformFn,\n cache: transform.cache !== false,\n }\n}\n\nexport interface StartManifestWithClientEntry {\n manifest: ServerManifest\n clientEntry: string\n}\n\n/**\n * Builds the client entry `<script>` tag from a (possibly transformed) client\n * entry URL.\n */\nexport function buildClientEntryScriptTag(\n clientEntry: string,\n scriptFormat: ScriptFormat = 'module',\n crossOrigin?: AssetCrossOrigin,\n): ManifestScript {\n return {\n attrs: {\n ...(scriptFormat === 'module' ? { type: 'module' } : {}),\n async: true,\n src: clientEntry,\n ...(crossOrigin ? { crossOrigin } : {}),\n },\n }\n}\n\ntype AssignableManifestLink = ManifestAssetLink | ManifestCssLink\n\nfunction assignManifestLink(\n link: ManifestAssetLink,\n next: { href: string; crossOrigin?: AssetCrossOrigin },\n): ManifestAssetLink\nfunction assignManifestLink(\n link: ManifestCssLink,\n next: { href: string; crossOrigin?: AssetCrossOrigin },\n): ManifestCssLink\nfunction assignManifestLink(\n link: AssignableManifestLink,\n next: { href: string; crossOrigin?: AssetCrossOrigin },\n): AssignableManifestLink {\n if (typeof link === 'string') {\n return next.crossOrigin ? next : next.href\n }\n\n const nextLink: Exclude<ManifestCssLink, string> = {\n ...link,\n href: next.href,\n }\n\n if (next.crossOrigin) {\n nextLink.crossOrigin = next.crossOrigin\n } else {\n delete nextLink.crossOrigin\n }\n\n return nextLink\n}\n\nfunction appendUniqueManifestAssetLink(\n target: Array<ManifestAssetLink> | undefined,\n link: ManifestAssetLink,\n) {\n const href = typeof link === 'string' ? link : link.href\n\n if (target) {\n for (const item of target) {\n if ((typeof item === 'string' ? item : item.href) === href) {\n return target\n }\n }\n }\n\n return [...(target ?? []), link]\n}\n\nfunction addClientEntryToManifest(\n manifest: ServerManifest,\n clientEntry: string,\n) {\n const rootRoute = manifest.routes.__root__ ?? {}\n const rootScripts = rootRoute.scripts ?? []\n const scripts = rootScripts.some(\n (script) => script.attrs?.src === clientEntry,\n )\n ? rootScripts\n : [\n ...rootScripts,\n buildClientEntryScriptTag(\n clientEntry,\n getManifestScriptFormat(manifest),\n ),\n ]\n\n manifest.routes = {\n ...manifest.routes,\n __root__: {\n ...rootRoute,\n preloads: appendUniqueManifestAssetLink(rootRoute.preloads, clientEntry),\n scripts,\n },\n }\n}\n\nexport async function transformManifestAssets(\n source: StartManifestWithClientEntry,\n transformFn: TransformAssetsFn,\n _opts?: {\n clone?: boolean\n inlineCss?: boolean\n },\n): Promise<ServerManifest> {\n const manifest = structuredClone(source.manifest)\n const inlineCssEnabled = _opts?.inlineCss !== false\n const scriptTransforms = new Map<\n string,\n Promise<Exclude<TransformAssetResult, string>>\n >()\n const transformScript = (url: string) => {\n const cached = scriptTransforms.get(url)\n if (cached) {\n return cached\n }\n\n const transformed = Promise.resolve(\n transformFn({\n url,\n kind: 'script',\n }),\n ).then(normalizeTransformAssetResult)\n scriptTransforms.set(url, transformed)\n return transformed\n }\n\n if (!inlineCssEnabled) {\n delete manifest.inlineCss\n } else if (manifest.inlineCss) {\n manifest.inlineCss = await transformInlineCssStyles(\n manifest.inlineCss,\n transformFn,\n )\n }\n\n addClientEntryToManifest(manifest, source.clientEntry)\n\n for (const route of Object.values(manifest.routes)) {\n if (route.preloads?.length) {\n route.preloads = await Promise.all(\n route.preloads.map(async (link) => {\n const resolved = resolveManifestAssetLink(link)\n const result = await transformScript(resolved.href)\n\n return assignManifestLink(link, {\n href: result.href,\n crossOrigin: result.crossOrigin,\n })\n }),\n )\n }\n\n if (route.css?.length && !manifest.inlineCss) {\n route.css = await Promise.all(\n route.css.map(async (link) => {\n const resolved = resolveManifestCssLink(link)\n const result = normalizeTransformAssetResult(\n await transformFn({\n url: resolved.href,\n kind: 'stylesheet',\n }),\n )\n\n return assignManifestLink(link, {\n href: result.href,\n crossOrigin: result.crossOrigin,\n })\n }),\n )\n }\n\n if (route.scripts?.length) {\n for (const script of route.scripts) {\n const src = script.attrs?.src\n if (typeof src !== 'string') {\n continue\n }\n\n const result = await transformScript(src)\n\n script.attrs = {\n ...script.attrs,\n src: result.href,\n }\n if (result.crossOrigin) {\n script.attrs.crossOrigin = result.crossOrigin\n } else {\n delete script.attrs.crossOrigin\n }\n }\n }\n }\n\n return manifest\n}\n\n/**\n * Builds a final ServerManifest from a StartManifestWithClientEntry without any\n * URL transforms. Used when no transformAssets option is provided.\n *\n * Returns a new manifest object so the cached base manifest is never mutated.\n */\nexport function buildManifestWithClientEntry(\n source: StartManifestWithClientEntry,\n opts?: { inlineCss?: boolean },\n): ServerManifest {\n const manifest: ServerManifest = {\n ...(source.manifest.scriptFormat\n ? { scriptFormat: source.manifest.scriptFormat }\n : {}),\n ...(opts?.inlineCss !== false && source.manifest.inlineCss\n ? { inlineCss: structuredClone(source.manifest.inlineCss) }\n : {}),\n routes: {\n ...source.manifest.routes,\n },\n }\n\n addClientEntryToManifest(manifest, source.clientEntry)\n\n return manifest\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-server-core",
3
- "version": "1.169.1",
3
+ "version": "1.169.3",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -67,9 +67,9 @@
67
67
  "h3-v2": "npm:h3@2.0.1-rc.20",
68
68
  "seroval": "^1.5.4",
69
69
  "@tanstack/history": "1.162.0",
70
- "@tanstack/router-core": "1.171.4",
71
- "@tanstack/start-client-core": "1.170.1",
72
- "@tanstack/start-storage-context": "1.167.6"
70
+ "@tanstack/router-core": "1.171.6",
71
+ "@tanstack/start-client-core": "1.170.3",
72
+ "@tanstack/start-storage-context": "1.167.8"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@standard-schema/spec": "^1.0.0",
@@ -497,7 +497,6 @@ export function createStartHandler<TRegister = Register>(
497
497
  manifest,
498
498
  getRequestAssets: () =>
499
499
  getStartContext({ throwIfNotFound: false })?.requestAssets,
500
- includeUnmatchedRouteAssets: false,
501
500
  })
502
501
 
503
502
  routerInstance.update({ additionalContext: { serverContext } })
@@ -1,13 +1,14 @@
1
1
  import {
2
+ getScriptPreloadAttrs,
2
3
  getStylesheetHref,
3
- resolveManifestAssetLink,
4
+ resolveManifestCssLink,
4
5
  } from '@tanstack/router-core'
5
6
  import type {
6
7
  AnyRoute,
7
8
  AnyRouteMatch,
8
9
  AssetCrossOrigin,
9
- Manifest,
10
10
  RouterManagedTag,
11
+ ServerManifest,
11
12
  } from '@tanstack/router-core'
12
13
 
13
14
  export type EarlyHint = {
@@ -49,7 +50,7 @@ export type ResponseLinkHeaderOptions = {
49
50
 
50
51
  export interface EarlyHintsCollector {
51
52
  collectStatic: (opts: {
52
- manifest: Manifest
53
+ manifest: ServerManifest
53
54
  matchedRoutes?: ReadonlyArray<AnyRoute>
54
55
  }) => void
55
56
  collectDynamic: (matches: ReadonlyArray<AnyRouteMatch>) => void
@@ -174,7 +175,7 @@ function linkAttrsToEarlyHint(
174
175
  }
175
176
 
176
177
  export function collectStaticHintsFromManifest(
177
- manifest: Manifest,
178
+ manifest: ServerManifest,
178
179
  matchedRoutes: ReadonlyArray<AnyRoute>,
179
180
  ): Array<EarlyHint> {
180
181
  const hints: Array<EarlyHint> = []
@@ -184,33 +185,32 @@ export function collectStaticHintsFromManifest(
184
185
  if (!routeManifest) continue
185
186
 
186
187
  for (const link of routeManifest.preloads ?? []) {
187
- const { href, crossOrigin } = resolveManifestAssetLink(link)
188
- const hint: EarlyHint = { href, rel: 'modulepreload', as: 'script' }
189
- if (crossOrigin !== undefined) hint.crossOrigin = crossOrigin
188
+ const attrs = getScriptPreloadAttrs(manifest, link)
189
+ const hint: EarlyHint = {
190
+ href: attrs.href,
191
+ rel: attrs.rel,
192
+ as: 'script',
193
+ }
194
+ if (attrs.crossOrigin !== undefined) hint.crossOrigin = attrs.crossOrigin
190
195
  hints.push(hint)
191
196
  }
192
197
 
193
- for (const asset of routeManifest.assets ?? []) {
194
- if (asset.tag !== 'link') continue
195
-
196
- const stylesheetHref = getStylesheetHref(asset)
197
- if (stylesheetHref) {
198
- if (manifest.inlineCss?.styles[stylesheetHref] !== undefined) continue
199
-
200
- const hint: EarlyHint = {
201
- href: stylesheetHref,
202
- rel: 'preload',
203
- as: 'style',
204
- }
205
- addEarlyHintFetchAttrs(hint, asset.attrs)
206
- hints.push(hint)
198
+ for (const link of routeManifest.css ?? []) {
199
+ const stylesheetHref = getStylesheetHref(link)
200
+ if (manifest.inlineCss?.styles[stylesheetHref] !== undefined) {
207
201
  continue
208
202
  }
203
+ const resolvedLink = resolveManifestCssLink(link)
209
204
 
210
- const hint = linkAttrsToEarlyHint(asset.attrs)
211
- if (hint) {
212
- hints.push(hint)
205
+ const hint: EarlyHint = {
206
+ href: stylesheetHref,
207
+ rel: 'preload',
208
+ as: 'style',
213
209
  }
210
+ if (resolvedLink.crossOrigin !== undefined) {
211
+ hint.crossOrigin = resolvedLink.crossOrigin
212
+ }
213
+ hints.push(hint)
214
214
  }
215
215
  }
216
216
 
@@ -7,7 +7,7 @@ import {
7
7
  getStaticHandlerInlineCssDefault,
8
8
  resolveInlineCssForRequest,
9
9
  } from './inlineCss'
10
- import type { Manifest } from '@tanstack/router-core'
10
+ import type { ServerManifest } from '@tanstack/router-core'
11
11
  import type { HandlerInlineCssOption } from './inlineCss'
12
12
  import type {
13
13
  CreateTransformAssetsContext,
@@ -45,7 +45,7 @@ export interface FinalManifestOptions {
45
45
  }
46
46
 
47
47
  type FinalManifestCacheKey = 'inline-css' | 'linked-css'
48
- type FinalManifestCache = Map<FinalManifestCacheKey, Promise<Manifest>>
48
+ type FinalManifestCache = Map<FinalManifestCacheKey, Promise<ServerManifest>>
49
49
  export type GetBaseManifest = () => Promise<StartManifestWithClientEntry>
50
50
 
51
51
  export interface FinalManifestRequestOptions {
@@ -66,9 +66,11 @@ interface FinalManifestTransformResolver {
66
66
  export interface FinalManifestResolver {
67
67
  warmup: (opts: {
68
68
  getBaseManifest: GetBaseManifest
69
- }) => Promise<Manifest> | undefined
70
- resolveCached: (opts: FinalManifestRequestOptions) => Promise<Manifest>
71
- resolveUncached: (opts: FinalManifestRequestOptions) => Promise<Manifest>
69
+ }) => Promise<ServerManifest> | undefined
70
+ resolveCached: (opts: FinalManifestRequestOptions) => Promise<ServerManifest>
71
+ resolveUncached: (
72
+ opts: FinalManifestRequestOptions,
73
+ ) => Promise<ServerManifest>
72
74
  }
73
75
 
74
76
  export function createCachedBaseManifestLoader(
@@ -206,8 +208,8 @@ function getFinalManifestCacheKey(inlineCss: boolean): FinalManifestCacheKey {
206
208
  function cacheFinalManifestPromise(
207
209
  cachedFinalManifestPromises: FinalManifestCache,
208
210
  cacheKey: FinalManifestCacheKey,
209
- promise: Promise<Manifest>,
210
- ): Promise<Manifest> {
211
+ promise: Promise<ServerManifest>,
212
+ ): Promise<ServerManifest> {
211
213
  const cachedFinalManifestPromise = promise.catch((error) => {
212
214
  if (
213
215
  cachedFinalManifestPromises.get(cacheKey) === cachedFinalManifestPromise
@@ -224,8 +226,8 @@ function cacheFinalManifestPromise(
224
226
  function getOrCreateCachedFinalManifestPromise(
225
227
  cachedFinalManifestPromises: FinalManifestCache,
226
228
  cacheKey: FinalManifestCacheKey,
227
- computeFinalManifest: () => Promise<Manifest>,
228
- ): Promise<Manifest> {
229
+ computeFinalManifest: () => Promise<ServerManifest>,
230
+ ): Promise<ServerManifest> {
229
231
  const cachedFinalManifestPromise = cachedFinalManifestPromises.get(cacheKey)
230
232
  if (cachedFinalManifestPromise) {
231
233
  return cachedFinalManifestPromise
@@ -242,7 +244,7 @@ async function buildFinalManifest(opts: {
242
244
  base: StartManifestWithClientEntry
243
245
  transformFn: TransformAssetsFn | undefined
244
246
  inlineCss: boolean
245
- }): Promise<Manifest> {
247
+ }): Promise<ServerManifest> {
246
248
  return opts.transformFn
247
249
  ? await transformManifestAssets(opts.base, opts.transformFn, {
248
250
  inlineCss: opts.inlineCss,
@@ -256,7 +258,7 @@ async function resolveFinalManifest(opts: {
256
258
  cache: boolean
257
259
  inlineCss: boolean
258
260
  finalManifestCache?: FinalManifestCache
259
- }): Promise<Manifest> {
261
+ }): Promise<ServerManifest> {
260
262
  const computeFinalManifest = async () => {
261
263
  return buildFinalManifest({
262
264
  base: await opts.getBaseManifest(),
@@ -284,7 +286,7 @@ function warmupFinalManifest(opts: {
284
286
  getBaseManifest: () => Promise<StartManifestWithClientEntry>
285
287
  getTransformFn: () => Promise<TransformAssetsFn | undefined>
286
288
  onError?: () => void
287
- }): Promise<Manifest> | undefined {
289
+ }): Promise<ServerManifest> | undefined {
288
290
  if (
289
291
  !opts.enabled ||
290
292
  opts.handlerDefaultInlineCss === undefined ||
@@ -9,7 +9,8 @@ type EarlyHintsOptions = {
9
9
  * Fire-and-forget callback for HTTP 103 Early Hints.
10
10
  * Only invoked in production (when TSS_DEV_SERVER !== 'true').
11
11
  *
12
- * The `static` phase contains transformed manifest assets for matched routes.
12
+ * The `static` phase contains transformed manifest preloads and stylesheets
13
+ * for matched routes.
13
14
  * The `dynamic` phase runs after route load, is skipped for redirects, and
14
15
  * can contain route `head().links` or empty `hints` and `links` arrays.
15
16
  * `hints` and `links` contain only values not emitted in earlier phases.