@lomray/vite-ssr-boost 6.1.0 → 6.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lomray/vite-ssr-boost",
3
- "version": "6.1.0",
3
+ "version": "6.2.0",
4
4
  "description": "Vite plugin for create awesome SSR or SPA applications on React.",
5
5
  "type": "module",
6
6
  "keywords": [
package/services/build.js CHANGED
@@ -1,2 +1,2 @@
1
- import i from"node:child_process";import s from"node:fs";import t from"node:path";import e from"chalk";import{resolveConfig as o}from"vite";import n from"../cli/helpers/vite-reset-cache.js";import r from"../helpers/create-focus-only.js";import{createDevMarker as l}from"../helpers/dev-marker.js";import a from"../helpers/plugin-config.js";import u from"../helpers/process-stop.js";import c from"./server-config.js";import d from"./ssr-manifest.js";class h{isProd;nodeEnv;buildDir;viteConfig;pluginConfig;params={mode:"",clientOptions:"",serverOptions:"",focusOnly:"app",isWatch:!1,isUnlockRobots:!1,isEject:!1,isServerless:!1,isNoWarnings:!1};abortController=null;runningBuild=[];hasPreviewModeExitListener=!1;constructor(i){this.params={...this.params,...i}}async makeConfig(){const{mode:i}=this.params;this.viteConfig=await o({},"build",i,"production"),this.pluginConfig=a(this.viteConfig),this.buildDir=t.resolve(this.viteConfig.root,this.viteConfig.build.outDir),this.nodeEnv=process.env.NODE_ENV||"production",this.isProd="production"===this.nodeEnv}clearBuildFolder(){s.existsSync(this.buildDir)&&s.rmSync(this.buildDir,{recursive:!0})}getIsProd(){return this.isProd}getNodeEnv(){return this.nodeEnv}getRunningBuildNames(){return this.runningBuild.map((({name:i})=>i))}promisifyProcess(i,s=!1){const t=new Promise(((t,e)=>{i.on("exit",(i=>{t(i)})),i.on("close",(i=>{t(i)})),i.on("error",(i=>{e(i)})),s&&i.stderr?.on("data",(i=>{const s=Buffer.from(i).toString();(s.includes("warning")||s.includes("WARNING"))&&t(1)}))}));return i.stdout?.pipe(process.stdout),i.stderr?.pipe(process.stderr),{promise:t,command:i}}buildManifest(){console.info(e.blue("Building routes manifest file"));const i=c.init({isProd:this.isProd,mode:this.params.mode},{root:this.viteConfig.root,clientFile:this.pluginConfig.clientFile});d.get(i,{buildDir:this.viteConfig.build.outDir,viteAliases:this.viteConfig.resolve.alias,basename:this.viteConfig.base}).buildRoutesManifest()}unlockRobots(){const i=`${this.buildDir}/client/robots.txt`;if(!s.existsSync(i))return void console.warn(`Failed to unlock robots.txt, file not exist: ${i}`);const t=s.readFileSync(i,{encoding:"utf-8"}).replace(/Disallow: \/$/m,"Allow: /");s.writeFileSync(i,t,{encoding:"utf-8"}),console.info(e.blue("\nrobots.txt unlocked."))}eject(){const i=`${this.buildDir}/server/start.js`;s.writeFileSync(i,"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\n\nconst VERSION = process.env.VERSION || \"1.0.0\";\nconst PORT = process.env.PORT || 3000;\nconst IS_HOST = process.env.IS_HOST || \"0\";\nconst ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\n\nawait runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\n",{encoding:"utf-8"})}createServerless(){const i=`${this.buildDir}/server/serverless.js`;s.writeFileSync(i,"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\n\nexport default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\n",{encoding:"utf-8"})}async spawnBuild(s,t,e={}){const{mode:o,isNoWarnings:n}=this.params,{focusOnly:l=this.params.focusOnly,shouldWait:a=!1,env:u={}}=e,c=o&&!t.includes("--mode")?`--mode ${o}`:"",d=this.promisifyProcess(i.spawn(`vite build ${t} ${c} --emptyOutDir`,{signal:this.abortController.signal,stdio:[process.stdin,"pipe","pipe"],shell:!0,env:{...process.env,...u,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:r(l).isOnlyClient()?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}),n);this.runningBuild.push({name:s,buildProcess:d}),a&&await this.waitLastBuild()}async waitLastBuild(){const i=this.runningBuild.at(-1);if(!i)return;const s=await i.buildProcess.promise;u(s,!0)}runPreviewMode(){this.hasPreviewModeExitListener||(process.on("exit",(()=>{this.abortController.abort()})),this.hasPreviewModeExitListener=!0);const{onFinish:i}=this.params;let s=this.runningBuild.length;const t=e=>{Buffer.from(e).toString().includes("built in")&&(s-=1,s||(this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.removeListener("data",t)})),l(this.isProd,this.viteConfig),i?.()))};this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.on("data",t)}))}async build(){await this.makeConfig(),await n(),this.clearBuildFolder();const{clientOptions:i,serverOptions:s,onFinish:t,isWatch:e,focusOnly:o,isEject:a,isServerless:u,isUnlockRobots:c}=this.params,{outDir:d}=this.viteConfig.build,h=r(o);this.abortController=new AbortController,this.runningBuild=[],h.isClient()&&await this.spawnBuild("client",`${i} --outDir ${d}/client`,{shouldWait:!e}),h.isServer()&&(await this.spawnBuild("server",`${s} --outDir ${d}/server --ssr ${this.pluginConfig.serverFile}`,{shouldWait:!e}),e||(this.buildManifest(),a&&this.eject(),u&&this.createServerless()));const{entrypoint:p}=this.pluginConfig;if(p?.length&&h.isEntrypoint())for(const{name:i,type:s,serverFile:t,buildOptions:o=""}of p){const n=t&&"ssr"===s?`--ssr ${t}`:"";await this.spawnBuild(i,`${o} ${n} --outDir ${d}/${i}`,{shouldWait:!e,focusOnly:"ssr"===s?"server":"client",env:{SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME:i}})}e?this.runPreviewMode():(c&&this.unlockRobots(),l(this.isProd,this.viteConfig),t?.())}}export{h as default};
1
+ import i from"node:child_process";import s from"node:fs";import t from"node:path";import e from"chalk";import{resolveConfig as o}from"vite";import r from"../cli/helpers/vite-reset-cache.js";import n from"../helpers/create-focus-only.js";import{createDevMarker as l}from"../helpers/dev-marker.js";import a from"../helpers/plugin-config.js";import u from"../helpers/process-stop.js";import c from"./server-config.js";import d from"./ssr-manifest.js";class h{isProd;nodeEnv;buildDir;viteConfig;pluginConfig;params={mode:"",clientOptions:"",serverOptions:"",focusOnly:"app",isWatch:!1,isUnlockRobots:!1,isEject:!1,isServerless:!1,isNoWarnings:!1};abortController=null;runningBuild=[];hasPreviewModeExitListener=!1;constructor(i){this.params={...this.params,...i}}async makeConfig(){const{mode:i}=this.params;this.viteConfig=await o({},"build",i,"production"),this.pluginConfig=a(this.viteConfig),this.buildDir=t.resolve(this.viteConfig.root,this.viteConfig.build.outDir),this.nodeEnv=process.env.NODE_ENV||"production",this.isProd="production"===this.nodeEnv}clearBuildFolder(){s.existsSync(this.buildDir)&&s.rmSync(this.buildDir,{recursive:!0})}getIsProd(){return this.isProd}getNodeEnv(){return this.nodeEnv}getRunningBuildNames(){return this.runningBuild.map((({name:i})=>i))}promisifyProcess(i,s=!1){const t=new Promise(((t,e)=>{i.on("exit",(i=>{t(i)})),i.on("close",(i=>{t(i)})),i.on("error",(i=>{e(i)})),s&&i.stderr?.on("data",(i=>{const s=Buffer.from(i).toString();(s.includes("warning")||s.includes("WARNING"))&&t(1)}))}));return i.stdout?.pipe(process.stdout),i.stderr?.pipe(process.stderr),{promise:t,command:i}}buildManifest(){console.info(e.blue("Building routes manifest file"));const i=c.init({isProd:this.isProd,mode:this.params.mode},{root:this.viteConfig.root,clientFile:this.pluginConfig.clientFile});d.get(i,{buildDir:this.viteConfig.build.outDir,viteAliases:this.viteConfig.resolve.alias,basename:this.viteConfig.base,renderBuiltUrl:this.viteConfig?.experimental?.renderBuiltUrl}).buildRoutesManifest()}unlockRobots(){const i=`${this.buildDir}/client/robots.txt`;if(!s.existsSync(i))return void console.warn(`Failed to unlock robots.txt, file not exist: ${i}`);const t=s.readFileSync(i,{encoding:"utf-8"}).replace(/Disallow: \/$/m,"Allow: /");s.writeFileSync(i,t,{encoding:"utf-8"}),console.info(e.blue("\nrobots.txt unlocked."))}eject(){const i=`${this.buildDir}/server/start.js`;s.writeFileSync(i,"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\n\nconst VERSION = process.env.VERSION || \"1.0.0\";\nconst PORT = process.env.PORT || 3000;\nconst IS_HOST = process.env.IS_HOST || \"0\";\nconst ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\n\nawait runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\n",{encoding:"utf-8"})}createServerless(){const i=`${this.buildDir}/server/serverless.js`;s.writeFileSync(i,"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\n\nexport default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\n",{encoding:"utf-8"})}async spawnBuild(s,t,e={}){const{mode:o,isNoWarnings:r}=this.params,{focusOnly:l=this.params.focusOnly,shouldWait:a=!1,env:u={}}=e,c=o&&!t.includes("--mode")?`--mode ${o}`:"",d=this.promisifyProcess(i.spawn(`vite build ${t} ${c} --emptyOutDir`,{signal:this.abortController.signal,stdio:[process.stdin,"pipe","pipe"],shell:!0,env:{...process.env,...u,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:n(l).isOnlyClient()?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}),r);this.runningBuild.push({name:s,buildProcess:d}),a&&await this.waitLastBuild()}async waitLastBuild(){const i=this.runningBuild.at(-1);if(!i)return;const s=await i.buildProcess.promise;u(s,!0)}runPreviewMode(){this.hasPreviewModeExitListener||(process.on("exit",(()=>{this.abortController.abort()})),this.hasPreviewModeExitListener=!0);const{onFinish:i}=this.params;let s=this.runningBuild.length;const t=e=>{Buffer.from(e).toString().includes("built in")&&(s-=1,s||(this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.removeListener("data",t)})),l(this.isProd,this.viteConfig),i?.()))};this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.on("data",t)}))}async build(){await this.makeConfig(),await r(),this.clearBuildFolder();const{clientOptions:i,serverOptions:s,onFinish:t,isWatch:e,focusOnly:o,isEject:a,isServerless:u,isUnlockRobots:c}=this.params,{outDir:d}=this.viteConfig.build,h=n(o);this.abortController=new AbortController,this.runningBuild=[],h.isClient()&&await this.spawnBuild("client",`${i} --outDir ${d}/client`,{shouldWait:!e}),h.isServer()&&(await this.spawnBuild("server",`${s} --outDir ${d}/server --ssr ${this.pluginConfig.serverFile}`,{shouldWait:!e}),e||(this.buildManifest(),a&&this.eject(),u&&this.createServerless()));const{entrypoint:p}=this.pluginConfig;if(p?.length&&h.isEntrypoint())for(const{name:i,type:s,serverFile:t,buildOptions:o=""}of p){const r=t&&"ssr"===s?`--ssr ${t}`:"";await this.spawnBuild(i,`${o} ${r} --outDir ${d}/${i}`,{shouldWait:!e,focusOnly:"ssr"===s?"server":"client",env:{SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME:i}})}e?this.runPreviewMode():(c&&this.unlockRobots(),l(this.isProd,this.viteConfig),t?.())}}export{h as default};
2
2
  //# sourceMappingURL=build.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sources":["../../src/services/build.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { ResolvedConfig } from 'vite';\nimport { resolveConfig } from 'vite';\nimport viteResetCache from '@cli/helpers/vite-reset-cache';\nimport createFocusOnly from '@helpers/create-focus-only';\nimport { createDevMarker } from '@helpers/dev-marker';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport processStop from '@helpers/process-stop';\nimport ServerConfig from '@services/server-config';\nimport SsrManifest from '@services/ssr-manifest';\n\nexport interface IBuildParams {\n mode: string;\n onFinish?: () => void;\n clientOptions?: string;\n serverOptions?: string;\n focusOnly?: 'all' | 'app' | 'client' | 'server' | 'entrypoint';\n isWatch?: boolean;\n isUnlockRobots?: boolean;\n isEject?: boolean;\n isServerless?: boolean;\n isNoWarnings?: boolean;\n}\n\ninterface IBuildProcess {\n promise: Promise<number | null | string>;\n command: childProcess.ChildProcess;\n}\n\ninterface ISpawnBuildParams {\n shouldWait?: boolean;\n focusOnly?: IBuildParams['focusOnly'];\n env?: Record<string, string>;\n}\n\nexport interface IBuildEntrypoint {\n // entrypoint name\n name: string;\n type: 'spa' | 'ssr';\n // custom index file, default: indexFile from plugin config\n indexFile?: string;\n // custom entry file for replace in indexFile, default: undefined (do nothing)\n clientFile?: string;\n // custom server file, indexFile and clientFile will be ignored\n serverFile?: string;\n // additional options for vite build command\n buildOptions?: string;\n}\n\n/**\n * Build service\n */\nclass Build {\n /**\n * Is production build\n */\n protected isProd: boolean;\n\n /**\n * Node environment\n */\n protected nodeEnv: string;\n\n /**\n * Build folder\n */\n protected buildDir: string;\n\n /**\n * Vite config\n */\n protected viteConfig: ResolvedConfig;\n\n /**\n * Plugin config\n */\n protected pluginConfig: IPluginConfig;\n\n /**\n * Build params\n */\n protected params: IBuildParams = {\n mode: '',\n clientOptions: '',\n serverOptions: '',\n focusOnly: 'app',\n isWatch: false,\n isUnlockRobots: false,\n isEject: false,\n isServerless: false,\n isNoWarnings: false,\n };\n\n /**\n * Abort controller for builds\n */\n protected abortController: AbortController | null = null;\n\n /**\n * Running builds\n */\n protected runningBuild: { name: string; buildProcess: IBuildProcess }[] = [];\n\n /**\n * Listener for preview has attached\n */\n protected hasPreviewModeExitListener = false;\n\n /**\n * @constructor\n */\n public constructor(params: IBuildParams) {\n this.params = { ...this.params, ...params };\n }\n\n /**\n * Make config\n */\n protected async makeConfig(): Promise<void> {\n const { mode } = this.params;\n\n this.viteConfig = await resolveConfig({}, 'build', mode, 'production');\n this.pluginConfig = getPluginConfig(this.viteConfig);\n this.buildDir = path.resolve(this.viteConfig.root, this.viteConfig.build.outDir);\n this.nodeEnv = process.env.NODE_ENV || 'production';\n this.isProd = this.nodeEnv === 'production';\n }\n\n /**\n * Clear build folder\n */\n public clearBuildFolder(): void {\n // clear build folder\n if (fs.existsSync(this.buildDir)) {\n fs.rmSync(this.buildDir, { recursive: true });\n }\n }\n\n /**\n * Return is prod indicator value\n */\n public getIsProd(): boolean {\n return this.isProd;\n }\n\n /**\n * Return node env value\n */\n public getNodeEnv(): string {\n return this.nodeEnv;\n }\n\n /**\n * Return build names\n */\n public getRunningBuildNames(): string[] {\n return this.runningBuild.map(({ name }) => name);\n }\n\n /**\n * Promisify spawn process\n */\n protected promisifyProcess(\n command: childProcess.ChildProcess,\n isRejectWarnings = false,\n ): IBuildProcess {\n const promise = new Promise<number | null | string>((resolve, reject): void => {\n command.on('exit', (code) => {\n resolve(code);\n });\n\n command.on('close', (code: number): void => {\n resolve(code);\n });\n\n command.on('error', (message: string): void => {\n reject(message);\n });\n\n if (isRejectWarnings) {\n command.stderr?.on('data', (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('warning') || msg.includes('WARNING')) {\n resolve(1);\n }\n });\n }\n });\n\n command.stdout?.pipe(process.stdout);\n command.stderr?.pipe(process.stderr);\n\n return { promise, command };\n }\n\n /**\n * Build assets manifest file\n */\n protected buildManifest(): void {\n console.info(chalk.blue('Building routes manifest file'));\n\n const serverConfig = ServerConfig.init(\n { isProd: this.isProd, mode: this.params.mode },\n { root: this.viteConfig.root, clientFile: this.pluginConfig.clientFile },\n );\n\n SsrManifest.get(serverConfig, {\n buildDir: this.viteConfig.build.outDir,\n viteAliases: this.viteConfig.resolve.alias,\n basename: this.viteConfig.base,\n }).buildRoutesManifest();\n }\n\n /**\n * Change general directive Disallow to Allow in robots.txt.\n */\n protected unlockRobots(): void {\n const robotsFile = `${this.buildDir}/client/robots.txt`;\n\n if (!fs.existsSync(robotsFile)) {\n console.warn(`Failed to unlock robots.txt, file not exist: ${robotsFile}`);\n\n return;\n }\n\n const data = fs\n .readFileSync(robotsFile, { encoding: 'utf-8' })\n .replace(/Disallow: \\/$/m, 'Allow: /');\n\n fs.writeFileSync(robotsFile, data, { encoding: 'utf-8' });\n\n console.info(chalk.blue('\\nrobots.txt unlocked.'));\n }\n\n /**\n * Eject cli to run app via node\n */\n protected eject(): void {\n const entrypoint = `${this.buildDir}/server/start.js`;\n const script =\n \"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\\n\\n\" +\n 'const VERSION = process.env.VERSION || \"1.0.0\";\\n' +\n 'const PORT = process.env.PORT || 3000;\\n' +\n 'const IS_HOST = process.env.IS_HOST || \"0\";\\n' +\n 'const ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\\n\\n' +\n `await runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Create serverless entrypoint\n */\n protected createServerless(): void {\n const entrypoint = `${this.buildDir}/server/serverless.js`;\n const script =\n \"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\\n\\n\" +\n `export default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Build specified entrypoint\n */\n protected async spawnBuild(\n name: string,\n buildOptions: string,\n params: ISpawnBuildParams = {},\n ): Promise<void> {\n const { mode, isNoWarnings } = this.params;\n const { focusOnly = this.params.focusOnly, shouldWait = false, env = {} } = params;\n const modeOpt = mode && !buildOptions.includes('--mode') ? `--mode ${mode}` : '';\n\n const buildProcess = this.promisifyProcess(\n childProcess.spawn(`vite build ${buildOptions} ${modeOpt} --emptyOutDir`, {\n signal: this.abortController!.signal,\n stdio: [process.stdin, 'pipe', 'pipe'],\n shell: true,\n env: {\n ...process.env,\n ...env,\n FORCE_COLOR: '2',\n SSR_BOOST_IS_SSR: createFocusOnly(focusOnly).isOnlyClient() ? '0' : '1',\n SSR_BOOST_ACTION: global.viteBoostAction,\n },\n }),\n isNoWarnings,\n );\n\n this.runningBuild.push({ name, buildProcess });\n\n if (!shouldWait) {\n return;\n }\n\n await this.waitLastBuild();\n }\n\n /**\n * Wait latest build and stop process in case error\n */\n protected async waitLastBuild(): Promise<void> {\n const latestProcess = this.runningBuild.at(-1);\n\n if (!latestProcess) {\n return;\n }\n\n const exitCode = await latestProcess.buildProcess.promise;\n\n processStop(exitCode, true);\n }\n\n /**\n * Run preview mode\n */\n protected runPreviewMode(): void {\n if (!this.hasPreviewModeExitListener) {\n process.on('exit', () => {\n this.abortController!.abort();\n });\n\n this.hasPreviewModeExitListener = true;\n }\n\n const { onFinish } = this.params;\n let buildCount = this.runningBuild.length;\n\n /**\n * Detect finished builds for process\n */\n const listener = (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('built in')) {\n buildCount -= 1;\n\n if (!buildCount) {\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.removeListener('data', listener);\n });\n createDevMarker(this.isProd, this.viteConfig);\n onFinish?.();\n }\n }\n };\n\n /**\n * Listen output for call onFinish\n */\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.on('data', listener);\n });\n }\n\n /**\n * Run app build\n */\n public async build(): Promise<void> {\n await this.makeConfig();\n // this is required step - build with different env may cause problems\n await viteResetCache();\n this.clearBuildFolder();\n\n const {\n clientOptions,\n serverOptions,\n onFinish,\n isWatch,\n focusOnly,\n isEject,\n isServerless,\n isUnlockRobots,\n } = this.params;\n const { outDir } = this.viteConfig.build;\n const focus = createFocusOnly(focusOnly);\n\n this.abortController = new AbortController();\n this.runningBuild = [];\n\n if (focus.isClient()) {\n /**\n * Build client\n */\n await this.spawnBuild('client', `${clientOptions} --outDir ${outDir}/client`, {\n shouldWait: !isWatch,\n });\n }\n\n /**\n * Build server\n */\n if (focus.isServer()) {\n await this.spawnBuild(\n 'server',\n `${serverOptions} --outDir ${outDir}/server --ssr ${this.pluginConfig.serverFile}`,\n {\n shouldWait: !isWatch,\n },\n );\n\n if (!isWatch) {\n this.buildManifest();\n\n if (isEject) {\n this.eject();\n }\n\n if (isServerless) {\n this.createServerless();\n }\n }\n }\n\n /**\n * Build additional entrypoint\n */\n const { entrypoint } = this.pluginConfig;\n\n if (entrypoint?.length && focus.isEntrypoint()) {\n for (const { name, type, serverFile, buildOptions = '' } of entrypoint) {\n const cliOptions = serverFile && type === 'ssr' ? `--ssr ${serverFile}` : '';\n\n await this.spawnBuild(name, `${buildOptions} ${cliOptions} --outDir ${outDir}/${name}`, {\n shouldWait: !isWatch,\n focusOnly: type === 'ssr' ? 'server' : 'client',\n env: {\n SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME: name,\n },\n });\n }\n }\n\n /**\n * Preview mode\n */\n if (isWatch) {\n this.runPreviewMode();\n\n return;\n }\n\n if (isUnlockRobots) {\n this.unlockRobots();\n }\n\n createDevMarker(this.isProd, this.viteConfig);\n onFinish?.();\n }\n}\n\nexport default Build;\n"],"names":["Build","isProd","nodeEnv","buildDir","viteConfig","pluginConfig","params","mode","clientOptions","serverOptions","focusOnly","isWatch","isUnlockRobots","isEject","isServerless","isNoWarnings","abortController","runningBuild","hasPreviewModeExitListener","constructor","this","async","resolveConfig","getPluginConfig","path","resolve","root","build","outDir","process","env","NODE_ENV","clearBuildFolder","fs","existsSync","rmSync","recursive","getIsProd","getNodeEnv","getRunningBuildNames","map","name","promisifyProcess","command","isRejectWarnings","promise","Promise","reject","on","code","message","stderr","buff","msg","Buffer","from","toString","includes","stdout","pipe","buildManifest","console","info","chalk","blue","serverConfig","ServerConfig","init","clientFile","SsrManifest","get","viteAliases","alias","basename","base","buildRoutesManifest","unlockRobots","robotsFile","warn","data","readFileSync","encoding","replace","writeFileSync","eject","entrypoint","createServerless","buildOptions","shouldWait","modeOpt","buildProcess","childProcess","spawn","signal","stdio","stdin","shell","FORCE_COLOR","SSR_BOOST_IS_SSR","createFocusOnly","isOnlyClient","SSR_BOOST_ACTION","global","viteBoostAction","push","waitLastBuild","latestProcess","at","exitCode","processStop","runPreviewMode","abort","onFinish","buildCount","length","listener","forEach","removeListener","createDevMarker","makeConfig","viteResetCache","focus","AbortController","isClient","spawnBuild","isServer","serverFile","isEntrypoint","type","cliOptions","SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME"],"mappings":"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"}
1
+ {"version":3,"file":"build.js","sources":["../../src/services/build.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { ResolvedConfig } from 'vite';\nimport { resolveConfig } from 'vite';\nimport viteResetCache from '@cli/helpers/vite-reset-cache';\nimport createFocusOnly from '@helpers/create-focus-only';\nimport { createDevMarker } from '@helpers/dev-marker';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport processStop from '@helpers/process-stop';\nimport ServerConfig from '@services/server-config';\nimport SsrManifest from '@services/ssr-manifest';\n\nexport interface IBuildParams {\n mode: string;\n onFinish?: () => void;\n clientOptions?: string;\n serverOptions?: string;\n focusOnly?: 'all' | 'app' | 'client' | 'server' | 'entrypoint';\n isWatch?: boolean;\n isUnlockRobots?: boolean;\n isEject?: boolean;\n isServerless?: boolean;\n isNoWarnings?: boolean;\n}\n\ninterface IBuildProcess {\n promise: Promise<number | null | string>;\n command: childProcess.ChildProcess;\n}\n\ninterface ISpawnBuildParams {\n shouldWait?: boolean;\n focusOnly?: IBuildParams['focusOnly'];\n env?: Record<string, string>;\n}\n\nexport interface IBuildEntrypoint {\n // entrypoint name\n name: string;\n type: 'spa' | 'ssr';\n // custom index file, default: indexFile from plugin config\n indexFile?: string;\n // custom entry file for replace in indexFile, default: undefined (do nothing)\n clientFile?: string;\n // custom server file, indexFile and clientFile will be ignored\n serverFile?: string;\n // additional options for vite build command\n buildOptions?: string;\n}\n\n/**\n * Build service\n */\nclass Build {\n /**\n * Is production build\n */\n protected isProd: boolean;\n\n /**\n * Node environment\n */\n protected nodeEnv: string;\n\n /**\n * Build folder\n */\n protected buildDir: string;\n\n /**\n * Vite config\n */\n protected viteConfig: ResolvedConfig;\n\n /**\n * Plugin config\n */\n protected pluginConfig: IPluginConfig;\n\n /**\n * Build params\n */\n protected params: IBuildParams = {\n mode: '',\n clientOptions: '',\n serverOptions: '',\n focusOnly: 'app',\n isWatch: false,\n isUnlockRobots: false,\n isEject: false,\n isServerless: false,\n isNoWarnings: false,\n };\n\n /**\n * Abort controller for builds\n */\n protected abortController: AbortController | null = null;\n\n /**\n * Running builds\n */\n protected runningBuild: { name: string; buildProcess: IBuildProcess }[] = [];\n\n /**\n * Listener for preview has attached\n */\n protected hasPreviewModeExitListener = false;\n\n /**\n * @constructor\n */\n public constructor(params: IBuildParams) {\n this.params = { ...this.params, ...params };\n }\n\n /**\n * Make config\n */\n protected async makeConfig(): Promise<void> {\n const { mode } = this.params;\n\n this.viteConfig = await resolveConfig({}, 'build', mode, 'production');\n this.pluginConfig = getPluginConfig(this.viteConfig);\n this.buildDir = path.resolve(this.viteConfig.root, this.viteConfig.build.outDir);\n this.nodeEnv = process.env.NODE_ENV || 'production';\n this.isProd = this.nodeEnv === 'production';\n }\n\n /**\n * Clear build folder\n */\n public clearBuildFolder(): void {\n // clear build folder\n if (fs.existsSync(this.buildDir)) {\n fs.rmSync(this.buildDir, { recursive: true });\n }\n }\n\n /**\n * Return is prod indicator value\n */\n public getIsProd(): boolean {\n return this.isProd;\n }\n\n /**\n * Return node env value\n */\n public getNodeEnv(): string {\n return this.nodeEnv;\n }\n\n /**\n * Return build names\n */\n public getRunningBuildNames(): string[] {\n return this.runningBuild.map(({ name }) => name);\n }\n\n /**\n * Promisify spawn process\n */\n protected promisifyProcess(\n command: childProcess.ChildProcess,\n isRejectWarnings = false,\n ): IBuildProcess {\n const promise = new Promise<number | null | string>((resolve, reject): void => {\n command.on('exit', (code) => {\n resolve(code);\n });\n\n command.on('close', (code: number): void => {\n resolve(code);\n });\n\n command.on('error', (message: string): void => {\n reject(message);\n });\n\n if (isRejectWarnings) {\n command.stderr?.on('data', (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('warning') || msg.includes('WARNING')) {\n resolve(1);\n }\n });\n }\n });\n\n command.stdout?.pipe(process.stdout);\n command.stderr?.pipe(process.stderr);\n\n return { promise, command };\n }\n\n /**\n * Build assets manifest file\n */\n protected buildManifest(): void {\n console.info(chalk.blue('Building routes manifest file'));\n\n const serverConfig = ServerConfig.init(\n { isProd: this.isProd, mode: this.params.mode },\n { root: this.viteConfig.root, clientFile: this.pluginConfig.clientFile },\n );\n\n SsrManifest.get(serverConfig, {\n buildDir: this.viteConfig.build.outDir,\n viteAliases: this.viteConfig.resolve.alias,\n basename: this.viteConfig.base,\n renderBuiltUrl: this.viteConfig?.experimental?.renderBuiltUrl,\n }).buildRoutesManifest();\n }\n\n /**\n * Change general directive Disallow to Allow in robots.txt.\n */\n protected unlockRobots(): void {\n const robotsFile = `${this.buildDir}/client/robots.txt`;\n\n if (!fs.existsSync(robotsFile)) {\n console.warn(`Failed to unlock robots.txt, file not exist: ${robotsFile}`);\n\n return;\n }\n\n const data = fs\n .readFileSync(robotsFile, { encoding: 'utf-8' })\n .replace(/Disallow: \\/$/m, 'Allow: /');\n\n fs.writeFileSync(robotsFile, data, { encoding: 'utf-8' });\n\n console.info(chalk.blue('\\nrobots.txt unlocked.'));\n }\n\n /**\n * Eject cli to run app via node\n */\n protected eject(): void {\n const entrypoint = `${this.buildDir}/server/start.js`;\n const script =\n \"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\\n\\n\" +\n 'const VERSION = process.env.VERSION || \"1.0.0\";\\n' +\n 'const PORT = process.env.PORT || 3000;\\n' +\n 'const IS_HOST = process.env.IS_HOST || \"0\";\\n' +\n 'const ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\\n\\n' +\n `await runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Create serverless entrypoint\n */\n protected createServerless(): void {\n const entrypoint = `${this.buildDir}/server/serverless.js`;\n const script =\n \"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\\n\\n\" +\n `export default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Build specified entrypoint\n */\n protected async spawnBuild(\n name: string,\n buildOptions: string,\n params: ISpawnBuildParams = {},\n ): Promise<void> {\n const { mode, isNoWarnings } = this.params;\n const { focusOnly = this.params.focusOnly, shouldWait = false, env = {} } = params;\n const modeOpt = mode && !buildOptions.includes('--mode') ? `--mode ${mode}` : '';\n\n const buildProcess = this.promisifyProcess(\n childProcess.spawn(`vite build ${buildOptions} ${modeOpt} --emptyOutDir`, {\n signal: this.abortController!.signal,\n stdio: [process.stdin, 'pipe', 'pipe'],\n shell: true,\n env: {\n ...process.env,\n ...env,\n FORCE_COLOR: '2',\n SSR_BOOST_IS_SSR: createFocusOnly(focusOnly).isOnlyClient() ? '0' : '1',\n SSR_BOOST_ACTION: global.viteBoostAction,\n },\n }),\n isNoWarnings,\n );\n\n this.runningBuild.push({ name, buildProcess });\n\n if (!shouldWait) {\n return;\n }\n\n await this.waitLastBuild();\n }\n\n /**\n * Wait latest build and stop process in case error\n */\n protected async waitLastBuild(): Promise<void> {\n const latestProcess = this.runningBuild.at(-1);\n\n if (!latestProcess) {\n return;\n }\n\n const exitCode = await latestProcess.buildProcess.promise;\n\n processStop(exitCode, true);\n }\n\n /**\n * Run preview mode\n */\n protected runPreviewMode(): void {\n if (!this.hasPreviewModeExitListener) {\n process.on('exit', () => {\n this.abortController!.abort();\n });\n\n this.hasPreviewModeExitListener = true;\n }\n\n const { onFinish } = this.params;\n let buildCount = this.runningBuild.length;\n\n /**\n * Detect finished builds for process\n */\n const listener = (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('built in')) {\n buildCount -= 1;\n\n if (!buildCount) {\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.removeListener('data', listener);\n });\n createDevMarker(this.isProd, this.viteConfig);\n onFinish?.();\n }\n }\n };\n\n /**\n * Listen output for call onFinish\n */\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.on('data', listener);\n });\n }\n\n /**\n * Run app build\n */\n public async build(): Promise<void> {\n await this.makeConfig();\n // this is required step - build with different env may cause problems\n await viteResetCache();\n this.clearBuildFolder();\n\n const {\n clientOptions,\n serverOptions,\n onFinish,\n isWatch,\n focusOnly,\n isEject,\n isServerless,\n isUnlockRobots,\n } = this.params;\n const { outDir } = this.viteConfig.build;\n const focus = createFocusOnly(focusOnly);\n\n this.abortController = new AbortController();\n this.runningBuild = [];\n\n if (focus.isClient()) {\n /**\n * Build client\n */\n await this.spawnBuild('client', `${clientOptions} --outDir ${outDir}/client`, {\n shouldWait: !isWatch,\n });\n }\n\n /**\n * Build server\n */\n if (focus.isServer()) {\n await this.spawnBuild(\n 'server',\n `${serverOptions} --outDir ${outDir}/server --ssr ${this.pluginConfig.serverFile}`,\n {\n shouldWait: !isWatch,\n },\n );\n\n if (!isWatch) {\n this.buildManifest();\n\n if (isEject) {\n this.eject();\n }\n\n if (isServerless) {\n this.createServerless();\n }\n }\n }\n\n /**\n * Build additional entrypoint\n */\n const { entrypoint } = this.pluginConfig;\n\n if (entrypoint?.length && focus.isEntrypoint()) {\n for (const { name, type, serverFile, buildOptions = '' } of entrypoint) {\n const cliOptions = serverFile && type === 'ssr' ? `--ssr ${serverFile}` : '';\n\n await this.spawnBuild(name, `${buildOptions} ${cliOptions} --outDir ${outDir}/${name}`, {\n shouldWait: !isWatch,\n focusOnly: type === 'ssr' ? 'server' : 'client',\n env: {\n SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME: name,\n },\n });\n }\n }\n\n /**\n * Preview mode\n */\n if (isWatch) {\n this.runPreviewMode();\n\n return;\n }\n\n if (isUnlockRobots) {\n this.unlockRobots();\n }\n\n createDevMarker(this.isProd, this.viteConfig);\n onFinish?.();\n }\n}\n\nexport default Build;\n"],"names":["Build","isProd","nodeEnv","buildDir","viteConfig","pluginConfig","params","mode","clientOptions","serverOptions","focusOnly","isWatch","isUnlockRobots","isEject","isServerless","isNoWarnings","abortController","runningBuild","hasPreviewModeExitListener","constructor","this","async","resolveConfig","getPluginConfig","path","resolve","root","build","outDir","process","env","NODE_ENV","clearBuildFolder","fs","existsSync","rmSync","recursive","getIsProd","getNodeEnv","getRunningBuildNames","map","name","promisifyProcess","command","isRejectWarnings","promise","Promise","reject","on","code","message","stderr","buff","msg","Buffer","from","toString","includes","stdout","pipe","buildManifest","console","info","chalk","blue","serverConfig","ServerConfig","init","clientFile","SsrManifest","get","viteAliases","alias","basename","base","renderBuiltUrl","experimental","buildRoutesManifest","unlockRobots","robotsFile","warn","data","readFileSync","encoding","replace","writeFileSync","eject","entrypoint","createServerless","buildOptions","shouldWait","modeOpt","buildProcess","childProcess","spawn","signal","stdio","stdin","shell","FORCE_COLOR","SSR_BOOST_IS_SSR","createFocusOnly","isOnlyClient","SSR_BOOST_ACTION","global","viteBoostAction","push","waitLastBuild","latestProcess","at","exitCode","processStop","runPreviewMode","abort","onFinish","buildCount","length","listener","forEach","removeListener","createDevMarker","makeConfig","viteResetCache","focus","AbortController","isClient","spawnBuild","isServer","serverFile","isEntrypoint","type","cliOptions","SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME"],"mappings":"gcAwDA,MAAMA,EAIMC,OAKAC,QAKAC,SAKAC,WAKAC,aAKAC,OAAuB,CAC/BC,KAAM,GACNC,cAAe,GACfC,cAAe,GACfC,UAAW,MACXC,SAAS,EACTC,gBAAgB,EAChBC,SAAS,EACTC,cAAc,EACdC,cAAc,GAMNC,gBAA0C,KAK1CC,aAAgE,GAKhEC,4BAA6B,EAKvCC,YAAmBb,GACjBc,KAAKd,OAAS,IAAKc,KAAKd,UAAWA,GAM3Be,mBACR,MAAMd,KAAEA,GAASa,KAAKd,OAEtBc,KAAKhB,iBAAmBkB,EAAc,CAAE,EAAE,QAASf,EAAM,cACzDa,KAAKf,aAAekB,EAAgBH,KAAKhB,YACzCgB,KAAKjB,SAAWqB,EAAKC,QAAQL,KAAKhB,WAAWsB,KAAMN,KAAKhB,WAAWuB,MAAMC,QACzER,KAAKlB,QAAU2B,QAAQC,IAAIC,UAAY,aACvCX,KAAKnB,OAA0B,eAAjBmB,KAAKlB,QAMd8B,mBAEDC,EAAGC,WAAWd,KAAKjB,WACrB8B,EAAGE,OAAOf,KAAKjB,SAAU,CAAEiC,WAAW,IAOnCC,YACL,OAAOjB,KAAKnB,OAMPqC,aACL,OAAOlB,KAAKlB,QAMPqC,uBACL,OAAOnB,KAAKH,aAAauB,KAAI,EAAGC,UAAWA,IAMnCC,iBACRC,EACAC,GAAmB,GAEnB,MAAMC,EAAU,IAAIC,SAAgC,CAACrB,EAASsB,KAC5DJ,EAAQK,GAAG,QAASC,IAClBxB,EAAQwB,EAAK,IAGfN,EAAQK,GAAG,SAAUC,IACnBxB,EAAQwB,EAAK,IAGfN,EAAQK,GAAG,SAAUE,IACnBH,EAAOG,EAAQ,IAGbN,GACFD,EAAQQ,QAAQH,GAAG,QAASI,IAC1B,MAAMC,EAAMC,OAAOC,KAAKH,GAAMI,YAE1BH,EAAII,SAAS,YAAcJ,EAAII,SAAS,aAC1ChC,EAAQ,SAShB,OAHAkB,EAAQe,QAAQC,KAAK9B,QAAQ6B,QAC7Bf,EAAQQ,QAAQQ,KAAK9B,QAAQsB,QAEtB,CAAEN,UAASF,WAMViB,gBACRC,QAAQC,KAAKC,EAAMC,KAAK,kCAExB,MAAMC,EAAeC,EAAaC,KAChC,CAAElE,OAAQmB,KAAKnB,OAAQM,KAAMa,KAAKd,OAAOC,MACzC,CAAEmB,KAAMN,KAAKhB,WAAWsB,KAAM0C,WAAYhD,KAAKf,aAAa+D,aAG9DC,EAAYC,IAAIL,EAAc,CAC5B9D,SAAUiB,KAAKhB,WAAWuB,MAAMC,OAChC2C,YAAanD,KAAKhB,WAAWqB,QAAQ+C,MACrCC,SAAUrD,KAAKhB,WAAWsE,KAC1BC,eAAgBvD,KAAKhB,YAAYwE,cAAcD,iBAC9CE,sBAMKC,eACR,MAAMC,EAAa,GAAG3D,KAAKjB,6BAE3B,IAAK8B,EAAGC,WAAW6C,GAGjB,YAFAlB,QAAQmB,KAAK,gDAAgDD,KAK/D,MAAME,EAAOhD,EACViD,aAAaH,EAAY,CAAEI,SAAU,UACrCC,QAAQ,iBAAkB,YAE7BnD,EAAGoD,cAAcN,EAAYE,EAAM,CAAEE,SAAU,UAE/CtB,QAAQC,KAAKC,EAAMC,KAAK,2BAMhBsB,QACR,MAAMC,EAAa,GAAGnE,KAAKjB,2BAe3B8B,EAAGoD,cAAcE,EAbf,2bAamC,CACnCJ,SAAU,UAOJK,mBACR,MAAMD,EAAa,GAAGnE,KAAKjB,gCAK3B8B,EAAGoD,cAAcE,EAHf,oKAGmC,CACnCJ,SAAU,UAOJ9D,iBACRoB,EACAgD,EACAnF,EAA4B,CAAA,GAE5B,MAAMC,KAAEA,EAAIQ,aAAEA,GAAiBK,KAAKd,QAC9BI,UAAEA,EAAYU,KAAKd,OAAOI,UAASgF,WAAEA,GAAa,EAAK5D,IAAEA,EAAM,CAAE,GAAKxB,EACtEqF,EAAUpF,IAASkF,EAAahC,SAAS,UAAY,UAAUlD,IAAS,GAExEqF,EAAexE,KAAKsB,iBACxBmD,EAAaC,MAAM,cAAcL,KAAgBE,kBAAyB,CACxEI,OAAQ3E,KAAKJ,gBAAiB+E,OAC9BC,MAAO,CAACnE,QAAQoE,MAAO,OAAQ,QAC/BC,OAAO,EACPpE,IAAK,IACAD,QAAQC,OACRA,EACHqE,YAAa,IACbC,iBAAkBC,EAAgB3F,GAAW4F,eAAiB,IAAM,IACpEC,iBAAkBC,OAAOC,mBAG7B1F,GAGFK,KAAKH,aAAayF,KAAK,CAAEjE,OAAMmD,iBAE1BF,SAICtE,KAAKuF,gBAMHtF,sBACR,MAAMuF,EAAgBxF,KAAKH,aAAa4F,IAAG,GAE3C,IAAKD,EACH,OAGF,MAAME,QAAiBF,EAAchB,aAAa/C,QAElDkE,EAAYD,GAAU,GAMdE,iBACH5F,KAAKF,6BACRW,QAAQmB,GAAG,QAAQ,KACjB5B,KAAKJ,gBAAiBiG,OAAO,IAG/B7F,KAAKF,4BAA6B,GAGpC,MAAMgG,SAAEA,GAAa9F,KAAKd,OAC1B,IAAI6G,EAAa/F,KAAKH,aAAamG,OAKnC,MAAMC,EAAYjE,IACJE,OAAOC,KAAKH,GAAMI,WAEtBC,SAAS,cACf0D,GAAc,EAETA,IACH/F,KAAKH,aAAaqG,SAAQ,EAAG1B,mBAC3BA,EAAajD,QAAQe,QAAQ6D,eAAe,OAAQF,EAAS,IAE/DG,EAAgBpG,KAAKnB,OAAQmB,KAAKhB,YAClC8G,SAQN9F,KAAKH,aAAaqG,SAAQ,EAAG1B,mBAC3BA,EAAajD,QAAQe,QAAQV,GAAG,OAAQqE,EAAS,IAO9ChG,oBACCD,KAAKqG,mBAELC,IACNtG,KAAKY,mBAEL,MAAMxB,cACJA,EAAaC,cACbA,EAAayG,SACbA,EAAQvG,QACRA,EAAOD,UACPA,EAASG,QACTA,EAAOC,aACPA,EAAYF,eACZA,GACEQ,KAAKd,QACHsB,OAAEA,GAAWR,KAAKhB,WAAWuB,MAC7BgG,EAAQtB,EAAgB3F,GAE9BU,KAAKJ,gBAAkB,IAAI4G,gBAC3BxG,KAAKH,aAAe,GAEhB0G,EAAME,kBAIFzG,KAAK0G,WAAW,SAAU,GAAGtH,cAA0BoB,WAAiB,CAC5E8D,YAAa/E,IAObgH,EAAMI,mBACF3G,KAAK0G,WACT,SACA,GAAGrH,cAA0BmB,kBAAuBR,KAAKf,aAAa2H,aACtE,CACEtC,YAAa/E,IAIZA,IACHS,KAAKwC,gBAED/C,GACFO,KAAKkE,QAGHxE,GACFM,KAAKoE,qBAQX,MAAMD,WAAEA,GAAenE,KAAKf,aAE5B,GAAIkF,GAAY6B,QAAUO,EAAMM,eAC9B,IAAK,MAAMxF,KAAEA,EAAIyF,KAAEA,EAAIF,WAAEA,EAAUvC,aAAEA,EAAe,MAAQF,EAAY,CACtE,MAAM4C,EAAaH,GAAuB,QAATE,EAAiB,SAASF,IAAe,SAEpE5G,KAAK0G,WAAWrF,EAAM,GAAGgD,KAAgB0C,cAAuBvG,KAAUa,IAAQ,CACtFiD,YAAa/E,EACbD,UAAoB,QAATwH,EAAiB,SAAW,SACvCpG,IAAK,CACHsG,uCAAwC3F,KAS5C9B,EACFS,KAAK4F,kBAKHpG,GACFQ,KAAK0D,eAGP0C,EAAgBpG,KAAKnB,OAAQmB,KAAKhB,YAClC8G"}
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import { Socket } from 'node:net';
3
3
  import { RouterState } from 'react-router';
4
- import { Alias, ModuleNode } from 'vite';
4
+ import { Alias, ModuleNode, RenderBuiltAssetUrl } from 'vite';
5
5
  import { IRequestContext } from "../node/render.js";
6
6
  import { TRoutesTree } from "./parse-routes.js";
7
7
  import PathNormalize from "./path-normalize.js";
@@ -10,6 +10,7 @@ interface ISsrManifestParams {
10
10
  buildDir?: string;
11
11
  viteAliases?: Alias[];
12
12
  basename?: string;
13
+ renderBuiltUrl?: RenderBuiltAssetUrl;
13
14
  }
14
15
  interface IManifest {
15
16
  [path: string]: {
@@ -77,6 +78,10 @@ declare class SsrManifest {
77
78
  * Vite base
78
79
  */
79
80
  protected readonly basename?: string;
81
+ /**
82
+ * Vite renderBuiltUrl config func
83
+ */
84
+ protected readonly renderBuiltUrl?: RenderBuiltAssetUrl;
80
85
  /**
81
86
  * Loaded assets manifest file
82
87
  */
@@ -87,7 +92,7 @@ declare class SsrManifest {
87
92
  /**
88
93
  * @constructor
89
94
  */
90
- protected constructor(config: ServerConfig, { buildDir, viteAliases, basename }?: ISsrManifestParams);
95
+ protected constructor(config: ServerConfig, { buildDir, viteAliases, basename, renderBuiltUrl }?: ISsrManifestParams);
91
96
  /**
92
97
  * Get singleton instance
93
98
  */
@@ -1,2 +1,2 @@
1
- import e from"node:fs";import t from"node:path";import s from"chalk";import i from"./parse-routes.js";import r from"./path-normalize.js";var n;!function(e){e.style="style",e.script="script",e.image="image",e.font="font"}(n||(n={}));const o="\r\n";class a{static instance=null;config;pathNormalize;root;buildDir;manifestName="manifest.json";assetsManifest="assets-manifest.json";viteAliases;basename;routesAssets=null;constructor(e,{buildDir:t,viteAliases:s,basename:i}={}){this.config=e,this.root=e.getParams().root,this.buildDir=t,this.viteAliases=s??e.getVite()?.config?.resolve.alias,this.pathNormalize=new r(e,s),this.basename=i}static get(e,t={}){return null===a.instance&&(a.instance=new a(e,t)),a.instance}getOutDir(){return t.resolve(this.root,this.buildDir||"")}getAssetsManifestFile(){return`${this.getOutDir()}/server/${this.assetsManifest}`}loadClientManifest(){const s=t.resolve(this.root,`${this.buildDir||""}/client/.vite`),i=`${s}/${this.manifestName}`;if(!e.existsSync(i))return{};const r=JSON.parse(e.readFileSync(i,{encoding:"utf-8"}));return e.rmSync(i),0===e.readdirSync(s).length&&e.rmSync(s,{recursive:!0}),r}loadAssetsManifest(){if(null!==this.routesAssets)return this.routesAssets;const t=this.getAssetsManifestFile();return e.existsSync(t)?(this.routesAssets=JSON.parse(e.readFileSync(t,{encoding:"utf-8"})),this.routesAssets):{}}getRoutesTreeIds(e,t){const s={};return e.forEach(((e,i)=>{const r=[t,String(i)].filter(Boolean).join("-");e.import&&(s[r]=this.pathNormalize.getAppPath(e.import)),e.children.length>0&&Object.assign(s,this.getRoutesTreeIds(e.children,r))})),s}sortAssets(e){return e.sort(((e,t)=>e.weight===t.weight?Number(e.isNested)-Number(t.isNested):e.weight-t.weight))}getRouteAssets(e,s,i=!1){const r=[...s?.assets??[],...s?.css??[],s?.file].reduce(((e,r)=>{if(r){const n=this.getAssetType(r),o=s.isEntry&&s.file===r;n&&(e[r]={url:t.posix.normalize(`${this.basename}/${r}`),weight:o?1.9:this.getAssetWeight(r),type:n,isNested:i,isPreload:!o})}return e}),{});return s?.imports?.length&&s.imports.forEach((t=>{const s=e[t];s&&Object.assign(r,this.getRouteAssets(e,s,!0))})),r}buildRoutesManifest(){const t=this.loadClientManifest(),s=new i(this.config,this.viteAliases),r=this.getRoutesTreeIds(s.parse()),n=this.pathNormalize.getImportPostfix(),o={};Object.entries(r).forEach((([e,s])=>{const i=n.find((e=>void 0!==t[`${s}${e}`])),r=t[`${s}${i||""}`];o[e]=this.sortAssets(Object.values(this.getRouteAssets(t,r)))})),e.writeFileSync(this.getAssetsManifestFile(),JSON.stringify(o,null,2),{encoding:"utf-8"})}getAssets(e){if(this.config.getVite())return this.getAssetsDev(e);const t=e?.map((({route:e})=>e.id)).filter(Boolean)??[];if(!t.length)return[];const s=this.loadAssetsManifest();return this.sortAssets(t.map((e=>s[e])).flat().filter(Boolean))}getAssetsDev(e){const s=e?.map((({route:e})=>this.pathNormalize.getAppPath(e?.pathId,!0))).filter(Boolean)??[];if(!s.length)return[];let i={};const r=this.pathNormalize.getImportPostfix();return[t.resolve(this.root,this.config.getPluginConfig()?.clientFile??"client.ts"),...s].forEach((e=>{for(const t of r){const s=this.config.getVite()?.moduleGraph.getModuleById(`${e}${t}`);if(s){i={...i,...this.getModuleAssets(s)};break}}})),Object.values(i)}getModuleAssets(e,t=new Set){if(!e?.clientImportedModules.size||t.has(e.file))return{};let i={};return t.add(e.file),e.clientImportedModules.forEach((e=>{const{file:r,clientImportedModules:o,transformResult:a}=e,l=r?.split(".").at(-1);if(r&&l&&["css","scss"].includes(l)){const e=a?.code.match(/__vite__css\s+=\s+"(?<css>.+)"/)?.groups?.css;if(e)try{i[r]={type:n.style,url:r,weight:this.getAssetWeight(r),content:JSON.parse(`{"style": "${e}"}`).style,isNested:Boolean(t.size),isPreload:!1}}catch(e){console.warn(s.yellowBright("Failed to parse style: ",r))}}else o.size&&(i={...i,...this.getModuleAssets(e,t)})})),i}getAssetWeight(e){switch(this.getAssetType(e)){case n.style:return 1;case n.script:return 2;default:return 3}}getAssetType(e){const t=e.split(".").at(-1)?.toLowerCase();switch(t){case"css":case"scss":return n.style;case"js":return n.script;case"svg":case"jpg":case"jpeg":case"png":case"webp":case"gif":case"ico":return n.image;case"ttf":case"otf":case"woff":case"woff2":return n.font;default:return null}}writeEarlyHits(e,t){t.write(`HTTP/1.1 103 Early Hints${o}`),e.forEach((({type:e,url:s})=>{e&&["style","script"].includes(e)&&t.write(`Link: <${s}>; rel=preload; as=${e}${o}`)})),t.write(o)}injectAssets({routerContext:e,html:t,res:s,hasEarlyHints:i=!1}){const r=this.getAssets(e?.matches),o=r.map((({type:e,url:t,isPreload:s,content:i=""})=>{switch(e){case n.style:return this.config.getVite()?`<style data-vite-dev-id="${t}">${i}</style>`:`<link rel="stylesheet" href="${t}">`;case n.script:return s?this.config.isModulePreload?`<link rel="modulepreload" as="script" crossorigin href="${t}">`:null:`<script async type="module" crossorigin src="${t}"><\/script>`}return null})).filter(Boolean);t.header=t.header.replace("</head>",`${o.join("\n")}</head>`),i&&o.length&&s.socket&&this.writeEarlyHits(r,s.socket)}}export{a as default};
1
+ import e from"node:fs";import t from"node:path";import s from"chalk";import i from"./parse-routes.js";import r from"./path-normalize.js";var n;!function(e){e.style="style",e.script="script",e.image="image",e.font="font"}(n||(n={}));const o="\r\n";class a{static instance=null;config;pathNormalize;root;buildDir;manifestName="manifest.json";assetsManifest="assets-manifest.json";viteAliases;basename;renderBuiltUrl;routesAssets=null;constructor(e,{buildDir:t,viteAliases:s,basename:i,renderBuiltUrl:n}={}){this.config=e,this.root=e.getParams().root,this.buildDir=t,this.viteAliases=s??e.getVite()?.config?.resolve.alias,this.pathNormalize=new r(e,s),this.basename=i,this.renderBuiltUrl=n}static get(e,t={}){return null===a.instance&&(a.instance=new a(e,t)),a.instance}getOutDir(){return t.resolve(this.root,this.buildDir||"")}getAssetsManifestFile(){return`${this.getOutDir()}/server/${this.assetsManifest}`}loadClientManifest(){const s=t.resolve(this.root,`${this.buildDir||""}/client/.vite`),i=`${s}/${this.manifestName}`;if(!e.existsSync(i))return{};const r=JSON.parse(e.readFileSync(i,{encoding:"utf-8"}));return e.rmSync(i),0===e.readdirSync(s).length&&e.rmSync(s,{recursive:!0}),r}loadAssetsManifest(){if(null!==this.routesAssets)return this.routesAssets;const t=this.getAssetsManifestFile();return e.existsSync(t)?(this.routesAssets=JSON.parse(e.readFileSync(t,{encoding:"utf-8"})),this.routesAssets):{}}getRoutesTreeIds(e,t){const s={};return e.forEach(((e,i)=>{const r=[t,String(i)].filter(Boolean).join("-");e.import&&(s[r]=this.pathNormalize.getAppPath(e.import)),e.children.length>0&&Object.assign(s,this.getRoutesTreeIds(e.children,r))})),s}sortAssets(e){return e.sort(((e,t)=>e.weight===t.weight?Number(e.isNested)-Number(t.isNested):e.weight-t.weight))}getRouteAssets(e,s,i=!1){const r=[...s?.assets??[],...s?.css??[],s?.file].reduce(((e,r)=>{if(r){const n=this.getAssetType(r),o=s.isEntry&&s.file===r;if(n){const s=t.posix.normalize(`${this.basename}/${r}`),a=this.renderBuiltUrl?.(s,{type:"asset",ssr:!0,hostId:"",hostType:s.split(".").at(-1)?.toLowerCase()});e[r]={url:"string"==typeof a?a:s,weight:o?1.9:this.getAssetWeight(r),type:n,isNested:i,isPreload:!o}}}return e}),{});return s?.imports?.length&&s.imports.forEach((t=>{const s=e[t];s&&Object.assign(r,this.getRouteAssets(e,s,!0))})),r}buildRoutesManifest(){const t=this.loadClientManifest(),s=new i(this.config,this.viteAliases),r=this.getRoutesTreeIds(s.parse()),n=this.pathNormalize.getImportPostfix(),o={};Object.entries(r).forEach((([e,s])=>{const i=n.find((e=>void 0!==t[`${s}${e}`])),r=t[`${s}${i||""}`];o[e]=this.sortAssets(Object.values(this.getRouteAssets(t,r)))})),e.writeFileSync(this.getAssetsManifestFile(),JSON.stringify(o,null,2),{encoding:"utf-8"})}getAssets(e){if(this.config.getVite())return this.getAssetsDev(e);const t=e?.map((({route:e})=>e.id)).filter(Boolean)??[];if(!t.length)return[];const s=this.loadAssetsManifest();return this.sortAssets(t.map((e=>s[e])).flat().filter(Boolean))}getAssetsDev(e){const s=e?.map((({route:e})=>this.pathNormalize.getAppPath(e?.pathId,!0))).filter(Boolean)??[];if(!s.length)return[];let i={};const r=this.pathNormalize.getImportPostfix();return[t.resolve(this.root,this.config.getPluginConfig()?.clientFile??"client.ts"),...s].forEach((e=>{for(const t of r){const s=this.config.getVite()?.moduleGraph.getModuleById(`${e}${t}`);if(s){i={...i,...this.getModuleAssets(s)};break}}})),Object.values(i)}getModuleAssets(e,t=new Set){if(!e?.clientImportedModules.size||t.has(e.file))return{};let i={};return t.add(e.file),e.clientImportedModules.forEach((e=>{const{file:r,clientImportedModules:o,transformResult:a}=e,l=r?.split(".").at(-1);if(r&&l&&["css","scss"].includes(l)){const e=a?.code.match(/__vite__css\s+=\s+"(?<css>.+)"/)?.groups?.css;if(e)try{i[r]={type:n.style,url:r,weight:this.getAssetWeight(r),content:JSON.parse(`{"style": "${e}"}`).style,isNested:Boolean(t.size),isPreload:!1}}catch(e){console.warn(s.yellowBright("Failed to parse style: ",r))}}else o.size&&(i={...i,...this.getModuleAssets(e,t)})})),i}getAssetWeight(e){switch(this.getAssetType(e)){case n.style:return 1;case n.script:return 2;default:return 3}}getAssetType(e){const t=e.split(".").at(-1)?.toLowerCase();switch(t){case"css":case"scss":return n.style;case"js":return n.script;case"svg":case"jpg":case"jpeg":case"png":case"webp":case"gif":case"ico":return n.image;case"ttf":case"otf":case"woff":case"woff2":return n.font;default:return null}}writeEarlyHits(e,t){t.write(`HTTP/1.1 103 Early Hints${o}`),e.forEach((({type:e,url:s})=>{e&&["style","script"].includes(e)&&t.write(`Link: <${s}>; rel=preload; as=${e}${o}`)})),t.write(o)}injectAssets({routerContext:e,html:t,res:s,hasEarlyHints:i=!1}){const r=this.getAssets(e?.matches),o=r.map((({type:e,url:t,isPreload:s,content:i=""})=>{switch(e){case n.style:return this.config.getVite()?`<style data-vite-dev-id="${t}">${i}</style>`:`<link rel="stylesheet" href="${t}">`;case n.script:return s?this.config.isModulePreload?`<link rel="modulepreload" as="script" crossorigin href="${t}">`:null:`<script async type="module" crossorigin src="${t}"><\/script>`}return null})).filter(Boolean);t.header=t.header.replace("</head>",`${o.join("\n")}</head>`),i&&o.length&&s.socket&&this.writeEarlyHits(r,s.socket)}}export{a as default};
2
2
  //# sourceMappingURL=ssr-manifest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-manifest.js","sources":["../../src/services/ssr-manifest.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { Socket } from 'node:net';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { RouterState } from 'react-router';\nimport type { Alias, ModuleNode } from 'vite';\nimport type { IAsyncRoute } from '@helpers/import-route';\nimport type { IRequestContext } from '@node/render';\nimport type { TRoutesTree } from '@services/parse-routes';\nimport ParseRoutes from '@services/parse-routes';\nimport PathNormalize from '@services/path-normalize';\nimport type ServerConfig from '@services/server-config';\n\ninterface ISsrManifestParams {\n buildDir?: string;\n viteAliases?: Alias[];\n basename?: string;\n}\n\ninterface IManifest {\n [path: string]: {\n assets: string[];\n css: string[];\n file: string;\n isEntry?: boolean;\n imports: string[];\n };\n}\n\nenum AssetType {\n style = 'style',\n script = 'script',\n image = 'image',\n font = 'font',\n}\n\ninterface IAsset {\n type: AssetType;\n url: string;\n weight: number;\n isNested: boolean;\n isPreload: boolean;\n content?: string;\n}\n\ntype TAssets = { [id: string]: IAsset };\n\nconst CRLF = '\\r\\n';\n\n/**\n * Working with SSR Manifest file\n */\nclass SsrManifest {\n /**\n * Singleton\n */\n protected static instance: SsrManifest | null = null;\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * Path normalize service\n */\n protected readonly pathNormalize: PathNormalize;\n\n /**\n * Project root path\n */\n protected readonly root: string;\n\n /**\n * Build dir\n */\n protected readonly buildDir?: string;\n\n /**\n * Client manifest file name\n */\n protected readonly manifestName = 'manifest.json';\n\n /**\n * Assets manifest file name\n */\n protected readonly assetsManifest = 'assets-manifest.json';\n\n /**\n * Vite resolve aliases\n */\n protected readonly viteAliases?: Alias[];\n\n /**\n * Vite base\n */\n protected readonly basename?: string;\n\n /**\n * Loaded assets manifest file\n */\n protected routesAssets: Record<string, IAsset[]> | null = null;\n\n /**\n * @constructor\n */\n protected constructor(\n config: ServerConfig,\n { buildDir, viteAliases, basename }: ISsrManifestParams = {},\n ) {\n this.config = config;\n this.root = config.getParams().root;\n this.buildDir = buildDir;\n this.viteAliases = viteAliases ?? config.getVite()?.config?.resolve.alias;\n this.pathNormalize = new PathNormalize(config, viteAliases);\n this.basename = basename;\n }\n\n /**\n * Get singleton instance\n */\n public static get(config: ServerConfig, params: ISsrManifestParams = {}): SsrManifest {\n if (SsrManifest.instance === null) {\n SsrManifest.instance = new SsrManifest(config, params);\n }\n\n return SsrManifest.instance;\n }\n\n /**\n * Get output dir\n */\n protected getOutDir() {\n return path.resolve(this.root, this.buildDir || '');\n }\n\n /**\n * Get assets manifest file name\n */\n protected getAssetsManifestFile(): string {\n return `${this.getOutDir()}/server/${this.assetsManifest}`;\n }\n\n /**\n * Load client ssr manifest\n */\n protected loadClientManifest(): IManifest {\n const clientManifestDir = path.resolve(this.root, `${this.buildDir || ''}/client/.vite`);\n const clientSsrManifest = `${clientManifestDir}/${this.manifestName}`;\n\n if (!fs.existsSync(clientSsrManifest)) {\n return {};\n }\n\n const result = JSON.parse(\n fs.readFileSync(clientSsrManifest, { encoding: 'utf-8' }),\n ) as IManifest;\n\n fs.rmSync(clientSsrManifest);\n\n // try to remove empty .vite dir\n if (fs.readdirSync(clientManifestDir).length === 0) {\n fs.rmSync(clientManifestDir, { recursive: true });\n }\n\n return result;\n }\n\n /**\n * Load assets manifest\n */\n protected loadAssetsManifest(): Record<string, IAsset[]> {\n if (this.routesAssets !== null) {\n return this.routesAssets;\n }\n\n const manifestFile = this.getAssetsManifestFile();\n\n if (!fs.existsSync(manifestFile)) {\n return {};\n }\n\n this.routesAssets = JSON.parse(fs.readFileSync(manifestFile, { encoding: 'utf-8' })) as Record<\n string,\n IAsset[]\n >;\n\n return this.routesAssets;\n }\n\n /**\n * Same as 'getAsyncRoutesIds' but for routes tree from 'ParseRoutes'\n */\n protected getRoutesTreeIds(\n routes: TRoutesTree[],\n index?: string,\n ): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = {};\n\n routes.forEach((route, routeIndex) => {\n const routeId = [index, String(routeIndex)].filter(Boolean).join('-');\n\n if (route.import) {\n result[routeId] = this.pathNormalize.getAppPath(route.import);\n }\n\n if (route.children.length > 0) {\n Object.assign(result, this.getRoutesTreeIds(route.children, routeId));\n }\n });\n\n return result;\n }\n\n /**\n * Sort assets\n */\n protected sortAssets(assets: IAsset[]): IAsset[] {\n return assets.sort((a, b) =>\n a.weight === b.weight ? Number(a.isNested) - Number(b.isNested) : a.weight - b.weight,\n );\n }\n\n /**\n * Get recursive module assets\n */\n protected getRouteAssets(\n manifest: IManifest,\n module: IManifest[string],\n isNested = false,\n ): Record<string, IAsset> {\n const rootAssets = [...(module?.assets ?? []), ...(module?.css ?? []), module?.file];\n\n const assets = rootAssets.reduce(\n (res, asset) => {\n if (asset) {\n const type = this.getAssetType(asset);\n const isEntry = module.isEntry && module.file === asset;\n\n // keep only js,css,image,fonts files\n if (type) {\n res[asset] = {\n url: path.posix.normalize(`${this.basename}/${asset}`),\n weight: isEntry ? 1.9 : this.getAssetWeight(asset),\n type,\n isNested,\n isPreload: !isEntry,\n };\n }\n }\n\n return res;\n },\n {} as Record<string, IAsset>,\n );\n\n // nested assets\n if (module?.imports?.length) {\n module.imports.forEach((nestedAsset) => {\n const nestedModule = manifest[nestedAsset];\n\n if (nestedModule) {\n Object.assign(assets, this.getRouteAssets(manifest, nestedModule, true));\n }\n });\n }\n\n return assets;\n }\n\n /**\n * Build routes manifest file\n */\n public buildRoutesManifest(): void {\n const manifest = this.loadClientManifest();\n const routesService = new ParseRoutes(this.config, this.viteAliases);\n const routesPaths = this.getRoutesTreeIds(routesService.parse());\n\n const postfixes = this.pathNormalize.getImportPostfix();\n const result: Record<string, IAsset[]> = {};\n\n // find route assets\n Object.entries(routesPaths).forEach(([routeId, routePath]) => {\n const routePostfix = postfixes.find((postfix) => {\n const filePath = `${routePath}${postfix}`;\n\n return manifest[filePath] !== undefined;\n });\n const routeFile = `${routePath}${routePostfix || ''}`;\n const routeMeta = manifest[routeFile];\n\n result[routeId] = this.sortAssets(Object.values(this.getRouteAssets(manifest, routeMeta)));\n });\n\n fs.writeFileSync(this.getAssetsManifestFile(), JSON.stringify(result, null, 2), {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Get route assets\n */\n protected getAssets(routes?: RouterState['matches']): IAsset[] {\n if (this.config.getVite()) {\n return this.getAssetsDev(routes);\n }\n\n const routeIds = routes?.map(({ route }) => route.id).filter(Boolean) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n const routesAssets = this.loadAssetsManifest();\n\n return this.sortAssets(\n routeIds\n .map((routeId) => routesAssets[routeId])\n .flat()\n .filter(Boolean),\n );\n }\n\n /**\n * Get development route assets\n */\n protected getAssetsDev(routes?: RouterState['matches']): IAsset[] {\n const routeIds =\n (routes\n ?.map(({ route }) => this.pathNormalize.getAppPath((route as IAsyncRoute)?.pathId, true))\n .filter(Boolean) as string[]) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n let assets: TAssets = {};\n const postfixes = this.pathNormalize.getImportPostfix();\n const rootId = path.resolve(\n this.root,\n this.config.getPluginConfig()?.clientFile ?? 'client.ts',\n );\n\n [rootId, ...routeIds].forEach((moduleId) => {\n for (const ext of postfixes) {\n const module = this.config.getVite()?.moduleGraph.getModuleById(`${moduleId}${ext}`);\n\n if (module) {\n assets = { ...assets, ...this.getModuleAssets(module) };\n break;\n }\n }\n });\n\n return Object.values(assets);\n }\n\n /**\n * Get module assets\n */\n protected getModuleAssets(module?: ModuleNode, skipModules: Set<string> = new Set()): TAssets {\n if (!module?.clientImportedModules.size || skipModules.has(module.file!)) {\n return {};\n }\n\n let assets: TAssets = {};\n\n skipModules.add(module.file!);\n\n module.clientImportedModules.forEach((subModule) => {\n const { file, clientImportedModules, transformResult } = subModule;\n const ext = file?.split('.').at(-1);\n\n if (file && ext && ['css', 'scss'].includes(ext)) {\n // @TODO investigate better method?\n const code = transformResult?.code.match(/__vite__css\\s+=\\s+\"(?<css>.+)\"/)?.groups?.css;\n\n if (code) {\n try {\n assets[file] = {\n type: AssetType.style,\n url: file,\n weight: this.getAssetWeight(file),\n content: (JSON.parse(`{\"style\": \"${code}\"}`) as { style: string }).style,\n isNested: Boolean(skipModules.size),\n isPreload: false,\n };\n } catch (e) {\n console.warn(chalk.yellowBright('Failed to parse style: ', file));\n }\n }\n } else if (clientImportedModules.size) {\n assets = {\n ...assets,\n ...this.getModuleAssets(subModule, skipModules),\n };\n }\n });\n\n return assets;\n }\n\n /**\n * Get asset weight\n */\n protected getAssetWeight(asset: string): number {\n const type = this.getAssetType(asset);\n\n switch (type) {\n case AssetType.style:\n return 1;\n\n case AssetType.script:\n return 2;\n\n default:\n return 3;\n }\n }\n\n /**\n * Get asset type\n */\n protected getAssetType(asset: string): AssetType | null {\n const ext = asset.split('.').at(-1)?.toLowerCase();\n\n switch (ext) {\n case 'css':\n case 'scss':\n return AssetType.style;\n\n case 'js':\n return AssetType.script;\n\n case 'svg':\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'webp':\n case 'gif':\n case 'ico':\n return AssetType.image;\n\n case 'ttf':\n case 'otf':\n case 'woff':\n case 'woff2':\n return AssetType.font;\n\n default:\n return null;\n }\n }\n\n /**\n * Write 103 Early Hits header\n */\n public writeEarlyHits(assets: IAsset[], socket: Socket): void {\n socket.write(`HTTP/1.1 103 Early Hints${CRLF}`);\n assets.forEach(({ type, url }) => {\n if (!type || !['style', 'script'].includes(type)) {\n return;\n }\n\n socket.write(`Link: <${url}>; rel=preload; as=${type}${CRLF}`);\n });\n socket.write(CRLF);\n }\n\n /**\n * Inject route assets to head html\n */\n public injectAssets({ routerContext, html, res, hasEarlyHints = false }: IRequestContext): void {\n const assets = this.getAssets(routerContext?.matches);\n const htmlAssets = assets\n .map(({ type, url, isPreload, content = '' }) => {\n switch (type) {\n case AssetType.style:\n return this.config.getVite()\n ? `<style data-vite-dev-id=\"${url}\">${content}</style>`\n : `<link rel=\"stylesheet\" href=\"${url}\">`;\n\n case AssetType.script:\n return isPreload\n ? this.config.isModulePreload\n ? // can reduce lighthouse performance\n `<link rel=\"modulepreload\" as=\"script\" crossorigin href=\"${url}\">`\n : null\n : `<script async type=\"module\" crossorigin src=\"${url}\"></script>`;\n }\n\n return null;\n })\n .filter(Boolean);\n\n html.header = html.header.replace('</head>', `${htmlAssets.join('\\n')}</head>`);\n\n if (hasEarlyHints && htmlAssets.length && res.socket) {\n this.writeEarlyHits(assets, res.socket);\n }\n }\n}\n\nexport default SsrManifest;\n"],"names":["AssetType","CRLF","SsrManifest","static","config","pathNormalize","root","buildDir","manifestName","assetsManifest","viteAliases","basename","routesAssets","constructor","this","getParams","getVite","resolve","alias","PathNormalize","params","instance","getOutDir","path","getAssetsManifestFile","loadClientManifest","clientManifestDir","clientSsrManifest","fs","existsSync","result","JSON","parse","readFileSync","encoding","rmSync","readdirSync","length","recursive","loadAssetsManifest","manifestFile","getRoutesTreeIds","routes","index","forEach","route","routeIndex","routeId","String","filter","Boolean","join","import","getAppPath","children","Object","assign","sortAssets","assets","sort","a","b","weight","Number","isNested","getRouteAssets","manifest","module","css","file","reduce","res","asset","type","getAssetType","isEntry","url","posix","normalize","getAssetWeight","isPreload","imports","nestedAsset","nestedModule","buildRoutesManifest","routesService","ParseRoutes","routesPaths","postfixes","getImportPostfix","entries","routePath","routePostfix","find","postfix","undefined","routeMeta","values","writeFileSync","stringify","getAssets","getAssetsDev","routeIds","map","id","flat","pathId","getPluginConfig","clientFile","moduleId","ext","moduleGraph","getModuleById","getModuleAssets","skipModules","Set","clientImportedModules","size","has","add","subModule","transformResult","split","at","includes","code","match","groups","style","content","e","console","warn","chalk","yellowBright","script","toLowerCase","image","font","writeEarlyHits","socket","write","injectAssets","routerContext","html","hasEarlyHints","matches","htmlAssets","isModulePreload","header","replace"],"mappings":"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"}
1
+ {"version":3,"file":"ssr-manifest.js","sources":["../../src/services/ssr-manifest.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { Socket } from 'node:net';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { RouterState } from 'react-router';\nimport type { Alias, ModuleNode, RenderBuiltAssetUrl } from 'vite';\nimport type { IAsyncRoute } from '@helpers/import-route';\nimport type { IRequestContext } from '@node/render';\nimport type { TRoutesTree } from '@services/parse-routes';\nimport ParseRoutes from '@services/parse-routes';\nimport PathNormalize from '@services/path-normalize';\nimport type ServerConfig from '@services/server-config';\n\ninterface ISsrManifestParams {\n buildDir?: string;\n viteAliases?: Alias[];\n basename?: string;\n renderBuiltUrl?: RenderBuiltAssetUrl;\n}\n\ninterface IManifest {\n [path: string]: {\n assets: string[];\n css: string[];\n file: string;\n isEntry?: boolean;\n imports: string[];\n };\n}\n\nenum AssetType {\n style = 'style',\n script = 'script',\n image = 'image',\n font = 'font',\n}\n\ninterface IAsset {\n type: AssetType;\n url: string;\n weight: number;\n isNested: boolean;\n isPreload: boolean;\n content?: string;\n}\n\ntype TAssets = { [id: string]: IAsset };\n\nconst CRLF = '\\r\\n';\n\n/**\n * Working with SSR Manifest file\n */\nclass SsrManifest {\n /**\n * Singleton\n */\n protected static instance: SsrManifest | null = null;\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * Path normalize service\n */\n protected readonly pathNormalize: PathNormalize;\n\n /**\n * Project root path\n */\n protected readonly root: string;\n\n /**\n * Build dir\n */\n protected readonly buildDir?: string;\n\n /**\n * Client manifest file name\n */\n protected readonly manifestName = 'manifest.json';\n\n /**\n * Assets manifest file name\n */\n protected readonly assetsManifest = 'assets-manifest.json';\n\n /**\n * Vite resolve aliases\n */\n protected readonly viteAliases?: Alias[];\n\n /**\n * Vite base\n */\n protected readonly basename?: string;\n\n /**\n * Vite renderBuiltUrl config func\n */\n protected readonly renderBuiltUrl?: RenderBuiltAssetUrl;\n\n /**\n * Loaded assets manifest file\n */\n protected routesAssets: Record<string, IAsset[]> | null = null;\n\n /**\n * @constructor\n */\n protected constructor(\n config: ServerConfig,\n { buildDir, viteAliases, basename, renderBuiltUrl }: ISsrManifestParams = {},\n ) {\n this.config = config;\n this.root = config.getParams().root;\n this.buildDir = buildDir;\n this.viteAliases = viteAliases ?? config.getVite()?.config?.resolve.alias;\n this.pathNormalize = new PathNormalize(config, viteAliases);\n this.basename = basename;\n this.renderBuiltUrl = renderBuiltUrl;\n }\n\n /**\n * Get singleton instance\n */\n public static get(config: ServerConfig, params: ISsrManifestParams = {}): SsrManifest {\n if (SsrManifest.instance === null) {\n SsrManifest.instance = new SsrManifest(config, params);\n }\n\n return SsrManifest.instance;\n }\n\n /**\n * Get output dir\n */\n protected getOutDir() {\n return path.resolve(this.root, this.buildDir || '');\n }\n\n /**\n * Get assets manifest file name\n */\n protected getAssetsManifestFile(): string {\n return `${this.getOutDir()}/server/${this.assetsManifest}`;\n }\n\n /**\n * Load client ssr manifest\n */\n protected loadClientManifest(): IManifest {\n const clientManifestDir = path.resolve(this.root, `${this.buildDir || ''}/client/.vite`);\n const clientSsrManifest = `${clientManifestDir}/${this.manifestName}`;\n\n if (!fs.existsSync(clientSsrManifest)) {\n return {};\n }\n\n const result = JSON.parse(\n fs.readFileSync(clientSsrManifest, { encoding: 'utf-8' }),\n ) as IManifest;\n\n fs.rmSync(clientSsrManifest);\n\n // try to remove empty .vite dir\n if (fs.readdirSync(clientManifestDir).length === 0) {\n fs.rmSync(clientManifestDir, { recursive: true });\n }\n\n return result;\n }\n\n /**\n * Load assets manifest\n */\n protected loadAssetsManifest(): Record<string, IAsset[]> {\n if (this.routesAssets !== null) {\n return this.routesAssets;\n }\n\n const manifestFile = this.getAssetsManifestFile();\n\n if (!fs.existsSync(manifestFile)) {\n return {};\n }\n\n this.routesAssets = JSON.parse(fs.readFileSync(manifestFile, { encoding: 'utf-8' })) as Record<\n string,\n IAsset[]\n >;\n\n return this.routesAssets;\n }\n\n /**\n * Same as 'getAsyncRoutesIds' but for routes tree from 'ParseRoutes'\n */\n protected getRoutesTreeIds(\n routes: TRoutesTree[],\n index?: string,\n ): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = {};\n\n routes.forEach((route, routeIndex) => {\n const routeId = [index, String(routeIndex)].filter(Boolean).join('-');\n\n if (route.import) {\n result[routeId] = this.pathNormalize.getAppPath(route.import);\n }\n\n if (route.children.length > 0) {\n Object.assign(result, this.getRoutesTreeIds(route.children, routeId));\n }\n });\n\n return result;\n }\n\n /**\n * Sort assets\n */\n protected sortAssets(assets: IAsset[]): IAsset[] {\n return assets.sort((a, b) =>\n a.weight === b.weight ? Number(a.isNested) - Number(b.isNested) : a.weight - b.weight,\n );\n }\n\n /**\n * Get recursive module assets\n */\n protected getRouteAssets(\n manifest: IManifest,\n module: IManifest[string],\n isNested = false,\n ): Record<string, IAsset> {\n const rootAssets = [...(module?.assets ?? []), ...(module?.css ?? []), module?.file];\n\n const assets = rootAssets.reduce(\n (res, asset) => {\n if (asset) {\n const type = this.getAssetType(asset);\n const isEntry = module.isEntry && module.file === asset;\n\n // keep only js,css,image,fonts files\n if (type) {\n const filename = path.posix.normalize(`${this.basename}/${asset}`);\n const modifiedFilename = this.renderBuiltUrl?.(filename, {\n type: 'asset',\n ssr: true,\n hostId: '',\n hostType: filename.split('.').at(-1)?.toLowerCase() as 'js',\n });\n\n res[asset] = {\n url: typeof modifiedFilename === 'string' ? modifiedFilename : filename,\n weight: isEntry ? 1.9 : this.getAssetWeight(asset),\n type,\n isNested,\n isPreload: !isEntry,\n };\n }\n }\n\n return res;\n },\n {} as Record<string, IAsset>,\n );\n\n // nested assets\n if (module?.imports?.length) {\n module.imports.forEach((nestedAsset) => {\n const nestedModule = manifest[nestedAsset];\n\n if (nestedModule) {\n Object.assign(assets, this.getRouteAssets(manifest, nestedModule, true));\n }\n });\n }\n\n return assets;\n }\n\n /**\n * Build routes manifest file\n */\n public buildRoutesManifest(): void {\n const manifest = this.loadClientManifest();\n const routesService = new ParseRoutes(this.config, this.viteAliases);\n const routesPaths = this.getRoutesTreeIds(routesService.parse());\n\n const postfixes = this.pathNormalize.getImportPostfix();\n const result: Record<string, IAsset[]> = {};\n\n // find route assets\n Object.entries(routesPaths).forEach(([routeId, routePath]) => {\n const routePostfix = postfixes.find((postfix) => {\n const filePath = `${routePath}${postfix}`;\n\n return manifest[filePath] !== undefined;\n });\n const routeFile = `${routePath}${routePostfix || ''}`;\n const routeMeta = manifest[routeFile];\n\n result[routeId] = this.sortAssets(Object.values(this.getRouteAssets(manifest, routeMeta)));\n });\n\n fs.writeFileSync(this.getAssetsManifestFile(), JSON.stringify(result, null, 2), {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Get route assets\n */\n protected getAssets(routes?: RouterState['matches']): IAsset[] {\n if (this.config.getVite()) {\n return this.getAssetsDev(routes);\n }\n\n const routeIds = routes?.map(({ route }) => route.id).filter(Boolean) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n const routesAssets = this.loadAssetsManifest();\n\n return this.sortAssets(\n routeIds\n .map((routeId) => routesAssets[routeId])\n .flat()\n .filter(Boolean),\n );\n }\n\n /**\n * Get development route assets\n */\n protected getAssetsDev(routes?: RouterState['matches']): IAsset[] {\n const routeIds =\n (routes\n ?.map(({ route }) => this.pathNormalize.getAppPath((route as IAsyncRoute)?.pathId, true))\n .filter(Boolean) as string[]) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n let assets: TAssets = {};\n const postfixes = this.pathNormalize.getImportPostfix();\n const rootId = path.resolve(\n this.root,\n this.config.getPluginConfig()?.clientFile ?? 'client.ts',\n );\n\n [rootId, ...routeIds].forEach((moduleId) => {\n for (const ext of postfixes) {\n const module = this.config.getVite()?.moduleGraph.getModuleById(`${moduleId}${ext}`);\n\n if (module) {\n assets = { ...assets, ...this.getModuleAssets(module) };\n break;\n }\n }\n });\n\n return Object.values(assets);\n }\n\n /**\n * Get module assets\n */\n protected getModuleAssets(module?: ModuleNode, skipModules: Set<string> = new Set()): TAssets {\n if (!module?.clientImportedModules.size || skipModules.has(module.file!)) {\n return {};\n }\n\n let assets: TAssets = {};\n\n skipModules.add(module.file!);\n\n module.clientImportedModules.forEach((subModule) => {\n const { file, clientImportedModules, transformResult } = subModule;\n const ext = file?.split('.').at(-1);\n\n if (file && ext && ['css', 'scss'].includes(ext)) {\n // @TODO investigate better method?\n const code = transformResult?.code.match(/__vite__css\\s+=\\s+\"(?<css>.+)\"/)?.groups?.css;\n\n if (code) {\n try {\n assets[file] = {\n type: AssetType.style,\n url: file,\n weight: this.getAssetWeight(file),\n content: (JSON.parse(`{\"style\": \"${code}\"}`) as { style: string }).style,\n isNested: Boolean(skipModules.size),\n isPreload: false,\n };\n } catch (e) {\n console.warn(chalk.yellowBright('Failed to parse style: ', file));\n }\n }\n } else if (clientImportedModules.size) {\n assets = {\n ...assets,\n ...this.getModuleAssets(subModule, skipModules),\n };\n }\n });\n\n return assets;\n }\n\n /**\n * Get asset weight\n */\n protected getAssetWeight(asset: string): number {\n const type = this.getAssetType(asset);\n\n switch (type) {\n case AssetType.style:\n return 1;\n\n case AssetType.script:\n return 2;\n\n default:\n return 3;\n }\n }\n\n /**\n * Get asset type\n */\n protected getAssetType(asset: string): AssetType | null {\n const ext = asset.split('.').at(-1)?.toLowerCase();\n\n switch (ext) {\n case 'css':\n case 'scss':\n return AssetType.style;\n\n case 'js':\n return AssetType.script;\n\n case 'svg':\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'webp':\n case 'gif':\n case 'ico':\n return AssetType.image;\n\n case 'ttf':\n case 'otf':\n case 'woff':\n case 'woff2':\n return AssetType.font;\n\n default:\n return null;\n }\n }\n\n /**\n * Write 103 Early Hits header\n */\n public writeEarlyHits(assets: IAsset[], socket: Socket): void {\n socket.write(`HTTP/1.1 103 Early Hints${CRLF}`);\n assets.forEach(({ type, url }) => {\n if (!type || !['style', 'script'].includes(type)) {\n return;\n }\n\n socket.write(`Link: <${url}>; rel=preload; as=${type}${CRLF}`);\n });\n socket.write(CRLF);\n }\n\n /**\n * Inject route assets to head html\n */\n public injectAssets({ routerContext, html, res, hasEarlyHints = false }: IRequestContext): void {\n const assets = this.getAssets(routerContext?.matches);\n const htmlAssets = assets\n .map(({ type, url, isPreload, content = '' }) => {\n switch (type) {\n case AssetType.style:\n return this.config.getVite()\n ? `<style data-vite-dev-id=\"${url}\">${content}</style>`\n : `<link rel=\"stylesheet\" href=\"${url}\">`;\n\n case AssetType.script:\n return isPreload\n ? this.config.isModulePreload\n ? // can reduce lighthouse performance\n `<link rel=\"modulepreload\" as=\"script\" crossorigin href=\"${url}\">`\n : null\n : `<script async type=\"module\" crossorigin src=\"${url}\"></script>`;\n }\n\n return null;\n })\n .filter(Boolean);\n\n html.header = html.header.replace('</head>', `${htmlAssets.join('\\n')}</head>`);\n\n if (hasEarlyHints && htmlAssets.length && res.socket) {\n this.writeEarlyHits(assets, res.socket);\n }\n }\n}\n\nexport default SsrManifest;\n"],"names":["AssetType","CRLF","SsrManifest","static","config","pathNormalize","root","buildDir","manifestName","assetsManifest","viteAliases","basename","renderBuiltUrl","routesAssets","constructor","this","getParams","getVite","resolve","alias","PathNormalize","params","instance","getOutDir","path","getAssetsManifestFile","loadClientManifest","clientManifestDir","clientSsrManifest","fs","existsSync","result","JSON","parse","readFileSync","encoding","rmSync","readdirSync","length","recursive","loadAssetsManifest","manifestFile","getRoutesTreeIds","routes","index","forEach","route","routeIndex","routeId","String","filter","Boolean","join","import","getAppPath","children","Object","assign","sortAssets","assets","sort","a","b","weight","Number","isNested","getRouteAssets","manifest","module","css","file","reduce","res","asset","type","getAssetType","isEntry","filename","posix","normalize","modifiedFilename","ssr","hostId","hostType","split","at","toLowerCase","url","getAssetWeight","isPreload","imports","nestedAsset","nestedModule","buildRoutesManifest","routesService","ParseRoutes","routesPaths","postfixes","getImportPostfix","entries","routePath","routePostfix","find","postfix","undefined","routeMeta","values","writeFileSync","stringify","getAssets","getAssetsDev","routeIds","map","id","flat","pathId","getPluginConfig","clientFile","moduleId","ext","moduleGraph","getModuleById","getModuleAssets","skipModules","Set","clientImportedModules","size","has","add","subModule","transformResult","includes","code","match","groups","style","content","e","console","warn","chalk","yellowBright","script","image","font","writeEarlyHits","socket","write","injectAssets","routerContext","html","hasEarlyHints","matches","htmlAssets","isModulePreload","header","replace"],"mappings":"yIA8BA,IAAKA,GAAL,SAAKA,GACHA,EAAA,MAAA,QACAA,EAAA,OAAA,SACAA,EAAA,MAAA,QACAA,EAAA,KAAA,MACD,CALD,CAAKA,IAAAA,EAKJ,CAAA,IAaD,MAAMC,EAAO,OAKb,MAAMC,EAIMC,gBAAsC,KAK7BC,OAKAC,cAKAC,KAKAC,SAKAC,aAAe,gBAKfC,eAAiB,uBAKjBC,YAKAC,SAKAC,eAKTC,aAAgD,KAK1DC,YACEV,GACAG,SAAEA,EAAQG,YAAEA,EAAWC,SAAEA,EAAQC,eAAEA,GAAuC,IAE1EG,KAAKX,OAASA,EACdW,KAAKT,KAAOF,EAAOY,YAAYV,KAC/BS,KAAKR,SAAWA,EAChBQ,KAAKL,YAAcA,GAAeN,EAAOa,WAAWb,QAAQc,QAAQC,MACpEJ,KAAKV,cAAgB,IAAIe,EAAchB,EAAQM,GAC/CK,KAAKJ,SAAWA,EAChBI,KAAKH,eAAiBA,EAMjBT,WAAWC,EAAsBiB,EAA6B,IAKnE,OAJ6B,OAAzBnB,EAAYoB,WACdpB,EAAYoB,SAAW,IAAIpB,EAAYE,EAAQiB,IAG1CnB,EAAYoB,SAMXC,YACR,OAAOC,EAAKN,QAAQH,KAAKT,KAAMS,KAAKR,UAAY,IAMxCkB,wBACR,MAAO,GAAGV,KAAKQ,sBAAsBR,KAAKN,iBAMlCiB,qBACR,MAAMC,EAAoBH,EAAKN,QAAQH,KAAKT,KAAM,GAAGS,KAAKR,UAAY,mBAChEqB,EAAoB,GAAGD,KAAqBZ,KAAKP,eAEvD,IAAKqB,EAAGC,WAAWF,GACjB,MAAO,CAAE,EAGX,MAAMG,EAASC,KAAKC,MAClBJ,EAAGK,aAAaN,EAAmB,CAAEO,SAAU,WAUjD,OAPAN,EAAGO,OAAOR,GAGuC,IAA7CC,EAAGQ,YAAYV,GAAmBW,QACpCT,EAAGO,OAAOT,EAAmB,CAAEY,WAAW,IAGrCR,EAMCS,qBACR,GAA0B,OAAtBzB,KAAKF,aACP,OAAOE,KAAKF,aAGd,MAAM4B,EAAe1B,KAAKU,wBAE1B,OAAKI,EAAGC,WAAWW,IAInB1B,KAAKF,aAAemB,KAAKC,MAAMJ,EAAGK,aAAaO,EAAc,CAAEN,SAAU,WAKlEpB,KAAKF,cARH,CAAE,EAcH6B,iBACRC,EACAC,GAEA,MAAMb,EAA6C,CAAE,EAcrD,OAZAY,EAAOE,SAAQ,CAACC,EAAOC,KACrB,MAAMC,EAAU,CAACJ,EAAOK,OAAOF,IAAaG,OAAOC,SAASC,KAAK,KAE7DN,EAAMO,SACRtB,EAAOiB,GAAWjC,KAAKV,cAAciD,WAAWR,EAAMO,SAGpDP,EAAMS,SAASjB,OAAS,GAC1BkB,OAAOC,OAAO1B,EAAQhB,KAAK2B,iBAAiBI,EAAMS,SAAUP,OAIzDjB,EAMC2B,WAAWC,GACnB,OAAOA,EAAOC,MAAK,CAACC,EAAGC,IACrBD,EAAEE,SAAWD,EAAEC,OAASC,OAAOH,EAAEI,UAAYD,OAAOF,EAAEG,UAAYJ,EAAEE,OAASD,EAAEC,SAOzEG,eACRC,EACAC,EACAH,GAAW,GAEX,MAEMN,EAFa,IAAKS,GAAQT,QAAU,MAASS,GAAQC,KAAO,GAAKD,GAAQE,MAErDC,QACxB,CAACC,EAAKC,KACJ,GAAIA,EAAO,CACT,MAAMC,EAAO3D,KAAK4D,aAAaF,GACzBG,EAAUR,EAAOQ,SAAWR,EAAOE,OAASG,EAGlD,GAAIC,EAAM,CACR,MAAMG,EAAWrD,EAAKsD,MAAMC,UAAU,GAAGhE,KAAKJ,YAAY8D,KACpDO,EAAmBjE,KAAKH,iBAAiBiE,EAAU,CACvDH,KAAM,QACNO,KAAK,EACLC,OAAQ,GACRC,SAAUN,EAASO,MAAM,KAAKC,IAAG,IAAKC,gBAGxCd,EAAIC,GAAS,CACXc,IAAiC,iBAArBP,EAAgCA,EAAmBH,EAC/Dd,OAAQa,EAAU,IAAM7D,KAAKyE,eAAef,GAC5CC,OACAT,WACAwB,WAAYb,IAKlB,OAAOJ,CAAG,GAEZ,IAcF,OAVIJ,GAAQsB,SAASpD,QACnB8B,EAAOsB,QAAQ7C,SAAS8C,IACtB,MAAMC,EAAezB,EAASwB,GAE1BC,GACFpC,OAAOC,OAAOE,EAAQ5C,KAAKmD,eAAeC,EAAUyB,GAAc,OAKjEjC,EAMFkC,sBACL,MAAM1B,EAAWpD,KAAKW,qBAChBoE,EAAgB,IAAIC,EAAYhF,KAAKX,OAAQW,KAAKL,aAClDsF,EAAcjF,KAAK2B,iBAAiBoD,EAAc7D,SAElDgE,EAAYlF,KAAKV,cAAc6F,mBAC/BnE,EAAmC,CAAE,EAG3CyB,OAAO2C,QAAQH,GAAanD,SAAQ,EAAEG,EAASoD,MAC7C,MAAMC,EAAeJ,EAAUK,MAAMC,QAGLC,IAAvBrC,EAFU,GAAGiC,IAAYG,OAK5BE,EAAYtC,EADA,GAAGiC,IAAYC,GAAgB,MAGjDtE,EAAOiB,GAAWjC,KAAK2C,WAAWF,OAAOkD,OAAO3F,KAAKmD,eAAeC,EAAUsC,IAAY,IAG5F5E,EAAG8E,cAAc5F,KAAKU,wBAAyBO,KAAK4E,UAAU7E,EAAQ,KAAM,GAAI,CAC9EI,SAAU,UAOJ0E,UAAUlE,GAClB,GAAI5B,KAAKX,OAAOa,UACd,OAAOF,KAAK+F,aAAanE,GAG3B,MAAMoE,EAAWpE,GAAQqE,KAAI,EAAGlE,WAAYA,EAAMmE,KAAI/D,OAAOC,UAAY,GAEzE,IAAK4D,EAASzE,OACZ,MAAO,GAGT,MAAMzB,EAAeE,KAAKyB,qBAE1B,OAAOzB,KAAK2C,WACVqD,EACGC,KAAKhE,GAAYnC,EAAamC,KAC9BkE,OACAhE,OAAOC,UAOJ2D,aAAanE,GACrB,MAAMoE,EACHpE,GACGqE,KAAI,EAAGlE,WAAY/B,KAAKV,cAAciD,WAAYR,GAAuBqE,QAAQ,KAClFjE,OAAOC,UAAyB,GAErC,IAAK4D,EAASzE,OACZ,MAAO,GAGT,IAAIqB,EAAkB,CAAE,EACxB,MAAMsC,EAAYlF,KAAKV,cAAc6F,mBAiBrC,MAXA,CALe1E,EAAKN,QAClBH,KAAKT,KACLS,KAAKX,OAAOgH,mBAAmBC,YAAc,gBAGnCN,GAAUlE,SAASyE,IAC7B,IAAK,MAAMC,KAAOtB,EAAW,CAC3B,MAAM7B,EAASrD,KAAKX,OAAOa,WAAWuG,YAAYC,cAAc,GAAGH,IAAWC,KAE9E,GAAInD,EAAQ,CACVT,EAAS,IAAKA,KAAW5C,KAAK2G,gBAAgBtD,IAC9C,WAKCZ,OAAOkD,OAAO/C,GAMb+D,gBAAgBtD,EAAqBuD,EAA2B,IAAIC,KAC5E,IAAKxD,GAAQyD,sBAAsBC,MAAQH,EAAYI,IAAI3D,EAAOE,MAChE,MAAO,CAAE,EAGX,IAAIX,EAAkB,CAAE,EAkCxB,OAhCAgE,EAAYK,IAAI5D,EAAOE,MAEvBF,EAAOyD,sBAAsBhF,SAASoF,IACpC,MAAM3D,KAAEA,EAAIuD,sBAAEA,EAAqBK,gBAAEA,GAAoBD,EACnDV,EAAMjD,GAAMc,MAAM,KAAKC,OAE7B,GAAIf,GAAQiD,GAAO,CAAC,MAAO,QAAQY,SAASZ,GAAM,CAEhD,MAAMa,EAAOF,GAAiBE,KAAKC,MAAM,mCAAmCC,QAAQjE,IAEpF,GAAI+D,EACF,IACEzE,EAAOW,GAAQ,CACbI,KAAM1E,EAAUuI,MAChBhD,IAAKjB,EACLP,OAAQhD,KAAKyE,eAAelB,GAC5BkE,QAAUxG,KAAKC,MAAM,cAAcmG,OAAgCG,MACnEtE,SAAUd,QAAQwE,EAAYG,MAC9BrC,WAAW,GAEb,MAAOgD,GACPC,QAAQC,KAAKC,EAAMC,aAAa,0BAA2BvE,UAGtDuD,EAAsBC,OAC/BnE,EAAS,IACJA,KACA5C,KAAK2G,gBAAgBO,EAAWN,QAKlChE,EAMC6B,eAAef,GAGvB,OAFa1D,KAAK4D,aAAaF,IAG7B,KAAKzE,EAAUuI,MACb,OAAO,EAET,KAAKvI,EAAU8I,OACb,OAAO,EAET,QACE,OAAO,GAOHnE,aAAaF,GACrB,MAAM8C,EAAM9C,EAAMW,MAAM,KAAKC,IAAG,IAAKC,cAErC,OAAQiC,GACN,IAAK,MACL,IAAK,OACH,OAAOvH,EAAUuI,MAEnB,IAAK,KACH,OAAOvI,EAAU8I,OAEnB,IAAK,MACL,IAAK,MACL,IAAK,OACL,IAAK,MACL,IAAK,OACL,IAAK,MACL,IAAK,MACH,OAAO9I,EAAU+I,MAEnB,IAAK,MACL,IAAK,MACL,IAAK,OACL,IAAK,QACH,OAAO/I,EAAUgJ,KAEnB,QACE,OAAO,MAONC,eAAetF,EAAkBuF,GACtCA,EAAOC,MAAM,2BAA2BlJ,KACxC0D,EAAOd,SAAQ,EAAG6B,OAAMa,UACjBb,GAAS,CAAC,QAAS,UAAUyD,SAASzD,IAI3CwE,EAAOC,MAAM,UAAU5D,uBAAyBb,IAAOzE,IAAO,IAEhEiJ,EAAOC,MAAMlJ,GAMRmJ,cAAaC,cAAEA,EAAaC,KAAEA,EAAI9E,IAAEA,EAAG+E,cAAEA,GAAgB,IAC9D,MAAM5F,EAAS5C,KAAK8F,UAAUwC,GAAeG,SACvCC,EAAa9F,EAChBqD,KAAI,EAAGtC,OAAMa,MAAKE,YAAW+C,UAAU,OACtC,OAAQ9D,GACN,KAAK1E,EAAUuI,MACb,OAAOxH,KAAKX,OAAOa,UACf,4BAA4BsE,MAAQiD,YACpC,gCAAgCjD,MAEtC,KAAKvF,EAAU8I,OACb,OAAOrD,EACH1E,KAAKX,OAAOsJ,gBAEV,2DAA2DnE,MAC3D,KACF,gDAAgDA,gBAGxD,OAAO,IAAI,IAEZrC,OAAOC,SAEVmG,EAAKK,OAASL,EAAKK,OAAOC,QAAQ,UAAW,GAAGH,EAAWrG,KAAK,gBAE5DmG,GAAiBE,EAAWnH,QAAUkC,EAAI0E,QAC5CnI,KAAKkI,eAAetF,EAAQa,EAAI0E"}