@lomray/vite-ssr-boost 2.3.7 → 2.4.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli/build.js CHANGED
@@ -1,2 +1,2 @@
1
- import e from"node:child_process";import{performance as o}from"node:perf_hooks";import i from"chalk";import s from"./helpers/vite-reset-cache.js";import t from"../constants/cli-name.js";import{createDevMarker as r}from"../helpers/dev-marker.js";import n from"../helpers/process-stop.js";import l from"../services/build.js";async function a({onFinish:a,mode:c="",clientOptions:p="",serverOptions:d="",isOnlyClient:m=!1,isWatch:f=!1,isUnlockRobots:u=!1,isEject:v=!1,isServerless:O=!1,isNoWarnings:S=!1}){const $=o.now(),h=new l({mode:c}),_=["client"],b=new AbortController,g=c?`--mode ${c}`:"";await h.makeConfig(),await s(),h.clearBuildFolder();const w=h.promisifyProcess(e.spawn(`vite build ${p} --emptyOutDir --outDir ${h.outDir}/client ${g}`,{signal:b.signal,stdio:[process.stdin,"pipe","pipe"],shell:!0,env:{...process.env,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:m?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}),S);if(!f){const e=await w.promise;n(e,!0)}let C=null;if(!m){if(C=h.promisifyProcess(e.spawn(`vite build ${d} --emptyOutDir --outDir ${h.outDir}/server --ssr ${h.serverFile} ${g}`,{signal:b.signal,stdio:[process.stdin,"pipe","pipe"],shell:!0,env:{...process.env,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:m?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}),S),!f){const e=await C.promise;n(e,!0),await h.buildManifest(),v&&h.eject(),O&&h.createServerless()}_.push("server")}if(f){process.on("exit",(()=>{b.abort()}));let e=m?1:2;const o=i=>{Buffer.from(i).toString().includes("built in")&&(e-=1,e||(w.command.stdout?.removeListener("data",o),C?.command.stdout?.removeListener("data",o),r(h.isProd,h.viteConfig),a?.()))};return w.command.stdout?.on("data",o),void C?.command.stdout?.on("data",o)}u&&h.unlockRobots(),r(h.isProd,h.viteConfig),a?.();const R=Math.ceil(o.now()-$),B=R>1e3?(R/1e3).toFixed(2):R,j=R>1e3?"s":"ms",y=i.dim(`${i.yellowBright(_.join(","))} built in ${i.reset(i.bold(B))} ${j}`);console.info(`\n ${i.green(`${i.bold(t.toUpperCase())}`)} ${y} ${h.isProd?"":i.redBright(`NODE_ENV=${h.nodeEnv}`)}\n`)}export{a as default};
1
+ import e from"node:child_process";import{performance as o}from"node:perf_hooks";import i from"chalk";import s from"./helpers/vite-reset-cache.js";import t from"../constants/cli-name.js";import{createDevMarker as r}from"../helpers/dev-marker.js";import n from"../helpers/process-stop.js";import{removeMeta as l}from"../helpers/ssr-meta.js";import a from"../services/build.js";async function m({onFinish:m,mode:p="",clientOptions:c="",serverOptions:d="",isOnlyClient:f=!1,isWatch:u=!1,isUnlockRobots:v=!1,isEject:O=!1,isServerless:S=!1,isNoWarnings:h=!1}){const $=o.now(),b=new a({mode:p}),_=["client"],g=new AbortController,w=p?`--mode ${p}`:"";await b.makeConfig(),await s(),b.clearBuildFolder();const C=b.promisifyProcess(e.spawn(`vite build ${c} --emptyOutDir --outDir ${b.outDir}/client ${w}`,{signal:g.signal,stdio:[process.stdin,"pipe","pipe"],shell:!0,env:{...process.env,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:f?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}),h);if(!u){const e=await C.promise;n(e,!0)}let R=null;if(!f){if(R=b.promisifyProcess(e.spawn(`vite build ${d} --emptyOutDir --outDir ${b.outDir}/server --ssr ${b.serverFile} ${w}`,{signal:g.signal,stdio:[process.stdin,"pipe","pipe"],shell:!0,env:{...process.env,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:f?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}),h),!u){const e=await R.promise;n(e,!0),await b.buildManifest(),O&&b.eject(),S&&b.createServerless()}_.push("server")}if(u){process.on("exit",(()=>{g.abort()}));let e=f?1:2;const o=i=>{Buffer.from(i).toString().includes("built in")&&(e-=1,e||(C.command.stdout?.removeListener("data",o),R?.command.stdout?.removeListener("data",o),r(b.isProd,b.viteConfig),m?.()))};return C.command.stdout?.on("data",o),void R?.command.stdout?.on("data",o)}v&&b.unlockRobots(),r(b.isProd,b.viteConfig),l(b.buildDir),m?.();const B=Math.ceil(o.now()-$),j=B>1e3?(B/1e3).toFixed(2):B,D=B>1e3?"s":"ms",y=i.dim(`${i.yellowBright(_.join(","))} built in ${i.reset(i.bold(j))} ${D}`);console.info(`\n ${i.green(`${i.bold(t.toUpperCase())}`)} ${y} ${b.isProd?"":i.redBright(`NODE_ENV=${b.nodeEnv}`)}\n`)}export{m as default};
2
2
  //# sourceMappingURL=build.js.map
package/cli/build.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sources":["../../src/cli/build.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport { performance } from 'node:perf_hooks';\nimport chalk from 'chalk';\nimport viteResetCache from '@cli/helpers/vite-reset-cache';\nimport cliName from '@constants/cli-name';\nimport { createDevMarker } from '@helpers/dev-marker';\nimport processStop from '@helpers/process-stop';\nimport Build from '@services/build';\n\ninterface IBuildParams {\n onFinish?: () => void;\n mode?: string;\n clientOptions?: string;\n serverOptions?: string;\n isOnlyClient?: boolean;\n isWatch?: boolean;\n isUnlockRobots?: boolean;\n isEject?: boolean;\n isServerless?: boolean;\n isNoWarnings?: boolean;\n}\n\n/**\n * Build production application\n */\nasync function build({\n onFinish,\n mode = '',\n clientOptions = '',\n serverOptions = '',\n isOnlyClient = false,\n isWatch = false,\n isUnlockRobots = false,\n isEject = false,\n isServerless = false,\n isNoWarnings = false,\n}: IBuildParams): Promise<void> {\n const perfStart = performance.now();\n const buildService = new Build({ mode });\n const types = ['client'];\n const controller = new AbortController();\n const modeOpt = mode ? `--mode ${mode}` : '';\n\n await buildService.makeConfig();\n\n // this is required step - build with different env may cause problems\n await viteResetCache();\n buildService.clearBuildFolder();\n\n /**\n * Build client\n */\n const clientProcess = buildService.promisifyProcess(\n childProcess.spawn(\n `vite build ${clientOptions} --emptyOutDir --outDir ${buildService.outDir}/client ${modeOpt}`,\n {\n signal: controller.signal,\n stdio: [process.stdin, 'pipe', 'pipe'],\n shell: true,\n env: {\n ...process.env,\n FORCE_COLOR: '2',\n SSR_BOOST_IS_SSR: isOnlyClient ? '0' : '1',\n SSR_BOOST_ACTION: global.viteBoostAction,\n },\n },\n ),\n isNoWarnings,\n );\n\n if (!isWatch) {\n const exitCode = await clientProcess.promise;\n\n processStop(exitCode, true);\n }\n\n let serverProcess: ReturnType<Build['promisifyProcess']> | null = null;\n\n /**\n * Build server\n */\n if (!isOnlyClient) {\n serverProcess = buildService.promisifyProcess(\n childProcess.spawn(\n `vite build ${serverOptions} --emptyOutDir --outDir ${buildService.outDir}/server --ssr ${buildService.serverFile} ${modeOpt}`,\n {\n signal: controller.signal,\n stdio: [process.stdin, 'pipe', 'pipe'],\n shell: true,\n env: {\n ...process.env,\n FORCE_COLOR: '2',\n SSR_BOOST_IS_SSR: isOnlyClient ? '0' : '1',\n SSR_BOOST_ACTION: global.viteBoostAction,\n },\n },\n ),\n isNoWarnings,\n );\n\n if (!isWatch) {\n const exitCode = await serverProcess.promise;\n\n processStop(exitCode, true);\n await buildService.buildManifest();\n\n if (isEject) {\n buildService.eject();\n }\n\n if (isServerless) {\n buildService.createServerless();\n }\n }\n\n types.push('server');\n }\n\n /**\n * Preview mode\n */\n if (isWatch) {\n process.on('exit', () => {\n controller.abort();\n });\n\n let buildCount = isOnlyClient ? 1 : 2;\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 clientProcess.command.stdout?.removeListener('data', listener);\n serverProcess?.command.stdout?.removeListener('data', listener);\n createDevMarker(buildService.isProd, buildService.viteConfig);\n onFinish?.();\n }\n }\n };\n\n /**\n * Listen output for call onFinish\n */\n clientProcess.command.stdout?.on('data', listener);\n serverProcess?.command.stdout?.on('data', listener);\n\n return;\n }\n\n if (isUnlockRobots) {\n buildService.unlockRobots();\n }\n\n createDevMarker(buildService.isProd, buildService.viteConfig);\n onFinish?.();\n\n const durationMs = Math.ceil(performance.now() - perfStart);\n const duration = durationMs > 1000 ? (durationMs / 1000).toFixed(2) : durationMs;\n const units = durationMs > 1000 ? 's' : 'ms';\n\n const buildDurationString = chalk.dim(\n `${chalk.yellowBright(types.join(','))} built in ${chalk.reset(chalk.bold(duration))} ${units}`,\n );\n\n console.info(\n `\\n ${chalk.green(`${chalk.bold(cliName.toUpperCase())}`)} ${buildDurationString} ${\n buildService.isProd ? '' : chalk.redBright(`NODE_ENV=${buildService.nodeEnv}`)\n }\\n`,\n );\n}\n\nexport default build;\n"],"names":["async","build","onFinish","mode","clientOptions","serverOptions","isOnlyClient","isWatch","isUnlockRobots","isEject","isServerless","isNoWarnings","perfStart","performance","now","buildService","Build","types","controller","AbortController","modeOpt","makeConfig","viteResetCache","clearBuildFolder","clientProcess","promisifyProcess","childProcess","spawn","outDir","signal","stdio","process","stdin","shell","env","FORCE_COLOR","SSR_BOOST_IS_SSR","SSR_BOOST_ACTION","global","viteBoostAction","exitCode","promise","processStop","serverProcess","serverFile","buildManifest","eject","createServerless","push","on","abort","buildCount","listener","buff","Buffer","from","toString","includes","command","stdout","removeListener","createDevMarker","isProd","viteConfig","unlockRobots","durationMs","Math","ceil","duration","toFixed","units","buildDurationString","chalk","dim","yellowBright","join","reset","bold","console","info","green","cliName","toUpperCase","redBright","nodeEnv"],"mappings":"mUAyBAA,eAAeC,GAAMC,SACnBA,EAAQC,KACRA,EAAO,GAAEC,cACTA,EAAgB,GAAEC,cAClBA,EAAgB,GAAEC,aAClBA,GAAe,EAAKC,QACpBA,GAAU,EAAKC,eACfA,GAAiB,EAAKC,QACtBA,GAAU,EAAKC,aACfA,GAAe,EAAKC,aACpBA,GAAe,IAEf,MAAMC,EAAYC,EAAYC,MACxBC,EAAe,IAAIC,EAAM,CAAEb,SAC3Bc,EAAQ,CAAC,UACTC,EAAa,IAAIC,gBACjBC,EAAUjB,EAAO,UAAUA,IAAS,SAEpCY,EAAaM,mBAGbC,IACNP,EAAaQ,mBAKb,MAAMC,EAAgBT,EAAaU,iBACjCC,EAAaC,MACX,cAAcvB,4BAAwCW,EAAaa,iBAAiBR,IACpF,CACES,OAAQX,EAAWW,OACnBC,MAAO,CAACC,QAAQC,MAAO,OAAQ,QAC/BC,OAAO,EACPC,IAAK,IACAH,QAAQG,IACXC,YAAa,IACbC,iBAAkB9B,EAAe,IAAM,IACvC+B,iBAAkBC,OAAOC,mBAI/B5B,GAGF,IAAKJ,EAAS,CACZ,MAAMiC,QAAiBhB,EAAciB,QAErCC,EAAYF,GAAU,EACvB,CAED,IAAIG,EAA8D,KAKlE,IAAKrC,EAAc,CAmBjB,GAlBAqC,EAAgB5B,EAAaU,iBAC3BC,EAAaC,MACX,cAActB,4BAAwCU,EAAaa,uBAAuBb,EAAa6B,cAAcxB,IACrH,CACES,OAAQX,EAAWW,OACnBC,MAAO,CAACC,QAAQC,MAAO,OAAQ,QAC/BC,OAAO,EACPC,IAAK,IACAH,QAAQG,IACXC,YAAa,IACbC,iBAAkB9B,EAAe,IAAM,IACvC+B,iBAAkBC,OAAOC,mBAI/B5B,IAGGJ,EAAS,CACZ,MAAMiC,QAAiBG,EAAcF,QAErCC,EAAYF,GAAU,SAChBzB,EAAa8B,gBAEfpC,GACFM,EAAa+B,QAGXpC,GACFK,EAAagC,kBAEhB,CAED9B,EAAM+B,KAAK,SACZ,CAKD,GAAIzC,EAAS,CACXwB,QAAQkB,GAAG,QAAQ,KACjB/B,EAAWgC,OAAO,IAGpB,IAAIC,EAAa7C,EAAe,EAAI,EACpC,MAAM8C,EAAYC,IACJC,OAAOC,KAAKF,GAAMG,WAEtBC,SAAS,cACfN,GAAc,EAETA,IACH3B,EAAckC,QAAQC,QAAQC,eAAe,OAAQR,GACrDT,GAAee,QAAQC,QAAQC,eAAe,OAAQR,GACtDS,EAAgB9C,EAAa+C,OAAQ/C,EAAagD,YAClD7D,OAEH,EASH,OAHAsB,EAAckC,QAAQC,QAAQV,GAAG,OAAQG,QACzCT,GAAee,QAAQC,QAAQV,GAAG,OAAQG,EAG3C,CAEG5C,GACFO,EAAaiD,eAGfH,EAAgB9C,EAAa+C,OAAQ/C,EAAagD,YAClD7D,MAEA,MAAM+D,EAAaC,KAAKC,KAAKtD,EAAYC,MAAQF,GAC3CwD,EAAWH,EAAa,KAAQA,EAAa,KAAMI,QAAQ,GAAKJ,EAChEK,EAAQL,EAAa,IAAO,IAAM,KAElCM,EAAsBC,EAAMC,IAChC,GAAGD,EAAME,aAAazD,EAAM0D,KAAK,kBAAkBH,EAAMI,MAAMJ,EAAMK,KAAKT,OAAcE,KAG1FQ,QAAQC,KACN,OAAOP,EAAMQ,MAAM,GAAGR,EAAMK,KAAKI,EAAQC,sBAAsBX,KAC7DxD,EAAa+C,OAAS,GAAKU,EAAMW,UAAU,YAAYpE,EAAaqE,eAG1E"}
1
+ {"version":3,"file":"build.js","sources":["../../src/cli/build.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport { performance } from 'node:perf_hooks';\nimport chalk from 'chalk';\nimport viteResetCache from '@cli/helpers/vite-reset-cache';\nimport cliName from '@constants/cli-name';\nimport { createDevMarker } from '@helpers/dev-marker';\nimport processStop from '@helpers/process-stop';\nimport { removeMeta } from '@helpers/ssr-meta';\nimport Build from '@services/build';\n\ninterface IBuildParams {\n onFinish?: () => void;\n mode?: string;\n clientOptions?: string;\n serverOptions?: string;\n isOnlyClient?: boolean;\n isWatch?: boolean;\n isUnlockRobots?: boolean;\n isEject?: boolean;\n isServerless?: boolean;\n isNoWarnings?: boolean;\n}\n\n/**\n * Build production application\n */\nasync function build({\n onFinish,\n mode = '',\n clientOptions = '',\n serverOptions = '',\n isOnlyClient = false,\n isWatch = false,\n isUnlockRobots = false,\n isEject = false,\n isServerless = false,\n isNoWarnings = false,\n}: IBuildParams): Promise<void> {\n const perfStart = performance.now();\n const buildService = new Build({ mode });\n const types = ['client'];\n const controller = new AbortController();\n const modeOpt = mode ? `--mode ${mode}` : '';\n\n await buildService.makeConfig();\n\n // this is required step - build with different env may cause problems\n await viteResetCache();\n buildService.clearBuildFolder();\n\n /**\n * Build client\n */\n const clientProcess = buildService.promisifyProcess(\n childProcess.spawn(\n `vite build ${clientOptions} --emptyOutDir --outDir ${buildService.outDir}/client ${modeOpt}`,\n {\n signal: controller.signal,\n stdio: [process.stdin, 'pipe', 'pipe'],\n shell: true,\n env: {\n ...process.env,\n FORCE_COLOR: '2',\n SSR_BOOST_IS_SSR: isOnlyClient ? '0' : '1',\n SSR_BOOST_ACTION: global.viteBoostAction,\n },\n },\n ),\n isNoWarnings,\n );\n\n if (!isWatch) {\n const exitCode = await clientProcess.promise;\n\n processStop(exitCode, true);\n }\n\n let serverProcess: ReturnType<Build['promisifyProcess']> | null = null;\n\n /**\n * Build server\n */\n if (!isOnlyClient) {\n serverProcess = buildService.promisifyProcess(\n childProcess.spawn(\n `vite build ${serverOptions} --emptyOutDir --outDir ${buildService.outDir}/server --ssr ${buildService.serverFile} ${modeOpt}`,\n {\n signal: controller.signal,\n stdio: [process.stdin, 'pipe', 'pipe'],\n shell: true,\n env: {\n ...process.env,\n FORCE_COLOR: '2',\n SSR_BOOST_IS_SSR: isOnlyClient ? '0' : '1',\n SSR_BOOST_ACTION: global.viteBoostAction,\n },\n },\n ),\n isNoWarnings,\n );\n\n if (!isWatch) {\n const exitCode = await serverProcess.promise;\n\n processStop(exitCode, true);\n await buildService.buildManifest();\n\n if (isEject) {\n buildService.eject();\n }\n\n if (isServerless) {\n buildService.createServerless();\n }\n }\n\n types.push('server');\n }\n\n /**\n * Preview mode\n */\n if (isWatch) {\n process.on('exit', () => {\n controller.abort();\n });\n\n let buildCount = isOnlyClient ? 1 : 2;\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 clientProcess.command.stdout?.removeListener('data', listener);\n serverProcess?.command.stdout?.removeListener('data', listener);\n createDevMarker(buildService.isProd, buildService.viteConfig);\n onFinish?.();\n }\n }\n };\n\n /**\n * Listen output for call onFinish\n */\n clientProcess.command.stdout?.on('data', listener);\n serverProcess?.command.stdout?.on('data', listener);\n\n return;\n }\n\n if (isUnlockRobots) {\n buildService.unlockRobots();\n }\n\n createDevMarker(buildService.isProd, buildService.viteConfig);\n removeMeta(buildService.buildDir);\n onFinish?.();\n\n const durationMs = Math.ceil(performance.now() - perfStart);\n const duration = durationMs > 1000 ? (durationMs / 1000).toFixed(2) : durationMs;\n const units = durationMs > 1000 ? 's' : 'ms';\n\n const buildDurationString = chalk.dim(\n `${chalk.yellowBright(types.join(','))} built in ${chalk.reset(chalk.bold(duration))} ${units}`,\n );\n\n console.info(\n `\\n ${chalk.green(`${chalk.bold(cliName.toUpperCase())}`)} ${buildDurationString} ${\n buildService.isProd ? '' : chalk.redBright(`NODE_ENV=${buildService.nodeEnv}`)\n }\\n`,\n );\n}\n\nexport default build;\n"],"names":["async","build","onFinish","mode","clientOptions","serverOptions","isOnlyClient","isWatch","isUnlockRobots","isEject","isServerless","isNoWarnings","perfStart","performance","now","buildService","Build","types","controller","AbortController","modeOpt","makeConfig","viteResetCache","clearBuildFolder","clientProcess","promisifyProcess","childProcess","spawn","outDir","signal","stdio","process","stdin","shell","env","FORCE_COLOR","SSR_BOOST_IS_SSR","SSR_BOOST_ACTION","global","viteBoostAction","exitCode","promise","processStop","serverProcess","serverFile","buildManifest","eject","createServerless","push","on","abort","buildCount","listener","buff","Buffer","from","toString","includes","command","stdout","removeListener","createDevMarker","isProd","viteConfig","unlockRobots","removeMeta","buildDir","durationMs","Math","ceil","duration","toFixed","units","buildDurationString","chalk","dim","yellowBright","join","reset","bold","console","info","green","cliName","toUpperCase","redBright","nodeEnv"],"mappings":"uXA0BAA,eAAeC,GAAMC,SACnBA,EAAQC,KACRA,EAAO,GAAEC,cACTA,EAAgB,GAAEC,cAClBA,EAAgB,GAAEC,aAClBA,GAAe,EAAKC,QACpBA,GAAU,EAAKC,eACfA,GAAiB,EAAKC,QACtBA,GAAU,EAAKC,aACfA,GAAe,EAAKC,aACpBA,GAAe,IAEf,MAAMC,EAAYC,EAAYC,MACxBC,EAAe,IAAIC,EAAM,CAAEb,SAC3Bc,EAAQ,CAAC,UACTC,EAAa,IAAIC,gBACjBC,EAAUjB,EAAO,UAAUA,IAAS,SAEpCY,EAAaM,mBAGbC,IACNP,EAAaQ,mBAKb,MAAMC,EAAgBT,EAAaU,iBACjCC,EAAaC,MACX,cAAcvB,4BAAwCW,EAAaa,iBAAiBR,IACpF,CACES,OAAQX,EAAWW,OACnBC,MAAO,CAACC,QAAQC,MAAO,OAAQ,QAC/BC,OAAO,EACPC,IAAK,IACAH,QAAQG,IACXC,YAAa,IACbC,iBAAkB9B,EAAe,IAAM,IACvC+B,iBAAkBC,OAAOC,mBAI/B5B,GAGF,IAAKJ,EAAS,CACZ,MAAMiC,QAAiBhB,EAAciB,QAErCC,EAAYF,GAAU,EACvB,CAED,IAAIG,EAA8D,KAKlE,IAAKrC,EAAc,CAmBjB,GAlBAqC,EAAgB5B,EAAaU,iBAC3BC,EAAaC,MACX,cAActB,4BAAwCU,EAAaa,uBAAuBb,EAAa6B,cAAcxB,IACrH,CACES,OAAQX,EAAWW,OACnBC,MAAO,CAACC,QAAQC,MAAO,OAAQ,QAC/BC,OAAO,EACPC,IAAK,IACAH,QAAQG,IACXC,YAAa,IACbC,iBAAkB9B,EAAe,IAAM,IACvC+B,iBAAkBC,OAAOC,mBAI/B5B,IAGGJ,EAAS,CACZ,MAAMiC,QAAiBG,EAAcF,QAErCC,EAAYF,GAAU,SAChBzB,EAAa8B,gBAEfpC,GACFM,EAAa+B,QAGXpC,GACFK,EAAagC,kBAEhB,CAED9B,EAAM+B,KAAK,SACZ,CAKD,GAAIzC,EAAS,CACXwB,QAAQkB,GAAG,QAAQ,KACjB/B,EAAWgC,OAAO,IAGpB,IAAIC,EAAa7C,EAAe,EAAI,EACpC,MAAM8C,EAAYC,IACJC,OAAOC,KAAKF,GAAMG,WAEtBC,SAAS,cACfN,GAAc,EAETA,IACH3B,EAAckC,QAAQC,QAAQC,eAAe,OAAQR,GACrDT,GAAee,QAAQC,QAAQC,eAAe,OAAQR,GACtDS,EAAgB9C,EAAa+C,OAAQ/C,EAAagD,YAClD7D,OAEH,EASH,OAHAsB,EAAckC,QAAQC,QAAQV,GAAG,OAAQG,QACzCT,GAAee,QAAQC,QAAQV,GAAG,OAAQG,EAG3C,CAEG5C,GACFO,EAAaiD,eAGfH,EAAgB9C,EAAa+C,OAAQ/C,EAAagD,YAClDE,EAAWlD,EAAamD,UACxBhE,MAEA,MAAMiE,EAAaC,KAAKC,KAAKxD,EAAYC,MAAQF,GAC3C0D,EAAWH,EAAa,KAAQA,EAAa,KAAMI,QAAQ,GAAKJ,EAChEK,EAAQL,EAAa,IAAO,IAAM,KAElCM,EAAsBC,EAAMC,IAChC,GAAGD,EAAME,aAAa3D,EAAM4D,KAAK,kBAAkBH,EAAMI,MAAMJ,EAAMK,KAAKT,OAAcE,KAG1FQ,QAAQC,KACN,OAAOP,EAAMQ,MAAM,GAAGR,EAAMK,KAAKI,EAAQC,sBAAsBX,KAC7D1D,EAAa+C,OAAS,GAAKY,EAAMW,UAAU,YAAYtE,EAAauE,eAG1E"}
@@ -5,6 +5,7 @@ declare enum StreamError {
5
5
  RenderAborted = "aborted",
6
6
  RenderTimeout = "timeout",
7
7
  RenderCancel = "cancel",
8
+ RenderSkip = "skip",
8
9
  Unknown = "unknown"
9
10
  }
10
11
  export { StreamError as default };
@@ -1,2 +1,2 @@
1
- var e;!function(e){e.RenderAborted="aborted",e.RenderTimeout="timeout",e.RenderCancel="cancel",e.Unknown="unknown"}(e||(e={}));var n=e;export{n as default};
1
+ var e;!function(e){e.RenderAborted="aborted",e.RenderTimeout="timeout",e.RenderCancel="cancel",e.RenderSkip="skip",e.Unknown="unknown"}(e||(e={}));var n=e;export{n as default};
2
2
  //# sourceMappingURL=stream-error.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream-error.js","sources":["../../src/constants/stream-error.ts"],"sourcesContent":["/**\n * React stream errors\n */\nenum StreamError {\n RenderAborted = 'aborted',\n RenderTimeout = 'timeout',\n RenderCancel = 'cancel',\n Unknown = 'unknown',\n}\n\nexport default StreamError;\n"],"names":["StreamError","StreamError$1"],"mappings":"AAGA,IAAKA,GAAL,SAAKA,GACHA,EAAA,cAAA,UACAA,EAAA,cAAA,UACAA,EAAA,aAAA,SACAA,EAAA,QAAA,SACD,CALD,CAAKA,IAAAA,EAKJ,CAAA,IAED,IAAAC,EAAeD"}
1
+ {"version":3,"file":"stream-error.js","sources":["../../src/constants/stream-error.ts"],"sourcesContent":["/**\n * React stream errors\n */\nenum StreamError {\n RenderAborted = 'aborted',\n RenderTimeout = 'timeout',\n RenderCancel = 'cancel',\n RenderSkip = 'skip',\n Unknown = 'unknown',\n}\n\nexport default StreamError;\n"],"names":["StreamError","StreamError$1"],"mappings":"AAGA,IAAKA,GAAL,SAAKA,GACHA,EAAA,cAAA,UACAA,EAAA,cAAA,UACAA,EAAA,aAAA,SACAA,EAAA,WAAA,OACAA,EAAA,QAAA,SACD,CAND,CAAKA,IAAAA,EAMJ,CAAA,IAED,IAAAC,EAAeD"}
@@ -1,2 +1,2 @@
1
- import e from"../constants/stream-error.js";const r=r=>{const o=(r?.message??"Unknown.").replace("Error: ","");return"The render was aborted by the server without a reason."===o?{code:e.RenderAborted,message:o,original:r}:{code:e.Unknown,message:o||"Unknown error",original:r}};export{r as default};
1
+ import e from"../constants/stream-error.js";const r=r=>{const o=(r?.message??"Unknown.").replace("Error: ","");return"The render was aborted by the server without a reason."===o?{code:e.RenderAborted,message:o,original:r}:"The destination stream closed early."===o?{code:e.RenderSkip,message:o,original:r}:{code:e.Unknown,message:o||"Unknown error",original:r}};export{r as default};
2
2
  //# sourceMappingURL=obtain-stream-error.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"obtain-stream-error.js","sources":["../../src/helpers/obtain-stream-error.ts"],"sourcesContent":["import StreamError from '@constants/stream-error';\n\nexport interface IObtainStreamErrorOut {\n code: StreamError;\n message: string;\n original: unknown;\n}\n\n/**\n * Get react stream error\n */\nconst obtainStreamError = (err: unknown): IObtainStreamErrorOut => {\n const message = ((err as Error)?.message ?? 'Unknown.').replace('Error: ', '');\n\n if (message === 'The render was aborted by the server without a reason.') {\n return {\n code: StreamError.RenderAborted,\n message,\n original: err,\n };\n }\n\n return {\n code: StreamError.Unknown,\n message: message || 'Unknown error',\n original: err,\n };\n};\n\nexport default obtainStreamError;\n"],"names":["obtainStreamError","err","message","replace","code","StreamError","RenderAborted","original","Unknown"],"mappings":"4CAWA,MAAMA,EAAqBC,IACzB,MAAMC,GAAYD,GAAeC,SAAW,YAAYC,QAAQ,UAAW,IAE3E,MAAgB,2DAAZD,EACK,CACLE,KAAMC,EAAYC,cAClBJ,UACAK,SAAUN,GAIP,CACLG,KAAMC,EAAYG,QAClBN,QAASA,GAAW,gBACpBK,SAAUN,EACX"}
1
+ {"version":3,"file":"obtain-stream-error.js","sources":["../../src/helpers/obtain-stream-error.ts"],"sourcesContent":["import StreamError from '@constants/stream-error';\n\nexport interface IObtainStreamErrorOut {\n code: StreamError;\n message: string;\n original: unknown;\n}\n\n/**\n * Get react stream error\n */\nconst obtainStreamError = (err: unknown): IObtainStreamErrorOut => {\n const message = ((err as Error)?.message ?? 'Unknown.').replace('Error: ', '');\n\n if (message === 'The render was aborted by the server without a reason.') {\n return {\n code: StreamError.RenderAborted,\n message,\n original: err,\n };\n } else if (message === 'The destination stream closed early.') {\n return {\n code: StreamError.RenderSkip,\n message,\n original: err,\n };\n }\n\n return {\n code: StreamError.Unknown,\n message: message || 'Unknown error',\n original: err,\n };\n};\n\nexport default obtainStreamError;\n"],"names":["obtainStreamError","err","message","replace","code","StreamError","RenderAborted","original","RenderSkip","Unknown"],"mappings":"4CAWA,MAAMA,EAAqBC,IACzB,MAAMC,GAAYD,GAAeC,SAAW,YAAYC,QAAQ,UAAW,IAE3E,MAAgB,2DAAZD,EACK,CACLE,KAAMC,EAAYC,cAClBJ,UACAK,SAAUN,GAES,yCAAZC,EACF,CACLE,KAAMC,EAAYG,WAClBN,UACAK,SAAUN,GAIP,CACLG,KAAMC,EAAYI,QAClBP,QAASA,GAAW,gBACpBK,SAAUN,EACX"}
@@ -0,0 +1,18 @@
1
+ interface ISsrMetadata {
2
+ routeFiles?: {
3
+ [originalFileName: string]: string;
4
+ };
5
+ }
6
+ /**
7
+ * Write build metadata
8
+ */
9
+ declare const writeMeta: (buildDir: string, data: ISsrMetadata) => void;
10
+ /**
11
+ * Read build metadata
12
+ */
13
+ declare const readMeta: (buildDir: string) => ISsrMetadata;
14
+ /**
15
+ * Remove metadata file
16
+ */
17
+ declare const removeMeta: (buildDir: string) => void;
18
+ export { writeMeta, readMeta, removeMeta };
@@ -0,0 +1,2 @@
1
+ import n from"node:fs";const t=n=>`${n}/meta.json`,r=(r,e)=>{const o=c(r);n.writeFileSync(t(r),JSON.stringify({...o,...e},null,2),{encoding:"utf-8"})},c=r=>{const c=t(r);try{return JSON.parse(n.readFileSync(c,{encoding:"utf-8"}))}catch(n){}return{}},e=r=>{try{n.unlinkSync(t(r))}catch(n){}};export{c as readMeta,e as removeMeta,r as writeMeta};
2
+ //# sourceMappingURL=ssr-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-meta.js","sources":["../../src/helpers/ssr-meta.ts"],"sourcesContent":["import fs from 'node:fs';\n\ninterface ISsrMetadata {\n routeFiles?: {\n // original => generated file\n [originalFileName: string]: string;\n };\n}\n\n/**\n * Return meta file path\n */\nconst getMetaFilepath = (buildDir: string): string => `${buildDir}/meta.json`;\n\n/**\n * Write build metadata\n */\nconst writeMeta = (buildDir: string, data: ISsrMetadata): void => {\n const meta = readMeta(buildDir);\n\n fs.writeFileSync(getMetaFilepath(buildDir), JSON.stringify({ ...meta, ...data }, null, 2), {\n encoding: 'utf-8',\n });\n};\n\n/**\n * Read build metadata\n */\nconst readMeta = (buildDir: string): ISsrMetadata => {\n const metaFile = getMetaFilepath(buildDir);\n\n try {\n return JSON.parse(fs.readFileSync(metaFile, { encoding: 'utf-8' })) as ISsrMetadata;\n } catch (e) {\n // ignore, file not exist\n }\n\n return {};\n};\n\n/**\n * Remove metadata file\n */\nconst removeMeta = (buildDir: string): void => {\n try {\n fs.unlinkSync(getMetaFilepath(buildDir));\n } catch (e) {\n // ignore\n }\n};\n\nexport { writeMeta, readMeta, removeMeta };\n"],"names":["getMetaFilepath","buildDir","writeMeta","data","meta","readMeta","fs","writeFileSync","JSON","stringify","encoding","metaFile","parse","readFileSync","e","removeMeta","unlinkSync"],"mappings":"uBAYA,MAAMA,EAAmBC,GAA6B,GAAGA,cAKnDC,EAAY,CAACD,EAAkBE,KACnC,MAAMC,EAAOC,EAASJ,GAEtBK,EAAGC,cAAcP,EAAgBC,GAAWO,KAAKC,UAAU,IAAKL,KAASD,GAAQ,KAAM,GAAI,CACzFO,SAAU,SACV,EAMEL,EAAYJ,IAChB,MAAMU,EAAWX,EAAgBC,GAEjC,IACE,OAAOO,KAAKI,MAAMN,EAAGO,aAAaF,EAAU,CAAED,SAAU,UACzD,CAAC,MAAOI,GAER,CAED,MAAO,EAAE,EAMLC,EAAcd,IAClB,IACEK,EAAGU,WAAWhB,EAAgBC,GAC/B,CAAC,MAAOa,GAER"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lomray/vite-ssr-boost",
3
- "version": "2.3.7",
3
+ "version": "2.4.0-beta.2",
4
4
  "description": "Vite plugin for create awesome SSR or SPA applications on React.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -34,46 +34,49 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "chalk": "^5.3.0",
37
- "commander": "^12.0.0",
37
+ "commander": "^12.1.0",
38
38
  "compression": "^1.7.4",
39
39
  "express": "^4.19.2",
40
40
  "hoist-non-react-statics": "^3.3.2",
41
41
  "json5": "^2.2.3"
42
42
  },
43
43
  "devDependencies": {
44
- "@commitlint/cli": "^19.2.2",
44
+ "@commitlint/cli": "^19.3.0",
45
45
  "@commitlint/config-conventional": "^19.2.2",
46
46
  "@lomray/eslint-config-react": "^5.0.6",
47
47
  "@lomray/prettier-config": "^2.0.1",
48
48
  "@rollup/plugin-terser": "^0.4.4",
49
- "@testing-library/react": "^15.0.2",
50
- "@types/chai": "^4.3.14",
49
+ "@testing-library/react": "^15.0.7",
50
+ "@types/babel__traverse": "^7.20.6",
51
+ "@types/chai": "^4.3.16",
51
52
  "@types/compression": "^1.7.5",
52
53
  "@types/hoist-non-react-statics": "^3.3.5",
53
- "@types/react-dom": "^18.2.19",
54
+ "@types/react-dom": "^18.3.0",
54
55
  "@types/sinon": "^17.0.3",
55
56
  "@types/sinon-chai": "^3.2.12",
56
- "@vitest/coverage-v8": "^1.5.0",
57
+ "@vitest/coverage-v8": "^1.6.0",
57
58
  "@zerollup/ts-transform-paths": "^1.7.18",
58
59
  "chai": "^4.4.1",
59
60
  "eslint": "^8.57.0",
60
61
  "husky": "^9.0.11",
61
62
  "jsdom": "^24.0.0",
62
- "lint-staged": "^15.2.2",
63
+ "lint-staged": "^15.2.4",
63
64
  "prettier": "^3.2.5",
64
- "rollup": "^4.14.3",
65
+ "rollup": "^4.18.0",
65
66
  "rollup-plugin-copy": "^3.5.0",
66
67
  "rollup-plugin-folder-input": "^1.0.1",
67
68
  "rollup-plugin-peer-deps-external": "^2.2.4",
68
69
  "rollup-plugin-preserve-shebangs": "^0.2.0",
69
70
  "rollup-plugin-ts": "^3.4.5",
70
- "semantic-release": "^23.0.8",
71
- "sinon": "^17.0.1",
71
+ "semantic-release": "^23.1.1",
72
+ "sinon": "^17.0.2",
72
73
  "sinon-chai": "^3.7.0",
73
74
  "typescript": "^5.3.3",
74
- "vitest": "^1.5.0"
75
+ "vitest": "^1.6.0"
75
76
  },
76
77
  "peerDependencies": {
78
+ "@babel/parser": ">=7.23.0",
79
+ "@babel/traverse": ">=7.23.0",
77
80
  "@types/express": ">=4.17.21",
78
81
  "react-dom": ">=18.2.0",
79
82
  "react-router-dom": ">=6.12.1",
package/plugin.d.ts CHANGED
@@ -6,6 +6,7 @@ interface IPluginOptions {
6
6
  serverFile?: string;
7
7
  clientFile?: string;
8
8
  routesPath?: string;
9
+ routesParsing?: 'node' | 'babel';
9
10
  tsconfigAliases?: boolean | IMakeAliasesPluginOptions;
10
11
  customShortcuts?: {
11
12
  key: string;
package/plugin.js CHANGED
@@ -1,2 +1,2 @@
1
- import i from"node:path";import e from"./constants/cli-actions.js";import s from"./constants/plugin-name.js";import t from"./plugins/make-aliases.js";import o from"./plugins/normalize-route.js";const n={indexFile:"index.html",serverFile:"server.ts",clientFile:"client.ts",tsconfigAliases:!0};function r(r={}){const l=new URL(import.meta.url),a=global.viteBoostAction||process.env.SSR_BOOST_ACTION,p={...n,...r},u="1"===process.env.SSR_BOOST_IS_SSR||a===e.dev,m=a===e.build,c=[{name:s,enforce:"pre",pluginOptions:{...p,pluginPath:i.dirname(l.pathname),action:a,isDev:a===e.dev},config:(i,{isSsrBuild:e})=>(i.define={...i.define??{},__IS_SSR__:u},i.build={...i.build??{}},e?{...i,...m?{appType:"custom"}:{},publicDir:!1}:(u&&m&&(i.build.manifest=!0),i))}],{tsconfigAliases:d,routesPath:f}=p;return d&&c.push(t("boolean"==typeof d?void 0:d)),c.push(o({isSSR:u,isBuild:m,routesPath:f})),c}export{r as default};
1
+ import e from"node:path";import i from"./constants/cli-actions.js";import s from"./constants/plugin-name.js";import o from"./plugins/make-aliases.js";import n from"./plugins/normalize-route.js";const t={indexFile:"index.html",serverFile:"server.ts",clientFile:"client.ts",routesParsing:"babel",tsconfigAliases:!0};function r(r={}){const a=new URL(import.meta.url),l=global.viteBoostAction||process.env.SSR_BOOST_ACTION,p={...t,...r},u="1"===process.env.SSR_BOOST_IS_SSR||l===i.dev,m=l===i.build,c=[{name:s,enforce:"pre",pluginOptions:{...p,pluginPath:e.dirname(a.pathname),action:l,isDev:l===i.dev},config:(e,{isSsrBuild:i})=>(e.define={...e.define??{},__IS_SSR__:u},e.build={...e.build??{}},i?{...e,...m?{appType:"custom"}:{},publicDir:!1}:(u&&m&&(e.build.manifest=!0),e))}],{tsconfigAliases:d,routesPath:f,routesParsing:S}=p;return d&&c.push(o("boolean"==typeof d?void 0:d)),c.push(n({isSSR:u,isBuild:m,routesPath:f,isNodeParsing:"node"===S})),c}export{r as default};
2
2
  //# sourceMappingURL=plugin.js.map
package/plugin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["../src/plugin.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Plugin } from 'vite';\nimport CliActions from '@constants/cli-actions';\nimport type { ICliContext } from '@constants/cli-context';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport type { IPluginOptions as IMakeAliasesPluginOptions } from '@plugins/make-aliases';\nimport ViteMakeAliasesPlugin from '@plugins/make-aliases';\nimport ViteNormalizeRouterPlugin from '@plugins/normalize-route';\n\nexport interface IPluginOptions {\n // default: index.html\n indexFile?: string;\n // default: server.ts\n serverFile?: string;\n // default: client.ts\n clientFile?: string;\n // Path contains routes declaration files (need to detect route files). default: undefined, e.g.: /routes/\n routesPath?: string;\n // Read aliases from tsconfig\n tsconfigAliases?: boolean | IMakeAliasesPluginOptions;\n customShortcuts?: {\n key: string;\n description: string;\n action: (cliContext: ICliContext) => Promise<void> | void;\n isOnlyDev?: boolean;\n }[];\n}\n\nconst defaultOptions: IPluginOptions = {\n indexFile: 'index.html',\n serverFile: 'server.ts',\n clientFile: 'client.ts',\n tsconfigAliases: true,\n};\n\n/**\n * Init plugin\n * @constructor\n */\nfunction ViteSsrBoostPlugin(options: IPluginOptions = {}): Plugin[] {\n const dirInfo = new URL(import.meta.url);\n const action = (global.viteBoostAction || process.env.SSR_BOOST_ACTION) as CliActions;\n const mergedOptions: IPluginOptions = { ...defaultOptions, ...options };\n const isSSR = process.env.SSR_BOOST_IS_SSR === '1' || action === CliActions.dev;\n const isBuild = action === CliActions.build;\n\n const plugins: Plugin[] = [\n {\n name: PLUGIN_NAME,\n enforce: 'pre',\n // @ts-ignore save custom options\n pluginOptions: {\n ...mergedOptions,\n pluginPath: path.dirname(dirInfo.pathname),\n action,\n isDev: action === CliActions.dev,\n },\n\n config(config, { isSsrBuild }) {\n config.define = {\n ...(config.define ?? {}),\n __IS_SSR__: isSSR,\n };\n\n config.build = {\n ...(config.build ?? {}),\n };\n\n if (!isSsrBuild) {\n if (isSSR && isBuild) {\n config.build.manifest = true;\n }\n\n return config;\n }\n\n return {\n ...config,\n ...(isBuild ? { appType: 'custom' } : {}),\n publicDir: false,\n };\n },\n },\n ];\n\n const { tsconfigAliases, routesPath } = mergedOptions;\n\n if (tsconfigAliases) {\n plugins.push(\n ViteMakeAliasesPlugin(typeof tsconfigAliases === 'boolean' ? undefined : tsconfigAliases),\n );\n }\n\n plugins.push(ViteNormalizeRouterPlugin({ isSSR, isBuild, routesPath }));\n\n return plugins;\n}\n\nexport default ViteSsrBoostPlugin;\n"],"names":["defaultOptions","indexFile","serverFile","clientFile","tsconfigAliases","ViteSsrBoostPlugin","options","dirInfo","URL","url","action","global","viteBoostAction","process","env","SSR_BOOST_ACTION","mergedOptions","isSSR","SSR_BOOST_IS_SSR","CliActions","dev","isBuild","build","plugins","name","PLUGIN_NAME","enforce","pluginOptions","pluginPath","path","dirname","pathname","isDev","config","isSsrBuild","define","__IS_SSR__","appType","publicDir","manifest","routesPath","push","ViteMakeAliasesPlugin","undefined","ViteNormalizeRouterPlugin"],"mappings":"kMA4BA,MAAMA,EAAiC,CACrCC,UAAW,aACXC,WAAY,YACZC,WAAY,YACZC,iBAAiB,GAOnB,SAASC,EAAmBC,EAA0B,IACpD,MAAMC,EAAU,IAAIC,gBAAgBC,KAC9BC,EAAUC,OAAOC,iBAAmBC,QAAQC,IAAIC,iBAChDC,EAAgC,IAAKhB,KAAmBM,GACxDW,EAAyC,MAAjCJ,QAAQC,IAAII,kBAA4BR,IAAWS,EAAWC,IACtEC,EAAUX,IAAWS,EAAWG,MAEhCC,EAAoB,CACxB,CACEC,KAAMC,EACNC,QAAS,MAETC,cAAe,IACVX,EACHY,WAAYC,EAAKC,QAAQvB,EAAQwB,UACjCrB,SACAsB,MAAOtB,IAAWS,EAAWC,KAG/Ba,OAAM,CAACA,GAAQC,WAAEA,MACfD,EAAOE,OAAS,IACVF,EAAOE,QAAU,CAAE,EACvBC,WAAYnB,GAGdgB,EAAOX,MAAQ,IACTW,EAAOX,OAAS,CAAE,GAGnBY,EAQE,IACFD,KACCZ,EAAU,CAAEgB,QAAS,UAAa,CAAE,EACxCC,WAAW,IAVPrB,GAASI,IACXY,EAAOX,MAAMiB,UAAW,GAGnBN,OAYT7B,gBAAEA,EAAeoC,WAAEA,GAAexB,EAUxC,OARIZ,GACFmB,EAAQkB,KACNC,EAAiD,kBAApBtC,OAAgCuC,EAAYvC,IAI7EmB,EAAQkB,KAAKG,EAA0B,CAAE3B,QAAOI,UAASmB,gBAElDjB,CACT"}
1
+ {"version":3,"file":"plugin.js","sources":["../src/plugin.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Plugin } from 'vite';\nimport CliActions from '@constants/cli-actions';\nimport type { ICliContext } from '@constants/cli-context';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport type { IPluginOptions as IMakeAliasesPluginOptions } from '@plugins/make-aliases';\nimport ViteMakeAliasesPlugin from '@plugins/make-aliases';\nimport ViteNormalizeRouterPlugin from '@plugins/normalize-route';\n\nexport interface IPluginOptions {\n // default: index.html\n indexFile?: string;\n // default: server.ts\n serverFile?: string;\n // default: client.ts\n clientFile?: string;\n // Path contains routes declaration files (need to detect route files). default: undefined, e.g.: /routes/\n routesPath?: string;\n // how parse routes\n // node - import routes file directly and walk through\n // babel - use babel travers to walk through and avoid import routes file\n // default: babel\n routesParsing?: 'node' | 'babel';\n // Read aliases from tsconfig\n tsconfigAliases?: boolean | IMakeAliasesPluginOptions;\n customShortcuts?: {\n key: string;\n description: string;\n action: (cliContext: ICliContext) => Promise<void> | void;\n isOnlyDev?: boolean;\n }[];\n}\n\nconst defaultOptions: IPluginOptions = {\n indexFile: 'index.html',\n serverFile: 'server.ts',\n clientFile: 'client.ts',\n routesParsing: 'babel',\n tsconfigAliases: true,\n};\n\n/**\n * Init plugin\n * @constructor\n */\nfunction ViteSsrBoostPlugin(options: IPluginOptions = {}): Plugin[] {\n const dirInfo = new URL(import.meta.url);\n const action = (global.viteBoostAction || process.env.SSR_BOOST_ACTION) as CliActions;\n const mergedOptions: IPluginOptions = { ...defaultOptions, ...options };\n const isSSR = process.env.SSR_BOOST_IS_SSR === '1' || action === CliActions.dev;\n const isBuild = action === CliActions.build;\n\n const plugins: Plugin[] = [\n {\n name: PLUGIN_NAME,\n enforce: 'pre',\n // @ts-ignore save custom options\n pluginOptions: {\n ...mergedOptions,\n pluginPath: path.dirname(dirInfo.pathname),\n action,\n isDev: action === CliActions.dev,\n },\n\n config(config, { isSsrBuild }) {\n config.define = {\n ...(config.define ?? {}),\n __IS_SSR__: isSSR,\n };\n\n config.build = {\n ...(config.build ?? {}),\n };\n\n if (!isSsrBuild) {\n if (isSSR && isBuild) {\n config.build.manifest = true;\n }\n\n return config;\n }\n\n return {\n ...config,\n ...(isBuild ? { appType: 'custom' } : {}),\n publicDir: false,\n };\n },\n },\n ];\n\n const { tsconfigAliases, routesPath, routesParsing } = mergedOptions;\n\n if (tsconfigAliases) {\n plugins.push(\n ViteMakeAliasesPlugin(typeof tsconfigAliases === 'boolean' ? undefined : tsconfigAliases),\n );\n }\n\n plugins.push(\n ViteNormalizeRouterPlugin({\n isSSR,\n isBuild,\n routesPath,\n isNodeParsing: routesParsing === 'node',\n }),\n );\n\n return plugins;\n}\n\nexport default ViteSsrBoostPlugin;\n"],"names":["defaultOptions","indexFile","serverFile","clientFile","routesParsing","tsconfigAliases","ViteSsrBoostPlugin","options","dirInfo","URL","url","action","global","viteBoostAction","process","env","SSR_BOOST_ACTION","mergedOptions","isSSR","SSR_BOOST_IS_SSR","CliActions","dev","isBuild","build","plugins","name","PLUGIN_NAME","enforce","pluginOptions","pluginPath","path","dirname","pathname","isDev","config","isSsrBuild","define","__IS_SSR__","appType","publicDir","manifest","routesPath","push","ViteMakeAliasesPlugin","undefined","ViteNormalizeRouterPlugin","isNodeParsing"],"mappings":"kMAiCA,MAAMA,EAAiC,CACrCC,UAAW,aACXC,WAAY,YACZC,WAAY,YACZC,cAAe,QACfC,iBAAiB,GAOnB,SAASC,EAAmBC,EAA0B,IACpD,MAAMC,EAAU,IAAIC,gBAAgBC,KAC9BC,EAAUC,OAAOC,iBAAmBC,QAAQC,IAAIC,iBAChDC,EAAgC,IAAKjB,KAAmBO,GACxDW,EAAyC,MAAjCJ,QAAQC,IAAII,kBAA4BR,IAAWS,EAAWC,IACtEC,EAAUX,IAAWS,EAAWG,MAEhCC,EAAoB,CACxB,CACEC,KAAMC,EACNC,QAAS,MAETC,cAAe,IACVX,EACHY,WAAYC,EAAKC,QAAQvB,EAAQwB,UACjCrB,SACAsB,MAAOtB,IAAWS,EAAWC,KAG/Ba,OAAM,CAACA,GAAQC,WAAEA,MACfD,EAAOE,OAAS,IACVF,EAAOE,QAAU,CAAE,EACvBC,WAAYnB,GAGdgB,EAAOX,MAAQ,IACTW,EAAOX,OAAS,CAAE,GAGnBY,EAQE,IACFD,KACCZ,EAAU,CAAEgB,QAAS,UAAa,CAAE,EACxCC,WAAW,IAVPrB,GAASI,IACXY,EAAOX,MAAMiB,UAAW,GAGnBN,OAYT7B,gBAAEA,EAAeoC,WAAEA,EAAUrC,cAAEA,GAAkBa,EAiBvD,OAfIZ,GACFmB,EAAQkB,KACNC,EAAiD,kBAApBtC,OAAgCuC,EAAYvC,IAI7EmB,EAAQkB,KACNG,EAA0B,CACxB3B,QACAI,UACAmB,aACAK,cAAiC,SAAlB1C,KAIZoB,CACT"}
@@ -5,7 +5,7 @@ interface IPluginOptions {
5
5
  }
6
6
  /**
7
7
  * Read tsconfig file and set vite aliases
8
- * @see SsrManifest.getAliases
8
+ * @see PathNormalize.getAliases
9
9
  * @constructor
10
10
  */
11
11
  declare function ViteMakeAliasesPlugin(options?: IPluginOptions): Plugin;
@@ -1 +1 @@
1
- {"version":3,"file":"make-aliases.js","sources":["../../src/plugins/make-aliases.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport JSON5 from 'json5';\nimport type { Plugin } from 'vite';\n// import without aliases for use in vitest.config.ts\nimport PLUGIN_NAME from '../constants/plugin-name';\nimport ViteAliases from '../helpers/vite-aliases';\n\nexport interface IPluginOptions {\n root?: string; // default: cwd()\n tsconfig?: string; // default: tsconfig.json\n}\n\nconst pluginName = `${PLUGIN_NAME}-make-aliases`;\nconst cleanupAlias = (str: string): string => str.replace('/*', '');\n\n/**\n * Read tsconfig file and set vite aliases\n * @see SsrManifest.getAliases\n * @constructor\n */\nfunction ViteMakeAliasesPlugin(options: IPluginOptions = {}): Plugin {\n const { root, tsconfig } = options;\n const projectRoot = root ?? process.cwd();\n const tsconfigPath = path.resolve(projectRoot, tsconfig ?? 'tsconfig.json');\n const aliases: [string, string][] = [];\n\n if (!fs.existsSync(tsconfigPath)) {\n console.error(`${pluginName}: tsconfig not exist in \"${tsconfigPath}\"`);\n } else {\n const tsJson = JSON5.parse<{ compilerOptions?: { paths: Record<string, string[]> } }>(\n fs.readFileSync(tsconfigPath, { encoding: 'utf-8' }),\n );\n const paths = tsJson?.compilerOptions?.paths ?? {};\n\n Object.entries(paths).forEach(([alias, aliasPaths]) => {\n aliases.push([cleanupAlias(alias), cleanupAlias(aliasPaths[0])]);\n });\n }\n\n return {\n name: pluginName,\n config(config) {\n if (aliases.length) {\n const resolveConfig = config.resolve ?? {};\n const defaultAliases = resolveConfig.alias ?? [];\n const normalizedAliases = Array.isArray(defaultAliases)\n ? defaultAliases\n : Object.entries(defaultAliases).map(([find, val]) => ({\n find,\n replacement: val as string,\n }));\n\n normalizedAliases.push(...ViteAliases(aliases, `${projectRoot}/${config?.root ?? ''}`));\n\n config.resolve = {\n ...resolveConfig,\n alias: normalizedAliases,\n };\n }\n\n return config;\n },\n };\n}\n\nexport default ViteMakeAliasesPlugin;\n"],"names":["pluginName","PLUGIN_NAME","cleanupAlias","str","replace","ViteMakeAliasesPlugin","options","root","tsconfig","projectRoot","process","cwd","tsconfigPath","path","resolve","aliases","fs","existsSync","tsJson","JSON5","parse","readFileSync","encoding","paths","compilerOptions","Object","entries","forEach","alias","aliasPaths","push","console","error","name","config","length","resolveConfig","defaultAliases","normalizedAliases","Array","isArray","map","find","val","replacement","ViteAliases"],"mappings":"sLAcA,MAAMA,EAAa,GAAGC,iBAChBC,EAAgBC,GAAwBA,EAAIC,QAAQ,KAAM,IAOhE,SAASC,EAAsBC,EAA0B,IACvD,MAAMC,KAAEA,EAAIC,SAAEA,GAAaF,EACrBG,EAAcF,GAAQG,EAAQC,MAC9BC,EAAeC,EAAKC,QAAQL,EAAaD,GAAY,iBACrDO,EAA8B,GAEpC,GAAKC,EAAGC,WAAWL,GAEZ,CACL,MAAMM,EAASC,EAAMC,MACnBJ,EAAGK,aAAaT,EAAc,CAAEU,SAAU,WAEtCC,EAAQL,GAAQM,iBAAiBD,OAAS,CAAA,EAEhDE,OAAOC,QAAQH,GAAOI,SAAQ,EAAEC,EAAOC,MACrCd,EAAQe,KAAK,CAAC5B,EAAa0B,GAAQ1B,EAAa2B,EAAW,KAAK,GAEnE,MAVCE,QAAQC,MAAM,GAAGhC,6BAAsCY,MAYzD,MAAO,CACLqB,KAAMjC,EACNkC,OAAOA,GACL,GAAInB,EAAQoB,OAAQ,CAClB,MAAMC,EAAgBF,EAAOpB,SAAW,GAClCuB,EAAiBD,EAAcR,OAAS,GACxCU,EAAoBC,MAAMC,QAAQH,GACpCA,EACAZ,OAAOC,QAAQW,GAAgBI,KAAI,EAAEC,EAAMC,MAAU,CACnDD,OACAE,YAAaD,MAGnBL,EAAkBR,QAAQe,EAAY9B,EAAS,GAAGN,KAAeyB,GAAQ3B,MAAQ,OAEjF2B,EAAOpB,QAAU,IACZsB,EACHR,MAAOU,EAEV,CAED,OAAOJ,CACR,EAEL"}
1
+ {"version":3,"file":"make-aliases.js","sources":["../../src/plugins/make-aliases.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport JSON5 from 'json5';\nimport type { Plugin } from 'vite';\n// import without aliases for use in vitest.config.ts\nimport PLUGIN_NAME from '../constants/plugin-name';\nimport ViteAliases from '../helpers/vite-aliases';\n\nexport interface IPluginOptions {\n root?: string; // default: cwd()\n tsconfig?: string; // default: tsconfig.json\n}\n\nconst pluginName = `${PLUGIN_NAME}-make-aliases`;\nconst cleanupAlias = (str: string): string => str.replace('/*', '');\n\n/**\n * Read tsconfig file and set vite aliases\n * @see PathNormalize.getAliases\n * @constructor\n */\nfunction ViteMakeAliasesPlugin(options: IPluginOptions = {}): Plugin {\n const { root, tsconfig } = options;\n const projectRoot = root ?? process.cwd();\n const tsconfigPath = path.resolve(projectRoot, tsconfig ?? 'tsconfig.json');\n const aliases: [string, string][] = [];\n\n if (!fs.existsSync(tsconfigPath)) {\n console.error(`${pluginName}: tsconfig not exist in \"${tsconfigPath}\"`);\n } else {\n const tsJson = JSON5.parse<{ compilerOptions?: { paths: Record<string, string[]> } }>(\n fs.readFileSync(tsconfigPath, { encoding: 'utf-8' }),\n );\n const paths = tsJson?.compilerOptions?.paths ?? {};\n\n Object.entries(paths).forEach(([alias, aliasPaths]) => {\n aliases.push([cleanupAlias(alias), cleanupAlias(aliasPaths[0])]);\n });\n }\n\n return {\n name: pluginName,\n config(config) {\n if (aliases.length) {\n const resolveConfig = config.resolve ?? {};\n const defaultAliases = resolveConfig.alias ?? [];\n const normalizedAliases = Array.isArray(defaultAliases)\n ? defaultAliases\n : Object.entries(defaultAliases).map(([find, val]) => ({\n find,\n replacement: val as string,\n }));\n\n normalizedAliases.push(...ViteAliases(aliases, `${projectRoot}/${config?.root ?? ''}`));\n\n config.resolve = {\n ...resolveConfig,\n alias: normalizedAliases,\n };\n }\n\n return config;\n },\n };\n}\n\nexport default ViteMakeAliasesPlugin;\n"],"names":["pluginName","PLUGIN_NAME","cleanupAlias","str","replace","ViteMakeAliasesPlugin","options","root","tsconfig","projectRoot","process","cwd","tsconfigPath","path","resolve","aliases","fs","existsSync","tsJson","JSON5","parse","readFileSync","encoding","paths","compilerOptions","Object","entries","forEach","alias","aliasPaths","push","console","error","name","config","length","resolveConfig","defaultAliases","normalizedAliases","Array","isArray","map","find","val","replacement","ViteAliases"],"mappings":"sLAcA,MAAMA,EAAa,GAAGC,iBAChBC,EAAgBC,GAAwBA,EAAIC,QAAQ,KAAM,IAOhE,SAASC,EAAsBC,EAA0B,IACvD,MAAMC,KAAEA,EAAIC,SAAEA,GAAaF,EACrBG,EAAcF,GAAQG,EAAQC,MAC9BC,EAAeC,EAAKC,QAAQL,EAAaD,GAAY,iBACrDO,EAA8B,GAEpC,GAAKC,EAAGC,WAAWL,GAEZ,CACL,MAAMM,EAASC,EAAMC,MACnBJ,EAAGK,aAAaT,EAAc,CAAEU,SAAU,WAEtCC,EAAQL,GAAQM,iBAAiBD,OAAS,CAAA,EAEhDE,OAAOC,QAAQH,GAAOI,SAAQ,EAAEC,EAAOC,MACrCd,EAAQe,KAAK,CAAC5B,EAAa0B,GAAQ1B,EAAa2B,EAAW,KAAK,GAEnE,MAVCE,QAAQC,MAAM,GAAGhC,6BAAsCY,MAYzD,MAAO,CACLqB,KAAMjC,EACNkC,OAAOA,GACL,GAAInB,EAAQoB,OAAQ,CAClB,MAAMC,EAAgBF,EAAOpB,SAAW,GAClCuB,EAAiBD,EAAcR,OAAS,GACxCU,EAAoBC,MAAMC,QAAQH,GACpCA,EACAZ,OAAOC,QAAQW,GAAgBI,KAAI,EAAEC,EAAMC,MAAU,CACnDD,OACAE,YAAaD,MAGnBL,EAAkBR,QAAQe,EAAY9B,EAAS,GAAGN,KAAeyB,GAAQ3B,MAAQ,OAEjF2B,EAAOpB,QAAU,IACZsB,EACHR,MAAOU,EAEV,CAED,OAAOJ,CACR,EAEL"}
@@ -3,6 +3,7 @@ interface IPluginOptions {
3
3
  isSSR?: boolean;
4
4
  isBuild?: boolean;
5
5
  routesPath?: string;
6
+ isNodeParsing?: boolean;
6
7
  }
7
8
  /**
8
9
  * Add possibility to export route components like FCRoute or FCCRoute
@@ -10,7 +11,7 @@ interface IPluginOptions {
10
11
  * USAGE: { path: '/', lazy: () => import('./pages/home') }
11
12
  * @see FCRoute
12
13
  * @see FCCRoute
13
- * @see SsrManifest.getRoutesIds
14
+ * @see SsrManifest.getAsyncRoutesIds
14
15
  * @see importRoute
15
16
  * @constructor
16
17
  */
@@ -1,2 +1,2 @@
1
- import{extname as e}from"node:path";import r from"../constants/plugin-name.js";import t from"../helpers/is-route-file.js";const s=(e,r=!1)=>{if(r)return e;const t=[...e.matchAll(/import\s+(\w+)\sfrom\s+['"]([^'"]+)['"]/g)].reduce(((e,[,r,t])=>({[r]:t,...e})),{});return e.replace(/(.*?(?:Component|element):[\s<]*(\w+)[^,}]*),*/gs,((e,r,s)=>{const n=t?.[s];return n?`${r},pathId: '${n}',`:e}))},n=(e,t)=>{const s=e.replace(/(lazy)(:\s*)(\(\)\s*=>\s*import\(([^)]+)\))/gs,t?"lazy$2n($3,$4)":"lazy$2n($3)");return e!==s?`import n from '${r}/helpers/import-route';${s}`:e};function o(o={}){const{isSSR:m=!1,isBuild:i=!1,routesPath:p}=o;return{name:`${r}-normalize-route`,enforce:"pre",transform(r,o){const[l]=e(o).split("?"),u=!p||o.includes(p);if(!o.includes("node_modules")&&[".js",".mjs",".ts",".tsx"].includes(l)&&u&&t(r))return{code:n(s(r,i),m),map:{mappings:""}}}}}export{o as default};
1
+ import{extname as e,resolve as r}from"node:path";import t from"../constants/plugin-name.js";import o from"../helpers/is-route-file.js";import{writeMeta as s}from"../helpers/ssr-meta.js";const n=(e,r=!1)=>{if(r)return e;const t=[...e.matchAll(/import\s+(\w+)\sfrom\s+['"]([^'"]+)['"]/g)].reduce(((e,[,r,t])=>({[r]:t,...e})),{});return e.replace(/(.*?(?:Component|element):[\s<]*(\w+)[^,}]*),*/gs,((e,r,o)=>{const s=t?.[o];return s?`${r},pathId: '${s}',`:e}))},i=(e,r)=>{const o=e.replace(/(lazy)(:\s*)(\(\)\s*=>\s*import\(([^)]+)\))/gs,r?"lazy$2n($3,$4)":"lazy$2n($3)");return e!==o?`import n from '${t}/helpers/import-route';${o}`:e};function l(l={}){const{routesPath:u,isNodeParsing:m=!1,isSSR:c=!1,isBuild:p=!1}=l,a=new Map,d={root:"",buildDir:""};return{name:`${t}-normalize-route`,enforce:"pre",transform(r,t){const[s]=e(t).split("?"),l=!u||t.includes(u);if(!t.includes("node_modules")&&[".js",".mjs",".ts",".tsx"].includes(s)&&l&&o(r))return a.set(t,""),{code:i(n(r,p),c&&(m||p)),map:{mappings:""}}},...m?{config(e,{isSsrBuild:r}){r||(d.root=e.root,d.buildDir=e.build.outDir)},generateBundle(e,r){for(const[e,t]of Object.entries(r))"chunk"===t.type&&Object.entries(t.modules).forEach((([r])=>{a.has(r)&&a.set(r,e)}))},writeBundle(){if(!d.root)return;const[e]=r(d.root,d.buildDir).split("/client");s(e,{routeFiles:Object.fromEntries(a)})}}:{}}}export{l as default};
2
2
  //# sourceMappingURL=normalize-route.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalize-route.js","sources":["../../src/plugins/normalize-route.ts"],"sourcesContent":["import { extname } from 'node:path';\nimport type { Plugin } from 'vite';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport isRoutesFile from '@helpers/is-route-file';\n\nexport interface IPluginOptions {\n isSSR?: boolean;\n isBuild?: boolean;\n routesPath?: string;\n}\n\n/**\n * Add pathId to route (where pathId - import string)\n * NOTE: only for dev mode\n */\nconst normalizeSyncRoutes = (code: string, isBuild = false): string => {\n if (isBuild) {\n return code;\n }\n\n const imports: Record<string, string> = [\n ...code.matchAll(/import\\s+(\\w+)\\sfrom\\s+['\"]([^'\"]+)['\"]/g),\n ].reduce(\n (res, [, key, value]) => ({\n [key]: value,\n ...res,\n }),\n {},\n );\n\n return code.replace(\n /(.*?(?:Component|element):[\\s<]*(\\w+)[^,}]*),*/gs,\n (fullMatch, before: string, routeName: string) => {\n const importPath = imports?.[routeName];\n\n if (!importPath) {\n return fullMatch;\n }\n\n return `${before},pathId: '${importPath}',`;\n },\n );\n};\n\n/**\n * Add normalize wrapper to lazy imports for client build\n */\nconst normalizeAsyncRoutes = (code: string, isSSR: boolean): string => {\n const modifiedCode = code.replace(\n /(lazy)(:\\s*)(\\(\\)\\s*=>\\s*import\\(([^)]+)\\))/gs,\n isSSR ? 'lazy$2n($3,$4)' : 'lazy$2n($3)',\n );\n\n if (code !== modifiedCode) {\n return `import n from '${PLUGIN_NAME}/helpers/import-route';${modifiedCode}`;\n }\n\n return code;\n};\n\n/**\n * Add possibility to export route components like FCRoute or FCCRoute\n * Add route path for generating manifest\n * USAGE: { path: '/', lazy: () => import('./pages/home') }\n * @see FCRoute\n * @see FCCRoute\n * @see SsrManifest.getRoutesIds\n * @see importRoute\n * @constructor\n */\nfunction ViteNormalizeRouterPlugin(options: IPluginOptions = {}): Plugin {\n const { isSSR = false, isBuild = false, routesPath } = options;\n\n return {\n name: `${PLUGIN_NAME}-normalize-route`,\n enforce: 'pre',\n transform(code, id) {\n const [extName] = extname(id).split('?');\n const isRoutesPath = !routesPath || id.includes(routesPath);\n\n if (\n id.includes('node_modules') ||\n !['.js', '.mjs', '.ts', '.tsx'].includes(extName) ||\n !isRoutesPath ||\n !isRoutesFile(code)\n ) {\n return;\n }\n\n return {\n code: normalizeAsyncRoutes(normalizeSyncRoutes(code, isBuild), isSSR),\n map: { mappings: '' },\n };\n },\n };\n}\n\nexport default ViteNormalizeRouterPlugin;\n"],"names":["normalizeSyncRoutes","code","isBuild","imports","matchAll","reduce","res","key","value","replace","fullMatch","before","routeName","importPath","normalizeAsyncRoutes","isSSR","modifiedCode","PLUGIN_NAME","ViteNormalizeRouterPlugin","options","routesPath","name","enforce","transform","id","extName","extname","split","isRoutesPath","includes","isRoutesFile","map","mappings"],"mappings":"0HAeA,MAAMA,EAAsB,CAACC,EAAcC,GAAU,KACnD,GAAIA,EACF,OAAOD,EAGT,MAAME,EAAkC,IACnCF,EAAKG,SAAS,6CACjBC,QACA,CAACC,GAAK,CAAGC,EAAKC,MAAY,CACxBD,CAACA,GAAMC,KACJF,KAEL,CAAE,GAGJ,OAAOL,EAAKQ,QACV,oDACA,CAACC,EAAWC,EAAgBC,KAC1B,MAAMC,EAAaV,IAAUS,GAE7B,OAAKC,EAIE,GAAGF,cAAmBE,MAHpBH,CAGkC,GAE9C,EAMGI,EAAuB,CAACb,EAAcc,KAC1C,MAAMC,EAAef,EAAKQ,QACxB,gDACAM,EAAQ,iBAAmB,eAG7B,OAAId,IAASe,EACJ,kBAAkBC,2BAAqCD,IAGzDf,CAAI,EAab,SAASiB,EAA0BC,EAA0B,IAC3D,MAAMJ,MAAEA,GAAQ,EAAKb,QAAEA,GAAU,EAAKkB,WAAEA,GAAeD,EAEvD,MAAO,CACLE,KAAM,GAAGJ,oBACTK,QAAS,MACTC,UAAUtB,EAAMuB,GACd,MAAOC,GAAWC,EAAQF,GAAIG,MAAM,KAC9BC,GAAgBR,GAAcI,EAAGK,SAAST,GAEhD,IACEI,EAAGK,SAAS,iBACX,CAAC,MAAO,OAAQ,MAAO,QAAQA,SAASJ,IACxCG,GACAE,EAAa7B,GAKhB,MAAO,CACLA,KAAMa,EAAqBd,EAAoBC,EAAMC,GAAUa,GAC/DgB,IAAK,CAAEC,SAAU,IAEpB,EAEL"}
1
+ {"version":3,"file":"normalize-route.js","sources":["../../src/plugins/normalize-route.ts"],"sourcesContent":["import { extname, resolve } from 'node:path';\nimport type { Plugin } from 'vite';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport isRoutesFile from '@helpers/is-route-file';\nimport { writeMeta } from '@helpers/ssr-meta';\n\nexport interface IPluginOptions {\n isSSR?: boolean;\n isBuild?: boolean;\n routesPath?: string;\n isNodeParsing?: boolean;\n}\n\n/**\n * Add pathId to route (where pathId - import string)\n * NOTE: only for dev mode\n */\nconst normalizeSyncRoutes = (code: string, isBuild = false): string => {\n if (isBuild) {\n return code;\n }\n\n const imports: Record<string, string> = [\n ...code.matchAll(/import\\s+(\\w+)\\sfrom\\s+['\"]([^'\"]+)['\"]/g),\n ].reduce(\n (res, [, key, value]) => ({\n [key]: value,\n ...res,\n }),\n {},\n );\n\n return code.replace(\n /(.*?(?:Component|element):[\\s<]*(\\w+)[^,}]*),*/gs,\n (fullMatch, before: string, routeName: string) => {\n const importPath = imports?.[routeName];\n\n if (!importPath) {\n return fullMatch;\n }\n\n return `${before},pathId: '${importPath}',`;\n },\n );\n};\n\n/**\n * Add normalize wrapper to lazy imports for client build\n */\nconst normalizeAsyncRoutes = (code: string, hasPathId: boolean): string => {\n const modifiedCode = code.replace(\n /(lazy)(:\\s*)(\\(\\)\\s*=>\\s*import\\(([^)]+)\\))/gs,\n hasPathId ? 'lazy$2n($3,$4)' : 'lazy$2n($3)',\n );\n\n if (code !== modifiedCode) {\n return `import n from '${PLUGIN_NAME}/helpers/import-route';${modifiedCode}`;\n }\n\n return code;\n};\n\n/**\n * Add possibility to export route components like FCRoute or FCCRoute\n * Add route path for generating manifest\n * USAGE: { path: '/', lazy: () => import('./pages/home') }\n * @see FCRoute\n * @see FCCRoute\n * @see SsrManifest.getAsyncRoutesIds\n * @see importRoute\n * @constructor\n */\nfunction ViteNormalizeRouterPlugin(options: IPluginOptions = {}): Plugin {\n const { routesPath, isNodeParsing = false, isSSR = false, isBuild = false } = options;\n const routeFiles = new Map<string, string>();\n const cfg = { root: '', buildDir: '' };\n\n return {\n name: `${PLUGIN_NAME}-normalize-route`,\n enforce: 'pre',\n transform(code, id) {\n const [extName] = extname(id).split('?');\n const isRoutesPath = !routesPath || id.includes(routesPath);\n\n if (\n id.includes('node_modules') ||\n !['.js', '.mjs', '.ts', '.tsx'].includes(extName) ||\n !isRoutesPath ||\n !isRoutesFile(code)\n ) {\n return;\n }\n\n routeFiles.set(id, '');\n\n return {\n // always add pathId to routes for development\n code: normalizeAsyncRoutes(\n normalizeSyncRoutes(code, isBuild),\n isSSR && (isNodeParsing || isBuild),\n ),\n map: { mappings: '' },\n };\n },\n ...(isNodeParsing\n ? {\n /**\n * Get build path\n */\n config(config, { isSsrBuild }): void {\n if (isSsrBuild) {\n return;\n }\n\n cfg.root = config.root!;\n cfg.buildDir = config.build!.outDir!;\n },\n /**\n * Get transformed route files\n */\n generateBundle(_, bundle) {\n for (const [fileName, chunk] of Object.entries(bundle)) {\n if (chunk.type === 'chunk') {\n Object.entries(chunk.modules).forEach(([modulePath]) => {\n if (routeFiles.has(modulePath)) {\n routeFiles.set(modulePath, fileName);\n }\n });\n }\n }\n },\n /**\n * Save metadata on for client build\n * @see config hook\n */\n writeBundle(): void {\n if (!cfg.root) {\n return;\n }\n\n const [buildDir] = resolve(cfg.root, cfg.buildDir).split('/client');\n\n writeMeta(buildDir, { routeFiles: Object.fromEntries(routeFiles) });\n },\n }\n : {}),\n };\n}\n\nexport default ViteNormalizeRouterPlugin;\n"],"names":["normalizeSyncRoutes","code","isBuild","imports","matchAll","reduce","res","key","value","replace","fullMatch","before","routeName","importPath","normalizeAsyncRoutes","hasPathId","modifiedCode","PLUGIN_NAME","ViteNormalizeRouterPlugin","options","routesPath","isNodeParsing","isSSR","routeFiles","Map","cfg","root","buildDir","name","enforce","transform","id","extName","extname","split","isRoutesPath","includes","isRoutesFile","set","map","mappings","config","isSsrBuild","build","outDir","generateBundle","_","bundle","fileName","chunk","Object","entries","type","modules","forEach","modulePath","has","writeBundle","resolve","writeMeta","fromEntries"],"mappings":"0LAiBA,MAAMA,EAAsB,CAACC,EAAcC,GAAU,KACnD,GAAIA,EACF,OAAOD,EAGT,MAAME,EAAkC,IACnCF,EAAKG,SAAS,6CACjBC,QACA,CAACC,GAAK,CAAGC,EAAKC,MAAY,CACxBD,CAACA,GAAMC,KACJF,KAEL,CAAE,GAGJ,OAAOL,EAAKQ,QACV,oDACA,CAACC,EAAWC,EAAgBC,KAC1B,MAAMC,EAAaV,IAAUS,GAE7B,OAAKC,EAIE,GAAGF,cAAmBE,MAHpBH,CAGkC,GAE9C,EAMGI,EAAuB,CAACb,EAAcc,KAC1C,MAAMC,EAAef,EAAKQ,QACxB,gDACAM,EAAY,iBAAmB,eAGjC,OAAId,IAASe,EACJ,kBAAkBC,2BAAqCD,IAGzDf,CAAI,EAab,SAASiB,EAA0BC,EAA0B,IAC3D,MAAMC,WAAEA,EAAUC,cAAEA,GAAgB,EAAKC,MAAEA,GAAQ,EAAKpB,QAAEA,GAAU,GAAUiB,EACxEI,EAAa,IAAIC,IACjBC,EAAM,CAAEC,KAAM,GAAIC,SAAU,IAElC,MAAO,CACLC,KAAM,GAAGX,oBACTY,QAAS,MACTC,UAAU7B,EAAM8B,GACd,MAAOC,GAAWC,EAAQF,GAAIG,MAAM,KAC9BC,GAAgBf,GAAcW,EAAGK,SAAShB,GAEhD,IACEW,EAAGK,SAAS,iBACX,CAAC,MAAO,OAAQ,MAAO,QAAQA,SAASJ,IACxCG,GACAE,EAAapC,GAOhB,OAFAsB,EAAWe,IAAIP,EAAI,IAEZ,CAEL9B,KAAMa,EACJd,EAAoBC,EAAMC,GAC1BoB,IAAUD,GAAiBnB,IAE7BqC,IAAK,CAAEC,SAAU,IAEpB,KACGnB,EACA,CAIEoB,OAAOA,GAAQC,WAAEA,IACXA,IAIJjB,EAAIC,KAAOe,EAAOf,KAClBD,EAAIE,SAAWc,EAAOE,MAAOC,OAC9B,EAIDC,eAAeC,EAAGC,GAChB,IAAK,MAAOC,EAAUC,KAAUC,OAAOC,QAAQJ,GAC1B,UAAfE,EAAMG,MACRF,OAAOC,QAAQF,EAAMI,SAASC,SAAQ,EAAEC,MAClChC,EAAWiC,IAAID,IACjBhC,EAAWe,IAAIiB,EAAYP,EAC5B,GAIR,EAKDS,cACE,IAAKhC,EAAIC,KACP,OAGF,MAAOC,GAAY+B,EAAQjC,EAAIC,KAAMD,EAAIE,UAAUO,MAAM,WAEzDyB,EAAUhC,EAAU,CAAEJ,WAAY2B,OAAOU,YAAYrC,IACtD,GAEH,CAAE,EAEV"}
@@ -79,6 +79,13 @@ declare class Build {
79
79
  * Build assets manifest file
80
80
  */
81
81
  buildManifest(): Promise<void>;
82
+ /**
83
+ * Remove pathId from client route files
84
+ */
85
+ /**
86
+ * Remove pathId from client route files
87
+ */
88
+ private cleanupClientRoutes;
82
89
  /**
83
90
  * Change general directive Disallow to Allow in robots.txt.
84
91
  */
package/services/build.js CHANGED
@@ -1,2 +1,2 @@
1
- import i from"node:fs";import o from"node:path";import s from"chalk";import{resolveConfig as e}from"vite";import r from"../helpers/plugin-config.js";import t from"./server-config.js";import n from"./ssr-manifest.js";class l{isProd;nodeEnv;buildDir;outDir;serverFile;viteConfig;pluginConfig;params;constructor(i){this.params=i}async makeConfig(){const{mode:i}=this.params;this.viteConfig=await e({},"build",i,"production"),this.pluginConfig=r(this.viteConfig),this.buildDir=o.resolve(this.viteConfig.root,this.viteConfig.build.outDir),this.outDir=this.viteConfig.build.outDir,this.serverFile=this.pluginConfig.serverFile,this.nodeEnv=process.env.NODE_ENV||"production",this.isProd="production"===this.nodeEnv}clearBuildFolder(){i.existsSync(this.buildDir)&&i.rmSync(this.buildDir,{recursive:!0})}promisifyProcess(i,o=!1){const s=new Promise(((s,e)=>{i.on("exit",(i=>{s(i)})),i.on("close",(i=>{s(i)})),i.on("error",(i=>{e(i)})),o&&i.stderr?.on("data",(i=>{const o=Buffer.from(i).toString();(o.includes("warning")||o.includes("WARNING"))&&s(1)}))}));return i.stdout?.pipe(process.stdout),i.stderr?.pipe(process.stderr),{promise:s,command:i}}async buildManifest(){console.log(s.blue("Building routes manifest file..."));const i=t.init({isProd:this.isProd,mode:this.params.mode},{root:this.viteConfig.root});await n.get(i,{buildDir:this.viteConfig.build.outDir,viteAliases:this.viteConfig.resolve.alias}).buildRoutesManifest()}unlockRobots(){const o=`${this.buildDir}/client/robots.txt`;if(!i.existsSync(o))return void console.warn(`Failed to unlock robots.txt, file not exist: ${o}`);const e=i.readFileSync(o,{encoding:"utf-8"}).replace(/Disallow: \/$/m,"Allow: /");i.writeFileSync(o,e,{encoding:"utf-8"}),console.info(s.blue("\nrobots.txt unlocked."))}eject(){const o=`${this.buildDir}/server/start.js`;i.writeFileSync(o,"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 o=`${this.buildDir}/server/serverless.js`;i.writeFileSync(o,"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"})}}export{l as default};
1
+ import i from"node:fs";import e from"node:path";import s from"chalk";import{resolveConfig as o}from"vite";import t from"../helpers/plugin-config.js";import{readMeta as r}from"../helpers/ssr-meta.js";import n from"./server-config.js";import l from"./ssr-manifest.js";class c{isProd;nodeEnv;buildDir;outDir;serverFile;viteConfig;pluginConfig;params;constructor(i){this.params=i}async makeConfig(){const{mode:i}=this.params;this.viteConfig=await o({},"build",i,"production"),this.pluginConfig=t(this.viteConfig),this.buildDir=e.resolve(this.viteConfig.root,this.viteConfig.build.outDir),this.outDir=this.viteConfig.build.outDir,this.serverFile=this.pluginConfig.serverFile,this.nodeEnv=process.env.NODE_ENV||"production",this.isProd="production"===this.nodeEnv}clearBuildFolder(){i.existsSync(this.buildDir)&&i.rmSync(this.buildDir,{recursive:!0})}promisifyProcess(i,e=!1){const s=new Promise(((s,o)=>{i.on("exit",(i=>{s(i)})),i.on("close",(i=>{s(i)})),i.on("error",(i=>{o(i)})),e&&i.stderr?.on("data",(i=>{const e=Buffer.from(i).toString();(e.includes("warning")||e.includes("WARNING"))&&s(1)}))}));return i.stdout?.pipe(process.stdout),i.stderr?.pipe(process.stderr),{promise:s,command:i}}async buildManifest(){console.info(s.blue(`Building routes manifest file: ${this.pluginConfig.routesParsing}`));const i="node"===this.pluginConfig.routesParsing,e=n.init({isProd:this.isProd,mode:this.params.mode},{root:this.viteConfig.root,clientFile:this.pluginConfig.clientFile});await l.get(e,{buildDir:this.viteConfig.build.outDir,viteAliases:this.viteConfig.resolve.alias}).buildRoutesManifest(i),i&&this.cleanupClientRoutes()}cleanupClientRoutes(){const{routeFiles:e}=r(this.buildDir),s=new Set(Object.values(e??[]));s.size&&s.forEach((e=>{const s=`${this.buildDir}/client/${e}`;try{const e=i.readFileSync(s,{encoding:"utf-8"}).replace(/(lazy:.*?\((.*?)\)),\s?".*?"\)/g,"$1)");i.writeFileSync(s,e)}catch(i){console.log(`Failed cleanup client route ${s}:`,i)}}))}unlockRobots(){const e=`${this.buildDir}/client/robots.txt`;if(!i.existsSync(e))return void console.warn(`Failed to unlock robots.txt, file not exist: ${e}`);const o=i.readFileSync(e,{encoding:"utf-8"}).replace(/Disallow: \/$/m,"Allow: /");i.writeFileSync(e,o,{encoding:"utf-8"}),console.info(s.blue("\nrobots.txt unlocked."))}eject(){const e=`${this.buildDir}/server/start.js`;i.writeFileSync(e,"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 e=`${this.buildDir}/server/serverless.js`;i.writeFileSync(e,"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"})}}export{c as default};
2
2
  //# sourceMappingURL=build.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sources":["../../src/services/build.ts"],"sourcesContent":["import type 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 type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport ServerConfig from '@services/server-config';\nimport SsrManifest from '@services/ssr-manifest';\n\ninterface IBuildParams {\n mode: string;\n}\n\n/**\n * Build service\n */\nclass Build {\n /**\n * Is production build\n */\n public isProd: boolean;\n\n /**\n * Node environment\n */\n public nodeEnv: string;\n\n /**\n * Build folder\n */\n public buildDir: string;\n\n /**\n * Relative build dir\n */\n public outDir: string;\n\n /**\n * Server file\n */\n public serverFile: string;\n\n /**\n * Vite config\n */\n public viteConfig: ResolvedConfig;\n\n /**\n * Plugin config\n */\n public pluginConfig: IPluginConfig;\n\n /**\n * Build params\n */\n protected params: IBuildParams;\n\n /**\n * @constructor\n */\n constructor(params: IBuildParams) {\n this.params = params;\n }\n\n /**\n * Make config\n */\n public 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.outDir = this.viteConfig.build.outDir;\n this.serverFile = this.pluginConfig.serverFile;\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 * Promisify spawn process\n */\n public promisifyProcess(\n command: childProcess.ChildProcess,\n isRejectWarnings = false,\n ): { promise: Promise<number | null | string>; command: childProcess.ChildProcess } {\n const promise = new Promise<number | null | string>((resolve, reject) => {\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 public async buildManifest(): Promise<void> {\n console.log(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 },\n );\n\n await SsrManifest.get(serverConfig, {\n buildDir: this.viteConfig.build.outDir,\n viteAliases: this.viteConfig.resolve.alias,\n }).buildRoutesManifest();\n }\n\n /**\n * Change general directive Disallow to Allow in robots.txt.\n */\n public 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 public 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 public 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\nexport default Build;\n"],"names":["Build","isProd","nodeEnv","buildDir","outDir","serverFile","viteConfig","pluginConfig","params","constructor","this","async","mode","resolveConfig","getPluginConfig","path","resolve","root","build","process","env","NODE_ENV","clearBuildFolder","fs","existsSync","rmSync","recursive","promisifyProcess","command","isRejectWarnings","promise","Promise","reject","on","code","message","stderr","buff","msg","Buffer","from","toString","includes","stdout","pipe","console","log","chalk","blue","serverConfig","ServerConfig","init","SsrManifest","get","viteAliases","alias","buildRoutesManifest","unlockRobots","robotsFile","warn","data","readFileSync","encoding","replace","writeFileSync","info","eject","entrypoint","createServerless"],"mappings":"wNAkBA,MAAMA,EAIGC,OAKAC,QAKAC,SAKAC,OAKAC,WAKAC,WAKAC,aAKGC,OAKVC,YAAYD,GACVE,KAAKF,OAASA,CACf,CAKMG,mBACL,MAAMC,KAAEA,GAASF,KAAKF,OAEtBE,KAAKJ,iBAAmBO,EAAc,CAAE,EAAE,QAASD,EAAM,cACzDF,KAAKH,aAAeO,EAAgBJ,KAAKJ,YACzCI,KAAKP,SAAWY,EAAKC,QAAQN,KAAKJ,WAAWW,KAAMP,KAAKJ,WAAWY,MAAMd,QACzEM,KAAKN,OAASM,KAAKJ,WAAWY,MAAMd,OACpCM,KAAKL,WAAaK,KAAKH,aAAaF,WACpCK,KAAKR,QAAUiB,QAAQC,IAAIC,UAAY,aACvCX,KAAKT,OAA0B,eAAjBS,KAAKR,OACpB,CAKMoB,mBAEDC,EAAGC,WAAWd,KAAKP,WACrBoB,EAAGE,OAAOf,KAAKP,SAAU,CAAEuB,WAAW,GAEzC,CAKMC,iBACLC,EACAC,GAAmB,GAEnB,MAAMC,EAAU,IAAIC,SAAgC,CAACf,EAASgB,KAC5DJ,EAAQK,GAAG,QAASC,IAClBlB,EAAQkB,EAAK,IAGfN,EAAQK,GAAG,SAAUC,IACnBlB,EAAQkB,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,aAC1C1B,EAAQ,EACT,GAEJ,IAMH,OAHAY,EAAQe,QAAQC,KAAKzB,QAAQwB,QAC7Bf,EAAQQ,QAAQQ,KAAKzB,QAAQiB,QAEtB,CAAEN,UAASF,UACnB,CAKMjB,sBACLkC,QAAQC,IAAIC,EAAMC,KAAK,qCAEvB,MAAMC,EAAeC,EAAaC,KAChC,CAAElD,OAAQS,KAAKT,OAAQW,KAAMF,KAAKF,OAAOI,MACzC,CAAEK,KAAMP,KAAKJ,WAAWW,aAGpBmC,EAAYC,IAAIJ,EAAc,CAClC9C,SAAUO,KAAKJ,WAAWY,MAAMd,OAChCkD,YAAa5C,KAAKJ,WAAWU,QAAQuC,QACpCC,qBACJ,CAKMC,eACL,MAAMC,EAAa,GAAGhD,KAAKP,6BAE3B,IAAKoB,EAAGC,WAAWkC,GAGjB,YAFAb,QAAQc,KAAK,gDAAgDD,KAK/D,MAAME,EAAOrC,EACVsC,aAAaH,EAAY,CAAEI,SAAU,UACrCC,QAAQ,iBAAkB,YAE7BxC,EAAGyC,cAAcN,EAAYE,EAAM,CAAEE,SAAU,UAE/CjB,QAAQoB,KAAKlB,EAAMC,KAAK,0BACzB,CAKMkB,QACL,MAAMC,EAAa,GAAGzD,KAAKP,2BAe3BoB,EAAGyC,cAAcG,EAbf,2bAamC,CACnCL,SAAU,SAEb,CAKMM,mBACL,MAAMD,EAAa,GAAGzD,KAAKP,gCAK3BoB,EAAGyC,cAAcG,EAHf,oKAGmC,CACnCL,SAAU,SAEb"}
1
+ {"version":3,"file":"build.js","sources":["../../src/services/build.ts"],"sourcesContent":["import type 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 type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport { readMeta } from '@helpers/ssr-meta';\nimport ServerConfig from '@services/server-config';\nimport SsrManifest from '@services/ssr-manifest';\n\ninterface IBuildParams {\n mode: string;\n}\n\n/**\n * Build service\n */\nclass Build {\n /**\n * Is production build\n */\n public isProd: boolean;\n\n /**\n * Node environment\n */\n public nodeEnv: string;\n\n /**\n * Build folder\n */\n public buildDir: string;\n\n /**\n * Relative build dir\n */\n public outDir: string;\n\n /**\n * Server file\n */\n public serverFile: string;\n\n /**\n * Vite config\n */\n public viteConfig: ResolvedConfig;\n\n /**\n * Plugin config\n */\n public pluginConfig: IPluginConfig;\n\n /**\n * Build params\n */\n protected params: IBuildParams;\n\n /**\n * @constructor\n */\n constructor(params: IBuildParams) {\n this.params = params;\n }\n\n /**\n * Make config\n */\n public 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.outDir = this.viteConfig.build.outDir;\n this.serverFile = this.pluginConfig.serverFile;\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 * Promisify spawn process\n */\n public promisifyProcess(\n command: childProcess.ChildProcess,\n isRejectWarnings = false,\n ): { promise: Promise<number | null | string>; command: childProcess.ChildProcess } {\n const promise = new Promise<number | null | string>((resolve, reject) => {\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 public async buildManifest(): Promise<void> {\n console.info(chalk.blue(`Building routes manifest file: ${this.pluginConfig.routesParsing}`));\n\n const isNodeParsing = this.pluginConfig.routesParsing === 'node';\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 await SsrManifest.get(serverConfig, {\n buildDir: this.viteConfig.build.outDir,\n viteAliases: this.viteConfig.resolve.alias,\n }).buildRoutesManifest(isNodeParsing);\n\n if (isNodeParsing) {\n this.cleanupClientRoutes();\n }\n }\n\n /**\n * Remove pathId from client route files\n */\n private cleanupClientRoutes(): void {\n const { routeFiles } = readMeta(this.buildDir);\n const files = new Set(Object.values(routeFiles ?? []));\n\n if (!files.size) {\n return;\n }\n\n files.forEach((file) => {\n const filepath = `${this.buildDir}/client/${file}`;\n\n try {\n const result = fs\n .readFileSync(filepath, { encoding: 'utf-8' })\n .replace(/(lazy:.*?\\((.*?)\\)),\\s?\".*?\"\\)/g, '$1)');\n\n fs.writeFileSync(filepath, result);\n } catch (e) {\n console.log(`Failed cleanup client route ${filepath}:`, e);\n }\n });\n }\n\n /**\n * Change general directive Disallow to Allow in robots.txt.\n */\n public 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 public 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 public 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\nexport default Build;\n"],"names":["Build","isProd","nodeEnv","buildDir","outDir","serverFile","viteConfig","pluginConfig","params","constructor","this","async","mode","resolveConfig","getPluginConfig","path","resolve","root","build","process","env","NODE_ENV","clearBuildFolder","fs","existsSync","rmSync","recursive","promisifyProcess","command","isRejectWarnings","promise","Promise","reject","on","code","message","stderr","buff","msg","Buffer","from","toString","includes","stdout","pipe","console","info","chalk","blue","routesParsing","isNodeParsing","serverConfig","ServerConfig","init","clientFile","SsrManifest","get","viteAliases","alias","buildRoutesManifest","cleanupClientRoutes","routeFiles","readMeta","files","Set","Object","values","size","forEach","file","filepath","result","readFileSync","encoding","replace","writeFileSync","e","log","unlockRobots","robotsFile","warn","data","eject","entrypoint","createServerless"],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ import { Alias } from 'vite';
2
+ import PathNormalize from "./path-normalize.js";
3
+ import ServerConfig from "./server-config.js";
4
+ type TRoutesTree = {
5
+ index: number;
6
+ import: string;
7
+ children: TRoutesTree[];
8
+ };
9
+ /**
10
+ * Parse react router routes array
11
+ */
12
+ declare class ParseRoutes {
13
+ /**
14
+ * Path normalize service
15
+ */
16
+ protected readonly pathNormalize: PathNormalize;
17
+ /**
18
+ * Server config
19
+ */
20
+ protected readonly config: ServerConfig;
21
+ /**
22
+ * @constructor
23
+ */
24
+ /**
25
+ * @constructor
26
+ */
27
+ constructor(config: ServerConfig, viteAliases?: Alias[]);
28
+ /**
29
+ * Parse routes
30
+ */
31
+ /**
32
+ * Parse routes
33
+ */
34
+ parse(): TRoutesTree[];
35
+ /**
36
+ * Parse file and return ast
37
+ */
38
+ /**
39
+ * Parse file and return ast
40
+ */
41
+ private parseFile;
42
+ /**
43
+ * Find route import filepath
44
+ */
45
+ /**
46
+ * Find route import filepath
47
+ */
48
+ private getImportPath;
49
+ /**
50
+ * Find routes array inside code
51
+ */
52
+ /**
53
+ * Find routes array inside code
54
+ */
55
+ private findRoutesDefinition;
56
+ /**
57
+ * Entrypoint routes file
58
+ */
59
+ /**
60
+ * Entrypoint routes file
61
+ */
62
+ private findRoutesEntrypoint;
63
+ /**
64
+ * Resolve route filename import
65
+ */
66
+ /**
67
+ * Resolve route filename import
68
+ */
69
+ private resolveFilename;
70
+ /**
71
+ * Parse ast array routes objects
72
+ */
73
+ /**
74
+ * Parse ast array routes objects
75
+ */
76
+ private parseRoutesArray;
77
+ /**
78
+ * Recursive build routes tree with dynamic imports
79
+ */
80
+ /**
81
+ * Recursive build routes tree with dynamic imports
82
+ */
83
+ private recursiveBuildRoutesTree;
84
+ }
85
+ export { ParseRoutes as default, TRoutesTree };
@@ -0,0 +1,2 @@
1
+ import e from"fs";import{resolve as t}from"node:path";import r from"path";import*as n from"@babel/parser";import a from"@babel/traverse";import i from"./path-normalize.js";const o=a.default??a;class l{pathNormalize;config;constructor(e,t){this.config=e,this.pathNormalize=new i(e,t)}parse(){const{clientFile:e,root:r}=this.config.getParams(),n=t(r,e),a=this.findRoutesEntrypoint(n);if(!a?.routesPath)throw new Error(`Unable to find routes file import in ${e}`);const{routesPath:i,exportName:o}=a,l=this.resolveFilename(i);return this.recursiveBuildRoutesTree(l,o)}parseFile(t){try{const r=e.readFileSync(t,"utf-8");return n.parse(r,{sourceType:"module",plugins:["typescript","jsx"]})}catch(e){return null}}getImportPath(e,t){let r=null,n=null;return o(e,{ImportDeclaration(e){const a=e.node;a.specifiers.forEach((e=>{e.local.name===t&&(n="ImportDefaultSpecifier"===e.type?null:t,r=a.source.value)}))}}),{routesPath:r,exportName:n}}findRoutesDefinition(e,t){let r=t;if(o(e,{ExportNamedDeclaration({node:e}){!e.declaration&&e.specifiers.length>0&&e.specifiers.forEach((e=>{const n=e.exported.name;null===t&&"ExportSpecifier"===e.type?"default"===e.local.name&&(r=n):n===t&&(r=e.local.name)}))},ExportDefaultDeclaration({node:e}){null===t&&("Identifier"===e.declaration.type?r=e.declaration.name:"VariableDeclaration"===e.declaration.type&&(r=e.declaration.declarations[0].id.name))}}),r){let t=null;return o(e,{VariableDeclaration({node:e}){e.declarations.forEach((n=>{n.id.name===r&&(t=e)}))}}),t}return null}findRoutesEntrypoint(e){const t=this.parseFile(e);let r=null;return t?(o(t,{CallExpression({node:e}){"entryClient"===e.callee.name&&e.arguments.length>=2&&"Identifier"===e.arguments[1].type&&(r=e.arguments[1].name)}}),this.getImportPath(t,r)):r}resolveFilename(e,t){let n=e;(e.startsWith("./")||e.startsWith("../"))&&t&&(n=r.resolve(r.dirname(t),e));const a=this.pathNormalize.getAppPath(n,!0);return this.pathNormalize.findAppFile(a)}parseRoutesArray(e,t,r){const n=[];return e.forEach(((e,a)=>{if("ObjectExpression"===e.type){const i={index:a,import:"",children:[]};e.properties.forEach((e=>{const n=e;if("children"===n.key.name&&"ArrayExpression"===n.value.type&&(i.children=this.parseRoutesArray(n.value.elements,t,r)),"lazy"===n.key.name&&"ArrowFunctionExpression"===n.value.type){const e=n.value.body;if("CallExpression"===e.type&&"Import"===e.callee.type){const[t]=e.arguments;"StringLiteral"===t.type&&(i.import=t.value)}}if("Component"===n.key.name&&"Identifier"===n.value.type){const e=n.value.name,{path:r}=t[e]??{};r&&(i.import=r)}if("element"===n.key.name&&"JSXElement"===n.value.type){const e=n.value?.openingElement?.name?.name,{path:r}=t[e]??{};r&&(i.import=r)}if("children"===n.key.name&&"Identifier"===n.value.type){const e=n.value.name,{path:a,isDefault:o}=t[e]??{};if(a){const t=this.resolveFilename(a,r);t&&(i.children=this.recursiveBuildRoutesTree(t,o?null:e))}}})),(i.import||i.children.length>0)&&n.push(i)}})),n}recursiveBuildRoutesTree(e,t=null){if(!e)return[];const r=this.parseFile(e);if(!r)return[];const n=this.findRoutesDefinition(r,t),a=[];if(!n)return a;const i={};o(r,{ImportDeclaration(e){const t=e.node;t.specifiers.forEach((e=>{i[e.local.name]={path:t.source.value,isDefault:"ImportDefaultSpecifier"===e.type}}))}});const l=n.declarations[0].init?.elements;return a.push(...this.parseRoutesArray(l,i,e)),a}}export{l as default};
2
+ //# sourceMappingURL=parse-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-routes.js","sources":["../../src/services/parse-routes.ts"],"sourcesContent":["import fs from 'fs';\nimport { resolve } from 'node:path';\nimport path from 'path';\nimport * as parser from '@babel/parser';\nimport type { ParseResult } from '@babel/parser';\nimport babelTraverse from '@babel/traverse';\nimport type * as TraverseTypes from '@babel/traverse';\nimport type { CallExpression, File as BabelFile, VariableDeclaration } from '@babel/types';\nimport type { Alias } from 'vite';\nimport PathNormalize from '@services/path-normalize';\nimport type ServerConfig from '@services/server-config';\n\n// @ts-expect-error known import problem\nconst traverse = (babelTraverse.default ?? babelTraverse) as (typeof TraverseTypes)['default'];\n\ninterface IPathImport {\n routesPath: string | null;\n exportName: string | null;\n}\n\ninterface IMapImports {\n [name: string]: {\n path: string;\n isDefault: boolean; // is default import?\n };\n}\n\nexport type TRoutesTree = {\n index: number;\n import: string;\n children: TRoutesTree[];\n};\n\n/**\n * Parse react router routes array\n */\nclass ParseRoutes {\n /**\n * Path normalize service\n */\n protected readonly pathNormalize: PathNormalize;\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * @constructor\n */\n constructor(config: ServerConfig, viteAliases?: Alias[]) {\n this.config = config;\n this.pathNormalize = new PathNormalize(config, viteAliases);\n }\n\n /**\n * Parse routes\n */\n public parse(): TRoutesTree[] {\n const { clientFile, root } = this.config.getParams();\n\n const clientEntrypoint = resolve(root, clientFile);\n const routesEntrypoint = this.findRoutesEntrypoint(clientEntrypoint);\n\n if (!routesEntrypoint?.routesPath) {\n throw new Error(`Unable to find routes file import in ${clientFile}`);\n }\n\n const { routesPath, exportName } = routesEntrypoint;\n const routeFilepath = this.resolveFilename(routesPath);\n\n return this.recursiveBuildRoutesTree(routeFilepath, exportName);\n }\n\n /**\n * Parse file and return ast\n */\n private parseFile(filename: string): ParseResult<BabelFile> | null {\n try {\n const code = fs.readFileSync(filename, 'utf-8');\n\n return parser.parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n });\n } catch (e) {\n return null;\n }\n }\n\n /**\n * Find route import filepath\n */\n private getImportPath(\n ast: ParseResult<BabelFile>,\n importName: string | null,\n ): IPathImport | null {\n let routesPath: string | null = null;\n let exportName: string | null = null;\n\n traverse(ast, {\n ImportDeclaration(nodePath) {\n const importNode = nodePath.node;\n\n importNode.specifiers.forEach((specifier) => {\n if (specifier.local.name === importName) {\n exportName = specifier.type === 'ImportDefaultSpecifier' ? null : importName;\n routesPath = importNode.source.value;\n }\n });\n },\n });\n\n return {\n routesPath,\n exportName,\n };\n }\n\n /**\n * Find routes array inside code\n */\n private findRoutesDefinition(\n ast: ParseResult<BabelFile>,\n exportName: string | null,\n ): null | VariableDeclaration {\n let exportNameResolved = exportName;\n\n // noinspection JSUnusedGlobalSymbols\n traverse(ast, {\n ExportNamedDeclaration({ node }) {\n if (!node.declaration && node.specifiers.length > 0) {\n node.specifiers.forEach((specifier) => {\n // @ts-expect-error missing in types\n const exportedName = specifier.exported.name as string;\n\n if (exportName === null && specifier.type === 'ExportSpecifier') {\n if (specifier.local.name === 'default') {\n exportNameResolved = exportedName;\n }\n } else if (exportedName === exportName) {\n // @ts-expect-error missing in types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n exportNameResolved = specifier.local.name as string;\n }\n });\n }\n },\n ExportDefaultDeclaration({ node }) {\n if (exportName === null) {\n if (node.declaration.type === 'Identifier') {\n exportNameResolved = node.declaration.name;\n // @ts-expect-error missing in types\n } else if (node.declaration.type === 'VariableDeclaration') {\n // @ts-expect-error missing in types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n exportNameResolved = node.declaration.declarations[0].id.name as string;\n }\n }\n },\n });\n\n if (exportNameResolved) {\n let variableNode: VariableDeclaration | null = null;\n\n traverse(ast, {\n VariableDeclaration({ node }) {\n node.declarations.forEach((declaration) => {\n // @ts-expect-error missing in types\n if (declaration.id.name === exportNameResolved) {\n variableNode = node;\n }\n });\n },\n });\n\n return variableNode;\n }\n\n return null;\n }\n\n /**\n * Entrypoint routes file\n */\n private findRoutesEntrypoint(clientEntrypoint: string): IPathImport | null {\n const ast = this.parseFile(clientEntrypoint);\n\n let routesVariable: string | null = null;\n\n if (!ast) {\n return routesVariable;\n }\n\n traverse(ast, {\n CallExpression({ node }) {\n if (\n // @ts-expect-error missing in types\n node.callee.name === 'entryClient' &&\n node.arguments.length >= 2 &&\n node.arguments[1].type === 'Identifier'\n ) {\n routesVariable = node.arguments[1].name;\n }\n },\n });\n\n return this.getImportPath(ast, routesVariable);\n }\n\n /**\n * Resolve route filename import\n */\n private resolveFilename(filename: string, relativeFile?: string): string | null {\n let resolvedFilename = filename;\n\n if ((filename.startsWith('./') || filename.startsWith('../')) && relativeFile) {\n resolvedFilename = path.resolve(path.dirname(relativeFile), filename);\n }\n\n const filepath = this.pathNormalize.getAppPath(resolvedFilename, true);\n\n return this.pathNormalize.findAppFile(filepath!);\n }\n\n /**\n * Parse ast array routes objects\n */\n private parseRoutesArray(\n elements: TraverseTypes.Node[],\n importsMap: IMapImports,\n relativeFile: string,\n ): TRoutesTree[] {\n const results: TRoutesTree[] = [];\n\n elements.forEach((node, index) => {\n if (node.type === 'ObjectExpression') {\n const routeInfo: TRoutesTree = { index, import: '', children: [] };\n\n node.properties.forEach((prop) => {\n const objectProp = prop as {\n key: { name: string };\n value: { type: string; elements: TraverseTypes.Node[] };\n };\n\n if (objectProp.key.name === 'children' && objectProp.value.type === 'ArrayExpression') {\n routeInfo.children = this.parseRoutesArray(\n objectProp.value.elements,\n importsMap,\n relativeFile,\n );\n }\n\n // async routes\n if (\n objectProp.key.name === 'lazy' &&\n objectProp.value.type === 'ArrowFunctionExpression'\n ) {\n // @ts-expect-error incorrect types\n const importCall = objectProp.value.body as CallExpression;\n\n if (importCall.type === 'CallExpression' && importCall.callee.type === 'Import') {\n const [importArg] = importCall.arguments;\n\n if (importArg.type === 'StringLiteral') {\n routeInfo.import = importArg.value;\n }\n }\n }\n\n // static routes: Component\n if (objectProp.key.name === 'Component' && objectProp.value.type === 'Identifier') {\n // @ts-expect-error incorrect types\n const importName = objectProp.value.name as string;\n const { path: importPath } = importsMap[importName] ?? {};\n\n if (importPath) {\n routeInfo.import = importPath;\n }\n }\n\n // static routes: element\n if (objectProp.key.name === 'element' && objectProp.value.type === 'JSXElement') {\n // @ts-expect-error incorrect types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const importName = objectProp.value?.openingElement?.name?.name as string;\n const { path: importPath } = importsMap[importName] ?? {};\n\n if (importPath) {\n routeInfo.import = importPath;\n }\n }\n\n if (objectProp.key.name === 'children' && objectProp.value.type === 'Identifier') {\n // @ts-expect-error incorrect types\n const importName = objectProp.value.name as string;\n const { path: importPath, isDefault } = importsMap[importName] ?? {};\n\n if (importPath) {\n const childrenFilePath = this.resolveFilename(importPath, relativeFile);\n\n if (childrenFilePath) {\n routeInfo.children = this.recursiveBuildRoutesTree(\n childrenFilePath,\n isDefault ? null : importName,\n );\n }\n }\n }\n });\n\n if (routeInfo.import || routeInfo.children.length > 0) {\n results.push(routeInfo);\n }\n }\n });\n\n return results;\n }\n\n /**\n * Recursive build routes tree with dynamic imports\n */\n private recursiveBuildRoutesTree(\n filename: string | null,\n exportName: string | null = null,\n ): TRoutesTree[] {\n if (!filename) {\n return [];\n }\n\n const ast = this.parseFile(filename);\n\n if (!ast) {\n return [];\n }\n\n const routesNode = this.findRoutesDefinition(ast, exportName);\n const results: TRoutesTree[] = [];\n\n if (!routesNode) {\n return results;\n }\n\n const importsMap: IMapImports = {};\n\n traverse(ast, {\n ImportDeclaration(nodePath) {\n const importNode = nodePath.node;\n\n importNode.specifiers.forEach((specifier) => {\n importsMap[specifier.local.name] = {\n path: importNode.source.value,\n isDefault: specifier.type === 'ImportDefaultSpecifier',\n };\n });\n },\n });\n\n // @ts-expect-error missing types\n const elements = routesNode.declarations[0].init?.elements as TraverseTypes.Node[];\n\n results.push(...this.parseRoutesArray(elements, importsMap, filename));\n\n return results;\n }\n}\n\nexport default ParseRoutes;\n"],"names":["traverse","babelTraverse","default","ParseRoutes","pathNormalize","config","constructor","viteAliases","this","PathNormalize","parse","clientFile","root","getParams","clientEntrypoint","resolve","routesEntrypoint","findRoutesEntrypoint","routesPath","Error","exportName","routeFilepath","resolveFilename","recursiveBuildRoutesTree","parseFile","filename","code","fs","readFileSync","parser","sourceType","plugins","e","getImportPath","ast","importName","ImportDeclaration","nodePath","importNode","node","specifiers","forEach","specifier","local","name","type","source","value","findRoutesDefinition","exportNameResolved","ExportNamedDeclaration","declaration","length","exportedName","exported","ExportDefaultDeclaration","declarations","id","variableNode","VariableDeclaration","routesVariable","CallExpression","callee","arguments","relativeFile","resolvedFilename","startsWith","path","dirname","filepath","getAppPath","findAppFile","parseRoutesArray","elements","importsMap","results","index","routeInfo","import","children","properties","prop","objectProp","key","importCall","body","importArg","importPath","openingElement","isDefault","childrenFilePath","push","routesNode","init"],"mappings":"4KAaA,MAAMA,EAAYC,EAAcC,SAAWD,EAuB3C,MAAME,EAIeC,cAKAC,OAKnBC,YAAYD,EAAsBE,GAChCC,KAAKH,OAASA,EACdG,KAAKJ,cAAgB,IAAIK,EAAcJ,EAAQE,EAChD,CAKMG,QACL,MAAMC,WAAEA,EAAUC,KAAEA,GAASJ,KAAKH,OAAOQ,YAEnCC,EAAmBC,EAAQH,EAAMD,GACjCK,EAAmBR,KAAKS,qBAAqBH,GAEnD,IAAKE,GAAkBE,WACrB,MAAM,IAAIC,MAAM,wCAAwCR,KAG1D,MAAMO,WAAEA,EAAUE,WAAEA,GAAeJ,EAC7BK,EAAgBb,KAAKc,gBAAgBJ,GAE3C,OAAOV,KAAKe,yBAAyBF,EAAeD,EACrD,CAKOI,UAAUC,GAChB,IACE,MAAMC,EAAOC,EAAGC,aAAaH,EAAU,SAEvC,OAAOI,EAAOnB,MAAMgB,EAAM,CACxBI,WAAY,SACZC,QAAS,CAAC,aAAc,QAE3B,CAAC,MAAOC,GACP,OAAO,IACR,CACF,CAKOC,cACNC,EACAC,GAEA,IAAIjB,EAA4B,KAC5BE,EAA4B,KAehC,OAbApB,EAASkC,EAAK,CACZE,kBAAkBC,GAChB,MAAMC,EAAaD,EAASE,KAE5BD,EAAWE,WAAWC,SAASC,IACzBA,EAAUC,MAAMC,OAAST,IAC3Bf,EAAgC,2BAAnBsB,EAAUG,KAAoC,KAAOV,EAClEjB,EAAaoB,EAAWQ,OAAOC,MAChC,GAEJ,IAGI,CACL7B,aACAE,aAEH,CAKO4B,qBACNd,EACAd,GAEA,IAAI6B,EAAqB7B,EAoCzB,GAjCApB,EAASkC,EAAK,CACZgB,wBAAuBX,KAAEA,KAClBA,EAAKY,aAAeZ,EAAKC,WAAWY,OAAS,GAChDb,EAAKC,WAAWC,SAASC,IAEvB,MAAMW,EAAeX,EAAUY,SAASV,KAErB,OAAfxB,GAA0C,oBAAnBsB,EAAUG,KACN,YAAzBH,EAAUC,MAAMC,OAClBK,EAAqBI,GAEdA,IAAiBjC,IAG1B6B,EAAqBP,EAAUC,MAAMC,KACtC,GAGN,EACDW,0BAAyBhB,KAAEA,IACN,OAAfnB,IAC4B,eAA1BmB,EAAKY,YAAYN,KACnBI,EAAqBV,EAAKY,YAAYP,KAEH,wBAA1BL,EAAKY,YAAYN,OAG1BI,EAAqBV,EAAKY,YAAYK,aAAa,GAAGC,GAAGb,MAG9D,IAGCK,EAAoB,CACtB,IAAIS,EAA2C,KAa/C,OAXA1D,EAASkC,EAAK,CACZyB,qBAAoBpB,KAAEA,IACpBA,EAAKiB,aAAaf,SAASU,IAErBA,EAAYM,GAAGb,OAASK,IAC1BS,EAAenB,EAChB,GAEJ,IAGImB,CACR,CAED,OAAO,IACR,CAKOzC,qBAAqBH,GAC3B,MAAMoB,EAAM1B,KAAKgB,UAAUV,GAE3B,IAAI8C,EAAgC,KAEpC,OAAK1B,GAILlC,EAASkC,EAAK,CACZ2B,gBAAetB,KAAEA,IAGQ,gBAArBA,EAAKuB,OAAOlB,MACZL,EAAKwB,UAAUX,QAAU,GACE,eAA3Bb,EAAKwB,UAAU,GAAGlB,OAElBe,EAAiBrB,EAAKwB,UAAU,GAAGnB,KAEtC,IAGIpC,KAAKyB,cAAcC,EAAK0B,IAhBtBA,CAiBV,CAKOtC,gBAAgBG,EAAkBuC,GACxC,IAAIC,EAAmBxC,GAElBA,EAASyC,WAAW,OAASzC,EAASyC,WAAW,SAAWF,IAC/DC,EAAmBE,EAAKpD,QAAQoD,EAAKC,QAAQJ,GAAevC,IAG9D,MAAM4C,EAAW7D,KAAKJ,cAAckE,WAAWL,GAAkB,GAEjE,OAAOzD,KAAKJ,cAAcmE,YAAYF,EACvC,CAKOG,iBACNC,EACAC,EACAV,GAEA,MAAMW,EAAyB,GAoF/B,OAlFAF,EAAShC,SAAQ,CAACF,EAAMqC,KACtB,GAAkB,qBAAdrC,EAAKM,KAA6B,CACpC,MAAMgC,EAAyB,CAAED,QAAOE,OAAQ,GAAIC,SAAU,IAE9DxC,EAAKyC,WAAWvC,SAASwC,IACvB,MAAMC,EAAaD,EAcnB,GAT4B,aAAxBC,EAAWC,IAAIvC,MAAiD,oBAA1BsC,EAAWnC,MAAMF,OACzDgC,EAAUE,SAAWvE,KAAKgE,iBACxBU,EAAWnC,MAAM0B,SACjBC,EACAV,IAMsB,SAAxBkB,EAAWC,IAAIvC,MACW,4BAA1BsC,EAAWnC,MAAMF,KACjB,CAEA,MAAMuC,EAAaF,EAAWnC,MAAMsC,KAEpC,GAAwB,mBAApBD,EAAWvC,MAAwD,WAA3BuC,EAAWtB,OAAOjB,KAAmB,CAC/E,MAAOyC,GAAaF,EAAWrB,UAER,kBAAnBuB,EAAUzC,OACZgC,EAAUC,OAASQ,EAAUvC,MAEhC,CACF,CAGD,GAA4B,cAAxBmC,EAAWC,IAAIvC,MAAkD,eAA1BsC,EAAWnC,MAAMF,KAAuB,CAEjF,MAAMV,EAAa+C,EAAWnC,MAAMH,MAC5BuB,KAAMoB,GAAeb,EAAWvC,IAAe,GAEnDoD,IACFV,EAAUC,OAASS,EAEtB,CAGD,GAA4B,YAAxBL,EAAWC,IAAIvC,MAAgD,eAA1BsC,EAAWnC,MAAMF,KAAuB,CAG/E,MAAMV,EAAa+C,EAAWnC,OAAOyC,gBAAgB5C,MAAMA,MACnDuB,KAAMoB,GAAeb,EAAWvC,IAAe,GAEnDoD,IACFV,EAAUC,OAASS,EAEtB,CAED,GAA4B,aAAxBL,EAAWC,IAAIvC,MAAiD,eAA1BsC,EAAWnC,MAAMF,KAAuB,CAEhF,MAAMV,EAAa+C,EAAWnC,MAAMH,MAC5BuB,KAAMoB,EAAUE,UAAEA,GAAcf,EAAWvC,IAAe,GAElE,GAAIoD,EAAY,CACd,MAAMG,EAAmBlF,KAAKc,gBAAgBiE,EAAYvB,GAEtD0B,IACFb,EAAUE,SAAWvE,KAAKe,yBACxBmE,EACAD,EAAY,KAAOtD,GAGxB,CACF,MAGC0C,EAAUC,QAAUD,EAAUE,SAAS3B,OAAS,IAClDuB,EAAQgB,KAAKd,EAEhB,KAGIF,CACR,CAKOpD,yBACNE,EACAL,EAA4B,MAE5B,IAAKK,EACH,MAAO,GAGT,MAAMS,EAAM1B,KAAKgB,UAAUC,GAE3B,IAAKS,EACH,MAAO,GAGT,MAAM0D,EAAapF,KAAKwC,qBAAqBd,EAAKd,GAC5CuD,EAAyB,GAE/B,IAAKiB,EACH,OAAOjB,EAGT,MAAMD,EAA0B,CAAA,EAEhC1E,EAASkC,EAAK,CACZE,kBAAkBC,GAChB,MAAMC,EAAaD,EAASE,KAE5BD,EAAWE,WAAWC,SAASC,IAC7BgC,EAAWhC,EAAUC,MAAMC,MAAQ,CACjCuB,KAAM7B,EAAWQ,OAAOC,MACxB0C,UAA8B,2BAAnB/C,EAAUG,KACtB,GAEJ,IAIH,MAAM4B,EAAWmB,EAAWpC,aAAa,GAAGqC,MAAMpB,SAIlD,OAFAE,EAAQgB,QAAQnF,KAAKgE,iBAAiBC,EAAUC,EAAYjD,IAErDkD,CACR"}
@@ -0,0 +1,51 @@
1
+ import { Alias } from 'vite';
2
+ import ServerConfig from "./server-config.js";
3
+ /**
4
+ * Service for work with path and aliases
5
+ */
6
+ declare class PathNormalize {
7
+ /**
8
+ * Vite resolve aliases
9
+ */
10
+ protected readonly viteAliases?: Alias[];
11
+ /**
12
+ * Server config
13
+ */
14
+ protected readonly config: ServerConfig;
15
+ /**
16
+ * @constructor
17
+ */
18
+ /**
19
+ * @constructor
20
+ */
21
+ constructor(config: ServerConfig, viteAliases?: Alias[]);
22
+ /**
23
+ * Get vite aliases
24
+ */
25
+ /**
26
+ * Get vite aliases
27
+ */
28
+ getAliases(): Record<string, string>;
29
+ /**
30
+ * Return filename postfix
31
+ */
32
+ /**
33
+ * Return filename postfix
34
+ */
35
+ getImportPostfix(): string[];
36
+ /**
37
+ * Resolve app path
38
+ */
39
+ /**
40
+ * Resolve app path
41
+ */
42
+ getAppPath(appPath?: string, withRoot?: boolean): string | undefined;
43
+ /**
44
+ * Find app filepath
45
+ */
46
+ /**
47
+ * Find app filepath
48
+ */
49
+ findAppFile(basePath: string): string | null;
50
+ }
51
+ export { PathNormalize as default };
@@ -0,0 +1,2 @@
1
+ import t from"node:fs";import s from"path";class e{viteAliases;config;constructor(t,s){this.config=t,this.viteAliases=s??t.getVite()?.config?.resolve.alias}getAliases(){const t={};return this.viteAliases?.forEach((({find:s,replacement:e})=>{"string"==typeof s&&(t[s]=e)})),t}getImportPostfix(){return["","/index"].map((t=>["",".js",".ts",".tsx"].map((s=>`${t}${s}`)))).flat()}getAppPath(t,e=!1){if(!t)return;const{root:i}=this.config.getParams();let r=t;if(t.startsWith("./")||t.startsWith("../"))r=s.resolve(i,t);else{const s=this.getAliases(),[e]=t.split("/");s[e]&&(r=t.replace(e,s[e]))}return r=r.split(s.win32.sep).join(s.posix.sep),e?r:r.replace(i,"").replace(/^(\/)|(\/)$/g,"")}findAppFile(s){const e=this.getImportPostfix();for(const i of e){const e=`${s}${i}`;if(t.existsSync(e)&&t.statSync(e).isFile())return e}return null}}export{e as default};
2
+ //# sourceMappingURL=path-normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-normalize.js","sources":["../../src/services/path-normalize.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'path';\nimport type { Alias } from 'vite';\nimport type ServerConfig from '@services/server-config';\n\n/**\n * Service for work with path and aliases\n */\nclass PathNormalize {\n /**\n * Vite resolve aliases\n */\n protected readonly viteAliases?: Alias[];\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * @constructor\n */\n constructor(config: ServerConfig, viteAliases?: Alias[]) {\n this.config = config;\n this.viteAliases = viteAliases ?? config.getVite()?.config?.resolve.alias;\n }\n\n /**\n * Get vite aliases\n */\n public getAliases(): Record<string, string> {\n const aliases: Record<string, string> = {};\n\n this.viteAliases?.forEach(({ find, replacement }) => {\n if (typeof find !== 'string') {\n return;\n }\n\n aliases[find] = replacement;\n });\n\n return aliases;\n }\n\n /**\n * Return filename postfix\n */\n public getImportPostfix(): string[] {\n return ['', '/index']\n .map((prefix) => ['', '.js', '.ts', '.tsx'].map((ext) => `${prefix}${ext}`))\n .flat();\n }\n\n /**\n * Resolve app path\n */\n public getAppPath(appPath?: string, withRoot = false): string | undefined {\n if (!appPath) {\n return;\n }\n\n const { root } = this.config.getParams();\n let fullPath = appPath;\n\n // relative import\n if (appPath.startsWith('./') || appPath.startsWith('../')) {\n fullPath = path.resolve(root, appPath);\n } else {\n // alias import\n const aliases = this.getAliases();\n // get alias\n const [routeAlias] = appPath.split('/');\n\n if (aliases[routeAlias]) {\n fullPath = appPath.replace(routeAlias, aliases[routeAlias]);\n }\n }\n\n // normalize slashes\n fullPath = fullPath.split(path.win32.sep).join(path.posix.sep);\n\n if (withRoot) {\n return fullPath;\n }\n\n return fullPath.replace(root, '').replace(/^(\\/)|(\\/)$/g, '');\n }\n\n /**\n * Find app filepath\n */\n public findAppFile(basePath: string): string | null {\n const postfixes = this.getImportPostfix();\n\n for (const postfix of postfixes) {\n const filepath = `${basePath}${postfix}`;\n\n if (fs.existsSync(filepath) && fs.statSync(filepath).isFile()) {\n return filepath;\n }\n }\n\n return null;\n }\n}\n\nexport default PathNormalize;\n"],"names":["PathNormalize","viteAliases","config","constructor","this","getVite","resolve","alias","getAliases","aliases","forEach","find","replacement","getImportPostfix","map","prefix","ext","flat","getAppPath","appPath","withRoot","root","getParams","fullPath","startsWith","path","routeAlias","split","replace","win32","sep","join","posix","findAppFile","basePath","postfixes","postfix","filepath","fs","existsSync","statSync","isFile"],"mappings":"2CAQA,MAAMA,EAIeC,YAKAC,OAKnBC,YAAYD,EAAsBD,GAChCG,KAAKF,OAASA,EACdE,KAAKH,YAAcA,GAAeC,EAAOG,WAAWH,QAAQI,QAAQC,KACrE,CAKMC,aACL,MAAMC,EAAkC,CAAA,EAUxC,OARAL,KAAKH,aAAaS,SAAQ,EAAGC,OAAMC,kBACb,iBAATD,IAIXF,EAAQE,GAAQC,EAAW,IAGtBH,CACR,CAKMI,mBACL,MAAO,CAAC,GAAI,UACTC,KAAKC,GAAW,CAAC,GAAI,MAAO,MAAO,QAAQD,KAAKE,GAAQ,GAAGD,IAASC,QACpEC,MACJ,CAKMC,WAAWC,EAAkBC,GAAW,GAC7C,IAAKD,EACH,OAGF,MAAME,KAAEA,GAASjB,KAAKF,OAAOoB,YAC7B,IAAIC,EAAWJ,EAGf,GAAIA,EAAQK,WAAW,OAASL,EAAQK,WAAW,OACjDD,EAAWE,EAAKnB,QAAQe,EAAMF,OACzB,CAEL,MAAMV,EAAUL,KAAKI,cAEdkB,GAAcP,EAAQQ,MAAM,KAE/BlB,EAAQiB,KACVH,EAAWJ,EAAQS,QAAQF,EAAYjB,EAAQiB,IAElD,CAKD,OAFAH,EAAWA,EAASI,MAAMF,EAAKI,MAAMC,KAAKC,KAAKN,EAAKO,MAAMF,KAEtDV,EACKG,EAGFA,EAASK,QAAQP,EAAM,IAAIO,QAAQ,eAAgB,GAC3D,CAKMK,YAAYC,GACjB,MAAMC,EAAY/B,KAAKS,mBAEvB,IAAK,MAAMuB,KAAWD,EAAW,CAC/B,MAAME,EAAW,GAAGH,IAAWE,IAE/B,GAAIE,EAAGC,WAAWF,IAAaC,EAAGE,SAASH,GAAUI,SACnD,OAAOJ,CAEV,CAED,OAAO,IACR"}
@@ -15,6 +15,7 @@ interface IConfigParams {
15
15
  isProd: boolean;
16
16
  isSPA: boolean;
17
17
  indexFile: string;
18
+ clientFile: string;
18
19
  serverFile: string;
19
20
  host: string;
20
21
  port: number;
@@ -1,2 +1,2 @@
1
- import i from"node:fs";import t from"node:path";import s from"../helpers/plugin-config.js";import r from"./logger.js";class e{isProd;isHost;isModulePreload;isSPA;mode;vite;app;params;prodParams;logger;defaultBuildRoots=["./build","./dist"];constructor({isProd:i=!1,isHost:t=!1,isOnlyClient:s=!1,isModulePreload:r=!1,mode:e="production"},o){this.isProd=i,this.isHost=t,this.isSPA=s,this.isModulePreload=r,this.mode=e,this.prodParams={publicDir:"/client",indexFile:"/client/index.html",serverFile:"/server/server.js",host:"127.0.0.1",port:3e3,...o},this.makeParams()}static init(i={},t={}){return new e(i,t)}getBuildDir(t){for(const s of[t,...this.defaultBuildRoots])if(s&&i.existsSync(s))return s;return t??this.defaultBuildRoots[0]}makeParams(){const i=this.getPluginConfig()??{},{config:s}=this.vite??{},e=s?.root??this.getBuildDir(this.prodParams.root),o=s?.publicDir??this.prodParams.publicDir,a=new URL(import.meta.url),l=i.pluginPath??t.resolve(t.dirname(a.pathname),"../"),h=i.indexFile??this.prodParams.indexFile,d=i.serverFile??this.prodParams.serverFile,p="boolean"==typeof s?.server.host||this.isHost?"0.0.0.0":s?.server.host??this.prodParams.host,n=s?.server.port??(this.isProd?this.prodParams.port:5173);this.params={root:e,publicDir:o,pluginPath:l,indexFile:h,serverFile:d,host:p,port:n,isSPA:this.isSPA,isProd:this.isProd},this.logger=this.vite?.config.logger??new r}setVite(i){this.vite=i,this.makeParams()}setApp(i){this.app=i}getVite(){return this.vite}getApp(){return this.app}getPluginConfig(){return this.vite?s(this.vite.config):void 0}getParams(){return this.params}getLogger(){return this.logger}}export{e as default};
1
+ import i from"node:fs";import t from"node:path";import s from"../helpers/plugin-config.js";import e from"./logger.js";class r{isProd;isHost;isModulePreload;isSPA;mode;vite;app;params;prodParams;logger;defaultBuildRoots=["./build","./dist"];constructor({isProd:i=!1,isHost:t=!1,isOnlyClient:s=!1,isModulePreload:e=!1,mode:r="production"},o){this.isProd=i,this.isHost=t,this.isSPA=s,this.isModulePreload=e,this.mode=r,this.prodParams={publicDir:"/client",indexFile:"/client/index.html",serverFile:"/server/server.js",host:"127.0.0.1",port:3e3,...o},this.makeParams()}static init(i={},t={}){return new r(i,t)}getBuildDir(t){for(const s of[t,...this.defaultBuildRoots])if(s&&i.existsSync(s))return s;return t??this.defaultBuildRoots[0]}makeParams(){const i=this.getPluginConfig()??{},{config:s}=this.vite??{},r=s?.root??this.getBuildDir(this.prodParams.root),o=s?.publicDir??this.prodParams.publicDir,a=new URL(import.meta.url),l=i.pluginPath??t.resolve(t.dirname(a.pathname),"../"),h=i.indexFile??this.prodParams.indexFile,d=i.serverFile??this.prodParams.serverFile,n=i.clientFile??this.prodParams.clientFile,p="boolean"==typeof s?.server.host||this.isHost?"0.0.0.0":s?.server.host??this.prodParams.host,m=s?.server.port??(this.isProd?this.prodParams.port:5173);this.params={root:r,publicDir:o,pluginPath:l,indexFile:h,clientFile:n,serverFile:d,host:p,port:m,isSPA:this.isSPA,isProd:this.isProd},this.logger=this.vite?.config.logger??new e}setVite(i){this.vite=i,this.makeParams()}setApp(i){this.app=i}getVite(){return this.vite}getApp(){return this.app}getPluginConfig(){return this.vite?s(this.vite.config):void 0}getParams(){return this.params}getLogger(){return this.logger}}export{r as default};
2
2
  //# sourceMappingURL=server-config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-config.js","sources":["../../src/services/server-config.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Express } from 'express';\nimport type { Logger, ViteDevServer } from 'vite';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport DefaultLogger from '@services/logger';\n\ninterface IConfigOptions {\n isProd?: boolean;\n isHost?: boolean;\n isOnlyClient?: boolean; // SPA mode\n isModulePreload?: boolean;\n mode?: string;\n}\n\ninterface IConfigParams {\n root: string;\n publicDir: string;\n pluginPath: string;\n isProd: boolean;\n isSPA: boolean;\n indexFile: string;\n serverFile: string;\n host: string;\n port: number;\n}\n\n/**\n * Server config\n */\nclass ServerConfig {\n /**\n * Production build\n */\n public readonly isProd: boolean;\n\n /**\n * Server host mode\n */\n public readonly isHost: boolean;\n\n /**\n * Add module preload scripts to server output\n */\n public readonly isModulePreload: boolean;\n\n /**\n * SPA mode\n */\n public readonly isSPA: boolean;\n\n /**\n * Env mode\n */\n public readonly mode: string;\n\n /**\n * Vite config - only for development\n */\n protected vite?: ViteDevServer;\n\n /**\n * Express application\n */\n protected app?: Express;\n\n /**\n * Config params\n */\n protected params: IConfigParams;\n\n /**\n * Default production params\n */\n protected prodParams: Partial<IConfigParams>;\n\n /**\n * Vite logger for dev mode or console for production\n */\n protected logger: Logger;\n\n /**\n * Default root dir\n */\n protected defaultBuildRoots = ['./build', './dist'];\n\n /**\n * @constructor\n */\n protected constructor(\n {\n isProd = false,\n isHost = false,\n isOnlyClient = false,\n isModulePreload = false,\n mode = 'production',\n }: IConfigOptions,\n prodParams: Partial<IConfigParams>,\n ) {\n this.isProd = isProd;\n this.isHost = isHost;\n this.isSPA = isOnlyClient;\n this.isModulePreload = isModulePreload;\n this.mode = mode;\n this.prodParams = {\n publicDir: '/client', // default for production,\n indexFile: '/client/index.html',\n serverFile: '/server/server.js',\n host: '127.0.0.1',\n port: 3000,\n ...prodParams,\n };\n\n this.makeParams();\n }\n\n /**\n * Initialize service\n */\n public static init(\n options: IConfigOptions = {},\n prodOptions: Partial<IConfigParams> = {},\n ): ServerConfig {\n return new ServerConfig(options, prodOptions);\n }\n\n /**\n * Lookup build folder\n */\n protected getBuildDir(root?: string): string {\n for (const dir of [root, ...this.defaultBuildRoots]) {\n if (dir && fs.existsSync(dir)) {\n return dir;\n }\n }\n\n return root ?? this.defaultBuildRoots[0];\n }\n\n /**\n * Make config params\n */\n protected makeParams(): void {\n const pluginConfig = (this.getPluginConfig() ?? {}) as Partial<IPluginConfig>;\n const { config } = this.vite ?? {};\n\n const root = config?.root ?? this.getBuildDir(this.prodParams.root);\n const publicDir = config?.publicDir ?? this.prodParams.publicDir!;\n const dirInfo = new URL(import.meta.url);\n const pluginPath =\n pluginConfig.pluginPath ?? path.resolve(path.dirname(dirInfo.pathname), '../');\n const indexFile = pluginConfig.indexFile ?? this.prodParams.indexFile!;\n const serverFile = pluginConfig.serverFile ?? this.prodParams.serverFile!;\n const host =\n typeof config?.server.host === 'boolean' || this.isHost\n ? '0.0.0.0'\n : config?.server.host ?? this.prodParams.host!;\n const port = config?.server.port ?? (this.isProd ? this.prodParams.port! : 5173);\n\n this.params = {\n root,\n publicDir,\n pluginPath,\n indexFile,\n serverFile,\n host,\n port,\n isSPA: this.isSPA,\n isProd: this.isProd,\n };\n this.logger = this.vite?.config.logger ?? new DefaultLogger();\n }\n\n /**\n * Set vite server\n */\n public setVite(vite: ViteDevServer): void {\n this.vite = vite;\n\n this.makeParams();\n }\n\n /**\n * Set express server\n */\n public setApp(express: Express): void {\n this.app = express;\n }\n\n /**\n * Return vite dev server\n * NOTE: only on development mode\n */\n public getVite(): ViteDevServer | undefined {\n return this.vite;\n }\n\n /**\n * Return express server\n */\n public getApp(): Express | undefined {\n return this.app;\n }\n\n /**\n * return plugin config\n * NOTE: only on development mode\n */\n public getPluginConfig(): IPluginConfig | undefined {\n return this.vite ? getPluginConfig(this.vite.config) : undefined;\n }\n\n /**\n * Return config params\n */\n public getParams(): IConfigParams {\n return this.params;\n }\n\n /**\n * Get server logger\n */\n public getLogger(): Logger {\n return this.logger;\n }\n}\n\nexport default ServerConfig;\n"],"names":["ServerConfig","isProd","isHost","isModulePreload","isSPA","mode","vite","app","params","prodParams","logger","defaultBuildRoots","constructor","isOnlyClient","this","publicDir","indexFile","serverFile","host","port","makeParams","static","options","prodOptions","getBuildDir","root","dir","fs","existsSync","pluginConfig","getPluginConfig","config","dirInfo","URL","url","pluginPath","path","resolve","dirname","pathname","server","DefaultLogger","setVite","setApp","express","getVite","getApp","undefined","getParams","getLogger"],"mappings":"sHA+BA,MAAMA,EAIYC,OAKAC,OAKAC,gBAKAC,MAKAC,KAKNC,KAKAC,IAKAC,OAKAC,WAKAC,OAKAC,kBAAoB,CAAC,UAAW,UAK1CC,aACEX,OACEA,GAAS,EAAKC,OACdA,GAAS,EAAKW,aACdA,GAAe,EAAKV,gBACpBA,GAAkB,EAAKE,KACvBA,EAAO,cAETI,GAEAK,KAAKb,OAASA,EACda,KAAKZ,OAASA,EACdY,KAAKV,MAAQS,EACbC,KAAKX,gBAAkBA,EACvBW,KAAKT,KAAOA,EACZS,KAAKL,WAAa,CAChBM,UAAW,UACXC,UAAW,qBACXC,WAAY,oBACZC,KAAM,YACNC,KAAM,OACHV,GAGLK,KAAKM,YACN,CAKMC,YACLC,EAA0B,GAC1BC,EAAsC,CAAA,GAEtC,OAAO,IAAIvB,EAAasB,EAASC,EAClC,CAKSC,YAAYC,GACpB,IAAK,MAAMC,IAAO,CAACD,KAASX,KAAKH,mBAC/B,GAAIe,GAAOC,EAAGC,WAAWF,GACvB,OAAOA,EAIX,OAAOD,GAAQX,KAAKH,kBAAkB,EACvC,CAKSS,aACR,MAAMS,EAAgBf,KAAKgB,mBAAqB,CAAE,GAC5CC,OAAEA,GAAWjB,KAAKR,MAAQ,CAAA,EAE1BmB,EAAOM,GAAQN,MAAQX,KAAKU,YAAYV,KAAKL,WAAWgB,MACxDV,EAAYgB,GAAQhB,WAAaD,KAAKL,WAAWM,UACjDiB,EAAU,IAAIC,gBAAgBC,KAC9BC,EACJN,EAAaM,YAAcC,EAAKC,QAAQD,EAAKE,QAAQN,EAAQO,UAAW,OACpEvB,EAAYa,EAAab,WAAaF,KAAKL,WAAWO,UACtDC,EAAaY,EAAaZ,YAAcH,KAAKL,WAAWQ,WACxDC,EAC2B,kBAAxBa,GAAQS,OAAOtB,MAAsBJ,KAAKZ,OAC7C,UACA6B,GAAQS,OAAOtB,MAAQJ,KAAKL,WAAWS,KACvCC,EAAOY,GAAQS,OAAOrB,OAASL,KAAKb,OAASa,KAAKL,WAAWU,KAAQ,MAE3EL,KAAKN,OAAS,CACZiB,OACAV,YACAoB,aACAnB,YACAC,aACAC,OACAC,OACAf,MAAOU,KAAKV,MACZH,OAAQa,KAAKb,QAEfa,KAAKJ,OAASI,KAAKR,MAAMyB,OAAOrB,QAAU,IAAI+B,CAC/C,CAKMC,QAAQpC,GACbQ,KAAKR,KAAOA,EAEZQ,KAAKM,YACN,CAKMuB,OAAOC,GACZ9B,KAAKP,IAAMqC,CACZ,CAMMC,UACL,OAAO/B,KAAKR,IACb,CAKMwC,SACL,OAAOhC,KAAKP,GACb,CAMMuB,kBACL,OAAOhB,KAAKR,KAAOwB,EAAgBhB,KAAKR,KAAKyB,aAAUgB,CACxD,CAKMC,YACL,OAAOlC,KAAKN,MACb,CAKMyC,YACL,OAAOnC,KAAKJ,MACb"}
1
+ {"version":3,"file":"server-config.js","sources":["../../src/services/server-config.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Express } from 'express';\nimport type { Logger, ViteDevServer } from 'vite';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport DefaultLogger from '@services/logger';\n\ninterface IConfigOptions {\n isProd?: boolean;\n isHost?: boolean;\n isOnlyClient?: boolean; // SPA mode\n isModulePreload?: boolean;\n mode?: string;\n}\n\ninterface IConfigParams {\n root: string;\n publicDir: string;\n pluginPath: string;\n isProd: boolean;\n isSPA: boolean;\n indexFile: string;\n clientFile: string;\n serverFile: string;\n host: string;\n port: number;\n}\n\n/**\n * Server config\n */\nclass ServerConfig {\n /**\n * Production build\n */\n public readonly isProd: boolean;\n\n /**\n * Server host mode\n */\n public readonly isHost: boolean;\n\n /**\n * Add module preload scripts to server output\n */\n public readonly isModulePreload: boolean;\n\n /**\n * SPA mode\n */\n public readonly isSPA: boolean;\n\n /**\n * Env mode\n */\n public readonly mode: string;\n\n /**\n * Vite config - only for development\n */\n protected vite?: ViteDevServer;\n\n /**\n * Express application\n */\n protected app?: Express;\n\n /**\n * Config params\n */\n protected params: IConfigParams;\n\n /**\n * Default production params\n */\n protected prodParams: Partial<IConfigParams>;\n\n /**\n * Vite logger for dev mode or console for production\n */\n protected logger: Logger;\n\n /**\n * Default root dir\n */\n protected defaultBuildRoots = ['./build', './dist'];\n\n /**\n * @constructor\n */\n protected constructor(\n {\n isProd = false,\n isHost = false,\n isOnlyClient = false,\n isModulePreload = false,\n mode = 'production',\n }: IConfigOptions,\n prodParams: Partial<IConfigParams>,\n ) {\n this.isProd = isProd;\n this.isHost = isHost;\n this.isSPA = isOnlyClient;\n this.isModulePreload = isModulePreload;\n this.mode = mode;\n this.prodParams = {\n publicDir: '/client', // default for production,\n indexFile: '/client/index.html',\n serverFile: '/server/server.js',\n host: '127.0.0.1',\n port: 3000,\n ...prodParams,\n };\n\n this.makeParams();\n }\n\n /**\n * Initialize service\n */\n public static init(\n options: IConfigOptions = {},\n prodOptions: Partial<IConfigParams> = {},\n ): ServerConfig {\n return new ServerConfig(options, prodOptions);\n }\n\n /**\n * Lookup build folder\n */\n protected getBuildDir(root?: string): string {\n for (const dir of [root, ...this.defaultBuildRoots]) {\n if (dir && fs.existsSync(dir)) {\n return dir;\n }\n }\n\n return root ?? this.defaultBuildRoots[0];\n }\n\n /**\n * Make config params\n */\n protected makeParams(): void {\n const pluginConfig = (this.getPluginConfig() ?? {}) as Partial<IPluginConfig>;\n const { config } = this.vite ?? {};\n\n const root = config?.root ?? this.getBuildDir(this.prodParams.root);\n const publicDir = config?.publicDir ?? this.prodParams.publicDir!;\n const dirInfo = new URL(import.meta.url);\n const pluginPath =\n pluginConfig.pluginPath ?? path.resolve(path.dirname(dirInfo.pathname), '../');\n const indexFile = pluginConfig.indexFile ?? this.prodParams.indexFile!;\n const serverFile = pluginConfig.serverFile ?? this.prodParams.serverFile!;\n const clientFile = pluginConfig.clientFile ?? this.prodParams.clientFile!;\n const host =\n typeof config?.server.host === 'boolean' || this.isHost\n ? '0.0.0.0'\n : config?.server.host ?? this.prodParams.host!;\n const port = config?.server.port ?? (this.isProd ? this.prodParams.port! : 5173);\n\n this.params = {\n root,\n publicDir,\n pluginPath,\n indexFile,\n clientFile,\n serverFile,\n host,\n port,\n isSPA: this.isSPA,\n isProd: this.isProd,\n };\n this.logger = this.vite?.config.logger ?? new DefaultLogger();\n }\n\n /**\n * Set vite server\n */\n public setVite(vite: ViteDevServer): void {\n this.vite = vite;\n\n this.makeParams();\n }\n\n /**\n * Set express server\n */\n public setApp(express: Express): void {\n this.app = express;\n }\n\n /**\n * Return vite dev server\n * NOTE: only on development mode\n */\n public getVite(): ViteDevServer | undefined {\n return this.vite;\n }\n\n /**\n * Return express server\n */\n public getApp(): Express | undefined {\n return this.app;\n }\n\n /**\n * return plugin config\n * NOTE: only on development mode\n */\n public getPluginConfig(): IPluginConfig | undefined {\n return this.vite ? getPluginConfig(this.vite.config) : undefined;\n }\n\n /**\n * Return config params\n */\n public getParams(): IConfigParams {\n return this.params;\n }\n\n /**\n * Get server logger\n */\n public getLogger(): Logger {\n return this.logger;\n }\n}\n\nexport default ServerConfig;\n"],"names":["ServerConfig","isProd","isHost","isModulePreload","isSPA","mode","vite","app","params","prodParams","logger","defaultBuildRoots","constructor","isOnlyClient","this","publicDir","indexFile","serverFile","host","port","makeParams","static","options","prodOptions","getBuildDir","root","dir","fs","existsSync","pluginConfig","getPluginConfig","config","dirInfo","URL","url","pluginPath","path","resolve","dirname","pathname","clientFile","server","DefaultLogger","setVite","setApp","express","getVite","getApp","undefined","getParams","getLogger"],"mappings":"sHAgCA,MAAMA,EAIYC,OAKAC,OAKAC,gBAKAC,MAKAC,KAKNC,KAKAC,IAKAC,OAKAC,WAKAC,OAKAC,kBAAoB,CAAC,UAAW,UAK1CC,aACEX,OACEA,GAAS,EAAKC,OACdA,GAAS,EAAKW,aACdA,GAAe,EAAKV,gBACpBA,GAAkB,EAAKE,KACvBA,EAAO,cAETI,GAEAK,KAAKb,OAASA,EACda,KAAKZ,OAASA,EACdY,KAAKV,MAAQS,EACbC,KAAKX,gBAAkBA,EACvBW,KAAKT,KAAOA,EACZS,KAAKL,WAAa,CAChBM,UAAW,UACXC,UAAW,qBACXC,WAAY,oBACZC,KAAM,YACNC,KAAM,OACHV,GAGLK,KAAKM,YACN,CAKMC,YACLC,EAA0B,GAC1BC,EAAsC,CAAA,GAEtC,OAAO,IAAIvB,EAAasB,EAASC,EAClC,CAKSC,YAAYC,GACpB,IAAK,MAAMC,IAAO,CAACD,KAASX,KAAKH,mBAC/B,GAAIe,GAAOC,EAAGC,WAAWF,GACvB,OAAOA,EAIX,OAAOD,GAAQX,KAAKH,kBAAkB,EACvC,CAKSS,aACR,MAAMS,EAAgBf,KAAKgB,mBAAqB,CAAE,GAC5CC,OAAEA,GAAWjB,KAAKR,MAAQ,CAAA,EAE1BmB,EAAOM,GAAQN,MAAQX,KAAKU,YAAYV,KAAKL,WAAWgB,MACxDV,EAAYgB,GAAQhB,WAAaD,KAAKL,WAAWM,UACjDiB,EAAU,IAAIC,gBAAgBC,KAC9BC,EACJN,EAAaM,YAAcC,EAAKC,QAAQD,EAAKE,QAAQN,EAAQO,UAAW,OACpEvB,EAAYa,EAAab,WAAaF,KAAKL,WAAWO,UACtDC,EAAaY,EAAaZ,YAAcH,KAAKL,WAAWQ,WACxDuB,EAAaX,EAAaW,YAAc1B,KAAKL,WAAW+B,WACxDtB,EAC2B,kBAAxBa,GAAQU,OAAOvB,MAAsBJ,KAAKZ,OAC7C,UACA6B,GAAQU,OAAOvB,MAAQJ,KAAKL,WAAWS,KACvCC,EAAOY,GAAQU,OAAOtB,OAASL,KAAKb,OAASa,KAAKL,WAAWU,KAAQ,MAE3EL,KAAKN,OAAS,CACZiB,OACAV,YACAoB,aACAnB,YACAwB,aACAvB,aACAC,OACAC,OACAf,MAAOU,KAAKV,MACZH,OAAQa,KAAKb,QAEfa,KAAKJ,OAASI,KAAKR,MAAMyB,OAAOrB,QAAU,IAAIgC,CAC/C,CAKMC,QAAQrC,GACbQ,KAAKR,KAAOA,EAEZQ,KAAKM,YACN,CAKMwB,OAAOC,GACZ/B,KAAKP,IAAMsC,CACZ,CAMMC,UACL,OAAOhC,KAAKR,IACb,CAKMyC,SACL,OAAOjC,KAAKP,GACb,CAMMuB,kBACL,OAAOhB,KAAKR,KAAOwB,EAAgBhB,KAAKR,KAAKyB,aAAUiB,CACxD,CAKMC,YACL,OAAOnC,KAAKN,MACb,CAKM0C,YACL,OAAOpC,KAAKJ,MACb"}
@@ -4,6 +4,8 @@ import { AgnosticDataRouteMatch } from '@remix-run/router/dist/utils';
4
4
  import { RouteObject } from 'react-router-dom';
5
5
  import { Alias, ModuleNode } from 'vite';
6
6
  import { IRequestContext } from "../node/render.js";
7
+ import { TRoutesTree } from "./parse-routes.js";
8
+ import PathNormalize from "./path-normalize.js";
7
9
  import ServerConfig from "./server-config.js";
8
10
  interface ISsrManifestParams {
9
11
  buildDir?: string;
@@ -47,6 +49,10 @@ declare class SsrManifest {
47
49
  * Server config
48
50
  */
49
51
  protected readonly config: ServerConfig;
52
+ /**
53
+ * Path normalize service
54
+ */
55
+ protected readonly pathNormalize: PathNormalize;
50
56
  /**
51
57
  * Project root path
52
58
  */
@@ -119,7 +125,14 @@ declare class SsrManifest {
119
125
  /**
120
126
  * Recursive walk routes and return id's with route import path
121
127
  */
122
- protected getRoutesIds(routes: RouteObject[], index?: string): Promise<Record<string, string | undefined>>;
128
+ protected getAsyncRoutesIds(routes: RouteObject[], index?: string): Promise<Record<string, string | undefined>>;
129
+ /**
130
+ * Same as 'getAsyncRoutesIds' but for routes tree from 'ParseRoutes'
131
+ */
132
+ /**
133
+ * Same as 'getAsyncRoutesIds' but for routes tree from 'ParseRoutes'
134
+ */
135
+ protected getRoutesTreeIds(routes: TRoutesTree[], index?: string): Record<string, string | undefined>;
123
136
  /**
124
137
  * Sort assets
125
138
  */
@@ -140,28 +153,7 @@ declare class SsrManifest {
140
153
  /**
141
154
  * Build routes manifest file
142
155
  */
143
- buildRoutesManifest(): Promise<void>;
144
- /**
145
- * Get vite aliases
146
- */
147
- /**
148
- * Get vite aliases
149
- */
150
- protected getAliases(): Record<string, string>;
151
- /**
152
- * Return route postfix
153
- */
154
- /**
155
- * Return route postfix
156
- */
157
- protected getRouteImportPostfix(): string[];
158
- /**
159
- * Normalized route path
160
- */
161
- /**
162
- * Normalized route path
163
- */
164
- protected normalizeRoutePath(routePath?: string, withRoot?: boolean): string | undefined;
156
+ buildRoutesManifest(isNodeParsing: boolean): Promise<void>;
165
157
  /**
166
158
  * Get route assets
167
159
  */
@@ -1,2 +1,2 @@
1
- import t from"node:fs";import e from"node:path";import s from"chalk";import i from"./prepare-server.js";import r from"./server-config.js";var o;!function(t){t.style="style",t.script="script",t.image="image",t.font="font"}(o||(o={}));const n="\r\n";class a{static instance=null;config;root;buildDir;manifestName="manifest.json";assetsManifest="assets-manifest.json";viteAliases;routesAssets=null;constructor(t,{buildDir:e,viteAliases:s}={}){this.config=t,this.root=t.getParams().root,this.buildDir=e,this.viteAliases=s??t.getVite()?.config?.resolve.alias}static get(t,e={}){return null===a.instance&&(a.instance=new a(t,e)),a.instance}getOutDir(){return e.resolve(this.root,this.buildDir||"")}getAssetsManifestFile(){return`${this.getOutDir()}/server/${this.assetsManifest}`}loadClientManifest(){const s=e.resolve(this.root,`${this.buildDir||""}/client/.vite`),i=`${s}/${this.manifestName}`;if(!t.existsSync(i))return{};const r=JSON.parse(t.readFileSync(i,{encoding:"utf-8"}));return t.rmSync(i),0===t.readdirSync(s).length&&t.rmSync(s,{recursive:!0}),r}loadAssetsManifest(){if(null!==this.routesAssets)return this.routesAssets;const e=this.getAssetsManifestFile();return t.existsSync(e)?(this.routesAssets=JSON.parse(t.readFileSync(e,{encoding:"utf-8"})),this.routesAssets):{}}async getRoutesIds(t,e){const i={};for(const r in t){const o=t[r],n=[e,r].filter(Boolean).join("-");if(o.lazy)try{const t=await o.lazy();i[n]=this.normalizeRoutePath(t?.pathId)}catch(t){console.error(s.red("Failed to load route:"),o.path,t)}else o.children&&Object.assign(i,await this.getRoutesIds(o.children,n))}return i}sortAssets(t){return t.sort(((t,e)=>t.weight===e.weight?Number(t.isNested)-Number(e.isNested):t.weight-e.weight))}getRouteAssets(t,e,s=!1){const i=[...e?.assets??[],...e?.css??[],e?.file].reduce(((t,i)=>{if(i){const r=this.getAssetType(i),o=e.isEntry&&e.file===i;r&&(t[i]={url:`/${i}`,weight:o?1.9:this.getAssetWeight(i),type:r,isNested:s,isPreload:!o})}return t}),{});return e?.imports?.length&&e.imports.forEach((e=>{const s=t[e];s&&Object.assign(i,this.getRouteAssets(t,s,!0))})),i}async buildRoutesManifest(){const e=i.init(r.init({isProd:!0},{root:this.getOutDir()})),s=this.loadClientManifest(),{routes:o}=await e.loadEntrypoint(!1),n=await this.getRoutesIds(o),a=this.getRouteImportPostfix(),l={};Object.entries(n).forEach((([t,e])=>{const i=a.find((t=>void 0!==s[`${e}${t}`])),r=s[`${e}${i||""}`];l[t]=this.sortAssets(Object.values(this.getRouteAssets(s,r)))})),t.writeFileSync(this.getAssetsManifestFile(),JSON.stringify(l,null,2),{encoding:"utf-8"})}getAliases(){const t={};return this.viteAliases?.forEach((({find:e,replacement:s})=>{"string"==typeof e&&(t[e]=s)})),t}getRouteImportPostfix(){return["","/index"].map((t=>["",".js",".ts",".tsx"].map((e=>`${t}${e}`)))).flat()}normalizeRoutePath(t,s=!1){if(!t)return;let i="";if(t.startsWith("./")||t.startsWith("../"))i=e.resolve(this.root,t);else{const e=this.getAliases(),[s]=t.split("/");e[s]&&(i=t.replace(s,e[s]))}return i=i.split(e.win32.sep).join(e.posix.sep),s?i:i.replace(this.root,"").replace(/^(\/)|(\/)$/g,"")}getAssets(t){if(this.config.getVite())return this.getAssetsDev(t);const e=t?.map((({route:t})=>t.id)).filter(Boolean)??[];if(!e.length)return[];const s=this.loadAssetsManifest();return this.sortAssets(e.map((t=>s[t])).flat().filter(Boolean))}getAssetsDev(t){const e=t?.map((({route:t})=>this.normalizeRoutePath(t?.pathId,!0))).filter(Boolean)??[];if(!e.length)return[];let s={};const i=this.getRouteImportPostfix();return[`${this.root}/${this.config.getPluginConfig()?.clientFile??"client.ts"}`,...e].forEach((t=>{for(const e of i){const i=this.config.getVite()?.moduleGraph.getModuleById(`${t}${e}`);if(i){s={...s,...this.getModuleAssets(i)};break}}})),Object.values(s)}getModuleAssets(t,e=new Set){if(!t?.clientImportedModules.size||e.has(t.file))return{};let i={};return e.add(t.file),t.clientImportedModules.forEach((t=>{const{file:r,clientImportedModules:n,transformResult:a}=t,l=r?.split(".").at(-1);if(r&&l&&["css","scss"].includes(l)){const t=a?.code.match(/__vite__css\s+=\s+"(?<css>.+)"/)?.groups?.css;if(t)try{i[r]={type:o.style,url:r,weight:this.getAssetWeight(r),content:JSON.parse(`{"style": "${t}"}`).style,isNested:Boolean(e.size),isPreload:!1}}catch(t){console.warn(s.yellowBright("Failed to parse style: ",r))}}else n.size&&(i={...i,...this.getModuleAssets(t,e)})})),i}getAssetWeight(t){switch(this.getAssetType(t)){case o.style:return 1;case o.script:return 2;default:return 3}}getAssetType(t){const e=t.split(".").at(-1)?.toLowerCase();switch(e){case"css":case"scss":return o.style;case"js":return o.script;case"svg":case"jpg":case"jpeg":case"png":case"webp":case"gif":case"ico":return o.image;case"ttf":case"otf":case"woff":case"woff2":return o.font;default:return null}}writeEarlyHits(t,e){e.write(`HTTP/1.1 103 Early Hints${n}`),t.forEach((({type:t,url:s})=>{t&&["style","script"].includes(t)&&e.write(`Link: <${s}>; rel=preload; as=${t}${n}`)})),e.write(n)}injectAssets({routerContext:t,html:e,res:s,hasEarlyHints:i=!1}){const r=this.getAssets(t?.matches),n=r.map((({type:t,url:e,isPreload:s,content:i=""})=>{switch(t){case o.style:return this.config.getVite()?`<style data-vite-dev-id="${e}">${i}</style>`:`<link rel="stylesheet" href="${e}">`;case o.script:return s?this.config.isModulePreload?`<link rel="modulepreload" as="script" crossorigin href="${e}">`:null:`<script async type="module" crossorigin src="${e}"><\/script>`}return null})).filter(Boolean);e.header=e.header.replace("</head>",`${n.join("\n")}</head>`),i&&n.length&&s.socket&&this.writeEarlyHits(r,s.socket)}}export{a as default};
1
+ import t from"node:fs";import e from"node:path";import s from"chalk";import i from"./parse-routes.js";import r from"./path-normalize.js";import o from"./prepare-server.js";import n from"./server-config.js";var a;!function(t){t.style="style",t.script="script",t.image="image",t.font="font"}(a||(a={}));const l="\r\n";class c{static instance=null;config;pathNormalize;root;buildDir;manifestName="manifest.json";assetsManifest="assets-manifest.json";viteAliases;routesAssets=null;constructor(t,{buildDir:e,viteAliases:s}={}){this.config=t,this.root=t.getParams().root,this.buildDir=e,this.viteAliases=s??t.getVite()?.config?.resolve.alias,this.pathNormalize=new r(t,s)}static get(t,e={}){return null===c.instance&&(c.instance=new c(t,e)),c.instance}getOutDir(){return e.resolve(this.root,this.buildDir||"")}getAssetsManifestFile(){return`${this.getOutDir()}/server/${this.assetsManifest}`}loadClientManifest(){const s=e.resolve(this.root,`${this.buildDir||""}/client/.vite`),i=`${s}/${this.manifestName}`;if(!t.existsSync(i))return{};const r=JSON.parse(t.readFileSync(i,{encoding:"utf-8"}));return t.rmSync(i),0===t.readdirSync(s).length&&t.rmSync(s,{recursive:!0}),r}loadAssetsManifest(){if(null!==this.routesAssets)return this.routesAssets;const e=this.getAssetsManifestFile();return t.existsSync(e)?(this.routesAssets=JSON.parse(t.readFileSync(e,{encoding:"utf-8"})),this.routesAssets):{}}async getAsyncRoutesIds(t,e){const i={};for(const r in t){const o=t[r],n=[e,r].filter(Boolean).join("-");if(o.lazy)try{const t=await o.lazy();i[n]=this.pathNormalize.getAppPath(t?.pathId)}catch(t){console.error(s.red("Failed to load route:"),o.path,t)}else o.children&&Object.assign(i,await this.getAsyncRoutesIds(o.children,n))}return i}getRoutesTreeIds(t,e){const s={};return t.forEach(((t,i)=>{const r=[e,String(i)].filter(Boolean).join("-");t.import&&(s[r]=this.pathNormalize.getAppPath(t.import)),t.children.length>0&&Object.assign(s,this.getRoutesTreeIds(t.children,r))})),s}sortAssets(t){return t.sort(((t,e)=>t.weight===e.weight?Number(t.isNested)-Number(e.isNested):t.weight-e.weight))}getRouteAssets(t,e,s=!1){const i=[...e?.assets??[],...e?.css??[],e?.file].reduce(((t,i)=>{if(i){const r=this.getAssetType(i),o=e.isEntry&&e.file===i;r&&(t[i]={url:`/${i}`,weight:o?1.9:this.getAssetWeight(i),type:r,isNested:s,isPreload:!o})}return t}),{});return e?.imports?.length&&e.imports.forEach((e=>{const s=t[e];s&&Object.assign(i,this.getRouteAssets(t,s,!0))})),i}async buildRoutesManifest(e){const s=o.init(n.init({isProd:!0},{root:this.getOutDir()})),r=this.loadClientManifest();let a;if(e){const{routes:t}=await s.loadEntrypoint(!1);a=await this.getAsyncRoutesIds(t)}else{const t=new i(this.config,this.viteAliases);a=this.getRoutesTreeIds(t.parse())}const l=this.pathNormalize.getImportPostfix(),c={};Object.entries(a).forEach((([t,e])=>{const s=l.find((t=>void 0!==r[`${e}${t}`])),i=r[`${e}${s||""}`];c[t]=this.sortAssets(Object.values(this.getRouteAssets(r,i)))})),t.writeFileSync(this.getAssetsManifestFile(),JSON.stringify(c,null,2),{encoding:"utf-8"})}getAssets(t){if(this.config.getVite())return this.getAssetsDev(t);const e=t?.map((({route:t})=>t.id)).filter(Boolean)??[];if(!e.length)return[];const s=this.loadAssetsManifest();return this.sortAssets(e.map((t=>s[t])).flat().filter(Boolean))}getAssetsDev(t){const e=t?.map((({route:t})=>this.pathNormalize.getAppPath(t?.pathId,!0))).filter(Boolean)??[];if(!e.length)return[];let s={};const i=this.pathNormalize.getImportPostfix();return[`${this.root}/${this.config.getPluginConfig()?.clientFile??"client.ts"}`,...e].forEach((t=>{for(const e of i){const i=this.config.getVite()?.moduleGraph.getModuleById(`${t}${e}`);if(i){s={...s,...this.getModuleAssets(i)};break}}})),Object.values(s)}getModuleAssets(t,e=new Set){if(!t?.clientImportedModules.size||e.has(t.file))return{};let i={};return e.add(t.file),t.clientImportedModules.forEach((t=>{const{file:r,clientImportedModules:o,transformResult:n}=t,l=r?.split(".").at(-1);if(r&&l&&["css","scss"].includes(l)){const t=n?.code.match(/__vite__css\s+=\s+"(?<css>.+)"/)?.groups?.css;if(t)try{i[r]={type:a.style,url:r,weight:this.getAssetWeight(r),content:JSON.parse(`{"style": "${t}"}`).style,isNested:Boolean(e.size),isPreload:!1}}catch(t){console.warn(s.yellowBright("Failed to parse style: ",r))}}else o.size&&(i={...i,...this.getModuleAssets(t,e)})})),i}getAssetWeight(t){switch(this.getAssetType(t)){case a.style:return 1;case a.script:return 2;default:return 3}}getAssetType(t){const e=t.split(".").at(-1)?.toLowerCase();switch(e){case"css":case"scss":return a.style;case"js":return a.script;case"svg":case"jpg":case"jpeg":case"png":case"webp":case"gif":case"ico":return a.image;case"ttf":case"otf":case"woff":case"woff2":return a.font;default:return null}}writeEarlyHits(t,e){e.write(`HTTP/1.1 103 Early Hints${l}`),t.forEach((({type:t,url:s})=>{t&&["style","script"].includes(t)&&e.write(`Link: <${s}>; rel=preload; as=${t}${l}`)})),e.write(l)}injectAssets({routerContext:t,html:e,res:s,hasEarlyHints:i=!1}){const r=this.getAssets(t?.matches),o=r.map((({type:t,url:e,isPreload:s,content:i=""})=>{switch(t){case a.style:return this.config.getVite()?`<style data-vite-dev-id="${e}">${i}</style>`:`<link rel="stylesheet" href="${e}">`;case a.script:return s?this.config.isModulePreload?`<link rel="modulepreload" as="script" crossorigin href="${e}">`:null:`<script async type="module" crossorigin src="${e}"><\/script>`}return null})).filter(Boolean);e.header=e.header.replace("</head>",`${o.join("\n")}</head>`),i&&o.length&&s.socket&&this.writeEarlyHits(r,s.socket)}}export{c 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 type { AgnosticDataRouteMatch } from '@remix-run/router/dist/utils';\nimport chalk from 'chalk';\nimport type { RouteObject } from 'react-router-dom';\nimport type { Alias, ModuleNode } from 'vite';\nimport type { IAsyncRoute } from '@helpers/import-route';\nimport type { IRequestContext } from '@node/render';\nimport PrepareServer from '@services/prepare-server';\nimport ServerConfig from '@services/server-config';\n\ninterface ISsrManifestParams {\n buildDir?: string;\n viteAliases?: Alias[];\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 * 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 * Loaded assets manifest file\n */\n protected routesAssets: Record<string, IAsset[]> | null = null;\n\n /**\n * @constructor\n */\n protected constructor(config: ServerConfig, { buildDir, viteAliases }: ISsrManifestParams = {}) {\n this.config = config;\n this.root = config.getParams().root;\n this.buildDir = buildDir;\n this.viteAliases = viteAliases ?? config.getVite()?.config?.resolve.alias;\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 * Recursive walk routes and return id's with route import path\n */\n protected async getRoutesIds(\n routes: RouteObject[],\n index?: string,\n ): Promise<Record<string, string | undefined>> {\n const result: Record<string, string | undefined> = {};\n\n // reason: await + array index\n // eslint-disable-next-line @typescript-eslint/no-for-in-array\n for (const routeIndex in routes) {\n const route = routes[routeIndex];\n const routeId = [index, routeIndex].filter(Boolean).join('-');\n\n if (route.lazy) {\n try {\n const resolvedRoute: IAsyncRoute = await route.lazy();\n\n result[routeId] = this.normalizeRoutePath(resolvedRoute?.pathId);\n } catch (e) {\n console.error(chalk.red('Failed to load route:'), route.path, e);\n }\n } else if (route.children) {\n Object.assign(result, await this.getRoutesIds(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: `/${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 async buildRoutesManifest(): Promise<void> {\n const prepareServer = PrepareServer.init(\n ServerConfig.init({ isProd: true }, { root: this.getOutDir() }),\n );\n const manifest = this.loadClientManifest();\n const { routes } = await prepareServer.loadEntrypoint(false);\n const routesPaths = await this.getRoutesIds(routes as RouteObject[]);\n const postfixes = this.getRouteImportPostfix();\n\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 vite aliases\n */\n protected getAliases(): Record<string, string> {\n const aliases: Record<string, string> = {};\n\n this.viteAliases?.forEach(({ find, replacement }) => {\n if (typeof find !== 'string') {\n return;\n }\n\n aliases[find] = replacement;\n });\n\n return aliases;\n }\n\n /**\n * Return route postfix\n */\n protected getRouteImportPostfix(): string[] {\n return ['', '/index']\n .map((prefix) => ['', '.js', '.ts', '.tsx'].map((ext) => `${prefix}${ext}`))\n .flat();\n }\n\n /**\n * Normalized route path\n */\n protected normalizeRoutePath(routePath?: string, withRoot = false): string | undefined {\n if (!routePath) {\n return;\n }\n\n let fullPath = '';\n\n // relative import\n if (routePath.startsWith('./') || routePath.startsWith('../')) {\n fullPath = path.resolve(this.root, routePath);\n } else {\n // alias import\n const aliases = this.getAliases();\n // get alias\n const [routeAlias] = routePath.split('/');\n\n if (aliases[routeAlias]) {\n fullPath = routePath.replace(routeAlias, aliases[routeAlias]);\n }\n }\n\n // normalize slashes\n fullPath = fullPath.split(path.win32.sep).join(path.posix.sep);\n\n if (withRoot) {\n return fullPath;\n }\n\n return fullPath.replace(this.root, '').replace(/^(\\/)|(\\/)$/g, '');\n }\n\n /**\n * Get route assets\n */\n protected getAssets(routes?: AgnosticDataRouteMatch[]): 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?: AgnosticDataRouteMatch[]): IAsset[] {\n const routeIds =\n (routes\n ?.map(({ route }) => this.normalizeRoutePath((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.getRouteImportPostfix();\n const rootId = `${this.root}/${this.config.getPluginConfig()?.clientFile ?? 'client.ts'}`;\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","root","buildDir","manifestName","assetsManifest","viteAliases","routesAssets","constructor","this","getParams","getVite","resolve","alias","params","instance","getOutDir","path","getAssetsManifestFile","loadClientManifest","clientManifestDir","clientSsrManifest","fs","existsSync","result","JSON","parse","readFileSync","encoding","rmSync","readdirSync","length","recursive","loadAssetsManifest","manifestFile","async","routes","index","routeIndex","route","routeId","filter","Boolean","join","lazy","resolvedRoute","normalizeRoutePath","pathId","e","console","error","chalk","red","children","Object","assign","getRoutesIds","sortAssets","assets","sort","a","b","weight","Number","isNested","getRouteAssets","manifest","module","css","file","reduce","res","asset","type","getAssetType","isEntry","url","getAssetWeight","isPreload","imports","forEach","nestedAsset","nestedModule","prepareServer","PrepareServer","init","ServerConfig","isProd","loadEntrypoint","routesPaths","postfixes","getRouteImportPostfix","entries","routePath","routePostfix","find","postfix","undefined","routeMeta","values","writeFileSync","stringify","getAliases","aliases","replacement","map","prefix","ext","flat","withRoot","fullPath","startsWith","routeAlias","split","replace","win32","sep","posix","getAssets","getAssetsDev","routeIds","id","getPluginConfig","clientFile","moduleId","moduleGraph","getModuleById","getModuleAssets","skipModules","Set","clientImportedModules","size","has","add","subModule","transformResult","at","includes","code","match","groups","style","content","warn","yellowBright","script","toLowerCase","image","font","writeEarlyHits","socket","write","injectAssets","routerContext","html","hasEarlyHints","matches","htmlAssets","isModulePreload","header"],"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 type { AgnosticDataRouteMatch } from '@remix-run/router/dist/utils';\nimport chalk from 'chalk';\nimport type { RouteObject } from 'react-router-dom';\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 PrepareServer from '@services/prepare-server';\nimport ServerConfig from '@services/server-config';\n\ninterface ISsrManifestParams {\n buildDir?: string;\n viteAliases?: Alias[];\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 * Loaded assets manifest file\n */\n protected routesAssets: Record<string, IAsset[]> | null = null;\n\n /**\n * @constructor\n */\n protected constructor(config: ServerConfig, { buildDir, viteAliases }: ISsrManifestParams = {}) {\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 }\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 * Recursive walk routes and return id's with route import path\n */\n protected async getAsyncRoutesIds(\n routes: RouteObject[],\n index?: string,\n ): Promise<Record<string, string | undefined>> {\n const result: Record<string, string | undefined> = {};\n\n // reason: await + array index\n // eslint-disable-next-line @typescript-eslint/no-for-in-array\n for (const routeIndex in routes) {\n const route = routes[routeIndex];\n const routeId = [index, routeIndex].filter(Boolean).join('-');\n\n if (route.lazy) {\n try {\n const resolvedRoute: IAsyncRoute = await route.lazy();\n\n result[routeId] = this.pathNormalize.getAppPath(resolvedRoute?.pathId);\n } catch (e) {\n console.error(chalk.red('Failed to load route:'), route.path, e);\n }\n } else if (route.children) {\n Object.assign(result, await this.getAsyncRoutesIds(route.children, routeId));\n }\n }\n\n return result;\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: `/${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 async buildRoutesManifest(isNodeParsing: boolean): Promise<void> {\n const prepareServer = PrepareServer.init(\n ServerConfig.init({ isProd: true }, { root: this.getOutDir() }),\n );\n const manifest = this.loadClientManifest();\n let routesPaths: Record<string, string | undefined>;\n\n if (isNodeParsing) {\n const { routes } = await prepareServer.loadEntrypoint(false);\n\n routesPaths = await this.getAsyncRoutesIds(routes as RouteObject[]);\n } else {\n const routesService = new ParseRoutes(this.config, this.viteAliases);\n\n routesPaths = this.getRoutesTreeIds(routesService.parse());\n }\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?: AgnosticDataRouteMatch[]): 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?: AgnosticDataRouteMatch[]): 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 = `${this.root}/${this.config.getPluginConfig()?.clientFile ?? 'client.ts'}`;\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","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","async","routes","index","routeIndex","route","routeId","filter","Boolean","join","lazy","resolvedRoute","getAppPath","pathId","e","console","error","chalk","red","children","Object","assign","getAsyncRoutesIds","getRoutesTreeIds","forEach","String","import","sortAssets","assets","sort","a","b","weight","Number","isNested","getRouteAssets","manifest","module","css","file","reduce","res","asset","type","getAssetType","isEntry","url","getAssetWeight","isPreload","imports","nestedAsset","nestedModule","isNodeParsing","prepareServer","PrepareServer","init","ServerConfig","isProd","routesPaths","loadEntrypoint","routesService","ParseRoutes","postfixes","getImportPostfix","entries","routePath","routePostfix","find","postfix","undefined","routeMeta","values","writeFileSync","stringify","getAssets","getAssetsDev","routeIds","map","id","flat","getPluginConfig","clientFile","moduleId","ext","moduleGraph","getModuleById","getModuleAssets","skipModules","Set","clientImportedModules","size","has","add","subModule","transformResult","split","at","includes","code","match","groups","style","content","warn","yellowBright","script","toLowerCase","image","font","writeEarlyHits","socket","write","injectAssets","routerContext","html","hasEarlyHints","matches","htmlAssets","isModulePreload","header","replace"],"mappings":"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"}