@tanstack/start-plugin-core 1.169.0 → 1.169.2
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/rsbuild/normalized-client-build.js +9 -1
- package/dist/esm/rsbuild/normalized-client-build.js.map +1 -1
- package/dist/esm/rsbuild/plugin.js +4 -1
- package/dist/esm/rsbuild/plugin.js.map +1 -1
- package/dist/esm/rsbuild/schema.d.ts +8 -0
- package/dist/esm/rsbuild/virtual-modules.js +14 -13
- package/dist/esm/rsbuild/virtual-modules.js.map +1 -1
- package/dist/esm/schema.d.ts +25 -0
- package/dist/esm/schema.js +4 -1
- package/dist/esm/schema.js.map +1 -1
- package/dist/esm/start-manifest-plugin/inlineCss.d.ts +6 -0
- package/dist/esm/start-manifest-plugin/inlineCss.js +59 -0
- package/dist/esm/start-manifest-plugin/inlineCss.js.map +1 -0
- package/dist/esm/start-manifest-plugin/manifestBuilder.d.ts +4 -0
- package/dist/esm/start-manifest-plugin/manifestBuilder.js +33 -3
- package/dist/esm/start-manifest-plugin/manifestBuilder.js.map +1 -1
- package/dist/esm/types.d.ts +1 -0
- package/dist/esm/vite/planning.d.ts +3 -0
- package/dist/esm/vite/planning.js +1 -0
- package/dist/esm/vite/planning.js.map +1 -1
- package/dist/esm/vite/plugin.js +1 -0
- package/dist/esm/vite/plugin.js.map +1 -1
- package/dist/esm/vite/schema.d.ts +8 -0
- package/dist/esm/vite/start-manifest-plugin/normalized-client-build.js +11 -1
- package/dist/esm/vite/start-manifest-plugin/normalized-client-build.js.map +1 -1
- package/dist/esm/vite/start-manifest-plugin/plugin.js +2 -1
- package/dist/esm/vite/start-manifest-plugin/plugin.js.map +1 -1
- package/package.json +8 -7
- package/src/rsbuild/normalized-client-build.ts +14 -0
- package/src/rsbuild/plugin.ts +7 -0
- package/src/rsbuild/virtual-modules.ts +15 -5
- package/src/schema.ts +6 -0
- package/src/start-manifest-plugin/inlineCss.ts +93 -0
- package/src/start-manifest-plugin/manifestBuilder.ts +76 -6
- package/src/types.ts +1 -0
- package/src/vite/planning.ts +5 -0
- package/src/vite/plugin.ts +2 -0
- package/src/vite/start-manifest-plugin/normalized-client-build.ts +19 -0
- package/src/vite/start-manifest-plugin/plugin.ts +2 -1
|
@@ -209,20 +209,25 @@ export declare const tanstackStartViteOptionsSchema: z.ZodDefault<z.ZodOptional<
|
|
|
209
209
|
entry: z.ZodOptional<z.ZodString>;
|
|
210
210
|
build: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
211
211
|
staticNodeEnv: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
212
|
+
inlineCss: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
212
213
|
}, "strip", z.ZodTypeAny, {
|
|
213
214
|
staticNodeEnv: boolean;
|
|
215
|
+
inlineCss: boolean;
|
|
214
216
|
}, {
|
|
215
217
|
staticNodeEnv?: boolean | undefined;
|
|
218
|
+
inlineCss?: boolean | undefined;
|
|
216
219
|
}>>>;
|
|
217
220
|
}, "strip", z.ZodTypeAny, {
|
|
218
221
|
build: {
|
|
219
222
|
staticNodeEnv: boolean;
|
|
223
|
+
inlineCss: boolean;
|
|
220
224
|
};
|
|
221
225
|
entry?: string | undefined;
|
|
222
226
|
}, {
|
|
223
227
|
entry?: string | undefined;
|
|
224
228
|
build?: {
|
|
225
229
|
staticNodeEnv?: boolean | undefined;
|
|
230
|
+
inlineCss?: boolean | undefined;
|
|
226
231
|
} | undefined;
|
|
227
232
|
}>>>;
|
|
228
233
|
serverFns: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
@@ -2391,6 +2396,7 @@ export declare const tanstackStartViteOptionsSchema: z.ZodDefault<z.ZodOptional<
|
|
|
2391
2396
|
server: {
|
|
2392
2397
|
build: {
|
|
2393
2398
|
staticNodeEnv: boolean;
|
|
2399
|
+
inlineCss: boolean;
|
|
2394
2400
|
};
|
|
2395
2401
|
entry?: string | undefined;
|
|
2396
2402
|
};
|
|
@@ -2720,6 +2726,7 @@ export declare const tanstackStartViteOptionsSchema: z.ZodDefault<z.ZodOptional<
|
|
|
2720
2726
|
entry?: string | undefined;
|
|
2721
2727
|
build?: {
|
|
2722
2728
|
staticNodeEnv?: boolean | undefined;
|
|
2729
|
+
inlineCss?: boolean | undefined;
|
|
2723
2730
|
} | undefined;
|
|
2724
2731
|
} | undefined;
|
|
2725
2732
|
srcDirectory?: string | undefined;
|
|
@@ -3099,6 +3106,7 @@ export declare function parseStartConfig(opts: z.input<typeof tanstackStartViteO
|
|
|
3099
3106
|
server: {
|
|
3100
3107
|
build: {
|
|
3101
3108
|
staticNodeEnv: boolean;
|
|
3109
|
+
inlineCss: boolean;
|
|
3102
3110
|
};
|
|
3103
3111
|
entry?: string | undefined;
|
|
3104
3112
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getCssAssetSource } from "../../start-manifest-plugin/inlineCss.js";
|
|
1
2
|
import { tsrSplit } from "@tanstack/router-plugin";
|
|
2
3
|
//#region src/vite/start-manifest-plugin/normalized-client-build.ts
|
|
3
4
|
function normalizeViteClientChunk(chunk) {
|
|
@@ -25,6 +26,7 @@ function normalizeViteClientBuild(clientBundle) {
|
|
|
25
26
|
const chunksByFileName = normalizeViteClientChunks(clientBundle);
|
|
26
27
|
const chunkFileNamesByRouteFilePath = /* @__PURE__ */ new Map();
|
|
27
28
|
const cssFilesBySourcePath = /* @__PURE__ */ new Map();
|
|
29
|
+
const cssContentByFileName = /* @__PURE__ */ new Map();
|
|
28
30
|
for (const chunk of chunksByFileName.values()) {
|
|
29
31
|
const bundleEntry = clientBundle[chunk.fileName];
|
|
30
32
|
if (chunk.isEntry) {
|
|
@@ -47,12 +49,20 @@ function normalizeViteClientBuild(clientBundle) {
|
|
|
47
49
|
cssFilesBySourcePath.set(sourcePath, existing ? Array.from(new Set([...existing, ...chunk.css])) : chunk.css.slice());
|
|
48
50
|
}
|
|
49
51
|
}
|
|
52
|
+
for (const fileName in clientBundle) {
|
|
53
|
+
if (!fileName.endsWith(".css")) continue;
|
|
54
|
+
const bundleEntry = clientBundle[fileName];
|
|
55
|
+
if (bundleEntry.type !== "asset") continue;
|
|
56
|
+
const css = getCssAssetSource(bundleEntry.source);
|
|
57
|
+
if (css !== void 0) cssContentByFileName.set(fileName, css);
|
|
58
|
+
}
|
|
50
59
|
if (!entryChunkFileName) throw new Error("No entry file found");
|
|
51
60
|
return {
|
|
52
61
|
entryChunkFileName,
|
|
53
62
|
chunksByFileName,
|
|
54
63
|
chunkFileNamesByRouteFilePath,
|
|
55
|
-
cssFilesBySourcePath
|
|
64
|
+
cssFilesBySourcePath,
|
|
65
|
+
cssContentByFileName
|
|
56
66
|
};
|
|
57
67
|
}
|
|
58
68
|
function getRouteFilePathsFromModuleIds(moduleIds) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalized-client-build.js","names":[],"sources":["../../../../src/vite/start-manifest-plugin/normalized-client-build.ts"],"sourcesContent":["import { tsrSplit } from '@tanstack/router-plugin'\nimport type { Rollup } from 'vite'\nimport type { NormalizedClientBuild, NormalizedClientChunk } from '../../types'\n\nexport function normalizeViteClientChunk(\n chunk: Rollup.OutputChunk,\n): NormalizedClientChunk {\n return {\n fileName: chunk.fileName,\n isEntry: chunk.isEntry,\n imports: chunk.imports,\n dynamicImports: chunk.dynamicImports,\n css: Array.from(chunk.viteMetadata?.importedCss ?? []),\n routeFilePaths: getRouteFilePathsFromModuleIds(chunk.moduleIds),\n }\n}\n\nexport function normalizeViteClientChunks(\n clientBundle: Rollup.OutputBundle,\n): ReadonlyMap<string, NormalizedClientChunk> {\n const chunksByFileName = new Map<string, NormalizedClientChunk>()\n\n for (const fileName in clientBundle) {\n const bundleEntry = clientBundle[fileName]!\n if (bundleEntry.type !== 'chunk') {\n continue\n }\n\n const normalizedChunk = normalizeViteClientChunk(bundleEntry)\n chunksByFileName.set(normalizedChunk.fileName, normalizedChunk)\n }\n\n return chunksByFileName\n}\n\nexport function normalizeViteClientBuild(\n clientBundle: Rollup.OutputBundle,\n): NormalizedClientBuild {\n let entryChunkFileName: string | undefined\n const chunksByFileName = normalizeViteClientChunks(clientBundle)\n const chunkFileNamesByRouteFilePath = new Map<string, Array<string>>()\n const cssFilesBySourcePath = new Map<string, Array<string>>()\n\n for (const chunk of chunksByFileName.values()) {\n const bundleEntry = clientBundle[chunk.fileName] as Rollup.OutputChunk\n\n if (chunk.isEntry) {\n if (entryChunkFileName) {\n throw new Error(\n `multiple entries detected: ${entryChunkFileName} ${chunk.fileName}`,\n )\n }\n entryChunkFileName = chunk.fileName\n }\n\n for (const routeFilePath of chunk.routeFilePaths) {\n let chunkFileNames = chunkFileNamesByRouteFilePath.get(routeFilePath)\n if (chunkFileNames === undefined) {\n chunkFileNames = []\n chunkFileNamesByRouteFilePath.set(routeFilePath, chunkFileNames)\n }\n chunkFileNames.push(chunk.fileName)\n }\n\n for (const moduleId of bundleEntry.moduleIds) {\n const queryIndex = moduleId.indexOf('?')\n const sourcePath =\n queryIndex >= 0 ? moduleId.slice(0, queryIndex) : moduleId\n if (!sourcePath) continue\n\n const existing = cssFilesBySourcePath.get(sourcePath)\n cssFilesBySourcePath.set(\n sourcePath,\n existing\n ? Array.from(new Set([...existing, ...chunk.css]))\n : chunk.css.slice(),\n )\n }\n }\n\n if (!entryChunkFileName) {\n throw new Error('No entry file found')\n }\n\n return {\n entryChunkFileName,\n chunksByFileName,\n chunkFileNamesByRouteFilePath,\n cssFilesBySourcePath,\n }\n}\n\nexport function getRouteFilePathsFromModuleIds(moduleIds: Array<string>) {\n let routeFilePaths: Array<string> | undefined\n let seenRouteFilePaths: Set<string> | undefined\n\n for (const moduleId of moduleIds) {\n const queryIndex = moduleId.indexOf('?')\n\n if (queryIndex < 0) {\n continue\n }\n\n const query = moduleId.slice(queryIndex + 1)\n\n if (!query.includes(tsrSplit)) {\n continue\n }\n\n if (!new URLSearchParams(query).has(tsrSplit)) {\n continue\n }\n\n const routeFilePath = moduleId.slice(0, queryIndex)\n\n if (seenRouteFilePaths?.has(routeFilePath)) {\n continue\n }\n\n if (routeFilePaths === undefined || seenRouteFilePaths === undefined) {\n routeFilePaths = []\n seenRouteFilePaths = new Set<string>()\n }\n\n routeFilePaths.push(routeFilePath)\n seenRouteFilePaths.add(routeFilePath)\n }\n\n return routeFilePaths ?? []\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"normalized-client-build.js","names":[],"sources":["../../../../src/vite/start-manifest-plugin/normalized-client-build.ts"],"sourcesContent":["import { tsrSplit } from '@tanstack/router-plugin'\nimport { getCssAssetSource } from '../../start-manifest-plugin/inlineCss'\nimport type { Rollup } from 'vite'\nimport type { NormalizedClientBuild, NormalizedClientChunk } from '../../types'\n\nexport function normalizeViteClientChunk(\n chunk: Rollup.OutputChunk,\n): NormalizedClientChunk {\n return {\n fileName: chunk.fileName,\n isEntry: chunk.isEntry,\n imports: chunk.imports,\n dynamicImports: chunk.dynamicImports,\n css: Array.from(chunk.viteMetadata?.importedCss ?? []),\n routeFilePaths: getRouteFilePathsFromModuleIds(chunk.moduleIds),\n }\n}\n\nexport function normalizeViteClientChunks(\n clientBundle: Rollup.OutputBundle,\n): ReadonlyMap<string, NormalizedClientChunk> {\n const chunksByFileName = new Map<string, NormalizedClientChunk>()\n\n for (const fileName in clientBundle) {\n const bundleEntry = clientBundle[fileName]!\n if (bundleEntry.type !== 'chunk') {\n continue\n }\n\n const normalizedChunk = normalizeViteClientChunk(bundleEntry)\n chunksByFileName.set(normalizedChunk.fileName, normalizedChunk)\n }\n\n return chunksByFileName\n}\n\nexport function normalizeViteClientBuild(\n clientBundle: Rollup.OutputBundle,\n): NormalizedClientBuild {\n let entryChunkFileName: string | undefined\n const chunksByFileName = normalizeViteClientChunks(clientBundle)\n const chunkFileNamesByRouteFilePath = new Map<string, Array<string>>()\n const cssFilesBySourcePath = new Map<string, Array<string>>()\n const cssContentByFileName = new Map<string, string>()\n\n for (const chunk of chunksByFileName.values()) {\n const bundleEntry = clientBundle[chunk.fileName] as Rollup.OutputChunk\n\n if (chunk.isEntry) {\n if (entryChunkFileName) {\n throw new Error(\n `multiple entries detected: ${entryChunkFileName} ${chunk.fileName}`,\n )\n }\n entryChunkFileName = chunk.fileName\n }\n\n for (const routeFilePath of chunk.routeFilePaths) {\n let chunkFileNames = chunkFileNamesByRouteFilePath.get(routeFilePath)\n if (chunkFileNames === undefined) {\n chunkFileNames = []\n chunkFileNamesByRouteFilePath.set(routeFilePath, chunkFileNames)\n }\n chunkFileNames.push(chunk.fileName)\n }\n\n for (const moduleId of bundleEntry.moduleIds) {\n const queryIndex = moduleId.indexOf('?')\n const sourcePath =\n queryIndex >= 0 ? moduleId.slice(0, queryIndex) : moduleId\n if (!sourcePath) continue\n\n const existing = cssFilesBySourcePath.get(sourcePath)\n cssFilesBySourcePath.set(\n sourcePath,\n existing\n ? Array.from(new Set([...existing, ...chunk.css]))\n : chunk.css.slice(),\n )\n }\n }\n\n for (const fileName in clientBundle) {\n if (!fileName.endsWith('.css')) {\n continue\n }\n\n const bundleEntry = clientBundle[fileName]!\n if (bundleEntry.type !== 'asset') {\n continue\n }\n\n const css = getCssAssetSource(bundleEntry.source)\n if (css !== undefined) {\n cssContentByFileName.set(fileName, css)\n }\n }\n\n if (!entryChunkFileName) {\n throw new Error('No entry file found')\n }\n\n return {\n entryChunkFileName,\n chunksByFileName,\n chunkFileNamesByRouteFilePath,\n cssFilesBySourcePath,\n cssContentByFileName,\n }\n}\n\nexport function getRouteFilePathsFromModuleIds(moduleIds: Array<string>) {\n let routeFilePaths: Array<string> | undefined\n let seenRouteFilePaths: Set<string> | undefined\n\n for (const moduleId of moduleIds) {\n const queryIndex = moduleId.indexOf('?')\n\n if (queryIndex < 0) {\n continue\n }\n\n const query = moduleId.slice(queryIndex + 1)\n\n if (!query.includes(tsrSplit)) {\n continue\n }\n\n if (!new URLSearchParams(query).has(tsrSplit)) {\n continue\n }\n\n const routeFilePath = moduleId.slice(0, queryIndex)\n\n if (seenRouteFilePaths?.has(routeFilePath)) {\n continue\n }\n\n if (routeFilePaths === undefined || seenRouteFilePaths === undefined) {\n routeFilePaths = []\n seenRouteFilePaths = new Set<string>()\n }\n\n routeFilePaths.push(routeFilePath)\n seenRouteFilePaths.add(routeFilePath)\n }\n\n return routeFilePaths ?? []\n}\n"],"mappings":";;;AAKA,SAAgB,yBACd,OACuB;AACvB,QAAO;EACL,UAAU,MAAM;EAChB,SAAS,MAAM;EACf,SAAS,MAAM;EACf,gBAAgB,MAAM;EACtB,KAAK,MAAM,KAAK,MAAM,cAAc,eAAe,EAAE,CAAC;EACtD,gBAAgB,+BAA+B,MAAM,UAAU;EAChE;;AAGH,SAAgB,0BACd,cAC4C;CAC5C,MAAM,mCAAmB,IAAI,KAAoC;AAEjE,MAAK,MAAM,YAAY,cAAc;EACnC,MAAM,cAAc,aAAa;AACjC,MAAI,YAAY,SAAS,QACvB;EAGF,MAAM,kBAAkB,yBAAyB,YAAY;AAC7D,mBAAiB,IAAI,gBAAgB,UAAU,gBAAgB;;AAGjE,QAAO;;AAGT,SAAgB,yBACd,cACuB;CACvB,IAAI;CACJ,MAAM,mBAAmB,0BAA0B,aAAa;CAChE,MAAM,gDAAgC,IAAI,KAA4B;CACtE,MAAM,uCAAuB,IAAI,KAA4B;CAC7D,MAAM,uCAAuB,IAAI,KAAqB;AAEtD,MAAK,MAAM,SAAS,iBAAiB,QAAQ,EAAE;EAC7C,MAAM,cAAc,aAAa,MAAM;AAEvC,MAAI,MAAM,SAAS;AACjB,OAAI,mBACF,OAAM,IAAI,MACR,8BAA8B,mBAAmB,GAAG,MAAM,WAC3D;AAEH,wBAAqB,MAAM;;AAG7B,OAAK,MAAM,iBAAiB,MAAM,gBAAgB;GAChD,IAAI,iBAAiB,8BAA8B,IAAI,cAAc;AACrE,OAAI,mBAAmB,KAAA,GAAW;AAChC,qBAAiB,EAAE;AACnB,kCAA8B,IAAI,eAAe,eAAe;;AAElE,kBAAe,KAAK,MAAM,SAAS;;AAGrC,OAAK,MAAM,YAAY,YAAY,WAAW;GAC5C,MAAM,aAAa,SAAS,QAAQ,IAAI;GACxC,MAAM,aACJ,cAAc,IAAI,SAAS,MAAM,GAAG,WAAW,GAAG;AACpD,OAAI,CAAC,WAAY;GAEjB,MAAM,WAAW,qBAAqB,IAAI,WAAW;AACrD,wBAAqB,IACnB,YACA,WACI,MAAM,KAAK,IAAI,IAAI,CAAC,GAAG,UAAU,GAAG,MAAM,IAAI,CAAC,CAAC,GAChD,MAAM,IAAI,OAAO,CACtB;;;AAIL,MAAK,MAAM,YAAY,cAAc;AACnC,MAAI,CAAC,SAAS,SAAS,OAAO,CAC5B;EAGF,MAAM,cAAc,aAAa;AACjC,MAAI,YAAY,SAAS,QACvB;EAGF,MAAM,MAAM,kBAAkB,YAAY,OAAO;AACjD,MAAI,QAAQ,KAAA,EACV,sBAAqB,IAAI,UAAU,IAAI;;AAI3C,KAAI,CAAC,mBACH,OAAM,IAAI,MAAM,sBAAsB;AAGxC,QAAO;EACL;EACA;EACA;EACA;EACA;EACD;;AAGH,SAAgB,+BAA+B,WAA0B;CACvE,IAAI;CACJ,IAAI;AAEJ,MAAK,MAAM,YAAY,WAAW;EAChC,MAAM,aAAa,SAAS,QAAQ,IAAI;AAExC,MAAI,aAAa,EACf;EAGF,MAAM,QAAQ,SAAS,MAAM,aAAa,EAAE;AAE5C,MAAI,CAAC,MAAM,SAAS,SAAS,CAC3B;AAGF,MAAI,CAAC,IAAI,gBAAgB,MAAM,CAAC,IAAI,SAAS,CAC3C;EAGF,MAAM,gBAAgB,SAAS,MAAM,GAAG,WAAW;AAEnD,MAAI,oBAAoB,IAAI,cAAc,CACxC;AAGF,MAAI,mBAAmB,KAAA,KAAa,uBAAuB,KAAA,GAAW;AACpE,oBAAiB,EAAE;AACnB,wCAAqB,IAAI,KAAa;;AAGxC,iBAAe,KAAK,cAAc;AAClC,qBAAmB,IAAI,cAAc;;AAGvC,QAAO,kBAAkB,EAAE"}
|
|
@@ -25,7 +25,7 @@ function startManifestPlugin(opts) {
|
|
|
25
25
|
moduleId: VIRTUAL_MODULES.startManifest,
|
|
26
26
|
enforce: "pre",
|
|
27
27
|
load() {
|
|
28
|
-
const { resolvedStartConfig } = opts.getConfig();
|
|
28
|
+
const { resolvedStartConfig, startConfig } = opts.getConfig();
|
|
29
29
|
const clientEntry = joinURL(resolvedStartConfig.basePaths.publicBase, "@id", ENTRY_POINTS.client);
|
|
30
30
|
if (this.environment.name !== START_ENVIRONMENT_NAMES.server) return getEmptyStartManifestModule(clientEntry);
|
|
31
31
|
if (this.environment.config.command === "serve") return getEmptyStartManifestModule(clientEntry);
|
|
@@ -35,6 +35,7 @@ function startManifestPlugin(opts) {
|
|
|
35
35
|
clientBuild,
|
|
36
36
|
routeTreeRoutes,
|
|
37
37
|
basePath: resolvedStartConfig.basePaths.publicBase,
|
|
38
|
+
inlineCss: startConfig.server.build.inlineCss,
|
|
38
39
|
additionalRouteAssets: getViteAdditionalRouteAssets({
|
|
39
40
|
cssCodeSplitDisabledFileName,
|
|
40
41
|
basePath: resolvedStartConfig.basePaths.publicBase,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../../../../src/vite/start-manifest-plugin/plugin.ts"],"sourcesContent":["import { joinURL } from 'ufo'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { rootRouteId } from '@tanstack/router-core'\nimport { ENTRY_POINTS, START_ENVIRONMENT_NAMES } from '../../constants'\nimport {\n buildStartManifest,\n createManifestAssetResolvers,\n normalizeViteClientBuild,\n serializeStartManifest,\n} from '../../start-manifest-plugin/manifestBuilder'\nimport { createVirtualModule } from '../createVirtualModule'\nimport type { GetConfigFn, NormalizedClientBuild } from '../../types'\nimport type { PluginOption, Rollup } from 'vite'\n\nexport function startManifestPlugin(opts: {\n getConfig: GetConfigFn\n}): PluginOption {\n let clientBuild: NormalizedClientBuild | undefined\n let cssCodeSplitDisabledFileName: string | undefined\n\n return [\n {\n name: 'tanstack-start:start-manifest-capture-client-build',\n applyToEnvironment(environment) {\n return environment.name === START_ENVIRONMENT_NAMES.client\n },\n enforce: 'post',\n generateBundle(_options, bundle) {\n if (this.environment.name !== START_ENVIRONMENT_NAMES.client) {\n throw new Error(\n `Unexpected environment for client build capture: ${this.environment.name}`,\n )\n }\n\n clientBuild = normalizeViteClientBuild(bundle)\n cssCodeSplitDisabledFileName = getAssetFileNameByName(\n bundle,\n 'style.css',\n )\n },\n },\n createVirtualModule({\n name: 'tanstack-start:start-manifest-plugin',\n moduleId: VIRTUAL_MODULES.startManifest,\n enforce: 'pre',\n load() {\n const { resolvedStartConfig } = opts.getConfig()\n const clientEntry = joinURL(\n resolvedStartConfig.basePaths.publicBase,\n '@id',\n ENTRY_POINTS.client,\n )\n\n if (this.environment.name !== START_ENVIRONMENT_NAMES.server) {\n return getEmptyStartManifestModule(clientEntry)\n }\n\n if (this.environment.config.command === 'serve') {\n return getEmptyStartManifestModule(clientEntry)\n }\n\n const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST\n // TODO this needs further discussion with vite-rsc, this is a temporary workaround\n // If the client bundle isn't available yet (e.g., during RSC scan builds),\n // return a dummy manifest. The real manifest will be generated in the actual build.\n if (!clientBuild) {\n return getEmptyStartManifestModule(clientEntry)\n }\n const startManifest = buildStartManifest({\n clientBuild,\n routeTreeRoutes,\n basePath: resolvedStartConfig.basePaths.publicBase,\n additionalRouteAssets: getViteAdditionalRouteAssets({\n cssCodeSplitDisabledFileName,\n basePath: resolvedStartConfig.basePaths.publicBase,\n cssCodeSplit: this.environment.config.build.cssCodeSplit,\n }),\n })\n\n return `export const tsrStartManifest = () => (${serializeStartManifest(startManifest)})`\n },\n }),\n ]\n}\n\nfunction getViteAdditionalRouteAssets(options: {\n cssCodeSplitDisabledFileName: string | undefined\n basePath: string\n cssCodeSplit: boolean | undefined\n}) {\n if (options.cssCodeSplit !== false) {\n return undefined\n }\n\n if (!options.cssCodeSplitDisabledFileName) {\n throw new Error(\n \"TanStack Start could not find Vite's generated `style.css` manifest entry while `build.cssCodeSplit` is disabled\",\n )\n }\n\n const { getStylesheetAsset } = createManifestAssetResolvers(options.basePath)\n\n return {\n [rootRouteId]: [getStylesheetAsset(options.cssCodeSplitDisabledFileName)],\n }\n}\n\nfunction getAssetFileNameByName(\n bundle: Rollup.OutputBundle,\n assetName: string,\n) {\n for (const fileName in bundle) {\n const bundleEntry = bundle[fileName]!\n\n if (bundleEntry.type !== 'asset') {\n continue\n }\n\n if (bundleEntry.name === assetName) {\n return fileName\n }\n\n if ('names' in bundleEntry && bundleEntry.names.includes(assetName)) {\n return fileName\n }\n }\n\n return undefined\n}\n\nfunction getEmptyStartManifestModule(clientEntry: string) {\n return `export const tsrStartManifest = () => ({\n routes: {},\n clientEntry: '${clientEntry}',\n })`\n}\n"],"mappings":";;;;;;;;AAcA,SAAgB,oBAAoB,MAEnB;CACf,IAAI;CACJ,IAAI;AAEJ,QAAO,CACL;EACE,MAAM;EACN,mBAAmB,aAAa;AAC9B,UAAO,YAAY,SAAS,wBAAwB;;EAEtD,SAAS;EACT,eAAe,UAAU,QAAQ;AAC/B,OAAI,KAAK,YAAY,SAAS,wBAAwB,OACpD,OAAM,IAAI,MACR,oDAAoD,KAAK,YAAY,OACtE;AAGH,iBAAc,yBAAyB,OAAO;AAC9C,kCAA+B,uBAC7B,QACA,YACD;;EAEJ,EACD,oBAAoB;EAClB,MAAM;EACN,UAAU,gBAAgB;EAC1B,SAAS;EACT,OAAO;GACL,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../../../../src/vite/start-manifest-plugin/plugin.ts"],"sourcesContent":["import { joinURL } from 'ufo'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { rootRouteId } from '@tanstack/router-core'\nimport { ENTRY_POINTS, START_ENVIRONMENT_NAMES } from '../../constants'\nimport {\n buildStartManifest,\n createManifestAssetResolvers,\n normalizeViteClientBuild,\n serializeStartManifest,\n} from '../../start-manifest-plugin/manifestBuilder'\nimport { createVirtualModule } from '../createVirtualModule'\nimport type { GetConfigFn, NormalizedClientBuild } from '../../types'\nimport type { PluginOption, Rollup } from 'vite'\n\nexport function startManifestPlugin(opts: {\n getConfig: GetConfigFn\n}): PluginOption {\n let clientBuild: NormalizedClientBuild | undefined\n let cssCodeSplitDisabledFileName: string | undefined\n\n return [\n {\n name: 'tanstack-start:start-manifest-capture-client-build',\n applyToEnvironment(environment) {\n return environment.name === START_ENVIRONMENT_NAMES.client\n },\n enforce: 'post',\n generateBundle(_options, bundle) {\n if (this.environment.name !== START_ENVIRONMENT_NAMES.client) {\n throw new Error(\n `Unexpected environment for client build capture: ${this.environment.name}`,\n )\n }\n\n clientBuild = normalizeViteClientBuild(bundle)\n cssCodeSplitDisabledFileName = getAssetFileNameByName(\n bundle,\n 'style.css',\n )\n },\n },\n createVirtualModule({\n name: 'tanstack-start:start-manifest-plugin',\n moduleId: VIRTUAL_MODULES.startManifest,\n enforce: 'pre',\n load() {\n const { resolvedStartConfig, startConfig } = opts.getConfig()\n const clientEntry = joinURL(\n resolvedStartConfig.basePaths.publicBase,\n '@id',\n ENTRY_POINTS.client,\n )\n\n if (this.environment.name !== START_ENVIRONMENT_NAMES.server) {\n return getEmptyStartManifestModule(clientEntry)\n }\n\n if (this.environment.config.command === 'serve') {\n return getEmptyStartManifestModule(clientEntry)\n }\n\n const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST\n // TODO this needs further discussion with vite-rsc, this is a temporary workaround\n // If the client bundle isn't available yet (e.g., during RSC scan builds),\n // return a dummy manifest. The real manifest will be generated in the actual build.\n if (!clientBuild) {\n return getEmptyStartManifestModule(clientEntry)\n }\n const startManifest = buildStartManifest({\n clientBuild,\n routeTreeRoutes,\n basePath: resolvedStartConfig.basePaths.publicBase,\n inlineCss: startConfig.server.build.inlineCss,\n additionalRouteAssets: getViteAdditionalRouteAssets({\n cssCodeSplitDisabledFileName,\n basePath: resolvedStartConfig.basePaths.publicBase,\n cssCodeSplit: this.environment.config.build.cssCodeSplit,\n }),\n })\n\n return `export const tsrStartManifest = () => (${serializeStartManifest(startManifest)})`\n },\n }),\n ]\n}\n\nfunction getViteAdditionalRouteAssets(options: {\n cssCodeSplitDisabledFileName: string | undefined\n basePath: string\n cssCodeSplit: boolean | undefined\n}) {\n if (options.cssCodeSplit !== false) {\n return undefined\n }\n\n if (!options.cssCodeSplitDisabledFileName) {\n throw new Error(\n \"TanStack Start could not find Vite's generated `style.css` manifest entry while `build.cssCodeSplit` is disabled\",\n )\n }\n\n const { getStylesheetAsset } = createManifestAssetResolvers(options.basePath)\n\n return {\n [rootRouteId]: [getStylesheetAsset(options.cssCodeSplitDisabledFileName)],\n }\n}\n\nfunction getAssetFileNameByName(\n bundle: Rollup.OutputBundle,\n assetName: string,\n) {\n for (const fileName in bundle) {\n const bundleEntry = bundle[fileName]!\n\n if (bundleEntry.type !== 'asset') {\n continue\n }\n\n if (bundleEntry.name === assetName) {\n return fileName\n }\n\n if ('names' in bundleEntry && bundleEntry.names.includes(assetName)) {\n return fileName\n }\n }\n\n return undefined\n}\n\nfunction getEmptyStartManifestModule(clientEntry: string) {\n return `export const tsrStartManifest = () => ({\n routes: {},\n clientEntry: '${clientEntry}',\n })`\n}\n"],"mappings":";;;;;;;;AAcA,SAAgB,oBAAoB,MAEnB;CACf,IAAI;CACJ,IAAI;AAEJ,QAAO,CACL;EACE,MAAM;EACN,mBAAmB,aAAa;AAC9B,UAAO,YAAY,SAAS,wBAAwB;;EAEtD,SAAS;EACT,eAAe,UAAU,QAAQ;AAC/B,OAAI,KAAK,YAAY,SAAS,wBAAwB,OACpD,OAAM,IAAI,MACR,oDAAoD,KAAK,YAAY,OACtE;AAGH,iBAAc,yBAAyB,OAAO;AAC9C,kCAA+B,uBAC7B,QACA,YACD;;EAEJ,EACD,oBAAoB;EAClB,MAAM;EACN,UAAU,gBAAgB;EAC1B,SAAS;EACT,OAAO;GACL,MAAM,EAAE,qBAAqB,gBAAgB,KAAK,WAAW;GAC7D,MAAM,cAAc,QAClB,oBAAoB,UAAU,YAC9B,OACA,aAAa,OACd;AAED,OAAI,KAAK,YAAY,SAAS,wBAAwB,OACpD,QAAO,4BAA4B,YAAY;AAGjD,OAAI,KAAK,YAAY,OAAO,YAAY,QACtC,QAAO,4BAA4B,YAAY;GAGjD,MAAM,kBAAkB,WAAW;AAInC,OAAI,CAAC,YACH,QAAO,4BAA4B,YAAY;AAcjD,UAAO,0CAA0C,uBAZ3B,mBAAmB;IACvC;IACA;IACA,UAAU,oBAAoB,UAAU;IACxC,WAAW,YAAY,OAAO,MAAM;IACpC,uBAAuB,6BAA6B;KAClD;KACA,UAAU,oBAAoB,UAAU;KACxC,cAAc,KAAK,YAAY,OAAO,MAAM;KAC7C,CAAC;IACH,CAAC,CAEoF,CAAC;;EAE1F,CAAC,CACH;;AAGH,SAAS,6BAA6B,SAInC;AACD,KAAI,QAAQ,iBAAiB,MAC3B;AAGF,KAAI,CAAC,QAAQ,6BACX,OAAM,IAAI,MACR,mHACD;CAGH,MAAM,EAAE,uBAAuB,6BAA6B,QAAQ,SAAS;AAE7E,QAAO,GACJ,cAAc,CAAC,mBAAmB,QAAQ,6BAA6B,CAAC,EAC1E;;AAGH,SAAS,uBACP,QACA,WACA;AACA,MAAK,MAAM,YAAY,QAAQ;EAC7B,MAAM,cAAc,OAAO;AAE3B,MAAI,YAAY,SAAS,QACvB;AAGF,MAAI,YAAY,SAAS,UACvB,QAAO;AAGT,MAAI,WAAW,eAAe,YAAY,MAAM,SAAS,UAAU,CACjE,QAAO;;;AAOb,SAAS,4BAA4B,aAAqB;AACxD,QAAO;;sBAEa,YAAY"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/start-plugin-core",
|
|
3
|
-
"version": "1.169.
|
|
3
|
+
"version": "1.169.2",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"seroval": "^1.5.0",
|
|
77
77
|
"cheerio": "^1.0.0",
|
|
78
78
|
"exsolve": "^1.0.7",
|
|
79
|
+
"lightningcss": "^1.32.0",
|
|
79
80
|
"pathe": "^2.0.3",
|
|
80
81
|
"picomatch": "^4.0.3",
|
|
81
82
|
"srvx": "^0.11.9",
|
|
@@ -85,15 +86,15 @@
|
|
|
85
86
|
"vitefu": "^1.1.1",
|
|
86
87
|
"xmlbuilder2": "^4.0.3",
|
|
87
88
|
"zod": "^3.24.2",
|
|
88
|
-
"@tanstack/router-core": "1.168.
|
|
89
|
-
"@tanstack/router-generator": "1.166.
|
|
90
|
-
"@tanstack/router-plugin": "1.167.
|
|
89
|
+
"@tanstack/router-core": "1.168.16",
|
|
90
|
+
"@tanstack/router-generator": "1.166.34",
|
|
91
|
+
"@tanstack/router-plugin": "1.167.24",
|
|
91
92
|
"@tanstack/router-utils": "1.161.7",
|
|
92
|
-
"@tanstack/start-client-core": "1.167.
|
|
93
|
-
"@tanstack/start-server-core": "1.167.
|
|
93
|
+
"@tanstack/start-client-core": "1.167.18",
|
|
94
|
+
"@tanstack/start-server-core": "1.167.20"
|
|
94
95
|
},
|
|
95
96
|
"devDependencies": {
|
|
96
|
-
"@rsbuild/core": "^2.0.
|
|
97
|
+
"@rsbuild/core": "^2.0.1",
|
|
97
98
|
"@types/babel__code-frame": "^7.0.6",
|
|
98
99
|
"@types/babel__core": "^7.20.5",
|
|
99
100
|
"@types/picomatch": "^4.0.2",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { tsrSplit } from '@tanstack/router-plugin'
|
|
2
|
+
import { getCssAssetSource } from '../start-manifest-plugin/inlineCss'
|
|
2
3
|
import { RSBUILD_ENVIRONMENT_NAMES } from './planning'
|
|
3
4
|
import type { RsbuildPluginAPI, Rspack } from '@rsbuild/core'
|
|
4
5
|
import type { NormalizedClientBuild, NormalizedClientChunk } from '../types'
|
|
@@ -162,6 +163,7 @@ export function normalizeRspackClientBuild(
|
|
|
162
163
|
const chunksByFileName = new Map<string, NormalizedClientChunk>()
|
|
163
164
|
const chunkFileNamesByRouteFilePath = new Map<string, Array<string>>()
|
|
164
165
|
const cssFilesBySourcePath = new Map<string, Array<string>>()
|
|
166
|
+
const cssContentByFileName = new Map<string, string>()
|
|
165
167
|
let entryChunkFileName: string | undefined
|
|
166
168
|
|
|
167
169
|
// Collect all initial JS file names from the main entry for computing
|
|
@@ -272,6 +274,17 @@ export function normalizeRspackClientBuild(
|
|
|
272
274
|
throw new Error('No entry file found in rspack client build')
|
|
273
275
|
}
|
|
274
276
|
|
|
277
|
+
for (const asset of compilation.getAssets()) {
|
|
278
|
+
if (!asset.name.endsWith('.css')) {
|
|
279
|
+
continue
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const css = getCssAssetSource(asset.source.source())
|
|
283
|
+
if (css !== undefined) {
|
|
284
|
+
cssContentByFileName.set(asset.name, css)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
275
288
|
// In RSC mode, CSS from server components is associated with the 'rsc'
|
|
276
289
|
// client entry chunk (not the main 'index' entry). The manifest builder
|
|
277
290
|
// merges the entry chunk's CSS into __root__, so by appending RSC CSS
|
|
@@ -299,6 +312,7 @@ export function normalizeRspackClientBuild(
|
|
|
299
312
|
chunksByFileName,
|
|
300
313
|
chunkFileNamesByRouteFilePath,
|
|
301
314
|
cssFilesBySourcePath,
|
|
315
|
+
cssContentByFileName,
|
|
302
316
|
}
|
|
303
317
|
}
|
|
304
318
|
|
package/src/rsbuild/plugin.ts
CHANGED
|
@@ -161,6 +161,7 @@ export function tanStackStartRsbuild(
|
|
|
161
161
|
routerBasepath,
|
|
162
162
|
serverFnBase: startConfig.serverFns.base,
|
|
163
163
|
})
|
|
164
|
+
const inlineCssEnabled = !isDev && startConfig.server.build.inlineCss
|
|
164
165
|
|
|
165
166
|
return mergeRsbuildConfig(rsbuildConfig, {
|
|
166
167
|
source: {
|
|
@@ -188,6 +189,12 @@ export function tanStackStartRsbuild(
|
|
|
188
189
|
'import.meta.env.TSS_DEV_SSR_STYLES_BASEPATH': JSON.stringify(
|
|
189
190
|
resolvedStartConfig.basePaths.publicBase,
|
|
190
191
|
),
|
|
192
|
+
'process.env.TSS_INLINE_CSS_ENABLED': JSON.stringify(
|
|
193
|
+
inlineCssEnabled ? 'true' : 'false',
|
|
194
|
+
),
|
|
195
|
+
'import.meta.env.TSS_INLINE_CSS_ENABLED': JSON.stringify(
|
|
196
|
+
inlineCssEnabled ? 'true' : 'false',
|
|
197
|
+
),
|
|
191
198
|
},
|
|
192
199
|
},
|
|
193
200
|
server: {
|
|
@@ -76,33 +76,39 @@ export const tsrStartManifest = () => globalThis[${JSON.stringify(DEV_START_MANI
|
|
|
76
76
|
function buildStartManifestData(
|
|
77
77
|
clientBuild: NormalizedClientBuild,
|
|
78
78
|
publicBase: string,
|
|
79
|
+
inlineCss: boolean,
|
|
79
80
|
) {
|
|
80
81
|
const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST
|
|
81
82
|
return buildStartManifest({
|
|
82
83
|
clientBuild,
|
|
83
84
|
routeTreeRoutes,
|
|
84
85
|
basePath: publicBase,
|
|
86
|
+
inlineCss,
|
|
85
87
|
})
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
function serializeStartManifestData(
|
|
89
91
|
clientBuild: NormalizedClientBuild,
|
|
90
92
|
publicBase: string,
|
|
93
|
+
inlineCss: boolean,
|
|
91
94
|
): string {
|
|
92
|
-
return JSON.stringify(
|
|
95
|
+
return JSON.stringify(
|
|
96
|
+
buildStartManifestData(clientBuild, publicBase, inlineCss),
|
|
97
|
+
)
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
function generateManifestModuleBuild(
|
|
96
101
|
clientBuild: NormalizedClientBuild | undefined,
|
|
97
102
|
publicBase: string,
|
|
98
103
|
_devClientEntryUrl: string,
|
|
104
|
+
inlineCss: boolean,
|
|
99
105
|
): string {
|
|
100
106
|
if (!clientBuild) {
|
|
101
107
|
return `const tsrStartManifestData = ${JSON.stringify(START_MANIFEST_PLACEHOLDER)}
|
|
102
108
|
export const tsrStartManifest = () => tsrStartManifestData`
|
|
103
109
|
}
|
|
104
110
|
|
|
105
|
-
return `export const tsrStartManifest = () => (${serializeStartManifestData(clientBuild, publicBase)})`
|
|
111
|
+
return `export const tsrStartManifest = () => (${serializeStartManifestData(clientBuild, publicBase, inlineCss)})`
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
// ---------------------------------------------------------------------------
|
|
@@ -345,7 +351,7 @@ export function registerVirtualModules(
|
|
|
345
351
|
// Generate initial content for each virtual module per environment
|
|
346
352
|
function getInitialContent(environmentName: string): Record<string, string> {
|
|
347
353
|
// Safe to call getConfig() here — this runs inside modifyRspackConfig
|
|
348
|
-
const { resolvedStartConfig } = opts.getConfig()
|
|
354
|
+
const { resolvedStartConfig, startConfig } = opts.getConfig()
|
|
349
355
|
const isServerEnv = environmentName === RSBUILD_ENVIRONMENT_NAMES.server
|
|
350
356
|
const isClientEnv = environmentName === RSBUILD_ENVIRONMENT_NAMES.client
|
|
351
357
|
const content: Record<string, string> = {}
|
|
@@ -361,6 +367,7 @@ export function registerVirtualModules(
|
|
|
361
367
|
clientBuild,
|
|
362
368
|
resolvedStartConfig.basePaths.publicBase,
|
|
363
369
|
devClientEntryUrl,
|
|
370
|
+
startConfig.server.build.inlineCss,
|
|
364
371
|
)
|
|
365
372
|
} else {
|
|
366
373
|
content[paths.manifest] = 'export default {}'
|
|
@@ -495,7 +502,7 @@ export function createFromReadableStream() { throw new Error('RSC SSR decode is
|
|
|
495
502
|
},
|
|
496
503
|
|
|
497
504
|
generateManifestContent(newClientBuild: NormalizedClientBuild): string {
|
|
498
|
-
const { resolvedStartConfig } = opts.getConfig()
|
|
505
|
+
const { resolvedStartConfig, startConfig } = opts.getConfig()
|
|
499
506
|
const devClientEntryUrl = opts.getDevClientEntryUrl(
|
|
500
507
|
resolvedStartConfig.basePaths.publicBase,
|
|
501
508
|
)
|
|
@@ -503,16 +510,18 @@ export function createFromReadableStream() { throw new Error('RSC SSR decode is
|
|
|
503
510
|
newClientBuild,
|
|
504
511
|
resolvedStartConfig.basePaths.publicBase,
|
|
505
512
|
devClientEntryUrl,
|
|
513
|
+
!isDev && startConfig.server.build.inlineCss,
|
|
506
514
|
)
|
|
507
515
|
},
|
|
508
516
|
|
|
509
517
|
generateManifestValueLiteral(
|
|
510
518
|
newClientBuild: NormalizedClientBuild,
|
|
511
519
|
): string {
|
|
512
|
-
const { resolvedStartConfig } = opts.getConfig()
|
|
520
|
+
const { resolvedStartConfig, startConfig } = opts.getConfig()
|
|
513
521
|
return serializeStartManifestData(
|
|
514
522
|
newClientBuild,
|
|
515
523
|
resolvedStartConfig.basePaths.publicBase,
|
|
524
|
+
!isDev && startConfig.server.build.inlineCss,
|
|
516
525
|
)
|
|
517
526
|
},
|
|
518
527
|
|
|
@@ -528,6 +537,7 @@ export function createFromReadableStream() { throw new Error('RSC SSR decode is
|
|
|
528
537
|
)[DEV_START_MANIFEST_GLOBAL] = buildStartManifestData(
|
|
529
538
|
clientBuild,
|
|
530
539
|
resolvedStartConfig.basePaths.publicBase,
|
|
540
|
+
false,
|
|
531
541
|
)
|
|
532
542
|
}
|
|
533
543
|
},
|
package/src/schema.ts
CHANGED
|
@@ -209,6 +209,12 @@ export const tanstackStartOptionsObjectSchema = z.object({
|
|
|
209
209
|
build: z
|
|
210
210
|
.object({
|
|
211
211
|
staticNodeEnv: z.boolean().optional().default(true),
|
|
212
|
+
/**
|
|
213
|
+
* Inline route CSS into the server-rendered HTML response.
|
|
214
|
+
*
|
|
215
|
+
* @experimental This option is experimental!
|
|
216
|
+
*/
|
|
217
|
+
inlineCss: z.boolean().optional().default(false),
|
|
212
218
|
})
|
|
213
219
|
.optional()
|
|
214
220
|
.default({}),
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { transform } from 'lightningcss'
|
|
2
|
+
|
|
3
|
+
const cssUrlPattern =
|
|
4
|
+
/url\(\s*(?:"([^"]*)"|'([^']*)'|([^)"']*?))\s*\)|@import\s+(?:url\(\s*(?:"([^"]*)"|'([^']*)'|([^)"']*?))\s*\)|"([^"]*)"|'([^']*)')/gi
|
|
5
|
+
|
|
6
|
+
function isRelativeCssUrl(url: string) {
|
|
7
|
+
if (!url) return false
|
|
8
|
+
if (url.startsWith('#')) return false
|
|
9
|
+
if (url.startsWith('/')) return false
|
|
10
|
+
if (/^[a-z][a-z\d+.-]*:/i.test(url)) return false
|
|
11
|
+
return true
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function shouldRebaseInlineCssUrls(css: string) {
|
|
15
|
+
cssUrlPattern.lastIndex = 0
|
|
16
|
+
|
|
17
|
+
for (const match of css.matchAll(cssUrlPattern)) {
|
|
18
|
+
const url = (
|
|
19
|
+
match[1] ??
|
|
20
|
+
match[2] ??
|
|
21
|
+
match[3] ??
|
|
22
|
+
match[4] ??
|
|
23
|
+
match[5] ??
|
|
24
|
+
match[6] ??
|
|
25
|
+
match[7] ??
|
|
26
|
+
match[8] ??
|
|
27
|
+
''
|
|
28
|
+
).trim()
|
|
29
|
+
|
|
30
|
+
if (isRelativeCssUrl(url)) {
|
|
31
|
+
return true
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function rebaseCssUrl(url: string, cssHref: string) {
|
|
39
|
+
if (!isRelativeCssUrl(url)) {
|
|
40
|
+
return url
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const fakeOrigin = 'http://tanstack.local'
|
|
44
|
+
const resolved = new URL(url, new URL(cssHref, fakeOrigin))
|
|
45
|
+
|
|
46
|
+
if (resolved.origin === fakeOrigin) {
|
|
47
|
+
return `${resolved.pathname}${resolved.search}${resolved.hash}`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return resolved.href
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function rebaseInlineCssUrls(options: { css: string; cssHref: string }) {
|
|
54
|
+
const css = options.css.trim()
|
|
55
|
+
|
|
56
|
+
if (!shouldRebaseInlineCssUrls(css)) {
|
|
57
|
+
return css
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = transform({
|
|
61
|
+
filename: options.cssHref,
|
|
62
|
+
code: Buffer.from(css),
|
|
63
|
+
minify: true,
|
|
64
|
+
visitor: {
|
|
65
|
+
Url(url) {
|
|
66
|
+
return {
|
|
67
|
+
...url,
|
|
68
|
+
url: rebaseCssUrl(url.url, options.cssHref),
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
Rule: {
|
|
72
|
+
import(rule: any) {
|
|
73
|
+
return {
|
|
74
|
+
...rule,
|
|
75
|
+
value: {
|
|
76
|
+
...rule.value,
|
|
77
|
+
url: rebaseCssUrl(rule.value.url, options.cssHref),
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
return Buffer.from(result.code).toString('utf8')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function getCssAssetSource(source: unknown) {
|
|
89
|
+
if (typeof source === 'string') return source
|
|
90
|
+
if (source instanceof Uint8Array) return Buffer.from(source).toString('utf8')
|
|
91
|
+
if (source == null) return undefined
|
|
92
|
+
return String(source)
|
|
93
|
+
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/prefer-for-of */
|
|
2
2
|
import { serialize } from 'seroval'
|
|
3
3
|
import { joinURL } from 'ufo'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
getStylesheetHref,
|
|
6
|
+
resolveManifestAssetLink,
|
|
7
|
+
rootRouteId,
|
|
8
|
+
} from '@tanstack/router-core'
|
|
5
9
|
import {
|
|
6
10
|
getRouteFilePathsFromModuleIds,
|
|
7
11
|
normalizeViteClientBuild,
|
|
8
12
|
normalizeViteClientChunk,
|
|
9
13
|
} from '../vite/start-manifest-plugin/normalized-client-build'
|
|
14
|
+
import { rebaseInlineCssUrls } from './inlineCss'
|
|
10
15
|
import type { ManifestAssetLink, RouterManagedTag } from '@tanstack/router-core'
|
|
11
16
|
import type { NormalizedClientBuild, NormalizedClientChunk } from '../types'
|
|
12
17
|
|
|
@@ -42,6 +47,9 @@ type DedupeRoute = {
|
|
|
42
47
|
export interface StartManifest {
|
|
43
48
|
routes: Record<string, RouteTreeRoute>
|
|
44
49
|
clientEntry: string
|
|
50
|
+
inlineCss?: {
|
|
51
|
+
styles: Record<string, string>
|
|
52
|
+
}
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
export function appendUniqueStrings(
|
|
@@ -152,6 +160,7 @@ export function buildStartManifest(options: {
|
|
|
152
160
|
clientBuild: NormalizedClientBuild
|
|
153
161
|
routeTreeRoutes: RouteTreeRoutes
|
|
154
162
|
basePath: string
|
|
163
|
+
inlineCss?: boolean
|
|
155
164
|
additionalRouteAssets?: Partial<
|
|
156
165
|
Record<string, ReadonlyArray<RouterManagedTag>>
|
|
157
166
|
>
|
|
@@ -180,10 +189,20 @@ export function buildStartManifest(options: {
|
|
|
180
189
|
}
|
|
181
190
|
}
|
|
182
191
|
|
|
183
|
-
|
|
192
|
+
const result: StartManifest = {
|
|
184
193
|
routes,
|
|
185
194
|
clientEntry: assetResolvers.getAssetPath(scannedChunks.entryChunk.fileName),
|
|
186
195
|
}
|
|
196
|
+
|
|
197
|
+
if (options.inlineCss) {
|
|
198
|
+
result.inlineCss = buildInlineCssManifestData({
|
|
199
|
+
routes,
|
|
200
|
+
basePath: options.basePath,
|
|
201
|
+
cssContentByFileName: options.clientBuild.cssContentByFileName,
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return result
|
|
187
206
|
}
|
|
188
207
|
|
|
189
208
|
export function serializeStartManifest(startManifest: StartManifest) {
|
|
@@ -323,10 +342,6 @@ export function createChunkCssAssetCollector(options: {
|
|
|
323
342
|
const assets: Array<RouterManagedTag> = []
|
|
324
343
|
const seenAssets = new Set<RouterManagedTag>()
|
|
325
344
|
|
|
326
|
-
for (const cssFile of chunk.css) {
|
|
327
|
-
appendAsset(assets, seenAssets, options.getStylesheetAsset(cssFile))
|
|
328
|
-
}
|
|
329
|
-
|
|
330
345
|
for (let i = 0; i < chunk.imports.length; i++) {
|
|
331
346
|
const importedChunk = options.chunksByFileName.get(chunk.imports[i]!)
|
|
332
347
|
if (!importedChunk) {
|
|
@@ -339,6 +354,10 @@ export function createChunkCssAssetCollector(options: {
|
|
|
339
354
|
}
|
|
340
355
|
}
|
|
341
356
|
|
|
357
|
+
for (const cssFile of chunk.css) {
|
|
358
|
+
appendAsset(assets, seenAssets, options.getStylesheetAsset(cssFile))
|
|
359
|
+
}
|
|
360
|
+
|
|
342
361
|
stateByChunk.delete(chunk)
|
|
343
362
|
assetsByChunk.set(chunk, assets)
|
|
344
363
|
return assets
|
|
@@ -347,6 +366,57 @@ export function createChunkCssAssetCollector(options: {
|
|
|
347
366
|
return { getChunkCssAssets }
|
|
348
367
|
}
|
|
349
368
|
|
|
369
|
+
function buildInlineCssManifestData(options: {
|
|
370
|
+
routes: Record<string, RouteTreeRoute>
|
|
371
|
+
basePath: string
|
|
372
|
+
cssContentByFileName: ReadonlyMap<string, string> | undefined
|
|
373
|
+
}): StartManifest['inlineCss'] {
|
|
374
|
+
const stylesheetHrefs = new Set<string>()
|
|
375
|
+
|
|
376
|
+
for (const route of Object.values(options.routes)) {
|
|
377
|
+
for (const asset of route.assets ?? []) {
|
|
378
|
+
const href = getStylesheetHref(asset)
|
|
379
|
+
if (href) {
|
|
380
|
+
stylesheetHrefs.add(href)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (stylesheetHrefs.size === 0) {
|
|
386
|
+
return { styles: {} }
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (!options.cssContentByFileName) {
|
|
390
|
+
throw new Error(
|
|
391
|
+
'TanStack Start inlineCss is enabled, but the client build did not provide CSS content',
|
|
392
|
+
)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const { getAssetPath } = createManifestAssetResolvers(options.basePath)
|
|
396
|
+
const styles: Record<string, string> = {}
|
|
397
|
+
const missingHrefs = new Set(stylesheetHrefs)
|
|
398
|
+
|
|
399
|
+
for (const [cssFile, css] of options.cssContentByFileName) {
|
|
400
|
+
const cssHref = getAssetPath(cssFile)
|
|
401
|
+
if (!stylesheetHrefs.has(cssHref)) {
|
|
402
|
+
continue
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
styles[cssHref] = rebaseInlineCssUrls({ css, cssHref })
|
|
406
|
+
missingHrefs.delete(cssHref)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (missingHrefs.size > 0) {
|
|
410
|
+
throw new Error(
|
|
411
|
+
`TanStack Start inlineCss could not find CSS content for: ${Array.from(
|
|
412
|
+
missingHrefs,
|
|
413
|
+
).join(', ')}`,
|
|
414
|
+
)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return { styles }
|
|
418
|
+
}
|
|
419
|
+
|
|
350
420
|
export function buildRouteManifestRoutes(options: {
|
|
351
421
|
routeTreeRoutes: RouteTreeRoutes
|
|
352
422
|
routeChunksByFilePath: ReadonlyMap<
|
package/src/types.ts
CHANGED
|
@@ -47,6 +47,7 @@ export interface NormalizedClientBuild {
|
|
|
47
47
|
chunksByFileName: ReadonlyMap<string, NormalizedClientChunk>
|
|
48
48
|
chunkFileNamesByRouteFilePath: ReadonlyMap<string, ReadonlyArray<string>>
|
|
49
49
|
cssFilesBySourcePath: ReadonlyMap<string, ReadonlyArray<string>>
|
|
50
|
+
cssContentByFileName?: ReadonlyMap<string, string>
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
export interface TanStackStartCoreOptions {
|
package/src/vite/planning.ts
CHANGED
|
@@ -136,6 +136,7 @@ export function createViteDefineConfig(opts: {
|
|
|
136
136
|
spaEnabled: boolean | undefined
|
|
137
137
|
devSsrStylesEnabled: boolean
|
|
138
138
|
devSsrStylesBasepath: string
|
|
139
|
+
inlineCssEnabled: boolean
|
|
139
140
|
staticNodeEnv: boolean
|
|
140
141
|
}) {
|
|
141
142
|
return {
|
|
@@ -156,6 +157,10 @@ export function createViteDefineConfig(opts: {
|
|
|
156
157
|
'TSS_DEV_SSR_STYLES_BASEPATH',
|
|
157
158
|
opts.devSsrStylesBasepath,
|
|
158
159
|
),
|
|
160
|
+
...defineReplaceEnv(
|
|
161
|
+
'TSS_INLINE_CSS_ENABLED',
|
|
162
|
+
opts.inlineCssEnabled ? 'true' : 'false',
|
|
163
|
+
),
|
|
159
164
|
...(opts.command === 'build' && opts.staticNodeEnv
|
|
160
165
|
? {
|
|
161
166
|
'process.env.NODE_ENV': JSON.stringify(
|
package/src/vite/plugin.ts
CHANGED
|
@@ -185,6 +185,8 @@ export function tanStackStartVite(
|
|
|
185
185
|
devSsrStylesBasepath:
|
|
186
186
|
startConfig.dev.ssrStyles.basepath ??
|
|
187
187
|
resolvedStartConfig.basePaths.publicBase,
|
|
188
|
+
inlineCssEnabled:
|
|
189
|
+
command === 'build' && startConfig.server.build.inlineCss,
|
|
188
190
|
staticNodeEnv: startConfig.server.build.staticNodeEnv,
|
|
189
191
|
}),
|
|
190
192
|
builder: {
|