@withl5e/l5e 1.0.1 → 1.1.0-alpha.0
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/index.js +2 -2
- package/dist/{server-DXNRPyGJ.js → server-CvsOU9cg.js} +35 -27
- package/dist/server-CvsOU9cg.js.map +1 -0
- package/dist/server.js +1 -1
- package/dist/vite-plugin-D9qTnleZ.js +469 -0
- package/dist/vite-plugin-D9qTnleZ.js.map +1 -0
- package/dist/vite-plugin.js +2 -333
- package/package.json +1 -1
- package/src/core/bundler.ts +1 -1
- package/src/core/chunking.ts +199 -0
- package/src/core/server.ts +20 -20
- package/src/core/vite-plugin.ts +68 -3
- package/dist/server-DXNRPyGJ.js.map +0 -1
- package/dist/vite-plugin.js.map +0 -1
package/src/core/vite-plugin.ts
CHANGED
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
import { dirname, join, relative, resolve } from 'path';
|
|
12
12
|
import type { Plugin, UserConfig } from 'vite';
|
|
13
13
|
import { findGlobalStyleInput } from './global-style';
|
|
14
|
+
import { createChunkPlanner, type CoreViteOptions } from './chunking';
|
|
15
|
+
export type { CoreViteOptions, ChunkingOptions, SharedChunkGroup } from './chunking';
|
|
14
16
|
|
|
15
17
|
const VIRTUAL_L5E_VIEWS = 'virtual:l5e-views';
|
|
16
18
|
const VIRTUAL_L5E_ROUTE = 'virtual:l5e-route';
|
|
@@ -436,8 +438,10 @@ function injectIslandKeys(
|
|
|
436
438
|
return result.join('');
|
|
437
439
|
}
|
|
438
440
|
|
|
439
|
-
export function coreVite(): Plugin {
|
|
441
|
+
export function coreVite(options: CoreViteOptions = {}): Plugin {
|
|
440
442
|
let rootDir: string = process.cwd();
|
|
443
|
+
let clientBuild = false;
|
|
444
|
+
const planner = options.chunking === false ? null : createChunkPlanner(options.chunking);
|
|
441
445
|
let islandRegistry = new Map<string, string>();
|
|
442
446
|
let pathToKey = new Map<string, string>();
|
|
443
447
|
let keyToSrc = new Map<string, string>();
|
|
@@ -450,6 +454,26 @@ export function coreVite(): Plugin {
|
|
|
450
454
|
configResolved(resolvedConfig) {
|
|
451
455
|
// Store root directory for later use
|
|
452
456
|
rootDir = resolvedConfig.root || process.cwd();
|
|
457
|
+
clientBuild = resolvedConfig.command === 'build' && !resolvedConfig.build.ssr;
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
async buildStart() {
|
|
461
|
+
if (clientBuild && planner) {
|
|
462
|
+
await planner.resolve(rootDir, async (id, importer) => {
|
|
463
|
+
const resolved = await this.resolve(id, importer);
|
|
464
|
+
return resolved && !resolved.external ? resolved.id : null;
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
generateBundle(_options, bundle) {
|
|
470
|
+
if (clientBuild && planner) {
|
|
471
|
+
this.emitFile({
|
|
472
|
+
type: 'asset',
|
|
473
|
+
fileName: '.vite/l5e-chunks.json',
|
|
474
|
+
source: JSON.stringify(planner.report(bundle), null, 2),
|
|
475
|
+
});
|
|
476
|
+
}
|
|
453
477
|
},
|
|
454
478
|
|
|
455
479
|
handleHotUpdate({ file, server }) {
|
|
@@ -492,7 +516,13 @@ export function coreVite(): Plugin {
|
|
|
492
516
|
}
|
|
493
517
|
},
|
|
494
518
|
|
|
495
|
-
buildEnd() {
|
|
519
|
+
buildEnd(error) {
|
|
520
|
+
if (!error && clientBuild && planner) {
|
|
521
|
+
const modules = [...this.getModuleIds()]
|
|
522
|
+
.map((id) => this.getModuleInfo(id))
|
|
523
|
+
.filter((mod) => mod !== null);
|
|
524
|
+
for (const warning of planner.analyze(modules)) this.warn(`[l5e chunking] ${warning}`);
|
|
525
|
+
}
|
|
496
526
|
// Cleanup temporary files after build
|
|
497
527
|
const tempDir = join(rootDir, '.l5e-temp');
|
|
498
528
|
if (existsSync(tempDir)) {
|
|
@@ -521,6 +551,16 @@ export function coreVite(): Plugin {
|
|
|
521
551
|
config(userConfig) {
|
|
522
552
|
// Auto-discover bundler input from useCss and useClientJs
|
|
523
553
|
const projectRoot = userConfig.root || process.cwd();
|
|
554
|
+
const output = userConfig.build?.rolldownOptions?.output;
|
|
555
|
+
const configureChunks = !userConfig.build?.ssr && planner !== null;
|
|
556
|
+
if (
|
|
557
|
+
configureChunks &&
|
|
558
|
+
(Array.isArray(output) || output?.manualChunks || output?.codeSplitting !== undefined)
|
|
559
|
+
) {
|
|
560
|
+
throw new Error(
|
|
561
|
+
'[l5e chunking] Use coreVite({ chunking: { shared: [...] } }) or set chunking: false when configuring raw Rolldown chunk placement.',
|
|
562
|
+
);
|
|
563
|
+
}
|
|
524
564
|
const discovered = discoverBundlerInput(projectRoot);
|
|
525
565
|
|
|
526
566
|
// Store island registries for use in other hooks
|
|
@@ -544,7 +584,32 @@ export function coreVite(): Plugin {
|
|
|
544
584
|
// Preserve exports for island component entries — without this,
|
|
545
585
|
// The bundler tree-shakes their exports since nothing imports them
|
|
546
586
|
// (they're loaded at runtime via dynamic import from the island runtime).
|
|
547
|
-
preserveEntrySignatures: 'exports-only',
|
|
587
|
+
preserveEntrySignatures: configureChunks ? 'allow-extension' : 'exports-only',
|
|
588
|
+
...(configureChunks
|
|
589
|
+
? {
|
|
590
|
+
output: {
|
|
591
|
+
...output,
|
|
592
|
+
strictExecutionOrder: true,
|
|
593
|
+
codeSplitting: { groups: planner!.groups() },
|
|
594
|
+
entryFileNames: (chunk) => {
|
|
595
|
+
const source = chunk.facadeModuleId?.replace(/\\/g, '/') || '';
|
|
596
|
+
if (source.endsWith('/src/client.global.ts'))
|
|
597
|
+
return 'assets/global-[hash].js';
|
|
598
|
+
if (/\/react\//.test(source))
|
|
599
|
+
return chunk.name.startsWith('island-')
|
|
600
|
+
? 'assets/[name]-[hash].js'
|
|
601
|
+
: 'assets/island-[name]-[hash].js';
|
|
602
|
+
return 'assets/bundle-[name]-[hash].js';
|
|
603
|
+
},
|
|
604
|
+
chunkFileNames: (chunk) =>
|
|
605
|
+
chunk.name.startsWith('shared-')
|
|
606
|
+
? 'assets/[name]-[hash].js'
|
|
607
|
+
: chunk.isDynamicEntry
|
|
608
|
+
? 'assets/bundle-[name]-[hash].js'
|
|
609
|
+
: 'assets/shared-[name]-[hash].js',
|
|
610
|
+
},
|
|
611
|
+
}
|
|
612
|
+
: {}),
|
|
548
613
|
},
|
|
549
614
|
},
|
|
550
615
|
} satisfies UserConfig;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server-DXNRPyGJ.js","names":["serialize"],"sources":["../src/core/bundler.ts","../src/core/script-bundle-policy.ts","../src/core/server.ts"],"sourcesContent":["/// <reference path=\"./jsx-types.d.ts\" />\nimport { createHash } from 'node:crypto';\nimport fs from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { InputOptions, OutputChunk, OutputOptions, Plugin } from 'rolldown';\nimport type { ScriptBundlePolicy } from './script-bundle-policy';\n\nlet rolldownModulePromise: Promise<typeof import('rolldown')> | null = null;\n\n/**\n * Resolve Rolldown lazily from the installed framework package. A bare static\n * import can be hoisted when the framework is bundled into a consumer's SSR\n * output, which breaks pnpm's strict dependency layout. Anchoring resolution at\n * @withl5e/l5e keeps the runtime dependency owned by the package that declares it.\n */\nfunction loadRolldown(): Promise<typeof import('rolldown')> {\n if (!rolldownModulePromise) {\n rolldownModulePromise = (async () => {\n const require = createRequire(import.meta.url);\n const frameworkEntry = require.resolve('@withl5e/l5e/server');\n const frameworkRequire = createRequire(frameworkEntry);\n const rolldownPath = frameworkRequire.resolve('rolldown');\n return (await import(\n /* @vite-ignore */ pathToFileURL(rolldownPath).href\n )) as typeof import('rolldown');\n })();\n }\n return rolldownModulePromise;\n}\n\ninterface BundledFile {\n content: string;\n hash: string;\n filename: string;\n mimeType: string;\n}\n\ninterface BundleResult {\n hash: string;\n filename: string;\n content: string;\n}\n\nconst EMPTY_RESULT: BundleResult = { hash: '', filename: '', content: '' };\n\n// Memory map để lưu bundled files\nconst bundledFilesMap = new Map<string, BundledFile>();\n\n/**\n * Single-flight map: cacheKey → promise của lần bundle đang chạy (hoặc đã xong).\n * Vì promise được giữ lại sau khi resolve, map này vừa là in-flight dedup vừa là\n * result cache. Bundle lỗi bị xoá khỏi map để request sau được thử lại.\n */\nconst bundlePromises = new Map<string, Promise<BundleResult>>();\n\n/**\n * Chạy `work` đúng một lần cho mỗi cacheKey, kể cả khi nhiều request đến đồng thời.\n */\nfunction dedupe(cacheKey: string, work: () => Promise<BundleResult>): Promise<BundleResult> {\n const pending = bundlePromises.get(cacheKey);\n if (pending) {\n return pending;\n }\n\n const promise = work().catch((error) => {\n bundlePromises.delete(cacheKey);\n throw error;\n });\n bundlePromises.set(cacheKey, promise);\n return promise;\n}\n\n/**\n * Generate hash từ content\n */\nfunction generateHash(content: string): string {\n return createHash('sha256').update(content).digest('hex').substring(0, 16);\n}\n\n// Rolldown coi id bắt đầu bằng \\0 là virtual — nó sẽ không cố đọc từ đĩa.\nconst VIRTUAL_ENTRY_ID = '\\0l5e:bundle-entry';\n\n/**\n * Entry của mỗi lần bundle chỉ là một danh sách import. Giữ nó trong memory thay\n * vì ghi ra đĩa: hai request đồng thời cùng một tập script sinh ra cùng nội dung\n * entry, nên file tạm dùng chung path sẽ bị request này xoá trong lúc Rolldown của\n * request kia còn đang đọc.\n */\nfunction virtualEntryPlugin(entryContent: string): Plugin {\n return {\n name: 'l5e-virtual-entry',\n resolveId(source) {\n return source === VIRTUAL_ENTRY_ID ? VIRTUAL_ENTRY_ID : null;\n },\n load(id) {\n return id === VIRTUAL_ENTRY_ID ? entryContent : null;\n },\n };\n}\n\n/** Inline page roots only; their emitted dependencies retain canonical URLs. */\nfunction preserveBuildChunksPlugin(policy: ScriptBundlePolicy): Plugin {\n return {\n name: 'preserve-build-chunks',\n resolveId(source, importer) {\n if (!importer || source === VIRTUAL_ENTRY_ID) return null;\n const file = path.isAbsolute(source)\n ? path.normalize(source)\n : source.startsWith('.')\n ? path.resolve(path.dirname(importer), source)\n : null;\n if (!file) return null;\n // The virtual entry is the only place where an unshared page entry may\n // be inlined. Never traverse an emitted dependency to re-bundle its body.\n if (importer === VIRTUAL_ENTRY_ID && !policy.isPreserved(file)) return null;\n return { id: policy.assetUrl(file), external: 'absolute' };\n },\n };\n}\n\nasync function runScriptBundle(\n uniquePaths: string[],\n policy: ScriptBundlePolicy,\n): Promise<BundleResult> {\n const roots = uniquePaths.map((p) => policy.fileForScript(p));\n const suffix = policy.inlineableRoots(roots);\n const prefixChunks = new Map(\n roots.flatMap((file, index) =>\n !policy.isPreserved(file) && !suffix.has(file) ? [[file, `entry-${index}`] as const] : [],\n ),\n );\n const entryContent = roots.map((filePath) => `import ${JSON.stringify(filePath)};`).join('\\n');\n\n const rolldownOptions: InputOptions = {\n input: VIRTUAL_ENTRY_ID,\n plugins: [virtualEntryPlugin(entryContent), preserveBuildChunksPlugin(policy)],\n platform: 'neutral',\n tsconfig: false,\n // Runtime scripts are imported for their side effects. Do not let an app's\n // package.json sideEffects flag erase those entry imports.\n // Rolldown 1.2 still consults package metadata for boolean `true`, so use\n // an explicit callback to override consumer `sideEffects: false`.\n treeshake: { moduleSideEffects: () => true },\n external: (id) => {\n // External node_modules\n if (!id.startsWith('.') && !path.isAbsolute(id) && id !== VIRTUAL_ENTRY_ID) {\n return true;\n }\n\n // Emitted file imports are resolved by preserveBuildChunksPlugin.\n return false;\n },\n };\n\n const outputOptions: OutputOptions = {\n format: 'es',\n codeSplitting: { groups: [{ name: (id) => prefixChunks.get(id) ?? null }] },\n minify: false,\n entryFileNames: 'bundle-[hash].js',\n chunkFileNames: `bundle-${generateHash(JSON.stringify(roots))}-[hash].js`,\n };\n\n const { rolldown } = await loadRolldown();\n const bundle = await rolldown(rolldownOptions);\n let output;\n try {\n ({ output } = await bundle.generate(outputOptions));\n } finally {\n await bundle.close();\n }\n\n for (const chunk of output) {\n if (chunk.type !== 'chunk') {\n continue;\n }\n bundledFilesMap.set(chunk.fileName, {\n content: chunk.code || '',\n hash: generateHash(chunk.code || ''),\n filename: chunk.fileName,\n mimeType: 'application/javascript',\n });\n }\n\n const entryChunk = output.find(\n (item): item is OutputChunk =>\n item.type === 'chunk' && item.isEntry && item.facadeModuleId === VIRTUAL_ENTRY_ID,\n );\n if (!entryChunk) {\n throw new Error('[bundler] rolldown produced no entry chunk');\n }\n\n return {\n hash: generateHash(entryChunk.code || ''),\n filename: entryChunk.fileName,\n content: entryChunk.code || '',\n };\n}\n\n/**\n * Bundle JavaScript files từ dist/client thành 1 file\n * Trong production, các file đã được build sẵn trong dist/client\n */\nexport async function bundleScripts(\n scriptPaths: string[],\n distClientDir: string,\n policy?: ScriptBundlePolicy,\n): Promise<BundleResult> {\n if (scriptPaths.length === 0) {\n return EMPTY_RESULT;\n }\n\n if (!policy || policy.directory !== path.resolve(distClientDir)) {\n console.warn('[bundler] No matching build manifest; serving original script entries.');\n return EMPTY_RESULT;\n }\n\n const uniquePaths = [...new Set(scriptPaths)].sort();\n const cacheKey = `scripts:${policy.cacheKey}:${JSON.stringify(uniquePaths)}`;\n\n try {\n return await dedupe(cacheKey, () => runScriptBundle(uniquePaths, policy));\n } catch (error) {\n console.error('[bundler] Error bundling scripts:', error);\n return EMPTY_RESULT;\n }\n}\n\nasync function runCssBundle(uniquePaths: string[], distClientDir: string): Promise<BundleResult> {\n const cssContents: string[] = [];\n\n for (const cssPath of uniquePaths) {\n // cssPath có thể là \"/assets/xxx.css\" hoặc từ manifest\n const filePath = cssPath.startsWith('/')\n ? path.join(distClientDir, cssPath.substring(1))\n : path.join(distClientDir, cssPath);\n\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n cssContents.push(`/* ${cssPath} */\\n${content}\\n`);\n } catch (err) {\n console.warn(`[bundler] Failed to read CSS file: ${cssPath}`, err);\n }\n }\n\n const bundledContent = cssContents.join('\\n\\n');\n const hash = generateHash(bundledContent);\n const filename = `bundle-${hash}.css`;\n\n bundledFilesMap.set(filename, {\n content: bundledContent,\n hash,\n filename,\n mimeType: 'text/css',\n });\n\n return { hash, filename, content: bundledContent };\n}\n\n/**\n * Bundle CSS files từ dist/client thành 1 file\n * Trong production, các file đã được build sẵn trong dist/client\n */\nexport async function bundleCss(cssPaths: string[], distClientDir: string): Promise<BundleResult> {\n if (cssPaths.length === 0) {\n return EMPTY_RESULT;\n }\n\n const uniquePaths = [...new Set(cssPaths)].sort();\n const cacheKey = `css:${JSON.stringify([path.resolve(distClientDir), uniquePaths])}`;\n\n try {\n return await dedupe(cacheKey, () => runCssBundle(uniquePaths, distClientDir));\n } catch (error) {\n console.error('[bundler] Error bundling CSS:', error);\n return EMPTY_RESULT;\n }\n}\n\n/**\n * Get bundled file từ map\n */\nexport function getBundledFile(filename: string): BundledFile | undefined {\n return bundledFilesMap.get(filename);\n}\n\n/**\n * Clear bundled files map (useful for testing)\n */\nexport function clearBundledFiles(): void {\n bundledFilesMap.clear();\n bundlePromises.clear();\n}\n","import { createHash } from 'node:crypto';\nimport { realpathSync } from 'node:fs';\nimport path from 'node:path';\nimport type { Manifest } from 'vite';\nimport { withAssetBase } from './global-style';\n\n/** Preserve the emitted module boundaries chosen by the application's build. */\nexport function createScriptBundlePolicy(manifest: Manifest, distClientDir: string, base = '/') {\n const directory = path.resolve(distClientDir);\n // Rolldown resolves entry importers through symlinks/junctions. Use the same\n // physical directory for identity while retaining the caller's output scope.\n const assetDirectory = realpathSync(directory);\n const files = new Set<string>();\n const preserved = new Set<string>();\n const staticImports = new Map<string, string[]>();\n const jsEntries = Object.entries(manifest).filter(([, entry]) => /\\.[cm]?js$/.test(entry.file));\n\n function absoluteFile(file: string) {\n const absolute = path.resolve(assetDirectory, file);\n const relative = path.relative(assetDirectory, absolute);\n if (\n !relative ||\n relative === '..' ||\n relative.startsWith(`..${path.sep}`) ||\n path.isAbsolute(relative)\n ) {\n throw new Error(`[bundler] Asset is outside the client output: ${file}`);\n }\n return absolute;\n }\n\n for (const [key, entry] of jsEntries) {\n const file = absoluteFile(entry.file);\n files.add(file);\n if (\n !entry.isEntry ||\n entry.isDynamicEntry ||\n key === 'src/client.global.ts' ||\n /(?:^|\\/)react\\//.test(entry.src || key)\n )\n preserved.add(file);\n }\n\n // Index the whole build, not just this request's roots. This also protects an\n // entry imported by another entry, even when both are selected on this page.\n for (const [, entry] of jsEntries) {\n for (const key of [...(entry.imports || []), ...(entry.dynamicImports || [])]) {\n const dependency = manifest[key];\n if (!dependency || !files.has(absoluteFile(dependency.file))) {\n throw new Error(`[bundler] Missing JavaScript manifest dependency: ${key}`);\n }\n preserved.add(absoluteFile(dependency.file));\n }\n staticImports.set(\n absoluteFile(entry.file),\n (entry.imports || []).map((key) => absoluteFile(manifest[key].file)),\n );\n }\n\n const cacheKey = createHash('sha256')\n .update(JSON.stringify([directory, assetDirectory, base, manifest]))\n .digest('hex');\n\n return {\n cacheKey,\n directory,\n fileForScript(script: string) {\n const file = absoluteFile(script.replace(/^\\/+/, ''));\n if (!files.has(file)) throw new Error(`[bundler] Script is missing from manifest: ${script}`);\n return file;\n },\n isPreserved(file: string) {\n return preserved.has(file);\n },\n inlineableRoots(roots: string[]) {\n // External ESM imports execute before any inlined body. Isolate prefix\n // roots in runtime chunks when a later root introduces a dependency effect;\n // only the suffix after the last such boundary can share the entry chunk.\n const visited = new Set<string>();\n let suffixStart = 0;\n function visit(file: string, rootIndex: number) {\n if (visited.has(file)) return;\n visited.add(file);\n for (const dependency of staticImports.get(file) || []) visit(dependency, rootIndex);\n if (preserved.has(file)) suffixStart = rootIndex;\n }\n roots.forEach((file, index) => {\n visit(file, index);\n if (preserved.has(file)) suffixStart = index + 1;\n });\n return new Set(roots.slice(suffixStart).filter((file) => !preserved.has(file)));\n },\n assetUrl(file: string) {\n if (!files.has(file)) throw new Error(`[bundler] Import is missing from manifest: ${file}`);\n return withAssetBase(base, path.relative(assetDirectory, file).replace(/\\\\/g, '/'));\n },\n };\n}\n\nexport type ScriptBundlePolicy = ReturnType<typeof createScriptBundlePolicy>;\n","import type { Request as ExpressRequest, Response as ExpressResponse } from 'express';\nimport { existsSync } from 'fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport requestIp from 'request-ip';\nimport serialize from 'serialize-javascript';\nimport type { ViteDevServer } from 'vite';\nimport { createContext, type MiddlewareHandler, type RewritePayload } from '../middleware';\nimport { bundleCss, bundleScripts, getBundledFile } from './bundler';\nimport type { RenderResult, RequestInfo } from './entry-server';\nimport { resolveGlobalStyleHref, withAssetBase } from './global-style';\nimport { createScriptBundlePolicy, type ScriptBundlePolicy } from './script-bundle-policy';\nimport type { Manifest } from 'vite';\nimport { escapeProp } from './render';\nimport { createHeadersFromExpressRequest, parseCookies } from './request';\n\nexport interface ServerOptions {\n root?: string;\n port?: number;\n base?: string;\n publicDir?: string;\n setupApp?: (app: any) => void | Promise<void>;\n app?: any; // Express\n /**\n * Externalize React island props into a single `<script id=\"_l5e_data_\">` at\n * the end of the document (referenced by `data-island-idx`) instead of inlining\n * a large `data-island-props` attribute on each element — keeps the SSR HTML\n * lean so crawlers read content first. Default true; set false for the legacy\n * inline behavior.\n */\n externalizeIslandProps?: boolean;\n}\n\nexport interface ServerContext {\n app: any; // Express app\n vite?: ViteDevServer;\n}\n\n/**\n * Apply or replace lang attribute on <html> tag\n */\nexport function applyHtmlLang(template: string, lang: string): string {\n // Escape so a loader-supplied lang (possibly derived from user input) cannot\n // break out of the attribute / <html> tag (XSS). escapeProp handles \", <, >, &.\n const safeLang = escapeProp(lang);\n return template.replace(/<html\\b([^>]*)>/i, (match, attrs) => {\n // Check if lang already exists\n if (/\\blang\\s*=/i.test(attrs)) {\n // Replace existing lang value (function replacer avoids $-pattern issues)\n return match.replace(/lang\\s*=\\s*\"[^\"]*\"/i, () => `lang=\"${safeLang}\"`);\n } else {\n // Add lang attribute\n return `<html lang=\"${safeLang}\"${attrs}>`;\n }\n });\n}\n\ntype EntryServerModule = {\n render: (\n url: string,\n requestInfo?: RequestInfo,\n options?: { externalizeIslandProps?: boolean },\n ) => Promise<RenderResult>;\n loadMiddleware?: () => Promise<MiddlewareHandler | undefined>;\n};\n\nfunction getRequestUrl(req: ExpressRequest): URL {\n return new URL(`${req.protocol}://${req.get('host')}${req.originalUrl}`);\n}\n\nfunction getRenderUrl(urlObject: URL, base: string): string {\n const requestPath = `${urlObject.pathname}${urlObject.search}`;\n return requestPath.replace(base, '') || '/';\n}\n\nfunction createWebRequestFromExpress(req: ExpressRequest): globalThis.Request {\n const init: RequestInit & { duplex?: 'half' } = {\n method: req.method,\n headers: createHeadersFromExpressRequest(req),\n };\n\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n init.body = req as unknown as BodyInit;\n init.duplex = 'half';\n }\n\n return new globalThis.Request(getRequestUrl(req).href, init);\n}\n\nfunction createRequestInfo(\n req: ExpressRequest,\n webRequest: globalThis.Request,\n base: string,\n locals: Record<string, unknown>,\n): RequestInfo {\n const urlObject = new URL(webRequest.url);\n const renderUrl = getRenderUrl(urlObject, base);\n const normalizedPath = renderUrl.startsWith('/') ? renderUrl : `/${renderUrl}`;\n const headers: Record<string, string> = {};\n webRequest.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n return {\n url: urlObject,\n path: normalizedPath,\n pathname: urlObject.pathname,\n method: webRequest.method,\n headers,\n cookies: parseCookies(webRequest.headers.get('cookie') ?? undefined),\n query: Object.fromEntries(urlObject.searchParams.entries()),\n ip: requestIp.getClientIp(req) ?? undefined,\n locals,\n };\n}\n\nfunction createRewriteRequest(\n payload: RewritePayload | undefined,\n currentRequest: globalThis.Request,\n currentUrl: URL,\n): globalThis.Request {\n if (!payload) {\n return currentRequest;\n }\n\n if (payload instanceof globalThis.Request) {\n return payload;\n }\n\n if (payload instanceof URL) {\n return new globalThis.Request(payload.href, currentRequest.clone());\n }\n\n return new globalThis.Request(new URL(payload, currentUrl).href, currentRequest.clone());\n}\n\nasync function sendWebResponse(\n req: ExpressRequest,\n res: ExpressResponse,\n response: globalThis.Response,\n): Promise<void> {\n res.status(response.status);\n const setCookieValues = getSetCookieHeaders(response.headers);\n response.headers.forEach((value, key) => {\n if (key.toLowerCase() === 'set-cookie') {\n return;\n }\n res.setHeader(key, value);\n });\n if (setCookieValues.length > 0) {\n res.setHeader('Set-Cookie', setCookieValues);\n }\n\n if (req.method === 'HEAD') {\n res.end();\n return;\n }\n\n const body = Buffer.from(await response.arrayBuffer());\n res.send(body);\n}\n\nfunction getSetCookieHeaders(headers: Headers): string[] {\n const getSetCookie = (headers as Headers & { getSetCookie?: () => string[] }).getSetCookie;\n if (typeof getSetCookie === 'function') {\n return getSetCookie.call(headers);\n }\n\n const raw = (headers as Headers & { raw?: () => Record<string, string[]> }).raw?.();\n if (raw?.['set-cookie']) {\n return raw['set-cookie'];\n }\n\n const value = headers.get('set-cookie');\n return value ? splitSetCookieHeader(value) : [];\n}\n\nfunction splitSetCookieHeader(value: string): string[] {\n const cookies: string[] = [];\n let start = 0;\n\n for (let i = 0; i < value.length; i++) {\n if (value[i] !== ',') continue;\n\n const rest = value.slice(i + 1);\n if (/^\\s*[^=;,]+=/.test(rest)) {\n cookies.push(value.slice(start, i).trim());\n start = i + 1;\n }\n }\n\n cookies.push(value.slice(start).trim());\n return cookies.filter(Boolean);\n}\n\nfunction createRawResponse(rendered: RenderResult): globalThis.Response | null {\n if (!rendered.rawResponse) {\n return null;\n }\n\n const { body, contentType, statusCode, headers } = rendered.rawResponse;\n const responseHeaders = new Headers(headers);\n responseHeaders.set('Content-Type', contentType);\n\n return new globalThis.Response(body as BodyInit, {\n status: statusCode || 200,\n headers: responseHeaders,\n });\n}\n\nasync function createPageResponse({\n rendered,\n template,\n manifest,\n root,\n distClientDir,\n isProduction,\n assetBase,\n scriptPolicy,\n}: {\n rendered: RenderResult;\n template: string;\n manifest?: Record<string, any>;\n root: string;\n distClientDir: string;\n isProduction: boolean;\n assetBase: string;\n scriptPolicy?: ScriptBundlePolicy;\n}): Promise<globalThis.Response> {\n const rawResponse = createRawResponse(rendered);\n if (rawResponse) {\n return rawResponse;\n }\n\n if (rendered.redirect) {\n return new globalThis.Response(null, {\n status: rendered.redirect.statusCode,\n headers: {\n Location: rendered.redirect.url,\n },\n });\n }\n\n let scriptSrcList: string[] = rendered.scripts || [];\n let cssSrcList: string[] = rendered.styles || [];\n const islandEntries = rendered.islands || [];\n let cacheTags: string[] = rendered.cacheTags || [];\n const maxAge: number | undefined = rendered.maxAge;\n const sMaxAge: number | undefined = rendered.sMaxAge;\n const swr: number | undefined = rendered.swr;\n\n let extraHead = '';\n const emittedStyles = new Set<string>();\n const appendStylesheet = (href: string, crossorigin = false) => {\n if (emittedStyles.has(href)) return;\n emittedStyles.add(href);\n extraHead += `<link rel=\"stylesheet\"${crossorigin ? ' crossorigin' : ''} href=\"${escapeProp(href)}\">`;\n };\n let globalScripts: string[] = [];\n let islandRegistryScript = '';\n\n const globalStyleHref = resolveGlobalStyleHref({\n root,\n manifest,\n isProduction,\n base: assetBase,\n });\n if (globalStyleHref) appendStylesheet(globalStyleHref, isProduction);\n\n // Externalized island props → a single JSON script at the end of the document.\n // serialize-javascript with isJSON escapes `<`, `>`, `&` (and U+2028/2029) so the\n // props can't break out of the <script> block or inject markup (XSS). The runtime\n // reads it via `document.getElementById('_l5e_data_')` + JSON.parse.\n let islandDataScript = '';\n if (rendered.islandData && rendered.islandData.length > 0) {\n islandDataScript = `<script type=\"application/json\" id=\"_l5e_data_\">${serialize(\n rendered.islandData,\n { isJSON: true },\n )}</script>`;\n }\n\n if (isProduction && manifest) {\n scriptSrcList = scriptSrcList.filter(\n (src) => src.replace(/^\\//, '') !== 'src/client.global.ts',\n );\n cssSrcList = cssSrcList.filter((src) => !src.includes('.global.'));\n\n const cssFiles = new Set<string>();\n const preloadFiles = new Set<string>();\n\n function collectFromEntry(entryKey: string): { file: string | null } {\n const entry = manifest![entryKey];\n if (!entry) return { file: null };\n\n if (entry.css) entry.css.forEach((css: string) => cssFiles.add(css));\n if (entry.imports) {\n entry.imports.forEach((importKey: string) => {\n const importedChunk = manifest![importKey];\n if (importedChunk?.file) preloadFiles.add(importedChunk.file);\n if (importedChunk?.css) {\n importedChunk.css.forEach((css: string) => cssFiles.add(css));\n }\n if (importedChunk?.imports) {\n importedChunk.imports.forEach((key: string) => {\n const chunk = manifest![key];\n if (chunk?.file) preloadFiles.add(chunk.file);\n if (chunk?.css) chunk.css.forEach((css: string) => cssFiles.add(css));\n });\n }\n });\n }\n\n return { file: entry.file };\n }\n\n const mappedScripts: string[] = [];\n for (const src of scriptSrcList) {\n const entryKey = src.replace(/^\\//, '');\n const { file } = collectFromEntry(entryKey);\n if (file) mappedScripts.push(`/${file}`);\n }\n\n const mappedCssFiles: string[] = [];\n for (const src of cssSrcList) {\n const entryKey = src.replace(/^\\//, '');\n const { file } = collectFromEntry(entryKey);\n if (file) {\n mappedCssFiles.push(`/${file}`);\n cssFiles.add(file);\n }\n }\n\n if (mappedScripts.length > 0) {\n const bundledScript = await bundleScripts(mappedScripts, distClientDir, scriptPolicy);\n scriptSrcList = bundledScript.filename\n ? [withAssetBase(assetBase, bundledScript.filename)]\n : mappedScripts.map((file) => withAssetBase(assetBase, file));\n }\n\n if (mappedCssFiles.length > 0) {\n const bundledCss = await bundleCss(mappedCssFiles, distClientDir);\n if (bundledCss.filename) {\n cssSrcList = [withAssetBase(assetBase, bundledCss.filename)];\n }\n }\n\n const globalEntry = manifest['src/client.global.ts'];\n if (globalEntry) {\n if (globalEntry.css && globalEntry.css.length > 0) {\n globalEntry.css.forEach((cssFile: string) => {\n appendStylesheet(withAssetBase(assetBase, cssFile), true);\n });\n }\n if (globalEntry.file) {\n globalScripts.push(withAssetBase(assetBase, globalEntry.file));\n }\n }\n\n if (islandEntries.length > 0) {\n const islandMap: Record<string, string> = {};\n for (const island of islandEntries) {\n const entry = manifest[island.src];\n if (entry?.file) {\n islandMap[island.key] = withAssetBase(assetBase, entry.file);\n }\n }\n if (Object.keys(islandMap).length > 0) {\n islandRegistryScript = `<script>window.__L5E_ISLANDS__=${serialize(islandMap)}</script>`;\n }\n }\n\n if (cssSrcList.length > 0) {\n cssSrcList.forEach((file) => appendStylesheet(file, true));\n }\n }\n\n if (!isProduction) {\n // Registry đã dedupe, nhưng vẫn lọc lại ở đây để dev không bao giờ ra thẻ trùng\n [...new Set(cssSrcList)].forEach((src) => appendStylesheet(src));\n }\n\n let allScripts = [...globalScripts, ...scriptSrcList];\n\n if (!isProduction) {\n const globalTsPath = path.join(root, 'src', 'client.global.ts');\n if (existsSync(globalTsPath)) {\n allScripts = ['/src/client.global.ts', ...allScripts];\n }\n\n // useClientJs('/src/client.global.ts') do user tự gọi sẽ trùng với entry\n // được prepend ở trên — registry không bắt được ca này nên dedupe lại\n allScripts = [...new Set(allScripts)];\n\n if (islandEntries.length > 0) {\n const islandMap: Record<string, string> = {};\n for (const island of islandEntries) {\n islandMap[island.key] = `/${island.src}`;\n }\n islandRegistryScript = `<script>window.__L5E_ISLANDS__=${serialize(islandMap)}</script>`;\n }\n }\n\n const scriptsHtml =\n islandDataScript +\n islandRegistryScript +\n allScripts.map((src) => `<script type=\"module\" src=\"${src}\"></script>`).join('');\n\n const templateWithLang = rendered.lang ? applyHtmlLang(template, rendered.lang) : template;\n\n // Replacer fns (not strings) so `$`-sequences in rendered HTML — e.g. an SSR\n // island body or user text containing \"$$\" / \"$&\" — aren't interpreted as\n // special String.replace replacement patterns.\n const html = rendered.rawHtml\n ? rendered.html || ''\n : templateWithLang\n .replace(`<!--app-head-->`, () => (rendered.head ?? '') + extraHead)\n .replace(`<!--app-html-->`, () => rendered.html ?? '')\n .replace(`<!--app-scripts-->`, () => scriptsHtml);\n\n const headers = new Headers({\n 'Content-Type': 'text/html',\n });\n\n const cacheControlParts: string[] = [];\n const cdnCacheControlParts: string[] = [];\n if (maxAge !== undefined) {\n cacheControlParts.push(`max-age=${maxAge}`);\n cdnCacheControlParts.push(`max-age=${maxAge}`);\n }\n\n if (sMaxAge !== undefined) {\n cdnCacheControlParts.push(`s-maxage=${sMaxAge}`);\n }\n\n // ko cho phép browser swr\n if (swr !== undefined) {\n cdnCacheControlParts.push(`stale-while-revalidate=${swr}`);\n }\n\n if ((cacheControlParts.length > 0 || cdnCacheControlParts.length > 0) && isProduction) {\n cacheControlParts.push('public');\n cacheControlParts.push('must-revalidate');\n\n cdnCacheControlParts.push('public');\n\n headers.set('Cache-Control', cacheControlParts.join(', '));\n headers.set('CDN-Cache-Control', cdnCacheControlParts.join(', '));\n }\n\n if (process.env.NODE_ENV === 'production') {\n cacheTags = optimizeCacheTags(cacheTags);\n }\n headers.set('Cache-Tag', ['global', ...cacheTags].join(','));\n\n return new globalThis.Response(html, {\n status: rendered.statusCode || 200,\n headers,\n });\n}\n\nexport async function createServer(options: ServerOptions = {}): Promise<ServerContext> {\n const root = options.root || process.cwd();\n const base = options.base || '/';\n const isProduction = process.env.NODE_ENV === 'production';\n const externalizeIslandProps = options.externalizeIslandProps ?? true;\n\n // Cached production assets\n const templateHtml = isProduction\n ? await fs.readFile(path.join(root, './index.html'), 'utf-8')\n : '';\n\n // Create http server\n // @ts-ignore\n const express = (await import('express')).default;\n const app = options.app || express();\n\n // Serve static files from public directory\n if (options.publicDir) {\n const publicPath = path.isAbsolute(options.publicDir)\n ? options.publicDir\n : path.join(root, options.publicDir);\n\n if (existsSync(publicPath)) {\n app.use(express.static(publicPath));\n }\n }\n\n // Add Vite or respective production middlewares\n let vite: ViteDevServer | undefined;\n const distClientDir = path.join(root, './dist/client');\n // A production server owns one immutable build. Share its manifest/policy\n // across requests, just like its cached HTML template and SSR module.\n const productionManifest: Manifest | undefined = isProduction\n ? JSON.parse(await fs.readFile(path.join(distClientDir, '.vite/manifest.json'), 'utf-8'))\n : undefined;\n let scriptPolicy: ScriptBundlePolicy | undefined;\n if (productionManifest) {\n try {\n scriptPolicy = createScriptBundlePolicy(productionManifest, distClientDir, base);\n } catch (error) {\n console.warn('[bundler] Invalid build manifest; serving original script entries.', error);\n }\n }\n\n if (!isProduction) {\n const { createServer } = await import('vite');\n const configFile = path.join(root, 'vite.config.js');\n vite = await createServer({\n root,\n configFile,\n server: { middlewareMode: true },\n appType: 'custom',\n base,\n optimizeDeps: {\n exclude: ['@withl5e/l5e', 'file-type'],\n },\n ssr: {\n resolve: {\n conditions: ['development', 'default'],\n },\n },\n resolve: {\n conditions: ['development', 'default'],\n },\n });\n app.use(vite.middlewares);\n } else {\n // @ts-ignore\n const compression = (await import('compression')).default;\n // @ts-ignore\n const sirv = (await import('sirv')).default;\n app.use(compression());\n app.use(base, sirv(distClientDir, { extensions: [] }));\n\n // Route để serve bundled files từ memory map\n // Äặt route nà y trước route HTML để catch request trước\n app.get(\n withAssetBase(base, 'bundle-:hash.:ext'),\n async (req: ExpressRequest, res: ExpressResponse) => {\n try {\n const { hash, ext } = req.params;\n const filename = `bundle-${hash}.${ext}`;\n const bundledFile = getBundledFile(filename);\n\n if (!bundledFile) {\n return res.status(404).send('Bundled file not found');\n }\n\n res.set({\n 'Content-Type': bundledFile.mimeType,\n 'Cache-Control': 'public, max-age=31536000, immutable',\n });\n res.send(bundledFile.content);\n } catch (e: any) {\n console.error('[server] Error serving bundled file:', e);\n res.status(500).end('Internal server error');\n }\n },\n );\n }\n\n // Action routes — JSON body parsing scoped to action endpoints only\n app.use('/_l5e/action', express.json({ limit: '100kb' }));\n\n // Validate action key format: actionName_hexHash\n const ACTION_KEY_RE = /^[a-zA-Z]\\w+_[0-9a-f]{1,4}$/;\n\n // Load action registry and viewActions glob from virtual module (dev) or built bundle (prod)\n let prodActionRegistry: Record<string, { modulePath: string; actionName: string }> | null = null;\n let prodViewActions: Record<string, () => Promise<any>> | null = null;\n\n async function getActionRegistry(): Promise<\n Record<string, { modulePath: string; actionName: string }>\n > {\n if (!isProduction) {\n const mod = await vite!.ssrLoadModule('virtual:l5e-actions');\n return mod.actionRegistry || {};\n }\n if (!prodActionRegistry) {\n const registryPath = path.join(root, './dist/server/action-registry.json');\n const json = await fs.readFile(registryPath, 'utf-8');\n prodActionRegistry = JSON.parse(json);\n }\n return prodActionRegistry!;\n }\n\n async function getViewActions(): Promise<Record<string, () => Promise<any>>> {\n if (!isProduction) {\n const mod = await vite!.ssrLoadModule('virtual:l5e-actions');\n return mod.viewActions || {};\n }\n if (!prodViewActions) {\n const entryServerPath = path.join(root, './dist/server/entry-server.js');\n const mod = await import(pathToFileURL(entryServerPath).href);\n prodViewActions = mod.viewActions || {};\n }\n return prodViewActions!;\n }\n\n // Action route handler — hashed action keys\n // URL: /_l5e/action/:actionKey (e.g., /_l5e/action/loadMoreComments_a1b2)\n app.all('/_l5e/action/:actionKey', async (req: ExpressRequest, res: ExpressResponse) => {\n try {\n const { actionKey } = req.params;\n\n // Validate action key format\n if (!ACTION_KEY_RE.test(actionKey)) {\n return res.status(400).send('Invalid action key');\n }\n\n // Look up action in registry\n const registry = await getActionRegistry();\n const entry = registry[actionKey];\n if (!entry) {\n return res.status(404).send('Action not found');\n }\n\n const { modulePath, actionName } = entry;\n\n // Import action module via viewActions glob (works in both dev and prod)\n let actionModule: any;\n if (!isProduction) {\n try {\n actionModule = await vite!.ssrLoadModule(`/src/${modulePath}/actions.tsx`);\n } catch {\n actionModule = await vite!.ssrLoadModule(`/src/${modulePath}/actions.ts`);\n }\n } else {\n const viewActions = await getViewActions();\n // Find matching glob entry by modulePath\n const globKey = viewActions[`/src/${modulePath}/actions.tsx`]\n ? `/src/${modulePath}/actions.tsx`\n : viewActions[`/src/${modulePath}/actions.ts`]\n ? `/src/${modulePath}/actions.ts`\n : null;\n if (!globKey) {\n return res.status(404).send('Action module not found');\n }\n actionModule = await viewActions[globKey]();\n }\n\n // Look up exported action — use hasOwnProperty to avoid prototype pollution\n if (!Object.prototype.hasOwnProperty.call(actionModule, actionName)) {\n return res.status(404).send('Action not found');\n }\n const action = actionModule[actionName];\n if (!action || !action.handler) {\n return res.status(404).send('Action not found');\n }\n\n // Enforce the action's declared HTTP method. Previously `app.all` accepted\n // any method and `action.method` was ignored, so a state-changing POST\n // action could be triggered via GET (e.g. <img src>) — a CSRF vector.\n const allowedMethod = (action.method || 'GET').toUpperCase();\n if (req.method.toUpperCase() !== allowedMethod) {\n return res.status(405).set('Allow', allowedMethod).send('Method Not Allowed');\n }\n\n // Run the request through the middleware chain first — same as the HTML\n // handler — so `locals` (locale, country, preview flags, ...) is\n // populated for actions too. Previously this endpoint built its own\n // bare `requestInfo` and never invoked middleware at all, so `locals`\n // (and thus `getLocale()`) was always empty inside an action handler.\n //\n // Crucially, the action must run *inside* the `next` callback passed to\n // the middleware — not after `await`ing the middleware call — because a\n // middleware like `fromFetchMiddleware(paraglideMiddleware)` calls\n // `next()` synchronously inside a library-owned `AsyncLocalStorage.run()`\n // scope. Running the action afterward, once that scope has already\n // closed, would silently lose the library's own ambient `getLocale()`.\n const entryServerPath = path.join(root, './dist/server/entry-server.js');\n const entryServerModule: EntryServerModule = isProduction\n ? await import(pathToFileURL(entryServerPath).href)\n : await vite!.ssrLoadModule('@withl5e/l5e/entry-server');\n const { runInRenderContext } = await (isProduction\n ? import(pathToFileURL(entryServerPath).href)\n : vite!.ssrLoadModule('@withl5e/l5e/jsx-runtime'));\n const { renderJsxToHtmlString } = await (isProduction\n ? import(pathToFileURL(entryServerPath).href)\n : vite!.ssrLoadModule('@withl5e/l5e'));\n\n const locals: Record<string, unknown> = {};\n const initialRequest = createWebRequestFromExpress(req);\n const middlewareContext = createContext({\n request: initialRequest,\n locals,\n clientAddress: requestIp.getClientIp(req) ?? undefined,\n });\n\n const loadedMiddleware = await entryServerModule.loadMiddleware?.();\n const middlewareHandler: MiddlewareHandler =\n typeof loadedMiddleware === 'function'\n ? loadedMiddleware\n : (_ctx, dummyNext) => dummyNext();\n\n // If middleware short-circuits (returns its own Response — a redirect, a\n // 403, ...) instead of calling `next`, this callback never runs and that\n // response wins, same as it would for a page request.\n const response = await middlewareHandler(middlewareContext, async (payload) => {\n const nextRequest = createRewriteRequest(\n payload,\n middlewareContext.request,\n middlewareContext.url,\n );\n const requestInfo = {\n ...createRequestInfo(req, nextRequest, base, locals),\n path: req.originalUrl,\n body: req.body,\n };\n\n const html = await runInRenderContext(\n async () => {\n const jsx = await action.handler(requestInfo);\n return renderJsxToHtmlString(jsx);\n },\n requestInfo,\n modulePath,\n );\n\n return new Response(html, { headers: { 'Content-Type': 'text/html' } });\n });\n\n await sendWebResponse(req, res, response);\n } catch (e: any) {\n vite?.ssrFixStacktrace?.(e);\n console.error('[l5e] Action error:', e.stack || e);\n res.status(500).send('Internal server error');\n }\n });\n\n // Serve HTML\n app.use(async (req: ExpressRequest, res: ExpressResponse) => {\n try {\n const url = req.originalUrl.replace(base, '');\n\n let template: string;\n let render: (\n url: string,\n requestInfo?: any,\n options?: { externalizeIslandProps?: boolean },\n ) => Promise<any>;\n let loadMiddleware: EntryServerModule['loadMiddleware'];\n let manifest: Record<string, any> | undefined;\n\n if (!isProduction) {\n // Always read fresh template in development\n template = await fs.readFile(path.join(root, './index.html'), 'utf-8');\n template = await vite!.transformIndexHtml(url, template);\n\n // Inject Vite HMR client for hot reload\n if (!template.includes('@vite/client')) {\n template = template.replace(\n '</head>',\n '<script type=\"module\" src=\"/@vite/client\"></script></head>',\n );\n }\n\n const entryServer = (await vite!.ssrLoadModule(\n '@withl5e/l5e/entry-server',\n )) as EntryServerModule;\n render = entryServer.render;\n loadMiddleware = entryServer.loadMiddleware;\n } else {\n template = templateHtml;\n const entryServerPath = path.join(root, './dist/server/entry-server.js');\n const entryServer = (await import(\n pathToFileURL(entryServerPath).href\n )) as EntryServerModule;\n render = entryServer.render;\n loadMiddleware = entryServer.loadMiddleware;\n manifest = productionManifest;\n }\n\n const loadedMiddleware = await loadMiddleware?.();\n const handler: MiddlewareHandler =\n typeof loadedMiddleware === 'function' ? loadedMiddleware : (_ctx, next) => next();\n\n const locals: Record<string, unknown> = {};\n const initialRequest = createWebRequestFromExpress(req);\n const context = createContext({\n request: initialRequest,\n requestInfo: createRequestInfo(req, initialRequest, base, locals),\n locals,\n clientAddress: requestIp.getClientIp(req),\n });\n\n const renderResponse = async (webRequest: globalThis.Request) => {\n const nextRequestInfo = createRequestInfo(req, webRequest, base, locals);\n const nextUrl = getRenderUrl(nextRequestInfo.url!, base);\n const nextRendered = await render(nextUrl, nextRequestInfo, {\n externalizeIslandProps,\n });\n return createPageResponse({\n rendered: nextRendered,\n template,\n manifest,\n root,\n distClientDir,\n isProduction,\n assetBase: isProduction ? base : vite!.config.base,\n scriptPolicy,\n });\n };\n\n const next = async (payload?: RewritePayload) => {\n const nextRequest = createRewriteRequest(payload, context.request, context.url);\n context.request = nextRequest;\n context.url = new URL(nextRequest.url);\n context.cookies = parseCookies(nextRequest.headers.get('cookie') ?? undefined);\n context.requestInfo = createRequestInfo(req, nextRequest, base, locals);\n return renderResponse(nextRequest);\n };\n\n context.rewrite = (payload: RewritePayload) => next(payload);\n\n const response = await handler(context, next);\n await sendWebResponse(req, res, response);\n } catch (e: any) {\n vite?.ssrFixStacktrace?.(e);\n console.error(e.stack);\n // Never leak stack traces to the client in production (info disclosure).\n res.status(500).end(isProduction ? 'Internal Server Error' : e.stack);\n }\n });\n\n return { app, vite };\n}\n\nexport async function startServer(options: ServerOptions = {}): Promise<void> {\n const port = options.port || 5173;\n\n // Create Express app first\n // @ts-ignore\n const express = (await import('express')).default;\n const app = express();\n\n // Call callback if provided to allow custom routes before setting up L5E server\n if (options.setupApp) {\n console.log('setupApp');\n await options.setupApp(app);\n }\n\n const { app: serverApp } = await createServer({ ...options, app });\n\n serverApp.listen(port, () => {\n console.log(`Server started at http://localhost:${port}`);\n });\n}\n\nconst MAX_TAGS = 1000;\n\nexport function hashTag(tag: string): string {\n // global tag is not hashed, better for ci/cd\n if (tag === 'global') {\n return 'global';\n }\n\n let hash = 0;\n for (let i = 0; i < tag.length; i++) {\n const char = tag.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return Math.abs(hash).toString(36).substring(0, 8);\n}\n\nexport function optimizeCacheTags(tags: Set<string> | string[]): string[] {\n const _tags = Array.isArray(tags) ? tags : [...tags];\n const result = _tags.slice(0, MAX_TAGS).map(hashTag);\n return result;\n}\n"],"mappings":"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"}
|
package/dist/vite-plugin.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vite-plugin.js","names":[],"sources":["../src/core/vite-plugin.ts"],"sourcesContent":["import { transform } from 'esbuild';\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n rmSync,\n statSync,\n writeFileSync,\n} from 'fs';\nimport { dirname, join, relative, resolve } from 'path';\nimport type { Plugin, UserConfig } from 'vite';\nimport { findGlobalStyleInput } from './global-style';\n\nconst VIRTUAL_L5E_VIEWS = 'virtual:l5e-views';\nconst VIRTUAL_L5E_ROUTE = 'virtual:l5e-route';\nconst VIRTUAL_L5E_SSR_ENTRY = 'virtual:l5e-ssr-entry';\nconst VIRTUAL_L5E_GLOBAL_LOADER = 'virtual:l5e-global-loader';\nconst VIRTUAL_L5E_ISLAND_STRATEGIES = 'virtual:l5e-island-strategies';\nconst VIRTUAL_L5E_ISLANDS = 'virtual:l5e-islands';\nconst VIRTUAL_L5E_ACTIONS = 'virtual:l5e-actions';\nconst VIRTUAL_L5E_MIDDLEWARE = 'virtual:l5e-middleware';\n\n/**\n * Vite's built-in env vars on `import.meta.env`. These are statically replaced\n * by Vite itself (DEV/PROD/SSR are booleans, MODE/BASE_URL strings), so the SSR\n * `import.meta.env.* -> process.env.*` rewrite must skip them — otherwise it\n * clobbers Vite defaults (e.g. `import.meta.env.DEV` would become the undefined\n * `process.env.DEV`).\n */\nconst VITE_RESERVED_ENV = new Set(['MODE', 'BASE_URL', 'PROD', 'DEV', 'SSR', 'LEGACY']);\n\n/**\n * Recursively scan directory for .tsx and .ts files\n */\nfunction scanTsFiles(dir: string, fileList: string[] = []): string[] {\n const files = readdirSync(dir);\n\n for (const file of files) {\n const filePath = join(dir, file);\n const stat = statSync(filePath);\n\n if (stat.isDirectory()) {\n // Skip node_modules and dist directories\n if (file === 'node_modules' || file === 'dist' || file === '.git') {\n continue;\n }\n scanTsFiles(filePath, fileList);\n } else if (file.endsWith('.tsx') || file.endsWith('.ts')) {\n fileList.push(filePath);\n }\n }\n\n return fileList;\n}\n\n/**\n * Short hash function for island keys\n */\nfunction shortHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) - hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return Math.abs(hash).toString(16).slice(0, 4);\n}\n\n/**\n * Derive component name from path: \"./react/Counter\" → \"Counter\"\n */\nfunction deriveComponentName(fromPath: string): string {\n const segments = fromPath.split('/');\n let filename = segments[segments.length - 1];\n // Remove extension if present\n filename = filename.replace(/\\.(tsx?|jsx?)$/, '');\n return filename;\n}\n\n/**\n * Create island registry key: \"/src/views/test-island/react/Counter\" → \"Counter_a3f2\"\n */\nfunction makeIslandKey(resolvedPath: string): string {\n const name = deriveComponentName(resolvedPath);\n return `${name}_${shortHash(resolvedPath)}`;\n}\n\n/**\n * Resolve file path with extension (.tsx, .ts, .jsx, .js)\n * Returns the relative path with extension (e.g., \"src/views/.../Counter.tsx\")\n * or null if file not found\n */\nfunction resolveWithExtension(resolvedPath: string, rootDir: string): string | null {\n // resolvedPath: \"/src/views/test-island/react/Counter\" (no extension, leading /)\n const relPath = resolvedPath.replace(/^\\//, ''); // \"src/views/.../Counter\"\n const absBase = resolve(rootDir, relPath);\n\n // Check with extensions\n for (const ext of ['.tsx', '.ts', '.jsx', '.js']) {\n if (existsSync(absBase + ext)) {\n return relPath + ext; // \"src/views/.../Counter.tsx\"\n }\n }\n // Check without extension (file might already have one)\n if (existsSync(absBase)) {\n return relPath;\n }\n return null;\n}\n\n/**\n * Extract island entries from file using regex on JSX source\n */\nfunction extractIslandEntries(\n filePath: string,\n rootDir: string,\n): Array<{ component: string; resolvedPath: string; src: string; key: string }> {\n const content = readFileSync(filePath, 'utf-8');\n const entries: Array<{ component: string; resolvedPath: string; src: string; key: string }> = [];\n\n // Regex scan JSX source (NOT post-transform code).\n // Just find <ClientIsland ... from=\"Y\" ...> — component name derived from path.\n const regex = /<ClientIsland\\s[^>]*?from\\s*=\\s*\"([^\"]+)\"/g;\n\n const seen = new Set<string>();\n let match;\n\n while ((match = regex.exec(content)) !== null) {\n const fromPath = match[1];\n\n // Resolve relative path → absolute\n let resolvedPath: string;\n if (fromPath.startsWith('~/')) {\n // Alias ~/ → /src/ (project convention)\n resolvedPath = '/src/' + fromPath.substring(2);\n } else if (fromPath.startsWith('/src/')) {\n resolvedPath = fromPath;\n } else if (fromPath.startsWith('./') || fromPath.startsWith('../')) {\n const abs = resolve(filePath, '..', fromPath);\n resolvedPath = '/' + relative(rootDir, abs).replace(/\\\\/g, '/');\n } else {\n resolvedPath = fromPath;\n }\n\n // Resolve file extension for manifest compatibility\n const src = resolveWithExtension(resolvedPath, rootDir);\n if (!src) {\n console.warn(\n `[l5e] Island component not found: ${resolvedPath} (from ${fromPath} in ${filePath})`,\n );\n continue;\n }\n\n const key = makeIslandKey(resolvedPath);\n if (!seen.has(key)) {\n seen.add(key);\n entries.push({\n component: deriveComponentName(resolvedPath),\n resolvedPath,\n src, // \"src/views/.../Counter.tsx\" — matches manifest key format\n key,\n });\n }\n }\n return entries;\n}\n\n/**\n * Extract paths from useCss and useClientJs calls using regex\n */\nfunction extractPathsFromFile(filePath: string, rootDir: string): { css: string[]; js: string[] } {\n const content = readFileSync(filePath, 'utf-8');\n const css: string[] = [];\n const js: string[] = [];\n\n // Regex patterns to match useCss('path') or useCss(\"path\")\n // Handles single and double quotes, and escaped quotes\n const useCssPattern = /useCss\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*,?\\s*\\)/g;\n const useClientJsPattern = /useClientJs\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*,?\\s*\\)/g;\n\n let match;\n\n // Extract CSS paths\n while ((match = useCssPattern.exec(content)) !== null) {\n const path = match[1];\n // Convert relative path to absolute if needed\n if (path.startsWith('/src/')) {\n css.push(path);\n } else if (path.startsWith('./') || path.startsWith('../')) {\n // Resolve relative path\n const absolutePath = resolve(filePath, '..', path);\n const relativePath = '/' + relative(rootDir, absolutePath).replace(/\\\\/g, '/');\n css.push(relativePath);\n } else {\n css.push(path);\n }\n }\n\n // Extract JS paths\n while ((match = useClientJsPattern.exec(content)) !== null) {\n const path = match[1];\n if (path.startsWith('/src/')) {\n js.push(path);\n } else if (path.startsWith('./') || path.startsWith('../')) {\n const absolutePath = resolve(filePath, '..', path);\n const relativePath = '/' + relative(rootDir, absolutePath).replace(/\\\\/g, '/');\n js.push(relativePath);\n } else {\n js.push(path);\n }\n }\n\n return { css, js };\n}\n\n/**\n * Auto-discover bundler input entries from useCss and useClientJs calls\n */\nfunction discoverBundlerInput(rootDir: string): {\n input: Record<string, string>;\n islandRegistry: Map<string, string>;\n pathToKey: Map<string, string>;\n keyToSrc: Map<string, string>;\n actionRegistry: Map<string, { modulePath: string; actionName: string }>;\n} {\n const input: Record<string, string> = {};\n const srcDir = join(rootDir, 'src');\n\n // Island registries\n const islandRegistry = new Map<string, string>(); // key → resolvedPath\n const pathToKey = new Map<string, string>(); // resolvedPath → key\n const keyToSrc = new Map<string, string>(); // key → src (manifest-compatible path with extension)\n\n // Action registry: actionKey → { modulePath, actionName }\n const actionRegistry = new Map<string, { modulePath: string; actionName: string }>();\n\n try {\n // Check if src directory exists\n if (!statSync(srcDir).isDirectory()) {\n return { input, islandRegistry, pathToKey, keyToSrc, actionRegistry };\n }\n\n // Check if src/client.global.ts exists and add it as an entry\n const globalTsPath = join(rootDir, 'src', 'client.global.ts');\n if (existsSync(globalTsPath)) {\n input['global'] = globalTsPath;\n console.log('[l5e] Detected src/client.global.ts and added as entry');\n }\n\n const globalStyleInput = findGlobalStyleInput(rootDir);\n if (globalStyleInput) {\n input['global-style'] = globalStyleInput;\n console.log('[l5e] Detected src/global.css and added as global stylesheet');\n }\n\n // Scan all .tsx and .ts files\n const tsFiles = scanTsFiles(srcDir);\n\n // Extract all paths\n const allCssPaths = new Set<string>();\n const allJsPaths = new Set<string>();\n\n for (const file of tsFiles) {\n const { css, js } = extractPathsFromFile(file, rootDir);\n css.forEach((path) => allCssPaths.add(path));\n js.forEach((path) => allJsPaths.add(path));\n\n // Extract island entries\n const islandEntries = extractIslandEntries(file, rootDir);\n for (const entry of islandEntries) {\n islandRegistry.set(entry.key, entry.resolvedPath);\n pathToKey.set(entry.resolvedPath, entry.key);\n keyToSrc.set(entry.key, entry.src);\n }\n\n // Extract action entries from actions.ts/tsx files\n const normalizedFile = file.replace(/\\\\/g, '/');\n if (/\\/actions\\.(ts|tsx)$/.test(normalizedFile)) {\n const content = readFileSync(file, 'utf-8');\n const actionExportRegex = /export\\s+const\\s+(\\w+)\\s*=\\s*defineAction\\s*\\(/g;\n let actionMatch;\n\n // Compute modulePath: relative path from src/ to parent dir\n const relFromSrc = relative(srcDir, dirname(file)).replace(/\\\\/g, '/');\n const modulePath = relFromSrc || '.';\n\n while ((actionMatch = actionExportRegex.exec(content)) !== null) {\n const actionName = actionMatch[1];\n const actionKey = `${actionName}_${shortHash(modulePath)}`;\n actionRegistry.set(actionKey, { modulePath, actionName });\n }\n }\n }\n\n // Convert paths to bundler input entries\n // CSS files\n for (const cssPath of allCssPaths) {\n // Remove leading /src/ and convert to relative path\n const relativePath = cssPath.startsWith('/src/')\n ? cssPath.substring(1) // Remove leading /\n : cssPath.startsWith('/')\n ? cssPath.substring(1)\n : cssPath;\n\n const absolutePath = resolve(rootDir, relativePath);\n\n // src/global.css is already a dedicated convention entry. Keeping one\n // Separate input entries avoid collisions if a shared layout also calls useCss().\n if (globalStyleInput && resolve(absolutePath) === resolve(globalStyleInput)) {\n continue;\n }\n\n // Only add if file exists\n if (!existsSync(absolutePath)) {\n console.warn(`[l5e] CSS file not found: ${absolutePath} (from ${cssPath})`);\n continue;\n }\n\n // Generate entry name from path (e.g., /src/views/home/home.css -> views-home-home)\n const entryName = relativePath\n .replace(/^src\\//, '')\n .replace(/\\.css$/, '')\n .replace(/\\//g, '-')\n .replace(/\\\\/g, '-');\n\n input[entryName] = absolutePath;\n }\n\n // JS/TS files\n for (const jsPath of allJsPaths) {\n const relativePath = jsPath.startsWith('/src/')\n ? jsPath.substring(1)\n : jsPath.startsWith('/')\n ? jsPath.substring(1)\n : jsPath;\n\n const absolutePath = resolve(rootDir, relativePath);\n\n // Only add if file exists\n if (!existsSync(absolutePath)) {\n console.warn(`[l5e] JS/TS file not found: ${absolutePath} (from ${jsPath})`);\n continue;\n }\n\n // Generate entry name from path\n const entryName = relativePath\n .replace(/^src\\//, '')\n .replace(/\\.(ts|tsx|js|jsx)$/, '')\n .replace(/\\//g, '-')\n .replace(/\\\\/g, '-');\n\n input[entryName] = absolutePath;\n }\n\n // Add island component files to the bundler input so they appear in the manifest.\n // server.ts looks up manifest at runtime to resolve per-page island URLs.\n for (const [key, src] of keyToSrc) {\n const absolutePath = resolve(rootDir, src);\n input[`island-${key}`] = absolutePath;\n }\n\n if (islandRegistry.size > 0) {\n console.log(`[l5e] Detected ${islandRegistry.size} island(s)`);\n }\n if (actionRegistry.size > 0) {\n console.log(`[l5e] Detected ${actionRegistry.size} action(s)`);\n }\n } catch (error) {\n // Silently fail if directory doesn't exist or other errors\n console.warn('[l5e] Failed to discover bundler input:', error);\n }\n\n return { input, islandRegistry, pathToKey, keyToSrc, actionRegistry };\n}\n\n/**\n * Inject __key prop into ClientIsland calls\n * Use anchor-based approach to avoid fragile regex with nested objects\n */\nfunction injectIslandKeys(\n code: string,\n fileId: string,\n rootDir: string,\n pathToKey: Map<string, string>,\n keyToSrc: Map<string, string>,\n): string {\n // After esbuild, code has form:\n // jsxFactory(ClientIsland, { from: \"./react/Counter\", props: { n: 5 } })\n // ^anchor ^find from here\n\n const result: string[] = [];\n let lastIndex = 0;\n\n // Find each position \"ClientIsland,\" (anchor)\n const anchorRegex = /ClientIsland\\s*,\\s*\\{/g;\n let anchorMatch;\n\n while ((anchorMatch = anchorRegex.exec(code)) !== null) {\n const searchStart = anchorMatch.index + anchorMatch[0].length;\n\n // Scan for from: \"...\" in window ~500 chars after anchor\n const window = code.substring(searchStart, searchStart + 500);\n const fromMatch = /from:\\s*\"([^\"]+)\"/.exec(window);\n\n if (!fromMatch) continue;\n\n const fromPath = fromMatch[1];\n\n // Resolve path (handle ~/ alias)\n let resolved: string;\n if (fromPath.startsWith('~/')) {\n resolved = '/src/' + fromPath.substring(2);\n } else if (fromPath.startsWith('/src/')) {\n resolved = fromPath;\n } else if (fromPath.startsWith('./') || fromPath.startsWith('../')) {\n const abs = resolve(fileId, '..', fromPath);\n resolved = '/' + relative(rootDir, abs).replace(/\\\\/g, '/');\n } else {\n resolved = fromPath;\n }\n\n const key = pathToKey.get(resolved);\n if (!key) continue;\n\n const src = keyToSrc.get(key);\n if (!src) continue;\n\n // Inject __key and __src right after from: \"...\"\n const insertPos = searchStart + fromMatch.index + fromMatch[0].length;\n result.push(code.substring(lastIndex, insertPos));\n result.push(`, __key: \"${key}\", __src: \"${src}\"`);\n lastIndex = insertPos;\n }\n\n result.push(code.substring(lastIndex));\n return result.join('');\n}\n\nexport function coreVite(): Plugin {\n let rootDir: string = process.cwd();\n let islandRegistry = new Map<string, string>();\n let pathToKey = new Map<string, string>();\n let keyToSrc = new Map<string, string>();\n let actionRegistry = new Map<string, { modulePath: string; actionName: string }>();\n\n return {\n name: 'l5e-jsx-classic',\n enforce: 'pre',\n\n configResolved(resolvedConfig) {\n // Store root directory for later use\n rootDir = resolvedConfig.root || process.cwd();\n },\n\n handleHotUpdate({ file, server }) {\n const relPath = relative(rootDir, file);\n\n // Re-scan action registry when an actions file changes\n if (/actions\\.(ts|tsx)$/.test(file)) {\n const discovered = discoverBundlerInput(rootDir);\n actionRegistry = discovered.actionRegistry;\n // Invalidate the virtual:l5e-actions module so server picks up new registry\n const mod = server.moduleGraph.getModuleById('\\0' + VIRTUAL_L5E_ACTIONS);\n if (mod) {\n server.moduleGraph.invalidateModule(mod);\n }\n console.log(\n `[l5e] Action file changed: ${relPath} — re-scanned ${actionRegistry.size} action(s)`,\n );\n }\n\n // Allow Vite's default HMR for CSS and client-side JS files\n if (file.endsWith('.css')) {\n console.log(`[l5e] CSS changed: ${relPath} - using Vite HMR`);\n return; // Let Vite handle CSS HMR\n }\n\n if (file.includes('client.ts')) {\n console.log(`[l5e] Client JS changed: ${relPath} - using Vite HMR`);\n return; // Let Vite handle client JS HMR\n }\n\n // Trigger full page reload for SSR-related file changes (components, loaders, routes)\n // This ensures that server-side rendered components update properly\n if (file.includes('/src/') || file.includes('\\\\src\\\\')) {\n console.log(`[l5e] SSR file changed: ${relPath} - triggering full reload`);\n server.ws.send({\n type: 'full-reload',\n path: '*',\n });\n return []; // Prevent Vite's default HMR behavior\n }\n },\n\n buildEnd() {\n // Cleanup temporary files after build\n const tempDir = join(rootDir, '.l5e-temp');\n if (existsSync(tempDir)) {\n try {\n rmSync(tempDir, { recursive: true, force: true });\n console.log('[l5e] Cleaned up temporary files');\n } catch (error) {\n console.warn('[l5e] Failed to cleanup temporary files:', error);\n }\n }\n },\n\n writeBundle(_options, _bundle) {\n // Emit action registry JSON for production server\n if (actionRegistry.size > 0) {\n const outDir = join(rootDir, 'dist', 'server');\n if (!existsSync(outDir)) {\n mkdirSync(outDir, { recursive: true });\n }\n const registryObj = Object.fromEntries(actionRegistry);\n writeFileSync(join(outDir, 'action-registry.json'), JSON.stringify(registryObj), 'utf-8');\n console.log(`[l5e] Wrote action-registry.json (${actionRegistry.size} actions)`);\n }\n },\n\n config(userConfig) {\n // Auto-discover bundler input from useCss and useClientJs\n const projectRoot = userConfig.root || process.cwd();\n const discovered = discoverBundlerInput(projectRoot);\n\n // Store island registries for use in other hooks\n islandRegistry = discovered.islandRegistry;\n pathToKey = discovered.pathToKey;\n keyToSrc = discovered.keyToSrc;\n actionRegistry = discovered.actionRegistry;\n\n // Merge with existing rolldownOptions.input if any\n const existingInput = userConfig.build?.rolldownOptions?.input || {};\n const mergedInput =\n typeof existingInput === 'object' && !Array.isArray(existingInput)\n ? { ...discovered.input, ...existingInput }\n : discovered.input;\n\n return {\n build: {\n rolldownOptions: {\n ...userConfig.build?.rolldownOptions,\n input: Object.keys(mergedInput).length > 0 ? mergedInput : undefined,\n // Preserve exports for island component entries — without this,\n // The bundler tree-shakes their exports since nothing imports them\n // (they're loaded at runtime via dynamic import from the island runtime).\n preserveEntrySignatures: 'exports-only',\n },\n },\n } satisfies UserConfig;\n },\n\n resolveId(id) {\n if (id === VIRTUAL_L5E_VIEWS) {\n return '\\0' + VIRTUAL_L5E_VIEWS;\n }\n if (id === VIRTUAL_L5E_ROUTE) {\n return '\\0' + VIRTUAL_L5E_ROUTE;\n }\n if (id === VIRTUAL_L5E_SSR_ENTRY) {\n return '\\0' + VIRTUAL_L5E_SSR_ENTRY;\n }\n if (id === VIRTUAL_L5E_GLOBAL_LOADER) {\n return '\\0' + VIRTUAL_L5E_GLOBAL_LOADER;\n }\n if (id === VIRTUAL_L5E_ISLAND_STRATEGIES) {\n return '\\0' + VIRTUAL_L5E_ISLAND_STRATEGIES;\n }\n if (id === VIRTUAL_L5E_ISLANDS) {\n return '\\0' + VIRTUAL_L5E_ISLANDS;\n }\n if (id === VIRTUAL_L5E_ACTIONS) {\n return '\\0' + VIRTUAL_L5E_ACTIONS;\n }\n if (id === VIRTUAL_L5E_MIDDLEWARE) {\n return '\\0' + VIRTUAL_L5E_MIDDLEWARE;\n }\n return null;\n },\n\n async transform(code, id, options) {\n // Auto-inject island runtime into client.global.ts so it's always loaded globally\n if (id.replace(/\\\\/g, '/').endsWith('src/client.global.ts') && !options?.ssr) {\n return {\n code: `import '@withl5e/l5e/island/runtime';\\n${code}`,\n map: null,\n };\n }\n\n // Client-side action transform: replace defineAction exports with fetch stubs\n // Supports actions anywhere under src/ (e.g., src/views/*, src/features/*, etc.)\n if (!options?.ssr) {\n const normalizedId = id.replace(/\\\\/g, '/');\n const actionMatch = normalizedId.match(/\\/src\\/(.+)\\/actions\\.(ts|tsx)$/);\n if (actionMatch) {\n const modulePath = actionMatch[1];\n\n // Parse exported action names\n const exportRegex = /export\\s+const\\s+(\\w+)\\s*=\\s*defineAction\\s*\\(/g;\n const actions: Array<{ name: string; method: string }> = [];\n let m;\n while ((m = exportRegex.exec(code)) !== null) {\n const name = m[1];\n // Find method for this action — scan from the defineAction( position\n const chunk = code.substring(m.index, m.index + 500);\n const methodMatch = chunk.match(/method:\\s*['\"](\\w+)['\"]/);\n const method = methodMatch ? methodMatch[1].toUpperCase() : 'GET';\n actions.push({ name, method });\n }\n\n if (actions.length > 0) {\n const stubs = actions.map(({ name, method }) => {\n const actionKey = `${name}_${shortHash(modulePath)}`;\n if (method === 'GET') {\n return `export async function ${name}(params) {\n const res = await fetch('/_l5e/action/${actionKey}?' + new URLSearchParams(params));\n if (!res.ok) throw Object.assign(new Error('HTTP ' + res.status), { status: res.status });\n return res;\n}`;\n }\n return `export async function ${name}(body) {\n const res = await fetch('/_l5e/action/${actionKey}', {\n method: '${method}',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n if (!res.ok) throw Object.assign(new Error('HTTP ' + res.status), { status: res.status });\n return res;\n}`;\n });\n\n return {\n code: stubs.join('\\n\\n'),\n map: null,\n };\n }\n }\n }\n\n // Skip node_modules\n if (id.includes('node_modules')) {\n return null;\n }\n\n // Skip files in /react/ directories\n if (id.includes('/react/') || id.includes('\\\\react\\\\')) {\n return null;\n }\n\n // Check if this is a JSX/TSX file that needs L5E JSX transformation\n const isJsxFile = /\\.(tsx|jsx)$/.test(id);\n\n if (isJsxFile) {\n try {\n const injected = `import { Fragment as __Fragment, jsxFactory } from \"@withl5e/l5e/jsx-runtime\"\\n${code}`;\n\n // Transform JSX using esbuild with L5E's JSX runtime (async)\n const result = await transform(injected, {\n loader: id.endsWith('.tsx') ? 'tsx' : 'jsx',\n jsx: 'transform',\n jsxFactory: 'jsxFactory',\n jsxFragment: '__Fragment',\n sourcemap: true,\n sourcefile: id,\n target: 'es2020',\n });\n\n let transformedCode = result.code;\n\n // Inject __key and __src for ALL ClientIsland calls\n if (transformedCode.includes('ClientIsland')) {\n transformedCode = injectIslandKeys(transformedCode, id, rootDir, pathToKey, keyToSrc);\n }\n\n return {\n code: transformedCode,\n map: result.map || null,\n };\n } catch (error) {\n // Log error but let Vite handle it\n console.error(`[l5e] Failed to transform JSX in ${id}:`, error);\n throw error;\n }\n }\n\n // Transform import.meta.env thà nh process.env ở server side\n // Server side có thể access tất cả env variables\n // Client side chỉ access được VITE_ env variables\n // Transform nà y chạy ở runtime khi module được load trong SSR context\n if (options?.ssr) {\n let transformedCode = code;\n let hasChanges = false;\n\n // Thay thế import.meta.env.VARIABLE_NAME thà nh process.env.VARIABLE_NAME\n // Match: import.meta.env.VITE_EVENT_CATEGORY_SLUG\n transformedCode = transformedCode.replace(\n /import\\.meta\\.env\\.([a-zA-Z_][a-zA-Z0-9_]*)/g,\n (match, varName) => {\n // Leave Vite's built-in env vars for Vite to handle.\n if (VITE_RESERVED_ENV.has(varName)) return match;\n hasChanges = true;\n return `process.env.${varName}`;\n },\n );\n\n // Thay thế import.meta.env['VARIABLE_NAME'] hoặc import.meta.env[\"VARIABLE_NAME\"]\n // thà nh process.env['VARIABLE_NAME'] hoặc process.env[\"VARIABLE_NAME\"]\n // Match: import.meta.env['VITE_EVENT_CATEGORY_SLUG'] hoặc import.meta.env[\"VITE_EVENT_CATEGORY_SLUG\"]\n transformedCode = transformedCode.replace(\n /import\\.meta\\.env\\[(['\"`])([^'\"`]+)\\1\\]/g,\n (match, quote, varName) => {\n // Leave Vite's built-in env vars for Vite to handle.\n if (VITE_RESERVED_ENV.has(varName)) return match;\n hasChanges = true;\n return `process.env[${quote}${varName}${quote}]`;\n },\n );\n\n if (hasChanges) {\n return {\n code: transformedCode,\n map: null, // Không cần source map cho env transform\n };\n }\n }\n return null;\n },\n\n load(id) {\n // Virtual module: l5e-views\n if (id === '\\0' + VIRTUAL_L5E_VIEWS) {\n return `\nexport const viewLoaders = import.meta.glob('/src/views/*/loader.{ts,tsx}');\nexport const viewComponents = import.meta.glob('/src/views/*/index.tsx');\n`;\n }\n\n // Virtual module: l5e-route\n if (id === '\\0' + VIRTUAL_L5E_ROUTE) {\n // Load /src/route.ts LAZILY (dynamic import inside the handler) rather\n // than via a static `export { default } from '/src/route.ts'`.\n //\n // A static re-export drags the user's route module — and everything it\n // imports — into entry-server's *static* SSR graph. Route files commonly\n // import the `@withl5e/l5e` barrel (for RedirectException, types, ...),\n // and that barrel re-exports `render` from entry-server. That closes a\n // static import cycle:\n // entry-server → virtual:l5e-route → /src/route.ts → @withl5e/l5e → entry-server\n // On the first dev request Vite can begin evaluating entry-server while\n // it is still suspended awaiting this very import, so `render()` runs\n // before `const __vite_ssr_import_N__ = await import('virtual:l5e-route')`\n // has been assigned — throwing \"Cannot access '__vite_ssr_import_N__'\n // before initialization\". A page reload \"fixes\" it only because the graph\n // is fully warm the second time.\n //\n // Importing lazily removes /src/route.ts from the static graph: entry-server\n // finishes initializing first, and the route module (with its barrel import\n // back to a now-complete entry-server) is pulled only on the first render()\n // call. No manual caching — Vite/ESM caches the evaluated module and later\n // imports still observe HMR invalidations of route.ts.\n return `export default function routeHandler(requestInfo) {\n return import('/src/route.ts').then((mod) => mod.default(requestInfo));\n}`;\n }\n\n // Virtual module: l5e-ssr-entry\n if (id === '\\0' + VIRTUAL_L5E_SSR_ENTRY) {\n return `export { render } from '@withl5e/l5e/entry-server';\nexport { viewActions } from 'virtual:l5e-actions';`;\n }\n\n // Virtual module: l5e-global-loader\n if (id === '\\0' + VIRTUAL_L5E_GLOBAL_LOADER) {\n return `export const globalLoader = import.meta.glob('/src/global-loader.{ts,tsx}', { eager: false });`;\n }\n\n // Virtual module: l5e-actions\n if (id === '\\0' + VIRTUAL_L5E_ACTIONS) {\n const registryObj = Object.fromEntries(actionRegistry);\n return `export const viewActions = import.meta.glob('/src/**/actions.{ts,tsx}');\nexport const actionRegistry = ${JSON.stringify(registryObj)};`;\n }\n\n // Virtual module: l5e-middleware\n if (id === '\\0' + VIRTUAL_L5E_MIDDLEWARE) {\n return `\nconst middlewareModules = import.meta.glob([\n '/src/middleware.{ts,tsx,js,jsx}',\n '/src/middleware/index.{ts,tsx,js,jsx}',\n]);\n\nconst middlewarePaths = [\n '/src/middleware.ts',\n '/src/middleware.tsx',\n '/src/middleware.js',\n '/src/middleware.jsx',\n '/src/middleware/index.ts',\n '/src/middleware/index.tsx',\n '/src/middleware/index.js',\n '/src/middleware/index.jsx',\n];\n\nexport async function loadMiddleware() {\n const middlewarePath = middlewarePaths.find((path) => middlewareModules[path]);\n if (!middlewarePath) return undefined;\n\n const mod = await middlewareModules[middlewarePath]();\n return mod.onRequest;\n}\n`;\n }\n\n // Virtual module: l5e-islands\n // Lazy glob of all React island components so the SSR entry can import a\n // component by its __src path and renderToString() it. Non-eager → only\n // islands actually present on a page (with ssr) get imported at runtime.\n if (id === '\\0' + VIRTUAL_L5E_ISLANDS) {\n return `export const islandModules = import.meta.glob('/src/**/react/*.{tsx,jsx}');`;\n }\n\n // Virtual module: l5e-island-strategies\n if (id === '\\0' + VIRTUAL_L5E_ISLAND_STRATEGIES) {\n // Check if src/island-strategies.ts exists\n const strategiesFile = join(rootDir, 'src', 'island-strategies.ts');\n if (existsSync(strategiesFile)) {\n // Re-export user's file → Vite will build this file\n return `import '/src/island-strategies.ts';`;\n }\n // No file → empty module, no error\n return `/* no custom island strategies */`;\n }\n\n return null;\n },\n };\n}\n\nexport default coreVite;\n"],"mappings":"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"}
|