@lomray/vite-ssr-boost 1.0.0-beta.26 → 1.0.0-beta.28

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/README.md CHANGED
@@ -98,6 +98,10 @@ import App from './App';
98
98
  import routes from './routes';
99
99
 
100
100
  export default entryServer(App, routes, {
101
+ /**
102
+ * Request timeout (If your backend is slow, increase this value)
103
+ */
104
+ abortDelay: 15000, // default: 15000 (ms)
101
105
  /**
102
106
  * Server configuration (optional)
103
107
  */
@@ -168,23 +172,25 @@ import type { FCRoute } from '@lomray/vite-ssr-boost/interfaces/fc-route';
168
172
  */
169
173
  SsrBoost({
170
174
  /**
171
- * With this option you can export route components like FCRoute or FCCRoute
172
- * @example
173
- * const Page: FCRoute = () => <div>Hi</div>;
174
- *
175
- * Page.ErrorBoudary = () => <div>Error</div>;
176
- *
177
- * export default Page;
178
- *
179
- * Routes:
180
- * const routes = [{ path: '/', lazyNR: () => import('./pages/home') }]
175
+ * index.html file path
181
176
  */
182
- hasLazyRoutePlugin: true, // default: true
183
-
177
+ indexFile: 'index.html', // default: index.html
178
+ /**
179
+ * Server entrypoint file
180
+ */
181
+ serverFile: 'server.ts', // default: server.ts
182
+ /**
183
+ * Client entrypoint file
184
+ */
185
+ clientFile: 'client.ts', // default: client.ts
184
186
  /**
185
187
  * Add tsconfig aliases to vite config aliases
186
188
  */
187
189
  tsconfigAliases: true, // default: true
190
+ /**
191
+ * Production: add scripts to head (server-side)
192
+ */
193
+ preloadAssets: false, // default: false
188
194
  })
189
195
  ```
190
196
 
package/cli.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as o}from"fs";import e from"chalk";import{Command as t,Option as n}from"commander";import i from"./cli/build.js";import r from"./cli/keyboard-input.js";import s from"./cli/run-dev.js";import d from"./cli/run-docker-build.js";import a from"./cli/run-prod.js";import c from"./cli/vite-reset-cache.js";import l from"./constants/cli-actions.js";import p from"./constants/cli-context.js";import m from"./constants/cli-name.js";const{description:u,version:f}=JSON.parse(o(new URL("./package.json",import.meta.url),"utf8")),v=()=>{process.stdin.isTTY&&(process.stdin.setRawMode(!0),process.stdin.on("data",r).setEncoding("utf8").resume())},O=new t;O.name(m).description(u).version(f).hook("preAction",((o,e)=>{global.viteBoostAction=e.name()}));const w=new n("--host","Ability to access the local instance on other devices under the same network.").default(!1),k=new n("--only-client","Build/run only client side part.").default(!1),g=new n("--port [port]","Server port.").default(3e3),y=new n("--mode [mode]","Env mode.").env("VITE_ENV_MODE").default("production");O.command(l.dev).description("Run development server.").addOption(w).addOption(new n("--reset-cache","Clear vite cache before run.").default(!1)).addOption(new n("--mode [mode]","Env mode.").env("VITE_ENV_MODE").default("development")).action((async({host:o,resetCache:e,mode:t})=>{e&&await c();const n=async e=>{const{server:n,config:i}=await s({version:f,isHost:o,isPrintInfo:e,mode:t});p.server=n,p.config=i};return p.reboot=n,v(),n()})),O.command(l.build).description("Create production build.").addOption(k).addOption(y).addOption(new n("--client-options [client-options]",'Pass vite build options for client. Example: --client-options="--ssrManifest"').env("VITE_BUILD_CLIENT_OPTIONS").default("")).addOption(new n("--server-options [server-options]","Pass vite build options for server.").env("VITE_BUILD_SERVER_OPTIONS").default("")).addOption(new n("--unlock-robots","Change general directive Disallow to Allow in robots.txt").default(!1)).addOption(new n("--eject","Produces entrypoint file to run app without cli").default(!1)).action((async({onlyClient:o,clientOptions:e,serverOptions:t,mode:n,unlockRobots:r,eject:s})=>{await i({isOnlyClient:o,isUnlockRobots:r,isEject:s,clientOptions:e,serverOptions:t,mode:n})})),O.command(l.start).description("Run production server.").addOption(w).addOption(g).addOption(k).action((({host:o,port:e,onlyClient:t})=>{const n=async n=>{const{server:i,config:r}=await a({version:f,isHost:o,isPrintInfo:n,port:e,onlyClient:t});p.server=i,p.config=r};return p.reboot=n,v(),n()})),O.command(l.preview).description("Build and preview production.").addOption(k).addOption(w).addOption(g).addOption(y).action((async({host:o,port:t,onlyClient:n,mode:r})=>{global.viteBoostStartTime=performance.now();const s=async i=>{const{server:r,config:s}=await a({version:f,isHost:o,isPrintInfo:i,port:t,onlyClient:n});r.on("listening",(()=>{setTimeout((()=>{s.getLogger().info(e.yellow("\n Running preview mode... \n"))}),0)})),p.server=r,p.config=s};p.reboot=s,v();await i({mode:r,isWatch:!0,isOnlyClient:n,clientOptions:"-w",serverOptions:"-w",onFinish:()=>{s()}})})),O.command(l.buildDocker).description("Create docker image with production build.").requiredOption("--image-name <image-name>","Docker image name.").addOption(new n("--docker-options [docker-options]","Extra docker options which pass to docker build command.")).addOption(new n("--docker-file [docker-file]","Name of the Dockerfile (Default is PLUGIN_PATH/workflow/Dockerfile).")).addOption(k).addOption(y).action((async({imageName:o,dockerOptions:e,dockerFile:t,onlyClient:n,mode:i})=>{await d({imageName:o,dockerOptions:e,dockerFile:t,isOnlyClient:n,mode:i})})),O.parse();
2
+ import{readFileSync as o}from"fs";import e from"chalk";import{Command as n,Option as t}from"commander";import i from"./cli/build.js";import r from"./cli/keyboard-input.js";import s from"./cli/run-dev.js";import d from"./cli/run-docker-build.js";import a from"./cli/run-prod.js";import c from"./cli/vite-reset-cache.js";import l from"./constants/cli-actions.js";import p from"./constants/cli-context.js";import m from"./constants/cli-name.js";const{description:u,version:f}=JSON.parse(o(new URL("./package.json",import.meta.url),"utf8")),v=()=>{process.stdin.isTTY&&(process.stdin.setRawMode(!0),process.stdin.on("data",r).setEncoding("utf8").resume())},O=new n;O.name(m).description(u).version(f).hook("preAction",((o,e)=>{global.viteBoostAction=e.name()}));const w=new t("--host","Ability to access the local instance on other devices under the same network.").default(!1),k=new t("--only-client","Build/run only client side part.").default(!1),g=new t("--port [port]","Server port.").default(3e3),y=new t("--mode [mode]","Env mode.").env("VITE_ENV_MODE").default("production");O.command(l.dev).description("Run development server.").addOption(w).addOption(new t("--reset-cache","Clear vite cache before run.").default(!1)).addOption(new t("--mode [mode]","Env mode.").env("VITE_ENV_MODE").default("development")).action((async({host:o,resetCache:n,mode:t})=>{n&&await c();const i=async n=>{console.info(e.cyan("Starting the development server..."));const{server:i,config:r}=await s({version:f,isHost:o,isPrintInfo:n,mode:t});p.server=i,p.config=r};return p.reboot=i,v(),i()})),O.command(l.build).description("Create production build.").addOption(k).addOption(y).addOption(new t("--client-options [client-options]",'Pass vite build options for client. Example: --client-options="--ssrManifest"').env("VITE_BUILD_CLIENT_OPTIONS").default("")).addOption(new t("--server-options [server-options]","Pass vite build options for server.").env("VITE_BUILD_SERVER_OPTIONS").default("")).addOption(new t("--unlock-robots","Change general directive Disallow to Allow in robots.txt").default(!1)).addOption(new t("--eject","Produces entrypoint file to run app without cli").default(!1)).action((async({onlyClient:o,clientOptions:e,serverOptions:n,mode:t,unlockRobots:r,eject:s})=>{await i({isOnlyClient:o,isUnlockRobots:r,isEject:s,clientOptions:e,serverOptions:n,mode:t})})),O.command(l.start).description("Run production server.").addOption(w).addOption(g).addOption(k).action((({host:o,port:e,onlyClient:n})=>{const t=async t=>{const{server:i,config:r}=await a({version:f,isHost:o,isPrintInfo:t,port:e,onlyClient:n});p.server=i,p.config=r};return p.reboot=t,v(),t()})),O.command(l.preview).description("Build and preview production.").addOption(k).addOption(w).addOption(g).addOption(y).action((async({host:o,port:n,onlyClient:t,mode:r})=>{global.viteBoostStartTime=performance.now();const s=async i=>{const{server:r,config:s}=await a({version:f,isHost:o,isPrintInfo:i,port:n,onlyClient:t});r.on("listening",(()=>{setTimeout((()=>{s.getLogger().info(e.yellow("\n Running preview mode... \n"))}),0)})),p.server=r,p.config=s};p.reboot=s,v();await i({mode:r,isWatch:!0,isOnlyClient:t,clientOptions:"-w",serverOptions:"-w",onFinish:()=>{s()}})})),O.command(l.buildDocker).description("Create docker image with production build.").requiredOption("--image-name <image-name>","Docker image name.").addOption(new t("--docker-options [docker-options]","Extra docker options which pass to docker build command.")).addOption(new t("--docker-file [docker-file]","Name of the Dockerfile (Default is PLUGIN_PATH/workflow/Dockerfile).")).addOption(k).addOption(y).action((async({imageName:o,dockerOptions:e,dockerFile:n,onlyClient:t,mode:i})=>{await d({imageName:o,dockerOptions:e,dockerFile:n,isOnlyClient:t,mode:i})})),O.parse();
3
3
  //# sourceMappingURL=cli.js.map
package/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readFileSync } from 'fs';\nimport chalk from 'chalk';\nimport { Command, Option } from 'commander';\nimport runBuild from '@cli/build';\nimport onKeyPress from '@cli/keyboard-input';\nimport runDev from '@cli/run-dev';\nimport runDockerBuild from '@cli/run-docker-build';\nimport runProd from '@cli/run-prod';\nimport viteResetCache from '@cli/vite-reset-cache';\nimport CliActions from '@constants/cli-actions';\nimport cliContext from '@constants/cli-context';\nimport cliName from '@constants/cli-name';\n\n/**\n * Parse package meta\n */\nconst { description, version } = JSON.parse(\n readFileSync(new URL('./package.json', import.meta.url), 'utf8'),\n) as { name: string; description: string; version: string };\n\n/**\n * Enable shortcuts\n * listen keyboard command\n */\nconst enableShortcuts = (): void => {\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n process.stdin.on('data', onKeyPress).setEncoding('utf8').resume();\n }\n};\n\nconst program = new Command();\n\nprogram\n .name(cliName)\n .description(description)\n .version(version)\n .hook('preAction', (_, actionCommand) => {\n // pass cli action to plugin config\n global.viteBoostAction = actionCommand.name();\n });\n\n/**\n * Common options\n */\nconst hostOption = new Option(\n '--host',\n 'Ability to access the local instance on other devices under the same network.',\n).default(false);\nconst onlyClientOption = new Option('--only-client', 'Build/run only client side part.').default(\n false,\n);\nconst portOption = new Option('--port [port]', 'Server port.').default(3000);\nconst envModeOption = new Option('--mode [mode]', 'Env mode.')\n .env('VITE_ENV_MODE')\n .default('production');\n\n/**\n * Cli commands\n */\n\nprogram\n .command(CliActions.dev)\n .description('Run development server.')\n .addOption(hostOption)\n .addOption(new Option('--reset-cache', 'Clear vite cache before run.').default(false))\n .addOption(new Option('--mode [mode]', 'Env mode.').env('VITE_ENV_MODE').default('development'))\n .action(async ({ host, resetCache, mode }) => {\n if (resetCache) {\n await viteResetCache();\n }\n\n const command = async (isPrintInfo?: boolean): Promise<void> => {\n const { server, config } = await runDev({ version, isHost: host, isPrintInfo, mode });\n\n cliContext.server = server;\n cliContext.config = config;\n };\n\n cliContext.reboot = command;\n\n enableShortcuts();\n\n return command();\n });\n\nprogram\n .command(CliActions.build)\n .description('Create production build.')\n .addOption(onlyClientOption)\n .addOption(envModeOption)\n .addOption(\n new Option(\n '--client-options [client-options]',\n 'Pass vite build options for client. Example: --client-options=\"--ssrManifest\"',\n )\n .env('VITE_BUILD_CLIENT_OPTIONS')\n .default(''),\n )\n .addOption(\n new Option('--server-options [server-options]', 'Pass vite build options for server.')\n .env('VITE_BUILD_SERVER_OPTIONS')\n .default(''),\n )\n .addOption(\n new Option(\n '--unlock-robots',\n 'Change general directive Disallow to Allow in robots.txt',\n ).default(false),\n )\n .addOption(\n new Option('--eject', 'Produces entrypoint file to run app without cli').default(false),\n )\n .action(async ({ onlyClient, clientOptions, serverOptions, mode, unlockRobots, eject }) => {\n await runBuild({\n isOnlyClient: onlyClient,\n isUnlockRobots: unlockRobots,\n isEject: eject,\n clientOptions,\n serverOptions,\n mode,\n });\n });\n\nprogram\n .command(CliActions.start)\n .description('Run production server.')\n .addOption(hostOption)\n .addOption(portOption)\n .addOption(onlyClientOption)\n .action(({ host, port, onlyClient }) => {\n const command = async (isPrintInfo?: boolean): Promise<void> => {\n const { server, config } = await runProd({\n version,\n isHost: host,\n isPrintInfo,\n port,\n onlyClient,\n });\n\n cliContext.server = server;\n cliContext.config = config;\n };\n\n cliContext.reboot = command;\n\n enableShortcuts();\n\n return command();\n });\n\nprogram\n .command(CliActions.preview)\n .description('Build and preview production.')\n .addOption(onlyClientOption)\n .addOption(hostOption)\n .addOption(portOption)\n .addOption(envModeOption)\n .action(async ({ host, port, onlyClient, mode }) => {\n global.viteBoostStartTime = performance.now();\n\n const command = async (isPrintInfo?: boolean): Promise<void> => {\n const { server, config } = await runProd({\n version,\n isHost: host,\n isPrintInfo,\n port,\n onlyClient,\n });\n\n server.on('listening', () => {\n setTimeout(() => {\n config.getLogger().info(chalk.yellow('\\n Running preview mode... \\n'));\n }, 0);\n });\n\n cliContext.server = server;\n cliContext.config = config;\n };\n\n cliContext.reboot = command;\n\n enableShortcuts();\n\n const buildOptions = '-w';\n\n await runBuild({\n mode,\n isWatch: true,\n isOnlyClient: onlyClient,\n clientOptions: buildOptions,\n serverOptions: buildOptions,\n onFinish: () => {\n void command();\n },\n });\n });\n\nprogram\n .command(CliActions.buildDocker)\n .description('Create docker image with production build.')\n .requiredOption('--image-name <image-name>', 'Docker image name.')\n .addOption(\n new Option(\n '--docker-options [docker-options]',\n 'Extra docker options which pass to docker build command.',\n ),\n )\n .addOption(\n new Option(\n '--docker-file [docker-file]',\n 'Name of the Dockerfile (Default is PLUGIN_PATH/workflow/Dockerfile).',\n ),\n )\n .addOption(onlyClientOption)\n .addOption(envModeOption)\n .action(async ({ imageName, dockerOptions, dockerFile, onlyClient, mode }) => {\n await runDockerBuild({\n imageName,\n dockerOptions,\n dockerFile,\n isOnlyClient: onlyClient,\n mode,\n });\n });\n\nprogram.parse();\n"],"names":["description","version","JSON","parse","readFileSync","URL","url","enableShortcuts","process","stdin","isTTY","setRawMode","on","onKeyPress","setEncoding","resume","program","Command","name","cliName","hook","_","actionCommand","global","viteBoostAction","hostOption","Option","default","onlyClientOption","portOption","envModeOption","env","command","CliActions","dev","addOption","action","async","host","resetCache","mode","viteResetCache","isPrintInfo","server","config","runDev","isHost","cliContext","reboot","build","onlyClient","clientOptions","serverOptions","unlockRobots","eject","runBuild","isOnlyClient","isUnlockRobots","isEject","start","port","runProd","preview","viteBoostStartTime","performance","now","setTimeout","getLogger","info","chalk","yellow","isWatch","onFinish","buildDocker","requiredOption","imageName","dockerOptions","dockerFile","runDockerBuild"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readFileSync } from 'fs';\nimport chalk from 'chalk';\nimport { Command, Option } from 'commander';\nimport runBuild from '@cli/build';\nimport onKeyPress from '@cli/keyboard-input';\nimport runDev from '@cli/run-dev';\nimport runDockerBuild from '@cli/run-docker-build';\nimport runProd from '@cli/run-prod';\nimport viteResetCache from '@cli/vite-reset-cache';\nimport CliActions from '@constants/cli-actions';\nimport cliContext from '@constants/cli-context';\nimport cliName from '@constants/cli-name';\n\n/**\n * Parse package meta\n */\nconst { description, version } = JSON.parse(\n readFileSync(new URL('./package.json', import.meta.url), 'utf8'),\n) as { name: string; description: string; version: string };\n\n/**\n * Enable shortcuts\n * listen keyboard command\n */\nconst enableShortcuts = (): void => {\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n process.stdin.on('data', onKeyPress).setEncoding('utf8').resume();\n }\n};\n\nconst program = new Command();\n\nprogram\n .name(cliName)\n .description(description)\n .version(version)\n .hook('preAction', (_, actionCommand) => {\n // pass cli action to plugin config\n global.viteBoostAction = actionCommand.name();\n });\n\n/**\n * Common options\n */\nconst hostOption = new Option(\n '--host',\n 'Ability to access the local instance on other devices under the same network.',\n).default(false);\nconst onlyClientOption = new Option('--only-client', 'Build/run only client side part.').default(\n false,\n);\nconst portOption = new Option('--port [port]', 'Server port.').default(3000);\nconst envModeOption = new Option('--mode [mode]', 'Env mode.')\n .env('VITE_ENV_MODE')\n .default('production');\n\n/**\n * Cli commands\n */\n\nprogram\n .command(CliActions.dev)\n .description('Run development server.')\n .addOption(hostOption)\n .addOption(new Option('--reset-cache', 'Clear vite cache before run.').default(false))\n .addOption(new Option('--mode [mode]', 'Env mode.').env('VITE_ENV_MODE').default('development'))\n .action(async ({ host, resetCache, mode }) => {\n if (resetCache) {\n await viteResetCache();\n }\n\n const command = async (isPrintInfo?: boolean): Promise<void> => {\n console.info(chalk.cyan('Starting the development server...'));\n\n const { server, config } = await runDev({ version, isHost: host, isPrintInfo, mode });\n\n cliContext.server = server;\n cliContext.config = config;\n };\n\n cliContext.reboot = command;\n\n enableShortcuts();\n\n return command();\n });\n\nprogram\n .command(CliActions.build)\n .description('Create production build.')\n .addOption(onlyClientOption)\n .addOption(envModeOption)\n .addOption(\n new Option(\n '--client-options [client-options]',\n 'Pass vite build options for client. Example: --client-options=\"--ssrManifest\"',\n )\n .env('VITE_BUILD_CLIENT_OPTIONS')\n .default(''),\n )\n .addOption(\n new Option('--server-options [server-options]', 'Pass vite build options for server.')\n .env('VITE_BUILD_SERVER_OPTIONS')\n .default(''),\n )\n .addOption(\n new Option(\n '--unlock-robots',\n 'Change general directive Disallow to Allow in robots.txt',\n ).default(false),\n )\n .addOption(\n new Option('--eject', 'Produces entrypoint file to run app without cli').default(false),\n )\n .action(async ({ onlyClient, clientOptions, serverOptions, mode, unlockRobots, eject }) => {\n await runBuild({\n isOnlyClient: onlyClient,\n isUnlockRobots: unlockRobots,\n isEject: eject,\n clientOptions,\n serverOptions,\n mode,\n });\n });\n\nprogram\n .command(CliActions.start)\n .description('Run production server.')\n .addOption(hostOption)\n .addOption(portOption)\n .addOption(onlyClientOption)\n .action(({ host, port, onlyClient }) => {\n const command = async (isPrintInfo?: boolean): Promise<void> => {\n const { server, config } = await runProd({\n version,\n isHost: host,\n isPrintInfo,\n port,\n onlyClient,\n });\n\n cliContext.server = server;\n cliContext.config = config;\n };\n\n cliContext.reboot = command;\n\n enableShortcuts();\n\n return command();\n });\n\nprogram\n .command(CliActions.preview)\n .description('Build and preview production.')\n .addOption(onlyClientOption)\n .addOption(hostOption)\n .addOption(portOption)\n .addOption(envModeOption)\n .action(async ({ host, port, onlyClient, mode }) => {\n global.viteBoostStartTime = performance.now();\n\n const command = async (isPrintInfo?: boolean): Promise<void> => {\n const { server, config } = await runProd({\n version,\n isHost: host,\n isPrintInfo,\n port,\n onlyClient,\n });\n\n server.on('listening', () => {\n setTimeout(() => {\n config.getLogger().info(chalk.yellow('\\n Running preview mode... \\n'));\n }, 0);\n });\n\n cliContext.server = server;\n cliContext.config = config;\n };\n\n cliContext.reboot = command;\n\n enableShortcuts();\n\n const buildOptions = '-w';\n\n await runBuild({\n mode,\n isWatch: true,\n isOnlyClient: onlyClient,\n clientOptions: buildOptions,\n serverOptions: buildOptions,\n onFinish: () => {\n void command();\n },\n });\n });\n\nprogram\n .command(CliActions.buildDocker)\n .description('Create docker image with production build.')\n .requiredOption('--image-name <image-name>', 'Docker image name.')\n .addOption(\n new Option(\n '--docker-options [docker-options]',\n 'Extra docker options which pass to docker build command.',\n ),\n )\n .addOption(\n new Option(\n '--docker-file [docker-file]',\n 'Name of the Dockerfile (Default is PLUGIN_PATH/workflow/Dockerfile).',\n ),\n )\n .addOption(onlyClientOption)\n .addOption(envModeOption)\n .action(async ({ imageName, dockerOptions, dockerFile, onlyClient, mode }) => {\n await runDockerBuild({\n imageName,\n dockerOptions,\n dockerFile,\n isOnlyClient: onlyClient,\n mode,\n });\n });\n\nprogram.parse();\n"],"names":["description","version","JSON","parse","readFileSync","URL","url","enableShortcuts","process","stdin","isTTY","setRawMode","on","onKeyPress","setEncoding","resume","program","Command","name","cliName","hook","_","actionCommand","global","viteBoostAction","hostOption","Option","default","onlyClientOption","portOption","envModeOption","env","command","CliActions","dev","addOption","action","async","host","resetCache","mode","viteResetCache","isPrintInfo","console","info","chalk","cyan","server","config","runDev","isHost","cliContext","reboot","build","onlyClient","clientOptions","serverOptions","unlockRobots","eject","runBuild","isOnlyClient","isUnlockRobots","isEject","start","port","runProd","preview","viteBoostStartTime","performance","now","setTimeout","getLogger","yellow","isWatch","onFinish","buildDocker","requiredOption","imageName","dockerOptions","dockerFile","runDockerBuild"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { FC } from 'react';
2
+ /**
3
+ * Scroll page to top on every pathname (url) change
4
+ * @constructor
5
+ */
6
+ declare const ScrollToTop: FC;
7
+ export { ScrollToTop as default };
@@ -0,0 +1,2 @@
1
+ import{useEffect as o}from"react";import{useLocation as r}from"react-router-dom";const t=()=>{const{pathname:t}=r();return o((()=>{window.scrollTo(0,0)}),[t]),null};export{t as default};
2
+ //# sourceMappingURL=scroll-to-top.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-to-top.js","sources":["../../src/components/scroll-to-top.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\n\n/**\n * Scroll page to top on every pathname (url) change\n * @constructor\n */\nconst ScrollToTop: FC = () => {\n const { pathname } = useLocation();\n\n useEffect(() => {\n window.scrollTo(0, 0);\n }, [pathname]);\n\n return null;\n};\n\nexport default ScrollToTop;\n"],"names":["ScrollToTop","pathname","useLocation","useEffect","window","scrollTo"],"mappings":"iFAQM,MAAAA,EAAkB,KACtB,MAAMC,SAAEA,GAAaC,IAMrB,OAJAC,GAAU,KACRC,OAAOC,SAAS,EAAG,EAAE,GACpB,CAACJ,IAEG,IAAI"}
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";import l from"../services/ssr-manifest.js";async function p({App:p,handler:u},f,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 u.query(w,{requestContext:h});const b=m(v,h.routerContext);if(!b)return;l.get(f.getParams().root).injectAssets(h);const{isStream:q=!0}=await(x?.({context:h}))??{};h.isStream=q,h.serverContext={response:null,isServer:!0};const T=o(u.dataRoutes,h.routerContext),A=v.write.bind(v),$=f.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:T,context:k,hydrate:!1}))),{onShellReady(){q&&d(h,{pipe:H,statusCode:b,onShellReady:S,getState:C})},onAllReady(){clearTimeout(B),q||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};
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:u},f,h,{onRouterReady:x,onShellReady:S,onResponse:R,onShellError:g,onError:y,getState:C,abortDelay:E=15e3}){const{req:j,res:v}=h,w=c(j);h.routerContext=await u.query(w,{requestContext:h});const b=m(v,h.routerContext);if(!b)return;l.get(f).injectAssets(h);const{isStream:q=!0}=await(x?.({context:h}))??{};h.isStream=q,h.serverContext={response:null,isServer:!0};const T=o(u.dataRoutes,h.routerContext),A=v.write.bind(v),$=f.getLogger();let B;v.write=(e,...r)=>{const t="string"==typeof e,o=t?e:Buffer.from(e).toString(),n=R?.({context:h,html:o});return n?A(t?`${n}${e}`:Buffer.concat([Buffer.from(n),e]),...r):A(e,...r)};const{serverContext:k,routerContext:D,appProps:H}=h,{pipe:L,abort:P}=t(r.createElement(a,{context:k},r.createElement(p,{server:{...H,req:j}},r.createElement(n,{router:T,context:D,hydrate:!1}))),{onShellReady(){q&&d(h,{pipe:L,statusCode:b,onShellReady:S,getState:C})},onAllReady(){clearTimeout(B),q||d(h,{pipe:L,statusCode:b,onShellReady:S,getState:C})},onShellError(e){const r=g?.({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,P()}),E),j.on("close",(()=>{h.didError=s.RenderCancel,P()}))}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';\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 hasEarlyHints?: 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, {\n requestContext: context,\n })) as StaticHandlerContext;\n\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","requestContext","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":"sfA0EAA,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,EAAc,CACzDI,eAAgBd,IAMlB,MAAMe,EAAaC,EAAeP,EAAKT,EAAQY,eAE/C,IAAKG,EACH,OAGFE,EAAYC,IAAInB,EAAOoB,YAAYC,MAAMC,aAAarB,GAEtD,MAAMsB,SAAEA,GAAW,SAAgBrB,IAAgB,CAAED,cAAe,GAEpEA,EAAQsB,SAAWA,EACnBtB,EAAQuB,cAAgB,CAAEC,SAAU,KAAMC,UAAU,GAEpD,MAAMC,EAASC,EAAmB7B,EAAQ8B,WAAY5B,EAAQY,eACxDiB,EAAQpB,EAAIoB,MAAMC,KAAKrB,GACvBsB,EAAShC,EAAOiC,YACtB,IAAIC,EAMJxB,EAAIoB,MAAQ,CAACK,KAA8BC,KACzC,MAAMC,EAA2B,iBAATF,EAClBG,EAAOD,EAAWF,EAAOI,OAAOC,KAAKL,GAAMM,WAC3CC,EAAiBtC,IAAa,CAAEH,UAASqC,SAE/C,OAAII,EACKZ,EACLO,EAAW,GAAGK,IAAiBP,IAASI,OAAOI,OAAO,CAACJ,OAAOC,KAAKE,GAAiBP,OACjFC,GAIAN,EAAMK,KAASC,EAAgB,EAGxC,MAAMZ,cAAEA,EAAaX,cAAEA,EAAa+B,SAAEA,GAAa3C,GAE7C4C,KAAEA,EAAIC,MAAEA,GAAUC,EACtBC,EAACC,cAAAC,EAAe,CAAAjD,QAASuB,GACvBwB,EAACC,cAAAnD,GAAIqD,OAAQ,IAAKP,EAAUnC,QAC1BuC,EAAAC,cAACG,EAAqB,CAAAzB,OAAQA,EAAQ1B,QAASY,EAAewC,SAAS,MAG3E,CACElD,eACOoB,GAIL+B,EAAcrD,EAAS,CACrB4C,OACA7B,aACAb,eACAI,YAEH,EACDgD,aACEC,aAAatB,GAETX,GAIJ+B,EAAcrD,EAAS,CACrB4C,OACA7B,aACAb,eACAI,YAEH,EACDF,aAAaoD,GACX,MAAMC,EACJrD,IAAe,CAAEJ,UAAS0D,MAAOF,KACjC,2CAA2CA,EAAEG,cAE/ClD,EAAImD,OAAO,KACXnD,EAAIoD,UAAU,eAAgB,aAC9BpD,EAAIqD,KAAKL,EACV,EACDpD,QAAQ0D,GACNR,aAAatB,GAEb,MAAMyB,EAAQM,EAAkBD,IAC1BE,KAAEA,EAAIN,QAAEA,GAAYD,GACpBQ,SAAEA,GAAalE,EAErBA,EAAQkE,SAAWA,GAAYD,EAE/B5D,IAAU,CAAEL,UAAS0D,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,KACtB5E,EAAQkE,SAAWI,EAAYE,cAC/B3B,GAAO,GACNtC,GAGHC,EAAIqE,GAAG,SAAS,KACd7E,EAAQkE,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 hasEarlyHints?: 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, {\n requestContext: context,\n })) as StaticHandlerContext;\n\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).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","requestContext","statusCode","handleResponse","SsrManifest","get","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":"sfA0EAA,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,EAAc,CACzDI,eAAgBd,IAMlB,MAAMe,EAAaC,EAAeP,EAAKT,EAAQY,eAE/C,IAAKG,EACH,OAGFE,EAAYC,IAAInB,GAAQoB,aAAanB,GAErC,MAAMoB,SAAEA,GAAW,SAAgBnB,IAAgB,CAAED,cAAe,GAEpEA,EAAQoB,SAAWA,EACnBpB,EAAQqB,cAAgB,CAAEC,SAAU,KAAMC,UAAU,GAEpD,MAAMC,EAASC,EAAmB3B,EAAQ4B,WAAY1B,EAAQY,eACxDe,EAAQlB,EAAIkB,MAAMC,KAAKnB,GACvBoB,EAAS9B,EAAO+B,YACtB,IAAIC,EAMJtB,EAAIkB,MAAQ,CAACK,KAA8BC,KACzC,MAAMC,EAA2B,iBAATF,EAClBG,EAAOD,EAAWF,EAAOI,OAAOC,KAAKL,GAAMM,WAC3CC,EAAiBpC,IAAa,CAAEH,UAASmC,SAE/C,OAAII,EACKZ,EACLO,EAAW,GAAGK,IAAiBP,IAASI,OAAOI,OAAO,CAACJ,OAAOC,KAAKE,GAAiBP,OACjFC,GAIAN,EAAMK,KAASC,EAAgB,EAGxC,MAAMZ,cAAEA,EAAaT,cAAEA,EAAa6B,SAAEA,GAAazC,GAE7C0C,KAAEA,EAAIC,MAAEA,GAAUC,EACtBC,EAACC,cAAAC,EAAe,CAAA/C,QAASqB,GACvBwB,EAACC,cAAAjD,GAAImD,OAAQ,IAAKP,EAAUjC,QAC1BqC,EAAAC,cAACG,EAAqB,CAAAzB,OAAQA,EAAQxB,QAASY,EAAesC,SAAS,MAG3E,CACEhD,eACOkB,GAIL+B,EAAcnD,EAAS,CACrB0C,OACA3B,aACAb,eACAI,YAEH,EACD8C,aACEC,aAAatB,GAETX,GAIJ+B,EAAcnD,EAAS,CACrB0C,OACA3B,aACAb,eACAI,YAEH,EACDF,aAAakD,GACX,MAAMC,EACJnD,IAAe,CAAEJ,UAASwD,MAAOF,KACjC,2CAA2CA,EAAEG,cAE/ChD,EAAIiD,OAAO,KACXjD,EAAIkD,UAAU,eAAgB,aAC9BlD,EAAImD,KAAKL,EACV,EACDlD,QAAQwD,GACNR,aAAatB,GAEb,MAAMyB,EAAQM,EAAkBD,IAC1BE,KAAEA,EAAIN,QAAEA,GAAYD,GACpBQ,SAAEA,GAAahE,EAErBA,EAAQgE,SAAWA,GAAYD,EAE/B1D,IAAU,CAAEL,UAASwD,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,KACtB1E,EAAQgE,SAAWI,EAAYE,cAC/B3B,GAAO,GACNpC,GAGHC,EAAImE,GAAG,SAAS,KACd3E,EAAQgE,SAAWI,EAAYG,aAC/B5B,GAAO,GAEX"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lomray/vite-ssr-boost",
3
- "version": "1.0.0-beta.26",
3
+ "version": "1.0.0-beta.28",
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,6 +4,7 @@ import { IPluginOptions as IMakeAliasesPluginOptions } from "./plugins/make-alia
4
4
  interface IPluginOptions {
5
5
  indexFile?: string;
6
6
  serverFile?: string;
7
+ clientFile?: string;
7
8
  preloadAssets?: boolean;
8
9
  tsconfigAliases?: boolean | IMakeAliasesPluginOptions;
9
10
  customShortcuts?: {
package/plugin.js CHANGED
@@ -1,2 +1,2 @@
1
- import e from"node:path";import i from"./constants/cli-actions.js";import s from"./constants/plugin-name.js";import o from"./plugins/make-aliases.js";import n from"./plugins/normalize-route.js";const t={indexFile:"index.html",serverFile:"server.ts",preloadAssets:!1,tsconfigAliases:!0};function r(r={}){const p=new URL(import.meta.url),a=global.viteBoostAction||process.env.SSR_BOOST_ACTION,l={...t,...r},m="1"===process.env.SSR_BOOST_IS_SSR||"dev"===a,u=a===i.build,c=[{name:s,enforce:"pre",pluginOptions:{...l,pluginPath:e.dirname(p.pathname),action:a,isDev:a===i.dev},config:(e,{ssrBuild:i})=>(e.define={...e.define??{},__IS_SSR__:m},e.build={...e.build??{}},i?{...e,...u?{appType:"custom"}:{},publicDir:!1}:(m&&u&&(e.build.manifest=!0),e))}],{tsconfigAliases:d}=l;return d&&c.push(o("boolean"==typeof d?void 0:d)),c.push(n({isSSR:m})),c}export{r as default};
1
+ import e from"node:path";import i from"./constants/cli-actions.js";import s from"./constants/plugin-name.js";import n from"./plugins/make-aliases.js";import o from"./plugins/normalize-route.js";const t={indexFile:"index.html",serverFile:"server.ts",clientFile:"client.ts",preloadAssets:!1,tsconfigAliases:!0};function r(r={}){const l=new URL(import.meta.url),p=global.viteBoostAction||process.env.SSR_BOOST_ACTION,a={...t,...r},m="1"===process.env.SSR_BOOST_IS_SSR||"dev"===p,c=p===i.build,u=[{name:s,enforce:"pre",pluginOptions:{...a,pluginPath:e.dirname(l.pathname),action:p,isDev:p===i.dev},config:(e,{ssrBuild:i})=>(e.define={...e.define??{},__IS_SSR__:m},e.build={...e.build??{}},i?{...e,...c?{appType:"custom"}:{},publicDir:!1}:(m&&c&&(e.build.manifest=!0),e))}],{tsconfigAliases:d}=a;return d&&u.push(n("boolean"==typeof d?void 0:d)),u.push(o({isSSR:m})),u}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 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: false,\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__","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,IAIjBa,EAQE,IACFD,KACCd,EAAU,CAAEkB,QAAS,UAAa,CAAA,EACtCC,WAAW,IAVPrB,GAASE,IACXc,EAAOZ,MAAOkB,UAAW,GAGpBN,OAYT7B,gBAAEA,GAAoBY,EAU5B,OARIZ,GACFkB,EAAQkB,KACNC,EAAiD,kBAApBrC,OAAgCsC,EAAYtC,IAI7EkB,EAAQkB,KAAKG,EAA0B,CAAE1B,WAElCK,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 clientFile?: string; // default: client.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 clientFile: 'client.ts',\n preloadAssets: false,\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","clientFile","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__","appType","publicDir","manifest","push","ViteMakeAliasesPlugin","undefined","ViteNormalizeRouterPlugin"],"mappings":"kMAuBA,MAAMA,EAAiC,CACrCC,UAAW,aACXC,WAAY,YACZC,WAAY,YACZC,eAAe,EACfC,iBAAiB,GAOnB,SAASC,EAAmBC,EAA0B,IACpD,MAAMC,EAAU,IAAIC,gBAAgBC,KAC9BC,EAAUC,OAAOC,iBAAmBC,QAAQC,IAAIC,iBAChDC,EAAgC,IAAKjB,KAAmBO,GACxDW,EAAyC,MAAjCJ,QAAQC,IAAII,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,IAIjBa,EAQE,IACFD,KACCd,EAAU,CAAEkB,QAAS,UAAa,CAAA,EACtCC,WAAW,IAVPrB,GAASE,IACXc,EAAOZ,MAAOkB,UAAW,GAGpBN,OAYT7B,gBAAEA,GAAoBY,EAU5B,OARIZ,GACFkB,EAAQkB,KACNC,EAAiD,kBAApBrC,OAAgCsC,EAAYtC,IAI7EkB,EAAQkB,KAAKG,EAA0B,CAAE1B,WAElCK,CACT"}
package/services/build.js CHANGED
@@ -1,2 +1,2 @@
1
- import i from"node:fs";import o from"node:path";import t from"chalk";import{resolveConfig as s}from"vite";import e from"../helpers/plugin-config.js";import n from"./ssr-manifest.js";class r{isProd;nodeEnv;buildDir;outDir;serverFile;viteConfig;pluginConfig;params;constructor(i){this.params=i}async makeConfig(){const{mode:i}=this.params;this.viteConfig=await s({},"build",i,"production"===i?"production":"development"),this.pluginConfig=e(this.viteConfig),this.buildDir=o.resolve(this.viteConfig.root,this.viteConfig.build.outDir),this.outDir=this.viteConfig.build.outDir,this.serverFile=this.pluginConfig.serverFile,this.nodeEnv=process.env.NODE_ENV||"development",this.isProd="production"===this.nodeEnv}clearBuildFolder(){i.existsSync(this.buildDir)&&i.rmSync(this.buildDir,{recursive:!0})}promisifyProcess(i){const o=new Promise(((o,t)=>{i.on("exit",(i=>{o(i)})),i.on("close",(i=>{o(i)})),i.on("error",(i=>{t(i)}))}));return i.stdout?.pipe(process.stdout),i.stderr?.pipe(process.stderr),o.command=i,o}async buildManifest(){await n.get(this.viteConfig.root,{alias:this.viteConfig.resolve.alias,buildDir:this.viteConfig.build.outDir}).buildRoutesManifest(this.pluginConfig.preloadAssets)}unlockRobots(){const o=`${this.buildDir}/client/robots.txt`;if(!i.existsSync(o))return void console.warn(`Failed to unlock robots.txt, file not exist: ${o}`);const s=i.readFileSync(o,{encoding:"utf-8"}).replace(/Disallow: \/$/m,"Allow: /");i.writeFileSync(o,s,{encoding:"utf-8"}),console.info(t.blue("\nrobots.txt unlocked."))}eject(){const o=`${this.buildDir}/server/start.js`;i.writeFileSync(o,"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\n\nconst VERSION = process.env.VERSION || \"1.0.0\";\nconst PORT = process.env.PORT || 3000;\nconst IS_HOST = process.env.IS_HOST || \"0\";\nconst ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\n\nawait runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\n",{encoding:"utf-8"})}}export{r as default};
1
+ import i from"node:fs";import o from"node:path";import t from"chalk";import{resolveConfig as s}from"vite";import e from"../helpers/plugin-config.js";import n from"./server-config.js";import r from"./ssr-manifest.js";class l{isProd;nodeEnv;buildDir;outDir;serverFile;viteConfig;pluginConfig;params;constructor(i){this.params=i}async makeConfig(){const{mode:i}=this.params;this.viteConfig=await s({},"build",i,"production"===i?"production":"development"),this.pluginConfig=e(this.viteConfig),this.buildDir=o.resolve(this.viteConfig.root,this.viteConfig.build.outDir),this.outDir=this.viteConfig.build.outDir,this.serverFile=this.pluginConfig.serverFile,this.nodeEnv=process.env.NODE_ENV||"development",this.isProd="production"===this.nodeEnv}clearBuildFolder(){i.existsSync(this.buildDir)&&i.rmSync(this.buildDir,{recursive:!0})}promisifyProcess(i){const o=new Promise(((o,t)=>{i.on("exit",(i=>{o(i)})),i.on("close",(i=>{o(i)})),i.on("error",(i=>{t(i)}))}));return i.stdout?.pipe(process.stdout),i.stderr?.pipe(process.stderr),o.command=i,o}async buildManifest(){const i=n.init({isProd:this.isProd,mode:this.params.mode},{root:this.viteConfig.root});await r.get(i,{buildDir:this.viteConfig.build.outDir,viteAliases:this.viteConfig.resolve.alias}).buildRoutesManifest(this.pluginConfig.preloadAssets)}unlockRobots(){const o=`${this.buildDir}/client/robots.txt`;if(!i.existsSync(o))return void console.warn(`Failed to unlock robots.txt, file not exist: ${o}`);const s=i.readFileSync(o,{encoding:"utf-8"}).replace(/Disallow: \/$/m,"Allow: /");i.writeFileSync(o,s,{encoding:"utf-8"}),console.info(t.blue("\nrobots.txt unlocked."))}eject(){const o=`${this.buildDir}/server/start.js`;i.writeFileSync(o,"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\n\nconst VERSION = process.env.VERSION || \"1.0.0\";\nconst PORT = process.env.PORT || 3000;\nconst IS_HOST = process.env.IS_HOST || \"0\";\nconst ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\n\nawait runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\n",{encoding:"utf-8"})}}export{l as default};
2
2
  //# sourceMappingURL=build.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sources":["../../src/services/build.ts"],"sourcesContent":["import type childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { ResolvedConfig } from 'vite';\nimport { resolveConfig } from 'vite';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport SsrManifest from '@services/ssr-manifest';\n\ninterface IBuildParams {\n mode: string;\n}\n\n/**\n * Build service\n */\nclass Build {\n /**\n * Is production build\n */\n public isProd: boolean;\n\n /**\n * Node environment\n */\n public nodeEnv: string;\n\n /**\n * Build folder\n */\n public buildDir: string;\n\n /**\n * Relative build dir\n */\n public outDir: string;\n\n /**\n * Server file\n */\n public serverFile: string;\n\n /**\n * Vite config\n */\n public viteConfig: ResolvedConfig;\n\n /**\n * Plugin config\n */\n public pluginConfig: IPluginConfig;\n\n /**\n * Build params\n */\n protected params: IBuildParams;\n\n /**\n * @constructor\n */\n constructor(params: IBuildParams) {\n this.params = params;\n }\n\n /**\n * Make config\n */\n public async makeConfig(): Promise<void> {\n const { mode } = this.params;\n\n this.viteConfig = await resolveConfig(\n {},\n 'build',\n mode,\n mode === 'production' ? 'production' : 'development',\n );\n this.pluginConfig = getPluginConfig(this.viteConfig);\n this.buildDir = path.resolve(this.viteConfig.root, this.viteConfig.build.outDir);\n this.outDir = this.viteConfig.build.outDir;\n this.serverFile = this.pluginConfig.serverFile;\n this.nodeEnv = process.env.NODE_ENV || 'development';\n this.isProd = this.nodeEnv === 'production';\n }\n\n /**\n * Clear build folder\n */\n public clearBuildFolder(): void {\n // clear build folder\n if (fs.existsSync(this.buildDir)) {\n fs.rmSync(this.buildDir, { recursive: true });\n }\n }\n\n /**\n * Promisify spawn process\n */\n public promisifyProcess(command: childProcess.ChildProcess): Promise<unknown> {\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 assets manifest file\n */\n public async buildManifest(): Promise<void> {\n await SsrManifest.get(this.viteConfig.root, {\n alias: this.viteConfig.resolve.alias,\n buildDir: this.viteConfig.build.outDir,\n }).buildRoutesManifest(this.pluginConfig.preloadAssets);\n }\n\n /**\n * Change general directive Disallow to Allow in robots.txt.\n */\n public unlockRobots(): void {\n const robotsFile = `${this.buildDir}/client/robots.txt`;\n\n if (!fs.existsSync(robotsFile)) {\n console.warn(`Failed to unlock robots.txt, file not exist: ${robotsFile}`);\n\n return;\n }\n\n const data = fs\n .readFileSync(robotsFile, { encoding: 'utf-8' })\n .replace(/Disallow: \\/$/m, 'Allow: /');\n\n fs.writeFileSync(robotsFile, data, { encoding: 'utf-8' });\n\n console.info(chalk.blue('\\nrobots.txt unlocked.'));\n }\n\n /**\n * Eject cli to run app via node\n */\n public eject(): void {\n const entrypoint = `${this.buildDir}/server/start.js`;\n const script =\n \"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\\n\\n\" +\n 'const VERSION = process.env.VERSION || \"1.0.0\";\\n' +\n 'const PORT = process.env.PORT || 3000;\\n' +\n 'const IS_HOST = process.env.IS_HOST || \"0\";\\n' +\n 'const ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\\n\\n' +\n `await runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n}\n\nexport default Build;\n"],"names":["Build","isProd","nodeEnv","buildDir","outDir","serverFile","viteConfig","pluginConfig","params","constructor","this","async","mode","resolveConfig","getPluginConfig","path","resolve","root","build","process","env","NODE_ENV","clearBuildFolder","fs","existsSync","rmSync","recursive","promisifyProcess","command","promise","Promise","reject","on","code","message","stdout","pipe","stderr","SsrManifest","get","alias","buildRoutesManifest","preloadAssets","unlockRobots","robotsFile","console","warn","data","readFileSync","encoding","replace","writeFileSync","info","chalk","blue","eject","entrypoint"],"mappings":"sLAiBA,MAAMA,EAIGC,OAKAC,QAKAC,SAKAC,OAKAC,WAKAC,WAKAC,aAKGC,OAKVC,YAAYD,GACVE,KAAKF,OAASA,CACf,CAKMG,mBACL,MAAMC,KAAEA,GAASF,KAAKF,OAEtBE,KAAKJ,iBAAmBO,EACtB,GACA,QACAD,EACS,eAATA,EAAwB,aAAe,eAEzCF,KAAKH,aAAeO,EAAgBJ,KAAKJ,YACzCI,KAAKP,SAAWY,EAAKC,QAAQN,KAAKJ,WAAWW,KAAMP,KAAKJ,WAAWY,MAAMd,QACzEM,KAAKN,OAASM,KAAKJ,WAAWY,MAAMd,OACpCM,KAAKL,WAAaK,KAAKH,aAAaF,WACpCK,KAAKR,QAAUiB,QAAQC,IAAIC,UAAY,cACvCX,KAAKT,OAA0B,eAAjBS,KAAKR,OACpB,CAKMoB,mBAEDC,EAAGC,WAAWd,KAAKP,WACrBoB,EAAGE,OAAOf,KAAKP,SAAU,CAAEuB,WAAW,GAEzC,CAKMC,iBAAiBC,GACtB,MAAMC,EAAU,IAAIC,SAAQ,CAACd,EAASe,KACpCH,EAAQI,GAAG,QAASC,IAClBjB,EAAQiB,EAAK,IAGfL,EAAQI,GAAG,SAAUC,IACnBjB,EAAQiB,EAAK,IAGfL,EAAQI,GAAG,SAAUE,IACnBH,EAAOG,EAAQ,GACf,IAQJ,OALAN,EAAQO,QAAQC,KAAKjB,QAAQgB,QAC7BP,EAAQS,QAAQD,KAAKjB,QAAQkB,QAE7BR,EAAiB,QAAID,EAEdC,CACR,CAKMlB,4BACC2B,EAAYC,IAAI7B,KAAKJ,WAAWW,KAAM,CAC1CuB,MAAO9B,KAAKJ,WAAWU,QAAQwB,MAC/BrC,SAAUO,KAAKJ,WAAWY,MAAMd,SAC/BqC,oBAAoB/B,KAAKH,aAAamC,cAC1C,CAKMC,eACL,MAAMC,EAAa,GAAGlC,KAAKP,6BAE3B,IAAKoB,EAAGC,WAAWoB,GAGjB,YAFAC,QAAQC,KAAK,gDAAgDF,KAK/D,MAAMG,EAAOxB,EACVyB,aAAaJ,EAAY,CAAEK,SAAU,UACrCC,QAAQ,iBAAkB,YAE7B3B,EAAG4B,cAAcP,EAAYG,EAAM,CAAEE,SAAU,UAE/CJ,QAAQO,KAAKC,EAAMC,KAAK,0BACzB,CAKMC,QACL,MAAMC,EAAa,GAAG9C,KAAKP,2BAe3BoB,EAAG4B,cAAcK,EAbf,2bAamC,CACnCP,SAAU,SAEb"}
1
+ {"version":3,"file":"build.js","sources":["../../src/services/build.ts"],"sourcesContent":["import type childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { ResolvedConfig } from 'vite';\nimport { resolveConfig } from 'vite';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport ServerConfig from '@services/server-config';\nimport SsrManifest from '@services/ssr-manifest';\n\ninterface IBuildParams {\n mode: string;\n}\n\n/**\n * Build service\n */\nclass Build {\n /**\n * Is production build\n */\n public isProd: boolean;\n\n /**\n * Node environment\n */\n public nodeEnv: string;\n\n /**\n * Build folder\n */\n public buildDir: string;\n\n /**\n * Relative build dir\n */\n public outDir: string;\n\n /**\n * Server file\n */\n public serverFile: string;\n\n /**\n * Vite config\n */\n public viteConfig: ResolvedConfig;\n\n /**\n * Plugin config\n */\n public pluginConfig: IPluginConfig;\n\n /**\n * Build params\n */\n protected params: IBuildParams;\n\n /**\n * @constructor\n */\n constructor(params: IBuildParams) {\n this.params = params;\n }\n\n /**\n * Make config\n */\n public async makeConfig(): Promise<void> {\n const { mode } = this.params;\n\n this.viteConfig = await resolveConfig(\n {},\n 'build',\n mode,\n mode === 'production' ? 'production' : 'development',\n );\n this.pluginConfig = getPluginConfig(this.viteConfig);\n this.buildDir = path.resolve(this.viteConfig.root, this.viteConfig.build.outDir);\n this.outDir = this.viteConfig.build.outDir;\n this.serverFile = this.pluginConfig.serverFile;\n this.nodeEnv = process.env.NODE_ENV || 'development';\n this.isProd = this.nodeEnv === 'production';\n }\n\n /**\n * Clear build folder\n */\n public clearBuildFolder(): void {\n // clear build folder\n if (fs.existsSync(this.buildDir)) {\n fs.rmSync(this.buildDir, { recursive: true });\n }\n }\n\n /**\n * Promisify spawn process\n */\n public promisifyProcess(command: childProcess.ChildProcess): Promise<unknown> {\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 assets manifest file\n */\n public async buildManifest(): Promise<void> {\n const serverConfig = ServerConfig.init(\n { isProd: this.isProd, mode: this.params.mode },\n { root: this.viteConfig.root },\n );\n\n await SsrManifest.get(serverConfig, {\n buildDir: this.viteConfig.build.outDir,\n viteAliases: this.viteConfig.resolve.alias,\n }).buildRoutesManifest(this.pluginConfig.preloadAssets);\n }\n\n /**\n * Change general directive Disallow to Allow in robots.txt.\n */\n public unlockRobots(): void {\n const robotsFile = `${this.buildDir}/client/robots.txt`;\n\n if (!fs.existsSync(robotsFile)) {\n console.warn(`Failed to unlock robots.txt, file not exist: ${robotsFile}`);\n\n return;\n }\n\n const data = fs\n .readFileSync(robotsFile, { encoding: 'utf-8' })\n .replace(/Disallow: \\/$/m, 'Allow: /');\n\n fs.writeFileSync(robotsFile, data, { encoding: 'utf-8' });\n\n console.info(chalk.blue('\\nrobots.txt unlocked.'));\n }\n\n /**\n * Eject cli to run app via node\n */\n public eject(): void {\n const entrypoint = `${this.buildDir}/server/start.js`;\n const script =\n \"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\\n\\n\" +\n 'const VERSION = process.env.VERSION || \"1.0.0\";\\n' +\n 'const PORT = process.env.PORT || 3000;\\n' +\n 'const IS_HOST = process.env.IS_HOST || \"0\";\\n' +\n 'const ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\\n\\n' +\n `await runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n}\n\nexport default Build;\n"],"names":["Build","isProd","nodeEnv","buildDir","outDir","serverFile","viteConfig","pluginConfig","params","constructor","this","async","mode","resolveConfig","getPluginConfig","path","resolve","root","build","process","env","NODE_ENV","clearBuildFolder","fs","existsSync","rmSync","recursive","promisifyProcess","command","promise","Promise","reject","on","code","message","stdout","pipe","stderr","serverConfig","ServerConfig","init","SsrManifest","get","viteAliases","alias","buildRoutesManifest","preloadAssets","unlockRobots","robotsFile","console","warn","data","readFileSync","encoding","replace","writeFileSync","info","chalk","blue","eject","entrypoint"],"mappings":"wNAkBA,MAAMA,EAIGC,OAKAC,QAKAC,SAKAC,OAKAC,WAKAC,WAKAC,aAKGC,OAKVC,YAAYD,GACVE,KAAKF,OAASA,CACf,CAKMG,mBACL,MAAMC,KAAEA,GAASF,KAAKF,OAEtBE,KAAKJ,iBAAmBO,EACtB,GACA,QACAD,EACS,eAATA,EAAwB,aAAe,eAEzCF,KAAKH,aAAeO,EAAgBJ,KAAKJ,YACzCI,KAAKP,SAAWY,EAAKC,QAAQN,KAAKJ,WAAWW,KAAMP,KAAKJ,WAAWY,MAAMd,QACzEM,KAAKN,OAASM,KAAKJ,WAAWY,MAAMd,OACpCM,KAAKL,WAAaK,KAAKH,aAAaF,WACpCK,KAAKR,QAAUiB,QAAQC,IAAIC,UAAY,cACvCX,KAAKT,OAA0B,eAAjBS,KAAKR,OACpB,CAKMoB,mBAEDC,EAAGC,WAAWd,KAAKP,WACrBoB,EAAGE,OAAOf,KAAKP,SAAU,CAAEuB,WAAW,GAEzC,CAKMC,iBAAiBC,GACtB,MAAMC,EAAU,IAAIC,SAAQ,CAACd,EAASe,KACpCH,EAAQI,GAAG,QAASC,IAClBjB,EAAQiB,EAAK,IAGfL,EAAQI,GAAG,SAAUC,IACnBjB,EAAQiB,EAAK,IAGfL,EAAQI,GAAG,SAAUE,IACnBH,EAAOG,EAAQ,GACf,IAQJ,OALAN,EAAQO,QAAQC,KAAKjB,QAAQgB,QAC7BP,EAAQS,QAAQD,KAAKjB,QAAQkB,QAE7BR,EAAiB,QAAID,EAEdC,CACR,CAKMlB,sBACL,MAAM2B,EAAeC,EAAaC,KAChC,CAAEvC,OAAQS,KAAKT,OAAQW,KAAMF,KAAKF,OAAOI,MACzC,CAAEK,KAAMP,KAAKJ,WAAWW,aAGpBwB,EAAYC,IAAIJ,EAAc,CAClCnC,SAAUO,KAAKJ,WAAWY,MAAMd,OAChCuC,YAAajC,KAAKJ,WAAWU,QAAQ4B,QACpCC,oBAAoBnC,KAAKH,aAAauC,cAC1C,CAKMC,eACL,MAAMC,EAAa,GAAGtC,KAAKP,6BAE3B,IAAKoB,EAAGC,WAAWwB,GAGjB,YAFAC,QAAQC,KAAK,gDAAgDF,KAK/D,MAAMG,EAAO5B,EACV6B,aAAaJ,EAAY,CAAEK,SAAU,UACrCC,QAAQ,iBAAkB,YAE7B/B,EAAGgC,cAAcP,EAAYG,EAAM,CAAEE,SAAU,UAE/CJ,QAAQO,KAAKC,EAAMC,KAAK,0BACzB,CAKMC,QACL,MAAMC,EAAa,GAAGlD,KAAKP,2BAe3BoB,EAAGgC,cAAcK,EAbf,2bAamC,CACnCP,SAAU,SAEb"}
@@ -5,7 +5,6 @@ interface IConfigOptions {
5
5
  isProd?: boolean;
6
6
  isHost?: boolean;
7
7
  isOnlyClient?: boolean;
8
- prodParams?: Partial<IConfigParams>;
9
8
  mode?: string;
10
9
  }
11
10
  interface IConfigParams {
@@ -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 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"}
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 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":"+FA6BA,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"}
@@ -2,11 +2,12 @@
2
2
  import { Socket } from 'node:net';
3
3
  import { AgnosticDataRouteMatch } from '@remix-run/router/dist/utils';
4
4
  import { RouteObject } from 'react-router-dom';
5
- import { Alias } from 'vite';
5
+ import { Alias, ModuleNode } from 'vite';
6
6
  import { IRequestContext } from "../node/render.js";
7
+ import ServerConfig from "./server-config.js";
7
8
  interface ISsrManifestParams {
8
- alias?: Alias[];
9
9
  buildDir?: string;
10
+ viteAliases?: Alias[];
10
11
  }
11
12
  interface IManifest {
12
13
  [path: string]: {
@@ -16,6 +17,17 @@ interface IManifest {
16
17
  imports: string[];
17
18
  };
18
19
  }
20
+ declare enum AssetType {
21
+ style = "style",
22
+ script = "script",
23
+ image = "image",
24
+ font = "font"
25
+ }
26
+ interface IAsset {
27
+ type: AssetType;
28
+ url: string;
29
+ content?: string;
30
+ }
19
31
  /**
20
32
  * Working with SSR Manifest file
21
33
  */
@@ -24,26 +36,30 @@ declare class SsrManifest {
24
36
  * Singleton
25
37
  */
26
38
  protected static instance: SsrManifest | null;
39
+ /**
40
+ * Server config
41
+ */
42
+ protected readonly config: ServerConfig;
27
43
  /**
28
44
  * Project root path
29
45
  */
30
- protected root: string;
46
+ protected readonly root: string;
31
47
  /**
32
48
  * Build dir
33
49
  */
34
- protected buildDir?: string;
50
+ protected readonly buildDir?: string;
35
51
  /**
36
52
  * Client manifest file name
37
53
  */
38
- protected manifestName: string;
54
+ protected readonly manifestName = "manifest.json";
39
55
  /**
40
56
  * Assets manifest file name
41
57
  */
42
- protected assetsManifest: string;
58
+ protected readonly assetsManifest = "assets-manifest.json";
43
59
  /**
44
60
  * Vite resolve aliases
45
61
  */
46
- protected alias?: Alias[];
62
+ protected readonly viteAliases?: Alias[];
47
63
  /**
48
64
  * Loaded assets manifest file
49
65
  */
@@ -54,14 +70,14 @@ declare class SsrManifest {
54
70
  /**
55
71
  * @constructor
56
72
  */
57
- protected constructor(root: string, { buildDir, alias }?: ISsrManifestParams);
73
+ protected constructor(config: ServerConfig, { buildDir, viteAliases }?: ISsrManifestParams);
58
74
  /**
59
75
  * Get singleton instance
60
76
  */
61
77
  /**
62
78
  * Get singleton instance
63
79
  */
64
- static get(root: string, params?: ISsrManifestParams): SsrManifest;
80
+ static get(config: ServerConfig, params?: ISsrManifestParams): SsrManifest;
65
81
  /**
66
82
  * Get assets manifest file name
67
83
  */
@@ -98,10 +114,10 @@ declare class SsrManifest {
98
114
  */
99
115
  buildRoutesManifest(shouldPreloadAssets: boolean): Promise<void>;
100
116
  /**
101
- * Load aliases manifest
117
+ * Get vite aliases
102
118
  */
103
119
  /**
104
- * Load aliases manifest
120
+ * Get vite aliases
105
121
  */
106
122
  protected getAliases(): Record<string, string>;
107
123
  /**
@@ -117,14 +133,30 @@ declare class SsrManifest {
117
133
  /**
118
134
  * Normalized route path
119
135
  */
120
- protected normalizeRoutePath(routePath?: string): string | undefined;
136
+ protected normalizeRoutePath(routePath?: string, withRoot?: boolean): string | undefined;
121
137
  /**
122
- * Get provided route assets
138
+ * Get route assets
123
139
  */
124
140
  /**
125
- * Get provided route assets
141
+ * Get route assets
126
142
  */
127
- getAssets(routes?: AgnosticDataRouteMatch[]): string[];
143
+ protected getAssets(routes?: AgnosticDataRouteMatch[]): IAsset[];
144
+ /**
145
+ * Get development route assets
146
+ */
147
+ /**
148
+ * Get development route assets
149
+ */
150
+ protected getAssetsDev(routes?: AgnosticDataRouteMatch[]): IAsset[];
151
+ /**
152
+ * Get module assets
153
+ */
154
+ /**
155
+ * Get module assets
156
+ */
157
+ protected getModuleAssets(module?: ModuleNode): {
158
+ [id: string]: IAsset;
159
+ };
128
160
  /**
129
161
  * Get asset weight
130
162
  */
@@ -138,14 +170,14 @@ declare class SsrManifest {
138
170
  /**
139
171
  * Get asset type
140
172
  */
141
- protected getAssetType(asset: string): string | null;
173
+ protected getAssetType(asset: string): AssetType | null;
142
174
  /**
143
175
  * Write 103 Early Hits header
144
176
  */
145
177
  /**
146
178
  * Write 103 Early Hits header
147
179
  */
148
- writeEarlyHits(assets: string[], socket: Socket): void;
180
+ writeEarlyHits(assets: IAsset[], socket: Socket): void;
149
181
  /**
150
182
  * Inject route assets to head html
151
183
  */
@@ -1,2 +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=e.split(s.win32.sep).join(s.posix.sep),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({routerContext:t,html:s,res:e,hasEarlyHints:i=!1}){const r=this.getAssets(t?.matches).sort(((t,s)=>{const e=this.getAssetWeight(t),i=this.getAssetWeight(s);return e===i?0:e-i})),n=r.map((t=>t.endsWith(".css")?`<link rel="stylesheet" href="${t}">`:t.endsWith(".js")?`<script async type="module" src="${t}"><\/script>`:null)).filter(Boolean);s.header=s.header.replace("</head>",`${n.join("\n")}</head>`),i&&n.length&&e.socket&&this.writeEarlyHits(r,e.socket)}}export{n as default};
1
+ import t from"node:fs";import e from"node:path";import s from"chalk";import i from"./prepare-server.js";var r;!function(t){t.style="style",t.script="script",t.image="image",t.font="font"}(r||(r={}));const n="\r\n";class o{static instance=null;config;root;buildDir;manifestName="manifest.json";assetsManifest="assets-manifest.json";viteAliases;routesAssets=null;constructor(t,{buildDir:e,viteAliases:s}={}){this.config=t,this.root=t.getParams().root,this.buildDir=e,this.viteAliases=s??t.getVite()?.config?.resolve.alias}static get(t,e={}){return null===o.instance&&(o.instance=new o(t,e)),o.instance}getAssetsManifestFile(){return`${e.resolve(this.root,this.buildDir||"")}/server/${this.assetsManifest}`}loadClientManifest(){const s=e.resolve(this.root,`${this.buildDir||""}/client/${this.manifestName}`);if(!t.existsSync(s))return{};const i=JSON.parse(t.readFileSync(s,{encoding:"utf-8"}));return t.rmSync(s),i}loadAssetsManifest(){if(null!==this.routesAssets)return this.routesAssets;const e=this.getAssetsManifestFile();return t.existsSync(e)?(this.routesAssets=JSON.parse(t.readFileSync(e,{encoding:"utf-8"})),this.routesAssets):{}}async getRoutesIds(t,e){const s={};for(const i in t){const r=t[i],n=[e,i].filter(Boolean).join("-");if(r.lazy){const t=await r.lazy();s[n]=this.normalizeRoutePath(t?.pathId)}else r.children&&Object.assign(s,await this.getRoutesIds(r.children,n))}return s}async buildRoutesManifest(e){const s=i.init(this.config),r=this.loadClientManifest(),{routes:n}=await s.loadEntrypoint(!1),o=await this.getRoutesIds(n),a=this.getRouteImportPostfix(),l={};Object.entries(o).forEach((([t,s])=>{const i=a.find((t=>void 0!==r[`${s}${t}`])),n=r[`${s}${i||""}`],o=[...n?.assets??[],...n?.css??[],n.file,...(e?n?.imports??[]:[]).map((t=>r[t]?.file))].filter((t=>t&&this.getAssetType(t))).map((t=>`/${t}`));o&&(l[t]=o)})),t.writeFileSync(this.getAssetsManifestFile(),JSON.stringify(l,null,2),{encoding:"utf-8"})}getAliases(){const t={};return this.viteAliases?.forEach((({find:e,replacement:s})=>{"string"==typeof e&&(t[e]=s)})),t}getRouteImportPostfix(){return["","/index"].map((t=>["",".js",".ts",".tsx"].map((e=>`${t}${e}`)))).flat()}normalizeRoutePath(t,s=!1){if(!t)return;let i="";if(t.startsWith("./")||t.startsWith("../"))i=e.resolve(this.root,t);else{const e=this.getAliases(),[s]=t.split("/");e[s]&&(i=t.replace(s,e[s]))}return i=i.split(e.win32.sep).join(e.posix.sep),s?i:i.replace(this.root,"").replace(/^\/|\/$/g,"")}getAssets(t){if(this.config.getVite())return this.getAssetsDev(t);const e=t?.map((({route:t})=>t.id)).filter(Boolean)??[];if(!e.length)return[];const s=this.loadAssetsManifest();return e.map((t=>s[t])).filter(Boolean).flat().sort(((t,e)=>{const s=this.getAssetWeight(t),i=this.getAssetWeight(e);return s===i?0:s-i})).map((t=>({type:this.getAssetType(t),url:t})))}getAssetsDev(t){const e=t?.map((({route:t})=>this.normalizeRoutePath(t?.pathId,!0))).filter(Boolean)??[];if(!e.length)return[];let s={};const i=this.getRouteImportPostfix();return[`${this.root}/${this.config.getPluginConfig()?.clientFile??"client.ts"}`,...e].forEach((t=>{for(const e of i){const i=this.config.getVite()?.moduleGraph.getModuleById(`${t}${e}`);if(i){s={...s,...this.getModuleAssets(i)};break}}})),Object.values(s)}getModuleAssets(t){if(!t?.clientImportedModules.size)return{};let e={};return t.clientImportedModules.forEach((t=>{const{file:i,clientImportedModules:n,transformResult:o}=t,a=i?.split(".").at(-1);if(i&&a&&["css","scss"].includes(a)){const t=o?.code.match(/__vite__css\s+=\s+"(?<css>.+)"/)?.groups?.css;if(t)try{e[i]={type:r.style,url:i,content:JSON.parse(`{"style": "${t}"}`).style}}catch(t){console.warn(s.yellowBright("Failed to parse style: ",i))}}else n.size&&(e={...e,...this.getModuleAssets(t)})})),e}getAssetWeight(t){switch(this.getAssetType(t)){case"style":return 1;case"script":return 2;default:return 3}}getAssetType(t){const e=t.split(".").at(-1)?.toLowerCase();switch(e){case"css":case"scss":return r.style;case"js":return r.script;case"svg":case"jpg":case"jpeg":case"png":case"webp":case"gif":case"ico":return r.image;case"ttf":case"otf":case"woff":case"woff2":return r.font;default:return null}}writeEarlyHits(t,e){e.write(`HTTP/1.1 103 Early Hints${n}`),t.forEach((({type:t,url:s})=>{t&&["style","script"].includes(t)&&e.write(`Link: <${s}>; rel=preload; as=${t}${n}`)})),e.write(n)}injectAssets({routerContext:t,html:e,res:s,hasEarlyHints:i=!1}){const r=this.getAssets(t?.matches),n=r.map((({type:t,url:e,content:s=""})=>{switch(t){case"style":return this.config.getVite()?`<style data-vite-dev-id="${e}">${s}</style>`:`<link rel="stylesheet" href="${e}">`;case"script":return`<script async type="module" src="${e}"><\/script>`}return null})).filter(Boolean);e.header=e.header.replace("</head>",`${n.join("\n")}</head>`),i&&n.length&&s.socket&&this.writeEarlyHits(r,s.socket)}}export{o as default};
2
2
  //# sourceMappingURL=ssr-manifest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-manifest.js","sources":["../../src/services/ssr-manifest.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { Socket } from 'node:net';\nimport path from 'node:path';\nimport type { AgnosticDataRouteMatch } from '@remix-run/router/dist/utils';\nimport 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 // normalize slashes\n fullPath = fullPath.split(path.win32.sep).join(path.posix.sep);\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({ routerContext, html, res, hasEarlyHints = false }: IRequestContext): void {\n const assets = this.getAssets(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 html.header = html.header.replace('</head>', `${htmlAssets.join('\\n')}</head>`);\n\n if (hasEarlyHints && htmlAssets.length && res.socket) {\n this.writeEarlyHits(assets, res.socket);\n }\n }\n}\n\nexport default SsrManifest;\n"],"names":["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","win32","sep","posix","getAssets","routeIds","id","length","getAssetWeight","at","toLowerCase","writeEarlyHits","socket","write","type","includes","injectAssets","routerContext","html","res","hasEarlyHints","matches","sort","a","b","aWeight","bWeight","htmlAssets","endsWith","header"],"mappings":"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"}
1
+ {"version":3,"file":"ssr-manifest.js","sources":["../../src/services/ssr-manifest.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { Socket } from 'node:net';\nimport path from 'node:path';\nimport type { AgnosticDataRouteMatch } from '@remix-run/router/dist/utils';\nimport chalk from 'chalk';\nimport type { RouteObject } from 'react-router-dom';\nimport type { Alias, ModuleNode } from 'vite';\nimport type { IRequestContext } from '@node/render';\nimport PrepareServer from '@services/prepare-server';\nimport type ServerConfig from '@services/server-config';\n\ninterface ISsrManifestParams {\n buildDir?: string;\n viteAliases?: Alias[];\n}\n\ninterface IManifest {\n [path: string]: {\n assets: string[];\n css: string[];\n file: string;\n imports: string[];\n };\n}\n\nenum AssetType {\n style = 'style',\n script = 'script',\n image = 'image',\n font = 'font',\n}\n\ninterface IAsset {\n type: AssetType;\n url: string;\n content?: string;\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 * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * Project root path\n */\n protected readonly root: string;\n\n /**\n * Build dir\n */\n protected readonly buildDir?: string;\n\n /**\n * Client manifest file name\n */\n protected readonly manifestName = 'manifest.json';\n\n /**\n * Assets manifest file name\n */\n protected readonly assetsManifest = 'assets-manifest.json';\n\n /**\n * Vite resolve aliases\n */\n protected readonly viteAliases?: Alias[];\n\n /**\n * Loaded assets manifest file\n */\n protected routesAssets: Record<string, string[]> | null = null;\n\n /**\n * @constructor\n */\n protected constructor(config: ServerConfig, { buildDir, viteAliases }: ISsrManifestParams = {}) {\n this.config = config;\n this.root = config.getParams().root;\n this.buildDir = buildDir;\n this.viteAliases = viteAliases ?? config.getVite()?.config?.resolve.alias;\n }\n\n /**\n * Get singleton instance\n */\n public static get(config: ServerConfig, params: ISsrManifestParams = {}): SsrManifest {\n if (SsrManifest.instance === null) {\n SsrManifest.instance = new SsrManifest(config, params);\n }\n\n return SsrManifest.instance;\n }\n\n /**\n * Get 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 prepareServer = PrepareServer.init(this.config);\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 * Get vite aliases\n */\n protected getAliases(): Record<string, string> {\n const aliases = {};\n\n this.viteAliases?.forEach(({ find, replacement }) => {\n if (typeof find !== 'string') {\n return;\n }\n\n aliases[find] = replacement;\n });\n\n return aliases;\n }\n\n /**\n * Return route postfix\n */\n protected getRouteImportPostfix(): string[] {\n return ['', '/index']\n .map((prefix) => ['', '.js', '.ts', '.tsx'].map((ext) => `${prefix}${ext}`))\n .flat();\n }\n\n /**\n * Normalized route path\n */\n protected normalizeRoutePath(routePath?: string, withRoot = false): string | undefined {\n if (!routePath) {\n return;\n }\n\n let fullPath = '';\n\n // relative import\n if (routePath.startsWith('./') || routePath.startsWith('../')) {\n fullPath = path.resolve(this.root, routePath);\n } else {\n // alias import\n const aliases = this.getAliases();\n // get alias\n const [routeAlias] = routePath.split('/');\n\n if (aliases[routeAlias]) {\n fullPath = routePath.replace(routeAlias, aliases[routeAlias]);\n }\n }\n\n // normalize slashes\n fullPath = fullPath.split(path.win32.sep).join(path.posix.sep);\n\n if (withRoot) {\n return fullPath;\n }\n\n return fullPath.replace(this.root, '').replace(/^\\/|\\/$/g, '');\n }\n\n /**\n * Get route assets\n */\n protected getAssets(routes?: AgnosticDataRouteMatch[]): IAsset[] {\n if (this.config.getVite()) {\n return this.getAssetsDev(routes);\n }\n\n const routeIds = routes?.map(({ route }) => route.id).filter(Boolean) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n const routesAssets = this.loadAssetsManifest();\n\n return routeIds\n .map((routeId) => routesAssets[routeId])\n .filter(Boolean)\n .flat()\n .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 .map((url) => ({\n type: this.getAssetType(url)!,\n url,\n }));\n }\n\n /**\n * Get development route assets\n */\n protected getAssetsDev(routes?: AgnosticDataRouteMatch[]): IAsset[] {\n const routeIds =\n (routes\n ?.map(({ route }) => this.normalizeRoutePath(route?.['pathId'] as string, true))\n .filter(Boolean) as string[]) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n let assets: { [id: string]: IAsset } = {};\n const postfixes = this.getRouteImportPostfix();\n const rootId = `${this.root}/${this.config.getPluginConfig()?.clientFile ?? 'client.ts'}`;\n\n [rootId, ...routeIds].forEach((moduleId) => {\n for (const ext of postfixes) {\n const module = this.config.getVite()?.moduleGraph.getModuleById(`${moduleId}${ext}`);\n\n if (module) {\n assets = { ...assets, ...this.getModuleAssets(module) };\n break;\n }\n }\n });\n\n return Object.values(assets);\n }\n\n /**\n * Get module assets\n */\n protected getModuleAssets(module?: ModuleNode): { [id: string]: IAsset } {\n if (!module?.clientImportedModules.size) {\n return {};\n }\n\n let assets: { [id: string]: IAsset } = {};\n\n module.clientImportedModules.forEach((subModule) => {\n const { file, clientImportedModules, transformResult } = subModule;\n const ext = file?.split('.').at(-1);\n\n if (file && ext && ['css', 'scss'].includes(ext)) {\n // @TODO investigate better method?\n const code = transformResult?.code.match(/__vite__css\\s+=\\s+\"(?<css>.+)\"/)?.groups?.css;\n\n if (code) {\n try {\n assets[file] = {\n type: AssetType.style,\n url: file,\n content: JSON.parse(`{\"style\": \"${code}\"}`).style,\n };\n } catch (e) {\n console.warn(chalk.yellowBright('Failed to parse style: ', file));\n }\n }\n } else if (clientImportedModules.size) {\n assets = {\n ...assets,\n ...this.getModuleAssets(subModule),\n };\n }\n });\n\n return assets;\n }\n\n /**\n * Get asset weight\n */\n protected getAssetWeight(asset: string): number {\n const type = this.getAssetType(asset);\n\n switch (type) {\n case '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): AssetType | null {\n const ext = asset.split('.').at(-1)?.toLowerCase();\n\n switch (ext) {\n case 'css':\n case 'scss':\n return AssetType.style;\n\n case 'js':\n return AssetType.script;\n\n case 'svg':\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'webp':\n case 'gif':\n case 'ico':\n return AssetType.image;\n\n case 'ttf':\n case 'otf':\n case 'woff':\n case 'woff2':\n return AssetType.font;\n\n default:\n return null;\n }\n }\n\n /**\n * Write 103 Early Hits header\n */\n public writeEarlyHits(assets: IAsset[], socket: Socket): void {\n socket.write(`HTTP/1.1 103 Early Hints${CRLF}`);\n assets.forEach(({ type, url }) => {\n if (!type || !['style', 'script'].includes(type)) {\n return;\n }\n\n socket.write(`Link: <${url}>; rel=preload; as=${type}${CRLF}`);\n });\n socket.write(CRLF);\n }\n\n /**\n * Inject route assets to head html\n */\n public injectAssets({ routerContext, html, res, hasEarlyHints = false }: IRequestContext): void {\n const assets = this.getAssets(routerContext?.matches);\n const htmlAssets = assets\n .map(({ type, url, content = '' }) => {\n switch (type) {\n case 'style':\n return this.config.getVite()\n ? `<style data-vite-dev-id=\"${url}\">${content}</style>`\n : `<link rel=\"stylesheet\" href=\"${url}\">`;\n\n case 'script':\n return `<script async type=\"module\" src=\"${url}\"></script>`;\n }\n\n return null;\n })\n .filter(Boolean);\n\n html.header = html.header.replace('</head>', `${htmlAssets.join('\\n')}</head>`);\n\n if (hasEarlyHints && htmlAssets.length && res.socket) {\n this.writeEarlyHits(assets, res.socket);\n }\n }\n}\n\nexport default SsrManifest;\n"],"names":["AssetType","CRLF","SsrManifest","static","config","root","buildDir","manifestName","assetsManifest","viteAliases","routesAssets","constructor","this","getParams","getVite","resolve","alias","params","instance","getAssetsManifestFile","path","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","prepareServer","PrepareServer","init","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","withRoot","fullPath","startsWith","routeAlias","split","replace","win32","sep","posix","getAssets","getAssetsDev","routeIds","id","length","sort","a","b","aWeight","getAssetWeight","bWeight","url","type","getPluginConfig","clientFile","moduleId","module","moduleGraph","getModuleById","getModuleAssets","values","clientImportedModules","size","subModule","transformResult","at","includes","code","match","groups","style","content","e","console","warn","chalk","yellowBright","toLowerCase","script","image","font","writeEarlyHits","socket","write","injectAssets","routerContext","html","res","hasEarlyHints","matches","htmlAssets","header"],"mappings":"wGAyBA,IAAKA,GAAL,SAAKA,GACHA,EAAA,MAAA,QACAA,EAAA,OAAA,SACAA,EAAA,MAAA,QACAA,EAAA,KAAA,MACD,CALD,CAAKA,IAAAA,EAKJ,CAAA,IAQD,MAAMC,EAAO,OAKb,MAAMC,EAIMC,gBAAsC,KAK7BC,OAKAC,KAKAC,SAKAC,aAAe,gBAKfC,eAAiB,uBAKjBC,YAKTC,aAAgD,KAK1DC,YAAsBP,GAAsBE,SAAEA,EAAQG,YAAEA,GAAoC,CAAA,GAC1FG,KAAKR,OAASA,EACdQ,KAAKP,KAAOD,EAAOS,YAAYR,KAC/BO,KAAKN,SAAWA,EAChBM,KAAKH,YAAcA,GAAeL,EAAOU,WAAWV,QAAQW,QAAQC,KACrE,CAKMb,WAAWC,EAAsBa,EAA6B,IAKnE,OAJ6B,OAAzBf,EAAYgB,WACdhB,EAAYgB,SAAW,IAAIhB,EAAYE,EAAQa,IAG1Cf,EAAYgB,QACpB,CAKSC,wBAGR,MAAO,GAFQC,EAAKL,QAAQH,KAAKP,KAAMO,KAAKN,UAAY,cAE7BM,KAAKJ,gBACjC,CAKSa,qBACR,MAAMC,EAAoBF,EAAKL,QAC7BH,KAAKP,KACL,GAAGO,KAAKN,UAAY,aAAaM,KAAKL,gBAGxC,IAAKgB,EAAGC,WAAWF,GACjB,MAAO,GAGT,MAAMG,EAASC,KAAKC,MAClBJ,EAAGK,aAAaN,EAAmB,CAAEO,SAAU,WAKjD,OAFAN,EAAGO,OAAOR,GAEHG,CACR,CAKSM,qBACR,GAA0B,OAAtBnB,KAAKF,aACP,OAAOE,KAAKF,aAGd,MAAMsB,EAAepB,KAAKO,wBAE1B,OAAKI,EAAGC,WAAWQ,IAInBpB,KAAKF,aAAegB,KAAKC,MAAMJ,EAAGK,aAAaI,EAAc,CAAEH,SAAU,WAKlEjB,KAAKF,cARH,EASV,CAKSuB,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,GAAW1B,KAAKgC,mBAAmBD,GAAwB,OACnE,MAAUN,EAAMQ,UACfC,OAAOC,OAAOtB,QAAcb,KAAKoC,aAAaX,EAAMQ,SAAUP,GAEjE,CAED,OAAOb,CACR,CAKMQ,0BAA0BgB,GAC/B,MAAMC,EAAgBC,EAAcC,KAAKxC,KAAKR,QACxCiD,EAAWzC,KAAKS,sBAChBa,OAAEA,SAAiBgB,EAAcI,gBAAe,GAChDC,QAAoB3C,KAAKoC,aAAad,GACtCsB,EAAY5C,KAAK6C,wBAEjBhC,EAAS,CAAA,EAGfqB,OAAOY,QAAQH,GAAaI,SAAQ,EAAErB,EAASsB,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,SACNpB,EAAsBgB,GAAWK,SAAW,GAAK,IAAIC,KACtDC,GAAgBnB,EAASmB,IAAcH,QAGzC9B,QACEkC,GAECA,GAAS7D,KAAK8D,aAAaD,KAE9BF,KAAKE,GAAU,IAAIA,MAElBP,IACFzC,EAAOa,GAAW4B,EACnB,IAGH3C,EAAGoD,cAAc/D,KAAKO,wBAAyBO,KAAKkD,UAAUnD,EAAQ,KAAM,GAAI,CAC9EI,SAAU,SAEb,CAKSgD,aACR,MAAMC,EAAU,CAAA,EAUhB,OARAlE,KAAKH,aAAakD,SAAQ,EAAGG,OAAMiB,kBACb,iBAATjB,IAIXgB,EAAQhB,GAAQiB,EAAW,IAGtBD,CACR,CAKSrB,wBACR,MAAO,CAAC,GAAI,UACTc,KAAKS,GAAW,CAAC,GAAI,MAAO,MAAO,QAAQT,KAAKU,GAAQ,GAAGD,IAASC,QACpEC,MACJ,CAKStC,mBAAmBgB,EAAoBuB,GAAW,GAC1D,IAAKvB,EACH,OAGF,IAAIwB,EAAW,GAGf,GAAIxB,EAAUyB,WAAW,OAASzB,EAAUyB,WAAW,OACrDD,EAAWhE,EAAKL,QAAQH,KAAKP,KAAMuD,OAC9B,CAEL,MAAMkB,EAAUlE,KAAKiE,cAEdS,GAAc1B,EAAU2B,MAAM,KAEjCT,EAAQQ,KACVF,EAAWxB,EAAU4B,QAAQF,EAAYR,EAAQQ,IAEpD,CAKD,OAFAF,EAAWA,EAASG,MAAMnE,EAAKqE,MAAMC,KAAKjD,KAAKrB,EAAKuE,MAAMD,KAEtDP,EACKC,EAGFA,EAASI,QAAQ5E,KAAKP,KAAM,IAAImF,QAAQ,WAAY,GAC5D,CAKSI,UAAU1D,GAClB,GAAItB,KAAKR,OAAOU,UACd,OAAOF,KAAKiF,aAAa3D,GAG3B,MAAM4D,EAAW5D,GAAQqC,KAAI,EAAGlC,WAAYA,EAAM0D,KAAIxD,OAAOC,UAAY,GAEzE,IAAKsD,EAASE,OACZ,MAAO,GAGT,MAAMtF,EAAeE,KAAKmB,qBAE1B,OAAO+D,EACJvB,KAAKjC,GAAY5B,EAAa4B,KAC9BC,OAAOC,SACP0C,OACAe,MAAK,CAACC,EAAGC,KACR,MAAMC,EAAUxF,KAAKyF,eAAeH,GAC9BI,EAAU1F,KAAKyF,eAAeF,GAEpC,OAAOC,IAAYE,EAAU,EAAIF,EAAUE,CAAO,IAEnD/B,KAAKgC,IAAS,CACbC,KAAM5F,KAAK8D,aAAa6B,GACxBA,SAEL,CAKSV,aAAa3D,GACrB,MAAM4D,EACH5D,GACGqC,KAAI,EAAGlC,WAAYzB,KAAKgC,mBAAmBP,GAAgB,QAAa,KACzEE,OAAOC,UAAyB,GAErC,IAAKsD,EAASE,OACZ,MAAO,GAGT,IAAI7B,EAAmC,CAAA,EACvC,MAAMX,EAAY5C,KAAK6C,wBAcvB,MAXA,CAFe,GAAG7C,KAAKP,QAAQO,KAAKR,OAAOqG,mBAAmBC,YAAc,iBAEhEZ,GAAUnC,SAASgD,IAC7B,IAAK,MAAM1B,KAAOzB,EAAW,CAC3B,MAAMoD,EAAShG,KAAKR,OAAOU,WAAW+F,YAAYC,cAAc,GAAGH,IAAW1B,KAE9E,GAAI2B,EAAQ,CACVzC,EAAS,IAAKA,KAAWvD,KAAKmG,gBAAgBH,IAC9C,KACD,CACF,KAGI9D,OAAOkE,OAAO7C,EACtB,CAKS4C,gBAAgBH,GACxB,IAAKA,GAAQK,sBAAsBC,KACjC,MAAO,GAGT,IAAI/C,EAAmC,CAAA,EA6BvC,OA3BAyC,EAAOK,sBAAsBtD,SAASwD,IACpC,MAAM9C,KAAEA,EAAI4C,sBAAEA,EAAqBG,gBAAEA,GAAoBD,EACnDlC,EAAMZ,GAAMkB,MAAM,KAAK8B,IAAI,GAEjC,GAAIhD,GAAQY,GAAO,CAAC,MAAO,QAAQqC,SAASrC,GAAM,CAEhD,MAAMsC,EAAOH,GAAiBG,KAAKC,MAAM,mCAAmCC,QAAQrD,IAEpF,GAAImD,EACF,IACEpD,EAAOE,GAAQ,CACbmC,KAAMxG,EAAU0H,MAChBnB,IAAKlC,EACLsD,QAASjG,KAAKC,MAAM,cAAc4F,OAAUG,MAE/C,CAAC,MAAOE,GACPC,QAAQC,KAAKC,EAAMC,aAAa,0BAA2B3D,GAC5D,CAEJ,MAAU4C,EAAsBC,OAC/B/C,EAAS,IACJA,KACAvD,KAAKmG,gBAAgBI,IAE3B,IAGIhD,CACR,CAKSkC,eAAe5B,GAGvB,OAFa7D,KAAK8D,aAAaD,IAG7B,IAAK,QACH,OAAO,EAET,IAAK,SACH,OAAO,EAET,QACE,OAAO,EAEZ,CAKSC,aAAaD,GACrB,MAAMQ,EAAMR,EAAMc,MAAM,KAAK8B,IAAI,IAAIY,cAErC,OAAQhD,GACN,IAAK,MACL,IAAK,OACH,OAAOjF,EAAU0H,MAEnB,IAAK,KACH,OAAO1H,EAAUkI,OAEnB,IAAK,MACL,IAAK,MACL,IAAK,OACL,IAAK,MACL,IAAK,OACL,IAAK,MACL,IAAK,MACH,OAAOlI,EAAUmI,MAEnB,IAAK,MACL,IAAK,MACL,IAAK,OACL,IAAK,QACH,OAAOnI,EAAUoI,KAEnB,QACE,OAAO,KAEZ,CAKMC,eAAelE,EAAkBmE,GACtCA,EAAOC,MAAM,2BAA2BtI,KACxCkE,EAAOR,SAAQ,EAAG6C,OAAMD,UACjBC,GAAS,CAAC,QAAS,UAAUc,SAASd,IAI3C8B,EAAOC,MAAM,UAAUhC,uBAAyBC,IAAOvG,IAAO,IAEhEqI,EAAOC,MAAMtI,EACd,CAKMuI,cAAaC,cAAEA,EAAaC,KAAEA,EAAIC,IAAEA,EAAGC,cAAEA,GAAgB,IAC9D,MAAMzE,EAASvD,KAAKgF,UAAU6C,GAAeI,SACvCC,EAAa3E,EAChBI,KAAI,EAAGiC,OAAMD,MAAKoB,UAAU,OAC3B,OAAQnB,GACN,IAAK,QACH,OAAO5F,KAAKR,OAAOU,UACf,4BAA4ByF,MAAQoB,YACpC,gCAAgCpB,MAEtC,IAAK,SACH,MAAO,oCAAoCA,gBAG/C,OAAO,IAAI,IAEZhE,OAAOC,SAEVkG,EAAKK,OAASL,EAAKK,OAAOvD,QAAQ,UAAW,GAAGsD,EAAWrG,KAAK,gBAE5DmG,GAAiBE,EAAW9C,QAAU2C,EAAIL,QAC5C1H,KAAKyH,eAAelE,EAAQwE,EAAIL,OAEnC"}