@lomray/vite-ssr-boost 1.0.0-beta.14 → 1.0.0-beta.16

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 o from"node:child_process";import e from"node:fs";import t from"node:path";import{performance as r}from"node:perf_hooks";import s from"chalk";import{resolveConfig as i}from"vite";import n from"./vite-reset-cache.js";import p from"../constants/cli-name.js";import{createDevMarker as c}from"../helpers/dev-marker.js";import l from"../helpers/plugin-config.js";import d from"../helpers/unlock-robots.js";const m=o=>{const e=new Promise(((e,t)=>{o.on("exit",(o=>{e(o)})),o.on("close",(o=>{e(o)})),o.on("error",(o=>{t(o)}))}));return o.stdout?.pipe(process.stdout),o.stderr?.pipe(process.stderr),e.command=o,e};async function a({onFinish:a,isOnlyClient:u=!1,isWatch:v=!1,isUnlockRobots:O=!1,clientOptions:f="",serverOptions:S="",mode:h=""}){const _=r.now(),$=await i({},"build",h,"production"===h?"production":"development"),b=l($),{outDir:g}=$.build,w=["client"],R=new AbortController,B=h?`--mode ${h}`:"",C=process.env.NODE_ENV||"development",y="production"===C,D=t.resolve($.root,g);await n(),e.existsSync(D)&&e.rmSync(D,{recursive:!0});const j=m(o.spawn(`vite build ${f} --emptyOutDir --outDir ${g}/client ${B}`,{signal:R.signal,stdio:[process.stdin,"pipe",process.stderr],shell:!0,env:{...process.env,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:u?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}));let E;if(v||await j,u||(E=m(o.spawn(`vite build ${S} --emptyOutDir --outDir ${g}/server --ssr ${b.serverFile} ${B}`,{signal:R.signal,stdio:[process.stdin,"pipe",process.stderr],shell:!0,env:{...process.env,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:u?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}})),v||await E,w.push("server")),v){process.on("exit",(()=>{R.abort()}));let o=u?1:2;const e=t=>{Buffer.from(t).toString().includes("built in")&&(o-=1,o||(j.command.stdout.removeListener("data",e),E?.command.stdout.removeListener("data",e),c(y,$),a?.()))};return j.command.stdout.on("data",e),void E?.command.stdout.on("data",e)}O&&d($.root,g),c(y,$),a?.();const N=s.dim(`${s.yellowBright(w.join(","))} built in ${s.reset(s.bold(Math.ceil(r.now()-_)))} ms`);console.info(`\n ${s.green(`${s.bold(p.toUpperCase())}`)} ${N} ${y?"":s.redBright(`NODE_ENV=${C}`)}\n`)}export{a as default};
1
+ import o from"node:child_process";import e from"node:fs";import t from"node:path";import{performance as s}from"node:perf_hooks";import r from"chalk";import{resolveConfig as i}from"vite";import n from"./vite-reset-cache.js";import l from"../constants/cli-name.js";import{createDevMarker as p}from"../helpers/dev-marker.js";import a from"../helpers/plugin-config.js";import c from"../helpers/unlock-robots.js";import d from"../services/ssr-manifest.js";const m=o=>{const e=new Promise(((e,t)=>{o.on("exit",(o=>{e(o)})),o.on("close",(o=>{e(o)})),o.on("error",(o=>{t(o)}))}));return o.stdout?.pipe(process.stdout),o.stderr?.pipe(process.stderr),e.command=o,e};async function u({onFinish:u,isOnlyClient:v=!1,isWatch:f=!1,isUnlockRobots:O=!1,clientOptions:S="",serverOptions:b="",mode:h=""}){const _=s.now(),$=await i({},"build",h,"production"===h?"production":"development"),g=a($),{outDir:w}=$.build,R=["client"],B=new AbortController,C=h?`--mode ${h}`:"",D=process.env.NODE_ENV||"development",j="production"===D,y=t.resolve($.root,w);await n(),e.existsSync(y)&&e.rmSync(y,{recursive:!0});const A=m(o.spawn(`vite build ${S} --emptyOutDir --outDir ${w}/client ${C}`,{signal:B.signal,stdio:[process.stdin,"pipe",process.stderr],shell:!0,env:{...process.env,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:v?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}));let E;if(f||await A,v||(E=m(o.spawn(`vite build ${b} --emptyOutDir --outDir ${w}/server --ssr ${g.serverFile} ${C}`,{signal:B.signal,stdio:[process.stdin,"pipe",process.stderr],shell:!0,env:{...process.env,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:v?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}})),f||(await E,await d.get($.root,{alias:$.resolve.alias,buildDir:w}).buildRoutesManifest(g.preloadAssets)),R.push("server")),f){process.on("exit",(()=>{B.abort()}));let o=v?1:2;const e=t=>{Buffer.from(t).toString().includes("built in")&&(o-=1,o||(A.command.stdout.removeListener("data",e),E?.command.stdout.removeListener("data",e),p(j,$),u?.()))};return A.command.stdout.on("data",e),void E?.command.stdout.on("data",e)}O&&c($.root,w),p(j,$),u?.();const N=r.dim(`${r.yellowBright(R.join(","))} built in ${r.reset(r.bold(Math.ceil(s.now()-_)))} ms`);console.info(`\n ${r.green(`${r.bold(l.toUpperCase())}`)} ${N} ${j?"":r.redBright(`NODE_ENV=${D}`)}\n`)}export{u 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 fs from 'node:fs';\nimport path from 'node:path';\nimport { performance } from 'node:perf_hooks';\nimport chalk from 'chalk';\nimport { resolveConfig } from 'vite';\nimport viteResetCache from '@cli/vite-reset-cache';\nimport cliName from '@constants/cli-name';\nimport { createDevMarker } from '@helpers/dev-marker';\nimport getPluginConfig from '@helpers/plugin-config';\nimport unlockRobots from '@helpers/unlock-robots';\n\ninterface IBuildParams {\n isOnlyClient?: boolean;\n isWatch?: boolean;\n isUnlockRobots?: boolean;\n clientOptions?: string;\n serverOptions?: string;\n mode?: string;\n onFinish?: () => void;\n}\n\n/**\n * Promisify spawn process\n */\nconst promisify = (command: childProcess.ChildProcess) => {\n const promise = new Promise((resolve, reject) => {\n command.on('exit', (code) => {\n resolve(code);\n });\n\n command.on('close', (code) => {\n resolve(code);\n });\n\n command.on('error', (message) => {\n reject(message);\n });\n });\n\n command.stdout?.pipe(process.stdout);\n command.stderr?.pipe(process.stderr);\n\n promise['command'] = command;\n\n return promise;\n};\n\n/**\n * Build production application\n */\nasync function build({\n onFinish,\n isOnlyClient = false,\n isWatch = false,\n isUnlockRobots = false,\n clientOptions = '',\n serverOptions = '',\n mode = '',\n}: IBuildParams): Promise<void> {\n const perfStart = performance.now();\n const config = await resolveConfig(\n {},\n 'build',\n mode,\n mode === 'production' ? 'production' : 'development',\n );\n const pluginConfig = getPluginConfig(config);\n const { outDir } = config.build;\n const types = ['client'];\n const controller = new AbortController();\n const modeOpt = mode ? `--mode ${mode}` : '';\n const nodeEnv = process.env.NODE_ENV || 'development';\n const isProd = nodeEnv === 'production';\n const buildDir = path.resolve(config.root, outDir);\n\n // this is required step - build with different env may cause problems\n await viteResetCache();\n\n // clear build folder\n if (fs.existsSync(buildDir)) {\n fs.rmSync(buildDir, { recursive: true });\n }\n\n /**\n * Build client\n */\n const clientProcess = promisify(\n childProcess.spawn(\n `vite build ${clientOptions} --emptyOutDir --outDir ${outDir}/client ${modeOpt}`,\n {\n signal: controller.signal,\n stdio: [process.stdin, 'pipe', process.stderr],\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 );\n\n if (!isWatch) {\n await clientProcess;\n }\n\n let serverProcess: Promise<unknown> | undefined;\n\n /**\n * Build server\n */\n if (!isOnlyClient) {\n serverProcess = promisify(\n childProcess.spawn(\n `vite build ${serverOptions} --emptyOutDir --outDir ${outDir}/server --ssr ${pluginConfig.serverFile} ${modeOpt}`,\n {\n signal: controller.signal,\n stdio: [process.stdin, 'pipe', process.stderr],\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 );\n\n if (!isWatch) {\n await serverProcess;\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(isProd, config);\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 unlockRobots(config.root, outDir);\n }\n\n createDevMarker(isProd, config);\n onFinish?.();\n\n const buildDurationString = chalk.dim(\n `${chalk.yellowBright(types.join(','))} built in ${chalk.reset(\n chalk.bold(Math.ceil(performance.now() - perfStart)),\n )} ms`,\n );\n\n console.info(\n `\\n ${chalk.green(`${chalk.bold(cliName.toUpperCase())}`)} ${buildDurationString} ${\n isProd ? '' : chalk.redBright(`NODE_ENV=${nodeEnv}`)\n }\\n`,\n );\n}\n\nexport default build;\n"],"names":["promisify","command","promise","Promise","resolve","reject","on","code","message","stdout","pipe","process","stderr","async","build","onFinish","isOnlyClient","isWatch","isUnlockRobots","clientOptions","serverOptions","mode","perfStart","performance","now","config","resolveConfig","pluginConfig","getPluginConfig","outDir","types","controller","AbortController","modeOpt","nodeEnv","env","NODE_ENV","isProd","buildDir","path","root","viteResetCache","fs","existsSync","rmSync","recursive","clientProcess","childProcess","spawn","signal","stdio","stdin","shell","FORCE_COLOR","SSR_BOOST_IS_SSR","SSR_BOOST_ACTION","global","viteBoostAction","serverProcess","serverFile","push","abort","buildCount","listener","buff","Buffer","from","toString","includes","removeListener","createDevMarker","unlockRobots","buildDurationString","chalk","dim","yellowBright","join","reset","bold","Math","ceil","console","info","green","cliName","toUpperCase","redBright"],"mappings":"wZAyBA,MAAMA,EAAaC,IACjB,MAAMC,EAAU,IAAIC,SAAQ,CAACC,EAASC,KACpCJ,EAAQK,GAAG,QAASC,IAClBH,EAAQG,EAAK,IAGfN,EAAQK,GAAG,SAAUC,IACnBH,EAAQG,EAAK,IAGfN,EAAQK,GAAG,SAAUE,IACnBH,EAAOG,EAAQ,GACf,IAQJ,OALAP,EAAQQ,QAAQC,KAAKC,QAAQF,QAC7BR,EAAQW,QAAQF,KAAKC,QAAQC,QAE7BV,EAAiB,QAAID,EAEdC,CAAO,EAMhBW,eAAeC,GAAMC,SACnBA,EAAQC,aACRA,GAAe,EAAKC,QACpBA,GAAU,EAAKC,eACfA,GAAiB,EAAKC,cACtBA,EAAgB,GAAEC,cAClBA,EAAgB,GAAEC,KAClBA,EAAO,KAEP,MAAMC,EAAYC,EAAYC,MACxBC,QAAeC,EACnB,CAAA,EACA,QACAL,EACS,eAATA,EAAwB,aAAe,eAEnCM,EAAeC,EAAgBH,IAC/BI,OAAEA,GAAWJ,EAAOX,MACpBgB,EAAQ,CAAC,UACTC,EAAa,IAAIC,gBACjBC,EAAUZ,EAAO,UAAUA,IAAS,GACpCa,EAAUvB,QAAQwB,IAAIC,UAAY,cAClCC,EAAqB,eAAZH,EACTI,EAAWC,EAAKnC,QAAQqB,EAAOe,KAAMX,SAGrCY,IAGFC,EAAGC,WAAWL,IAChBI,EAAGE,OAAON,EAAU,CAAEO,WAAW,IAMnC,MAAMC,EAAgB9C,EACpB+C,EAAaC,MACX,cAAc7B,4BAAwCU,YAAiBI,IACvE,CACEgB,OAAQlB,EAAWkB,OACnBC,MAAO,CAACvC,QAAQwC,MAAO,OAAQxC,QAAQC,QACvCwC,OAAO,EACPjB,IAAK,IACAxB,QAAQwB,IACXkB,YAAa,IACbC,iBAAkBtC,EAAe,IAAM,IACvCuC,iBAAkBC,OAAOC,oBAUjC,IAAIC,EAiCJ,GArCKzC,SACG6B,EAQH9B,IACH0C,EAAgB1D,EACd+C,EAAaC,MACX,cAAc5B,4BAAwCS,kBAAuBF,EAAagC,cAAc1B,IACxG,CACEgB,OAAQlB,EAAWkB,OACnBC,MAAO,CAACvC,QAAQwC,MAAO,OAAQxC,QAAQC,QACvCwC,OAAO,EACPjB,IAAK,IACAxB,QAAQwB,IACXkB,YAAa,IACbC,iBAAkBtC,EAAe,IAAM,IACvCuC,iBAAkBC,OAAOC,oBAM5BxC,SACGyC,EAGR5B,EAAM8B,KAAK,WAMT3C,EAAS,CACXN,QAAQL,GAAG,QAAQ,KACjByB,EAAW8B,OAAO,IAGpB,IAAIC,EAAa9C,EAAe,EAAI,EACpC,MAAM+C,EAAYC,IACJC,OAAOC,KAAKF,GAAMG,WAEtBC,SAAS,cACfN,GAAc,EAETA,IACHhB,EAAuB,QAAErC,OAAO4D,eAAe,OAAQN,GACvDL,GAAyB,QAAEjD,OAAO4D,eAAe,OAAQN,GACzDO,EAAgBjC,EAAQZ,GACxBV,OAEH,EASH,OAHA+B,EAAuB,QAAErC,OAAOH,GAAG,OAAQyD,QAC3CL,GAAyB,QAAEjD,OAAOH,GAAG,OAAQyD,EAG9C,CAEG7C,GACFqD,EAAa9C,EAAOe,KAAMX,GAG5ByC,EAAgBjC,EAAQZ,GACxBV,MAEA,MAAMyD,EAAsBC,EAAMC,IAChC,GAAGD,EAAME,aAAa7C,EAAM8C,KAAK,kBAAkBH,EAAMI,MACvDJ,EAAMK,KAAKC,KAAKC,KAAKzD,EAAYC,MAAQF,WAI7C2D,QAAQC,KACN,OAAOT,EAAMU,MAAM,GAAGV,EAAMK,KAAKM,EAAQC,sBAAsBb,KAC7DnC,EAAS,GAAKoC,EAAMa,UAAU,YAAYpD,SAGhD"}
1
+ {"version":3,"file":"build.js","sources":["../../src/cli/build.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { performance } from 'node:perf_hooks';\nimport chalk from 'chalk';\nimport { resolveConfig } from 'vite';\nimport viteResetCache from '@cli/vite-reset-cache';\nimport cliName from '@constants/cli-name';\nimport { createDevMarker } from '@helpers/dev-marker';\nimport getPluginConfig from '@helpers/plugin-config';\nimport unlockRobots from '@helpers/unlock-robots';\nimport SsrManifest from '@services/ssr-manifest';\n\ninterface IBuildParams {\n isOnlyClient?: boolean;\n isWatch?: boolean;\n isUnlockRobots?: boolean;\n clientOptions?: string;\n serverOptions?: string;\n mode?: string;\n onFinish?: () => void;\n}\n\n/**\n * Promisify spawn process\n */\nconst promisify = (command: childProcess.ChildProcess) => {\n const promise = new Promise((resolve, reject) => {\n command.on('exit', (code) => {\n resolve(code);\n });\n\n command.on('close', (code) => {\n resolve(code);\n });\n\n command.on('error', (message) => {\n reject(message);\n });\n });\n\n command.stdout?.pipe(process.stdout);\n command.stderr?.pipe(process.stderr);\n\n promise['command'] = command;\n\n return promise;\n};\n\n/**\n * Build production application\n */\nasync function build({\n onFinish,\n isOnlyClient = false,\n isWatch = false,\n isUnlockRobots = false,\n clientOptions = '',\n serverOptions = '',\n mode = '',\n}: IBuildParams): Promise<void> {\n const perfStart = performance.now();\n const config = await resolveConfig(\n {},\n 'build',\n mode,\n mode === 'production' ? 'production' : 'development',\n );\n const pluginConfig = getPluginConfig(config);\n const { outDir } = config.build;\n const types = ['client'];\n const controller = new AbortController();\n const modeOpt = mode ? `--mode ${mode}` : '';\n const nodeEnv = process.env.NODE_ENV || 'development';\n const isProd = nodeEnv === 'production';\n const buildDir = path.resolve(config.root, outDir);\n\n // this is required step - build with different env may cause problems\n await viteResetCache();\n\n // clear build folder\n if (fs.existsSync(buildDir)) {\n fs.rmSync(buildDir, { recursive: true });\n }\n\n /**\n * Build client\n */\n const clientProcess = promisify(\n childProcess.spawn(\n `vite build ${clientOptions} --emptyOutDir --outDir ${outDir}/client ${modeOpt}`,\n {\n signal: controller.signal,\n stdio: [process.stdin, 'pipe', process.stderr],\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 );\n\n if (!isWatch) {\n await clientProcess;\n }\n\n let serverProcess: Promise<unknown> | undefined;\n\n /**\n * Build server\n */\n if (!isOnlyClient) {\n serverProcess = promisify(\n childProcess.spawn(\n `vite build ${serverOptions} --emptyOutDir --outDir ${outDir}/server --ssr ${pluginConfig.serverFile} ${modeOpt}`,\n {\n signal: controller.signal,\n stdio: [process.stdin, 'pipe', process.stderr],\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 );\n\n if (!isWatch) {\n await serverProcess;\n await SsrManifest.get(config.root, {\n alias: config.resolve.alias,\n buildDir: outDir,\n }).buildRoutesManifest(pluginConfig.preloadAssets);\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(isProd, config);\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 unlockRobots(config.root, outDir);\n }\n\n createDevMarker(isProd, config);\n onFinish?.();\n\n const buildDurationString = chalk.dim(\n `${chalk.yellowBright(types.join(','))} built in ${chalk.reset(\n chalk.bold(Math.ceil(performance.now() - perfStart)),\n )} ms`,\n );\n\n console.info(\n `\\n ${chalk.green(`${chalk.bold(cliName.toUpperCase())}`)} ${buildDurationString} ${\n isProd ? '' : chalk.redBright(`NODE_ENV=${nodeEnv}`)\n }\\n`,\n );\n}\n\nexport default build;\n"],"names":["promisify","command","promise","Promise","resolve","reject","on","code","message","stdout","pipe","process","stderr","async","build","onFinish","isOnlyClient","isWatch","isUnlockRobots","clientOptions","serverOptions","mode","perfStart","performance","now","config","resolveConfig","pluginConfig","getPluginConfig","outDir","types","controller","AbortController","modeOpt","nodeEnv","env","NODE_ENV","isProd","buildDir","path","root","viteResetCache","fs","existsSync","rmSync","recursive","clientProcess","childProcess","spawn","signal","stdio","stdin","shell","FORCE_COLOR","SSR_BOOST_IS_SSR","SSR_BOOST_ACTION","global","viteBoostAction","serverProcess","serverFile","SsrManifest","get","alias","buildRoutesManifest","preloadAssets","push","abort","buildCount","listener","buff","Buffer","from","toString","includes","removeListener","createDevMarker","unlockRobots","buildDurationString","chalk","dim","yellowBright","join","reset","bold","Math","ceil","console","info","green","cliName","toUpperCase","redBright"],"mappings":"mcA0BA,MAAMA,EAAaC,IACjB,MAAMC,EAAU,IAAIC,SAAQ,CAACC,EAASC,KACpCJ,EAAQK,GAAG,QAASC,IAClBH,EAAQG,EAAK,IAGfN,EAAQK,GAAG,SAAUC,IACnBH,EAAQG,EAAK,IAGfN,EAAQK,GAAG,SAAUE,IACnBH,EAAOG,EAAQ,GACf,IAQJ,OALAP,EAAQQ,QAAQC,KAAKC,QAAQF,QAC7BR,EAAQW,QAAQF,KAAKC,QAAQC,QAE7BV,EAAiB,QAAID,EAEdC,CAAO,EAMhBW,eAAeC,GAAMC,SACnBA,EAAQC,aACRA,GAAe,EAAKC,QACpBA,GAAU,EAAKC,eACfA,GAAiB,EAAKC,cACtBA,EAAgB,GAAEC,cAClBA,EAAgB,GAAEC,KAClBA,EAAO,KAEP,MAAMC,EAAYC,EAAYC,MACxBC,QAAeC,EACnB,CAAA,EACA,QACAL,EACS,eAATA,EAAwB,aAAe,eAEnCM,EAAeC,EAAgBH,IAC/BI,OAAEA,GAAWJ,EAAOX,MACpBgB,EAAQ,CAAC,UACTC,EAAa,IAAIC,gBACjBC,EAAUZ,EAAO,UAAUA,IAAS,GACpCa,EAAUvB,QAAQwB,IAAIC,UAAY,cAClCC,EAAqB,eAAZH,EACTI,EAAWC,EAAKnC,QAAQqB,EAAOe,KAAMX,SAGrCY,IAGFC,EAAGC,WAAWL,IAChBI,EAAGE,OAAON,EAAU,CAAEO,WAAW,IAMnC,MAAMC,EAAgB9C,EACpB+C,EAAaC,MACX,cAAc7B,4BAAwCU,YAAiBI,IACvE,CACEgB,OAAQlB,EAAWkB,OACnBC,MAAO,CAACvC,QAAQwC,MAAO,OAAQxC,QAAQC,QACvCwC,OAAO,EACPjB,IAAK,IACAxB,QAAQwB,IACXkB,YAAa,IACbC,iBAAkBtC,EAAe,IAAM,IACvCuC,iBAAkBC,OAAOC,oBAUjC,IAAIC,EAqCJ,GAzCKzC,SACG6B,EAQH9B,IACH0C,EAAgB1D,EACd+C,EAAaC,MACX,cAAc5B,4BAAwCS,kBAAuBF,EAAagC,cAAc1B,IACxG,CACEgB,OAAQlB,EAAWkB,OACnBC,MAAO,CAACvC,QAAQwC,MAAO,OAAQxC,QAAQC,QACvCwC,OAAO,EACPjB,IAAK,IACAxB,QAAQwB,IACXkB,YAAa,IACbC,iBAAkBtC,EAAe,IAAM,IACvCuC,iBAAkBC,OAAOC,oBAM5BxC,UACGyC,QACAE,EAAYC,IAAIpC,EAAOe,KAAM,CACjCsB,MAAOrC,EAAOrB,QAAQ0D,MACtBxB,SAAUT,IACTkC,oBAAoBpC,EAAaqC,gBAGtClC,EAAMmC,KAAK,WAMThD,EAAS,CACXN,QAAQL,GAAG,QAAQ,KACjByB,EAAWmC,OAAO,IAGpB,IAAIC,EAAanD,EAAe,EAAI,EACpC,MAAMoD,EAAYC,IACJC,OAAOC,KAAKF,GAAMG,WAEtBC,SAAS,cACfN,GAAc,EAETA,IACHrB,EAAuB,QAAErC,OAAOiE,eAAe,OAAQN,GACvDV,GAAyB,QAAEjD,OAAOiE,eAAe,OAAQN,GACzDO,EAAgBtC,EAAQZ,GACxBV,OAEH,EASH,OAHA+B,EAAuB,QAAErC,OAAOH,GAAG,OAAQ8D,QAC3CV,GAAyB,QAAEjD,OAAOH,GAAG,OAAQ8D,EAG9C,CAEGlD,GACF0D,EAAanD,EAAOe,KAAMX,GAG5B8C,EAAgBtC,EAAQZ,GACxBV,MAEA,MAAM8D,EAAsBC,EAAMC,IAChC,GAAGD,EAAME,aAAalD,EAAMmD,KAAK,kBAAkBH,EAAMI,MACvDJ,EAAMK,KAAKC,KAAKC,KAAK9D,EAAYC,MAAQF,WAI7CgE,QAAQC,KACN,OAAOT,EAAMU,MAAM,GAAGV,EAAMK,KAAKM,EAAQC,sBAAsBb,KAC7DxC,EAAS,GAAKyC,EAAMa,UAAU,YAAYzD,SAGhD"}
@@ -248,5 +248,5 @@ type IAsyncRoute = Omit<IndexRouteObject, ImmutableRouteKey> | Omit<NonIndexRout
248
248
  /**
249
249
  * Import dynamic route
250
250
  */
251
- declare const importRoute: (route: IDynamicRoute) => Promise<IAsyncRoute>;
251
+ declare const importRoute: (route: IDynamicRoute, id?: string) => Promise<IAsyncRoute>;
252
252
  export { importRoute as default, IDynamicRoute, IAsyncRoute };
@@ -1,2 +1,2 @@
1
- import e from"../components/with-suspense.js";import{keys as o}from"../interfaces/fc-route.js";const s=async s=>{const t=(await s()).default,n={Component:t};return o.forEach((e=>{t[e]&&(n[e]=t[e])})),t.Suspense&&(n.Component=e(t,t.Suspense)),n};export{s as default};
1
+ import t from"../components/with-suspense.js";import{keys as e}from"../interfaces/fc-route.js";const n=(t,e)=>{e&&(t.pathId=e)},o=async(o,s)=>{const r=await o();if(r.Component)return n(r,s),r;const p=r.default,a={Component:p};return e.forEach((t=>{p[t]&&(a[t]=p[t])})),p.Suspense&&(a.Component=t(p,p.Suspense)),n(a,s),a};export{o as default};
2
2
  //# sourceMappingURL=import-route.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"import-route.js","sources":["../../src/helpers/import-route.ts"],"sourcesContent":["import type { ImmutableRouteKey } from '@remix-run/router/utils';\nimport type { IndexRouteObject, NonIndexRouteObject } from 'react-router-dom';\nimport withSuspense from '@components/with-suspense';\nimport type { FCCRoute, FCRoute } from '@interfaces/fc-route';\nimport { keys } from '@interfaces/fc-route';\n\nexport type IDynamicRoute = () => Promise<{ default: FCRoute | FCCRoute<any> }>;\n\nexport type IAsyncRoute =\n | Omit<IndexRouteObject, ImmutableRouteKey>\n | Omit<NonIndexRouteObject, ImmutableRouteKey>;\n\n/**\n * Import dynamic route\n */\nconst importRoute = async (route: IDynamicRoute): Promise<IAsyncRoute> => {\n const Component = (await route()).default;\n const result = { Component };\n\n keys.forEach((key) => {\n if (Component[key]) {\n result[key] = Component[key];\n }\n });\n\n if (Component.Suspense) {\n result.Component = withSuspense(Component, Component.Suspense);\n }\n\n return result;\n};\n\nexport default importRoute;\n"],"names":["importRoute","async","route","Component","default","result","keys","forEach","key","Suspense","withSuspense"],"mappings":"+FAeA,MAAMA,EAAcC,MAAOC,IACzB,MAAMC,SAAmBD,KAASE,QAC5BC,EAAS,CAAEF,aAYjB,OAVAG,EAAKC,SAASC,IACRL,EAAUK,KACZH,EAAOG,GAAOL,EAAUK,GACzB,IAGCL,EAAUM,WACZJ,EAAOF,UAAYO,EAAaP,EAAWA,EAAUM,WAGhDJ,CAAM"}
1
+ {"version":3,"file":"import-route.js","sources":["../../src/helpers/import-route.ts"],"sourcesContent":["import type { ImmutableRouteKey } from '@remix-run/router/utils';\nimport type { IndexRouteObject, NonIndexRouteObject } from 'react-router-dom';\nimport withSuspense from '@components/with-suspense';\nimport type { FCCRoute, FCRoute } from '@interfaces/fc-route';\nimport { keys } from '@interfaces/fc-route';\n\nexport type IDynamicRoute = () => Promise<{ default: FCRoute | FCCRoute<any> }>;\n\nexport type IAsyncRoute =\n | Omit<IndexRouteObject, ImmutableRouteKey>\n | Omit<NonIndexRouteObject, ImmutableRouteKey>;\n\n/**\n * Assign route path id to component\n */\nconst assignId = (response: Record<string, any>, id?: string) => {\n if (!id) {\n return;\n }\n\n response['pathId'] = id;\n};\n\n/**\n * Import dynamic route\n */\nconst importRoute = async (route: IDynamicRoute, id?: string): Promise<IAsyncRoute> => {\n const resolved = await route();\n\n // fallback to react router export style\n if (resolved['Component']) {\n assignId(resolved, id);\n\n return resolved as IAsyncRoute;\n }\n\n const Component = resolved.default;\n const result = { Component };\n\n keys.forEach((key) => {\n if (Component[key]) {\n result[key] = Component[key];\n }\n });\n\n if (Component.Suspense) {\n result.Component = withSuspense(Component, Component.Suspense);\n }\n\n assignId(result, id);\n\n return result;\n};\n\nexport default importRoute;\n"],"names":["assignId","response","id","importRoute","async","route","resolved","Component","default","result","keys","forEach","key","Suspense","withSuspense"],"mappings":"+FAeA,MAAMA,EAAW,CAACC,EAA+BC,KAC1CA,IAILD,EAAiB,OAAIC,EAAE,EAMnBC,EAAcC,MAAOC,EAAsBH,KAC/C,MAAMI,QAAiBD,IAGvB,GAAIC,EAAoB,UAGtB,OAFAN,EAASM,EAAUJ,GAEZI,EAGT,MAAMC,EAAYD,EAASE,QACrBC,EAAS,CAAEF,aAcjB,OAZAG,EAAKC,SAASC,IACRL,EAAUK,KACZH,EAAOG,GAAOL,EAAUK,GACzB,IAGCL,EAAUM,WACZJ,EAAOF,UAAYO,EAAaP,EAAWA,EAAUM,WAGvDb,EAASS,EAAQP,GAEVO,CAAM"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Detect route file
3
+ */
4
+ declare const isRoutesFile: (code: string) => boolean;
5
+ export { isRoutesFile as default };
@@ -0,0 +1,2 @@
1
+ const t=t=>/\[.*{.*path:.*lazyNR:.+import/s.test(t);export{t as default};
2
+ //# sourceMappingURL=is-route-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-route-file.js","sources":["../../src/helpers/is-route-file.ts"],"sourcesContent":["/**\n * Detect route file\n */\nconst isRoutesFile = (code: string): boolean => /\\[.*{.*path:.*lazyNR:.+import/s.test(code);\n\nexport default isRoutesFile;\n"],"names":["isRoutesFile","code","test"],"mappings":"AAGA,MAAMA,EAAgBC,GAA0B,iCAAiCC,KAAKD"}
package/node/entry.d.ts CHANGED
@@ -20,12 +20,15 @@ interface IEntrypointOptions<TAppProps = Record<string, any>> {
20
20
  interface IPrepareRenderOut<TAppProps = Record<string, any>> {
21
21
  render: TRender;
22
22
  init: IEntryServerOptions<TAppProps>['init'];
23
+ routes: TRouteObject[];
24
+ abortDelay?: number;
23
25
  }
24
26
  interface IAppServerProps<T = Record<string, any>> {
25
27
  server: T;
26
28
  }
27
29
  type TApp<T> = FC<PropsWithChildren<Record<string, any> & IAppServerProps<T>>>;
28
30
  interface IEntryServerOptions<TAppProps = Record<string, any>> {
31
+ abortDelay?: number;
29
32
  init?: (params: {
30
33
  config: ServerConfig;
31
34
  }) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;
@@ -33,5 +36,5 @@ interface IEntryServerOptions<TAppProps = Record<string, any>> {
33
36
  /**
34
37
  * Render server side application
35
38
  */
36
- declare function entry<TAppProps>(App: TApp<TAppProps>, routes: TRouteObject[], { init }?: IEntryServerOptions<TAppProps>): IPrepareRenderOut<TAppProps>;
39
+ declare function entry<TAppProps>(App: TApp<TAppProps>, routes: TRouteObject[], { init, abortDelay }?: IEntryServerOptions<TAppProps>): IPrepareRenderOut<TAppProps>;
37
40
  export { entry as default, IInitServerRequestOut, IEntrypointOptions, IPrepareRenderOut, IAppServerProps, TApp, IEntryServerOptions };
package/node/entry.js CHANGED
@@ -1,2 +1,2 @@
1
- import{createStaticHandler as r}from"react-router-dom/server.mjs";import e from"./render.js";function n(n,t,{init:o}={}){const i=r(t);return{render:e.bind(null,{handler:i,App:n}),init:o}}export{n as default};
1
+ import{createStaticHandler as r}from"react-router-dom/server.mjs";import e from"./render.js";function t(t,o,{init:n,abortDelay:a}={}){const i=r(o);return{render:e.bind(null,{handler:i,App:t}),init:n,routes:o,abortDelay:a}}export{t as default};
2
2
  //# sourceMappingURL=entry.js.map
package/node/entry.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"entry.js","sources":["../../src/node/entry.tsx"],"sourcesContent":["import type { Express, Request, Response as ExpressResponse } from 'express';\nimport type { FC, PropsWithChildren } from 'react';\nimport type { RouteObject } from 'react-router-dom';\nimport { createStaticHandler } from 'react-router-dom/server.mjs';\nimport type { TRouteObject } from '@interfaces/route-object';\nimport type { IRenderOptions, IRenderParams, TRender } from '@node/render';\nimport render from '@node/render';\nimport type ServerConfig from '@services/server-config';\n\nexport interface IInitServerRequestOut<T = Record<string, any>> {\n appProps?: T;\n}\n\nexport interface IEntrypointOptions<TAppProps = Record<string, any>> {\n onServerCreated?: (app: Express) => Promise<void> | void;\n onRequest?: (\n req: Request,\n res: ExpressResponse,\n ) => Promise<IInitServerRequestOut<TAppProps>> | IInitServerRequestOut<TAppProps>;\n onRouterReady?: IRenderOptions<TAppProps>['onRouterReady'];\n onShellReady?: IRenderOptions<TAppProps>['onShellReady'];\n onShellError?: IRenderOptions<TAppProps>['onShellError'];\n onResponse?: IRenderOptions<TAppProps>['onResponse'];\n onError?: IRenderOptions<TAppProps>['onError'];\n getState?: IRenderOptions<TAppProps>['getState'];\n}\n\nexport interface IPrepareRenderOut<TAppProps = Record<string, any>> {\n render: TRender;\n init: IEntryServerOptions<TAppProps>['init'];\n}\n\nexport interface IAppServerProps<T = Record<string, any>> {\n server: T;\n}\n\nexport type TApp<T> = FC<PropsWithChildren<Record<string, any> & IAppServerProps<T>>>;\n\nexport interface IEntryServerOptions<TAppProps = Record<string, any>> {\n init?: (params: {\n config: ServerConfig;\n }) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;\n}\n\n/**\n * Render server side application\n */\nfunction entry<TAppProps>(\n App: TApp<TAppProps>,\n routes: TRouteObject[],\n { init }: IEntryServerOptions<TAppProps> = {},\n): IPrepareRenderOut<TAppProps> {\n const handler = createStaticHandler(routes as RouteObject[]);\n\n return {\n render: render.bind(null, { handler, App } as IRenderParams<TAppProps>) as TRender,\n init,\n };\n}\n\nexport default entry;\n"],"names":["entry","App","routes","init","handler","createStaticHandler","render","bind"],"mappings":"6FA+CA,SAASA,EACPC,EACAC,GACAC,KAAEA,GAAyC,CAAA,GAE3C,MAAMC,EAAUC,EAAoBH,GAEpC,MAAO,CACLI,OAAQA,EAAOC,KAAK,KAAM,CAAEH,UAASH,QACrCE,OAEJ"}
1
+ {"version":3,"file":"entry.js","sources":["../../src/node/entry.tsx"],"sourcesContent":["import type { Express, Request, Response as ExpressResponse } from 'express';\nimport type { FC, PropsWithChildren } from 'react';\nimport type { RouteObject } from 'react-router-dom';\nimport { createStaticHandler } from 'react-router-dom/server.mjs';\nimport type { TRouteObject } from '@interfaces/route-object';\nimport type { IRenderOptions, IRenderParams, TRender } from '@node/render';\nimport render from '@node/render';\nimport type ServerConfig from '@services/server-config';\n\nexport interface IInitServerRequestOut<T = Record<string, any>> {\n appProps?: T;\n}\n\nexport interface IEntrypointOptions<TAppProps = Record<string, any>> {\n onServerCreated?: (app: Express) => Promise<void> | void;\n onRequest?: (\n req: Request,\n res: ExpressResponse,\n ) => Promise<IInitServerRequestOut<TAppProps>> | IInitServerRequestOut<TAppProps>;\n onRouterReady?: IRenderOptions<TAppProps>['onRouterReady'];\n onShellReady?: IRenderOptions<TAppProps>['onShellReady'];\n onShellError?: IRenderOptions<TAppProps>['onShellError'];\n onResponse?: IRenderOptions<TAppProps>['onResponse'];\n onError?: IRenderOptions<TAppProps>['onError'];\n getState?: IRenderOptions<TAppProps>['getState'];\n}\n\nexport interface IPrepareRenderOut<TAppProps = Record<string, any>> {\n render: TRender;\n init: IEntryServerOptions<TAppProps>['init'];\n routes: TRouteObject[];\n abortDelay?: number;\n}\n\nexport interface IAppServerProps<T = Record<string, any>> {\n server: T;\n}\n\nexport type TApp<T> = FC<PropsWithChildren<Record<string, any> & IAppServerProps<T>>>;\n\nexport interface IEntryServerOptions<TAppProps = Record<string, any>> {\n abortDelay?: number;\n init?: (params: {\n config: ServerConfig;\n }) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;\n}\n\n/**\n * Render server side application\n */\nfunction entry<TAppProps>(\n App: TApp<TAppProps>,\n routes: TRouteObject[],\n { init, abortDelay }: IEntryServerOptions<TAppProps> = {},\n): IPrepareRenderOut<TAppProps> {\n const handler = createStaticHandler(routes as RouteObject[]);\n\n return {\n render: render.bind(null, { handler, App } as IRenderParams<TAppProps>) as TRender,\n init,\n routes,\n abortDelay,\n };\n}\n\nexport default entry;\n"],"names":["entry","App","routes","init","abortDelay","handler","createStaticHandler","render","bind"],"mappings":"6FAkDA,SAASA,EACPC,EACAC,GACAC,KAAEA,EAAIC,WAAEA,GAA+C,IAEvD,MAAMC,EAAUC,EAAoBJ,GAEpC,MAAO,CACLK,OAAQA,EAAOC,KAAK,KAAM,CAAEH,UAASJ,QACrCE,OACAD,SACAE,aAEJ"}
package/node/render.d.ts CHANGED
@@ -26,7 +26,7 @@ interface IRenderParams<TAppProps = Record<string, any>> {
26
26
  handler: StaticHandler;
27
27
  }
28
28
  interface IRenderOptions<TAppProps = Record<string, any>> {
29
- appProps?: TAppProps;
29
+ abortDelay?: number;
30
30
  onRouterReady?: (params: {
31
31
  context: IRequestContext<TAppProps>;
32
32
  }) => Promise<IRouterReadyOut> | IRouterReadyOut;
@@ -60,5 +60,5 @@ interface IShellReadyOut {
60
60
  * Render application
61
61
  */
62
62
  declare function render({ App, handler }: IRenderParams, // @see entry (bind)
63
- config: ServerConfig, context: IRequestContext, { onRouterReady, onShellReady, onResponse, onShellError, onError, getState }: IRenderOptions): Promise<void>;
63
+ config: ServerConfig, context: IRequestContext, { onRouterReady, onShellReady, onResponse, onShellError, onError, getState, abortDelay, }: IRenderOptions): Promise<void>;
64
64
  export { render as default, IRequestContext, TRender, IRenderParams, IRenderOptions, IRouterReadyOut, IShellReadyOut };
package/node/render.js CHANGED
@@ -1,2 +1,2 @@
1
- import e from"chalk";import r from"react";import{renderToPipeableStream as t}from"react-dom/server";import{createStaticRouter as o,StaticRouterProvider as n}from"react-router-dom/server.mjs";import s from"../constants/stream-error.js";import{ServerProvider as a}from"../context/server.js";import m from"../helpers/handle-response.js";import i from"../helpers/obtain-stream-error.js";import c from"./create-fetch-request.js";import d from"./write-response.js";async function l({App:l,handler:p},u,f,{onRouterReady:h,onShellReady:x,onResponse:S,onShellError:R,onError:g,getState:y}){const{req:C,res:E}=f,j=c(C);f.routerContext=await p.query(j);const v=m(E,f.routerContext);if(!v)return;const{isStream:w=!0}=await(h?.({context:f}))??{};f.isStream=w,f.serverContext={response:null,isServer:!0};const b=o(p.dataRoutes,f.routerContext),T=E.write.bind(E),q=u.getLogger();let $;E.write=(e,...r)=>{const t="string"==typeof e,o=t?e:Buffer.from(e).toString(),n=S?.({context:f,html:o});return n?T(t?`${n}${e}`:Buffer.concat([Buffer.from(n),e]),...r):T(e,...r)};const{serverContext:A,routerContext:B,appProps:P}=f,{pipe:k,abort:D}=t(r.createElement(a,{context:A},r.createElement(l,{server:{...P,req:C}},r.createElement(n,{router:b,context:B,hydrate:!1}))),{onShellReady(){w&&d(f,{pipe:k,statusCode:v,onShellReady:x,getState:y})},onAllReady(){clearTimeout($),w||d(f,{pipe:k,statusCode:v,onShellReady:x,getState:y})},onShellError(e){const r=R?.({context:f,error:e})||`<!doctype html><p>Something went wrong: ${e.message}</p>`;E.status(500),E.setHeader("content-type","text/html"),E.send(r)},onError(r){clearTimeout($);const t=i(r),{code:o,message:n}=t,{didError:a}=f;f.didError=a??o,g?.({context:f,error:t}),q.info(e.red(`Stream error. Code: ${o}`)),[s.RenderAborted,s.RenderTimeout,s.RenderCancel].includes(o)?q.info(e.dim(n)):q.error(r)}});$=setTimeout((()=>{f.didError=s.RenderTimeout,D()}),u.getParams().abortDelay),C.on("close",(()=>{f.didError=s.RenderCancel,D()}))}export{l as default};
1
+ import e from"chalk";import r from"react";import{renderToPipeableStream as t}from"react-dom/server";import{createStaticRouter as o,StaticRouterProvider as n}from"react-router-dom/server.mjs";import s from"../constants/stream-error.js";import{ServerProvider as a}from"../context/server.js";import m from"../helpers/handle-response.js";import i from"../helpers/obtain-stream-error.js";import c from"./create-fetch-request.js";import d from"./write-response.js";import l from"../services/ssr-manifest.js";async function p({App:p,handler:f},u,h,{onRouterReady:x,onShellReady:S,onResponse:g,onShellError:R,onError:y,getState:C,abortDelay:E=15e3}){const{req:j,res:v}=h,w=c(j);h.routerContext=await f.query(w);const b=m(v,h.routerContext);if(!b)return;l.get(u.getParams().root).injectAssets(h);const{isStream:T=!0}=await(x?.({context:h}))??{};h.isStream=T,h.serverContext={response:null,isServer:!0};const q=o(f.dataRoutes,h.routerContext),A=v.write.bind(v),$=u.getLogger();let B;v.write=(e,...r)=>{const t="string"==typeof e,o=t?e:Buffer.from(e).toString(),n=g?.({context:h,html:o});return n?A(t?`${n}${e}`:Buffer.concat([Buffer.from(n),e]),...r):A(e,...r)};const{serverContext:P,routerContext:k,appProps:D}=h,{pipe:H,abort:L}=t(r.createElement(a,{context:P},r.createElement(p,{server:{...D,req:j}},r.createElement(n,{router:q,context:k,hydrate:!1}))),{onShellReady(){T&&d(h,{pipe:H,statusCode:b,onShellReady:S,getState:C})},onAllReady(){clearTimeout(B),T||d(h,{pipe:H,statusCode:b,onShellReady:S,getState:C})},onShellError(e){const r=R?.({context:h,error:e})||`<!doctype html><p>Something went wrong: ${e.message}</p>`;v.status(500),v.setHeader("content-type","text/html"),v.send(r)},onError(r){clearTimeout(B);const t=i(r),{code:o,message:n}=t,{didError:a}=h;h.didError=a??o,y?.({context:h,error:t}),$.info(e.red(`Stream error. Code: ${o}`)),[s.RenderAborted,s.RenderTimeout,s.RenderCancel].includes(o)?$.info(e.dim(n)):$.error(r)}});B=setTimeout((()=>{h.didError=s.RenderTimeout,L()}),E),j.on("close",(()=>{h.didError=s.RenderCancel,L()}))}export{p as default};
2
2
  //# sourceMappingURL=render.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"render.js","sources":["../../src/node/render.tsx"],"sourcesContent":["import type { StaticHandler } from '@remix-run/router';\nimport chalk from 'chalk';\nimport type { Request, Response as ExpressResponse } from 'express';\nimport React from 'react';\nimport { renderToPipeableStream } from 'react-dom/server';\nimport type { StaticHandlerContext } from 'react-router-dom/server';\nimport { createStaticRouter, StaticRouterProvider } from 'react-router-dom/server.mjs';\nimport StreamError from '@constants/stream-error';\nimport type { IServerContext } from '@context/server';\nimport { ServerProvider } from '@context/server';\nimport handleResponse from '@helpers/handle-response';\nimport type { IObtainStreamErrorOut } from '@helpers/obtain-stream-error';\nimport obtainStreamError from '@helpers/obtain-stream-error';\nimport createFetchRequest from '@node/create-fetch-request';\nimport type { TApp } from '@node/entry';\nimport writeResponse from '@node/write-response';\nimport type ServerConfig from '@services/server-config';\n\nexport interface IRequestContext<TAppProps = Record<any, any>> {\n req: Request;\n res: ExpressResponse;\n appProps: NonNullable<TAppProps>;\n html: { header: string; footer: string };\n routerContext?: StaticHandlerContext;\n serverContext?: IServerContext;\n isStream?: boolean;\n didError?: StreamError;\n}\n\nexport type TRender<TAppProps = Record<any, any>> = (\n config: ServerConfig,\n context: IRequestContext<TAppProps>,\n options: IRenderOptions,\n) => Promise<void>;\n\nexport interface IRenderParams<TAppProps = Record<string, any>> {\n App: TApp<TAppProps>;\n handler: StaticHandler;\n}\n\nexport interface IRenderOptions<TAppProps = Record<string, any>> {\n appProps?: TAppProps;\n onRouterReady?: (params: {\n context: IRequestContext<TAppProps>;\n }) => Promise<IRouterReadyOut> | IRouterReadyOut;\n onShellReady?: (params: { context: IRequestContext<TAppProps> }) => IShellReadyOut;\n onShellError?: (params: {\n context: IRequestContext<TAppProps>;\n error: Error;\n }) => string | undefined | void; // return html or undefined\n onError?: (params: { context: IRequestContext<TAppProps>; error: IObtainStreamErrorOut }) => void;\n onResponse?: (params: {\n context: IRequestContext<TAppProps>;\n html: string;\n }) => string | undefined | void;\n getState?: (params: {\n context: IRequestContext<TAppProps>;\n }) => Record<string, Record<string, any>> | undefined | void;\n}\n\nexport interface IRouterReadyOut {\n isStream?: boolean;\n}\n\nexport interface IShellReadyOut {\n header?: string;\n footer?: string;\n}\n\n/**\n * Render application\n */\nasync function render(\n { App, handler }: IRenderParams, // @see entry (bind)\n config: ServerConfig,\n context: IRequestContext,\n { onRouterReady, onShellReady, onResponse, onShellError, onError, getState }: IRenderOptions,\n): Promise<void> {\n const { req, res } = context;\n const fetchRequest = createFetchRequest(req);\n\n context.routerContext = (await handler.query(fetchRequest)) as StaticHandlerContext;\n /**\n * Handle response from page loader, router context can be Response\n */\n const statusCode = handleResponse(res, context.routerContext);\n\n if (!statusCode) {\n return;\n }\n\n const { isStream = true } = (await onRouterReady?.({ context })) ?? {};\n\n context.isStream = isStream;\n context.serverContext = { response: null, isServer: true };\n\n const router = createStaticRouter(handler.dataRoutes, context.routerContext);\n const write = res.write.bind(res);\n const Logger = config.getLogger();\n let abortTimer: NodeJS.Timer | undefined = undefined;\n\n /**\n * Listen response and stream to add possibility modify html on fly\n * E.g. listen stream and append some data\n */\n res.write = (data: string | Uint8Array, ...args): boolean => {\n const isString = typeof data === 'string';\n const html = isString ? data : Buffer.from(data).toString();\n const additionalHtml = onResponse?.({ context, html });\n\n if (additionalHtml) {\n return write(\n isString ? `${additionalHtml}${data}` : Buffer.concat([Buffer.from(additionalHtml), data]),\n ...args,\n ) as boolean;\n }\n\n return write(data, ...args) as boolean;\n };\n\n const { serverContext, routerContext, appProps } = context;\n\n const { pipe, abort } = renderToPipeableStream(\n <ServerProvider context={serverContext}>\n <App server={{ ...appProps, req }}>\n <StaticRouterProvider router={router} context={routerContext} hydrate={false} />\n </App>\n </ServerProvider>,\n {\n onShellReady(): void {\n if (!isStream) {\n return;\n }\n\n writeResponse(context, {\n pipe,\n statusCode,\n onShellReady,\n getState,\n });\n },\n onAllReady(): void {\n clearTimeout(abortTimer);\n\n if (isStream) {\n return;\n }\n\n writeResponse(context, {\n pipe,\n statusCode,\n onShellReady,\n getState,\n });\n },\n onShellError(e: Error): void {\n const htmlError =\n onShellError?.({ context, error: e }) ||\n `<!doctype html><p>Something went wrong: ${e.message}</p>`;\n\n res.status(500);\n res.setHeader('content-type', 'text/html');\n res.send(htmlError);\n },\n onError(err): void {\n clearTimeout(abortTimer);\n\n const error = obtainStreamError(err);\n const { code, message } = error;\n const { didError } = context;\n\n context.didError = didError ?? code;\n\n onError?.({ context, error });\n Logger.info(chalk.red(`Stream error. Code: ${code}`));\n\n if (\n [StreamError.RenderAborted, StreamError.RenderTimeout, StreamError.RenderCancel].includes(\n code,\n )\n ) {\n Logger.info(chalk.dim(message));\n\n return;\n }\n\n Logger.error(err as string);\n },\n },\n );\n\n // Abandon and switch to client rendering if enough time passes.\n abortTimer = setTimeout(() => {\n context.didError = StreamError.RenderTimeout;\n abort();\n }, config.getParams().abortDelay);\n\n // Detect cancel request\n req.on('close', () => {\n context.didError = StreamError.RenderCancel;\n abort();\n });\n}\n\nexport default render;\n"],"names":["async","render","App","handler","config","context","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","req","res","fetchRequest","createFetchRequest","routerContext","query","statusCode","handleResponse","isStream","serverContext","response","isServer","router","createStaticRouter","dataRoutes","write","bind","Logger","getLogger","abortTimer","data","args","isString","html","Buffer","from","toString","additionalHtml","concat","appProps","pipe","abort","renderToPipeableStream","React","createElement","ServerProvider","server","StaticRouterProvider","hydrate","writeResponse","onAllReady","clearTimeout","e","htmlError","error","message","status","setHeader","send","err","obtainStreamError","code","didError","info","chalk","red","StreamError","RenderAborted","RenderTimeout","RenderCancel","includes","dim","setTimeout","getParams","abortDelay","on"],"mappings":"2cAwEAA,eAAeC,GACbC,IAAEA,EAAGC,QAAEA,GACPC,EACAC,GACAC,cAAEA,EAAaC,aAAEA,EAAYC,WAAEA,EAAUC,aAAEA,EAAYC,QAAEA,EAAOC,SAAEA,IAElE,MAAMC,IAAEA,EAAGC,IAAEA,GAAQR,EACfS,EAAeC,EAAmBH,GAExCP,EAAQW,oBAAuBb,EAAQc,MAAMH,GAI7C,MAAMI,EAAaC,EAAeN,EAAKR,EAAQW,eAE/C,IAAKE,EACH,OAGF,MAAME,SAAEA,GAAW,SAAgBd,IAAgB,CAAED,cAAe,GAEpEA,EAAQe,SAAWA,EACnBf,EAAQgB,cAAgB,CAAEC,SAAU,KAAMC,UAAU,GAEpD,MAAMC,EAASC,EAAmBtB,EAAQuB,WAAYrB,EAAQW,eACxDW,EAAQd,EAAIc,MAAMC,KAAKf,GACvBgB,EAASzB,EAAO0B,YACtB,IAAIC,EAMJlB,EAAIc,MAAQ,CAACK,KAA8BC,KACzC,MAAMC,EAA2B,iBAATF,EAClBG,EAAOD,EAAWF,EAAOI,OAAOC,KAAKL,GAAMM,WAC3CC,EAAiB/B,IAAa,CAAEH,UAAS8B,SAE/C,OAAII,EACKZ,EACLO,EAAW,GAAGK,IAAiBP,IAASI,OAAOI,OAAO,CAACJ,OAAOC,KAAKE,GAAiBP,OACjFC,GAIAN,EAAMK,KAASC,EAAgB,EAGxC,MAAMZ,cAAEA,EAAaL,cAAEA,EAAayB,SAAEA,GAAapC,GAE7CqC,KAAEA,EAAIC,MAAEA,GAAUC,EACtBC,EAACC,cAAAC,EAAe,CAAA1C,QAASgB,GACvBwB,EAACC,cAAA5C,GAAI8C,OAAQ,IAAKP,EAAU7B,QAC1BiC,EAAAC,cAACG,EAAqB,CAAAzB,OAAQA,EAAQnB,QAASW,EAAekC,SAAS,MAG3E,CACE3C,eACOa,GAIL+B,EAAc9C,EAAS,CACrBqC,OACAxB,aACAX,eACAI,YAEH,EACDyC,aACEC,aAAatB,GAETX,GAIJ+B,EAAc9C,EAAS,CACrBqC,OACAxB,aACAX,eACAI,YAEH,EACDF,aAAa6C,GACX,MAAMC,EACJ9C,IAAe,CAAEJ,UAASmD,MAAOF,KACjC,2CAA2CA,EAAEG,cAE/C5C,EAAI6C,OAAO,KACX7C,EAAI8C,UAAU,eAAgB,aAC9B9C,EAAI+C,KAAKL,EACV,EACD7C,QAAQmD,GACNR,aAAatB,GAEb,MAAMyB,EAAQM,EAAkBD,IAC1BE,KAAEA,EAAIN,QAAEA,GAAYD,GACpBQ,SAAEA,GAAa3D,EAErBA,EAAQ2D,SAAWA,GAAYD,EAE/BrD,IAAU,CAAEL,UAASmD,UACrB3B,EAAOoC,KAAKC,EAAMC,IAAI,uBAAuBJ,MAG3C,CAACK,EAAYC,cAAeD,EAAYE,cAAeF,EAAYG,cAAcC,SAC/ET,GAGFlC,EAAOoC,KAAKC,EAAMO,IAAIhB,IAKxB5B,EAAO2B,MAAMK,EACd,IAKL9B,EAAa2C,YAAW,KACtBrE,EAAQ2D,SAAWI,EAAYE,cAC/B3B,GAAO,GACNvC,EAAOuE,YAAYC,YAGtBhE,EAAIiE,GAAG,SAAS,KACdxE,EAAQ2D,SAAWI,EAAYG,aAC/B5B,GAAO,GAEX"}
1
+ {"version":3,"file":"render.js","sources":["../../src/node/render.tsx"],"sourcesContent":["import type { StaticHandler } from '@remix-run/router';\nimport chalk from 'chalk';\nimport type { Request, Response as ExpressResponse } from 'express';\nimport React from 'react';\nimport { renderToPipeableStream } from 'react-dom/server';\nimport type { StaticHandlerContext } from 'react-router-dom/server';\nimport { createStaticRouter, StaticRouterProvider } from 'react-router-dom/server.mjs';\nimport StreamError from '@constants/stream-error';\nimport type { IServerContext } from '@context/server';\nimport { ServerProvider } from '@context/server';\nimport handleResponse from '@helpers/handle-response';\nimport type { IObtainStreamErrorOut } from '@helpers/obtain-stream-error';\nimport obtainStreamError from '@helpers/obtain-stream-error';\nimport createFetchRequest from '@node/create-fetch-request';\nimport type { TApp } from '@node/entry';\nimport writeResponse from '@node/write-response';\nimport type ServerConfig from '@services/server-config';\nimport SsrManifest from '@services/ssr-manifest';\n\nexport interface IRequestContext<TAppProps = Record<any, any>> {\n req: Request;\n res: ExpressResponse;\n appProps: NonNullable<TAppProps>;\n html: { header: string; footer: string };\n routerContext?: StaticHandlerContext;\n serverContext?: IServerContext;\n isStream?: boolean;\n didError?: StreamError;\n}\n\nexport type TRender<TAppProps = Record<any, any>> = (\n config: ServerConfig,\n context: IRequestContext<TAppProps>,\n options: IRenderOptions,\n) => Promise<void>;\n\nexport interface IRenderParams<TAppProps = Record<string, any>> {\n App: TApp<TAppProps>;\n handler: StaticHandler;\n}\n\nexport interface IRenderOptions<TAppProps = Record<string, any>> {\n abortDelay?: number;\n onRouterReady?: (params: {\n context: IRequestContext<TAppProps>;\n }) => Promise<IRouterReadyOut> | IRouterReadyOut;\n onShellReady?: (params: { context: IRequestContext<TAppProps> }) => IShellReadyOut;\n onShellError?: (params: {\n context: IRequestContext<TAppProps>;\n error: Error;\n }) => string | undefined | void; // return html or undefined\n onError?: (params: { context: IRequestContext<TAppProps>; error: IObtainStreamErrorOut }) => void;\n onResponse?: (params: {\n context: IRequestContext<TAppProps>;\n html: string;\n }) => string | undefined | void;\n getState?: (params: {\n context: IRequestContext<TAppProps>;\n }) => Record<string, Record<string, any>> | undefined | void;\n}\n\nexport interface IRouterReadyOut {\n isStream?: boolean;\n}\n\nexport interface IShellReadyOut {\n header?: string;\n footer?: string;\n}\n\n/**\n * Render application\n */\nasync function render(\n { App, handler }: IRenderParams, // @see entry (bind)\n config: ServerConfig,\n context: IRequestContext,\n {\n onRouterReady,\n onShellReady,\n onResponse,\n onShellError,\n onError,\n getState,\n abortDelay = 15000,\n }: IRenderOptions,\n): Promise<void> {\n const { req, res } = context;\n const fetchRequest = createFetchRequest(req);\n\n context.routerContext = (await handler.query(fetchRequest)) as StaticHandlerContext;\n /**\n * Handle response from page loader, router context can be Response\n */\n const statusCode = handleResponse(res, context.routerContext);\n\n if (!statusCode) {\n return;\n }\n\n SsrManifest.get(config.getParams().root).injectAssets(context);\n\n const { isStream = true } = (await onRouterReady?.({ context })) ?? {};\n\n context.isStream = isStream;\n context.serverContext = { response: null, isServer: true };\n\n const router = createStaticRouter(handler.dataRoutes, context.routerContext);\n const write = res.write.bind(res);\n const Logger = config.getLogger();\n let abortTimer: NodeJS.Timer | undefined = undefined;\n\n /**\n * Listen response and stream to add possibility modify html on fly\n * E.g. listen stream and append some data\n */\n res.write = (data: string | Uint8Array, ...args): boolean => {\n const isString = typeof data === 'string';\n const html = isString ? data : Buffer.from(data).toString();\n const additionalHtml = onResponse?.({ context, html });\n\n if (additionalHtml) {\n return write(\n isString ? `${additionalHtml}${data}` : Buffer.concat([Buffer.from(additionalHtml), data]),\n ...args,\n ) as boolean;\n }\n\n return write(data, ...args) as boolean;\n };\n\n const { serverContext, routerContext, appProps } = context;\n\n const { pipe, abort } = renderToPipeableStream(\n <ServerProvider context={serverContext}>\n <App server={{ ...appProps, req }}>\n <StaticRouterProvider router={router} context={routerContext} hydrate={false} />\n </App>\n </ServerProvider>,\n {\n onShellReady(): void {\n if (!isStream) {\n return;\n }\n\n writeResponse(context, {\n pipe,\n statusCode,\n onShellReady,\n getState,\n });\n },\n onAllReady(): void {\n clearTimeout(abortTimer);\n\n if (isStream) {\n return;\n }\n\n writeResponse(context, {\n pipe,\n statusCode,\n onShellReady,\n getState,\n });\n },\n onShellError(e: Error): void {\n const htmlError =\n onShellError?.({ context, error: e }) ||\n `<!doctype html><p>Something went wrong: ${e.message}</p>`;\n\n res.status(500);\n res.setHeader('content-type', 'text/html');\n res.send(htmlError);\n },\n onError(err): void {\n clearTimeout(abortTimer);\n\n const error = obtainStreamError(err);\n const { code, message } = error;\n const { didError } = context;\n\n context.didError = didError ?? code;\n\n onError?.({ context, error });\n Logger.info(chalk.red(`Stream error. Code: ${code}`));\n\n if (\n [StreamError.RenderAborted, StreamError.RenderTimeout, StreamError.RenderCancel].includes(\n code,\n )\n ) {\n Logger.info(chalk.dim(message));\n\n return;\n }\n\n Logger.error(err as string);\n },\n },\n );\n\n // Abandon and switch to client rendering if enough time passes.\n abortTimer = setTimeout(() => {\n context.didError = StreamError.RenderTimeout;\n abort();\n }, abortDelay);\n\n // Detect cancel request\n req.on('close', () => {\n context.didError = StreamError.RenderCancel;\n abort();\n });\n}\n\nexport default render;\n"],"names":["async","render","App","handler","config","context","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","abortDelay","req","res","fetchRequest","createFetchRequest","routerContext","query","statusCode","handleResponse","SsrManifest","get","getParams","root","injectAssets","isStream","serverContext","response","isServer","router","createStaticRouter","dataRoutes","write","bind","Logger","getLogger","abortTimer","data","args","isString","html","Buffer","from","toString","additionalHtml","concat","appProps","pipe","abort","renderToPipeableStream","React","createElement","ServerProvider","server","StaticRouterProvider","hydrate","writeResponse","onAllReady","clearTimeout","e","htmlError","error","message","status","setHeader","send","err","obtainStreamError","code","didError","info","chalk","red","StreamError","RenderAborted","RenderTimeout","RenderCancel","includes","dim","setTimeout","on"],"mappings":"sfAyEAA,eAAeC,GACbC,IAAEA,EAAGC,QAAEA,GACPC,EACAC,GACAC,cACEA,EAAaC,aACbA,EAAYC,WACZA,EAAUC,aACVA,EAAYC,QACZA,EAAOC,SACPA,EAAQC,WACRA,EAAa,OAGf,MAAMC,IAAEA,EAAGC,IAAEA,GAAQT,EACfU,EAAeC,EAAmBH,GAExCR,EAAQY,oBAAuBd,EAAQe,MAAMH,GAI7C,MAAMI,EAAaC,EAAeN,EAAKT,EAAQY,eAE/C,IAAKE,EACH,OAGFE,EAAYC,IAAIlB,EAAOmB,YAAYC,MAAMC,aAAapB,GAEtD,MAAMqB,SAAEA,GAAW,SAAgBpB,IAAgB,CAAED,cAAe,GAEpEA,EAAQqB,SAAWA,EACnBrB,EAAQsB,cAAgB,CAAEC,SAAU,KAAMC,UAAU,GAEpD,MAAMC,EAASC,EAAmB5B,EAAQ6B,WAAY3B,EAAQY,eACxDgB,EAAQnB,EAAImB,MAAMC,KAAKpB,GACvBqB,EAAS/B,EAAOgC,YACtB,IAAIC,EAMJvB,EAAImB,MAAQ,CAACK,KAA8BC,KACzC,MAAMC,EAA2B,iBAATF,EAClBG,EAAOD,EAAWF,EAAOI,OAAOC,KAAKL,GAAMM,WAC3CC,EAAiBrC,IAAa,CAAEH,UAASoC,SAE/C,OAAII,EACKZ,EACLO,EAAW,GAAGK,IAAiBP,IAASI,OAAOI,OAAO,CAACJ,OAAOC,KAAKE,GAAiBP,OACjFC,GAIAN,EAAMK,KAASC,EAAgB,EAGxC,MAAMZ,cAAEA,EAAaV,cAAEA,EAAa8B,SAAEA,GAAa1C,GAE7C2C,KAAEA,EAAIC,MAAEA,GAAUC,EACtBC,EAACC,cAAAC,EAAe,CAAAhD,QAASsB,GACvBwB,EAACC,cAAAlD,GAAIoD,OAAQ,IAAKP,EAAUlC,QAC1BsC,EAAAC,cAACG,EAAqB,CAAAzB,OAAQA,EAAQzB,QAASY,EAAeuC,SAAS,MAG3E,CACEjD,eACOmB,GAIL+B,EAAcpD,EAAS,CACrB2C,OACA7B,aACAZ,eACAI,YAEH,EACD+C,aACEC,aAAatB,GAETX,GAIJ+B,EAAcpD,EAAS,CACrB2C,OACA7B,aACAZ,eACAI,YAEH,EACDF,aAAamD,GACX,MAAMC,EACJpD,IAAe,CAAEJ,UAASyD,MAAOF,KACjC,2CAA2CA,EAAEG,cAE/CjD,EAAIkD,OAAO,KACXlD,EAAImD,UAAU,eAAgB,aAC9BnD,EAAIoD,KAAKL,EACV,EACDnD,QAAQyD,GACNR,aAAatB,GAEb,MAAMyB,EAAQM,EAAkBD,IAC1BE,KAAEA,EAAIN,QAAEA,GAAYD,GACpBQ,SAAEA,GAAajE,EAErBA,EAAQiE,SAAWA,GAAYD,EAE/B3D,IAAU,CAAEL,UAASyD,UACrB3B,EAAOoC,KAAKC,EAAMC,IAAI,uBAAuBJ,MAG3C,CAACK,EAAYC,cAAeD,EAAYE,cAAeF,EAAYG,cAAcC,SAC/ET,GAGFlC,EAAOoC,KAAKC,EAAMO,IAAIhB,IAKxB5B,EAAO2B,MAAMK,EACd,IAKL9B,EAAa2C,YAAW,KACtB3E,EAAQiE,SAAWI,EAAYE,cAC/B3B,GAAO,GACNrC,GAGHC,EAAIoE,GAAG,SAAS,KACd5E,EAAQiE,SAAWI,EAAYG,aAC/B5B,GAAO,GAEX"}
package/node/server.js CHANGED
@@ -1,2 +1,2 @@
1
- import e from"path";import r from"compression";import o from"express";import t from"../helpers/print-server-info.js";import s from"../services/prepare-server.js";async function n(n){const i=o();if(n.setApp(i),n.isProd){const{root:t,publicDir:s,isSPA:a}=n.getParams();i.use(r()),a||i.use(((e,r,o)=>{"/index.html"===e.url&&(e.url="/index-not-found.html"),o()})),i.use(o.static(e.resolve(`${t}/${s}`),{index:!!a&&void 0}))}else{const e=await(await import("vite")).createServer({server:{middlewareMode:!0,watch:{usePolling:!0,interval:100}},appType:"custom",mode:n.mode});i.use(e.middlewares),n.setVite(e)}const a=s.init(n);return n.isSPA?i.use("*",((e,r,o)=>{(async()=>{try{const o=(await a.loadHtml(e)).join("");r.send(o)}catch(e){o(e)}})()})):(await a.onAppCreated(),i.use("*",((e,r,o)=>{(async()=>{try{const[{render:o,onRequest:t,onRouterReady:s,onShellReady:i,onResponse:p,onShellError:l,onError:d,getState:m},c]=await Promise.all([a.loadEntrypoint(),a.loadHtml(e)]),{appProps:u}=await(t?.(e,r))??{},[h,f]=c,v={req:e,res:r,appProps:u??{},html:{header:h,footer:f}};await o(n,v,{onRouterReady:s,onShellReady:i,onShellError:l,onResponse:p,onError:d,getState:m})}catch(e){o(e)}})()}))),{run:({version:e,isPrintInfo:r=!0}={})=>{const{port:o,host:s}=n.getParams();n.isHost&&!n.isProd&&(n.getVite().config.server.host=s);const a=i.listen(o,s,(()=>{r&&t(a,n,{version:e})}));return a}}}export{n as default};
1
+ import e from"path";import r from"compression";import o from"express";import t from"../helpers/print-server-info.js";import s from"../services/prepare-server.js";async function a(a){const n=o().disable("x-powered-by");if(a.setApp(n),a.isProd){const{root:t,publicDir:s,isSPA:i}=a.getParams();n.use(r()),i||n.use(((e,r,o)=>{"/index.html"===e.url&&(e.url="/index-not-found.html"),o()})),n.use(o.static(e.resolve(`${t}/${s}`),{index:!!i&&void 0}))}else{const e=await(await import("vite")).createServer({server:{middlewareMode:!0,watch:{usePolling:!0,interval:100}},appType:"custom",mode:a.mode});n.use(e.middlewares),a.setVite(e)}const i=s.init(a);return a.isSPA?n.use("*",((e,r,o)=>{(async()=>{try{const o=(await i.loadHtml(e)).join("");r.send(o)}catch(e){o(e)}})()})):(await i.onAppCreated(),n.use("*",((e,r,o)=>{(async()=>{try{const[{render:o,abortDelay:t,onRequest:s,onRouterReady:n,onShellReady:l,onResponse:p,onShellError:d,onError:m,getState:c},u]=await Promise.all([i.loadEntrypoint(),i.loadHtml(e)]),{appProps:h}=await(s?.(e,r))??{},[y,f]=u,v={req:e,res:r,appProps:h??{},html:{header:y,footer:f}};await o(a,v,{abortDelay:t,onRouterReady:n,onShellReady:l,onShellError:d,onResponse:p,onError:m,getState:c})}catch(e){o(e)}})()}))),{run:({version:e,isPrintInfo:r=!0}={})=>{const{port:o,host:s}=a.getParams();a.isHost&&!a.isProd&&(a.getVite().config.server.host=s);const i=n.listen(o,s,(()=>{r&&t(i,a,{version:e})}));return i}}}export{a as default};
2
2
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sources":["../../src/node/server.ts"],"sourcesContent":["import type { Server } from 'node:net';\nimport path from 'path';\nimport compression from 'compression';\nimport express from 'express';\nimport printServerInfo from '@helpers/print-server-info';\nimport type { IRequestContext } from '@node/render';\nimport PrepareServer from '@services/prepare-server';\nimport type ServerConfig from '@services/server-config';\n\nexport interface ICreateServerOut {\n run: (options?: { version?: string; isPrintInfo?: boolean }) => Server;\n}\n\n/**\n * Create SSR server\n */\nasync function createServer(config: ServerConfig): Promise<ICreateServerOut> {\n const app = express();\n\n config.setApp(app);\n\n if (!config.isProd) {\n // Create Vite server in middleware mode and configure the app type as\n // 'custom', disabling Vite's own HTML serving logic so parent server\n // can take control\n const vite = await (\n await import('vite')\n ).createServer({\n server: {\n middlewareMode: true,\n watch: {\n // During tests, we edit the files too fast and sometimes chokidar\n // misses change events, so enforce polling for consistency\n usePolling: true,\n interval: 100,\n },\n },\n appType: 'custom',\n mode: config.mode,\n });\n\n // Use vite's connect instance as middleware\n app.use(vite.middlewares);\n\n config.setVite(vite);\n } else {\n const { root, publicDir, isSPA } = config.getParams();\n\n app.use(compression());\n\n if (!isSPA) {\n // ignore index.html file in SSR mode\n app.use((req, res, next) => {\n if (req.url === '/index.html') {\n req.url = '/index-not-found.html';\n }\n\n next();\n });\n }\n\n app.use(\n express.static(path.resolve(`${root}/${publicDir}`), {\n index: isSPA ? undefined : false,\n }),\n );\n }\n\n const prepareServer = PrepareServer.init(config);\n\n // SSR mode\n if (!config.isSPA) {\n await prepareServer.onAppCreated();\n\n app.use('*', (req, res, next) => {\n void (async () => {\n try {\n const [\n {\n render,\n onRequest,\n onRouterReady,\n onShellReady,\n onResponse,\n onShellError,\n onError,\n getState,\n },\n clientHtml,\n ] = await Promise.all([prepareServer.loadEntrypoint(), prepareServer.loadHtml(req)]);\n const { appProps } = (await onRequest?.(req, res)) ?? {};\n const [header, footer] = clientHtml;\n\n const context: IRequestContext = {\n req,\n res,\n appProps: appProps ?? {},\n html: { header, footer },\n };\n\n await render(config, context, {\n onRouterReady,\n onShellReady,\n onShellError,\n onResponse,\n onError,\n getState,\n });\n } catch (e) {\n next(e);\n }\n })();\n });\n } else {\n // SPA mode, redirect any request to index.html\n app.use('*', (req, res, next) => {\n void (async () => {\n try {\n const html = (await prepareServer.loadHtml(req)).join('');\n\n res.send(html);\n } catch (e) {\n next(e);\n }\n })();\n });\n }\n\n return {\n run: ({ version, isPrintInfo = true } = {}): Server => {\n const { port, host } = config.getParams();\n\n // update resolved host for print network link\n if (config.isHost && !config.isProd) {\n config.getVite()!.config.server.host = host;\n }\n\n const server = app.listen(port, host, () => {\n if (!isPrintInfo) {\n return;\n }\n\n void printServerInfo(server, config, { version });\n });\n\n return server;\n },\n };\n}\n\nexport default createServer;\n"],"names":["async","createServer","config","app","express","setApp","isProd","root","publicDir","isSPA","getParams","use","compression","req","res","next","url","static","path","resolve","index","undefined","vite","import","server","middlewareMode","watch","usePolling","interval","appType","mode","middlewares","setVite","prepareServer","PrepareServer","init","html","loadHtml","join","send","e","onAppCreated","render","onRequest","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","clientHtml","Promise","all","loadEntrypoint","appProps","header","footer","context","run","version","isPrintInfo","port","host","isHost","getVite","listen","printServerInfo"],"mappings":"kKAgBAA,eAAeC,EAAaC,GAC1B,MAAMC,EAAMC,IAIZ,GAFAF,EAAOG,OAAOF,GAETD,EAAOI,OAwBL,CACL,MAAMC,KAAEA,EAAIC,UAAEA,EAASC,MAAEA,GAAUP,EAAOQ,YAE1CP,EAAIQ,IAAIC,KAEHH,GAEHN,EAAIQ,KAAI,CAACE,EAAKC,EAAKC,KACD,gBAAZF,EAAIG,MACNH,EAAIG,IAAM,yBAGZD,GAAM,IAIVZ,EAAIQ,IACFP,EAAQa,OAAOC,EAAKC,QAAQ,GAAGZ,KAAQC,KAAc,CACnDY,QAAOX,QAAQY,IAGpB,KA7CmB,CAIlB,MAAMC,cACEC,OAAO,SACbtB,aAAa,CACbuB,OAAQ,CACNC,gBAAgB,EAChBC,MAAO,CAGLC,YAAY,EACZC,SAAU,MAGdC,QAAS,SACTC,KAAM5B,EAAO4B,OAIf3B,EAAIQ,IAAIW,EAAKS,aAEb7B,EAAO8B,QAAQV,EAChB,CAuBD,MAAMW,EAAgBC,EAAcC,KAAKjC,GA4DzC,OAzDKA,EAAOO,MA4CVN,EAAIQ,IAAI,KAAK,CAACE,EAAKC,EAAKC,KACjB,WACH,IACE,MAAMqB,SAAcH,EAAcI,SAASxB,IAAMyB,KAAK,IAEtDxB,EAAIyB,KAAKH,EACV,CAAC,MAAOI,GACPzB,EAAKyB,EACN,CACF,EARI,EAQD,WApDAP,EAAcQ,eAEpBtC,EAAIQ,IAAI,KAAK,CAACE,EAAKC,EAAKC,KACjB,WACH,IACE,OACE2B,OACEA,EAAMC,UACNA,EAASC,cACTA,EAAaC,aACbA,EAAYC,WACZA,EAAUC,aACVA,EAAYC,QACZA,EAAOC,SACPA,GAEFC,SACQC,QAAQC,IAAI,CAACnB,EAAcoB,iBAAkBpB,EAAcI,SAASxB,MACxEyC,SAAEA,SAAoBX,IAAY9B,EAAKC,KAAS,IAC/CyC,EAAQC,GAAUN,EAEnBO,EAA2B,CAC/B5C,MACAC,MACAwC,SAAUA,GAAY,CAAE,EACxBlB,KAAM,CAAEmB,SAAQC,iBAGZd,EAAOxC,EAAQuD,EAAS,CAC5Bb,gBACAC,eACAE,eACAD,aACAE,UACAC,YAEH,CAAC,MAAOT,GACPzB,EAAKyB,EACN,CACF,EApCI,EAoCD,KAiBD,CACLkB,IAAK,EAAGC,UAASC,eAAc,GAAS,CAAA,KACtC,MAAMC,KAAEA,EAAIC,KAAEA,GAAS5D,EAAOQ,YAG1BR,EAAO6D,SAAW7D,EAAOI,SAC3BJ,EAAO8D,UAAW9D,OAAOsB,OAAOsC,KAAOA,GAGzC,MAAMtC,EAASrB,EAAI8D,OAAOJ,EAAMC,GAAM,KAC/BF,GAIAM,EAAgB1C,EAAQtB,EAAQ,CAAEyD,WAAU,IAGnD,OAAOnC,CAAM,EAGnB"}
1
+ {"version":3,"file":"server.js","sources":["../../src/node/server.ts"],"sourcesContent":["import type { Server } from 'node:net';\nimport path from 'path';\nimport compression from 'compression';\nimport express from 'express';\nimport printServerInfo from '@helpers/print-server-info';\nimport type { IRequestContext } from '@node/render';\nimport PrepareServer from '@services/prepare-server';\nimport type ServerConfig from '@services/server-config';\n\nexport interface ICreateServerOut {\n run: (options?: { version?: string; isPrintInfo?: boolean }) => Server;\n}\n\n/**\n * Create SSR server\n */\nasync function createServer(config: ServerConfig): Promise<ICreateServerOut> {\n const app = express().disable('x-powered-by');\n\n config.setApp(app);\n\n if (!config.isProd) {\n // Create Vite server in middleware mode and configure the app type as\n // 'custom', disabling Vite's own HTML serving logic so parent server\n // can take control\n const vite = await (\n await import('vite')\n ).createServer({\n server: {\n middlewareMode: true,\n watch: {\n // During tests, we edit the files too fast and sometimes chokidar\n // misses change events, so enforce polling for consistency\n usePolling: true,\n interval: 100,\n },\n },\n appType: 'custom',\n mode: config.mode,\n });\n\n // Use vite's connect instance as middleware\n app.use(vite.middlewares);\n\n config.setVite(vite);\n } else {\n const { root, publicDir, isSPA } = config.getParams();\n\n app.use(compression());\n\n if (!isSPA) {\n // ignore index.html file in SSR mode\n app.use((req, res, next) => {\n if (req.url === '/index.html') {\n req.url = '/index-not-found.html';\n }\n\n next();\n });\n }\n\n app.use(\n express.static(path.resolve(`${root}/${publicDir}`), {\n index: isSPA ? undefined : false,\n }),\n );\n }\n\n const prepareServer = PrepareServer.init(config);\n\n // SSR mode\n if (!config.isSPA) {\n await prepareServer.onAppCreated();\n\n app.use('*', (req, res, next) => {\n void (async () => {\n try {\n const [\n {\n render,\n abortDelay,\n onRequest,\n onRouterReady,\n onShellReady,\n onResponse,\n onShellError,\n onError,\n getState,\n },\n clientHtml,\n ] = await Promise.all([prepareServer.loadEntrypoint(), prepareServer.loadHtml(req)]);\n const { appProps } = (await onRequest?.(req, res)) ?? {};\n const [header, footer] = clientHtml;\n\n const context: IRequestContext = {\n req,\n res,\n appProps: appProps ?? {},\n html: { header, footer },\n };\n\n await render(config, context, {\n abortDelay,\n onRouterReady,\n onShellReady,\n onShellError,\n onResponse,\n onError,\n getState,\n });\n } catch (e) {\n next(e);\n }\n })();\n });\n } else {\n // SPA mode, redirect any request to index.html\n app.use('*', (req, res, next) => {\n void (async () => {\n try {\n const html = (await prepareServer.loadHtml(req)).join('');\n\n res.send(html);\n } catch (e) {\n next(e);\n }\n })();\n });\n }\n\n return {\n run: ({ version, isPrintInfo = true } = {}): Server => {\n const { port, host } = config.getParams();\n\n // update resolved host for print network link\n if (config.isHost && !config.isProd) {\n config.getVite()!.config.server.host = host;\n }\n\n const server = app.listen(port, host, () => {\n if (!isPrintInfo) {\n return;\n }\n\n void printServerInfo(server, config, { version });\n });\n\n return server;\n },\n };\n}\n\nexport default createServer;\n"],"names":["async","createServer","config","app","express","disable","setApp","isProd","root","publicDir","isSPA","getParams","use","compression","req","res","next","url","static","path","resolve","index","undefined","vite","import","server","middlewareMode","watch","usePolling","interval","appType","mode","middlewares","setVite","prepareServer","PrepareServer","init","html","loadHtml","join","send","e","onAppCreated","render","abortDelay","onRequest","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","clientHtml","Promise","all","loadEntrypoint","appProps","header","footer","context","run","version","isPrintInfo","port","host","isHost","getVite","listen","printServerInfo"],"mappings":"kKAgBAA,eAAeC,EAAaC,GAC1B,MAAMC,EAAMC,IAAUC,QAAQ,gBAI9B,GAFAH,EAAOI,OAAOH,GAETD,EAAOK,OAwBL,CACL,MAAMC,KAAEA,EAAIC,UAAEA,EAASC,MAAEA,GAAUR,EAAOS,YAE1CR,EAAIS,IAAIC,KAEHH,GAEHP,EAAIS,KAAI,CAACE,EAAKC,EAAKC,KACD,gBAAZF,EAAIG,MACNH,EAAIG,IAAM,yBAGZD,GAAM,IAIVb,EAAIS,IACFR,EAAQc,OAAOC,EAAKC,QAAQ,GAAGZ,KAAQC,KAAc,CACnDY,QAAOX,QAAQY,IAGpB,KA7CmB,CAIlB,MAAMC,cACEC,OAAO,SACbvB,aAAa,CACbwB,OAAQ,CACNC,gBAAgB,EAChBC,MAAO,CAGLC,YAAY,EACZC,SAAU,MAGdC,QAAS,SACTC,KAAM7B,EAAO6B,OAIf5B,EAAIS,IAAIW,EAAKS,aAEb9B,EAAO+B,QAAQV,EAChB,CAuBD,MAAMW,EAAgBC,EAAcC,KAAKlC,GA8DzC,OA3DKA,EAAOQ,MA8CVP,EAAIS,IAAI,KAAK,CAACE,EAAKC,EAAKC,KACjB,WACH,IACE,MAAMqB,SAAcH,EAAcI,SAASxB,IAAMyB,KAAK,IAEtDxB,EAAIyB,KAAKH,EACV,CAAC,MAAOI,GACPzB,EAAKyB,EACN,CACF,EARI,EAQD,WAtDAP,EAAcQ,eAEpBvC,EAAIS,IAAI,KAAK,CAACE,EAAKC,EAAKC,KACjB,WACH,IACE,OACE2B,OACEA,EAAMC,WACNA,EAAUC,UACVA,EAASC,cACTA,EAAaC,aACbA,EAAYC,WACZA,EAAUC,aACVA,EAAYC,QACZA,EAAOC,SACPA,GAEFC,SACQC,QAAQC,IAAI,CAACpB,EAAcqB,iBAAkBrB,EAAcI,SAASxB,MACxE0C,SAAEA,SAAoBX,IAAY/B,EAAKC,KAAS,IAC/C0C,EAAQC,GAAUN,EAEnBO,EAA2B,CAC/B7C,MACAC,MACAyC,SAAUA,GAAY,CAAE,EACxBnB,KAAM,CAAEoB,SAAQC,iBAGZf,EAAOzC,EAAQyD,EAAS,CAC5Bf,aACAE,gBACAC,eACAE,eACAD,aACAE,UACAC,YAEH,CAAC,MAAOV,GACPzB,EAAKyB,EACN,CACF,EAtCI,EAsCD,KAiBD,CACLmB,IAAK,EAAGC,UAASC,eAAc,GAAS,CAAA,KACtC,MAAMC,KAAEA,EAAIC,KAAEA,GAAS9D,EAAOS,YAG1BT,EAAO+D,SAAW/D,EAAOK,SAC3BL,EAAOgE,UAAWhE,OAAOuB,OAAOuC,KAAOA,GAGzC,MAAMvC,EAAStB,EAAIgE,OAAOJ,EAAMC,GAAM,KAC/BF,GAIAM,EAAgB3C,EAAQvB,EAAQ,CAAE2D,WAAU,IAGnD,OAAOpC,CAAM,EAGnB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lomray/vite-ssr-boost",
3
- "version": "1.0.0-beta.14",
3
+ "version": "1.0.0-beta.16",
4
4
  "description": "Vite plugin for create awesome SSR or SPA applications on React.",
5
5
  "type": "module",
6
6
  "keywords": [
package/plugin.d.ts CHANGED
@@ -4,8 +4,7 @@ import { IPluginOptions as IMakeAliasesPluginOptions } from "./plugins/make-alia
4
4
  interface IPluginOptions {
5
5
  indexFile?: string;
6
6
  serverFile?: string;
7
- abortDelay?: number;
8
- hasLazyRoutePlugin?: boolean;
7
+ preloadAssets?: boolean;
9
8
  tsconfigAliases?: boolean | IMakeAliasesPluginOptions;
10
9
  customShortcuts?: {
11
10
  key: string;
package/plugin.js CHANGED
@@ -1,2 +1,2 @@
1
- import e from"node:path";import o from"./constants/cli-actions.js";import i from"./constants/plugin-name.js";import s from"./plugins/make-aliases.js";import n from"./plugins/normalize-route.js";const t={indexFile:"index.html",serverFile:"server.ts",abortDelay:1e4,hasLazyRoutePlugin:!0,tsconfigAliases:!0};function a(a={}){const r=new URL(import.meta.url),p=global.viteBoostAction||process.env.SSR_BOOST_ACTION,l={...t,...a},m=[{name:i,enforce:"pre",pluginOptions:{...l,pluginPath:e.dirname(r.pathname),action:p,isDev:p===o.dev},config:(e,{ssrBuild:i})=>(e.define={...e.define??{},__IS_SSR__:"1"===process.env.SSR_BOOST_IS_SSR||"dev"===p},i?{...e,publicDir:!1,...p===o.build?{appType:"custom"}:{}}:e)}],{hasLazyRoutePlugin:u,tsconfigAliases:c}=l;return u&&m.push(n()),c&&m.push(s("boolean"==typeof c?void 0:c)),m}export{a as default};
1
+ import e from"node:path";import i from"./constants/cli-actions.js";import o from"./constants/plugin-name.js";import s from"./plugins/make-aliases.js";import n from"./plugins/normalize-route.js";const t={indexFile:"index.html",serverFile:"server.ts",preloadAssets:!0,tsconfigAliases:!0};function r(r={}){const l=new URL(import.meta.url),a=global.viteBoostAction||process.env.SSR_BOOST_ACTION,p={...t,...r},m="1"===process.env.SSR_BOOST_IS_SSR||"dev"===a,u=a===i.build,d=[{name:o,enforce:"pre",pluginOptions:{...p,pluginPath:e.dirname(l.pathname),action:a,isDev:a===i.dev},config:(e,{ssrBuild:i})=>(e.define={...e.define??{},__IS_SSR__:m},e.build={...e.build??{},modulePreload:e.build?.modulePreload??!1},i?{...e,...u?{appType:"custom"}:{},publicDir:!1}:(m&&u&&(e.build.manifest=!0),e))}],{tsconfigAliases:c}=p;return c&&d.push(s("boolean"==typeof c?void 0:c)),d.push(n({isSSR:m})),d}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 indexFile?: string; // default: index.html\n serverFile?: string; // default: server.ts\n abortDelay?: number; // How long the server waits for data before giving up. default: 10000 (10 sec)\n hasLazyRoutePlugin?: boolean; // Possibility to use custom export route component @see FCRoute interface\n tsconfigAliases?: boolean | IMakeAliasesPluginOptions; // Read aliases from tsconfig\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 abortDelay: 10000,\n hasLazyRoutePlugin: true,\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\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 config(config, { ssrBuild }) {\n config.define = {\n ...(config.define ?? {}),\n __IS_SSR__: process.env.SSR_BOOST_IS_SSR === '1' || action === 'dev',\n };\n\n if (!ssrBuild) {\n return config;\n }\n\n return {\n ...config,\n publicDir: false,\n ...(action === CliActions.build ? { appType: 'custom' } : {}),\n };\n },\n },\n ];\n\n const { hasLazyRoutePlugin, tsconfigAliases } = mergedOptions;\n\n if (hasLazyRoutePlugin) {\n plugins.push(ViteNormalizeRouterPlugin());\n }\n\n if (tsconfigAliases) {\n plugins.push(\n ViteMakeAliasesPlugin(typeof tsconfigAliases === 'boolean' ? undefined : tsconfigAliases),\n );\n }\n\n return plugins;\n}\n\nexport default ViteSsrBoostPlugin;\n"],"names":["defaultOptions","indexFile","serverFile","abortDelay","hasLazyRoutePlugin","tsconfigAliases","ViteSsrBoostPlugin","options","dirInfo","URL","url","action","global","viteBoostAction","process","env","SSR_BOOST_ACTION","mergedOptions","plugins","name","PLUGIN_NAME","enforce","pluginOptions","pluginPath","path","dirname","pathname","isDev","CliActions","dev","config","ssrBuild","define","__IS_SSR__","SSR_BOOST_IS_SSR","publicDir","build","appType","push","ViteNormalizeRouterPlugin","ViteMakeAliasesPlugin","undefined"],"mappings":"kMAuBA,MAAMA,EAAiC,CACrCC,UAAW,aACXC,WAAY,YACZC,WAAY,IACZC,oBAAoB,EACpBC,iBAAiB,GAOnB,SAASC,EAAmBC,EAA0B,IACpD,MAAMC,EAAU,IAAIC,gBAAgBC,KAC9BC,EAAUC,OAAOC,iBAAmBC,QAAQC,IAAIC,iBAChDC,EAAgC,IAAKjB,KAAmBO,GAExDW,EAAoB,CACxB,CACEC,KAAMC,EACNC,QAAS,MAETC,cAAe,IACVL,EACHM,WAAYC,EAAKC,QAAQjB,EAAQkB,UACjCf,SACAgB,MAAOhB,IAAWiB,EAAWC,KAE/BC,OAAM,CAACA,GAAQC,SAAEA,MACfD,EAAOE,OAAS,IACVF,EAAOE,QAAU,GACrBC,WAA6C,MAAjCnB,QAAQC,IAAImB,kBAAuC,QAAXvB,GAGjDoB,EAIE,IACFD,EACHK,WAAW,KACPxB,IAAWiB,EAAWQ,MAAQ,CAAEC,QAAS,UAAa,IANnDP,MAYT1B,mBAAEA,EAAkBC,gBAAEA,GAAoBY,EAYhD,OAVIb,GACFc,EAAQoB,KAAKC,KAGXlC,GACFa,EAAQoB,KACNE,EAAiD,kBAApBnC,OAAgCoC,EAAYpC,IAItEa,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 indexFile?: string; // default: index.html\n serverFile?: string; // default: server.ts\n preloadAssets?: boolean; // default: true\n tsconfigAliases?: boolean | IMakeAliasesPluginOptions; // Read aliases from tsconfig\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 preloadAssets: true,\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 === '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, { ssrBuild }) {\n config.define = {\n ...(config.define ?? {}),\n __IS_SSR__: isSSR,\n };\n\n config.build = {\n ...(config.build ?? {}),\n modulePreload: config.build?.modulePreload ?? false,\n };\n\n if (!ssrBuild) {\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 } = mergedOptions;\n\n if (tsconfigAliases) {\n plugins.push(\n ViteMakeAliasesPlugin(typeof tsconfigAliases === 'boolean' ? undefined : tsconfigAliases),\n );\n }\n\n plugins.push(ViteNormalizeRouterPlugin({ isSSR }));\n\n return plugins;\n}\n\nexport default ViteSsrBoostPlugin;\n"],"names":["defaultOptions","indexFile","serverFile","preloadAssets","tsconfigAliases","ViteSsrBoostPlugin","options","dirInfo","URL","url","action","global","viteBoostAction","process","env","SSR_BOOST_ACTION","mergedOptions","isSSR","SSR_BOOST_IS_SSR","isBuild","CliActions","build","plugins","name","PLUGIN_NAME","enforce","pluginOptions","pluginPath","path","dirname","pathname","isDev","dev","config","ssrBuild","define","__IS_SSR__","modulePreload","appType","publicDir","manifest","push","ViteMakeAliasesPlugin","undefined","ViteNormalizeRouterPlugin"],"mappings":"kMAsBA,MAAMA,EAAiC,CACrCC,UAAW,aACXC,WAAY,YACZC,eAAe,EACfC,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,kBAAuC,QAAXR,EAChDS,EAAUT,IAAWU,EAAWC,MAEhCC,EAAoB,CACxB,CACEC,KAAMC,EACNC,QAAS,MAETC,cAAe,IACVV,EACHW,WAAYC,EAAKC,QAAQtB,EAAQuB,UACjCpB,SACAqB,MAAOrB,IAAWU,EAAWY,KAG/BC,OAAM,CAACA,GAAQC,SAAEA,MACfD,EAAOE,OAAS,IACVF,EAAOE,QAAU,GACrBC,WAAYnB,GAGdgB,EAAOZ,MAAQ,IACTY,EAAOZ,OAAS,GACpBgB,cAAeJ,EAAOZ,OAAOgB,gBAAiB,GAG3CH,EAQE,IACFD,KACCd,EAAU,CAAEmB,QAAS,UAAa,CAAA,EACtCC,WAAW,IAVPtB,GAASE,IACXc,EAAOZ,MAAOmB,UAAW,GAGpBP,OAYT7B,gBAAEA,GAAoBY,EAU5B,OARIZ,GACFkB,EAAQmB,KACNC,EAAiD,kBAApBtC,OAAgCuC,EAAYvC,IAI7EkB,EAAQmB,KAAKG,EAA0B,CAAE3B,WAElCK,CACT"}
@@ -1,4 +1,7 @@
1
1
  import { Plugin } from 'vite';
2
+ interface IPluginOptions {
3
+ isSSR?: boolean;
4
+ }
2
5
  /**
3
6
  * Add possibility to export route components like FCRoute or FCCRoute
4
7
  * USAGE: { path: '/', lazyNR: () => import('./pages/home') }
@@ -6,5 +9,5 @@ import { Plugin } from 'vite';
6
9
  * @see FCCRoute
7
10
  * @constructor
8
11
  */
9
- declare function ViteNormalizeRouterPlugin(): Plugin;
10
- export { ViteNormalizeRouterPlugin as default };
12
+ declare function ViteNormalizeRouterPlugin(options?: IPluginOptions): Plugin;
13
+ export { ViteNormalizeRouterPlugin as default, IPluginOptions };
@@ -1,2 +1,2 @@
1
- import{extname as t}from"node:path";import o from"../constants/plugin-name.js";const s=t=>`import n from '${o}/helpers/import-route';${t}`.replace(/(lazyNR)(:\s*)(\(\)\s*=>\s*import\([^)]+\))/gs,"lazy$2()=>n($3)");function r(){return{name:`${o}-normalize-route`,transform:(o,r)=>{const e=t(r).split("?")[0];if(!r.includes("node_modules")&&[".js",".ts",".tsx"].includes(e)&&(t=>/\[.*{.*path:.*lazyNR:.+import/s.test(t))(o))return{code:s(o),map:{mappings:""}}}}}export{r as default};
1
+ import{extname as r}from"node:path";import o from"../constants/plugin-name.js";import s from"../helpers/is-route-file.js";const e=r=>`import n from '${o}/helpers/import-route';${r}`.replace(/(lazyNR|lazy)(:\s*)(\(\)\s*=>\s*import\(([^)]+)\))/gs,"lazy$2()=>n($3,$4)"),t=r=>`import n from '${o}/helpers/import-route';${r}`.replace(/(lazyNR)(:\s*)(\(\)\s*=>\s*import\(([^)]+)\))/gs,"lazy$2()=>n($3)");function n(n={}){const{isSSR:m=!1}=n;return{name:`${o}-normalize-route`,transform:(o,n)=>{const p=r(n).split("?")[0];if(!n.includes("node_modules")&&[".js",".ts",".tsx"].includes(p)&&s(o))return{code:m?e(o):t(o),map:{mappings:""}}}}}export{n 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';\n\n/**\n * Detect route file\n */\nconst isRoutesFile = (code: string): boolean => /\\[.*{.*path:.*lazyNR:.+import/s.test(code);\n\n/**\n * Add normalize wrapper to lazy imports\n */\nconst normalizeRoutes = (code: string): string =>\n `import n from '${PLUGIN_NAME}/helpers/import-route';${code}`.replace(\n /(lazyNR)(:\\s*)(\\(\\)\\s*=>\\s*import\\([^)]+\\))/gs,\n 'lazy$2()=>n($3)',\n );\n\n/**\n * Add possibility to export route components like FCRoute or FCCRoute\n * USAGE: { path: '/', lazyNR: () => import('./pages/home') }\n * @see FCRoute\n * @see FCCRoute\n * @constructor\n */\nfunction ViteNormalizeRouterPlugin(): Plugin {\n return {\n name: `${PLUGIN_NAME}-normalize-route`,\n transform: (code, id) => {\n const extName = extname(id).split('?')[0]!;\n\n if (\n id.includes('node_modules') ||\n !['.js', '.ts', '.tsx'].includes(extName) ||\n !isRoutesFile(code)\n ) {\n return;\n }\n\n return {\n code: normalizeRoutes(code),\n map: { mappings: '' },\n };\n },\n };\n}\n\nexport default ViteNormalizeRouterPlugin;\n"],"names":["normalizeRoutes","code","PLUGIN_NAME","replace","ViteNormalizeRouterPlugin","name","transform","id","extName","extname","split","includes","test","isRoutesFile","map","mappings"],"mappings":"+EAOA,MAKMA,EAAmBC,GACvB,kBAAkBC,2BAAqCD,IAAOE,QAC5D,gDACA,mBAUJ,SAASC,IACP,MAAO,CACLC,KAAM,GAAGH,oBACTI,UAAW,CAACL,EAAMM,KAChB,MAAMC,EAAUC,EAAQF,GAAIG,MAAM,KAAK,GAEvC,IACEH,EAAGI,SAAS,iBACX,CAAC,MAAO,MAAO,QAAQA,SAASH,IA1BpB,CAACP,GAA0B,iCAAiCW,KAAKX,GA2B7EY,CAAaZ,GAKhB,MAAO,CACLA,KAAMD,EAAgBC,GACtBa,IAAK,CAAEC,SAAU,IAClB,EAGP"}
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}\n\n/**\n * Add normalize wrapper to lazy imports for server build\n */\nconst normalizeRoutesSSR = (code: string): string =>\n `import n from '${PLUGIN_NAME}/helpers/import-route';${code}`.replace(\n /(lazyNR|lazy)(:\\s*)(\\(\\)\\s*=>\\s*import\\(([^)]+)\\))/gs,\n 'lazy$2()=>n($3,$4)',\n );\n\n/**\n * Add normalize wrapper to lazy imports for client build\n */\nconst normalizeRoutes = (code: string): string =>\n `import n from '${PLUGIN_NAME}/helpers/import-route';${code}`.replace(\n /(lazyNR)(:\\s*)(\\(\\)\\s*=>\\s*import\\(([^)]+)\\))/gs,\n 'lazy$2()=>n($3)',\n );\n\n/**\n * Add possibility to export route components like FCRoute or FCCRoute\n * USAGE: { path: '/', lazyNR: () => import('./pages/home') }\n * @see FCRoute\n * @see FCCRoute\n * @constructor\n */\nfunction ViteNormalizeRouterPlugin(options: IPluginOptions = {}): Plugin {\n const { isSSR = false } = options;\n\n return {\n name: `${PLUGIN_NAME}-normalize-route`,\n transform: (code, id) => {\n const extName = extname(id).split('?')[0]!;\n\n if (\n id.includes('node_modules') ||\n !['.js', '.ts', '.tsx'].includes(extName) ||\n !isRoutesFile(code)\n ) {\n return;\n }\n\n return {\n code: isSSR ? normalizeRoutesSSR(code) : normalizeRoutes(code),\n map: { mappings: '' },\n };\n },\n };\n}\n\nexport default ViteNormalizeRouterPlugin;\n"],"names":["normalizeRoutesSSR","code","PLUGIN_NAME","replace","normalizeRoutes","ViteNormalizeRouterPlugin","options","isSSR","name","transform","id","extName","extname","split","includes","isRoutesFile","map","mappings"],"mappings":"0HAYA,MAAMA,EAAsBC,GAC1B,kBAAkBC,2BAAqCD,IAAOE,QAC5D,uDACA,sBAMEC,EAAmBH,GACvB,kBAAkBC,2BAAqCD,IAAOE,QAC5D,kDACA,mBAUJ,SAASE,EAA0BC,EAA0B,IAC3D,MAAMC,MAAEA,GAAQ,GAAUD,EAE1B,MAAO,CACLE,KAAM,GAAGN,oBACTO,UAAW,CAACR,EAAMS,KAChB,MAAMC,EAAUC,EAAQF,GAAIG,MAAM,KAAK,GAEvC,IACEH,EAAGI,SAAS,iBACX,CAAC,MAAO,MAAO,QAAQA,SAASH,IAChCI,EAAad,GAKhB,MAAO,CACLA,KAAMM,EAAQP,EAAmBC,GAAQG,EAAgBH,GACzDe,IAAK,CAAEC,SAAU,IAClB,EAGP"}
@@ -1,9 +1,12 @@
1
1
  import { Request } from 'express';
2
+ import { TRouteObject } from "../interfaces/route-object.js";
2
3
  import { IEntrypointOptions } from "../node/entry.js";
3
4
  import { TRender } from "../node/render.js";
4
5
  import ServerConfig from "./server-config.js";
5
6
  interface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {
6
7
  render: TRender;
8
+ routes: TRouteObject[];
9
+ abortDelay?: number;
7
10
  onRequest?: IEntrypointOptions<TAppProps>['onRequest'];
8
11
  onRouterReady?: IEntrypointOptions<TAppProps>['onRouterReady'];
9
12
  onShellReady?: IEntrypointOptions<TAppProps>['onShellReady'];
@@ -53,7 +56,7 @@ declare class PrepareServer {
53
56
  /**
54
57
  * Resolve and return entrypoint params
55
58
  */
56
- loadEntrypoint(): Promise<IPrepareServerEntrypointLoadOut>;
59
+ loadEntrypoint(shouldInit?: boolean): Promise<IPrepareServerEntrypointLoadOut>;
57
60
  /**
58
61
  * Load and return html shell
59
62
  */
@@ -1,2 +1,2 @@
1
- import t from"fs";import e from"node:process";import r from"path";import o from"chalk";class i{config;entrypoint;onServerCreated;html;constructor(t){this.config=t}static init(t){return new i(t)}async loadEntrypoint(){if(this.entrypoint&&this.config.isProd)return this.entrypoint;const{root:t,isProd:i,serverFile:n}=this.config.getParams(),s=r.resolve(`${t}/${n}`);let a;try{a=i?(await import(s)).default:(await this.config.getVite().ssrLoadModule(s,{fixStacktrace:!0})).default}catch(t){if(t.message.includes("Cannot find module")&&t.message.includes("/build/"))return this.config.getLogger().error(`Before starting the server, you need to create a build: ${o.yellow("ssr-boost build")}`),e.exit(1);throw t}const{render:l,init:c}=a,{onServerCreated:h,onRequest:d,onRouterReady:f,onShellReady:g,onResponse:p,onShellError:u,onError:m,getState:y}=await(c?.({config:this.config}))??{};return this.entrypoint={render:l,onRequest:d,onRouterReady:f,onShellReady:g,onShellError:u,onResponse:p,onError:m,getState:y},this.onServerCreated=h,this.entrypoint}async loadHtml(e){const{isProd:o,root:i,indexFile:n}=this.config.getParams();this.html&&o||(this.html=t.readFileSync(r.resolve(`${i}/${n}`),"utf-8"));let s=this.html;return o||(s=(await this.config.getVite().transformIndexHtml(e.originalUrl,this.html)).replace(/(<script.+)(>[\s\S]+injectIntoGlobalHook.+)/,"$1async$2")),s.split("\x3c!--ssr-outlet--\x3e")}async onAppCreated(){return await this.loadEntrypoint(),await(this.onServerCreated?.(this.config.getApp())),this}}export{i as default};
1
+ import t from"fs";import e from"node:process";import r from"path";import o from"chalk";class i{config;entrypoint;onServerCreated;html;constructor(t){this.config=t}static init(t){return new i(t)}async loadEntrypoint(t=!0){if(this.entrypoint&&this.config.isProd)return this.entrypoint;const{root:i,isProd:n,serverFile:s}=this.config.getParams(),a=r.resolve(`${i}/${s}`);let l;try{l=n?(await import(a)).default:(await this.config.getVite().ssrLoadModule(a,{fixStacktrace:!0})).default}catch(t){if(t.message.includes("Cannot find module")&&t.message.includes("/build/"))return this.config.getLogger().error(o.red(`Before starting the server, you need to create a build: ${o.yellow("ssr-boost build")}`)),e.exit(1);throw t}!t&&l.init&&delete l.init;const{render:c,init:d,routes:h,abortDelay:f}=l,{onServerCreated:u,onRequest:g,onRouterReady:p,onShellReady:m,onResponse:y,onShellError:S,onError:R,getState:w}=await(d?.({config:this.config}))??{};return this.entrypoint={render:c,routes:h,abortDelay:f,onRequest:g,onRouterReady:p,onShellReady:m,onShellError:S,onResponse:y,onError:R,getState:w},this.onServerCreated=u,this.entrypoint}async loadHtml(e){const{isProd:o,root:i,indexFile:n}=this.config.getParams();this.html&&o||(this.html=t.readFileSync(r.resolve(`${i}/${n}`),"utf-8"));let s=this.html;return o||(s=(await this.config.getVite().transformIndexHtml(e.originalUrl,this.html)).replace(/(<script.+)(>[\s\S]+injectIntoGlobalHook.+)/,"$1async$2")),s.split("\x3c!--ssr-outlet--\x3e")}async onAppCreated(){return await this.loadEntrypoint(),await(this.onServerCreated?.(this.config.getApp())),this}}export{i as default};
2
2
  //# sourceMappingURL=prepare-server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prepare-server.js","sources":["../../src/services/prepare-server.ts"],"sourcesContent":["import fs from 'fs';\nimport process from 'node:process';\nimport path from 'path';\nimport chalk from 'chalk';\nimport type { Request } from 'express';\nimport type { IEntrypointOptions, IPrepareRenderOut } from '@node/entry';\nimport type { TRender } from '@node/render';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {\n render: TRender;\n onRequest?: IEntrypointOptions<TAppProps>['onRequest'];\n onRouterReady?: IEntrypointOptions<TAppProps>['onRouterReady'];\n onShellReady?: IEntrypointOptions<TAppProps>['onShellReady'];\n onShellError?: IEntrypointOptions<TAppProps>['onShellError'];\n onResponse?: IEntrypointOptions<TAppProps>['onResponse'];\n onError?: IEntrypointOptions<TAppProps>['onError'];\n getState?: IEntrypointOptions<TAppProps>['getState'];\n}\n\n/**\n * Load server entrypoint and template\n * DEV MODE: refresh entrypoint and template\n */\nclass PrepareServer {\n /**\n * Server configuration\n */\n protected readonly config: ServerConfig;\n\n /**\n * Entrypoint resolved params\n */\n protected entrypoint?: IPrepareServerEntrypointLoadOut;\n\n /**\n * Hook which calls after express server created\n */\n protected onServerCreated?: IEntrypointOptions['onServerCreated'];\n\n /**\n * Html shell\n */\n protected html: string;\n\n /**\n * @constructor\n */\n protected constructor(config: ServerConfig) {\n this.config = config;\n }\n\n /**\n * Init service\n */\n public static init(config: ServerConfig): PrepareServer {\n return new PrepareServer(config);\n }\n\n /**\n * Resolve and return entrypoint params\n */\n public async loadEntrypoint(): Promise<IPrepareServerEntrypointLoadOut> {\n // load server entrypoint each time only in development mode (for fast refresh)\n if (this.entrypoint && this.config.isProd) {\n return this.entrypoint;\n }\n\n const { root, isProd, serverFile } = this.config.getParams();\n const entrypointPath = path.resolve(`${root}/${serverFile}`);\n\n let resolvedEntrypoint: IPrepareRenderOut;\n\n try {\n if (!isProd) {\n resolvedEntrypoint = (\n await this.config.getVite()!.ssrLoadModule(entrypointPath, {\n fixStacktrace: true,\n })\n ).default;\n } else {\n resolvedEntrypoint = (await import(entrypointPath)).default;\n }\n } catch (e) {\n if (e.message.includes('Cannot find module') && e.message.includes('/build/')) {\n this.config\n .getLogger()\n .error(\n `Before starting the server, you need to create a build: ${chalk.yellow(\n 'ssr-boost build',\n )}`,\n );\n\n return process.exit(1);\n }\n\n throw e;\n }\n\n const { render, init } = resolvedEntrypoint;\n const {\n onServerCreated,\n onRequest,\n onRouterReady,\n onShellReady,\n onResponse,\n onShellError,\n onError,\n getState,\n } =\n (await init?.({\n config: this.config,\n })) ?? {};\n\n this.entrypoint = {\n render,\n onRequest,\n onRouterReady,\n onShellReady,\n onShellError,\n onResponse,\n onError,\n getState,\n };\n this.onServerCreated = onServerCreated;\n\n return this.entrypoint;\n }\n\n /**\n * Load and return html shell\n */\n public async loadHtml(req: Request): Promise<[string, string]> {\n const { isProd, root, indexFile } = this.config.getParams();\n\n if (!this.html || !isProd) {\n this.html = fs.readFileSync(path.resolve(`${root}/${indexFile}`), 'utf-8');\n }\n\n let modifiedHtml = this.html;\n\n if (!isProd) {\n // Apply Vite HTML transforms. This injects the Vite HMR client,\n // and also applies HTML transforms from Vite plugins, e.g. global\n // preambles from @vitejs/plugin-react\n modifiedHtml = (await this.config.getVite()!.transformIndexHtml(req.originalUrl, this.html))\n // Make vite script 'async'\n .replace(/(<script.+)(>[\\s\\S]+injectIntoGlobalHook.+)/, '$1async$2');\n }\n\n return modifiedHtml.split('<!--ssr-outlet-->') as [string, string];\n }\n\n /**\n * Run server created hook\n */\n public async onAppCreated(): Promise<PrepareServer> {\n await this.loadEntrypoint();\n await this.onServerCreated?.(this.config.getApp()!);\n\n return this;\n }\n}\n\nexport default PrepareServer;\n"],"names":["PrepareServer","config","entrypoint","onServerCreated","html","constructor","this","static","async","isProd","root","serverFile","getParams","entrypointPath","path","resolve","resolvedEntrypoint","import","default","getVite","ssrLoadModule","fixStacktrace","e","message","includes","getLogger","error","chalk","yellow","process","exit","render","init","onRequest","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","req","indexFile","fs","readFileSync","modifiedHtml","transformIndexHtml","originalUrl","replace","split","loadEntrypoint","getApp"],"mappings":"uFAwBA,MAAMA,EAIeC,OAKTC,WAKAC,gBAKAC,KAKVC,YAAsBJ,GACpBK,KAAKL,OAASA,CACf,CAKMM,YAAYN,GACjB,OAAO,IAAID,EAAcC,EAC1B,CAKMO,uBAEL,GAAIF,KAAKJ,YAAcI,KAAKL,OAAOQ,OACjC,OAAOH,KAAKJ,WAGd,MAAMQ,KAAEA,EAAID,OAAEA,EAAME,WAAEA,GAAeL,KAAKL,OAAOW,YAC3CC,EAAiBC,EAAKC,QAAQ,GAAGL,KAAQC,KAE/C,IAAIK,EAEJ,IAQIA,EAPGP,SAOyBQ,OAAOJ,IAAiBK,eAL5CZ,KAAKL,OAAOkB,UAAWC,cAAcP,EAAgB,CACzDQ,eAAe,KAEjBH,OAIL,CAAC,MAAOI,GACP,GAAIA,EAAEC,QAAQC,SAAS,uBAAyBF,EAAEC,QAAQC,SAAS,WASjE,OARAlB,KAAKL,OACFwB,YACAC,MACC,2DAA2DC,EAAMC,OAC/D,sBAICC,EAAQC,KAAK,GAGtB,MAAMR,CACP,CAED,MAAMS,OAAEA,EAAMC,KAAEA,GAAShB,GACnBb,gBACJA,EAAe8B,UACfA,EAASC,cACTA,EAAaC,aACbA,EAAYC,WACZA,EAAUC,aACVA,EAAYC,QACZA,EAAOC,SACPA,SAEOP,IAAO,CACZ/B,OAAQK,KAAKL,WACR,CAAA,EAcT,OAZAK,KAAKJ,WAAa,CAChB6B,SACAE,YACAC,gBACAC,eACAE,eACAD,aACAE,UACAC,YAEFjC,KAAKH,gBAAkBA,EAEhBG,KAAKJ,UACb,CAKMM,eAAegC,GACpB,MAAM/B,OAAEA,EAAMC,KAAEA,EAAI+B,UAAEA,GAAcnC,KAAKL,OAAOW,YAE3CN,KAAKF,MAASK,IACjBH,KAAKF,KAAOsC,EAAGC,aAAa7B,EAAKC,QAAQ,GAAGL,KAAQ+B,KAAc,UAGpE,IAAIG,EAAetC,KAAKF,KAWxB,OATKK,IAIHmC,SAAsBtC,KAAKL,OAAOkB,UAAW0B,mBAAmBL,EAAIM,YAAaxC,KAAKF,OAEnF2C,QAAQ,8CAA+C,cAGrDH,EAAaI,MAAM,0BAC3B,CAKMxC,qBAIL,aAHMF,KAAK2C,uBACL3C,KAAKH,kBAAkBG,KAAKL,OAAOiD,WAElC5C,IACR"}
1
+ {"version":3,"file":"prepare-server.js","sources":["../../src/services/prepare-server.ts"],"sourcesContent":["import fs from 'fs';\nimport process from 'node:process';\nimport path from 'path';\nimport chalk from 'chalk';\nimport type { Request } from 'express';\nimport type { TRouteObject } from '@interfaces/route-object';\nimport type { IEntrypointOptions, IPrepareRenderOut } from '@node/entry';\nimport type { TRender } from '@node/render';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {\n render: TRender;\n routes: TRouteObject[];\n abortDelay?: number;\n onRequest?: IEntrypointOptions<TAppProps>['onRequest'];\n onRouterReady?: IEntrypointOptions<TAppProps>['onRouterReady'];\n onShellReady?: IEntrypointOptions<TAppProps>['onShellReady'];\n onShellError?: IEntrypointOptions<TAppProps>['onShellError'];\n onResponse?: IEntrypointOptions<TAppProps>['onResponse'];\n onError?: IEntrypointOptions<TAppProps>['onError'];\n getState?: IEntrypointOptions<TAppProps>['getState'];\n}\n\n/**\n * Load server entrypoint and template\n * DEV MODE: refresh entrypoint and template\n */\nclass PrepareServer {\n /**\n * Server configuration\n */\n protected readonly config: ServerConfig;\n\n /**\n * Entrypoint resolved params\n */\n protected entrypoint?: IPrepareServerEntrypointLoadOut;\n\n /**\n * Hook which calls after express server created\n */\n protected onServerCreated?: IEntrypointOptions['onServerCreated'];\n\n /**\n * Html shell\n */\n protected html: string;\n\n /**\n * @constructor\n */\n protected constructor(config: ServerConfig) {\n this.config = config;\n }\n\n /**\n * Init service\n */\n public static init(config: ServerConfig): PrepareServer {\n return new PrepareServer(config);\n }\n\n /**\n * Resolve and return entrypoint params\n */\n public async loadEntrypoint(shouldInit = true): Promise<IPrepareServerEntrypointLoadOut> {\n // load server entrypoint each time only in development mode (for fast refresh)\n if (this.entrypoint && this.config.isProd) {\n return this.entrypoint;\n }\n\n const { root, isProd, serverFile } = this.config.getParams();\n const entrypointPath = path.resolve(`${root}/${serverFile}`);\n\n let resolvedEntrypoint: IPrepareRenderOut;\n\n try {\n if (!isProd) {\n resolvedEntrypoint = (\n await this.config.getVite()!.ssrLoadModule(entrypointPath, {\n fixStacktrace: true,\n })\n ).default;\n } else {\n resolvedEntrypoint = (await import(entrypointPath)).default;\n }\n } catch (e) {\n if (e.message.includes('Cannot find module') && e.message.includes('/build/')) {\n this.config\n .getLogger()\n .error(\n chalk.red(\n `Before starting the server, you need to create a build: ${chalk.yellow(\n 'ssr-boost build',\n )}`,\n ),\n );\n\n return process.exit(1);\n }\n\n throw e;\n }\n\n if (!shouldInit && resolvedEntrypoint.init) {\n delete resolvedEntrypoint.init;\n }\n\n const { render, init, routes, abortDelay } = resolvedEntrypoint;\n const {\n onServerCreated,\n onRequest,\n onRouterReady,\n onShellReady,\n onResponse,\n onShellError,\n onError,\n getState,\n } =\n (await init?.({\n config: this.config,\n })) ?? {};\n\n this.entrypoint = {\n render,\n routes,\n abortDelay,\n onRequest,\n onRouterReady,\n onShellReady,\n onShellError,\n onResponse,\n onError,\n getState,\n };\n this.onServerCreated = onServerCreated;\n\n return this.entrypoint;\n }\n\n /**\n * Load and return html shell\n */\n public async loadHtml(req: Request): Promise<[string, string]> {\n const { isProd, root, indexFile } = this.config.getParams();\n\n if (!this.html || !isProd) {\n this.html = fs.readFileSync(path.resolve(`${root}/${indexFile}`), 'utf-8');\n }\n\n let modifiedHtml = this.html;\n\n if (!isProd) {\n // Apply Vite HTML transforms. This injects the Vite HMR client,\n // and also applies HTML transforms from Vite plugins, e.g. global\n // preambles from @vitejs/plugin-react\n modifiedHtml = (await this.config.getVite()!.transformIndexHtml(req.originalUrl, this.html))\n // Make vite script 'async'\n .replace(/(<script.+)(>[\\s\\S]+injectIntoGlobalHook.+)/, '$1async$2');\n }\n\n return modifiedHtml.split('<!--ssr-outlet-->') as [string, string];\n }\n\n /**\n * Run server created hook\n */\n public async onAppCreated(): Promise<PrepareServer> {\n await this.loadEntrypoint();\n await this.onServerCreated?.(this.config.getApp()!);\n\n return this;\n }\n}\n\nexport default PrepareServer;\n"],"names":["PrepareServer","config","entrypoint","onServerCreated","html","constructor","this","static","async","shouldInit","isProd","root","serverFile","getParams","entrypointPath","path","resolve","resolvedEntrypoint","import","default","getVite","ssrLoadModule","fixStacktrace","e","message","includes","getLogger","error","chalk","red","yellow","process","exit","init","render","routes","abortDelay","onRequest","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","req","indexFile","fs","readFileSync","modifiedHtml","transformIndexHtml","originalUrl","replace","split","loadEntrypoint","getApp"],"mappings":"uFA2BA,MAAMA,EAIeC,OAKTC,WAKAC,gBAKAC,KAKVC,YAAsBJ,GACpBK,KAAKL,OAASA,CACf,CAKMM,YAAYN,GACjB,OAAO,IAAID,EAAcC,EAC1B,CAKMO,qBAAqBC,GAAa,GAEvC,GAAIH,KAAKJ,YAAcI,KAAKL,OAAOS,OACjC,OAAOJ,KAAKJ,WAGd,MAAMS,KAAEA,EAAID,OAAEA,EAAME,WAAEA,GAAeN,KAAKL,OAAOY,YAC3CC,EAAiBC,EAAKC,QAAQ,GAAGL,KAAQC,KAE/C,IAAIK,EAEJ,IAQIA,EAPGP,SAOyBQ,OAAOJ,IAAiBK,eAL5Cb,KAAKL,OAAOmB,UAAWC,cAAcP,EAAgB,CACzDQ,eAAe,KAEjBH,OAIL,CAAC,MAAOI,GACP,GAAIA,EAAEC,QAAQC,SAAS,uBAAyBF,EAAEC,QAAQC,SAAS,WAWjE,OAVAnB,KAAKL,OACFyB,YACAC,MACCC,EAAMC,IACJ,2DAA2DD,EAAME,OAC/D,uBAKDC,EAAQC,KAAK,GAGtB,MAAMT,CACP,EAEId,GAAcQ,EAAmBgB,aAC7BhB,EAAmBgB,KAG5B,MAAMC,OAAEA,EAAMD,KAAEA,EAAIE,OAAEA,EAAMC,WAAEA,GAAenB,GACvCd,gBACJA,EAAekC,UACfA,EAASC,cACTA,EAAaC,aACbA,EAAYC,WACZA,EAAUC,aACVA,EAAYC,QACZA,EAAOC,SACPA,SAEOV,IAAO,CACZhC,OAAQK,KAAKL,WACR,CAAA,EAgBT,OAdAK,KAAKJ,WAAa,CAChBgC,SACAC,SACAC,aACAC,YACAC,gBACAC,eACAE,eACAD,aACAE,UACAC,YAEFrC,KAAKH,gBAAkBA,EAEhBG,KAAKJ,UACb,CAKMM,eAAeoC,GACpB,MAAMlC,OAAEA,EAAMC,KAAEA,EAAIkC,UAAEA,GAAcvC,KAAKL,OAAOY,YAE3CP,KAAKF,MAASM,IACjBJ,KAAKF,KAAO0C,EAAGC,aAAahC,EAAKC,QAAQ,GAAGL,KAAQkC,KAAc,UAGpE,IAAIG,EAAe1C,KAAKF,KAWxB,OATKM,IAIHsC,SAAsB1C,KAAKL,OAAOmB,UAAW6B,mBAAmBL,EAAIM,YAAa5C,KAAKF,OAEnF+C,QAAQ,8CAA+C,cAGrDH,EAAaI,MAAM,0BAC3B,CAKM5C,qBAIL,aAHMF,KAAK+C,uBACL/C,KAAKH,kBAAkBG,KAAKL,OAAOqD,WAElChD,IACR"}
@@ -16,7 +16,6 @@ interface IConfigParams {
16
16
  isSPA: boolean;
17
17
  indexFile: string;
18
18
  serverFile: string;
19
- abortDelay: number;
20
19
  host: string;
21
20
  port: number;
22
21
  }
@@ -95,13 +94,6 @@ declare class ServerConfig {
95
94
  * Set express server
96
95
  */
97
96
  setApp(express: Express): void;
98
- /**
99
- * Set custom abort delay
100
- */
101
- /**
102
- * Set custom abort delay
103
- */
104
- setAbortDelay(ms: number): void;
105
97
  /**
106
98
  * Return vite dev server
107
99
  * NOTE: only on development mode
@@ -1,2 +1,2 @@
1
- import r from"node:path";import t from"../helpers/plugin-config.js";import i from"./logger.js";class s{isProd;isHost;isSPA;mode;vite;app;params;prodParams;logger;constructor({isProd:r=!1,isHost:t=!1,isOnlyClient:i=!1,mode:s="production"},e){this.isProd=r,this.isHost=t,this.isSPA=i,this.mode=s,this.prodParams={root:"./build",publicDir:"/client",indexFile:"/client/index.html",serverFile:"/server/server.js",host:"127.0.0.1",port:3e3,abortDelay:1e4,...e},this.makeParams()}static init(r={},t={}){return new s(r,t)}makeParams(){const t=this.getPluginConfig()??{},{config:s}=this.vite??{},e=s?.root??this.prodParams.root??"",o=s?.publicDir??this.prodParams.publicDir,a=new URL(import.meta.url),p=t.pluginPath??r.resolve(`../${r.dirname(a.pathname)}`),h=t.indexFile??this.prodParams.indexFile,l=t.serverFile??this.prodParams.serverFile,n="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),d=t.abortDelay??this.prodParams.abortDelay;this.params={root:e,publicDir:o,pluginPath:p,indexFile:h,serverFile:l,host:n,port:m,abortDelay:d,isSPA:this.isSPA,isProd:this.isProd},this.logger=this.vite?.config.logger??new i}setVite(r){this.vite=r,this.makeParams()}setApp(r){this.app=r}setAbortDelay(r){this.prodParams.abortDelay=r,this.params.abortDelay=r}getVite(){return this.vite}getApp(){return this.app}getPluginConfig(){return this.vite?t(this.vite.config):void 0}getParams(){return this.params}getLogger(){return this.logger}}export{s as default};
1
+ import i from"node:path";import t from"../helpers/plugin-config.js";import r from"./logger.js";class s{isProd;isHost;isSPA;mode;vite;app;params;prodParams;logger;constructor({isProd:i=!1,isHost:t=!1,isOnlyClient:r=!1,mode:s="production"},e){this.isProd=i,this.isHost=t,this.isSPA=r,this.mode=s,this.prodParams={root:"./build",publicDir:"/client",indexFile:"/client/index.html",serverFile:"/server/server.js",host:"127.0.0.1",port:3e3,...e},this.makeParams()}static init(i={},t={}){return new s(i,t)}makeParams(){const t=this.getPluginConfig()??{},{config:s}=this.vite??{},e=s?.root??this.prodParams.root??"",o=s?.publicDir??this.prodParams.publicDir,a=new URL(import.meta.url),p=t.pluginPath??i.resolve(i.dirname(a.pathname),"../"),h=t.indexFile??this.prodParams.indexFile,n=t.serverFile??this.prodParams.serverFile,l="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:e,publicDir:o,pluginPath:p,indexFile:h,serverFile:n,host:l,port:m,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?t(this.vite.config):void 0}getParams(){return this.params}getLogger(){return this.logger}}export{s 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 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 prodParams?: Partial<IConfigParams>;\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 abortDelay: number;\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 * 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 * @constructor\n */\n protected constructor(\n { isProd = false, isHost = false, isOnlyClient = false, mode = 'production' }: IConfigOptions,\n prodParams: Partial<IConfigParams>,\n ) {\n this.isProd = isProd;\n this.isHost = isHost;\n this.isSPA = isOnlyClient;\n this.mode = mode;\n this.prodParams = {\n root: './build',\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 abortDelay: 10000,\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 * 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.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 const abortDelay = pluginConfig.abortDelay ?? this.prodParams.abortDelay!;\n\n this.params = {\n root,\n publicDir,\n pluginPath,\n indexFile,\n serverFile,\n host,\n port,\n abortDelay,\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 * Set custom abort delay\n */\n public setAbortDelay(ms: number): void {\n this.prodParams.abortDelay = ms;\n this.params.abortDelay = ms;\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","isSPA","mode","vite","app","params","prodParams","logger","constructor","isOnlyClient","this","root","publicDir","indexFile","serverFile","host","port","abortDelay","makeParams","static","options","prodOptions","pluginConfig","getPluginConfig","config","dirInfo","URL","url","pluginPath","path","resolve","dirname","pathname","server","DefaultLogger","setVite","setApp","express","setAbortDelay","ms","getVite","getApp","undefined","getParams","getLogger"],"mappings":"+FA+BA,MAAMA,EAIYC,OAKAC,OAKAC,MAKAC,KAKNC,KAKAC,IAKAC,OAKAC,WAKAC,OAKVC,aACET,OAAEA,GAAS,EAAKC,OAAEA,GAAS,EAAKS,aAAEA,GAAe,EAAKP,KAAEA,EAAO,cAC/DI,GAEAI,KAAKX,OAASA,EACdW,KAAKV,OAASA,EACdU,KAAKT,MAAQQ,EACbC,KAAKR,KAAOA,EACZQ,KAAKJ,WAAa,CAChBK,KAAM,UACNC,UAAW,UACXC,UAAW,qBACXC,WAAY,oBACZC,KAAM,YACNC,KAAM,IACNC,WAAY,OACTX,GAGLI,KAAKQ,YACN,CAKMC,YACLC,EAA0B,GAC1BC,EAAsC,CAAA,GAEtC,OAAO,IAAIvB,EAAasB,EAASC,EAClC,CAKSH,aACR,MAAMI,EAAgBZ,KAAKa,mBAAqB,CAAE,GAC5CC,OAAEA,GAAWd,KAAKP,MAAQ,CAAA,EAE1BQ,EAAOa,GAAQb,MAAQD,KAAKJ,WAAWK,MAAQ,GAC/CC,EAAYY,GAAQZ,WAAaF,KAAKJ,WAAWM,UACjDa,EAAU,IAAIC,gBAAgBC,KAC9BC,EACJN,EAAaM,YAAcC,EAAKC,QAAQ,MAAMD,EAAKE,QAAQN,EAAQO,aAC/DnB,EAAYS,EAAaT,WAAaH,KAAKJ,WAAWO,UACtDC,EAAaQ,EAAaR,YAAcJ,KAAKJ,WAAWQ,WACxDC,EAC2B,kBAAxBS,GAAQS,OAAOlB,MAAsBL,KAAKV,OAC7C,UACAwB,GAAQS,OAAOlB,MAAQL,KAAKJ,WAAWS,KACvCC,EAAOQ,GAAQS,OAAOjB,OAASN,KAAKX,OAASW,KAAKJ,WAAWU,KAAQ,MACrEC,EAAaK,EAAaL,YAAcP,KAAKJ,WAAWW,WAE9DP,KAAKL,OAAS,CACZM,OACAC,YACAgB,aACAf,YACAC,aACAC,OACAC,OACAC,aACAhB,MAAOS,KAAKT,MACZF,OAAQW,KAAKX,QAEfW,KAAKH,OAASG,KAAKP,MAAMqB,OAAOjB,QAAU,IAAI2B,CAC/C,CAKMC,QAAQhC,GACbO,KAAKP,KAAOA,EAEZO,KAAKQ,YACN,CAKMkB,OAAOC,GACZ3B,KAAKN,IAAMiC,CACZ,CAKMC,cAAcC,GACnB7B,KAAKJ,WAAWW,WAAasB,EAC7B7B,KAAKL,OAAOY,WAAasB,CAC1B,CAMMC,UACL,OAAO9B,KAAKP,IACb,CAKMsC,SACL,OAAO/B,KAAKN,GACb,CAMMmB,kBACL,OAAOb,KAAKP,KAAOoB,EAAgBb,KAAKP,KAAKqB,aAAUkB,CACxD,CAKMC,YACL,OAAOjC,KAAKL,MACb,CAKMuC,YACL,OAAOlC,KAAKH,MACb"}
1
+ {"version":3,"file":"server-config.js","sources":["../../src/services/server-config.ts"],"sourcesContent":["import 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 prodParams?: Partial<IConfigParams>;\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 * 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 * @constructor\n */\n protected constructor(\n { isProd = false, isHost = false, isOnlyClient = false, mode = 'production' }: IConfigOptions,\n prodParams: Partial<IConfigParams>,\n ) {\n this.isProd = isProd;\n this.isHost = isHost;\n this.isSPA = isOnlyClient;\n this.mode = mode;\n this.prodParams = {\n root: './build',\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 * 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.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","isSPA","mode","vite","app","params","prodParams","logger","constructor","isOnlyClient","this","root","publicDir","indexFile","serverFile","host","port","makeParams","static","options","prodOptions","pluginConfig","getPluginConfig","config","dirInfo","URL","url","pluginPath","path","resolve","dirname","pathname","server","DefaultLogger","setVite","setApp","express","getVite","getApp","undefined","getParams","getLogger"],"mappings":"+FA8BA,MAAMA,EAIYC,OAKAC,OAKAC,MAKAC,KAKNC,KAKAC,IAKAC,OAKAC,WAKAC,OAKVC,aACET,OAAEA,GAAS,EAAKC,OAAEA,GAAS,EAAKS,aAAEA,GAAe,EAAKP,KAAEA,EAAO,cAC/DI,GAEAI,KAAKX,OAASA,EACdW,KAAKV,OAASA,EACdU,KAAKT,MAAQQ,EACbC,KAAKR,KAAOA,EACZQ,KAAKJ,WAAa,CAChBK,KAAM,UACNC,UAAW,UACXC,UAAW,qBACXC,WAAY,oBACZC,KAAM,YACNC,KAAM,OACHV,GAGLI,KAAKO,YACN,CAKMC,YACLC,EAA0B,GAC1BC,EAAsC,CAAA,GAEtC,OAAO,IAAItB,EAAaqB,EAASC,EAClC,CAKSH,aACR,MAAMI,EAAgBX,KAAKY,mBAAqB,CAAE,GAC5CC,OAAEA,GAAWb,KAAKP,MAAQ,CAAA,EAE1BQ,EAAOY,GAAQZ,MAAQD,KAAKJ,WAAWK,MAAQ,GAC/CC,EAAYW,GAAQX,WAAaF,KAAKJ,WAAWM,UACjDY,EAAU,IAAIC,gBAAgBC,KAC9BC,EACJN,EAAaM,YAAcC,EAAKC,QAAQD,EAAKE,QAAQN,EAAQO,UAAW,OACpElB,EAAYQ,EAAaR,WAAaH,KAAKJ,WAAWO,UACtDC,EAAaO,EAAaP,YAAcJ,KAAKJ,WAAWQ,WACxDC,EAC2B,kBAAxBQ,GAAQS,OAAOjB,MAAsBL,KAAKV,OAC7C,UACAuB,GAAQS,OAAOjB,MAAQL,KAAKJ,WAAWS,KACvCC,EAAOO,GAAQS,OAAOhB,OAASN,KAAKX,OAASW,KAAKJ,WAAWU,KAAQ,MAE3EN,KAAKL,OAAS,CACZM,OACAC,YACAe,aACAd,YACAC,aACAC,OACAC,OACAf,MAAOS,KAAKT,MACZF,OAAQW,KAAKX,QAEfW,KAAKH,OAASG,KAAKP,MAAMoB,OAAOhB,QAAU,IAAI0B,CAC/C,CAKMC,QAAQ/B,GACbO,KAAKP,KAAOA,EAEZO,KAAKO,YACN,CAKMkB,OAAOC,GACZ1B,KAAKN,IAAMgC,CACZ,CAMMC,UACL,OAAO3B,KAAKP,IACb,CAKMmC,SACL,OAAO5B,KAAKN,GACb,CAMMkB,kBACL,OAAOZ,KAAKP,KAAOmB,EAAgBZ,KAAKP,KAAKoB,aAAUgB,CACxD,CAKMC,YACL,OAAO9B,KAAKL,MACb,CAKMoC,YACL,OAAO/B,KAAKH,MACb"}
@@ -0,0 +1,157 @@
1
+ /// <reference types="node" />
2
+ import { Socket } from 'node:net';
3
+ import { AgnosticDataRouteMatch } from '@remix-run/router/dist/utils';
4
+ import { RouteObject } from 'react-router-dom';
5
+ import { Alias } from 'vite';
6
+ import { IRequestContext } from "../node/render.js";
7
+ interface ISsrManifestParams {
8
+ alias?: Alias[];
9
+ buildDir?: string;
10
+ }
11
+ interface IManifest {
12
+ [path: string]: {
13
+ assets: string[];
14
+ css: string[];
15
+ file: string;
16
+ imports: string[];
17
+ };
18
+ }
19
+ /**
20
+ * Working with SSR Manifest file
21
+ */
22
+ declare class SsrManifest {
23
+ /**
24
+ * Singleton
25
+ */
26
+ protected static instance: SsrManifest | null;
27
+ /**
28
+ * Project root path
29
+ */
30
+ protected root: string;
31
+ /**
32
+ * Build dir
33
+ */
34
+ protected buildDir?: string;
35
+ /**
36
+ * Client manifest file name
37
+ */
38
+ protected manifestName: string;
39
+ /**
40
+ * Assets manifest file name
41
+ */
42
+ protected assetsManifest: string;
43
+ /**
44
+ * Vite resolve aliases
45
+ */
46
+ protected alias?: Alias[];
47
+ /**
48
+ * Loaded assets manifest file
49
+ */
50
+ protected routesAssets: Record<string, string[]> | null;
51
+ /**
52
+ * @constructor
53
+ */
54
+ /**
55
+ * @constructor
56
+ */
57
+ protected constructor(root: string, { buildDir, alias }?: ISsrManifestParams);
58
+ /**
59
+ * Get singleton instance
60
+ */
61
+ /**
62
+ * Get singleton instance
63
+ */
64
+ static get(root: string, params?: ISsrManifestParams): SsrManifest;
65
+ /**
66
+ * Get assets manifest file name
67
+ */
68
+ /**
69
+ * Get assets manifest file name
70
+ */
71
+ protected getAssetsManifestFile(): string;
72
+ /**
73
+ * Load client ssr manifest
74
+ */
75
+ /**
76
+ * Load client ssr manifest
77
+ */
78
+ protected loadClientManifest(): IManifest;
79
+ /**
80
+ * Load assets manifest
81
+ */
82
+ /**
83
+ * Load assets manifest
84
+ */
85
+ protected loadAssetsManifest(): Record<string, string[]>;
86
+ /**
87
+ * Recursive walk routes and return id's with route import path
88
+ */
89
+ /**
90
+ * Recursive walk routes and return id's with route import path
91
+ */
92
+ protected getRoutesIds(routes: RouteObject[], index?: string): Promise<Record<string, string>>;
93
+ /**
94
+ * Build routes manifest file
95
+ */
96
+ /**
97
+ * Build routes manifest file
98
+ */
99
+ buildRoutesManifest(shouldPreloadAssets: boolean): Promise<void>;
100
+ /**
101
+ * Load aliases manifest
102
+ */
103
+ /**
104
+ * Load aliases manifest
105
+ */
106
+ protected getAliases(): Record<string, string>;
107
+ /**
108
+ * Return route postfix
109
+ */
110
+ /**
111
+ * Return route postfix
112
+ */
113
+ protected getRouteImportPostfix(): string[];
114
+ /**
115
+ * Normalized route path
116
+ */
117
+ /**
118
+ * Normalized route path
119
+ */
120
+ protected normalizeRoutePath(routePath?: string): string | undefined;
121
+ /**
122
+ * Get provided route assets
123
+ */
124
+ /**
125
+ * Get provided route assets
126
+ */
127
+ getAssets(routes?: AgnosticDataRouteMatch[]): string[];
128
+ /**
129
+ * Get asset weight
130
+ */
131
+ /**
132
+ * Get asset weight
133
+ */
134
+ protected getAssetWeight(asset: string): number;
135
+ /**
136
+ * Get asset type
137
+ */
138
+ /**
139
+ * Get asset type
140
+ */
141
+ protected getAssetType(asset: string): string | null;
142
+ /**
143
+ * Write 103 Early Hits header
144
+ */
145
+ /**
146
+ * Write 103 Early Hits header
147
+ */
148
+ writeEarlyHits(assets: string[], socket: Socket): void;
149
+ /**
150
+ * Inject route assets to head html
151
+ */
152
+ /**
153
+ * Inject route assets to head html
154
+ */
155
+ injectAssets(context: IRequestContext, hasEarlyHits?: boolean): void;
156
+ }
157
+ export { SsrManifest as default };
@@ -0,0 +1,2 @@
1
+ import t from"node:fs";import s from"node:path";import e from"./prepare-server.js";import i from"./server-config.js";const r="\r\n";class n{static instance=null;root;buildDir;manifestName="manifest.json";assetsManifest="assets-manifest.json";alias;routesAssets=null;constructor(t,{buildDir:s,alias:e}={}){this.root=t,this.buildDir=s,this.alias=e}static get(t,s={}){return null===n.instance&&(n.instance=new n(t,s)),n.instance}getAssetsManifestFile(){return`${s.resolve(this.root,this.buildDir||"")}/server/${this.assetsManifest}`}loadClientManifest(){const e=s.resolve(this.root,`${this.buildDir||""}/client/${this.manifestName}`);if(!t.existsSync(e))return{};const i=JSON.parse(t.readFileSync(e,{encoding:"utf-8"}));return t.rmSync(e),i}loadAssetsManifest(){if(null!==this.routesAssets)return this.routesAssets;const s=this.getAssetsManifestFile();return t.existsSync(s)?(this.routesAssets=JSON.parse(t.readFileSync(s,{encoding:"utf-8"})),this.routesAssets):{}}async getRoutesIds(t,s){const e={};for(const i in t){const r=t[i],n=[s,i].filter(Boolean).join("-");if(r.lazy){const t=await r.lazy();e[n]=this.normalizeRoutePath(t?.pathId)}else r.children&&Object.assign(e,await this.getRoutesIds(r.children,n))}return e}async buildRoutesManifest(s){const r=i.init({isProd:!0}),n=e.init(r),a=this.loadClientManifest(),{routes:o}=await n.loadEntrypoint(!1),l=await this.getRoutesIds(o),c=this.getRouteImportPostfix(),h={};Object.entries(l).forEach((([t,e])=>{const i=c.find((t=>void 0!==a[`${e}${t}`])),r=a[`${e}${i||""}`],n=[...r?.assets??[],...r?.css??[],r.file,...(s?r?.imports??[]:[]).map((t=>a[t]?.file))].filter((t=>t&&this.getAssetType(t))).map((t=>`/${t}`));n&&(h[t]=n)})),t.writeFileSync(this.getAssetsManifestFile(),JSON.stringify(h,null,2),{encoding:"utf-8"})}getAliases(){const t={};return this.alias?.forEach((({find:s,replacement:e})=>{"string"==typeof s&&(t[s]=e)})),t}getRouteImportPostfix(){return["","/index"].map((t=>[".js",".ts",".tsx"].map((s=>`${t}${s}`)))).flat()}normalizeRoutePath(t){if(!t)return;let e="";if(t.startsWith("./")||t.startsWith("../"))e=s.resolve(this.root,t);else{const s=this.getAliases(),[i]=t.split("/");s[i]&&(e=t.replace(i,s[i]))}return e.replace(this.root,"").replace(/^\/|\/$/g,"")}getAssets(t){const s=t?.map((({route:t})=>t.id)).filter(Boolean)??[];if(!s.length)return[];const e=this.loadAssetsManifest();return s.map((t=>e[t])).filter(Boolean).flat()}getAssetWeight(t){switch(this.getAssetType(t)){case"style":return 1;case"script":return 2;default:return 3}}getAssetType(t){const s=t.split(".").at(-1)?.toLowerCase();switch(s){case"css":return"style";case"js":return"script";case"svg":case"jpg":case"jpeg":case"png":case"webp":case"gif":case"ico":return"image";case"ttf":case"otf":case"woff":case"woff2":return"font";default:return null}}writeEarlyHits(t,s){s.write(`HTTP/1.1 103 Early Hints${r}`),t.forEach((t=>{const e=this.getAssetType(t);e&&["style","script"].includes(e)&&s.write(`Link: <${t}>; rel=preload; as=${e}${r}`)})),s.write(r)}injectAssets(t,s=!0){const e=this.getAssets(t.routerContext?.matches).sort(((t,s)=>{const e=this.getAssetWeight(t),i=this.getAssetWeight(s);return e===i?0:e-i})),i=e.map((t=>t.endsWith(".css")?`<link rel="stylesheet" href="${t}">`:t.endsWith(".js")?`<script async type="module" src="${t}"><\/script>`:null)).filter(Boolean);t.html.header=t.html.header.replace("</head>",`${i.join("\n")}</head>`),s&&i.length&&t.res.socket&&this.writeEarlyHits(e,t.res.socket)}}export{n as default};
2
+ //# sourceMappingURL=ssr-manifest.js.map
@@ -0,0 +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 type { RouteObject } from 'react-router-dom';\nimport type { Alias } from 'vite';\nimport type { IRequestContext } from '@node/render';\nimport PrepareServer from '@services/prepare-server';\nimport ServerConfig from '@services/server-config';\n\ninterface ISsrManifestParams {\n alias?: Alias[];\n buildDir?: string;\n}\n\ninterface IManifest {\n [path: string]: {\n assets: string[];\n css: string[];\n file: string;\n imports: string[];\n };\n}\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 * Project root path\n */\n protected root: string;\n\n /**\n * Build dir\n */\n protected buildDir?: string;\n\n /**\n * Client manifest file name\n */\n protected manifestName = 'manifest.json';\n\n /**\n * Assets manifest file name\n */\n protected assetsManifest = 'assets-manifest.json';\n\n /**\n * Vite resolve aliases\n */\n protected alias?: Alias[];\n\n /**\n * Loaded assets manifest file\n */\n protected routesAssets: Record<string, string[]> | null = null;\n\n /**\n * @constructor\n */\n protected constructor(root: string, { buildDir, alias }: ISsrManifestParams = {}) {\n this.root = root;\n this.buildDir = buildDir;\n this.alias = alias;\n }\n\n /**\n * Get singleton instance\n */\n public static get(root: string, params: ISsrManifestParams = {}): SsrManifest {\n if (SsrManifest.instance === null) {\n SsrManifest.instance = new SsrManifest(root, params);\n }\n\n return SsrManifest.instance;\n }\n\n /**\n * Get assets manifest file name\n */\n protected getAssetsManifestFile(): string {\n const outDir = path.resolve(this.root, this.buildDir || '');\n\n return `${outDir}/server/${this.assetsManifest}`;\n }\n\n /**\n * Load client ssr manifest\n */\n protected loadClientManifest(): IManifest {\n const clientSsrManifest = path.resolve(\n this.root,\n `${this.buildDir || ''}/client/${this.manifestName}`,\n );\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 return result;\n }\n\n /**\n * Load assets manifest\n */\n protected loadAssetsManifest(): Record<string, string[]> {\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 string[]\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>> {\n const result = {};\n\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 const resolvedRoute = await route.lazy();\n\n result[routeId] = this.normalizeRoutePath(resolvedRoute?.['pathId'] as string);\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 * Build routes manifest file\n */\n public async buildRoutesManifest(shouldPreloadAssets: boolean): Promise<void> {\n const serverConfig = ServerConfig.init({ isProd: true });\n const prepareServer = PrepareServer.init(serverConfig);\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 = {};\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 const routeAssets = [\n ...(routeMeta?.assets ?? []),\n ...(routeMeta?.css ?? []),\n routeMeta.file,\n ...(shouldPreloadAssets ? routeMeta?.imports ?? [] : []).map(\n (nestedAsset) => manifest[nestedAsset]?.file,\n ),\n ]\n .filter(\n (asset) =>\n // keep only js,css,image,fonts files\n asset && this.getAssetType(asset),\n )\n .map((asset) => `/${asset}`);\n\n if (routeAssets) {\n result[routeId] = routeAssets;\n }\n });\n\n fs.writeFileSync(this.getAssetsManifestFile(), JSON.stringify(result, null, 2), {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Load aliases manifest\n */\n protected getAliases(): Record<string, string> {\n const aliases = {};\n\n this.alias?.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): 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 return fullPath.replace(this.root, '').replace(/^\\/|\\/$/g, '');\n }\n\n /**\n * Get provided route assets\n */\n public getAssets(routes?: AgnosticDataRouteMatch[]): string[] {\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 routeIds\n .map((routeId) => routesAssets[routeId])\n .filter(Boolean)\n .flat();\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 'style':\n return 1;\n\n case 'script':\n return 2;\n\n default:\n return 3;\n }\n }\n\n /**\n * Get asset type\n */\n protected getAssetType(asset: string): string | null {\n const ext = asset.split('.').at(-1)?.toLowerCase();\n\n switch (ext) {\n case 'css':\n return 'style';\n\n case 'js':\n return '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 'image';\n\n case 'ttf':\n case 'otf':\n case 'woff':\n case 'woff2':\n return 'font';\n\n default:\n return null;\n }\n }\n\n /**\n * Write 103 Early Hits header\n */\n public writeEarlyHits(assets: string[], socket: Socket): void {\n socket.write(`HTTP/1.1 103 Early Hints${CRLF}`);\n assets.forEach((asset) => {\n const type = this.getAssetType(asset);\n\n if (!type || !['style', 'script'].includes(type)) {\n return;\n }\n\n socket.write(`Link: <${asset}>; rel=preload; as=${type}${CRLF}`);\n });\n socket.write(CRLF);\n }\n\n /**\n * Inject route assets to head html\n */\n public injectAssets(context: IRequestContext, hasEarlyHits = true): void {\n const assets = this.getAssets(context.routerContext?.matches).sort((a, b) => {\n const aWeight = this.getAssetWeight(a);\n const bWeight = this.getAssetWeight(b);\n\n return aWeight === bWeight ? 0 : aWeight - bWeight;\n });\n const htmlAssets = assets\n .map((asset) => {\n if (asset.endsWith('.css')) {\n return `<link rel=\"stylesheet\" href=\"${asset}\">`;\n } else if (asset.endsWith('.js')) {\n return `<script async type=\"module\" src=\"${asset}\"></script>`;\n }\n\n return null;\n })\n .filter(Boolean);\n\n context.html.header = context.html.header.replace('</head>', `${htmlAssets.join('\\n')}</head>`);\n\n if (hasEarlyHits && htmlAssets.length && context.res.socket) {\n this.writeEarlyHits(assets, context.res.socket);\n }\n }\n}\n\nexport default SsrManifest;\n"],"names":["CRLF","SsrManifest","static","root","buildDir","manifestName","assetsManifest","alias","routesAssets","constructor","this","params","instance","getAssetsManifestFile","path","resolve","loadClientManifest","clientSsrManifest","fs","existsSync","result","JSON","parse","readFileSync","encoding","rmSync","loadAssetsManifest","manifestFile","async","routes","index","routeIndex","route","routeId","filter","Boolean","join","lazy","resolvedRoute","normalizeRoutePath","children","Object","assign","getRoutesIds","shouldPreloadAssets","serverConfig","ServerConfig","init","isProd","prepareServer","PrepareServer","manifest","loadEntrypoint","routesPaths","postfixes","getRouteImportPostfix","entries","forEach","routePath","routePostfix","find","postfix","undefined","routeMeta","routeAssets","assets","css","file","imports","map","nestedAsset","asset","getAssetType","writeFileSync","stringify","getAliases","aliases","replacement","prefix","ext","flat","fullPath","startsWith","routeAlias","split","replace","getAssets","routeIds","id","length","getAssetWeight","at","toLowerCase","writeEarlyHits","socket","write","type","includes","injectAssets","context","hasEarlyHits","routerContext","matches","sort","a","b","aWeight","bWeight","htmlAssets","endsWith","html","header","res"],"mappings":"qHAwBA,MAAMA,EAAO,OAKb,MAAMC,EAIMC,gBAAsC,KAKtCC,KAKAC,SAKAC,aAAe,gBAKfC,eAAiB,uBAKjBC,MAKAC,aAAgD,KAK1DC,YAAsBN,GAAcC,SAAEA,EAAQG,MAAEA,GAA8B,CAAA,GAC5EG,KAAKP,KAAOA,EACZO,KAAKN,SAAWA,EAChBM,KAAKH,MAAQA,CACd,CAKML,WAAWC,EAAcQ,EAA6B,IAK3D,OAJ6B,OAAzBV,EAAYW,WACdX,EAAYW,SAAW,IAAIX,EAAYE,EAAMQ,IAGxCV,EAAYW,QACpB,CAKSC,wBAGR,MAAO,GAFQC,EAAKC,QAAQL,KAAKP,KAAMO,KAAKN,UAAY,cAE7BM,KAAKJ,gBACjC,CAKSU,qBACR,MAAMC,EAAoBH,EAAKC,QAC7BL,KAAKP,KACL,GAAGO,KAAKN,UAAY,aAAaM,KAAKL,gBAGxC,IAAKa,EAAGC,WAAWF,GACjB,MAAO,GAGT,MAAMG,EAASC,KAAKC,MAClBJ,EAAGK,aAAaN,EAAmB,CAAEO,SAAU,WAKjD,OAFAN,EAAGO,OAAOR,GAEHG,CACR,CAKSM,qBACR,GAA0B,OAAtBhB,KAAKF,aACP,OAAOE,KAAKF,aAGd,MAAMmB,EAAejB,KAAKG,wBAE1B,OAAKK,EAAGC,WAAWQ,IAInBjB,KAAKF,aAAea,KAAKC,MAAMJ,EAAGK,aAAaI,EAAc,CAAEH,SAAU,WAKlEd,KAAKF,cARH,EASV,CAKSoB,mBACRC,EACAC,GAEA,MAAMV,EAAS,CAAA,EAEf,IAAK,MAAMW,KAAcF,EAAQ,CAC/B,MAAMG,EAAQH,EAAOE,GACfE,EAAU,CAACH,EAAOC,GAAYG,OAAOC,SAASC,KAAK,KAEzD,GAAIJ,EAAMK,KAAM,CACd,MAAMC,QAAsBN,EAAMK,OAElCjB,EAAOa,GAAWvB,KAAK6B,mBAAmBD,GAAwB,OACnE,MAAUN,EAAMQ,UACfC,OAAOC,OAAOtB,QAAcV,KAAKiC,aAAaX,EAAMQ,SAAUP,GAEjE,CAED,OAAOb,CACR,CAKMQ,0BAA0BgB,GAC/B,MAAMC,EAAeC,EAAaC,KAAK,CAAEC,QAAQ,IAC3CC,EAAgBC,EAAcH,KAAKF,GACnCM,EAAWzC,KAAKM,sBAChBa,OAAEA,SAAiBoB,EAAcG,gBAAe,GAChDC,QAAoB3C,KAAKiC,aAAad,GACtCyB,EAAY5C,KAAK6C,wBAEjBnC,EAAS,CAAA,EAGfqB,OAAOe,QAAQH,GAAaI,SAAQ,EAAExB,EAASyB,MAC7C,MAAMC,EAAeL,EAAUM,MAAMC,QAGLC,IAAvBX,EAFU,GAAGO,IAAYG,OAK5BE,EAAYZ,EADA,GAAGO,IAAYC,GAAgB,MAE3CK,EAAc,IACdD,GAAWE,QAAU,MACrBF,GAAWG,KAAO,GACtBH,EAAUI,SACNvB,EAAsBmB,GAAWK,SAAW,GAAK,IAAIC,KACtDC,GAAgBnB,EAASmB,IAAcH,QAGzCjC,QACEqC,GAECA,GAAS7D,KAAK8D,aAAaD,KAE9BF,KAAKE,GAAU,IAAIA,MAElBP,IACF5C,EAAOa,GAAW+B,EACnB,IAGH9C,EAAGuD,cAAc/D,KAAKG,wBAAyBQ,KAAKqD,UAAUtD,EAAQ,KAAM,GAAI,CAC9EI,SAAU,SAEb,CAKSmD,aACR,MAAMC,EAAU,CAAA,EAUhB,OARAlE,KAAKH,OAAOkD,SAAQ,EAAGG,OAAMiB,kBACP,iBAATjB,IAIXgB,EAAQhB,GAAQiB,EAAW,IAGtBD,CACR,CAKSrB,wBACR,MAAO,CAAC,GAAI,UACTc,KAAKS,GAAW,CAAC,MAAO,MAAO,QAAQT,KAAKU,GAAQ,GAAGD,IAASC,QAChEC,MACJ,CAKSzC,mBAAmBmB,GAC3B,IAAKA,EACH,OAGF,IAAIuB,EAAW,GAGf,GAAIvB,EAAUwB,WAAW,OAASxB,EAAUwB,WAAW,OACrDD,EAAWnE,EAAKC,QAAQL,KAAKP,KAAMuD,OAC9B,CAEL,MAAMkB,EAAUlE,KAAKiE,cAEdQ,GAAczB,EAAU0B,MAAM,KAEjCR,EAAQO,KACVF,EAAWvB,EAAU2B,QAAQF,EAAYP,EAAQO,IAEpD,CAED,OAAOF,EAASI,QAAQ3E,KAAKP,KAAM,IAAIkF,QAAQ,WAAY,GAC5D,CAKMC,UAAUzD,GACf,MAAM0D,EAAW1D,GAAQwC,KAAI,EAAGrC,WAAYA,EAAMwD,KAAItD,OAAOC,UAAY,GAEzE,IAAKoD,EAASE,OACZ,MAAO,GAGT,MAAMjF,EAAeE,KAAKgB,qBAE1B,OAAO6D,EACJlB,KAAKpC,GAAYzB,EAAayB,KAC9BC,OAAOC,SACP6C,MACJ,CAKSU,eAAenB,GAGvB,OAFa7D,KAAK8D,aAAaD,IAG7B,IAAK,QACH,OAAO,EAET,IAAK,SACH,OAAO,EAET,QACE,OAAO,EAEZ,CAKSC,aAAaD,GACrB,MAAMQ,EAAMR,EAAMa,MAAM,KAAKO,IAAI,IAAIC,cAErC,OAAQb,GACN,IAAK,MACH,MAAO,QAET,IAAK,KACH,MAAO,SAET,IAAK,MACL,IAAK,MACL,IAAK,OACL,IAAK,MACL,IAAK,OACL,IAAK,MACL,IAAK,MACH,MAAO,QAET,IAAK,MACL,IAAK,MACL,IAAK,OACL,IAAK,QACH,MAAO,OAET,QACE,OAAO,KAEZ,CAKMc,eAAe5B,EAAkB6B,GACtCA,EAAOC,MAAM,2BAA2B/F,KACxCiE,EAAOR,SAASc,IACd,MAAMyB,EAAOtF,KAAK8D,aAAaD,GAE1ByB,GAAS,CAAC,QAAS,UAAUC,SAASD,IAI3CF,EAAOC,MAAM,UAAUxB,uBAA2ByB,IAAOhG,IAAO,IAElE8F,EAAOC,MAAM/F,EACd,CAKMkG,aAAaC,EAA0BC,GAAe,GAC3D,MAAMnC,EAASvD,KAAK4E,UAAUa,EAAQE,eAAeC,SAASC,MAAK,CAACC,EAAGC,KACrE,MAAMC,EAAUhG,KAAKgF,eAAec,GAC9BG,EAAUjG,KAAKgF,eAAee,GAEpC,OAAOC,IAAYC,EAAU,EAAID,EAAUC,CAAO,IAE9CC,EAAa3C,EAChBI,KAAKE,GACAA,EAAMsC,SAAS,QACV,gCAAgCtC,MAC9BA,EAAMsC,SAAS,OACjB,oCAAoCtC,gBAGtC,OAERrC,OAAOC,SAEVgE,EAAQW,KAAKC,OAASZ,EAAQW,KAAKC,OAAO1B,QAAQ,UAAW,GAAGuB,EAAWxE,KAAK,gBAE5EgE,GAAgBQ,EAAWnB,QAAUU,EAAQa,IAAIlB,QACnDpF,KAAKmF,eAAe5B,EAAQkC,EAAQa,IAAIlB,OAE3C"}
@@ -1,73 +0,0 @@
1
- name: SSR BOOST Build
2
-
3
- on:
4
- workflow_call:
5
- inputs:
6
- node-version:
7
- required: false
8
- type: string
9
- default: 18.13.0
10
- app-build-args:
11
- required: false
12
- type: string
13
- docker-file:
14
- required: false
15
- type: string
16
- default: node_modules/@lomray/vite-ssr-boost/workflow/Dockerfile
17
- secrets:
18
- github-token:
19
- required: false
20
-
21
- jobs:
22
- build:
23
- runs-on: ubuntu-latest
24
- concurrency:
25
- group: ${{ github.ref }}-build
26
- cancel-in-progress: true
27
-
28
- steps:
29
- - uses: actions/checkout@v3
30
-
31
- - run: echo "//npm.pkg.github.com/:_authToken=${{ secrets.github-token }}" > ~/.npmrc
32
-
33
- - uses: actions/setup-node@v3
34
- with:
35
- node-version: ${{ inputs.config-path }}
36
- cache: 'npm'
37
-
38
- - name: Install dependencies
39
- run: npm ci
40
-
41
- - name: Run eslint
42
- run: npm run lint:check
43
-
44
- - name: Typescript check
45
- run: npm run ts:check
46
-
47
- - name: Stylelint check
48
- run: npm run style:check
49
-
50
- - name: Build application
51
- run: |
52
- npm pkg delete scripts.prepare
53
- npm run build -- ${{ inputs.app-build-args }}
54
-
55
- - name: Prepare docker file
56
- if: ${{ inputs.docker-file != '' }}
57
- shell: bash
58
- id: prepare-docker
59
- run: |
60
- # ignore errors
61
- set +e
62
- cp ${{ inputs.docker-file }} Dockerfile
63
- echo "docker-file-name=Dockerfile" >> $GITHUB_OUTPUT
64
-
65
- - name: Archive build
66
- uses: actions/upload-artifact@v3
67
- with:
68
- name: build-artifact
69
- path: |
70
- build
71
- package.json
72
- package-lock.json
73
- ${{ steps.prepare-docker.outputs.docker-file-name }}
@@ -1,60 +0,0 @@
1
- name: SSR BOOST Deploy AWS
2
-
3
- on:
4
- workflow_call:
5
- inputs:
6
- image:
7
- required: true
8
- type: string
9
- service:
10
- required: true
11
- type: string
12
- cluster:
13
- required: true
14
- type: string
15
- task-container-name:
16
- required: true
17
- type: string
18
- task-definition:
19
- required: false
20
- type: string
21
- default: .github/task-definition.json
22
- secrets:
23
- AWS_ACCESS_KEY_ID:
24
- required: true
25
- AWS_SECRET_ACCESS_KEY:
26
- required: true
27
- AWS_REGION:
28
- required: true
29
-
30
- jobs:
31
- deploy-aws:
32
- runs-on: ubuntu-latest
33
- concurrency:
34
- group: ${{ github.ref }}-deploy-aws
35
- cancel-in-progress: true
36
-
37
- steps:
38
- - uses: actions/checkout@v3
39
-
40
- - name: Configure AWS credentials
41
- uses: aws-actions/configure-aws-credentials@v2
42
- with:
43
- aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
44
- aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
45
- aws-region: ${{ secrets.AWS_REGION }}
46
-
47
- - name: Update AWS ECS Task Definition
48
- id: task-def
49
- uses: aws-actions/amazon-ecs-render-task-definition@v1
50
- with:
51
- task-definition: ${{ inputs.task-definition }}
52
- container-name: ${{ inputs.task-container-name }}
53
- image: ${{ inputs.image }}
54
-
55
- - name: Deploy AWS ECS Task Definition
56
- uses: aws-actions/amazon-ecs-deploy-task-definition@v1
57
- with:
58
- task-definition: ${{ steps.task-def.outputs.task-definition }}
59
- service: ${{ inputs.service }}
60
- cluster: ${{ inputs.cluster }}
@@ -1,71 +0,0 @@
1
- name: SSR BOOST Docker build
2
-
3
- on:
4
- workflow_call:
5
- inputs:
6
- registry:
7
- required: true
8
- type: string
9
- image-name:
10
- required: true
11
- type: string
12
- version:
13
- required: true
14
- type: string
15
- app-build-path:
16
- required: true
17
- type: string
18
- secrets:
19
- github-token:
20
- required: true
21
- outputs:
22
- image-tag:
23
- description: "Docker image with new tag version"
24
- value: ${{ jobs.docker-build.outputs.image-tag }}
25
-
26
- jobs:
27
- docker-build:
28
- runs-on: ubuntu-latest
29
- concurrency:
30
- group: ${{ github.ref }}-build-docker
31
- cancel-in-progress: true
32
- outputs:
33
- # get docker image tag with version
34
- image-tag: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}
35
-
36
- steps:
37
- - uses: actions/download-artifact@v3
38
- with:
39
- name: build-artifact
40
-
41
- - name: Setup Docker buildx
42
- uses: docker/setup-buildx-action@v2
43
-
44
- - name: Log into registry
45
- uses: docker/login-action@v2
46
- with:
47
- registry: ${{ inputs.registry }}
48
- username: ${{ github.actor }}
49
- password: ${{ secrets.github-token }}
50
-
51
- - name: Extract Docker metadata
52
- id: meta
53
- uses: docker/metadata-action@v4
54
- with:
55
- images: ${{ inputs.registry }}/${{ inputs.image-name }}
56
- tags: |
57
- type=raw,prefix={{branch}}-,value=${{ inputs.version }}
58
- type=raw,prefix=latest-,value={{branch}}
59
-
60
- - name: Build and push Docker image
61
- id: build-and-push
62
- uses: docker/build-push-action@v4
63
- with:
64
- context: .
65
- push: true
66
- tags: ${{ steps.meta.outputs.tags }}
67
- labels: ${{ steps.meta.outputs.labels }}
68
- cache-from: type=gha
69
- cache-to: type=gha,mode=max
70
- build-args: |
71
- BUILD_PATH=${{ inputs.app-build-path }}
@@ -1,48 +0,0 @@
1
- name: SSR BOOST Release
2
-
3
- on:
4
- workflow_call:
5
- inputs:
6
- has-release-asset:
7
- required: false
8
- type: boolean
9
- default: false
10
- secrets:
11
- github-token:
12
- required: true
13
- outputs:
14
- version:
15
- description: "New package version"
16
- value: ${{ jobs.release.outputs.version }}
17
-
18
- jobs:
19
- release:
20
- runs-on: ubuntu-latest
21
- concurrency:
22
- group: ${{ github.ref }}-release
23
- cancel-in-progress: true
24
- outputs:
25
- version: ${{ steps.package-version.outputs.version }}
26
-
27
- steps:
28
- - uses: actions/checkout@v3
29
-
30
- - uses: actions/download-artifact@v3
31
- with:
32
- name: build-artifact
33
-
34
- - name: Create release asset
35
- if: ${{ inputs.has-release-asset }}
36
- run: zip -r build.zip build package.json package-lock.json README.md
37
-
38
- - name: Install dependencies
39
- run: npm ci
40
-
41
- - name: Release
42
- env:
43
- GITHUB_TOKEN: ${{ secrets.github-token }}
44
- run: npx semantic-release
45
-
46
- - name: Get version
47
- id: package-version
48
- run: npx @lomray/microservices-cli package-version