@tanstack/start-plugin-core 1.149.3 → 1.149.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.
|
@@ -12,3 +12,13 @@ export interface CollectDevStylesOptions {
|
|
|
12
12
|
* Collect CSS content from the module graph starting from the given entry points.
|
|
13
13
|
*/
|
|
14
14
|
export declare function collectDevStyles(opts: CollectDevStylesOptions): Promise<string | undefined>;
|
|
15
|
+
/**
|
|
16
|
+
* Extract CSS content from Vite's transformed CSS module code.
|
|
17
|
+
*
|
|
18
|
+
* Vite embeds CSS into the module as a JS string via `JSON.stringify(cssContent)`,
|
|
19
|
+
* e.g. `const __vite__css = ${JSON.stringify('...css...')}`.
|
|
20
|
+
*
|
|
21
|
+
* We locate that JSON string literal and run `JSON.parse` on it to reverse the
|
|
22
|
+
* escaping (\\n, \\t, \\", \\\\, \\uXXXX, etc.).
|
|
23
|
+
*/
|
|
24
|
+
export declare function extractCssFromCode(code: string): string | undefined;
|
|
@@ -6,7 +6,7 @@ function normalizeCssModuleCacheKey(idOrFile) {
|
|
|
6
6
|
return baseId.replace(/\\/g, "/");
|
|
7
7
|
}
|
|
8
8
|
const CSS_SIDE_EFFECT_FREE_PARAMS = ["url", "inline", "raw", "inline-css"];
|
|
9
|
-
const
|
|
9
|
+
const VITE_CSS_MARKER = "const __vite__css = ";
|
|
10
10
|
const ESCAPE_CSS_COMMENT_START_REGEX = /\/\*/g;
|
|
11
11
|
const ESCAPE_CSS_COMMENT_END_REGEX = /\*\//g;
|
|
12
12
|
function isCssFile(file) {
|
|
@@ -140,13 +140,33 @@ async function fetchCssFromModule(viteDevServer, node) {
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
function extractCssFromCode(code) {
|
|
143
|
-
const
|
|
144
|
-
if (
|
|
145
|
-
|
|
143
|
+
const startIdx = code.indexOf(VITE_CSS_MARKER);
|
|
144
|
+
if (startIdx === -1) return void 0;
|
|
145
|
+
const valueStart = startIdx + VITE_CSS_MARKER.length;
|
|
146
|
+
if (code.charCodeAt(valueStart) !== 34) return void 0;
|
|
147
|
+
const codeLength = code.length;
|
|
148
|
+
let i = valueStart + 1;
|
|
149
|
+
while (i < codeLength) {
|
|
150
|
+
const charCode = code.charCodeAt(i);
|
|
151
|
+
if (charCode === 34) {
|
|
152
|
+
try {
|
|
153
|
+
return JSON.parse(code.slice(valueStart, i + 1));
|
|
154
|
+
} catch {
|
|
155
|
+
return void 0;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (charCode === 92) {
|
|
159
|
+
i += 2;
|
|
160
|
+
} else {
|
|
161
|
+
i++;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return void 0;
|
|
146
165
|
}
|
|
147
166
|
export {
|
|
148
167
|
CSS_MODULES_REGEX,
|
|
149
168
|
collectDevStyles,
|
|
169
|
+
extractCssFromCode,
|
|
150
170
|
isCssModulesFile,
|
|
151
171
|
normalizeCssModuleCacheKey
|
|
152
172
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-styles.js","sources":["../../../src/dev-server-plugin/dev-styles.ts"],"sourcesContent":["/**\n * CSS collection for dev mode.\n * Crawls the Vite module graph to collect CSS from the router entry and all its dependencies.\n */\nimport path from 'node:path'\nimport type { ModuleNode, ViteDevServer } from 'vite'\n\n// CSS file extensions supported by Vite\nconst CSS_FILE_REGEX =\n /\\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\\?)/\n// CSS modules file pattern - exported for use in plugin hook filters\n// Note: allow query/hash suffix since Vite ids often include them.\nexport const CSS_MODULES_REGEX =\n /\\.module\\.(css|less|sass|scss|styl|stylus)(?:$|[?#])/i\n\nexport function normalizeCssModuleCacheKey(idOrFile: string): string {\n const baseId = idOrFile.split('?')[0]!.split('#')[0]!\n return baseId.replace(/\\\\/g, '/')\n}\n// URL params that indicate CSS should not be injected (e.g., ?url, ?inline)\nconst CSS_SIDE_EFFECT_FREE_PARAMS = ['url', 'inline', 'raw', 'inline-css']\n\nconst VITE_CSS_REGEX = /const\\s+__vite__css\\s*=\\s*[\"'`]([\\s\\S]*?)[\"'`]/\n\nconst ESCAPE_CSS_COMMENT_START_REGEX = /\\/\\*/g\nconst ESCAPE_CSS_COMMENT_END_REGEX = /\\*\\//g\n\nfunction isCssFile(file: string): boolean {\n return CSS_FILE_REGEX.test(file)\n}\n\nexport function isCssModulesFile(file: string): boolean {\n return CSS_MODULES_REGEX.test(file)\n}\n\nfunction hasCssSideEffectFreeParam(url: string): boolean {\n const queryString = url.split('?')[1]\n if (!queryString) return false\n\n const params = new URLSearchParams(queryString)\n return CSS_SIDE_EFFECT_FREE_PARAMS.some(\n (param) =>\n params.get(param) === '' &&\n !url.includes(`?${param}=`) &&\n !url.includes(`&${param}=`),\n )\n}\n\n/**\n * Resolve a file path to a Vite dev server URL.\n * Files within the root directory use relative paths, files outside use /@fs prefix.\n */\nfunction resolveDevUrl(rootDirectory: string, filePath: string): string {\n const normalizedPath = filePath.replace(/\\\\/g, '/')\n const relativePath = path.posix.relative(\n rootDirectory.replace(/\\\\/g, '/'),\n normalizedPath,\n )\n const isWithinRoot =\n !relativePath.startsWith('..') && !path.isAbsolute(relativePath)\n\n if (isWithinRoot) {\n return path.posix.join('/', relativePath)\n }\n // Files outside root need /@fs prefix\n return path.posix.join('/@fs', normalizedPath)\n}\n\nexport interface CollectDevStylesOptions {\n viteDevServer: ViteDevServer\n entries: Array<string>\n /** Cache of CSS modules content captured during transform hook */\n cssModulesCache?: Record<string, string>\n}\n\n/**\n * Collect CSS content from the module graph starting from the given entry points.\n */\nexport async function collectDevStyles(\n opts: CollectDevStylesOptions,\n): Promise<string | undefined> {\n const { viteDevServer, entries, cssModulesCache = {} } = opts\n const styles: Map<string, string> = new Map()\n const visited = new Set<ModuleNode>()\n\n const rootDirectory = viteDevServer.config.root\n\n // Process entries in parallel - each entry is independent\n await Promise.all(\n entries.map((entry) =>\n processEntry(viteDevServer, resolveDevUrl(rootDirectory, entry), visited),\n ),\n )\n\n // Collect CSS from visited modules in parallel\n const cssPromises: Array<Promise<readonly [string, string] | null>> = []\n\n for (const dep of visited) {\n if (hasCssSideEffectFreeParam(dep.url)) {\n continue\n }\n\n if (dep.file && isCssModulesFile(dep.file)) {\n const css = cssModulesCache[normalizeCssModuleCacheKey(dep.file)]\n if (!css) {\n throw new Error(\n `[tanstack-start] Missing CSS module in cache: ${dep.file}`,\n )\n }\n styles.set(dep.url, css)\n continue\n }\n\n const fileOrUrl = dep.file ?? dep.url\n if (!isCssFile(fileOrUrl)) {\n continue\n }\n\n // Load regular CSS files in parallel\n cssPromises.push(\n fetchCssFromModule(viteDevServer, dep).then((css) =>\n css ? ([dep.url, css] as const) : null,\n ),\n )\n }\n\n // Wait for all CSS loads to complete\n const cssResults = await Promise.all(cssPromises)\n for (const result of cssResults) {\n if (result) {\n styles.set(result[0], result[1])\n }\n }\n\n if (styles.size === 0) return undefined\n\n const parts: Array<string> = []\n for (const [fileName, css] of styles.entries()) {\n const escapedFileName = fileName\n .replace(ESCAPE_CSS_COMMENT_START_REGEX, '/\\\\*')\n .replace(ESCAPE_CSS_COMMENT_END_REGEX, '*\\\\/')\n parts.push(`\\n/* ${escapedFileName} */\\n${css}`)\n }\n return parts.join('\\n')\n}\n\n/**\n * Process an entry URL: transform it if needed, get the module node, and crawl its dependencies.\n */\nasync function processEntry(\n viteDevServer: ViteDevServer,\n entryUrl: string,\n visited: Set<ModuleNode>,\n): Promise<void> {\n let node = await viteDevServer.moduleGraph.getModuleByUrl(entryUrl)\n\n // Only transform if not yet SSR-transformed (need ssrTransformResult.deps for crawling)\n if (!node?.ssrTransformResult) {\n try {\n await viteDevServer.transformRequest(entryUrl)\n } catch {\n // ignore - module might not exist yet\n }\n node = await viteDevServer.moduleGraph.getModuleByUrl(entryUrl)\n }\n\n if (!node || visited.has(node)) return\n\n visited.add(node)\n await findModuleDeps(viteDevServer, node, visited)\n}\n\n/**\n * Find all module dependencies by crawling the module graph.\n * Uses transformResult.deps for URL-based lookups (parallel) and\n * importedModules for already-resolved nodes (parallel).\n */\nasync function findModuleDeps(\n viteDevServer: ViteDevServer,\n node: ModuleNode,\n visited: Set<ModuleNode>,\n): Promise<void> {\n // Note: caller must add node to visited BEFORE calling this function\n // to prevent race conditions with parallel traversal\n\n // Process deps from transformResult if available (URLs including bare imports)\n const deps =\n node.ssrTransformResult?.deps ?? node.transformResult?.deps ?? null\n\n const importedModules = node.importedModules\n\n // Fast path: no deps and no imports\n if ((!deps || deps.length === 0) && importedModules.size === 0) {\n return\n }\n\n // Build branches only when needed (avoid array allocation on leaf nodes)\n const branches: Array<Promise<void>> = []\n\n if (deps) {\n for (const depUrl of deps) {\n const dep = await viteDevServer.moduleGraph.getModuleByUrl(depUrl)\n if (!dep) continue\n\n if (visited.has(dep)) continue\n visited.add(dep)\n branches.push(findModuleDeps(viteDevServer, dep, visited))\n }\n }\n\n // ALWAYS also traverse importedModules - this catches:\n // - Code-split chunks (e.g. ?tsr-split=component) not in deps\n // - Already-resolved nodes\n for (const depNode of importedModules) {\n if (visited.has(depNode)) continue\n visited.add(depNode)\n branches.push(findModuleDeps(viteDevServer, depNode, visited))\n }\n\n if (branches.length === 1) {\n await branches[0]\n return\n }\n\n await Promise.all(branches)\n}\n\nasync function fetchCssFromModule(\n viteDevServer: ViteDevServer,\n node: ModuleNode,\n): Promise<string | undefined> {\n // Use cached transform result if available\n const cachedCode = node.transformResult?.code ?? node.ssrTransformResult?.code\n if (cachedCode) {\n return extractCssFromCode(cachedCode)\n }\n\n // Otherwise request a fresh transform\n try {\n const transformResult = await viteDevServer.transformRequest(node.url)\n if (!transformResult?.code) return undefined\n\n return extractCssFromCode(transformResult.code)\n } catch {\n // Preprocessor partials (e.g., Sass files with mixins) can't compile in isolation.\n // The root stylesheet that @imports them will contain the compiled CSS.\n return undefined\n }\n}\n\nfunction extractCssFromCode(code: string): string | undefined {\n const match = VITE_CSS_REGEX.exec(code)\n if (!match?.[1]) return undefined\n\n return match[1]\n .replace(/\\\\n/g, '\\n')\n .replace(/\\\\t/g, '\\t')\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\\\\\/g, '\\\\')\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dev-styles.js","sources":["../../../src/dev-server-plugin/dev-styles.ts"],"sourcesContent":["/**\n * CSS collection for dev mode.\n * Crawls the Vite module graph to collect CSS from the router entry and all its dependencies.\n */\nimport path from 'node:path'\nimport type { ModuleNode, ViteDevServer } from 'vite'\n\n// CSS file extensions supported by Vite\nconst CSS_FILE_REGEX =\n /\\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\\?)/\n// CSS modules file pattern - exported for use in plugin hook filters\n// Note: allow query/hash suffix since Vite ids often include them.\nexport const CSS_MODULES_REGEX =\n /\\.module\\.(css|less|sass|scss|styl|stylus)(?:$|[?#])/i\n\nexport function normalizeCssModuleCacheKey(idOrFile: string): string {\n const baseId = idOrFile.split('?')[0]!.split('#')[0]!\n return baseId.replace(/\\\\/g, '/')\n}\n// URL params that indicate CSS should not be injected (e.g., ?url, ?inline)\nconst CSS_SIDE_EFFECT_FREE_PARAMS = ['url', 'inline', 'raw', 'inline-css']\n\n// Marker to find the CSS string in Vite's transformed output\nconst VITE_CSS_MARKER = 'const __vite__css = '\n\nconst ESCAPE_CSS_COMMENT_START_REGEX = /\\/\\*/g\nconst ESCAPE_CSS_COMMENT_END_REGEX = /\\*\\//g\n\nfunction isCssFile(file: string): boolean {\n return CSS_FILE_REGEX.test(file)\n}\n\nexport function isCssModulesFile(file: string): boolean {\n return CSS_MODULES_REGEX.test(file)\n}\n\nfunction hasCssSideEffectFreeParam(url: string): boolean {\n const queryString = url.split('?')[1]\n if (!queryString) return false\n\n const params = new URLSearchParams(queryString)\n return CSS_SIDE_EFFECT_FREE_PARAMS.some(\n (param) =>\n params.get(param) === '' &&\n !url.includes(`?${param}=`) &&\n !url.includes(`&${param}=`),\n )\n}\n\n/**\n * Resolve a file path to a Vite dev server URL.\n * Files within the root directory use relative paths, files outside use /@fs prefix.\n */\nfunction resolveDevUrl(rootDirectory: string, filePath: string): string {\n const normalizedPath = filePath.replace(/\\\\/g, '/')\n const relativePath = path.posix.relative(\n rootDirectory.replace(/\\\\/g, '/'),\n normalizedPath,\n )\n const isWithinRoot =\n !relativePath.startsWith('..') && !path.isAbsolute(relativePath)\n\n if (isWithinRoot) {\n return path.posix.join('/', relativePath)\n }\n // Files outside root need /@fs prefix\n return path.posix.join('/@fs', normalizedPath)\n}\n\nexport interface CollectDevStylesOptions {\n viteDevServer: ViteDevServer\n entries: Array<string>\n /** Cache of CSS modules content captured during transform hook */\n cssModulesCache?: Record<string, string>\n}\n\n/**\n * Collect CSS content from the module graph starting from the given entry points.\n */\nexport async function collectDevStyles(\n opts: CollectDevStylesOptions,\n): Promise<string | undefined> {\n const { viteDevServer, entries, cssModulesCache = {} } = opts\n const styles: Map<string, string> = new Map()\n const visited = new Set<ModuleNode>()\n\n const rootDirectory = viteDevServer.config.root\n\n // Process entries in parallel - each entry is independent\n await Promise.all(\n entries.map((entry) =>\n processEntry(viteDevServer, resolveDevUrl(rootDirectory, entry), visited),\n ),\n )\n\n // Collect CSS from visited modules in parallel\n const cssPromises: Array<Promise<readonly [string, string] | null>> = []\n\n for (const dep of visited) {\n if (hasCssSideEffectFreeParam(dep.url)) {\n continue\n }\n\n if (dep.file && isCssModulesFile(dep.file)) {\n const css = cssModulesCache[normalizeCssModuleCacheKey(dep.file)]\n if (!css) {\n throw new Error(\n `[tanstack-start] Missing CSS module in cache: ${dep.file}`,\n )\n }\n styles.set(dep.url, css)\n continue\n }\n\n const fileOrUrl = dep.file ?? dep.url\n if (!isCssFile(fileOrUrl)) {\n continue\n }\n\n // Load regular CSS files in parallel\n cssPromises.push(\n fetchCssFromModule(viteDevServer, dep).then((css) =>\n css ? ([dep.url, css] as const) : null,\n ),\n )\n }\n\n // Wait for all CSS loads to complete\n const cssResults = await Promise.all(cssPromises)\n for (const result of cssResults) {\n if (result) {\n styles.set(result[0], result[1])\n }\n }\n\n if (styles.size === 0) return undefined\n\n const parts: Array<string> = []\n for (const [fileName, css] of styles.entries()) {\n const escapedFileName = fileName\n .replace(ESCAPE_CSS_COMMENT_START_REGEX, '/\\\\*')\n .replace(ESCAPE_CSS_COMMENT_END_REGEX, '*\\\\/')\n parts.push(`\\n/* ${escapedFileName} */\\n${css}`)\n }\n return parts.join('\\n')\n}\n\n/**\n * Process an entry URL: transform it if needed, get the module node, and crawl its dependencies.\n */\nasync function processEntry(\n viteDevServer: ViteDevServer,\n entryUrl: string,\n visited: Set<ModuleNode>,\n): Promise<void> {\n let node = await viteDevServer.moduleGraph.getModuleByUrl(entryUrl)\n\n // Only transform if not yet SSR-transformed (need ssrTransformResult.deps for crawling)\n if (!node?.ssrTransformResult) {\n try {\n await viteDevServer.transformRequest(entryUrl)\n } catch {\n // ignore - module might not exist yet\n }\n node = await viteDevServer.moduleGraph.getModuleByUrl(entryUrl)\n }\n\n if (!node || visited.has(node)) return\n\n visited.add(node)\n await findModuleDeps(viteDevServer, node, visited)\n}\n\n/**\n * Find all module dependencies by crawling the module graph.\n * Uses transformResult.deps for URL-based lookups (parallel) and\n * importedModules for already-resolved nodes (parallel).\n */\nasync function findModuleDeps(\n viteDevServer: ViteDevServer,\n node: ModuleNode,\n visited: Set<ModuleNode>,\n): Promise<void> {\n // Note: caller must add node to visited BEFORE calling this function\n // to prevent race conditions with parallel traversal\n\n // Process deps from transformResult if available (URLs including bare imports)\n const deps =\n node.ssrTransformResult?.deps ?? node.transformResult?.deps ?? null\n\n const importedModules = node.importedModules\n\n // Fast path: no deps and no imports\n if ((!deps || deps.length === 0) && importedModules.size === 0) {\n return\n }\n\n // Build branches only when needed (avoid array allocation on leaf nodes)\n const branches: Array<Promise<void>> = []\n\n if (deps) {\n for (const depUrl of deps) {\n const dep = await viteDevServer.moduleGraph.getModuleByUrl(depUrl)\n if (!dep) continue\n\n if (visited.has(dep)) continue\n visited.add(dep)\n branches.push(findModuleDeps(viteDevServer, dep, visited))\n }\n }\n\n // ALWAYS also traverse importedModules - this catches:\n // - Code-split chunks (e.g. ?tsr-split=component) not in deps\n // - Already-resolved nodes\n for (const depNode of importedModules) {\n if (visited.has(depNode)) continue\n visited.add(depNode)\n branches.push(findModuleDeps(viteDevServer, depNode, visited))\n }\n\n if (branches.length === 1) {\n await branches[0]\n return\n }\n\n await Promise.all(branches)\n}\n\nasync function fetchCssFromModule(\n viteDevServer: ViteDevServer,\n node: ModuleNode,\n): Promise<string | undefined> {\n // Use cached transform result if available\n const cachedCode = node.transformResult?.code ?? node.ssrTransformResult?.code\n if (cachedCode) {\n return extractCssFromCode(cachedCode)\n }\n\n // Otherwise request a fresh transform\n try {\n const transformResult = await viteDevServer.transformRequest(node.url)\n if (!transformResult?.code) return undefined\n\n return extractCssFromCode(transformResult.code)\n } catch {\n // Preprocessor partials (e.g., Sass files with mixins) can't compile in isolation.\n // The root stylesheet that @imports them will contain the compiled CSS.\n return undefined\n }\n}\n\n/**\n * Extract CSS content from Vite's transformed CSS module code.\n *\n * Vite embeds CSS into the module as a JS string via `JSON.stringify(cssContent)`,\n * e.g. `const __vite__css = ${JSON.stringify('...css...')}`.\n *\n * We locate that JSON string literal and run `JSON.parse` on it to reverse the\n * escaping (\\\\n, \\\\t, \\\\\", \\\\\\\\, \\\\uXXXX, etc.).\n */\nexport function extractCssFromCode(code: string): string | undefined {\n const startIdx = code.indexOf(VITE_CSS_MARKER)\n if (startIdx === -1) return undefined\n\n const valueStart = startIdx + VITE_CSS_MARKER.length\n // Vite emits `const __vite__css = ${JSON.stringify(cssContent)}` which always\n // produces double-quoted JSON string literals.\n if (code.charCodeAt(valueStart) !== 34) return undefined\n\n const codeLength = code.length\n let i = valueStart + 1\n while (i < codeLength) {\n const charCode = code.charCodeAt(i)\n // 34 = '\"'\n if (charCode === 34) {\n try {\n return JSON.parse(code.slice(valueStart, i + 1))\n } catch {\n return undefined\n }\n }\n // 92 = '\\\\'\n if (charCode === 92) {\n i += 2\n } else {\n i++\n }\n }\n\n return undefined\n}\n"],"names":[],"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.149.
|
|
3
|
+
"version": "1.149.4",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
"vitefu": "^1.1.1",
|
|
60
60
|
"xmlbuilder2": "^4.0.3",
|
|
61
61
|
"zod": "^3.24.2",
|
|
62
|
-
"@tanstack/router-core": "1.149.3",
|
|
63
62
|
"@tanstack/router-generator": "1.149.3",
|
|
63
|
+
"@tanstack/router-core": "1.149.3",
|
|
64
64
|
"@tanstack/router-plugin": "1.149.3",
|
|
65
65
|
"@tanstack/start-client-core": "1.149.3",
|
|
66
66
|
"@tanstack/router-utils": "1.143.11",
|
|
@@ -20,7 +20,8 @@ export function normalizeCssModuleCacheKey(idOrFile: string): string {
|
|
|
20
20
|
// URL params that indicate CSS should not be injected (e.g., ?url, ?inline)
|
|
21
21
|
const CSS_SIDE_EFFECT_FREE_PARAMS = ['url', 'inline', 'raw', 'inline-css']
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
// Marker to find the CSS string in Vite's transformed output
|
|
24
|
+
const VITE_CSS_MARKER = 'const __vite__css = '
|
|
24
25
|
|
|
25
26
|
const ESCAPE_CSS_COMMENT_START_REGEX = /\/\*/g
|
|
26
27
|
const ESCAPE_CSS_COMMENT_END_REGEX = /\*\//g
|
|
@@ -248,13 +249,43 @@ async function fetchCssFromModule(
|
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Extract CSS content from Vite's transformed CSS module code.
|
|
254
|
+
*
|
|
255
|
+
* Vite embeds CSS into the module as a JS string via `JSON.stringify(cssContent)`,
|
|
256
|
+
* e.g. `const __vite__css = ${JSON.stringify('...css...')}`.
|
|
257
|
+
*
|
|
258
|
+
* We locate that JSON string literal and run `JSON.parse` on it to reverse the
|
|
259
|
+
* escaping (\\n, \\t, \\", \\\\, \\uXXXX, etc.).
|
|
260
|
+
*/
|
|
261
|
+
export function extractCssFromCode(code: string): string | undefined {
|
|
262
|
+
const startIdx = code.indexOf(VITE_CSS_MARKER)
|
|
263
|
+
if (startIdx === -1) return undefined
|
|
264
|
+
|
|
265
|
+
const valueStart = startIdx + VITE_CSS_MARKER.length
|
|
266
|
+
// Vite emits `const __vite__css = ${JSON.stringify(cssContent)}` which always
|
|
267
|
+
// produces double-quoted JSON string literals.
|
|
268
|
+
if (code.charCodeAt(valueStart) !== 34) return undefined
|
|
269
|
+
|
|
270
|
+
const codeLength = code.length
|
|
271
|
+
let i = valueStart + 1
|
|
272
|
+
while (i < codeLength) {
|
|
273
|
+
const charCode = code.charCodeAt(i)
|
|
274
|
+
// 34 = '"'
|
|
275
|
+
if (charCode === 34) {
|
|
276
|
+
try {
|
|
277
|
+
return JSON.parse(code.slice(valueStart, i + 1))
|
|
278
|
+
} catch {
|
|
279
|
+
return undefined
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// 92 = '\\'
|
|
283
|
+
if (charCode === 92) {
|
|
284
|
+
i += 2
|
|
285
|
+
} else {
|
|
286
|
+
i++
|
|
287
|
+
}
|
|
288
|
+
}
|
|
254
289
|
|
|
255
|
-
return
|
|
256
|
-
.replace(/\\n/g, '\n')
|
|
257
|
-
.replace(/\\t/g, '\t')
|
|
258
|
-
.replace(/\\"/g, '"')
|
|
259
|
-
.replace(/\\\\/g, '\\')
|
|
290
|
+
return undefined
|
|
260
291
|
}
|