@tanstack/start-server-core 1.167.2 → 1.167.4
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 +85 -5
- package/dist/esm/createStartHandler.js +16 -10
- package/dist/esm/createStartHandler.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/router-manifest.js.map +1 -1
- package/dist/esm/transformAssetUrls.d.ts +67 -20
- package/dist/esm/transformAssetUrls.js +98 -38
- package/dist/esm/transformAssetUrls.js.map +1 -1
- package/package.json +8 -7
- package/src/createStartHandler.ts +128 -21
- package/src/index.tsx +8 -0
- package/src/router-manifest.ts +7 -3
- package/src/transformAssetUrls.ts +277 -72
|
@@ -1,15 +1,30 @@
|
|
|
1
|
-
import { rootRouteId } from "@tanstack/router-core";
|
|
1
|
+
import { resolveManifestAssetLink, rootRouteId } from "@tanstack/router-core";
|
|
2
2
|
//#region src/transformAssetUrls.ts
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
var hasWarnedAboutDeprecatedTransformAssetUrls = false;
|
|
4
|
+
function warnDeprecatedTransformAssetUrls() {
|
|
5
|
+
if ((process.env.NODE_ENV === "development" || process.env.TSS_DEV_SERVER === "true") && !hasWarnedAboutDeprecatedTransformAssetUrls) {
|
|
6
|
+
hasWarnedAboutDeprecatedTransformAssetUrls = true;
|
|
7
|
+
console.warn("[TanStack Start] `transformAssetUrls` is deprecated. Use `transformAssets` instead.");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function normalizeTransformAssetResult(result) {
|
|
11
|
+
if (typeof result === "string") return { href: result };
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
function resolveTransformAssetsCrossOrigin(config, kind) {
|
|
15
|
+
if (!config) return void 0;
|
|
16
|
+
if (typeof config === "string") return config;
|
|
17
|
+
return config[kind];
|
|
18
|
+
}
|
|
19
|
+
function isObjectShorthand(transform) {
|
|
20
|
+
return "prefix" in transform;
|
|
21
|
+
}
|
|
22
|
+
function resolveTransformAssetsConfig(transform) {
|
|
8
23
|
if (typeof transform === "string") {
|
|
9
24
|
const prefix = transform;
|
|
10
25
|
return {
|
|
11
26
|
type: "transform",
|
|
12
|
-
transformFn: ({ url }) => `${prefix}${url}
|
|
27
|
+
transformFn: ({ url }) => ({ href: `${prefix}${url}` }),
|
|
13
28
|
cache: true
|
|
14
29
|
};
|
|
15
30
|
}
|
|
@@ -18,6 +33,22 @@ function resolveTransformConfig(transform) {
|
|
|
18
33
|
transformFn: transform,
|
|
19
34
|
cache: true
|
|
20
35
|
};
|
|
36
|
+
if (isObjectShorthand(transform)) {
|
|
37
|
+
const { prefix, crossOrigin } = transform;
|
|
38
|
+
return {
|
|
39
|
+
type: "transform",
|
|
40
|
+
transformFn: ({ url, kind }) => {
|
|
41
|
+
const href = `${prefix}${url}`;
|
|
42
|
+
if (kind === "clientEntry") return { href };
|
|
43
|
+
const co = resolveTransformAssetsCrossOrigin(crossOrigin, kind);
|
|
44
|
+
return co ? {
|
|
45
|
+
href,
|
|
46
|
+
crossOrigin: co
|
|
47
|
+
} : { href };
|
|
48
|
+
},
|
|
49
|
+
cache: true
|
|
50
|
+
};
|
|
51
|
+
}
|
|
21
52
|
if ("createTransform" in transform && transform.createTransform) return {
|
|
22
53
|
type: "createTransform",
|
|
23
54
|
createTransform: transform.createTransform,
|
|
@@ -25,10 +56,31 @@ function resolveTransformConfig(transform) {
|
|
|
25
56
|
};
|
|
26
57
|
return {
|
|
27
58
|
type: "transform",
|
|
28
|
-
transformFn: typeof transform.transform === "string" ? (({ url }) => `${transform.transform}${url}`) : transform.transform,
|
|
59
|
+
transformFn: typeof transform.transform === "string" ? (({ url }) => ({ href: `${transform.transform}${url}` })) : transform.transform,
|
|
29
60
|
cache: transform.cache !== false
|
|
30
61
|
};
|
|
31
62
|
}
|
|
63
|
+
function adaptTransformAssetUrlsToTransformAssets(transformFn) {
|
|
64
|
+
return async ({ url, kind }) => ({ href: await transformFn({
|
|
65
|
+
url,
|
|
66
|
+
type: kind
|
|
67
|
+
}) });
|
|
68
|
+
}
|
|
69
|
+
function adaptTransformAssetUrlsConfigToTransformAssets(transform) {
|
|
70
|
+
warnDeprecatedTransformAssetUrls();
|
|
71
|
+
if (typeof transform === "string") return transform;
|
|
72
|
+
if (typeof transform === "function") return adaptTransformAssetUrlsToTransformAssets(transform);
|
|
73
|
+
if ("createTransform" in transform && transform.createTransform) return {
|
|
74
|
+
createTransform: async (ctx) => adaptTransformAssetUrlsToTransformAssets(await transform.createTransform(ctx)),
|
|
75
|
+
cache: transform.cache,
|
|
76
|
+
warmup: transform.warmup
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
transform: typeof transform.transform === "string" ? transform.transform : adaptTransformAssetUrlsToTransformAssets(transform.transform),
|
|
80
|
+
cache: transform.cache,
|
|
81
|
+
warmup: transform.warmup
|
|
82
|
+
};
|
|
83
|
+
}
|
|
32
84
|
/**
|
|
33
85
|
* Builds the client entry `<script>` tag from a (possibly transformed) client
|
|
34
86
|
* entry URL and optional injected head scripts.
|
|
@@ -45,39 +97,47 @@ function buildClientEntryScriptTag(clientEntry, injectedHeadScripts) {
|
|
|
45
97
|
children: script
|
|
46
98
|
};
|
|
47
99
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
100
|
+
function assignManifestAssetLink(link, next) {
|
|
101
|
+
if (typeof link === "string") return next.crossOrigin ? next : next.href;
|
|
102
|
+
return next.crossOrigin ? next : { href: next.href };
|
|
103
|
+
}
|
|
104
|
+
async function transformManifestAssets(source, transformFn, _opts) {
|
|
105
|
+
const manifest = structuredClone(source.manifest);
|
|
106
|
+
for (const route of Object.values(manifest.routes)) {
|
|
107
|
+
if (route.preloads) route.preloads = await Promise.all(route.preloads.map(async (link) => {
|
|
108
|
+
const result = normalizeTransformAssetResult(await transformFn({
|
|
109
|
+
url: resolveManifestAssetLink(link).href,
|
|
110
|
+
kind: "modulepreload"
|
|
111
|
+
}));
|
|
112
|
+
return assignManifestAssetLink(link, {
|
|
113
|
+
href: result.href,
|
|
114
|
+
crossOrigin: result.crossOrigin
|
|
115
|
+
});
|
|
116
|
+
}));
|
|
117
|
+
if (route.assets) {
|
|
118
|
+
for (const asset of route.assets) if (asset.tag === "link" && asset.attrs?.href) {
|
|
119
|
+
const rel = asset.attrs.rel;
|
|
120
|
+
if (!(typeof rel === "string" ? rel.split(/\s+/) : []).includes("stylesheet")) continue;
|
|
121
|
+
const result = normalizeTransformAssetResult(await transformFn({
|
|
65
122
|
url: asset.attrs.href,
|
|
66
|
-
|
|
123
|
+
kind: "stylesheet"
|
|
67
124
|
}));
|
|
125
|
+
asset.attrs.href = result.href;
|
|
126
|
+
if (result.crossOrigin) asset.attrs.crossOrigin = result.crossOrigin;
|
|
127
|
+
else delete asset.attrs.crossOrigin;
|
|
68
128
|
}
|
|
69
129
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
130
|
+
}
|
|
131
|
+
const transformedClientEntry = normalizeTransformAssetResult(await transformFn({
|
|
132
|
+
url: source.clientEntry,
|
|
133
|
+
kind: "clientEntry"
|
|
134
|
+
}));
|
|
135
|
+
const rootRoute = manifest.routes[rootRouteId];
|
|
136
|
+
if (rootRoute) {
|
|
137
|
+
rootRoute.assets = rootRoute.assets || [];
|
|
138
|
+
rootRoute.assets.push(buildClientEntryScriptTag(transformedClientEntry.href, source.injectedHeadScripts));
|
|
139
|
+
}
|
|
140
|
+
return manifest;
|
|
81
141
|
}
|
|
82
142
|
/**
|
|
83
143
|
* Builds a final Manifest from a StartManifestWithClientEntry without any
|
|
@@ -97,6 +157,6 @@ function buildManifestWithClientEntry(source) {
|
|
|
97
157
|
} };
|
|
98
158
|
}
|
|
99
159
|
//#endregion
|
|
100
|
-
export { buildManifestWithClientEntry,
|
|
160
|
+
export { adaptTransformAssetUrlsConfigToTransformAssets, buildManifestWithClientEntry, resolveTransformAssetsConfig, transformManifestAssets };
|
|
101
161
|
|
|
102
162
|
//# sourceMappingURL=transformAssetUrls.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transformAssetUrls.js","names":[],"sources":["../../src/transformAssetUrls.ts"],"sourcesContent":["import { rootRouteId } from '@tanstack/router-core'\n\nimport type {\n Awaitable,\n Manifest,\n RouterManagedTag,\n} from '@tanstack/router-core'\n\nexport type AssetUrlType = 'modulepreload' | 'stylesheet' | 'clientEntry'\n\nexport interface TransformAssetUrlsContext {\n url: string\n type: AssetUrlType\n}\n\nexport type TransformAssetUrlsFn = (\n context: TransformAssetUrlsContext,\n) => Awaitable<string>\n\nexport type CreateTransformAssetUrlsContext =\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\n/**\n * Async factory that runs once per manifest computation and returns the\n * per-asset transform.\n */\nexport type CreateTransformAssetUrlsFn = (\n ctx: CreateTransformAssetUrlsContext,\n) => Awaitable<TransformAssetUrlsFn>\n\ntype TransformAssetUrlsOptionsBase = {\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 TransformAssetUrlsOptions =\n | (TransformAssetUrlsOptionsBase & {\n /**\n * The transform to apply to asset URLs. Can be a string prefix or a callback.\n *\n * **String** — prepended to every asset URL.\n * **Callback** — receives `{ url, type }` and returns a new URL.\n */\n transform: string | TransformAssetUrlsFn\n createTransform?: never\n })\n | (TransformAssetUrlsOptionsBase & {\n /**\n * Create a per-asset transform function.\n *\n * This factory runs once per manifest computation (per request when\n * `cache: false`, or once per server when `cache: true`). It can do async\n * setup work (fetch config, read from a KV, etc.) and return a fast\n * per-asset transformer.\n */\n createTransform: CreateTransformAssetUrlsFn\n transform?: never\n })\n\nexport type TransformAssetUrls =\n | string\n | TransformAssetUrlsFn\n | TransformAssetUrlsOptions\n\nexport type ResolvedTransformAssetUrlsConfig =\n | {\n type: 'transform'\n transformFn: TransformAssetUrlsFn\n cache: boolean\n }\n | {\n type: 'createTransform'\n createTransform: CreateTransformAssetUrlsFn\n cache: boolean\n }\n\n/**\n * Resolves a TransformAssetUrls value (string prefix, callback, or options\n * object) into a concrete transform function and cache flag.\n */\nexport function resolveTransformConfig(\n transform: TransformAssetUrls,\n): ResolvedTransformAssetUrlsConfig {\n // String shorthand\n if (typeof transform === 'string') {\n const prefix = transform\n return {\n type: 'transform',\n transformFn: ({ url }) => `${prefix}${url}`,\n cache: true,\n }\n }\n\n // Callback shorthand\n if (typeof transform === 'function') {\n return {\n type: 'transform',\n transformFn: transform,\n cache: true,\n }\n }\n\n // Options object\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 }: TransformAssetUrlsContext) =>\n `${transform.transform}${url}`) as TransformAssetUrlsFn)\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\n/**\n * Applies a URL transform to every asset URL in the manifest and returns a\n * new manifest with a client entry script tag appended to the root route's\n * assets.\n *\n * The source manifest is deep-cloned so the cached original is never mutated.\n */\nexport function transformManifestUrls(\n source: StartManifestWithClientEntry,\n transformFn: TransformAssetUrlsFn,\n opts?: {\n /** When true, clone the source manifest before mutating it. */\n clone?: boolean\n },\n): Promise<Manifest> {\n return (async () => {\n const manifest = opts?.clone\n ? structuredClone(source.manifest)\n : source.manifest\n\n for (const route of Object.values(manifest.routes)) {\n // Transform preload URLs (modulepreload)\n if (route.preloads) {\n route.preloads = await Promise.all(\n route.preloads.map((url) =>\n Promise.resolve(transformFn({ url, type: 'modulepreload' })),\n ),\n )\n }\n\n // Transform asset tag URLs\n if (route.assets) {\n for (const asset of route.assets) {\n if (asset.tag === 'link' && asset.attrs?.href) {\n asset.attrs.href = await Promise.resolve(\n transformFn({\n url: asset.attrs.href,\n type: 'stylesheet',\n }),\n )\n }\n }\n }\n }\n\n // Transform and append the client entry script tag\n const transformedClientEntry = await Promise.resolve(\n transformFn({\n url: source.clientEntry,\n type: 'clientEntry',\n }),\n )\n\n const rootRoute = manifest.routes[rootRouteId]\n if (rootRoute) {\n rootRoute.assets = rootRoute.assets || []\n rootRoute.assets.push(\n buildClientEntryScriptTag(\n transformedClientEntry,\n source.injectedHeadScripts,\n ),\n )\n }\n\n return manifest\n })()\n}\n\n/**\n * Builds a final Manifest from a StartManifestWithClientEntry without any\n * URL transforms. Used when no transformAssetUrls 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): 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 ...(baseRootRoute\n ? {\n [rootRouteId]: {\n ...baseRootRoute,\n assets: [...(baseRootRoute.assets || []), scriptTag],\n },\n }\n : {}),\n }\n\n return { routes }\n}\n"],"mappings":";;;;;;AAkHA,SAAgB,uBACd,WACkC;AAElC,KAAI,OAAO,cAAc,UAAU;EACjC,MAAM,SAAS;AACf,SAAO;GACL,MAAM;GACN,cAAc,EAAE,UAAU,GAAG,SAAS;GACtC,OAAO;GACR;;AAIH,KAAI,OAAO,cAAc,WACvB,QAAO;EACL,MAAM;EACN,aAAa;EACb,OAAO;EACR;AAIH,KAAI,qBAAqB,aAAa,UAAU,gBAC9C,QAAO;EACL,MAAM;EACN,iBAAiB,UAAU;EAC3B,OAAO,UAAU,UAAU;EAC5B;AASH,QAAO;EACL,MAAM;EACN,aAPA,OAAO,UAAU,cAAc,aACxB,EAAE,UACH,GAAG,UAAU,YAAY,SAC3B,UAAU;EAKd,OAAO,UAAU,UAAU;EAC5B;;;;;;AAcH,SAAgB,0BACd,aACA,qBACkB;CAElB,IAAI,SAAS,UADc,KAAK,UAAU,YAAY,CACZ;AAC1C,KAAI,oBACF,UAAS,GAAG,oBAAoB,GAAG;AAErC,QAAO;EACL,KAAK;EACL,OAAO;GACL,MAAM;GACN,OAAO;GACR;EACD,UAAU;EACX;;;;;;;;;AAUH,SAAgB,sBACd,QACA,aACA,MAImB;AACnB,SAAQ,YAAY;EAClB,MAAM,WAAW,MAAM,QACnB,gBAAgB,OAAO,SAAS,GAChC,OAAO;AAEX,OAAK,MAAM,SAAS,OAAO,OAAO,SAAS,OAAO,EAAE;AAElD,OAAI,MAAM,SACR,OAAM,WAAW,MAAM,QAAQ,IAC7B,MAAM,SAAS,KAAK,QAClB,QAAQ,QAAQ,YAAY;IAAE;IAAK,MAAM;IAAiB,CAAC,CAAC,CAC7D,CACF;AAIH,OAAI,MAAM;SACH,MAAM,SAAS,MAAM,OACxB,KAAI,MAAM,QAAQ,UAAU,MAAM,OAAO,KACvC,OAAM,MAAM,OAAO,MAAM,QAAQ,QAC/B,YAAY;KACV,KAAK,MAAM,MAAM;KACjB,MAAM;KACP,CAAC,CACH;;;EAOT,MAAM,yBAAyB,MAAM,QAAQ,QAC3C,YAAY;GACV,KAAK,OAAO;GACZ,MAAM;GACP,CAAC,CACH;EAED,MAAM,YAAY,SAAS,OAAO;AAClC,MAAI,WAAW;AACb,aAAU,SAAS,UAAU,UAAU,EAAE;AACzC,aAAU,OAAO,KACf,0BACE,wBACA,OAAO,oBACR,CACF;;AAGH,SAAO;KACL;;;;;;;;AASN,SAAgB,6BACd,QACU;CACV,MAAM,YAAY,0BAChB,OAAO,aACP,OAAO,oBACR;CAED,MAAM,gBAAgB,OAAO,SAAS,OAAO;AAa7C,QAAO,EAAE,QAZM;EACb,GAAG,OAAO,SAAS;EACnB,GAAI,gBACA,GACG,cAAc;GACb,GAAG;GACH,QAAQ,CAAC,GAAI,cAAc,UAAU,EAAE,EAAG,UAAU;GACrD,EACF,GACD,EAAE;EACP,EAEgB"}
|
|
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 TransformAssetKind = 'modulepreload' | 'stylesheet' | 'clientEntry'\n\ntype TransformAssetsShorthandCrossOriginKind = Exclude<\n TransformAssetKind,\n 'clientEntry'\n>\n\nexport type AssetUrlType = TransformAssetKind\n\nexport interface TransformAssetsContext {\n url: string\n kind: TransformAssetKind\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 interface TransformAssetUrlsContext {\n url: string\n type: AssetUrlType\n}\n\nexport type TransformAssetUrlsFn = (\n context: TransformAssetUrlsContext,\n) => Awaitable<string>\n\nexport type CreateTransformAssetUrlsContext =\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\n/**\n * Async factory that runs once per manifest computation and returns the\n * per-asset transform.\n */\nexport type CreateTransformAssetUrlsFn = (\n ctx: CreateTransformAssetUrlsContext,\n) => Awaitable<TransformAssetUrlsFn>\n\nexport type CreateTransformAssetsFn = (\n ctx: CreateTransformAssetUrlsContext,\n) => Awaitable<TransformAssetsFn>\n\ntype TransformAssetUrlsOptionsBase = {\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 TransformAssetUrlsOptions =\n | (TransformAssetUrlsOptionsBase & {\n /**\n * The transform to apply to asset URLs. Can be a string prefix or a callback.\n *\n * **String** — prepended to every asset URL.\n * **Callback** — receives `{ url, type }` and returns a new URL.\n */\n transform: string | TransformAssetUrlsFn\n createTransform?: never\n })\n | (TransformAssetUrlsOptionsBase & {\n /**\n * Create a per-asset transform function.\n *\n * This factory runs once per manifest computation (per request when\n * `cache: false`, or once per server when `cache: true`). It can do async\n * setup work (fetch config, read from a KV, etc.) and return a fast\n * per-asset transformer.\n */\n createTransform: CreateTransformAssetUrlsFn\n transform?: never\n })\n\nexport type TransformAssetsOptions =\n | (TransformAssetUrlsOptionsBase & {\n transform: string | TransformAssetsFn\n createTransform?: never\n })\n | (TransformAssetUrlsOptionsBase & {\n createTransform: CreateTransformAssetsFn\n transform?: never\n })\n\nexport type TransformAssetUrls =\n | string\n | TransformAssetUrlsFn\n | TransformAssetUrlsOptions\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\nlet hasWarnedAboutDeprecatedTransformAssetUrls = false\n\nexport function warnDeprecatedTransformAssetUrls() {\n if (\n (process.env.NODE_ENV === 'development' ||\n process.env.TSS_DEV_SERVER === 'true') &&\n !hasWarnedAboutDeprecatedTransformAssetUrls\n ) {\n hasWarnedAboutDeprecatedTransformAssetUrls = true\n console.warn(\n '[TanStack Start] `transformAssetUrls` is deprecated. Use `transformAssets` instead.',\n )\n }\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 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') {\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 function adaptTransformAssetUrlsToTransformAssets(\n transformFn: TransformAssetUrlsFn,\n): TransformAssetsFn {\n return async ({ url, kind }) => ({\n href: await transformFn({ url, type: kind }),\n })\n}\n\nexport function adaptTransformAssetUrlsConfigToTransformAssets(\n transform: TransformAssetUrls,\n): TransformAssets {\n warnDeprecatedTransformAssetUrls()\n\n if (typeof transform === 'string') {\n return transform\n }\n\n if (typeof transform === 'function') {\n return adaptTransformAssetUrlsToTransformAssets(transform)\n }\n\n if ('createTransform' in transform && transform.createTransform) {\n return {\n createTransform: async (ctx: CreateTransformAssetUrlsContext) =>\n adaptTransformAssetUrlsToTransformAssets(\n await transform.createTransform(ctx),\n ),\n cache: transform.cache,\n warmup: transform.warmup,\n }\n }\n\n return {\n transform:\n typeof transform.transform === 'string'\n ? transform.transform\n : adaptTransformAssetUrlsToTransformAssets(transform.transform),\n cache: transform.cache,\n warmup: transform.warmup,\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 },\n): Promise<Manifest> {\n const manifest = structuredClone(source.manifest)\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) {\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 if (rootRoute) {\n rootRoute.assets = rootRoute.assets || []\n rootRoute.assets.push(\n buildClientEntryScriptTag(\n transformedClientEntry.href,\n source.injectedHeadScripts,\n ),\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 transformAssetUrls 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): 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 ...(baseRootRoute\n ? {\n [rootRouteId]: {\n ...baseRootRoute,\n assets: [...(baseRootRoute.assets || []), scriptTag],\n },\n }\n : {}),\n }\n\n return { routes }\n}\n"],"mappings":";;AAqMA,IAAI,6CAA6C;AAEjD,SAAgB,mCAAmC;AACjD,MAAA,QAAA,IAAA,aAC4B,iBACxB,QAAQ,IAAI,mBAAmB,WACjC,CAAC,4CACD;AACA,+CAA6C;AAC7C,UAAQ,KACN,sFACD;;;AAIL,SAAS,8BACP,QACuC;AACvC,KAAI,OAAO,WAAW,SACpB,QAAO,EAAE,MAAM,QAAQ;AAGzB,QAAO;;AAGT,SAAS,kCACP,QACA,MAC8B;AAC9B,KAAI,CAAC,OAAQ,QAAO,KAAA;AACpB,KAAI,OAAO,WAAW,SAAU,QAAO;AAEvC,QAAO,OAAO;;AAGhB,SAAS,kBACP,WAC6C;AAC7C,QAAO,YAAY;;AAGrB,SAAgB,6BACd,WAC+B;AAC/B,KAAI,OAAO,cAAc,UAAU;EACjC,MAAM,SAAS;AACf,SAAO;GACL,MAAM;GACN,cAAc,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,OAAO;GACtD,OAAO;GACR;;AAGH,KAAI,OAAO,cAAc,WACvB,QAAO;EACL,MAAM;EACN,aAAa;EACb,OAAO;EACR;AAIH,KAAI,kBAAkB,UAAU,EAAE;EAChC,MAAM,EAAE,QAAQ,gBAAgB;AAEhC,SAAO;GACL,MAAM;GACN,cAAc,EAAE,KAAK,WAAW;IAC9B,MAAM,OAAO,GAAG,SAAS;AAEzB,QAAI,SAAS,cACX,QAAO,EAAE,MAAM;IAGjB,MAAM,KAAK,kCAAkC,aAAa,KAAK;AAC/D,WAAO,KAAK;KAAE;KAAM,aAAa;KAAI,GAAG,EAAE,MAAM;;GAElD,OAAO;GACR;;AAGH,KAAI,qBAAqB,aAAa,UAAU,gBAC9C,QAAO;EACL,MAAM;EACN,iBAAiB,UAAU;EAC3B,OAAO,UAAU,UAAU;EAC5B;AAUH,QAAO;EACL,MAAM;EACN,aARA,OAAO,UAAU,cAAc,aACxB,EAAE,WAAmC,EACtC,MAAM,GAAG,UAAU,YAAY,OAChC,KACD,UAAU;EAKd,OAAO,UAAU,UAAU;EAC5B;;AAGH,SAAgB,yCACd,aACmB;AACnB,QAAO,OAAO,EAAE,KAAK,YAAY,EAC/B,MAAM,MAAM,YAAY;EAAE;EAAK,MAAM;EAAM,CAAC,EAC7C;;AAGH,SAAgB,+CACd,WACiB;AACjB,mCAAkC;AAElC,KAAI,OAAO,cAAc,SACvB,QAAO;AAGT,KAAI,OAAO,cAAc,WACvB,QAAO,yCAAyC,UAAU;AAG5D,KAAI,qBAAqB,aAAa,UAAU,gBAC9C,QAAO;EACL,iBAAiB,OAAO,QACtB,yCACE,MAAM,UAAU,gBAAgB,IAAI,CACrC;EACH,OAAO,UAAU;EACjB,QAAQ,UAAU;EACnB;AAGH,QAAO;EACL,WACE,OAAO,UAAU,cAAc,WAC3B,UAAU,YACV,yCAAyC,UAAU,UAAU;EACnE,OAAO,UAAU;EACjB,QAAQ,UAAU;EACnB;;;;;;AAcH,SAAgB,0BACd,aACA,qBACkB;CAElB,IAAI,SAAS,UADc,KAAK,UAAU,YAAY,CACZ;AAC1C,KAAI,oBACF,UAAS,GAAG,oBAAoB,GAAG;AAErC,QAAO;EACL,KAAK;EACL,OAAO;GACL,MAAM;GACN,OAAO;GACR;EACD,UAAU;EACX;;AAGH,SAAS,wBACP,MACA,MACmB;AACnB,KAAI,OAAO,SAAS,SAClB,QAAO,KAAK,cAAc,OAAO,KAAK;AAGxC,QAAO,KAAK,cAAc,OAAO,EAAE,MAAM,KAAK,MAAM;;AAGtD,eAAsB,wBACpB,QACA,aACA,OAGmB;CACnB,MAAM,WAAW,gBAAgB,OAAO,SAAS;AAEjD,MAAK,MAAM,SAAS,OAAO,OAAO,SAAS,OAAO,EAAE;AAClD,MAAI,MAAM,SACR,OAAM,WAAW,MAAM,QAAQ,IAC7B,MAAM,SAAS,IAAI,OAAO,SAAS;GAEjC,MAAM,SAAS,8BACb,MAAM,YAAY;IAChB,KAHa,yBAAyB,KAAK,CAG7B;IACd,MAAM;IACP,CAAC,CACH;AAED,UAAO,wBAAwB,MAAM;IACnC,MAAM,OAAO;IACb,aAAa,OAAO;IACrB,CAAC;IACF,CACH;AAGH,MAAI,MAAM;QACH,MAAM,SAAS,MAAM,OACxB,KAAI,MAAM,QAAQ,UAAU,MAAM,OAAO,MAAM;IAC7C,MAAM,MAAM,MAAM,MAAM;AAGxB,QAAI,EAFc,OAAO,QAAQ,WAAW,IAAI,MAAM,MAAM,GAAG,EAAE,EAElD,SAAS,aAAa,CACnC;IAGF,MAAM,SAAS,8BACb,MAAM,YAAY;KAChB,KAAK,MAAM,MAAM;KACjB,MAAM;KACP,CAAC,CACH;AAED,UAAM,MAAM,OAAO,OAAO;AAC1B,QAAI,OAAO,YACT,OAAM,MAAM,cAAc,OAAO;QAEjC,QAAO,MAAM,MAAM;;;;CAO7B,MAAM,yBAAyB,8BAC7B,MAAM,YAAY;EAChB,KAAK,OAAO;EACZ,MAAM;EACP,CAAC,CACH;CAED,MAAM,YAAY,SAAS,OAAO;AAClC,KAAI,WAAW;AACb,YAAU,SAAS,UAAU,UAAU,EAAE;AACzC,YAAU,OAAO,KACf,0BACE,uBAAuB,MACvB,OAAO,oBACR,CACF;;AAGH,QAAO;;;;;;;;AAST,SAAgB,6BACd,QACU;CACV,MAAM,YAAY,0BAChB,OAAO,aACP,OAAO,oBACR;CAED,MAAM,gBAAgB,OAAO,SAAS,OAAO;AAa7C,QAAO,EAAE,QAZM;EACb,GAAG,OAAO,SAAS;EACnB,GAAI,gBACA,GACG,cAAc;GACb,GAAG;GACH,QAAQ,CAAC,GAAI,cAAc,UAAU,EAAE,EAAG,UAAU;GACrD,EACF,GACD,EAAE;EACP,EAEgB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/start-server-core",
|
|
3
|
-
"version": "1.167.
|
|
3
|
+
"version": "1.167.4",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -66,16 +66,17 @@
|
|
|
66
66
|
"h3-v2": "npm:h3@2.0.1-rc.16",
|
|
67
67
|
"seroval": "^1.4.2",
|
|
68
68
|
"@tanstack/history": "1.161.6",
|
|
69
|
-
"@tanstack/router-core": "1.168.
|
|
70
|
-
"@tanstack/start-client-core": "1.167.
|
|
71
|
-
"@tanstack/start-storage-context": "1.166.
|
|
69
|
+
"@tanstack/router-core": "1.168.4",
|
|
70
|
+
"@tanstack/start-client-core": "1.167.4",
|
|
71
|
+
"@tanstack/start-storage-context": "1.166.18"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@standard-schema/spec": "^1.0.0",
|
|
75
75
|
"@tanstack/intent": "^0.0.14",
|
|
76
76
|
"cookie-es": "^2.0.0",
|
|
77
77
|
"fetchdts": "^0.1.6",
|
|
78
|
-
"vite": "*"
|
|
78
|
+
"vite": "*",
|
|
79
|
+
"@types/node": ">=20"
|
|
79
80
|
},
|
|
80
81
|
"bin": {
|
|
81
82
|
"intent": "./bin/intent.js"
|
|
@@ -86,12 +87,12 @@
|
|
|
86
87
|
"test:unit": "vitest",
|
|
87
88
|
"test:eslint": "eslint ./src",
|
|
88
89
|
"test:types": "pnpm run \"/^test:types:ts[0-9]{2}$/\"",
|
|
89
|
-
"test:types:ts54": "node ../../node_modules/typescript54/lib/tsc.js",
|
|
90
90
|
"test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js",
|
|
91
91
|
"test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js",
|
|
92
92
|
"test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js",
|
|
93
93
|
"test:types:ts58": "node ../../node_modules/typescript58/lib/tsc.js",
|
|
94
|
-
"test:types:ts59": "tsc",
|
|
94
|
+
"test:types:ts59": "node ../../node_modules/typescript59/lib/tsc.js",
|
|
95
|
+
"test:types:ts60": "tsc",
|
|
95
96
|
"test:build": "publint --strict && attw --ignore-rules no-resolution --pack .",
|
|
96
97
|
"build": "vite build"
|
|
97
98
|
}
|
|
@@ -20,9 +20,10 @@ import { requestHandler } from './request-response'
|
|
|
20
20
|
import { getStartManifest } from './router-manifest'
|
|
21
21
|
import { handleServerAction } from './server-functions-handler'
|
|
22
22
|
import {
|
|
23
|
+
adaptTransformAssetUrlsConfigToTransformAssets,
|
|
23
24
|
buildManifestWithClientEntry,
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
resolveTransformAssetsConfig,
|
|
26
|
+
transformManifestAssets,
|
|
26
27
|
} from './transformAssetUrls'
|
|
27
28
|
|
|
28
29
|
import { HEADERS } from './constants'
|
|
@@ -47,7 +48,8 @@ import type { HandlerCallback } from '@tanstack/router-core/ssr/server'
|
|
|
47
48
|
import type {
|
|
48
49
|
StartManifestWithClientEntry,
|
|
49
50
|
TransformAssetUrls,
|
|
50
|
-
|
|
51
|
+
TransformAssets,
|
|
52
|
+
TransformAssetsFn,
|
|
51
53
|
} from './transformAssetUrls'
|
|
52
54
|
|
|
53
55
|
type TODO = any
|
|
@@ -59,7 +61,87 @@ type AnyMiddlewareServerFn =
|
|
|
59
61
|
export interface CreateStartHandlerOptions {
|
|
60
62
|
handler: HandlerCallback<AnyRouter>
|
|
61
63
|
/**
|
|
62
|
-
* Transform asset URLs at runtime, e.g. to prepend a CDN prefix.
|
|
64
|
+
* Transform asset URLs and attributes at runtime, e.g. to prepend a CDN prefix.
|
|
65
|
+
*
|
|
66
|
+
* **String** — a URL prefix prepended to every asset URL (cached by default):
|
|
67
|
+
* ```ts
|
|
68
|
+
* createStartHandler({
|
|
69
|
+
* handler: defaultStreamHandler,
|
|
70
|
+
* transformAssets: 'https://cdn.example.com',
|
|
71
|
+
* })
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* **Object shorthand** — a URL prefix with optional `crossOrigin`:
|
|
75
|
+
* ```ts
|
|
76
|
+
* createStartHandler({
|
|
77
|
+
* handler: defaultStreamHandler,
|
|
78
|
+
* transformAssets: {
|
|
79
|
+
* prefix: 'https://cdn.example.com',
|
|
80
|
+
* crossOrigin: 'anonymous',
|
|
81
|
+
* },
|
|
82
|
+
* })
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* `crossOrigin` accepts a single value or a per-kind record:
|
|
86
|
+
* ```ts
|
|
87
|
+
* transformAssets: {
|
|
88
|
+
* prefix: 'https://cdn.example.com',
|
|
89
|
+
* crossOrigin: {
|
|
90
|
+
* modulepreload: 'anonymous',
|
|
91
|
+
* stylesheet: 'use-credentials',
|
|
92
|
+
* },
|
|
93
|
+
* }
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* **Callback** — receives `{ kind, url }` and returns either a string URL or
|
|
97
|
+
* `{ href, crossOrigin? }` (cached by default — runs once on first request):
|
|
98
|
+
* ```ts
|
|
99
|
+
* createStartHandler({
|
|
100
|
+
* handler: defaultStreamHandler,
|
|
101
|
+
* transformAssets: ({ kind, url }) => {
|
|
102
|
+
* const href = `https://cdn.example.com${url}`
|
|
103
|
+
*
|
|
104
|
+
* if (kind === 'modulepreload') {
|
|
105
|
+
* return { href, crossOrigin: 'anonymous' }
|
|
106
|
+
* }
|
|
107
|
+
*
|
|
108
|
+
* return { href }
|
|
109
|
+
* },
|
|
110
|
+
* })
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* **Object** — for explicit cache control:
|
|
114
|
+
* ```ts
|
|
115
|
+
* createStartHandler({
|
|
116
|
+
* handler: defaultStreamHandler,
|
|
117
|
+
* transformAssets: {
|
|
118
|
+
* transform: ({ url }) => {
|
|
119
|
+
* const region = getRequest().headers.get('x-region') || 'us'
|
|
120
|
+
* return { href: `https://cdn-${region}.example.com${url}` }
|
|
121
|
+
* },
|
|
122
|
+
* cache: false,
|
|
123
|
+
* },
|
|
124
|
+
* })
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* `kind` is one of `'modulepreload' | 'stylesheet' | 'clientEntry'`.
|
|
128
|
+
* `crossOrigin` applies to manifest-managed `<link>` assets.
|
|
129
|
+
*
|
|
130
|
+
* By default, the transformed manifest is cached after the first request
|
|
131
|
+
* (`cache: true`). Set `cache: false` for per-request transforms.
|
|
132
|
+
*
|
|
133
|
+
* If you're using a cached transform, you can optionally set `warmup: true`
|
|
134
|
+
* (object form only) to compute the transformed manifest in the background at
|
|
135
|
+
* server startup.
|
|
136
|
+
*
|
|
137
|
+
* Note: This only transforms URLs managed by TanStack Start's manifest
|
|
138
|
+
* (JS preloads, CSS links, and the client entry script). For asset imports
|
|
139
|
+
* used directly in components (e.g. `import logo from './logo.svg'`),
|
|
140
|
+
* configure Vite's `experimental.renderBuiltUrl` in your vite.config.ts.
|
|
141
|
+
*/
|
|
142
|
+
transformAssets?: TransformAssets
|
|
143
|
+
/**
|
|
144
|
+
* @deprecated Use `transformAssets` instead.
|
|
63
145
|
*
|
|
64
146
|
* **String** — a URL prefix prepended to every asset URL (cached by default):
|
|
65
147
|
* ```ts
|
|
@@ -181,14 +263,14 @@ function getBaseManifest(
|
|
|
181
263
|
*/
|
|
182
264
|
async function resolveManifest(
|
|
183
265
|
matchedRoutes: ReadonlyArray<AnyRoute> | undefined,
|
|
184
|
-
transformFn:
|
|
266
|
+
transformFn: TransformAssetsFn | undefined,
|
|
185
267
|
cache: boolean,
|
|
186
268
|
): Promise<Manifest> {
|
|
187
269
|
const base = await getBaseManifest(matchedRoutes)
|
|
188
270
|
|
|
189
271
|
const computeFinalManifest = async () => {
|
|
190
272
|
return transformFn
|
|
191
|
-
? await
|
|
273
|
+
? await transformManifestAssets(base, transformFn, { clone: !cache })
|
|
192
274
|
: buildManifestWithClientEntry(base)
|
|
193
275
|
}
|
|
194
276
|
|
|
@@ -332,7 +414,7 @@ function handlerToMiddleware(
|
|
|
332
414
|
* ```ts
|
|
333
415
|
* export default createStartHandler({
|
|
334
416
|
* handler: defaultStreamHandler,
|
|
335
|
-
*
|
|
417
|
+
* transformAssets: 'https://cdn.example.com',
|
|
336
418
|
* })
|
|
337
419
|
* ```
|
|
338
420
|
*
|
|
@@ -340,10 +422,10 @@ function handlerToMiddleware(
|
|
|
340
422
|
* ```ts
|
|
341
423
|
* export default createStartHandler({
|
|
342
424
|
* handler: defaultStreamHandler,
|
|
343
|
-
*
|
|
425
|
+
* transformAssets: {
|
|
344
426
|
* transform: ({ url }) => {
|
|
345
427
|
* const cdnBase = getRequest().headers.get('x-cdn-base') || ''
|
|
346
|
-
* return `${cdnBase}${url}`
|
|
428
|
+
* return { href: `${cdnBase}${url}` }
|
|
347
429
|
* },
|
|
348
430
|
* cache: false,
|
|
349
431
|
* },
|
|
@@ -356,40 +438,65 @@ export function createStartHandler<TRegister = Register>(
|
|
|
356
438
|
// Normalize the overloaded argument
|
|
357
439
|
const cb: HandlerCallback<AnyRouter> =
|
|
358
440
|
typeof cbOrOptions === 'function' ? cbOrOptions : cbOrOptions.handler
|
|
441
|
+
const transformAssetsOption: TransformAssets | undefined =
|
|
442
|
+
typeof cbOrOptions === 'function' ? undefined : cbOrOptions.transformAssets
|
|
359
443
|
const transformAssetUrlsOption: TransformAssetUrls | undefined =
|
|
360
444
|
typeof cbOrOptions === 'function'
|
|
361
445
|
? undefined
|
|
362
446
|
: cbOrOptions.transformAssetUrls
|
|
363
447
|
|
|
448
|
+
const transformOption =
|
|
449
|
+
transformAssetsOption !== undefined
|
|
450
|
+
? resolveTransformAssetsConfig(transformAssetsOption)
|
|
451
|
+
: transformAssetUrlsOption !== undefined
|
|
452
|
+
? resolveTransformAssetsConfig(
|
|
453
|
+
adaptTransformAssetUrlsConfigToTransformAssets(
|
|
454
|
+
transformAssetUrlsOption,
|
|
455
|
+
),
|
|
456
|
+
)
|
|
457
|
+
: undefined
|
|
458
|
+
|
|
364
459
|
const warmupTransformManifest =
|
|
365
|
-
!!
|
|
366
|
-
|
|
367
|
-
|
|
460
|
+
(!!transformAssetsOption &&
|
|
461
|
+
typeof transformAssetsOption === 'object' &&
|
|
462
|
+
'warmup' in transformAssetsOption &&
|
|
463
|
+
transformAssetsOption.warmup === true) ||
|
|
464
|
+
(!!transformAssetUrlsOption &&
|
|
465
|
+
typeof transformAssetUrlsOption === 'object' &&
|
|
466
|
+
transformAssetUrlsOption.warmup === true)
|
|
368
467
|
|
|
369
468
|
// Pre-resolve the transform function and cache flag
|
|
370
|
-
const resolvedTransformConfig =
|
|
371
|
-
? resolveTransformConfig(transformAssetUrlsOption)
|
|
372
|
-
: undefined
|
|
469
|
+
const resolvedTransformConfig = transformOption
|
|
373
470
|
const cache = resolvedTransformConfig ? resolvedTransformConfig.cache : true
|
|
471
|
+
const shouldCacheCreateTransform =
|
|
472
|
+
cache && process.env.TSS_DEV_SERVER !== 'true'
|
|
374
473
|
|
|
375
|
-
// Memoize a single createTransform() result when caching is enabled
|
|
376
|
-
|
|
474
|
+
// Memoize a single createTransform() result when caching is enabled outside
|
|
475
|
+
// of the dev server.
|
|
476
|
+
let cachedCreateTransformPromise: Promise<TransformAssetsFn> | undefined
|
|
377
477
|
|
|
378
478
|
const getTransformFn = async (
|
|
379
479
|
opts: { warmup: true } | { warmup: false; request: Request },
|
|
380
|
-
): Promise<
|
|
480
|
+
): Promise<TransformAssetsFn | undefined> => {
|
|
381
481
|
if (!resolvedTransformConfig) return undefined
|
|
482
|
+
|
|
382
483
|
if (resolvedTransformConfig.type === 'createTransform') {
|
|
383
|
-
if (
|
|
484
|
+
if (shouldCacheCreateTransform) {
|
|
384
485
|
if (!cachedCreateTransformPromise) {
|
|
385
486
|
cachedCreateTransformPromise = Promise.resolve(
|
|
386
487
|
resolvedTransformConfig.createTransform(opts),
|
|
387
|
-
)
|
|
488
|
+
).catch((error) => {
|
|
489
|
+
cachedCreateTransformPromise = undefined
|
|
490
|
+
throw error
|
|
491
|
+
})
|
|
388
492
|
}
|
|
493
|
+
|
|
389
494
|
return cachedCreateTransformPromise
|
|
390
495
|
}
|
|
496
|
+
|
|
391
497
|
return resolvedTransformConfig.createTransform(opts)
|
|
392
498
|
}
|
|
499
|
+
|
|
393
500
|
return resolvedTransformConfig.transformFn
|
|
394
501
|
}
|
|
395
502
|
|
|
@@ -408,7 +515,7 @@ export function createStartHandler<TRegister = Register>(
|
|
|
408
515
|
const base = await getBaseManifest(undefined)
|
|
409
516
|
const transformFn = await getTransformFn({ warmup: true })
|
|
410
517
|
return transformFn
|
|
411
|
-
? await
|
|
518
|
+
? await transformManifestAssets(base, transformFn, { clone: false })
|
|
412
519
|
: buildManifestWithClientEntry(base)
|
|
413
520
|
})()
|
|
414
521
|
cachedFinalManifestPromise = warmupPromise
|
package/src/index.tsx
CHANGED
|
@@ -2,11 +2,19 @@ export { createStartHandler } from './createStartHandler'
|
|
|
2
2
|
export type { CreateStartHandlerOptions } from './createStartHandler'
|
|
3
3
|
|
|
4
4
|
export type {
|
|
5
|
+
TransformAssets,
|
|
6
|
+
TransformAssetsFn,
|
|
7
|
+
TransformAssetsContext,
|
|
8
|
+
TransformAssetsOptions,
|
|
9
|
+
TransformAssetsObjectShorthand,
|
|
10
|
+
TransformAssetsCrossOriginConfig,
|
|
11
|
+
TransformAssetResult,
|
|
5
12
|
TransformAssetUrls,
|
|
6
13
|
TransformAssetUrlsFn,
|
|
7
14
|
TransformAssetUrlsContext,
|
|
8
15
|
TransformAssetUrlsOptions,
|
|
9
16
|
AssetUrlType,
|
|
17
|
+
TransformAssetKind,
|
|
10
18
|
} from './transformAssetUrls'
|
|
11
19
|
|
|
12
20
|
export {
|
package/src/router-manifest.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { buildDevStylesUrl, rootRouteId } from '@tanstack/router-core'
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
AnyRoute,
|
|
4
|
+
ManifestAssetLink,
|
|
5
|
+
RouterManagedTag,
|
|
6
|
+
} from '@tanstack/router-core'
|
|
3
7
|
import type { StartManifestWithClientEntry } from './transformAssetUrls'
|
|
4
8
|
|
|
5
9
|
// Pre-computed constant for dev styles URL basepath.
|
|
@@ -61,7 +65,7 @@ export async function getStartManifest(
|
|
|
61
65
|
routes: Object.fromEntries(
|
|
62
66
|
Object.entries(startManifest.routes).flatMap(([k, v]) => {
|
|
63
67
|
const result = {} as {
|
|
64
|
-
preloads?: Array<
|
|
68
|
+
preloads?: Array<ManifestAssetLink>
|
|
65
69
|
assets?: Array<RouterManagedTag>
|
|
66
70
|
}
|
|
67
71
|
let hasData = false
|
|
@@ -82,7 +86,7 @@ export async function getStartManifest(
|
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
return {
|
|
85
|
-
manifest,
|
|
89
|
+
manifest: manifest as StartManifestWithClientEntry['manifest'],
|
|
86
90
|
clientEntry: startManifest.clientEntry,
|
|
87
91
|
injectedHeadScripts,
|
|
88
92
|
}
|