@tanstack/start-plugin-core 1.133.21 → 1.133.25
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/prerender.js +7 -2
- package/dist/esm/prerender.js.map +1 -1
- package/dist/esm/start-manifest-plugin/plugin.d.ts +0 -2
- package/dist/esm/start-manifest-plugin/plugin.js +22 -5
- package/dist/esm/start-manifest-plugin/plugin.js.map +1 -1
- package/package.json +7 -7
- package/src/prerender.ts +11 -2
- package/src/start-manifest-plugin/plugin.ts +26 -2
package/dist/esm/prerender.js
CHANGED
|
@@ -115,7 +115,7 @@ async function prerender({
|
|
|
115
115
|
const contentType = res.headers.get("content-type") || "";
|
|
116
116
|
const isImplicitHTML = !cleanPagePath.endsWith(".html") && contentType.includes("html");
|
|
117
117
|
const routeWithIndex = cleanPagePath.endsWith("/") ? cleanPagePath + "index" : cleanPagePath;
|
|
118
|
-
const htmlPath = cleanPagePath.endsWith("/") || prerenderOptions.autoSubfolderIndex ? joinURL(cleanPagePath, "index.html") : cleanPagePath + ".html";
|
|
118
|
+
const htmlPath = cleanPagePath.endsWith("/") || (prerenderOptions.autoSubfolderIndex ?? true) ? joinURL(cleanPagePath, "index.html") : cleanPagePath + ".html";
|
|
119
119
|
const filename = withoutBase(
|
|
120
120
|
isImplicitHTML ? htmlPath : routeWithIndex,
|
|
121
121
|
routerBasePath
|
|
@@ -177,10 +177,15 @@ async function writeBundleToDisk({
|
|
|
177
177
|
bundle,
|
|
178
178
|
outDir
|
|
179
179
|
}) {
|
|
180
|
+
const createdDirs = /* @__PURE__ */ new Set();
|
|
180
181
|
for (const [fileName, asset] of Object.entries(bundle)) {
|
|
181
182
|
const fullPath = path.join(outDir, fileName);
|
|
183
|
+
const dir = path.dirname(fullPath);
|
|
182
184
|
const content = asset.type === "asset" ? asset.source : asset.code;
|
|
183
|
-
|
|
185
|
+
if (!createdDirs.has(dir)) {
|
|
186
|
+
await promises.mkdir(dir, { recursive: true });
|
|
187
|
+
createdDirs.add(dir);
|
|
188
|
+
}
|
|
184
189
|
await promises.writeFile(fullPath, content);
|
|
185
190
|
}
|
|
186
191
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prerender.js","sources":["../../src/prerender.ts"],"sourcesContent":["import { existsSync, promises as fsp, rmSync } from 'node:fs'\nimport { pathToFileURL } from 'node:url'\nimport os from 'node:os'\nimport path from 'pathe'\nimport { joinURL, withBase, withoutBase } from 'ufo'\nimport { VITE_ENVIRONMENT_NAMES } from './constants'\nimport { createLogger } from './utils'\nimport { Queue } from './queue'\nimport type { Rollup, ViteBuilder } from 'vite'\nimport type { Page, TanStackStartOutputConfig } from './schema'\n\nexport async function prerender({\n startConfig,\n builder,\n serverBundle,\n}: {\n startConfig: TanStackStartOutputConfig\n builder: ViteBuilder\n serverBundle: Rollup.OutputBundle\n}) {\n const logger = createLogger('prerender')\n logger.info('Prerendering pages...')\n\n // If prerender is enabled but no pages are provided, default to prerendering the root page\n if (startConfig.prerender?.enabled && !startConfig.pages.length) {\n startConfig.pages = [\n {\n path: '/',\n },\n ]\n }\n\n const serverEnv = builder.environments[VITE_ENVIRONMENT_NAMES.server]\n\n if (!serverEnv) {\n throw new Error(\n `Vite's \"${VITE_ENVIRONMENT_NAMES.server}\" environment not found`,\n )\n }\n\n const clientEnv = builder.environments[VITE_ENVIRONMENT_NAMES.client]\n if (!clientEnv) {\n throw new Error(\n `Vite's \"${VITE_ENVIRONMENT_NAMES.client}\" environment not found`,\n )\n }\n\n const outputDir = clientEnv.config.build.outDir\n\n const entryFile = findEntryFileInBundle(serverBundle)\n let fullEntryFilePath = path.join(serverEnv.config.build.outDir, entryFile)\n process.env.TSS_PRERENDERING = 'true'\n\n if (!existsSync(fullEntryFilePath)) {\n // if the file does not exist, we need to write the bundle to a temporary directory\n // this can happen e.g. with nitro that postprocesses the bundle and thus does not write SSR build to disk\n const bundleOutputDir = path.resolve(\n serverEnv.config.root,\n '.tanstack',\n 'start',\n 'prerender',\n )\n rmSync(bundleOutputDir, { recursive: true, force: true })\n await writeBundleToDisk({ bundle: serverBundle, outDir: bundleOutputDir })\n fullEntryFilePath = path.join(bundleOutputDir, entryFile)\n }\n\n const { default: serverEntrypoint } = await import(\n pathToFileURL(fullEntryFilePath).toString()\n )\n\n function localFetch(path: string, options?: RequestInit): Promise<Response> {\n const url = new URL(`http://localhost${path}`)\n return serverEntrypoint.fetch(new Request(url, options))\n }\n\n try {\n // Crawl all pages\n const pages = await prerenderPages({ outputDir })\n\n logger.info(`Prerendered ${pages.length} pages:`)\n pages.forEach((page) => {\n logger.info(`- ${page}`)\n })\n } catch (error) {\n logger.error(error)\n }\n\n function extractLinks(html: string): Array<string> {\n const linkRegex = /<a[^>]+href=[\"']([^\"']+)[\"'][^>]*>/g\n const links: Array<string> = []\n let match\n\n while ((match = linkRegex.exec(html)) !== null) {\n const href = match[1]\n if (href && (href.startsWith('/') || href.startsWith('./'))) {\n links.push(href)\n }\n }\n\n return links\n }\n\n async function prerenderPages({ outputDir }: { outputDir: string }) {\n const seen = new Set<string>()\n const retriesByPath = new Map<string, number>()\n const concurrency = startConfig.prerender?.concurrency ?? os.cpus().length\n logger.info(`Concurrency: ${concurrency}`)\n const queue = new Queue({ concurrency })\n const routerBasePath = joinURL('/', startConfig.router.basepath ?? '')\n\n startConfig.pages.forEach((page) => addCrawlPageTask(page))\n\n await queue.start()\n\n return Array.from(seen)\n\n function addCrawlPageTask(page: Page) {\n // Was the page already seen?\n if (seen.has(page.path)) return\n\n // Add the page to the seen set\n seen.add(page.path)\n\n if (page.fromCrawl) {\n startConfig.pages.push(page)\n }\n\n // If not enabled, skip\n if (!(page.prerender?.enabled ?? true)) return\n\n // If there is a filter link, check if the page should be prerendered\n if (startConfig.prerender?.filter && !startConfig.prerender.filter(page))\n return\n\n // Resolve the merged default and page-specific prerender options\n const prerenderOptions = {\n ...startConfig.prerender,\n ...page.prerender,\n }\n\n // Add the task\n queue.add(async () => {\n logger.info(`Crawling: ${page.path}`)\n const retries = retriesByPath.get(page.path) || 0\n try {\n // Fetch the route\n const encodedRoute = encodeURI(page.path)\n\n const res = await localFetch(withBase(encodedRoute, routerBasePath), {\n headers: {\n ...prerenderOptions.headers,\n },\n })\n\n if (!res.ok) {\n throw new Error(`Failed to fetch ${page.path}: ${res.statusText}`, {\n cause: res,\n })\n }\n\n const cleanPagePath = (\n prerenderOptions.outputPath || page.path\n ).split(/[?#]/)[0]!\n\n // Guess route type and populate fileName\n const contentType = res.headers.get('content-type') || ''\n const isImplicitHTML =\n !cleanPagePath.endsWith('.html') && contentType.includes('html')\n // &&\n // !JsonSigRx.test(dataBuff.subarray(0, 32).toString('utf8'))\n const routeWithIndex = cleanPagePath.endsWith('/')\n ? cleanPagePath + 'index'\n : cleanPagePath\n\n const htmlPath =\n cleanPagePath.endsWith('/') || prerenderOptions.autoSubfolderIndex\n ? joinURL(cleanPagePath, 'index.html')\n : cleanPagePath + '.html'\n\n const filename = withoutBase(\n isImplicitHTML ? htmlPath : routeWithIndex,\n routerBasePath,\n )\n\n const html = await res.text()\n\n const filepath = path.join(outputDir, filename)\n\n await fsp.mkdir(path.dirname(filepath), {\n recursive: true,\n })\n\n await fsp.writeFile(filepath, html)\n\n const newPage = await prerenderOptions.onSuccess?.({ page, html })\n\n if (newPage) {\n Object.assign(page, newPage)\n }\n\n // Find new links\n if (prerenderOptions.crawlLinks ?? true) {\n const links = extractLinks(html)\n for (const link of links) {\n addCrawlPageTask({ path: link, fromCrawl: true })\n }\n }\n } catch (error) {\n if (retries < (prerenderOptions.retryCount ?? 0)) {\n logger.warn(`Encountered error, retrying: ${page.path} in 500ms`)\n await new Promise((resolve) =>\n setTimeout(resolve, prerenderOptions.retryDelay),\n )\n retriesByPath.set(page.path, retries + 1)\n addCrawlPageTask(page)\n } else {\n if (prerenderOptions.failOnError ?? true) {\n throw error\n }\n }\n }\n })\n }\n }\n}\n\nfunction findEntryFileInBundle(bundle: Rollup.OutputBundle): string {\n let entryFile: string | undefined\n\n for (const [_name, file] of Object.entries(bundle)) {\n if (file.type === 'chunk') {\n if (file.isEntry) {\n if (entryFile !== undefined) {\n throw new Error(\n `Multiple entry points found. Only one entry point is allowed.`,\n )\n }\n entryFile = file.fileName\n }\n }\n }\n if (entryFile === undefined) {\n throw new Error(`No entry point found in the bundle.`)\n }\n return entryFile\n}\n\nexport async function writeBundleToDisk({\n bundle,\n outDir,\n}: {\n bundle: Rollup.OutputBundle\n outDir: string\n}) {\n for (const [fileName, asset] of Object.entries(bundle)) {\n const fullPath = path.join(outDir, fileName)\n const content = asset.type === 'asset' ? asset.source : asset.code\n await fsp.mkdir(path.dirname(fullPath), { recursive: true })\n await fsp.writeFile(fullPath, content)\n }\n}\n"],"names":["path","outputDir","fsp"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"prerender.js","sources":["../../src/prerender.ts"],"sourcesContent":["import { existsSync, promises as fsp, rmSync } from 'node:fs'\nimport { pathToFileURL } from 'node:url'\nimport os from 'node:os'\nimport path from 'pathe'\nimport { joinURL, withBase, withoutBase } from 'ufo'\nimport { VITE_ENVIRONMENT_NAMES } from './constants'\nimport { createLogger } from './utils'\nimport { Queue } from './queue'\nimport type { Rollup, ViteBuilder } from 'vite'\nimport type { Page, TanStackStartOutputConfig } from './schema'\n\nexport async function prerender({\n startConfig,\n builder,\n serverBundle,\n}: {\n startConfig: TanStackStartOutputConfig\n builder: ViteBuilder\n serverBundle: Rollup.OutputBundle\n}) {\n const logger = createLogger('prerender')\n logger.info('Prerendering pages...')\n\n // If prerender is enabled but no pages are provided, default to prerendering the root page\n if (startConfig.prerender?.enabled && !startConfig.pages.length) {\n startConfig.pages = [\n {\n path: '/',\n },\n ]\n }\n\n const serverEnv = builder.environments[VITE_ENVIRONMENT_NAMES.server]\n\n if (!serverEnv) {\n throw new Error(\n `Vite's \"${VITE_ENVIRONMENT_NAMES.server}\" environment not found`,\n )\n }\n\n const clientEnv = builder.environments[VITE_ENVIRONMENT_NAMES.client]\n if (!clientEnv) {\n throw new Error(\n `Vite's \"${VITE_ENVIRONMENT_NAMES.client}\" environment not found`,\n )\n }\n\n const outputDir = clientEnv.config.build.outDir\n\n const entryFile = findEntryFileInBundle(serverBundle)\n let fullEntryFilePath = path.join(serverEnv.config.build.outDir, entryFile)\n process.env.TSS_PRERENDERING = 'true'\n\n if (!existsSync(fullEntryFilePath)) {\n // if the file does not exist, we need to write the bundle to a temporary directory\n // this can happen e.g. with nitro that postprocesses the bundle and thus does not write SSR build to disk\n const bundleOutputDir = path.resolve(\n serverEnv.config.root,\n '.tanstack',\n 'start',\n 'prerender',\n )\n rmSync(bundleOutputDir, { recursive: true, force: true })\n await writeBundleToDisk({ bundle: serverBundle, outDir: bundleOutputDir })\n fullEntryFilePath = path.join(bundleOutputDir, entryFile)\n }\n\n const { default: serverEntrypoint } = await import(\n pathToFileURL(fullEntryFilePath).toString()\n )\n\n function localFetch(path: string, options?: RequestInit): Promise<Response> {\n const url = new URL(`http://localhost${path}`)\n return serverEntrypoint.fetch(new Request(url, options))\n }\n\n try {\n // Crawl all pages\n const pages = await prerenderPages({ outputDir })\n\n logger.info(`Prerendered ${pages.length} pages:`)\n pages.forEach((page) => {\n logger.info(`- ${page}`)\n })\n } catch (error) {\n logger.error(error)\n }\n\n function extractLinks(html: string): Array<string> {\n const linkRegex = /<a[^>]+href=[\"']([^\"']+)[\"'][^>]*>/g\n const links: Array<string> = []\n let match\n\n while ((match = linkRegex.exec(html)) !== null) {\n const href = match[1]\n if (href && (href.startsWith('/') || href.startsWith('./'))) {\n links.push(href)\n }\n }\n\n return links\n }\n\n async function prerenderPages({ outputDir }: { outputDir: string }) {\n const seen = new Set<string>()\n const retriesByPath = new Map<string, number>()\n const concurrency = startConfig.prerender?.concurrency ?? os.cpus().length\n logger.info(`Concurrency: ${concurrency}`)\n const queue = new Queue({ concurrency })\n const routerBasePath = joinURL('/', startConfig.router.basepath ?? '')\n\n startConfig.pages.forEach((page) => addCrawlPageTask(page))\n\n await queue.start()\n\n return Array.from(seen)\n\n function addCrawlPageTask(page: Page) {\n // Was the page already seen?\n if (seen.has(page.path)) return\n\n // Add the page to the seen set\n seen.add(page.path)\n\n if (page.fromCrawl) {\n startConfig.pages.push(page)\n }\n\n // If not enabled, skip\n if (!(page.prerender?.enabled ?? true)) return\n\n // If there is a filter link, check if the page should be prerendered\n if (startConfig.prerender?.filter && !startConfig.prerender.filter(page))\n return\n\n // Resolve the merged default and page-specific prerender options\n const prerenderOptions = {\n ...startConfig.prerender,\n ...page.prerender,\n }\n\n // Add the task\n queue.add(async () => {\n logger.info(`Crawling: ${page.path}`)\n const retries = retriesByPath.get(page.path) || 0\n try {\n // Fetch the route\n const encodedRoute = encodeURI(page.path)\n\n const res = await localFetch(withBase(encodedRoute, routerBasePath), {\n headers: {\n ...prerenderOptions.headers,\n },\n })\n\n if (!res.ok) {\n throw new Error(`Failed to fetch ${page.path}: ${res.statusText}`, {\n cause: res,\n })\n }\n\n const cleanPagePath = (\n prerenderOptions.outputPath || page.path\n ).split(/[?#]/)[0]!\n\n // Guess route type and populate fileName\n const contentType = res.headers.get('content-type') || ''\n const isImplicitHTML =\n !cleanPagePath.endsWith('.html') && contentType.includes('html')\n // &&\n // !JsonSigRx.test(dataBuff.subarray(0, 32).toString('utf8'))\n const routeWithIndex = cleanPagePath.endsWith('/')\n ? cleanPagePath + 'index'\n : cleanPagePath\n\n const htmlPath =\n cleanPagePath.endsWith('/') ||\n (prerenderOptions.autoSubfolderIndex ?? true)\n ? joinURL(cleanPagePath, 'index.html')\n : cleanPagePath + '.html'\n\n const filename = withoutBase(\n isImplicitHTML ? htmlPath : routeWithIndex,\n routerBasePath,\n )\n\n const html = await res.text()\n\n const filepath = path.join(outputDir, filename)\n\n await fsp.mkdir(path.dirname(filepath), {\n recursive: true,\n })\n\n await fsp.writeFile(filepath, html)\n\n const newPage = await prerenderOptions.onSuccess?.({ page, html })\n\n if (newPage) {\n Object.assign(page, newPage)\n }\n\n // Find new links\n if (prerenderOptions.crawlLinks ?? true) {\n const links = extractLinks(html)\n for (const link of links) {\n addCrawlPageTask({ path: link, fromCrawl: true })\n }\n }\n } catch (error) {\n if (retries < (prerenderOptions.retryCount ?? 0)) {\n logger.warn(`Encountered error, retrying: ${page.path} in 500ms`)\n await new Promise((resolve) =>\n setTimeout(resolve, prerenderOptions.retryDelay),\n )\n retriesByPath.set(page.path, retries + 1)\n addCrawlPageTask(page)\n } else {\n if (prerenderOptions.failOnError ?? true) {\n throw error\n }\n }\n }\n })\n }\n }\n}\n\nfunction findEntryFileInBundle(bundle: Rollup.OutputBundle): string {\n let entryFile: string | undefined\n\n for (const [_name, file] of Object.entries(bundle)) {\n if (file.type === 'chunk') {\n if (file.isEntry) {\n if (entryFile !== undefined) {\n throw new Error(\n `Multiple entry points found. Only one entry point is allowed.`,\n )\n }\n entryFile = file.fileName\n }\n }\n }\n if (entryFile === undefined) {\n throw new Error(`No entry point found in the bundle.`)\n }\n return entryFile\n}\n\nexport async function writeBundleToDisk({\n bundle,\n outDir,\n}: {\n bundle: Rollup.OutputBundle\n outDir: string\n}) {\n const createdDirs = new Set<string>()\n\n for (const [fileName, asset] of Object.entries(bundle)) {\n const fullPath = path.join(outDir, fileName)\n const dir = path.dirname(fullPath)\n const content = asset.type === 'asset' ? asset.source : asset.code\n\n if (!createdDirs.has(dir)) {\n await fsp.mkdir(dir, { recursive: true })\n createdDirs.add(dir)\n }\n\n await fsp.writeFile(fullPath, content)\n }\n}\n"],"names":["path","outputDir","fsp"],"mappings":"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"}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { GetConfigFn } from '../plugin.js';
|
|
2
2
|
import { PluginOption, Rollup } from 'vite';
|
|
3
|
-
import { RouterManagedTag } from '@tanstack/router-core';
|
|
4
|
-
export declare const getCSSRecursively: (chunk: Rollup.OutputChunk, chunksByFileName: Map<string, Rollup.OutputChunk>, basePath: string) => RouterManagedTag[];
|
|
5
3
|
export declare function startManifestPlugin(opts: {
|
|
6
4
|
getClientBundle: () => Rollup.OutputBundle;
|
|
7
5
|
getConfig: GetConfigFn;
|
|
@@ -4,7 +4,15 @@ import { VIRTUAL_MODULES } from "@tanstack/start-server-core";
|
|
|
4
4
|
import { tsrSplit } from "@tanstack/router-plugin";
|
|
5
5
|
import { resolveViteId } from "../utils.js";
|
|
6
6
|
import { ENTRY_POINTS } from "../constants.js";
|
|
7
|
-
const getCSSRecursively = (chunk, chunksByFileName, basePath) => {
|
|
7
|
+
const getCSSRecursively = (chunk, chunksByFileName, basePath, cache, visited = /* @__PURE__ */ new Set()) => {
|
|
8
|
+
if (visited.has(chunk)) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
visited.add(chunk);
|
|
12
|
+
const cachedResult = cache.get(chunk);
|
|
13
|
+
if (cachedResult) {
|
|
14
|
+
return cachedResult;
|
|
15
|
+
}
|
|
8
16
|
const result = [];
|
|
9
17
|
for (const cssFile of chunk.viteMetadata?.importedCss ?? []) {
|
|
10
18
|
result.push({
|
|
@@ -20,10 +28,17 @@ const getCSSRecursively = (chunk, chunksByFileName, basePath) => {
|
|
|
20
28
|
const importedChunk = chunksByFileName.get(importedFileName);
|
|
21
29
|
if (importedChunk) {
|
|
22
30
|
result.push(
|
|
23
|
-
...getCSSRecursively(
|
|
31
|
+
...getCSSRecursively(
|
|
32
|
+
importedChunk,
|
|
33
|
+
chunksByFileName,
|
|
34
|
+
basePath,
|
|
35
|
+
cache,
|
|
36
|
+
visited
|
|
37
|
+
)
|
|
24
38
|
);
|
|
25
39
|
}
|
|
26
40
|
}
|
|
41
|
+
cache.set(chunk, result);
|
|
27
42
|
return result;
|
|
28
43
|
};
|
|
29
44
|
const resolvedModuleId = resolveViteId(VIRTUAL_MODULES.startManifest);
|
|
@@ -57,6 +72,7 @@ function startManifestPlugin(opts) {
|
|
|
57
72
|
})`;
|
|
58
73
|
}
|
|
59
74
|
const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST.routes;
|
|
75
|
+
const cssPerChunkCache = /* @__PURE__ */ new Map();
|
|
60
76
|
let entryFile;
|
|
61
77
|
const clientBundle = opts.getClientBundle();
|
|
62
78
|
const chunksByFileName = /* @__PURE__ */ new Map();
|
|
@@ -119,7 +135,8 @@ function startManifestPlugin(opts) {
|
|
|
119
135
|
const cssAssetsList = getCSSRecursively(
|
|
120
136
|
chunk,
|
|
121
137
|
chunksByFileName,
|
|
122
|
-
resolvedStartConfig.viteAppBase
|
|
138
|
+
resolvedStartConfig.viteAppBase,
|
|
139
|
+
cssPerChunkCache
|
|
123
140
|
);
|
|
124
141
|
routeTreeRoutes[routeId] = {
|
|
125
142
|
...v,
|
|
@@ -141,7 +158,8 @@ function startManifestPlugin(opts) {
|
|
|
141
158
|
const entryCssAssetsList = getCSSRecursively(
|
|
142
159
|
entryFile,
|
|
143
160
|
chunksByFileName,
|
|
144
|
-
resolvedStartConfig.viteAppBase
|
|
161
|
+
resolvedStartConfig.viteAppBase,
|
|
162
|
+
cssPerChunkCache
|
|
145
163
|
);
|
|
146
164
|
routeTreeRoutes[rootRouteId].assets = [
|
|
147
165
|
...routeTreeRoutes[rootRouteId].assets || [],
|
|
@@ -178,7 +196,6 @@ function startManifestPlugin(opts) {
|
|
|
178
196
|
};
|
|
179
197
|
}
|
|
180
198
|
export {
|
|
181
|
-
getCSSRecursively,
|
|
182
199
|
startManifestPlugin
|
|
183
200
|
};
|
|
184
201
|
//# sourceMappingURL=plugin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["../../../src/start-manifest-plugin/plugin.ts"],"sourcesContent":["import { joinURL } from 'ufo'\nimport { rootRouteId } from '@tanstack/router-core'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { tsrSplit } from '@tanstack/router-plugin'\nimport { resolveViteId } from '../utils'\nimport { ENTRY_POINTS } from '../constants'\nimport type { GetConfigFn } from '../plugin'\nimport type { PluginOption, Rollup } from 'vite'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\nexport const getCSSRecursively = (\n chunk: Rollup.OutputChunk,\n chunksByFileName: Map<string, Rollup.OutputChunk>,\n basePath: string,\n) => {\n const result: Array<RouterManagedTag> = []\n\n // Get all css imports from the file\n for (const cssFile of chunk.viteMetadata?.importedCss ?? []) {\n result.push({\n tag: 'link',\n attrs: {\n rel: 'stylesheet',\n href: joinURL(basePath, cssFile),\n type: 'text/css',\n },\n })\n }\n\n // Recursively get CSS from imports\n for (const importedFileName of chunk.imports) {\n const importedChunk = chunksByFileName.get(importedFileName)\n if (importedChunk) {\n result.push(\n ...getCSSRecursively(importedChunk, chunksByFileName, basePath),\n )\n }\n }\n\n return result\n}\n\nconst resolvedModuleId = resolveViteId(VIRTUAL_MODULES.startManifest)\nexport function startManifestPlugin(opts: {\n getClientBundle: () => Rollup.OutputBundle\n getConfig: GetConfigFn\n}): PluginOption {\n return {\n name: 'tanstack-start:start-manifest-plugin',\n enforce: 'pre',\n resolveId: {\n filter: { id: new RegExp(VIRTUAL_MODULES.startManifest) },\n handler(id) {\n if (id === VIRTUAL_MODULES.startManifest) {\n return resolvedModuleId\n }\n return undefined\n },\n },\n load: {\n filter: {\n id: new RegExp(resolvedModuleId),\n },\n handler(id) {\n const { resolvedStartConfig } = opts.getConfig()\n if (id === resolvedModuleId) {\n if (this.environment.config.consumer !== 'server') {\n // this will ultimately fail the build if the plugin is used outside the server environment\n // TODO: do we need special handling for `serve`?\n return `export default {}`\n }\n\n // If we're in development, return a dummy manifest\n if (this.environment.config.command === 'serve') {\n return `export const tsrStartManifest = () => ({\n routes: {},\n clientEntry: '${joinURL(resolvedStartConfig.viteAppBase, '@id', ENTRY_POINTS.client)}',\n })`\n }\n\n // This the manifest pulled from the generated route tree and later used by the Router.\n // i.e what's located in `src/routeTree.gen.ts`\n const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST.routes\n\n // This is where hydration will start, from when the SSR'd page reaches the browser.\n let entryFile: Rollup.OutputChunk | undefined\n\n const clientBundle = opts.getClientBundle()\n const chunksByFileName = new Map<string, Rollup.OutputChunk>()\n\n const routeChunks: Record<\n string /** fullPath of route file **/,\n Array<Rollup.OutputChunk>\n > = {}\n for (const bundleEntry of Object.values(clientBundle)) {\n if (bundleEntry.type === 'chunk') {\n chunksByFileName.set(bundleEntry.fileName, bundleEntry)\n if (bundleEntry.isEntry) {\n if (entryFile) {\n throw new Error(\n `multiple entries detected: ${entryFile.fileName} ${bundleEntry.fileName}`,\n )\n }\n entryFile = bundleEntry\n }\n const routePieces = bundleEntry.moduleIds.flatMap((m) => {\n const [id, query] = m.split('?')\n if (id === undefined) {\n throw new Error('expected id to be defined')\n }\n if (query === undefined) {\n return []\n }\n const searchParams = new URLSearchParams(query)\n const split = searchParams.get(tsrSplit)\n\n if (split !== null) {\n return {\n id,\n split,\n }\n }\n return []\n })\n if (routePieces.length > 0) {\n routePieces.forEach((r) => {\n let array = routeChunks[r.id]\n if (array === undefined) {\n array = []\n routeChunks[r.id] = array\n }\n array.push(bundleEntry)\n })\n }\n }\n }\n\n // Add preloads to the routes from the vite manifest\n Object.entries(routeTreeRoutes).forEach(([routeId, v]) => {\n if (!v.filePath) {\n throw new Error(`expected filePath to be set for ${routeId}`)\n }\n const chunks = routeChunks[v.filePath]\n if (chunks) {\n chunks.forEach((chunk) => {\n // Map the relevant imports to their route paths,\n // so that it can be imported in the browser.\n const preloads = chunk.imports.map((d) => {\n const assetPath = joinURL(resolvedStartConfig.viteAppBase, d)\n return assetPath\n })\n\n // Since this is the most important JS entry for the route,\n // it should be moved to the front of the preloads so that\n // it has the best chance of being loaded first.\n preloads.unshift(\n joinURL(resolvedStartConfig.viteAppBase, chunk.fileName),\n )\n\n const cssAssetsList = getCSSRecursively(\n chunk,\n chunksByFileName,\n resolvedStartConfig.viteAppBase,\n )\n\n routeTreeRoutes[routeId] = {\n ...v,\n assets: [...(v.assets || []), ...cssAssetsList],\n preloads: [...(v.preloads || []), ...preloads],\n }\n })\n }\n })\n\n if (!entryFile) {\n throw new Error('No entry file found')\n }\n routeTreeRoutes[rootRouteId]!.preloads = [\n joinURL(resolvedStartConfig.viteAppBase, entryFile.fileName),\n ...entryFile.imports.map((d) =>\n joinURL(resolvedStartConfig.viteAppBase, d),\n ),\n ]\n\n // Gather all the CSS files from the entry file in\n // the `css` key and add them to the root route\n const entryCssAssetsList = getCSSRecursively(\n entryFile,\n chunksByFileName,\n resolvedStartConfig.viteAppBase,\n )\n\n routeTreeRoutes[rootRouteId]!.assets = [\n ...(routeTreeRoutes[rootRouteId]!.assets || []),\n ...entryCssAssetsList,\n ]\n\n const recurseRoute = (\n route: {\n preloads?: Array<string>\n children?: Array<any>\n },\n seenPreloads = {} as Record<string, true>,\n ) => {\n route.preloads = route.preloads?.filter((preload) => {\n if (seenPreloads[preload]) {\n return false\n }\n seenPreloads[preload] = true\n return true\n })\n\n if (route.children) {\n route.children.forEach((child) => {\n const childRoute = routeTreeRoutes[child]!\n recurseRoute(childRoute, { ...seenPreloads })\n })\n }\n }\n\n recurseRoute(routeTreeRoutes[rootRouteId]!)\n\n const startManifest = {\n routes: routeTreeRoutes,\n clientEntry: joinURL(\n resolvedStartConfig.viteAppBase,\n entryFile.fileName,\n ),\n }\n\n return `export const tsrStartManifest = () => (${JSON.stringify(startManifest)})`\n }\n\n return undefined\n },\n },\n }\n}\n"],"names":["id"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["../../../src/start-manifest-plugin/plugin.ts"],"sourcesContent":["import { joinURL } from 'ufo'\nimport { rootRouteId } from '@tanstack/router-core'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { tsrSplit } from '@tanstack/router-plugin'\nimport { resolveViteId } from '../utils'\nimport { ENTRY_POINTS } from '../constants'\nimport type { GetConfigFn } from '../plugin'\nimport type { PluginOption, Rollup } from 'vite'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\nconst getCSSRecursively = (\n chunk: Rollup.OutputChunk,\n chunksByFileName: Map<string, Rollup.OutputChunk>,\n basePath: string,\n cache: Map<Rollup.OutputChunk, Array<RouterManagedTag>>,\n visited = new Set<Rollup.OutputChunk>(),\n) => {\n if (visited.has(chunk)) {\n return []\n }\n visited.add(chunk)\n const cachedResult = cache.get(chunk)\n if (cachedResult) {\n return cachedResult\n }\n const result: Array<RouterManagedTag> = []\n\n // Get all css imports from the file\n for (const cssFile of chunk.viteMetadata?.importedCss ?? []) {\n result.push({\n tag: 'link',\n attrs: {\n rel: 'stylesheet',\n href: joinURL(basePath, cssFile),\n type: 'text/css',\n },\n })\n }\n\n // Recursively get CSS from imports\n for (const importedFileName of chunk.imports) {\n const importedChunk = chunksByFileName.get(importedFileName)\n if (importedChunk) {\n result.push(\n ...getCSSRecursively(\n importedChunk,\n chunksByFileName,\n basePath,\n cache,\n visited,\n ),\n )\n }\n }\n\n cache.set(chunk, result)\n return result\n}\n\nconst resolvedModuleId = resolveViteId(VIRTUAL_MODULES.startManifest)\nexport function startManifestPlugin(opts: {\n getClientBundle: () => Rollup.OutputBundle\n getConfig: GetConfigFn\n}): PluginOption {\n return {\n name: 'tanstack-start:start-manifest-plugin',\n enforce: 'pre',\n resolveId: {\n filter: { id: new RegExp(VIRTUAL_MODULES.startManifest) },\n handler(id) {\n if (id === VIRTUAL_MODULES.startManifest) {\n return resolvedModuleId\n }\n return undefined\n },\n },\n load: {\n filter: {\n id: new RegExp(resolvedModuleId),\n },\n handler(id) {\n const { resolvedStartConfig } = opts.getConfig()\n if (id === resolvedModuleId) {\n if (this.environment.config.consumer !== 'server') {\n // this will ultimately fail the build if the plugin is used outside the server environment\n // TODO: do we need special handling for `serve`?\n return `export default {}`\n }\n\n // If we're in development, return a dummy manifest\n if (this.environment.config.command === 'serve') {\n return `export const tsrStartManifest = () => ({\n routes: {},\n clientEntry: '${joinURL(resolvedStartConfig.viteAppBase, '@id', ENTRY_POINTS.client)}',\n })`\n }\n\n // This the manifest pulled from the generated route tree and later used by the Router.\n // i.e what's located in `src/routeTree.gen.ts`\n const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST.routes\n\n const cssPerChunkCache = new Map<\n Rollup.OutputChunk,\n Array<RouterManagedTag>\n >()\n\n // This is where hydration will start, from when the SSR'd page reaches the browser.\n let entryFile: Rollup.OutputChunk | undefined\n\n const clientBundle = opts.getClientBundle()\n const chunksByFileName = new Map<string, Rollup.OutputChunk>()\n\n const routeChunks: Record<\n string /** fullPath of route file **/,\n Array<Rollup.OutputChunk>\n > = {}\n for (const bundleEntry of Object.values(clientBundle)) {\n if (bundleEntry.type === 'chunk') {\n chunksByFileName.set(bundleEntry.fileName, bundleEntry)\n if (bundleEntry.isEntry) {\n if (entryFile) {\n throw new Error(\n `multiple entries detected: ${entryFile.fileName} ${bundleEntry.fileName}`,\n )\n }\n entryFile = bundleEntry\n }\n const routePieces = bundleEntry.moduleIds.flatMap((m) => {\n const [id, query] = m.split('?')\n if (id === undefined) {\n throw new Error('expected id to be defined')\n }\n if (query === undefined) {\n return []\n }\n const searchParams = new URLSearchParams(query)\n const split = searchParams.get(tsrSplit)\n\n if (split !== null) {\n return {\n id,\n split,\n }\n }\n return []\n })\n if (routePieces.length > 0) {\n routePieces.forEach((r) => {\n let array = routeChunks[r.id]\n if (array === undefined) {\n array = []\n routeChunks[r.id] = array\n }\n array.push(bundleEntry)\n })\n }\n }\n }\n\n // Add preloads to the routes from the vite manifest\n Object.entries(routeTreeRoutes).forEach(([routeId, v]) => {\n if (!v.filePath) {\n throw new Error(`expected filePath to be set for ${routeId}`)\n }\n const chunks = routeChunks[v.filePath]\n if (chunks) {\n chunks.forEach((chunk) => {\n // Map the relevant imports to their route paths,\n // so that it can be imported in the browser.\n const preloads = chunk.imports.map((d) => {\n const assetPath = joinURL(resolvedStartConfig.viteAppBase, d)\n return assetPath\n })\n\n // Since this is the most important JS entry for the route,\n // it should be moved to the front of the preloads so that\n // it has the best chance of being loaded first.\n preloads.unshift(\n joinURL(resolvedStartConfig.viteAppBase, chunk.fileName),\n )\n\n const cssAssetsList = getCSSRecursively(\n chunk,\n chunksByFileName,\n resolvedStartConfig.viteAppBase,\n cssPerChunkCache,\n )\n\n routeTreeRoutes[routeId] = {\n ...v,\n assets: [...(v.assets || []), ...cssAssetsList],\n preloads: [...(v.preloads || []), ...preloads],\n }\n })\n }\n })\n\n if (!entryFile) {\n throw new Error('No entry file found')\n }\n routeTreeRoutes[rootRouteId]!.preloads = [\n joinURL(resolvedStartConfig.viteAppBase, entryFile.fileName),\n ...entryFile.imports.map((d) =>\n joinURL(resolvedStartConfig.viteAppBase, d),\n ),\n ]\n\n // Gather all the CSS files from the entry file in\n // the `css` key and add them to the root route\n const entryCssAssetsList = getCSSRecursively(\n entryFile,\n chunksByFileName,\n resolvedStartConfig.viteAppBase,\n cssPerChunkCache,\n )\n\n routeTreeRoutes[rootRouteId]!.assets = [\n ...(routeTreeRoutes[rootRouteId]!.assets || []),\n ...entryCssAssetsList,\n ]\n\n const recurseRoute = (\n route: {\n preloads?: Array<string>\n children?: Array<any>\n },\n seenPreloads = {} as Record<string, true>,\n ) => {\n route.preloads = route.preloads?.filter((preload) => {\n if (seenPreloads[preload]) {\n return false\n }\n seenPreloads[preload] = true\n return true\n })\n\n if (route.children) {\n route.children.forEach((child) => {\n const childRoute = routeTreeRoutes[child]!\n recurseRoute(childRoute, { ...seenPreloads })\n })\n }\n }\n\n recurseRoute(routeTreeRoutes[rootRouteId]!)\n\n const startManifest = {\n routes: routeTreeRoutes,\n clientEntry: joinURL(\n resolvedStartConfig.viteAppBase,\n entryFile.fileName,\n ),\n }\n\n return `export const tsrStartManifest = () => (${JSON.stringify(startManifest)})`\n }\n\n return undefined\n },\n },\n }\n}\n"],"names":["id"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/start-plugin-core",
|
|
3
|
-
"version": "1.133.
|
|
3
|
+
"version": "1.133.25",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -59,13 +59,13 @@
|
|
|
59
59
|
"vitefu": "^1.1.1",
|
|
60
60
|
"xmlbuilder2": "^3.1.1",
|
|
61
61
|
"zod": "^3.24.2",
|
|
62
|
-
"@tanstack/router-core": "1.133.
|
|
62
|
+
"@tanstack/router-core": "1.133.25",
|
|
63
|
+
"@tanstack/router-plugin": "1.133.25",
|
|
64
|
+
"@tanstack/router-generator": "1.133.25",
|
|
63
65
|
"@tanstack/router-utils": "1.133.19",
|
|
64
|
-
"@tanstack/
|
|
65
|
-
"@tanstack/server-
|
|
66
|
-
"@tanstack/
|
|
67
|
-
"@tanstack/start-client-core": "1.133.20",
|
|
68
|
-
"@tanstack/start-server-core": "1.133.20"
|
|
66
|
+
"@tanstack/server-functions-plugin": "1.133.25",
|
|
67
|
+
"@tanstack/start-server-core": "1.133.25",
|
|
68
|
+
"@tanstack/start-client-core": "1.133.25"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/babel__code-frame": "^7.0.6",
|
package/src/prerender.ts
CHANGED
|
@@ -174,7 +174,8 @@ export async function prerender({
|
|
|
174
174
|
: cleanPagePath
|
|
175
175
|
|
|
176
176
|
const htmlPath =
|
|
177
|
-
cleanPagePath.endsWith('/') ||
|
|
177
|
+
cleanPagePath.endsWith('/') ||
|
|
178
|
+
(prerenderOptions.autoSubfolderIndex ?? true)
|
|
178
179
|
? joinURL(cleanPagePath, 'index.html')
|
|
179
180
|
: cleanPagePath + '.html'
|
|
180
181
|
|
|
@@ -253,10 +254,18 @@ export async function writeBundleToDisk({
|
|
|
253
254
|
bundle: Rollup.OutputBundle
|
|
254
255
|
outDir: string
|
|
255
256
|
}) {
|
|
257
|
+
const createdDirs = new Set<string>()
|
|
258
|
+
|
|
256
259
|
for (const [fileName, asset] of Object.entries(bundle)) {
|
|
257
260
|
const fullPath = path.join(outDir, fileName)
|
|
261
|
+
const dir = path.dirname(fullPath)
|
|
258
262
|
const content = asset.type === 'asset' ? asset.source : asset.code
|
|
259
|
-
|
|
263
|
+
|
|
264
|
+
if (!createdDirs.has(dir)) {
|
|
265
|
+
await fsp.mkdir(dir, { recursive: true })
|
|
266
|
+
createdDirs.add(dir)
|
|
267
|
+
}
|
|
268
|
+
|
|
260
269
|
await fsp.writeFile(fullPath, content)
|
|
261
270
|
}
|
|
262
271
|
}
|
|
@@ -8,11 +8,21 @@ import type { GetConfigFn } from '../plugin'
|
|
|
8
8
|
import type { PluginOption, Rollup } from 'vite'
|
|
9
9
|
import type { RouterManagedTag } from '@tanstack/router-core'
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
const getCSSRecursively = (
|
|
12
12
|
chunk: Rollup.OutputChunk,
|
|
13
13
|
chunksByFileName: Map<string, Rollup.OutputChunk>,
|
|
14
14
|
basePath: string,
|
|
15
|
+
cache: Map<Rollup.OutputChunk, Array<RouterManagedTag>>,
|
|
16
|
+
visited = new Set<Rollup.OutputChunk>(),
|
|
15
17
|
) => {
|
|
18
|
+
if (visited.has(chunk)) {
|
|
19
|
+
return []
|
|
20
|
+
}
|
|
21
|
+
visited.add(chunk)
|
|
22
|
+
const cachedResult = cache.get(chunk)
|
|
23
|
+
if (cachedResult) {
|
|
24
|
+
return cachedResult
|
|
25
|
+
}
|
|
16
26
|
const result: Array<RouterManagedTag> = []
|
|
17
27
|
|
|
18
28
|
// Get all css imports from the file
|
|
@@ -32,11 +42,18 @@ export const getCSSRecursively = (
|
|
|
32
42
|
const importedChunk = chunksByFileName.get(importedFileName)
|
|
33
43
|
if (importedChunk) {
|
|
34
44
|
result.push(
|
|
35
|
-
...getCSSRecursively(
|
|
45
|
+
...getCSSRecursively(
|
|
46
|
+
importedChunk,
|
|
47
|
+
chunksByFileName,
|
|
48
|
+
basePath,
|
|
49
|
+
cache,
|
|
50
|
+
visited,
|
|
51
|
+
),
|
|
36
52
|
)
|
|
37
53
|
}
|
|
38
54
|
}
|
|
39
55
|
|
|
56
|
+
cache.set(chunk, result)
|
|
40
57
|
return result
|
|
41
58
|
}
|
|
42
59
|
|
|
@@ -82,6 +99,11 @@ export function startManifestPlugin(opts: {
|
|
|
82
99
|
// i.e what's located in `src/routeTree.gen.ts`
|
|
83
100
|
const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST.routes
|
|
84
101
|
|
|
102
|
+
const cssPerChunkCache = new Map<
|
|
103
|
+
Rollup.OutputChunk,
|
|
104
|
+
Array<RouterManagedTag>
|
|
105
|
+
>()
|
|
106
|
+
|
|
85
107
|
// This is where hydration will start, from when the SSR'd page reaches the browser.
|
|
86
108
|
let entryFile: Rollup.OutputChunk | undefined
|
|
87
109
|
|
|
@@ -161,6 +183,7 @@ export function startManifestPlugin(opts: {
|
|
|
161
183
|
chunk,
|
|
162
184
|
chunksByFileName,
|
|
163
185
|
resolvedStartConfig.viteAppBase,
|
|
186
|
+
cssPerChunkCache,
|
|
164
187
|
)
|
|
165
188
|
|
|
166
189
|
routeTreeRoutes[routeId] = {
|
|
@@ -188,6 +211,7 @@ export function startManifestPlugin(opts: {
|
|
|
188
211
|
entryFile,
|
|
189
212
|
chunksByFileName,
|
|
190
213
|
resolvedStartConfig.viteAppBase,
|
|
214
|
+
cssPerChunkCache,
|
|
191
215
|
)
|
|
192
216
|
|
|
193
217
|
routeTreeRoutes[rootRouteId]!.assets = [
|