@lomray/vite-ssr-boost 3.0.0-beta.4 → 3.0.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lomray/vite-ssr-boost",
3
- "version": "3.0.0-beta.4",
3
+ "version": "3.0.0-beta.6",
4
4
  "description": "Vite plugin for create awesome SSR or SPA applications on React.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -1,2 +1,2 @@
1
- import e from"node:fs";import n from"node:path";import r from"node:process";import o from"../constants/plugin-name.js";const t=`${o}-handle-custom-entrypoint`,i=()=>r.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME,l=e=>{r.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME=e},s=(e,n=i())=>{if(!e.length||!n)return null;for(const r of e)if(r.name===n&&!r.serverFile)return r;return null};function c(r){const{entrypoint:i}=r;let l="",s="";return{name:t,enforce:"pre",apply:(e,{isSsrBuild:n})=>!n&&Boolean(i),config(e){const{indexFile:r}=i,o=e.build??{},t=r?n.resolve(e.root??"",r):void 0;return{...e,build:{...o,rollupOptions:{...o.rollupOptions??{},input:t}}}},configResolved(e){const r=e.plugins.find((e=>e.name===o));l=n.resolve(e.root,e.build.outDir),s=r.pluginOptions.clientFile},transform(e,n){if(n.endsWith(".html")){const{clientFile:n}=i;if(n)return e.replace(s,n)}return e},transformIndexHtml(e,{originalUrl:n,server:r}){const{clientFile:o}=i;return o&&"serve"===r?.config.command&&n?.endsWith(".html")?e.replace(s,o):e},closeBundle(){const{indexFile:r}=i;if(!r)return;const o=n.resolve(l,n.basename(r));e.existsSync(o)&&e.renameSync(o,n.resolve(l,"index.html"))}}}export{c as ViteHandleCustomEntrypointPlugin,s as getCurrentEntrypoint,i as getCurrentEntrypointName,l as setCurrentEntrypointName};
1
+ import e from"node:fs";import n from"node:path";import o from"node:process";import r from"../constants/plugin-name.js";const t=`${r}-handle-custom-entrypoint`,i=()=>o.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME,l=e=>{o.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME=e},s=(e,n=i())=>{if(!e.length||!n)return null;for(const o of e)if(o.name===n&&!o.serverFile)return o;return null};function c(o){const{entrypoint:i}=o;let l="",s="";return{name:t,enforce:"pre",apply:(e,{isSsrBuild:n})=>!n&&Boolean(i),config(e){const{indexFile:o}=i,r=e.build??{},t=o?n.resolve(e.root??"",o):void 0;return{...e,build:{...r,rollupOptions:{...r.rollupOptions??{},input:t}}}},configResolved(e){const o=e.plugins.find((e=>e.name===r));l=n.resolve(e.root,e.build.outDir),s=o.pluginOptions.clientFile},transform(e,n){if(n.endsWith(".html")){const{clientFile:n}=i;if(n)return{code:e.replace(s,n),map:this.getCombinedSourcemap()}}return{code:e,map:this.getCombinedSourcemap()}},transformIndexHtml(e,{originalUrl:n,server:o}){const{clientFile:r}=i;return r&&"serve"===o?.config.command&&n?.endsWith(".html")?e.replace(s,r):e},closeBundle(){const{indexFile:o}=i;if(!o)return;const r=n.resolve(l,n.basename(o));e.existsSync(r)&&e.renameSync(r,n.resolve(l,"index.html"))}}}export{c as ViteHandleCustomEntrypointPlugin,s as getCurrentEntrypoint,i as getCurrentEntrypointName,l as setCurrentEntrypointName};
2
2
  //# sourceMappingURL=handle-custom-entrypoint.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"handle-custom-entrypoint.js","sources":["../../src/plugins/handle-custom-entrypoint.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport type { Plugin } from 'vite';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport type { IBuildEntrypoint } from '@services/build';\n\nexport interface IPluginOptions {\n entrypoint: IBuildEntrypoint;\n}\n\nconst pluginName = `${PLUGIN_NAME}-handle-custom-entrypoint`;\n\n/**\n * Get current entrypoint name\n */\nconst getCurrentEntrypointName = (): string | undefined =>\n process.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME;\n\n/**\n * Set current entrypoint name\n */\nconst setCurrentEntrypointName = (name: string): void => {\n process.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME = name;\n};\n\n/**\n * Find current entrypoint by env\n */\nconst getCurrentEntrypoint = (\n entrypoint: IBuildEntrypoint[],\n currentEntrypointName = getCurrentEntrypointName(),\n): IBuildEntrypoint | null => {\n if (!entrypoint.length || !currentEntrypointName) {\n return null;\n }\n\n for (const entry of entrypoint) {\n if (entry.name === currentEntrypointName && !entry.serverFile) {\n return entry;\n }\n }\n\n return null;\n};\n\n/**\n * Return custom entrypoint instead default (index.html).\n *\n * E.g. for build multiple entrypoint\n * @constructor\n */\nfunction ViteHandleCustomEntrypointPlugin(options: IPluginOptions): Plugin {\n const { entrypoint } = options;\n let outPath = '';\n let origClientFile = '';\n\n return {\n name: pluginName,\n enforce: 'pre',\n /**\n * Apply only on build but not for SSR and only for custom entrypoint\n */\n apply(_, { isSsrBuild }): boolean {\n return !isSsrBuild && Boolean(entrypoint);\n },\n config(config) {\n const { indexFile } = entrypoint;\n const buildConfig = config.build ?? {};\n const indexFilePath = indexFile ? path.resolve(config.root ?? '', indexFile) : undefined;\n\n return {\n ...config,\n build: {\n ...buildConfig,\n rollupOptions: {\n ...(buildConfig.rollupOptions ?? {}),\n input: indexFilePath,\n },\n },\n };\n },\n configResolved(config) {\n const pluginConfig = config.plugins.find((plugin) => plugin.name === PLUGIN_NAME);\n\n outPath = path.resolve(config.root, config.build.outDir);\n // @ts-expect-error pluginOptions is custom param\n origClientFile = (pluginConfig.pluginOptions as Record<string, any>).clientFile as string;\n },\n transform(code, id): string {\n if (id.endsWith('.html')) {\n const { clientFile } = entrypoint;\n\n if (clientFile) {\n return code.replace(origClientFile, clientFile);\n }\n }\n\n return code;\n },\n /**\n * Development mode\n */\n transformIndexHtml(html, { originalUrl, server }): string {\n const { clientFile } = entrypoint;\n\n if (clientFile && server?.config.command === 'serve' && originalUrl?.endsWith('.html')) {\n return html.replace(origClientFile, clientFile);\n }\n\n return html;\n },\n closeBundle() {\n const { indexFile } = entrypoint;\n\n if (!indexFile) {\n return;\n }\n\n const indexFilePath = path.resolve(outPath, path.basename(indexFile));\n\n if (fs.existsSync(indexFilePath)) {\n fs.renameSync(indexFilePath, path.resolve(outPath, 'index.html'));\n }\n },\n };\n}\n\nexport {\n ViteHandleCustomEntrypointPlugin,\n getCurrentEntrypoint,\n getCurrentEntrypointName,\n setCurrentEntrypointName,\n};\n"],"names":["pluginName","PLUGIN_NAME","getCurrentEntrypointName","process","env","SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME","setCurrentEntrypointName","name","getCurrentEntrypoint","entrypoint","currentEntrypointName","length","entry","serverFile","ViteHandleCustomEntrypointPlugin","options","outPath","origClientFile","enforce","apply","_","isSsrBuild","Boolean","config","indexFile","buildConfig","build","indexFilePath","path","resolve","root","undefined","rollupOptions","input","configResolved","pluginConfig","plugins","find","plugin","outDir","pluginOptions","clientFile","transform","code","id","endsWith","replace","transformIndexHtml","html","originalUrl","server","command","closeBundle","basename","fs","existsSync","renameSync"],"mappings":"uHAWA,MAAMA,EAAa,GAAGC,6BAKhBC,EAA2B,IAC/BC,EAAQC,IAAIC,uCAKRC,EAA4BC,IAChCJ,EAAQC,IAAIC,uCAAyCE,CAAI,EAMrDC,EAAuB,CAC3BC,EACAC,EAAwBR,OAExB,IAAKO,EAAWE,SAAWD,EACzB,OAAO,KAGT,IAAK,MAAME,KAASH,EAClB,GAAIG,EAAML,OAASG,IAA0BE,EAAMC,WACjD,OAAOD,EAIX,OAAO,IAAI,EASb,SAASE,EAAiCC,GACxC,MAAMN,WAAEA,GAAeM,EACvB,IAAIC,EAAU,GACVC,EAAiB,GAErB,MAAO,CACLV,KAAMP,EACNkB,QAAS,MAITC,MAAK,CAACC,GAAGC,WAAEA,MACDA,GAAcC,QAAQb,GAEhCc,OAAOA,GACL,MAAMC,UAAEA,GAAcf,EAChBgB,EAAcF,EAAOG,OAAS,GAC9BC,EAAgBH,EAAYI,EAAKC,QAAQN,EAAOO,MAAQ,GAAIN,QAAaO,EAE/E,MAAO,IACFR,EACHG,MAAO,IACFD,EACHO,cAAe,IACTP,EAAYO,eAAiB,CAAE,EACnCC,MAAON,IAId,EACDO,eAAeX,GACb,MAAMY,EAAeZ,EAAOa,QAAQC,MAAMC,GAAWA,EAAO/B,OAASN,IAErEe,EAAUY,EAAKC,QAAQN,EAAOO,KAAMP,EAAOG,MAAMa,QAEjDtB,EAAkBkB,EAAaK,cAAsCC,UACtE,EACDC,UAAUC,EAAMC,GACd,GAAIA,EAAGC,SAAS,SAAU,CACxB,MAAMJ,WAAEA,GAAehC,EAEvB,GAAIgC,EACF,OAAOE,EAAKG,QAAQ7B,EAAgBwB,EAEvC,CAED,OAAOE,CACR,EAIDI,mBAAmBC,GAAMC,YAAEA,EAAWC,OAAEA,IACtC,MAAMT,WAAEA,GAAehC,EAEvB,OAAIgC,GAAyC,UAA3BS,GAAQ3B,OAAO4B,SAAuBF,GAAaJ,SAAS,SACrEG,EAAKF,QAAQ7B,EAAgBwB,GAG/BO,CACR,EACDI,cACE,MAAM5B,UAAEA,GAAcf,EAEtB,IAAKe,EACH,OAGF,MAAMG,EAAgBC,EAAKC,QAAQb,EAASY,EAAKyB,SAAS7B,IAEtD8B,EAAGC,WAAW5B,IAChB2B,EAAGE,WAAW7B,EAAeC,EAAKC,QAAQb,EAAS,cAEtD,EAEL"}
1
+ {"version":3,"file":"handle-custom-entrypoint.js","sources":["../../src/plugins/handle-custom-entrypoint.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport type { Plugin } from 'vite';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport type { IBuildEntrypoint } from '@services/build';\n\nexport interface IPluginOptions {\n entrypoint: IBuildEntrypoint;\n}\n\nconst pluginName = `${PLUGIN_NAME}-handle-custom-entrypoint`;\n\n/**\n * Get current entrypoint name\n */\nconst getCurrentEntrypointName = (): string | undefined =>\n process.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME;\n\n/**\n * Set current entrypoint name\n */\nconst setCurrentEntrypointName = (name: string): void => {\n process.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME = name;\n};\n\n/**\n * Find current entrypoint by env\n */\nconst getCurrentEntrypoint = (\n entrypoint: IBuildEntrypoint[],\n currentEntrypointName = getCurrentEntrypointName(),\n): IBuildEntrypoint | null => {\n if (!entrypoint.length || !currentEntrypointName) {\n return null;\n }\n\n for (const entry of entrypoint) {\n if (entry.name === currentEntrypointName && !entry.serverFile) {\n return entry;\n }\n }\n\n return null;\n};\n\n/**\n * Return custom entrypoint instead default (index.html).\n *\n * E.g. for build multiple entrypoint\n * @constructor\n */\nfunction ViteHandleCustomEntrypointPlugin(options: IPluginOptions): Plugin {\n const { entrypoint } = options;\n let outPath = '';\n let origClientFile = '';\n\n return {\n name: pluginName,\n enforce: 'pre',\n /**\n * Apply only on build but not for SSR and only for custom entrypoint\n */\n apply(_, { isSsrBuild }): boolean {\n return !isSsrBuild && Boolean(entrypoint);\n },\n config(config) {\n const { indexFile } = entrypoint;\n const buildConfig = config.build ?? {};\n const indexFilePath = indexFile ? path.resolve(config.root ?? '', indexFile) : undefined;\n\n return {\n ...config,\n build: {\n ...buildConfig,\n rollupOptions: {\n ...(buildConfig.rollupOptions ?? {}),\n input: indexFilePath,\n },\n },\n };\n },\n configResolved(config) {\n const pluginConfig = config.plugins.find((plugin) => plugin.name === PLUGIN_NAME);\n\n outPath = path.resolve(config.root, config.build.outDir);\n // @ts-expect-error pluginOptions is custom param\n origClientFile = (pluginConfig.pluginOptions as Record<string, any>).clientFile as string;\n },\n transform(code, id) {\n if (id.endsWith('.html')) {\n const { clientFile } = entrypoint;\n\n if (clientFile) {\n return {\n code: code.replace(origClientFile, clientFile),\n map: this.getCombinedSourcemap(),\n };\n }\n }\n\n return {\n code,\n map: this.getCombinedSourcemap(),\n };\n },\n /**\n * Development mode\n */\n transformIndexHtml(html, { originalUrl, server }): string {\n const { clientFile } = entrypoint;\n\n if (clientFile && server?.config.command === 'serve' && originalUrl?.endsWith('.html')) {\n return html.replace(origClientFile, clientFile);\n }\n\n return html;\n },\n closeBundle() {\n const { indexFile } = entrypoint;\n\n if (!indexFile) {\n return;\n }\n\n const indexFilePath = path.resolve(outPath, path.basename(indexFile));\n\n if (fs.existsSync(indexFilePath)) {\n fs.renameSync(indexFilePath, path.resolve(outPath, 'index.html'));\n }\n },\n };\n}\n\nexport {\n ViteHandleCustomEntrypointPlugin,\n getCurrentEntrypoint,\n getCurrentEntrypointName,\n setCurrentEntrypointName,\n};\n"],"names":["pluginName","PLUGIN_NAME","getCurrentEntrypointName","process","env","SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME","setCurrentEntrypointName","name","getCurrentEntrypoint","entrypoint","currentEntrypointName","length","entry","serverFile","ViteHandleCustomEntrypointPlugin","options","outPath","origClientFile","enforce","apply","_","isSsrBuild","Boolean","config","indexFile","buildConfig","build","indexFilePath","path","resolve","root","undefined","rollupOptions","input","configResolved","pluginConfig","plugins","find","plugin","outDir","pluginOptions","clientFile","transform","code","id","endsWith","replace","map","this","getCombinedSourcemap","transformIndexHtml","html","originalUrl","server","command","closeBundle","basename","fs","existsSync","renameSync"],"mappings":"uHAWA,MAAMA,EAAa,GAAGC,6BAKhBC,EAA2B,IAC/BC,EAAQC,IAAIC,uCAKRC,EAA4BC,IAChCJ,EAAQC,IAAIC,uCAAyCE,CAAI,EAMrDC,EAAuB,CAC3BC,EACAC,EAAwBR,OAExB,IAAKO,EAAWE,SAAWD,EACzB,OAAO,KAGT,IAAK,MAAME,KAASH,EAClB,GAAIG,EAAML,OAASG,IAA0BE,EAAMC,WACjD,OAAOD,EAIX,OAAO,IAAI,EASb,SAASE,EAAiCC,GACxC,MAAMN,WAAEA,GAAeM,EACvB,IAAIC,EAAU,GACVC,EAAiB,GAErB,MAAO,CACLV,KAAMP,EACNkB,QAAS,MAITC,MAAK,CAACC,GAAGC,WAAEA,MACDA,GAAcC,QAAQb,GAEhCc,OAAOA,GACL,MAAMC,UAAEA,GAAcf,EAChBgB,EAAcF,EAAOG,OAAS,GAC9BC,EAAgBH,EAAYI,EAAKC,QAAQN,EAAOO,MAAQ,GAAIN,QAAaO,EAE/E,MAAO,IACFR,EACHG,MAAO,IACFD,EACHO,cAAe,IACTP,EAAYO,eAAiB,CAAE,EACnCC,MAAON,IAId,EACDO,eAAeX,GACb,MAAMY,EAAeZ,EAAOa,QAAQC,MAAMC,GAAWA,EAAO/B,OAASN,IAErEe,EAAUY,EAAKC,QAAQN,EAAOO,KAAMP,EAAOG,MAAMa,QAEjDtB,EAAkBkB,EAAaK,cAAsCC,UACtE,EACDC,UAAUC,EAAMC,GACd,GAAIA,EAAGC,SAAS,SAAU,CACxB,MAAMJ,WAAEA,GAAehC,EAEvB,GAAIgC,EACF,MAAO,CACLE,KAAMA,EAAKG,QAAQ7B,EAAgBwB,GACnCM,IAAKC,KAAKC,uBAGf,CAED,MAAO,CACLN,OACAI,IAAKC,KAAKC,uBAEb,EAIDC,mBAAmBC,GAAMC,YAAEA,EAAWC,OAAEA,IACtC,MAAMZ,WAAEA,GAAehC,EAEvB,OAAIgC,GAAyC,UAA3BY,GAAQ9B,OAAO+B,SAAuBF,GAAaP,SAAS,SACrEM,EAAKL,QAAQ7B,EAAgBwB,GAG/BU,CACR,EACDI,cACE,MAAM/B,UAAEA,GAAcf,EAEtB,IAAKe,EACH,OAGF,MAAMG,EAAgBC,EAAKC,QAAQb,EAASY,EAAK4B,SAAShC,IAEtDiC,EAAGC,WAAW/B,IAChB8B,EAAGE,WAAWhC,EAAeC,EAAKC,QAAQb,EAAS,cAEtD,EAEL"}
@@ -29,7 +29,7 @@ interface IBuildEntrypoint {
29
29
  indexFile?: string;
30
30
  clientFile?: string;
31
31
  serverFile?: string;
32
- options?: string;
32
+ buildOptions?: string;
33
33
  }
34
34
  /**
35
35
  * Build service
package/services/build.js CHANGED
@@ -1,2 +1,2 @@
1
- import i from"node:child_process";import s from"node:fs";import t from"node:path";import e from"chalk";import{resolveConfig as o}from"vite";import n from"../cli/helpers/vite-reset-cache.js";import r from"../helpers/create-focus-only.js";import{createDevMarker as l}from"../helpers/dev-marker.js";import a from"../helpers/plugin-config.js";import c from"../helpers/process-stop.js";import{readMeta as u,removeMeta as d}from"../helpers/ssr-meta.js";import h from"./server-config.js";import p from"./ssr-manifest.js";class f{isProd;nodeEnv;buildDir;viteConfig;pluginConfig;params={mode:"",clientOptions:"",serverOptions:"",focusOnly:"app",isWatch:!1,isUnlockRobots:!1,isEject:!1,isServerless:!1,isNoWarnings:!1};abortController=null;runningBuild=[];hasPreviewModeExitListener=!1;constructor(i){this.params={...this.params,...i}}async makeConfig(){const{mode:i}=this.params;this.viteConfig=await o({},"build",i,"production"),this.pluginConfig=a(this.viteConfig),this.buildDir=t.resolve(this.viteConfig.root,this.viteConfig.build.outDir),this.nodeEnv=process.env.NODE_ENV||"production",this.isProd="production"===this.nodeEnv}clearBuildFolder(){s.existsSync(this.buildDir)&&s.rmSync(this.buildDir,{recursive:!0})}getIsProd(){return this.isProd}getNodeEnv(){return this.nodeEnv}getRunningBuildNames(){return this.runningBuild.map((({name:i})=>i))}promisifyProcess(i,s=!1){const t=new Promise(((t,e)=>{i.on("exit",(i=>{t(i)})),i.on("close",(i=>{t(i)})),i.on("error",(i=>{e(i)})),s&&i.stderr?.on("data",(i=>{const s=Buffer.from(i).toString();(s.includes("warning")||s.includes("WARNING"))&&t(1)}))}));return i.stdout?.pipe(process.stdout),i.stderr?.pipe(process.stderr),{promise:t,command:i}}async buildManifest(){console.info(e.blue(`Building routes manifest file: ${this.pluginConfig.routesParsing}`));const i="node"===this.pluginConfig.routesParsing,s=h.init({isProd:this.isProd,mode:this.params.mode},{root:this.viteConfig.root,clientFile:this.pluginConfig.clientFile});await p.get(s,{buildDir:this.viteConfig.build.outDir,viteAliases:this.viteConfig.resolve.alias}).buildRoutesManifest(i),i&&this.cleanupClientRoutes()}cleanupClientRoutes(){const{routeFiles:i}=u(this.buildDir),t=new Set(Object.values(i??[]));t.size&&t.forEach((i=>{const t=`${this.buildDir}/client/${i}`;try{const i=s.readFileSync(t,{encoding:"utf-8"}).replace(/(lazy:.*?\((.*?)\)),\s?".*?"\)/g,"$1)");s.writeFileSync(t,i)}catch(i){console.log(`Failed cleanup client route ${t}:`,i)}}))}unlockRobots(){const i=`${this.buildDir}/client/robots.txt`;if(!s.existsSync(i))return void console.warn(`Failed to unlock robots.txt, file not exist: ${i}`);const t=s.readFileSync(i,{encoding:"utf-8"}).replace(/Disallow: \/$/m,"Allow: /");s.writeFileSync(i,t,{encoding:"utf-8"}),console.info(e.blue("\nrobots.txt unlocked."))}eject(){const i=`${this.buildDir}/server/start.js`;s.writeFileSync(i,"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\n\nconst VERSION = process.env.VERSION || \"1.0.0\";\nconst PORT = process.env.PORT || 3000;\nconst IS_HOST = process.env.IS_HOST || \"0\";\nconst ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\n\nawait runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\n",{encoding:"utf-8"})}createServerless(){const i=`${this.buildDir}/server/serverless.js`;s.writeFileSync(i,"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\n\nexport default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\n",{encoding:"utf-8"})}async spawnBuild(s,t,e={}){const{mode:o,isNoWarnings:n}=this.params,{focusOnly:l=this.params.focusOnly,shouldWait:a=!1,env:c={}}=e,u=o?`--mode ${o}`:"",d=this.promisifyProcess(i.spawn(`vite build ${t} ${u} --emptyOutDir`,{signal:this.abortController.signal,stdio:[process.stdin,"pipe","pipe"],shell:!0,env:{...process.env,...c,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:r(l).isOnlyClient()?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}),n);this.runningBuild.push({name:s,buildProcess:d}),a&&await this.waitLastBuild()}async waitLastBuild(){const i=this.runningBuild.at(-1);if(!i)return;const s=await i.buildProcess.promise;c(s,!0)}runPreviewMode(){this.hasPreviewModeExitListener||(process.on("exit",(()=>{this.abortController.abort()})),this.hasPreviewModeExitListener=!0);const{onFinish:i}=this.params;let s=this.runningBuild.length;const t=e=>{Buffer.from(e).toString().includes("built in")&&(s-=1,s||(this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.removeListener("data",t)})),l(this.isProd,this.viteConfig),i?.()))};this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.on("data",t)}))}async build(){await this.makeConfig(),await n(),this.clearBuildFolder();const{clientOptions:i,serverOptions:s,onFinish:t,isWatch:e,focusOnly:o,isEject:a,isServerless:c,isUnlockRobots:u}=this.params,{outDir:h}=this.viteConfig.build,p=r(o);this.abortController=new AbortController,this.runningBuild=[],p.isClient()&&await this.spawnBuild("client",`${i} --outDir ${h}/client`,{shouldWait:!e}),p.isServer()&&(await this.spawnBuild("server",`${s} --outDir ${h}/server --ssr ${this.pluginConfig.serverFile}`,{shouldWait:!e}),e||(await this.buildManifest(),a&&this.eject(),c&&this.createServerless()));const{entrypoint:f}=this.pluginConfig;if(f?.length&&p.isEntrypoint())for(const{name:i,type:s,serverFile:t,options:o=""}of f){const n=t&&"ssr"===s?`--ssr ${t}`:"";await this.spawnBuild(i,`${o} ${n} --outDir ${h}/${i}`,{shouldWait:!e,focusOnly:"ssr"===s?"server":"client",env:{SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME:i}})}e?this.runPreviewMode():(u&&this.unlockRobots(),l(this.isProd,this.viteConfig),d(this.buildDir),t?.())}}export{f as default};
1
+ import i from"node:child_process";import s from"node:fs";import t from"node:path";import e from"chalk";import{resolveConfig as o}from"vite";import n from"../cli/helpers/vite-reset-cache.js";import r from"../helpers/create-focus-only.js";import{createDevMarker as l}from"../helpers/dev-marker.js";import a from"../helpers/plugin-config.js";import c from"../helpers/process-stop.js";import{readMeta as u,removeMeta as d}from"../helpers/ssr-meta.js";import h from"./server-config.js";import p from"./ssr-manifest.js";class f{isProd;nodeEnv;buildDir;viteConfig;pluginConfig;params={mode:"",clientOptions:"",serverOptions:"",focusOnly:"app",isWatch:!1,isUnlockRobots:!1,isEject:!1,isServerless:!1,isNoWarnings:!1};abortController=null;runningBuild=[];hasPreviewModeExitListener=!1;constructor(i){this.params={...this.params,...i}}async makeConfig(){const{mode:i}=this.params;this.viteConfig=await o({},"build",i,"production"),this.pluginConfig=a(this.viteConfig),this.buildDir=t.resolve(this.viteConfig.root,this.viteConfig.build.outDir),this.nodeEnv=process.env.NODE_ENV||"production",this.isProd="production"===this.nodeEnv}clearBuildFolder(){s.existsSync(this.buildDir)&&s.rmSync(this.buildDir,{recursive:!0})}getIsProd(){return this.isProd}getNodeEnv(){return this.nodeEnv}getRunningBuildNames(){return this.runningBuild.map((({name:i})=>i))}promisifyProcess(i,s=!1){const t=new Promise(((t,e)=>{i.on("exit",(i=>{t(i)})),i.on("close",(i=>{t(i)})),i.on("error",(i=>{e(i)})),s&&i.stderr?.on("data",(i=>{const s=Buffer.from(i).toString();(s.includes("warning")||s.includes("WARNING"))&&t(1)}))}));return i.stdout?.pipe(process.stdout),i.stderr?.pipe(process.stderr),{promise:t,command:i}}async buildManifest(){console.info(e.blue(`Building routes manifest file: ${this.pluginConfig.routesParsing}`));const i="node"===this.pluginConfig.routesParsing,s=h.init({isProd:this.isProd,mode:this.params.mode},{root:this.viteConfig.root,clientFile:this.pluginConfig.clientFile});await p.get(s,{buildDir:this.viteConfig.build.outDir,viteAliases:this.viteConfig.resolve.alias}).buildRoutesManifest(i),i&&this.cleanupClientRoutes()}cleanupClientRoutes(){const{routeFiles:i}=u(this.buildDir),t=new Set(Object.values(i??[]));t.size&&t.forEach((i=>{const t=`${this.buildDir}/client/${i}`;try{const i=s.readFileSync(t,{encoding:"utf-8"}).replace(/(lazy:.*?\((.*?)\)),\s?".*?"\)/g,"$1)");s.writeFileSync(t,i)}catch(i){console.log(`Failed cleanup client route ${t}:`,i)}}))}unlockRobots(){const i=`${this.buildDir}/client/robots.txt`;if(!s.existsSync(i))return void console.warn(`Failed to unlock robots.txt, file not exist: ${i}`);const t=s.readFileSync(i,{encoding:"utf-8"}).replace(/Disallow: \/$/m,"Allow: /");s.writeFileSync(i,t,{encoding:"utf-8"}),console.info(e.blue("\nrobots.txt unlocked."))}eject(){const i=`${this.buildDir}/server/start.js`;s.writeFileSync(i,"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\n\nconst VERSION = process.env.VERSION || \"1.0.0\";\nconst PORT = process.env.PORT || 3000;\nconst IS_HOST = process.env.IS_HOST || \"0\";\nconst ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\n\nawait runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\n",{encoding:"utf-8"})}createServerless(){const i=`${this.buildDir}/server/serverless.js`;s.writeFileSync(i,"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\n\nexport default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\n",{encoding:"utf-8"})}async spawnBuild(s,t,e={}){const{mode:o,isNoWarnings:n}=this.params,{focusOnly:l=this.params.focusOnly,shouldWait:a=!1,env:c={}}=e,u=o?`--mode ${o}`:"",d=this.promisifyProcess(i.spawn(`vite build ${t} ${u} --emptyOutDir`,{signal:this.abortController.signal,stdio:[process.stdin,"pipe","pipe"],shell:!0,env:{...process.env,...c,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:r(l).isOnlyClient()?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}),n);this.runningBuild.push({name:s,buildProcess:d}),a&&await this.waitLastBuild()}async waitLastBuild(){const i=this.runningBuild.at(-1);if(!i)return;const s=await i.buildProcess.promise;c(s,!0)}runPreviewMode(){this.hasPreviewModeExitListener||(process.on("exit",(()=>{this.abortController.abort()})),this.hasPreviewModeExitListener=!0);const{onFinish:i}=this.params;let s=this.runningBuild.length;const t=e=>{Buffer.from(e).toString().includes("built in")&&(s-=1,s||(this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.removeListener("data",t)})),l(this.isProd,this.viteConfig),i?.()))};this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.on("data",t)}))}async build(){await this.makeConfig(),await n(),this.clearBuildFolder();const{clientOptions:i,serverOptions:s,onFinish:t,isWatch:e,focusOnly:o,isEject:a,isServerless:c,isUnlockRobots:u}=this.params,{outDir:h}=this.viteConfig.build,p=r(o);this.abortController=new AbortController,this.runningBuild=[],p.isClient()&&await this.spawnBuild("client",`${i} --outDir ${h}/client`,{shouldWait:!e}),p.isServer()&&(await this.spawnBuild("server",`${s} --outDir ${h}/server --ssr ${this.pluginConfig.serverFile}`,{shouldWait:!e}),e||(await this.buildManifest(),a&&this.eject(),c&&this.createServerless()));const{entrypoint:f}=this.pluginConfig;if(f?.length&&p.isEntrypoint())for(const{name:i,type:s,serverFile:t,buildOptions:o=""}of f){const n=t&&"ssr"===s?`--ssr ${t}`:"";await this.spawnBuild(i,`${o} ${n} --outDir ${h}/${i}`,{shouldWait:!e,focusOnly:"ssr"===s?"server":"client",env:{SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME:i}})}e?this.runPreviewMode():(u&&this.unlockRobots(),l(this.isProd,this.viteConfig),d(this.buildDir),t?.())}}export{f as default};
2
2
  //# sourceMappingURL=build.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sources":["../../src/services/build.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { ResolvedConfig } from 'vite';\nimport { resolveConfig } from 'vite';\nimport viteResetCache from '@cli/helpers/vite-reset-cache';\nimport createFocusOnly from '@helpers/create-focus-only';\nimport { createDevMarker } from '@helpers/dev-marker';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport processStop from '@helpers/process-stop';\nimport { readMeta, removeMeta } from '@helpers/ssr-meta';\nimport ServerConfig from '@services/server-config';\nimport SsrManifest from '@services/ssr-manifest';\n\nexport interface IBuildParams {\n mode: string;\n onFinish?: () => void;\n clientOptions?: string;\n serverOptions?: string;\n focusOnly?: 'all' | 'app' | 'client' | 'server' | 'entrypoint';\n isWatch?: boolean;\n isUnlockRobots?: boolean;\n isEject?: boolean;\n isServerless?: boolean;\n isNoWarnings?: boolean;\n}\n\ninterface IBuildProcess {\n promise: Promise<number | null | string>;\n command: childProcess.ChildProcess;\n}\n\ninterface ISpawnBuildParams {\n shouldWait?: boolean;\n focusOnly?: IBuildParams['focusOnly'];\n env?: Record<string, string>;\n}\n\nexport interface IBuildEntrypoint {\n // entrypoint name\n name: string;\n type: 'spa' | 'ssr';\n // custom index file, default: indexFile from plugin config\n indexFile?: string;\n // custom entry file for replace in indexFile, default: undefined (do nothing)\n clientFile?: string;\n // custom server file, indexFile and clientFile will be ignored\n serverFile?: string;\n // additional options for vite build command\n options?: string;\n}\n\n/**\n * Build service\n */\nclass Build {\n /**\n * Is production build\n */\n protected isProd: boolean;\n\n /**\n * Node environment\n */\n protected nodeEnv: string;\n\n /**\n * Build folder\n */\n protected buildDir: string;\n\n /**\n * Vite config\n */\n protected viteConfig: ResolvedConfig;\n\n /**\n * Plugin config\n */\n protected pluginConfig: IPluginConfig;\n\n /**\n * Build params\n */\n protected params: IBuildParams = {\n mode: '',\n clientOptions: '',\n serverOptions: '',\n focusOnly: 'app',\n isWatch: false,\n isUnlockRobots: false,\n isEject: false,\n isServerless: false,\n isNoWarnings: false,\n };\n\n /**\n * Abort controller for builds\n */\n protected abortController: AbortController | null = null;\n\n /**\n * Running builds\n */\n protected runningBuild: { name: string; buildProcess: IBuildProcess }[] = [];\n\n /**\n * Listener for preview has attached\n */\n protected hasPreviewModeExitListener = false;\n\n /**\n * @constructor\n */\n public constructor(params: IBuildParams) {\n this.params = { ...this.params, ...params };\n }\n\n /**\n * Make config\n */\n protected async makeConfig(): Promise<void> {\n const { mode } = this.params;\n\n this.viteConfig = await resolveConfig({}, 'build', mode, 'production');\n this.pluginConfig = getPluginConfig(this.viteConfig);\n this.buildDir = path.resolve(this.viteConfig.root, this.viteConfig.build.outDir);\n this.nodeEnv = process.env.NODE_ENV || 'production';\n this.isProd = this.nodeEnv === 'production';\n }\n\n /**\n * Clear build folder\n */\n public clearBuildFolder(): void {\n // clear build folder\n if (fs.existsSync(this.buildDir)) {\n fs.rmSync(this.buildDir, { recursive: true });\n }\n }\n\n /**\n * Return is prod indicator value\n */\n public getIsProd(): boolean {\n return this.isProd;\n }\n\n /**\n * Return node env value\n */\n public getNodeEnv(): string {\n return this.nodeEnv;\n }\n\n /**\n * Return build names\n */\n public getRunningBuildNames(): string[] {\n return this.runningBuild.map(({ name }) => name);\n }\n\n /**\n * Promisify spawn process\n */\n protected promisifyProcess(\n command: childProcess.ChildProcess,\n isRejectWarnings = false,\n ): IBuildProcess {\n const promise = new Promise<number | null | string>((resolve, reject): void => {\n command.on('exit', (code) => {\n resolve(code);\n });\n\n command.on('close', (code: number): void => {\n resolve(code);\n });\n\n command.on('error', (message: string): void => {\n reject(message);\n });\n\n if (isRejectWarnings) {\n command.stderr?.on('data', (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('warning') || msg.includes('WARNING')) {\n resolve(1);\n }\n });\n }\n });\n\n command.stdout?.pipe(process.stdout);\n command.stderr?.pipe(process.stderr);\n\n return { promise, command };\n }\n\n /**\n * Build assets manifest file\n */\n protected async buildManifest(): Promise<void> {\n console.info(chalk.blue(`Building routes manifest file: ${this.pluginConfig.routesParsing}`));\n\n const isNodeParsing = this.pluginConfig.routesParsing === 'node';\n const serverConfig = ServerConfig.init(\n { isProd: this.isProd, mode: this.params.mode },\n { root: this.viteConfig.root, clientFile: this.pluginConfig.clientFile },\n );\n\n await SsrManifest.get(serverConfig, {\n buildDir: this.viteConfig.build.outDir,\n viteAliases: this.viteConfig.resolve.alias,\n }).buildRoutesManifest(isNodeParsing);\n\n if (isNodeParsing) {\n this.cleanupClientRoutes();\n }\n }\n\n /**\n * Remove pathId from client route files\n */\n private cleanupClientRoutes(): void {\n const { routeFiles } = readMeta(this.buildDir);\n const files = new Set(Object.values(routeFiles ?? []));\n\n if (!files.size) {\n return;\n }\n\n files.forEach((file) => {\n const filepath = `${this.buildDir}/client/${file}`;\n\n try {\n const result = fs\n .readFileSync(filepath, { encoding: 'utf-8' })\n .replace(/(lazy:.*?\\((.*?)\\)),\\s?\".*?\"\\)/g, '$1)');\n\n fs.writeFileSync(filepath, result);\n } catch (e) {\n console.log(`Failed cleanup client route ${filepath}:`, e);\n }\n });\n }\n\n /**\n * Change general directive Disallow to Allow in robots.txt.\n */\n protected unlockRobots(): void {\n const robotsFile = `${this.buildDir}/client/robots.txt`;\n\n if (!fs.existsSync(robotsFile)) {\n console.warn(`Failed to unlock robots.txt, file not exist: ${robotsFile}`);\n\n return;\n }\n\n const data = fs\n .readFileSync(robotsFile, { encoding: 'utf-8' })\n .replace(/Disallow: \\/$/m, 'Allow: /');\n\n fs.writeFileSync(robotsFile, data, { encoding: 'utf-8' });\n\n console.info(chalk.blue('\\nrobots.txt unlocked.'));\n }\n\n /**\n * Eject cli to run app via node\n */\n protected eject(): void {\n const entrypoint = `${this.buildDir}/server/start.js`;\n const script =\n \"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\\n\\n\" +\n 'const VERSION = process.env.VERSION || \"1.0.0\";\\n' +\n 'const PORT = process.env.PORT || 3000;\\n' +\n 'const IS_HOST = process.env.IS_HOST || \"0\";\\n' +\n 'const ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\\n\\n' +\n `await runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Create serverless entrypoint\n */\n protected createServerless(): void {\n const entrypoint = `${this.buildDir}/server/serverless.js`;\n const script =\n \"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\\n\\n\" +\n `export default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Build specified entrypoint\n */\n protected async spawnBuild(\n name: string,\n buildOptions: string,\n params: ISpawnBuildParams = {},\n ): Promise<void> {\n const { mode, isNoWarnings } = this.params;\n const { focusOnly = this.params.focusOnly, shouldWait = false, env = {} } = params;\n const modeOpt = mode ? `--mode ${mode}` : '';\n\n const buildProcess = this.promisifyProcess(\n childProcess.spawn(`vite build ${buildOptions} ${modeOpt} --emptyOutDir`, {\n signal: this.abortController!.signal,\n stdio: [process.stdin, 'pipe', 'pipe'],\n shell: true,\n env: {\n ...process.env,\n ...env,\n FORCE_COLOR: '2',\n SSR_BOOST_IS_SSR: createFocusOnly(focusOnly).isOnlyClient() ? '0' : '1',\n SSR_BOOST_ACTION: global.viteBoostAction,\n },\n }),\n isNoWarnings,\n );\n\n this.runningBuild.push({ name, buildProcess });\n\n if (!shouldWait) {\n return;\n }\n\n await this.waitLastBuild();\n }\n\n /**\n * Wait latest build and stop process in case error\n */\n protected async waitLastBuild(): Promise<void> {\n const latestProcess = this.runningBuild.at(-1);\n\n if (!latestProcess) {\n return;\n }\n\n const exitCode = await latestProcess.buildProcess.promise;\n\n processStop(exitCode, true);\n }\n\n /**\n * Run preview mode\n */\n protected runPreviewMode(): void {\n if (!this.hasPreviewModeExitListener) {\n process.on('exit', () => {\n this.abortController!.abort();\n });\n\n this.hasPreviewModeExitListener = true;\n }\n\n const { onFinish } = this.params;\n let buildCount = this.runningBuild.length;\n\n /**\n * Detect finished builds for process\n */\n const listener = (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('built in')) {\n buildCount -= 1;\n\n if (!buildCount) {\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.removeListener('data', listener);\n });\n createDevMarker(this.isProd, this.viteConfig);\n onFinish?.();\n }\n }\n };\n\n /**\n * Listen output for call onFinish\n */\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.on('data', listener);\n });\n }\n\n /**\n * Run app build\n */\n public async build(): Promise<void> {\n await this.makeConfig();\n // this is required step - build with different env may cause problems\n await viteResetCache();\n this.clearBuildFolder();\n\n const {\n clientOptions,\n serverOptions,\n onFinish,\n isWatch,\n focusOnly,\n isEject,\n isServerless,\n isUnlockRobots,\n } = this.params;\n const { outDir } = this.viteConfig.build;\n const focus = createFocusOnly(focusOnly);\n\n this.abortController = new AbortController();\n this.runningBuild = [];\n\n if (focus.isClient()) {\n /**\n * Build client\n */\n await this.spawnBuild('client', `${clientOptions} --outDir ${outDir}/client`, {\n shouldWait: !isWatch,\n });\n }\n\n /**\n * Build server\n */\n if (focus.isServer()) {\n await this.spawnBuild(\n 'server',\n `${serverOptions} --outDir ${outDir}/server --ssr ${this.pluginConfig.serverFile}`,\n {\n shouldWait: !isWatch,\n },\n );\n\n if (!isWatch) {\n await this.buildManifest();\n\n if (isEject) {\n this.eject();\n }\n\n if (isServerless) {\n this.createServerless();\n }\n }\n }\n\n /**\n * Build additional entrypoint\n */\n const { entrypoint } = this.pluginConfig;\n\n if (entrypoint?.length && focus.isEntrypoint()) {\n for (const { name, type, serverFile, options = '' } of entrypoint) {\n const cliOptions = serverFile && type === 'ssr' ? `--ssr ${serverFile}` : '';\n\n await this.spawnBuild(name, `${options} ${cliOptions} --outDir ${outDir}/${name}`, {\n shouldWait: !isWatch,\n focusOnly: type === 'ssr' ? 'server' : 'client',\n env: {\n SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME: name,\n },\n });\n }\n }\n\n /**\n * Preview mode\n */\n if (isWatch) {\n this.runPreviewMode();\n\n return;\n }\n\n if (isUnlockRobots) {\n this.unlockRobots();\n }\n\n createDevMarker(this.isProd, this.viteConfig);\n removeMeta(this.buildDir);\n onFinish?.();\n }\n}\n\nexport default Build;\n"],"names":["Build","isProd","nodeEnv","buildDir","viteConfig","pluginConfig","params","mode","clientOptions","serverOptions","focusOnly","isWatch","isUnlockRobots","isEject","isServerless","isNoWarnings","abortController","runningBuild","hasPreviewModeExitListener","constructor","this","async","resolveConfig","getPluginConfig","path","resolve","root","build","outDir","process","env","NODE_ENV","clearBuildFolder","fs","existsSync","rmSync","recursive","getIsProd","getNodeEnv","getRunningBuildNames","map","name","promisifyProcess","command","isRejectWarnings","promise","Promise","reject","on","code","message","stderr","buff","msg","Buffer","from","toString","includes","stdout","pipe","console","info","chalk","blue","routesParsing","isNodeParsing","serverConfig","ServerConfig","init","clientFile","SsrManifest","get","viteAliases","alias","buildRoutesManifest","cleanupClientRoutes","routeFiles","readMeta","files","Set","Object","values","size","forEach","file","filepath","result","readFileSync","encoding","replace","writeFileSync","e","log","unlockRobots","robotsFile","warn","data","eject","entrypoint","createServerless","buildOptions","shouldWait","modeOpt","buildProcess","childProcess","spawn","signal","stdio","stdin","shell","FORCE_COLOR","SSR_BOOST_IS_SSR","createFocusOnly","isOnlyClient","SSR_BOOST_ACTION","global","viteBoostAction","push","waitLastBuild","latestProcess","at","exitCode","processStop","runPreviewMode","abort","onFinish","buildCount","length","listener","removeListener","createDevMarker","makeConfig","viteResetCache","focus","AbortController","isClient","spawnBuild","isServer","serverFile","buildManifest","isEntrypoint","type","options","cliOptions","SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME","removeMeta"],"mappings":"kgBAyDA,MAAMA,EAIMC,OAKAC,QAKAC,SAKAC,WAKAC,aAKAC,OAAuB,CAC/BC,KAAM,GACNC,cAAe,GACfC,cAAe,GACfC,UAAW,MACXC,SAAS,EACTC,gBAAgB,EAChBC,SAAS,EACTC,cAAc,EACdC,cAAc,GAMNC,gBAA0C,KAK1CC,aAAgE,GAKhEC,4BAA6B,EAKvCC,YAAmBb,GACjBc,KAAKd,OAAS,IAAKc,KAAKd,UAAWA,EACpC,CAKSe,mBACR,MAAMd,KAAEA,GAASa,KAAKd,OAEtBc,KAAKhB,iBAAmBkB,EAAc,CAAE,EAAE,QAASf,EAAM,cACzDa,KAAKf,aAAekB,EAAgBH,KAAKhB,YACzCgB,KAAKjB,SAAWqB,EAAKC,QAAQL,KAAKhB,WAAWsB,KAAMN,KAAKhB,WAAWuB,MAAMC,QACzER,KAAKlB,QAAU2B,QAAQC,IAAIC,UAAY,aACvCX,KAAKnB,OAA0B,eAAjBmB,KAAKlB,OACpB,CAKM8B,mBAEDC,EAAGC,WAAWd,KAAKjB,WACrB8B,EAAGE,OAAOf,KAAKjB,SAAU,CAAEiC,WAAW,GAEzC,CAKMC,YACL,OAAOjB,KAAKnB,MACb,CAKMqC,aACL,OAAOlB,KAAKlB,OACb,CAKMqC,uBACL,OAAOnB,KAAKH,aAAauB,KAAI,EAAGC,UAAWA,GAC5C,CAKSC,iBACRC,EACAC,GAAmB,GAEnB,MAAMC,EAAU,IAAIC,SAAgC,CAACrB,EAASsB,KAC5DJ,EAAQK,GAAG,QAASC,IAClBxB,EAAQwB,EAAK,IAGfN,EAAQK,GAAG,SAAUC,IACnBxB,EAAQwB,EAAK,IAGfN,EAAQK,GAAG,SAAUE,IACnBH,EAAOG,EAAQ,IAGbN,GACFD,EAAQQ,QAAQH,GAAG,QAASI,IAC1B,MAAMC,EAAMC,OAAOC,KAAKH,GAAMI,YAE1BH,EAAII,SAAS,YAAcJ,EAAII,SAAS,aAC1ChC,EAAQ,EACT,GAEJ,IAMH,OAHAkB,EAAQe,QAAQC,KAAK9B,QAAQ6B,QAC7Bf,EAAQQ,QAAQQ,KAAK9B,QAAQsB,QAEtB,CAAEN,UAASF,UACnB,CAKStB,sBACRuC,QAAQC,KAAKC,EAAMC,KAAK,kCAAkC3C,KAAKf,aAAa2D,kBAE5E,MAAMC,EAAoD,SAApC7C,KAAKf,aAAa2D,cAClCE,EAAeC,EAAaC,KAChC,CAAEnE,OAAQmB,KAAKnB,OAAQM,KAAMa,KAAKd,OAAOC,MACzC,CAAEmB,KAAMN,KAAKhB,WAAWsB,KAAM2C,WAAYjD,KAAKf,aAAagE,mBAGxDC,EAAYC,IAAIL,EAAc,CAClC/D,SAAUiB,KAAKhB,WAAWuB,MAAMC,OAChC4C,YAAapD,KAAKhB,WAAWqB,QAAQgD,QACpCC,oBAAoBT,GAEnBA,GACF7C,KAAKuD,qBAER,CAKOA,sBACN,MAAMC,WAAEA,GAAeC,EAASzD,KAAKjB,UAC/B2E,EAAQ,IAAIC,IAAIC,OAAOC,OAAOL,GAAc,KAE7CE,EAAMI,MAIXJ,EAAMK,SAASC,IACb,MAAMC,EAAW,GAAGjE,KAAKjB,mBAAmBiF,IAE5C,IACE,MAAME,EAASrD,EACZsD,aAAaF,EAAU,CAAEG,SAAU,UACnCC,QAAQ,kCAAmC,OAE9CxD,EAAGyD,cAAcL,EAAUC,EAC5B,CAAC,MAAOK,GACP/B,QAAQgC,IAAI,+BAA+BP,KAAaM,EACzD,IAEJ,CAKSE,eACR,MAAMC,EAAa,GAAG1E,KAAKjB,6BAE3B,IAAK8B,EAAGC,WAAW4D,GAGjB,YAFAlC,QAAQmC,KAAK,gDAAgDD,KAK/D,MAAME,EAAO/D,EACVsD,aAAaO,EAAY,CAAEN,SAAU,UACrCC,QAAQ,iBAAkB,YAE7BxD,EAAGyD,cAAcI,EAAYE,EAAM,CAAER,SAAU,UAE/C5B,QAAQC,KAAKC,EAAMC,KAAK,0BACzB,CAKSkC,QACR,MAAMC,EAAa,GAAG9E,KAAKjB,2BAe3B8B,EAAGyD,cAAcQ,EAbf,2bAamC,CACnCV,SAAU,SAEb,CAKSW,mBACR,MAAMD,EAAa,GAAG9E,KAAKjB,gCAK3B8B,EAAGyD,cAAcQ,EAHf,oKAGmC,CACnCV,SAAU,SAEb,CAKSnE,iBACRoB,EACA2D,EACA9F,EAA4B,CAAA,GAE5B,MAAMC,KAAEA,EAAIQ,aAAEA,GAAiBK,KAAKd,QAC9BI,UAAEA,EAAYU,KAAKd,OAAOI,UAAS2F,WAAEA,GAAa,EAAKvE,IAAEA,EAAM,IAAOxB,EACtEgG,EAAU/F,EAAO,UAAUA,IAAS,GAEpCgG,EAAenF,KAAKsB,iBACxB8D,EAAaC,MAAM,cAAcL,KAAgBE,kBAAyB,CACxEI,OAAQtF,KAAKJ,gBAAiB0F,OAC9BC,MAAO,CAAC9E,QAAQ+E,MAAO,OAAQ,QAC/BC,OAAO,EACP/E,IAAK,IACAD,QAAQC,OACRA,EACHgF,YAAa,IACbC,iBAAkBC,EAAgBtG,GAAWuG,eAAiB,IAAM,IACpEC,iBAAkBC,OAAOC,mBAG7BrG,GAGFK,KAAKH,aAAaoG,KAAK,CAAE5E,OAAM8D,iBAE1BF,SAICjF,KAAKkG,eACZ,CAKSjG,sBACR,MAAMkG,EAAgBnG,KAAKH,aAAauG,IAAI,GAE5C,IAAKD,EACH,OAGF,MAAME,QAAiBF,EAAchB,aAAa1D,QAElD6E,EAAYD,GAAU,EACvB,CAKSE,iBACHvG,KAAKF,6BACRW,QAAQmB,GAAG,QAAQ,KACjB5B,KAAKJ,gBAAiB4G,OAAO,IAG/BxG,KAAKF,4BAA6B,GAGpC,MAAM2G,SAAEA,GAAazG,KAAKd,OAC1B,IAAIwH,EAAa1G,KAAKH,aAAa8G,OAKnC,MAAMC,EAAY5E,IACJE,OAAOC,KAAKH,GAAMI,WAEtBC,SAAS,cACfqE,GAAc,EAETA,IACH1G,KAAKH,aAAakE,SAAQ,EAAGoB,mBAC3BA,EAAa5D,QAAQe,QAAQuE,eAAe,OAAQD,EAAS,IAE/DE,EAAgB9G,KAAKnB,OAAQmB,KAAKhB,YAClCyH,OAEH,EAMHzG,KAAKH,aAAakE,SAAQ,EAAGoB,mBAC3BA,EAAa5D,QAAQe,QAAQV,GAAG,OAAQgF,EAAS,GAEpD,CAKM3G,oBACCD,KAAK+G,mBAELC,IACNhH,KAAKY,mBAEL,MAAMxB,cACJA,EAAaC,cACbA,EAAaoH,SACbA,EAAQlH,QACRA,EAAOD,UACPA,EAASG,QACTA,EAAOC,aACPA,EAAYF,eACZA,GACEQ,KAAKd,QACHsB,OAAEA,GAAWR,KAAKhB,WAAWuB,MAC7B0G,EAAQrB,EAAgBtG,GAE9BU,KAAKJ,gBAAkB,IAAIsH,gBAC3BlH,KAAKH,aAAe,GAEhBoH,EAAME,kBAIFnH,KAAKoH,WAAW,SAAU,GAAGhI,cAA0BoB,WAAiB,CAC5EyE,YAAa1F,IAOb0H,EAAMI,mBACFrH,KAAKoH,WACT,SACA,GAAG/H,cAA0BmB,kBAAuBR,KAAKf,aAAaqI,aACtE,CACErC,YAAa1F,IAIZA,UACGS,KAAKuH,gBAEP9H,GACFO,KAAK6E,QAGHnF,GACFM,KAAK+E,qBAQX,MAAMD,WAAEA,GAAe9E,KAAKf,aAE5B,GAAI6F,GAAY6B,QAAUM,EAAMO,eAC9B,IAAK,MAAMnG,KAAEA,EAAIoG,KAAEA,EAAIH,WAAEA,EAAUI,QAAEA,EAAU,MAAQ5C,EAAY,CACjE,MAAM6C,EAAaL,GAAuB,QAATG,EAAiB,SAASH,IAAe,SAEpEtH,KAAKoH,WAAW/F,EAAM,GAAGqG,KAAWC,cAAuBnH,KAAUa,IAAQ,CACjF4D,YAAa1F,EACbD,UAAoB,QAATmI,EAAiB,SAAW,SACvC/G,IAAK,CACHkH,uCAAwCvG,IAG7C,CAMC9B,EACFS,KAAKuG,kBAKH/G,GACFQ,KAAKyE,eAGPqC,EAAgB9G,KAAKnB,OAAQmB,KAAKhB,YAClC6I,EAAW7H,KAAKjB,UAChB0H,MACD"}
1
+ {"version":3,"file":"build.js","sources":["../../src/services/build.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { ResolvedConfig } from 'vite';\nimport { resolveConfig } from 'vite';\nimport viteResetCache from '@cli/helpers/vite-reset-cache';\nimport createFocusOnly from '@helpers/create-focus-only';\nimport { createDevMarker } from '@helpers/dev-marker';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport processStop from '@helpers/process-stop';\nimport { readMeta, removeMeta } from '@helpers/ssr-meta';\nimport ServerConfig from '@services/server-config';\nimport SsrManifest from '@services/ssr-manifest';\n\nexport interface IBuildParams {\n mode: string;\n onFinish?: () => void;\n clientOptions?: string;\n serverOptions?: string;\n focusOnly?: 'all' | 'app' | 'client' | 'server' | 'entrypoint';\n isWatch?: boolean;\n isUnlockRobots?: boolean;\n isEject?: boolean;\n isServerless?: boolean;\n isNoWarnings?: boolean;\n}\n\ninterface IBuildProcess {\n promise: Promise<number | null | string>;\n command: childProcess.ChildProcess;\n}\n\ninterface ISpawnBuildParams {\n shouldWait?: boolean;\n focusOnly?: IBuildParams['focusOnly'];\n env?: Record<string, string>;\n}\n\nexport interface IBuildEntrypoint {\n // entrypoint name\n name: string;\n type: 'spa' | 'ssr';\n // custom index file, default: indexFile from plugin config\n indexFile?: string;\n // custom entry file for replace in indexFile, default: undefined (do nothing)\n clientFile?: string;\n // custom server file, indexFile and clientFile will be ignored\n serverFile?: string;\n // additional options for vite build command\n buildOptions?: string;\n}\n\n/**\n * Build service\n */\nclass Build {\n /**\n * Is production build\n */\n protected isProd: boolean;\n\n /**\n * Node environment\n */\n protected nodeEnv: string;\n\n /**\n * Build folder\n */\n protected buildDir: string;\n\n /**\n * Vite config\n */\n protected viteConfig: ResolvedConfig;\n\n /**\n * Plugin config\n */\n protected pluginConfig: IPluginConfig;\n\n /**\n * Build params\n */\n protected params: IBuildParams = {\n mode: '',\n clientOptions: '',\n serverOptions: '',\n focusOnly: 'app',\n isWatch: false,\n isUnlockRobots: false,\n isEject: false,\n isServerless: false,\n isNoWarnings: false,\n };\n\n /**\n * Abort controller for builds\n */\n protected abortController: AbortController | null = null;\n\n /**\n * Running builds\n */\n protected runningBuild: { name: string; buildProcess: IBuildProcess }[] = [];\n\n /**\n * Listener for preview has attached\n */\n protected hasPreviewModeExitListener = false;\n\n /**\n * @constructor\n */\n public constructor(params: IBuildParams) {\n this.params = { ...this.params, ...params };\n }\n\n /**\n * Make config\n */\n protected async makeConfig(): Promise<void> {\n const { mode } = this.params;\n\n this.viteConfig = await resolveConfig({}, 'build', mode, 'production');\n this.pluginConfig = getPluginConfig(this.viteConfig);\n this.buildDir = path.resolve(this.viteConfig.root, this.viteConfig.build.outDir);\n this.nodeEnv = process.env.NODE_ENV || 'production';\n this.isProd = this.nodeEnv === 'production';\n }\n\n /**\n * Clear build folder\n */\n public clearBuildFolder(): void {\n // clear build folder\n if (fs.existsSync(this.buildDir)) {\n fs.rmSync(this.buildDir, { recursive: true });\n }\n }\n\n /**\n * Return is prod indicator value\n */\n public getIsProd(): boolean {\n return this.isProd;\n }\n\n /**\n * Return node env value\n */\n public getNodeEnv(): string {\n return this.nodeEnv;\n }\n\n /**\n * Return build names\n */\n public getRunningBuildNames(): string[] {\n return this.runningBuild.map(({ name }) => name);\n }\n\n /**\n * Promisify spawn process\n */\n protected promisifyProcess(\n command: childProcess.ChildProcess,\n isRejectWarnings = false,\n ): IBuildProcess {\n const promise = new Promise<number | null | string>((resolve, reject): void => {\n command.on('exit', (code) => {\n resolve(code);\n });\n\n command.on('close', (code: number): void => {\n resolve(code);\n });\n\n command.on('error', (message: string): void => {\n reject(message);\n });\n\n if (isRejectWarnings) {\n command.stderr?.on('data', (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('warning') || msg.includes('WARNING')) {\n resolve(1);\n }\n });\n }\n });\n\n command.stdout?.pipe(process.stdout);\n command.stderr?.pipe(process.stderr);\n\n return { promise, command };\n }\n\n /**\n * Build assets manifest file\n */\n protected async buildManifest(): Promise<void> {\n console.info(chalk.blue(`Building routes manifest file: ${this.pluginConfig.routesParsing}`));\n\n const isNodeParsing = this.pluginConfig.routesParsing === 'node';\n const serverConfig = ServerConfig.init(\n { isProd: this.isProd, mode: this.params.mode },\n { root: this.viteConfig.root, clientFile: this.pluginConfig.clientFile },\n );\n\n await SsrManifest.get(serverConfig, {\n buildDir: this.viteConfig.build.outDir,\n viteAliases: this.viteConfig.resolve.alias,\n }).buildRoutesManifest(isNodeParsing);\n\n if (isNodeParsing) {\n this.cleanupClientRoutes();\n }\n }\n\n /**\n * Remove pathId from client route files\n */\n private cleanupClientRoutes(): void {\n const { routeFiles } = readMeta(this.buildDir);\n const files = new Set(Object.values(routeFiles ?? []));\n\n if (!files.size) {\n return;\n }\n\n files.forEach((file) => {\n const filepath = `${this.buildDir}/client/${file}`;\n\n try {\n const result = fs\n .readFileSync(filepath, { encoding: 'utf-8' })\n .replace(/(lazy:.*?\\((.*?)\\)),\\s?\".*?\"\\)/g, '$1)');\n\n fs.writeFileSync(filepath, result);\n } catch (e) {\n console.log(`Failed cleanup client route ${filepath}:`, e);\n }\n });\n }\n\n /**\n * Change general directive Disallow to Allow in robots.txt.\n */\n protected unlockRobots(): void {\n const robotsFile = `${this.buildDir}/client/robots.txt`;\n\n if (!fs.existsSync(robotsFile)) {\n console.warn(`Failed to unlock robots.txt, file not exist: ${robotsFile}`);\n\n return;\n }\n\n const data = fs\n .readFileSync(robotsFile, { encoding: 'utf-8' })\n .replace(/Disallow: \\/$/m, 'Allow: /');\n\n fs.writeFileSync(robotsFile, data, { encoding: 'utf-8' });\n\n console.info(chalk.blue('\\nrobots.txt unlocked.'));\n }\n\n /**\n * Eject cli to run app via node\n */\n protected eject(): void {\n const entrypoint = `${this.buildDir}/server/start.js`;\n const script =\n \"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\\n\\n\" +\n 'const VERSION = process.env.VERSION || \"1.0.0\";\\n' +\n 'const PORT = process.env.PORT || 3000;\\n' +\n 'const IS_HOST = process.env.IS_HOST || \"0\";\\n' +\n 'const ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\\n\\n' +\n `await runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Create serverless entrypoint\n */\n protected createServerless(): void {\n const entrypoint = `${this.buildDir}/server/serverless.js`;\n const script =\n \"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\\n\\n\" +\n `export default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Build specified entrypoint\n */\n protected async spawnBuild(\n name: string,\n buildOptions: string,\n params: ISpawnBuildParams = {},\n ): Promise<void> {\n const { mode, isNoWarnings } = this.params;\n const { focusOnly = this.params.focusOnly, shouldWait = false, env = {} } = params;\n const modeOpt = mode ? `--mode ${mode}` : '';\n\n const buildProcess = this.promisifyProcess(\n childProcess.spawn(`vite build ${buildOptions} ${modeOpt} --emptyOutDir`, {\n signal: this.abortController!.signal,\n stdio: [process.stdin, 'pipe', 'pipe'],\n shell: true,\n env: {\n ...process.env,\n ...env,\n FORCE_COLOR: '2',\n SSR_BOOST_IS_SSR: createFocusOnly(focusOnly).isOnlyClient() ? '0' : '1',\n SSR_BOOST_ACTION: global.viteBoostAction,\n },\n }),\n isNoWarnings,\n );\n\n this.runningBuild.push({ name, buildProcess });\n\n if (!shouldWait) {\n return;\n }\n\n await this.waitLastBuild();\n }\n\n /**\n * Wait latest build and stop process in case error\n */\n protected async waitLastBuild(): Promise<void> {\n const latestProcess = this.runningBuild.at(-1);\n\n if (!latestProcess) {\n return;\n }\n\n const exitCode = await latestProcess.buildProcess.promise;\n\n processStop(exitCode, true);\n }\n\n /**\n * Run preview mode\n */\n protected runPreviewMode(): void {\n if (!this.hasPreviewModeExitListener) {\n process.on('exit', () => {\n this.abortController!.abort();\n });\n\n this.hasPreviewModeExitListener = true;\n }\n\n const { onFinish } = this.params;\n let buildCount = this.runningBuild.length;\n\n /**\n * Detect finished builds for process\n */\n const listener = (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('built in')) {\n buildCount -= 1;\n\n if (!buildCount) {\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.removeListener('data', listener);\n });\n createDevMarker(this.isProd, this.viteConfig);\n onFinish?.();\n }\n }\n };\n\n /**\n * Listen output for call onFinish\n */\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.on('data', listener);\n });\n }\n\n /**\n * Run app build\n */\n public async build(): Promise<void> {\n await this.makeConfig();\n // this is required step - build with different env may cause problems\n await viteResetCache();\n this.clearBuildFolder();\n\n const {\n clientOptions,\n serverOptions,\n onFinish,\n isWatch,\n focusOnly,\n isEject,\n isServerless,\n isUnlockRobots,\n } = this.params;\n const { outDir } = this.viteConfig.build;\n const focus = createFocusOnly(focusOnly);\n\n this.abortController = new AbortController();\n this.runningBuild = [];\n\n if (focus.isClient()) {\n /**\n * Build client\n */\n await this.spawnBuild('client', `${clientOptions} --outDir ${outDir}/client`, {\n shouldWait: !isWatch,\n });\n }\n\n /**\n * Build server\n */\n if (focus.isServer()) {\n await this.spawnBuild(\n 'server',\n `${serverOptions} --outDir ${outDir}/server --ssr ${this.pluginConfig.serverFile}`,\n {\n shouldWait: !isWatch,\n },\n );\n\n if (!isWatch) {\n await this.buildManifest();\n\n if (isEject) {\n this.eject();\n }\n\n if (isServerless) {\n this.createServerless();\n }\n }\n }\n\n /**\n * Build additional entrypoint\n */\n const { entrypoint } = this.pluginConfig;\n\n if (entrypoint?.length && focus.isEntrypoint()) {\n for (const { name, type, serverFile, buildOptions = '' } of entrypoint) {\n const cliOptions = serverFile && type === 'ssr' ? `--ssr ${serverFile}` : '';\n\n await this.spawnBuild(name, `${buildOptions} ${cliOptions} --outDir ${outDir}/${name}`, {\n shouldWait: !isWatch,\n focusOnly: type === 'ssr' ? 'server' : 'client',\n env: {\n SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME: name,\n },\n });\n }\n }\n\n /**\n * Preview mode\n */\n if (isWatch) {\n this.runPreviewMode();\n\n return;\n }\n\n if (isUnlockRobots) {\n this.unlockRobots();\n }\n\n createDevMarker(this.isProd, this.viteConfig);\n removeMeta(this.buildDir);\n onFinish?.();\n }\n}\n\nexport default Build;\n"],"names":["Build","isProd","nodeEnv","buildDir","viteConfig","pluginConfig","params","mode","clientOptions","serverOptions","focusOnly","isWatch","isUnlockRobots","isEject","isServerless","isNoWarnings","abortController","runningBuild","hasPreviewModeExitListener","constructor","this","async","resolveConfig","getPluginConfig","path","resolve","root","build","outDir","process","env","NODE_ENV","clearBuildFolder","fs","existsSync","rmSync","recursive","getIsProd","getNodeEnv","getRunningBuildNames","map","name","promisifyProcess","command","isRejectWarnings","promise","Promise","reject","on","code","message","stderr","buff","msg","Buffer","from","toString","includes","stdout","pipe","console","info","chalk","blue","routesParsing","isNodeParsing","serverConfig","ServerConfig","init","clientFile","SsrManifest","get","viteAliases","alias","buildRoutesManifest","cleanupClientRoutes","routeFiles","readMeta","files","Set","Object","values","size","forEach","file","filepath","result","readFileSync","encoding","replace","writeFileSync","e","log","unlockRobots","robotsFile","warn","data","eject","entrypoint","createServerless","buildOptions","shouldWait","modeOpt","buildProcess","childProcess","spawn","signal","stdio","stdin","shell","FORCE_COLOR","SSR_BOOST_IS_SSR","createFocusOnly","isOnlyClient","SSR_BOOST_ACTION","global","viteBoostAction","push","waitLastBuild","latestProcess","at","exitCode","processStop","runPreviewMode","abort","onFinish","buildCount","length","listener","removeListener","createDevMarker","makeConfig","viteResetCache","focus","AbortController","isClient","spawnBuild","isServer","serverFile","buildManifest","isEntrypoint","type","cliOptions","SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME","removeMeta"],"mappings":"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"}