@lomray/vite-ssr-boost 1.0.0-beta.2 → 1.0.0-beta.21
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 +207 -0
- package/browser/entry.d.ts +8 -7
- package/browser/entry.js +1 -1
- package/browser/entry.js.map +1 -1
- package/cli/build.d.ts +4 -1
- package/cli/build.js +1 -1
- package/cli/build.js.map +1 -1
- package/cli/run-dev.d.ts +2 -1
- package/cli/run-dev.js +1 -1
- package/cli/run-dev.js.map +1 -1
- package/cli/run-docker-build.d.ts +12 -0
- package/cli/run-docker-build.js +2 -0
- package/cli/run-docker-build.js.map +1 -0
- package/cli/run-prod.d.ts +1 -0
- package/cli/run-prod.js +1 -1
- package/cli/run-prod.js.map +1 -1
- package/cli.js +1 -1
- package/cli.js.map +1 -1
- package/components/with-suspense.d.ts +7 -0
- package/components/with-suspense.js +2 -0
- package/components/with-suspense.js.map +1 -0
- package/constants/cli-actions.d.ts +1 -0
- package/constants/cli-actions.js +1 -1
- package/constants/cli-actions.js.map +1 -1
- package/helpers/dev-marker.d.ts +9 -0
- package/helpers/dev-marker.js +2 -0
- package/helpers/dev-marker.js.map +1 -0
- package/helpers/get-server-state.d.ts +5 -0
- package/helpers/get-server-state.js +2 -0
- package/helpers/get-server-state.js.map +1 -0
- package/helpers/import-route.d.ts +252 -0
- package/helpers/import-route.js +2 -0
- package/helpers/import-route.js.map +1 -0
- package/helpers/is-route-file.d.ts +5 -0
- package/helpers/is-route-file.js +2 -0
- package/helpers/is-route-file.js.map +1 -0
- package/helpers/print-server-info.js +1 -1
- package/helpers/print-server-info.js.map +1 -1
- package/helpers/print-server-urls.js +1 -1
- package/helpers/print-server-urls.js.map +1 -1
- package/helpers/unlock-robots.d.ts +5 -0
- package/helpers/unlock-robots.js +2 -0
- package/helpers/unlock-robots.js.map +1 -0
- package/helpers/vite-aliases.d.ts +6 -0
- package/helpers/vite-aliases.js +2 -0
- package/helpers/vite-aliases.js.map +1 -0
- package/interfaces/fc-route.d.ts +19 -0
- package/interfaces/fc-route.js +2 -0
- package/interfaces/fc-route.js.map +1 -0
- package/interfaces/fc.d.ts +4 -0
- package/interfaces/fc.js +2 -0
- package/interfaces/fc.js.map +1 -0
- package/interfaces/route-object.d.ts +8 -0
- package/interfaces/route-object.js +2 -0
- package/interfaces/route-object.js.map +1 -0
- package/node/entry.d.ts +11 -9
- package/node/entry.js +1 -1
- package/node/entry.js.map +1 -1
- package/node/render.d.ts +5 -4
- package/node/render.js +1 -1
- package/node/render.js.map +1 -1
- package/node/server.js +1 -1
- package/node/server.js.map +1 -1
- package/package.json +8 -4
- package/plugin.d.ts +6 -4
- package/plugin.js +1 -1
- package/plugin.js.map +1 -1
- package/plugins/make-aliases.d.ts +11 -0
- package/plugins/make-aliases.js +2 -0
- package/plugins/make-aliases.js.map +1 -0
- package/plugins/normalize-route.d.ts +13 -0
- package/plugins/normalize-route.js +2 -0
- package/plugins/normalize-route.js.map +1 -0
- package/services/prepare-server.d.ts +4 -3
- package/services/prepare-server.js +1 -1
- package/services/prepare-server.js.map +1 -1
- package/services/server-config.d.ts +6 -9
- package/services/server-config.js +1 -1
- package/services/server-config.js.map +1 -1
- package/services/ssr-manifest.d.ts +157 -0
- package/services/ssr-manifest.js +2 -0
- package/services/ssr-manifest.js.map +1 -0
- package/workflow/Dockerfile +23 -0
package/node/server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"path";import r from"compression";import
|
|
1
|
+
import e from"path";import r from"compression";import t from"express";import s from"../helpers/print-server-info.js";import o from"../services/prepare-server.js";async function i(i){const a=t().disable("x-powered-by");if(i.setApp(a),i.isProd){const{root:s,publicDir:o,isSPA:n}=i.getParams();a.use(r()),n||a.use(((e,r,t)=>{"/index.html"===e.url&&(e.url="/index-not-found.html"),t()})),a.use(t.static(e.resolve(`${s}/${o}`),{index:!!n&&void 0}))}else{const e=await(await import("vite")).createServer({server:{middlewareMode:!0,watch:{usePolling:!0,interval:100}},appType:"custom",mode:i.mode});a.use(e.middlewares),i.setVite(e)}const n=o.init(i);return i.isSPA?a.use("*",((e,r,t)=>{(async()=>{try{const t=(await n.loadHtml(e)).join("");r.send(t)}catch(e){t(e)}})()})):(await n.onAppCreated(),a.use("*",((e,r,t)=>{(async()=>{try{const[{render:t,onRequest:s,...o},a]=await Promise.all([n.loadEntrypoint(),n.loadHtml(e)]),{appProps:p,hasEarlyHints:l}=await(s?.(e,r))??{},[m,c]=a,d={req:e,res:r,hasEarlyHints:l,appProps:p??{},html:{header:m,footer:c}};await t(i,d,o)}catch(e){t(e)}})()}))),{run:({version:e,isPrintInfo:r=!0}={})=>{const{port:t,host:o}=i.getParams();i.isHost&&!i.isProd&&(i.getVite().config.server.host=o);const n=a.listen(t,o,(()=>{r&&s(n,i,{version:e})}));return n}}}export{i as default};
|
|
2
2
|
//# sourceMappingURL=server.js.map
|
package/node/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sources":["../../src/node/server.ts"],"sourcesContent":["import type { Server } from 'node:net';\nimport path from 'path';\nimport compression from 'compression';\nimport express from 'express';\nimport printServerInfo from '@helpers/print-server-info';\nimport type { IRequestContext } from '@node/render';\nimport PrepareServer from '@services/prepare-server';\nimport type ServerConfig from '@services/server-config';\n\nexport interface ICreateServerOut {\n run: (options?: { version?: string; isPrintInfo?: boolean }) => Server;\n}\n\n/**\n * Create SSR server\n */\nasync function createServer(config: ServerConfig): Promise<ICreateServerOut> {\n const app = express();\n\n config.setApp(app);\n\n if (!config.isProd) {\n // Create Vite server in middleware mode and configure the app type as\n // 'custom', disabling Vite's own HTML serving logic so parent server\n // can take control\n const vite = await (\n await import('vite')\n ).createServer({\n server: {\n middlewareMode: true,\n watch: {\n // During tests, we edit the files too fast and sometimes chokidar\n // misses change events, so enforce polling for consistency\n usePolling: true,\n interval: 100,\n },\n },\n appType: 'custom',\n });\n\n // Use vite's connect instance as middleware\n app.use(vite.middlewares);\n\n config.setVite(vite);\n } else {\n const { root, publicDir, isSPA } = config.getParams();\n\n app.use(compression());\n\n if (!isSPA) {\n // ignore index.html file in SSR mode\n app.use((req, res, next) => {\n if (req.url === '/index.html') {\n req.url = '/index-not-found.html';\n }\n\n next();\n });\n }\n\n app.use(\n express.static(path.resolve(`${root}/${publicDir}`), {\n index: isSPA ? undefined : false,\n }),\n );\n }\n\n const prepareServer = PrepareServer.init(config);\n\n // SSR mode\n if (!config.isSPA) {\n await prepareServer.onAppCreated();\n\n app.use('*', (req, res, next) => {\n void (async () => {\n try {\n const [
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../../src/node/server.ts"],"sourcesContent":["import type { Server } from 'node:net';\nimport path from 'path';\nimport compression from 'compression';\nimport express from 'express';\nimport printServerInfo from '@helpers/print-server-info';\nimport type { IRequestContext } from '@node/render';\nimport PrepareServer from '@services/prepare-server';\nimport type ServerConfig from '@services/server-config';\n\nexport interface ICreateServerOut {\n run: (options?: { version?: string; isPrintInfo?: boolean }) => Server;\n}\n\n/**\n * Create SSR server\n */\nasync function createServer(config: ServerConfig): Promise<ICreateServerOut> {\n const app = express().disable('x-powered-by');\n\n config.setApp(app);\n\n if (!config.isProd) {\n // Create Vite server in middleware mode and configure the app type as\n // 'custom', disabling Vite's own HTML serving logic so parent server\n // can take control\n const vite = await (\n await import('vite')\n ).createServer({\n server: {\n middlewareMode: true,\n watch: {\n // During tests, we edit the files too fast and sometimes chokidar\n // misses change events, so enforce polling for consistency\n usePolling: true,\n interval: 100,\n },\n },\n appType: 'custom',\n mode: config.mode,\n });\n\n // Use vite's connect instance as middleware\n app.use(vite.middlewares);\n\n config.setVite(vite);\n } else {\n const { root, publicDir, isSPA } = config.getParams();\n\n app.use(compression());\n\n if (!isSPA) {\n // ignore index.html file in SSR mode\n app.use((req, res, next) => {\n if (req.url === '/index.html') {\n req.url = '/index-not-found.html';\n }\n\n next();\n });\n }\n\n app.use(\n express.static(path.resolve(`${root}/${publicDir}`), {\n index: isSPA ? undefined : false,\n }),\n );\n }\n\n const prepareServer = PrepareServer.init(config);\n\n // SSR mode\n if (!config.isSPA) {\n await prepareServer.onAppCreated();\n\n app.use('*', (req, res, next) => {\n void (async () => {\n try {\n const [{ render, onRequest, ...renderParams }, clientHtml] = await Promise.all([\n prepareServer.loadEntrypoint(),\n prepareServer.loadHtml(req),\n ]);\n const { appProps, hasEarlyHints } = (await onRequest?.(req, res)) ?? {};\n const [header, footer] = clientHtml;\n\n const context: IRequestContext = {\n req,\n res,\n hasEarlyHints,\n appProps: appProps ?? {},\n html: { header, footer },\n };\n\n await render(config, context, renderParams);\n } catch (e) {\n next(e);\n }\n })();\n });\n } else {\n // SPA mode, redirect any request to index.html\n app.use('*', (req, res, next) => {\n void (async () => {\n try {\n const html = (await prepareServer.loadHtml(req)).join('');\n\n res.send(html);\n } catch (e) {\n next(e);\n }\n })();\n });\n }\n\n return {\n run: ({ version, isPrintInfo = true } = {}): Server => {\n const { port, host } = config.getParams();\n\n // update resolved host for print network link\n if (config.isHost && !config.isProd) {\n config.getVite()!.config.server.host = host;\n }\n\n const server = app.listen(port, host, () => {\n if (!isPrintInfo) {\n return;\n }\n\n void printServerInfo(server, config, { version });\n });\n\n return server;\n },\n };\n}\n\nexport default createServer;\n"],"names":["async","createServer","config","app","express","disable","setApp","isProd","root","publicDir","isSPA","getParams","use","compression","req","res","next","url","static","path","resolve","index","undefined","vite","import","server","middlewareMode","watch","usePolling","interval","appType","mode","middlewares","setVite","prepareServer","PrepareServer","init","html","loadHtml","join","send","e","onAppCreated","render","onRequest","renderParams","clientHtml","Promise","all","loadEntrypoint","appProps","hasEarlyHints","header","footer","context","run","version","isPrintInfo","port","host","isHost","getVite","listen","printServerInfo"],"mappings":"kKAgBAA,eAAeC,EAAaC,GAC1B,MAAMC,EAAMC,IAAUC,QAAQ,gBAI9B,GAFAH,EAAOI,OAAOH,GAETD,EAAOK,OAwBL,CACL,MAAMC,KAAEA,EAAIC,UAAEA,EAASC,MAAEA,GAAUR,EAAOS,YAE1CR,EAAIS,IAAIC,KAEHH,GAEHP,EAAIS,KAAI,CAACE,EAAKC,EAAKC,KACD,gBAAZF,EAAIG,MACNH,EAAIG,IAAM,yBAGZD,GAAM,IAIVb,EAAIS,IACFR,EAAQc,OAAOC,EAAKC,QAAQ,GAAGZ,KAAQC,KAAc,CACnDY,QAAOX,QAAQY,IAGpB,KA7CmB,CAIlB,MAAMC,cACEC,OAAO,SACbvB,aAAa,CACbwB,OAAQ,CACNC,gBAAgB,EAChBC,MAAO,CAGLC,YAAY,EACZC,SAAU,MAGdC,QAAS,SACTC,KAAM7B,EAAO6B,OAIf5B,EAAIS,IAAIW,EAAKS,aAEb9B,EAAO+B,QAAQV,EAChB,CAuBD,MAAMW,EAAgBC,EAAcC,KAAKlC,GA6CzC,OA1CKA,EAAOQ,MA6BVP,EAAIS,IAAI,KAAK,CAACE,EAAKC,EAAKC,KACjB,WACH,IACE,MAAMqB,SAAcH,EAAcI,SAASxB,IAAMyB,KAAK,IAEtDxB,EAAIyB,KAAKH,EACV,CAAC,MAAOI,GACPzB,EAAKyB,EACN,CACF,EARI,EAQD,WArCAP,EAAcQ,eAEpBvC,EAAIS,IAAI,KAAK,CAACE,EAAKC,EAAKC,KACjB,WACH,IACE,OAAO2B,OAAEA,EAAMC,UAAEA,KAAcC,GAAgBC,SAAoBC,QAAQC,IAAI,CAC7Ed,EAAce,iBACdf,EAAcI,SAASxB,MAEnBoC,SAAEA,EAAQC,cAAEA,SAAyBP,IAAY9B,EAAKC,KAAS,IAC9DqC,EAAQC,GAAUP,EAEnBQ,EAA2B,CAC/BxC,MACAC,MACAoC,gBACAD,SAAUA,GAAY,CAAE,EACxBb,KAAM,CAAEe,SAAQC,iBAGZV,EAAOzC,EAAQoD,EAAST,EAC/B,CAAC,MAAOJ,GACPzB,EAAKyB,EACN,CACF,EArBI,EAqBD,KAiBD,CACLc,IAAK,EAAGC,UAASC,eAAc,GAAS,CAAA,KACtC,MAAMC,KAAEA,EAAIC,KAAEA,GAASzD,EAAOS,YAG1BT,EAAO0D,SAAW1D,EAAOK,SAC3BL,EAAO2D,UAAW3D,OAAOuB,OAAOkC,KAAOA,GAGzC,MAAMlC,EAAStB,EAAI2D,OAAOJ,EAAMC,GAAM,KAC/BF,GAIAM,EAAgBtC,EAAQvB,EAAQ,CAAEsD,WAAU,IAGnD,OAAO/B,CAAM,EAGnB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lomray/vite-ssr-boost",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.21",
|
|
4
4
|
"description": "Vite plugin for create awesome SSR or SPA applications on React.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"commander": "^10.0.1",
|
|
37
37
|
"compression": "^1.7.4",
|
|
38
38
|
"express": "^4.18.2",
|
|
39
|
-
"
|
|
40
|
-
"react-
|
|
39
|
+
"hjson": "^3.2.2",
|
|
40
|
+
"hoist-non-react-statics": "^3.3.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@commitlint/cli": "^17.6.5",
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"@lomray/prettier-config": "^1.2.0",
|
|
47
47
|
"@rollup/plugin-terser": "^0.4.3",
|
|
48
48
|
"@types/compression": "^1.7.2",
|
|
49
|
-
"@types/
|
|
49
|
+
"@types/hjson": "^2.4.3",
|
|
50
|
+
"@types/hoist-non-react-statics": "^3.3.1",
|
|
50
51
|
"@types/react-dom": "^18.2.5",
|
|
51
52
|
"@typescript-eslint/eslint-plugin": "^5.59.11",
|
|
52
53
|
"@zerollup/ts-transform-paths": "^1.7.18",
|
|
@@ -68,6 +69,9 @@
|
|
|
68
69
|
"typescript": "^4.9.5"
|
|
69
70
|
},
|
|
70
71
|
"peerDependencies": {
|
|
72
|
+
"@types/express": "^4.17.17",
|
|
73
|
+
"react-dom": ">=18.2.0",
|
|
74
|
+
"react-router-dom": ">=6.12.1",
|
|
71
75
|
"vite": "^4.3.9"
|
|
72
76
|
},
|
|
73
77
|
"bin": {
|
package/plugin.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
import { ICliContext } from "./constants/cli-context.js";
|
|
3
|
+
import { IPluginOptions as IMakeAliasesPluginOptions } from "./plugins/make-aliases.js";
|
|
3
4
|
interface IPluginOptions {
|
|
4
5
|
indexFile?: string;
|
|
5
6
|
serverFile?: string;
|
|
6
|
-
|
|
7
|
+
preloadAssets?: boolean;
|
|
8
|
+
tsconfigAliases?: boolean | IMakeAliasesPluginOptions;
|
|
7
9
|
customShortcuts?: {
|
|
8
10
|
key: string;
|
|
9
11
|
description: string;
|
|
@@ -12,8 +14,8 @@ interface IPluginOptions {
|
|
|
12
14
|
}[];
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
|
-
* Init
|
|
17
|
+
* Init plugin
|
|
16
18
|
* @constructor
|
|
17
19
|
*/
|
|
18
|
-
declare function
|
|
19
|
-
export {
|
|
20
|
+
declare function ViteSsrBoostPlugin(options?: IPluginOptions): Plugin[];
|
|
21
|
+
export { ViteSsrBoostPlugin as default, IPluginOptions };
|
package/plugin.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"node:path";import
|
|
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};
|
|
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';\n\nexport interface IPluginOptions {\n indexFile?: string; // default: index.html\n serverFile?: string; // default: server.ts\n
|
|
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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
interface IPluginOptions {
|
|
3
|
+
root?: string;
|
|
4
|
+
tsconfig?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Read tsconfig file and set vite aliases
|
|
8
|
+
* @constructor
|
|
9
|
+
*/
|
|
10
|
+
declare function ViteMakeAliasesPlugin(options?: IPluginOptions): Plugin;
|
|
11
|
+
export { ViteMakeAliasesPlugin as default, IPluginOptions };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"node:fs";import o from"node:path";import s from"node:process";import r from"hjson";import t from"../constants/plugin-name.js";import n from"../helpers/vite-aliases.js";const i=`${t}-make-aliases`,a=e=>e.replace("/*","");function c(t={}){const{root:c,tsconfig:p}=t,f=c??s.cwd(),m=o.resolve(f,p??"tsconfig.json"),l=[];if(e.existsSync(m)){const o=r.parse(e.readFileSync(m,{encoding:"utf-8"})),s=o?.compilerOptions?.paths??{};Object.entries(s).forEach((([e,o])=>{l.push([a(e),a(o[0])])}))}else console.error(`${i}: tsconfig not exist in "${m}"`);return{name:i,config(e){if(l.length){const o=e.resolve??{},s=o.alias??[],r=Array.isArray(s)?s:Object.entries(s).map((([e,o])=>({find:e,replacement:o})));r.push(...n(l,`${f}/${e?.root??""}`)),e.resolve={...o,alias:r}}return e}}}export{c as default};
|
|
2
|
+
//# sourceMappingURL=make-aliases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"make-aliases.js","sources":["../../src/plugins/make-aliases.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport Hjson from 'hjson';\nimport type { Plugin } from 'vite';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport ViteAliases from '@helpers/vite-aliases';\n\nexport interface IPluginOptions {\n root?: string; // default: cwd()\n tsconfig?: string; // default: tsconfig.json\n}\n\nconst pluginName = `${PLUGIN_NAME}-make-aliases`;\nconst cleanupAlias = (str: string): string => str.replace('/*', '');\n\n/**\n * Read tsconfig file and set vite aliases\n * @constructor\n */\nfunction ViteMakeAliasesPlugin(options: IPluginOptions = {}): Plugin {\n const { root, tsconfig } = options;\n const projectRoot = root ?? process.cwd();\n const tsconfigPath = path.resolve(projectRoot, tsconfig ?? 'tsconfig.json');\n const aliases: [string, string][] = [];\n\n if (!fs.existsSync(tsconfigPath)) {\n console.error(`${pluginName}: tsconfig not exist in \"${tsconfigPath}\"`);\n } else {\n const tsJson = Hjson.parse(fs.readFileSync(tsconfigPath, { encoding: 'utf-8' }));\n const paths: Record<string, string[]> = tsJson?.compilerOptions?.paths ?? {};\n\n Object.entries(paths).forEach(([alias, aliasPaths]) => {\n aliases.push([cleanupAlias(alias), cleanupAlias(aliasPaths[0])]);\n });\n }\n\n return {\n name: pluginName,\n config(config) {\n if (aliases.length) {\n const resolveConfig = config.resolve ?? {};\n const defaultAliases = resolveConfig.alias ?? [];\n const normalizedAliases = Array.isArray(defaultAliases)\n ? defaultAliases\n : Object.entries(defaultAliases).map(([find, val]) => ({ find, replacement: val }));\n\n normalizedAliases.push(...ViteAliases(aliases, `${projectRoot}/${config?.root ?? ''}`));\n\n config.resolve = {\n ...resolveConfig,\n alias: normalizedAliases,\n };\n }\n\n return config;\n },\n };\n}\n\nexport default ViteMakeAliasesPlugin;\n"],"names":["pluginName","PLUGIN_NAME","cleanupAlias","str","replace","ViteMakeAliasesPlugin","options","root","tsconfig","projectRoot","process","cwd","tsconfigPath","path","resolve","aliases","fs","existsSync","tsJson","Hjson","parse","readFileSync","encoding","paths","compilerOptions","Object","entries","forEach","alias","aliasPaths","push","console","error","name","config","length","resolveConfig","defaultAliases","normalizedAliases","Array","isArray","map","find","val","replacement","ViteAliases"],"mappings":"sLAaA,MAAMA,EAAa,GAAGC,iBAChBC,EAAgBC,GAAwBA,EAAIC,QAAQ,KAAM,IAMhE,SAASC,EAAsBC,EAA0B,IACvD,MAAMC,KAAEA,EAAIC,SAAEA,GAAaF,EACrBG,EAAcF,GAAQG,EAAQC,MAC9BC,EAAeC,EAAKC,QAAQL,EAAaD,GAAY,iBACrDO,EAA8B,GAEpC,GAAKC,EAAGC,WAAWL,GAEZ,CACL,MAAMM,EAASC,EAAMC,MAAMJ,EAAGK,aAAaT,EAAc,CAAEU,SAAU,WAC/DC,EAAkCL,GAAQM,iBAAiBD,OAAS,CAAA,EAE1EE,OAAOC,QAAQH,GAAOI,SAAQ,EAAEC,EAAOC,MACrCd,EAAQe,KAAK,CAAC5B,EAAa0B,GAAQ1B,EAAa2B,EAAW,KAAK,GAEnE,MARCE,QAAQC,MAAM,GAAGhC,6BAAsCY,MAUzD,MAAO,CACLqB,KAAMjC,EACNkC,OAAOA,GACL,GAAInB,EAAQoB,OAAQ,CAClB,MAAMC,EAAgBF,EAAOpB,SAAW,GAClCuB,EAAiBD,EAAcR,OAAS,GACxCU,EAAoBC,MAAMC,QAAQH,GACpCA,EACAZ,OAAOC,QAAQW,GAAgBI,KAAI,EAAEC,EAAMC,MAAU,CAAED,OAAME,YAAaD,MAE9EL,EAAkBR,QAAQe,EAAY9B,EAAS,GAAGN,KAAeyB,GAAQ3B,MAAQ,OAEjF2B,EAAOpB,QAAU,IACZsB,EACHR,MAAOU,EAEV,CAED,OAAOJ,CACR,EAEL"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
interface IPluginOptions {
|
|
3
|
+
isSSR?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Add possibility to export route components like FCRoute or FCCRoute
|
|
7
|
+
* USAGE: { path: '/', lazyNR: () => import('./pages/home') }
|
|
8
|
+
* @see FCRoute
|
|
9
|
+
* @see FCCRoute
|
|
10
|
+
* @constructor
|
|
11
|
+
*/
|
|
12
|
+
declare function ViteNormalizeRouterPlugin(options?: IPluginOptions): Plugin;
|
|
13
|
+
export { ViteNormalizeRouterPlugin as default, IPluginOptions };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{extname as r}from"node:path";import o from"../constants/plugin-name.js";import s from"../helpers/is-route-file.js";const e=r=>`import n from '${o}/helpers/import-route';${r}`.replace(/(lazyNR|lazy)(:\s*)(\(\)\s*=>\s*import\(([^)]+)\))/gs,"lazy$2()=>n($3,$4)"),t=r=>`import n from '${o}/helpers/import-route';${r}`.replace(/(lazyNR)(:\s*)(\(\)\s*=>\s*import\(([^)]+)\))/gs,"lazy$2()=>n($3)");function n(n={}){const{isSSR:m=!1}=n;return{name:`${o}-normalize-route`,transform:(o,n)=>{const p=r(n).split("?")[0];if(!n.includes("node_modules")&&[".js",".ts",".tsx"].includes(p)&&s(o))return{code:m?e(o):t(o),map:{mappings:""}}}}}export{n as default};
|
|
2
|
+
//# sourceMappingURL=normalize-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize-route.js","sources":["../../src/plugins/normalize-route.ts"],"sourcesContent":["import { extname } from 'node:path';\nimport type { Plugin } from 'vite';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport isRoutesFile from '@helpers/is-route-file';\n\nexport interface IPluginOptions {\n isSSR?: boolean;\n}\n\n/**\n * Add normalize wrapper to lazy imports for server build\n */\nconst normalizeRoutesSSR = (code: string): string =>\n `import n from '${PLUGIN_NAME}/helpers/import-route';${code}`.replace(\n /(lazyNR|lazy)(:\\s*)(\\(\\)\\s*=>\\s*import\\(([^)]+)\\))/gs,\n 'lazy$2()=>n($3,$4)',\n );\n\n/**\n * Add normalize wrapper to lazy imports for client build\n */\nconst normalizeRoutes = (code: string): string =>\n `import n from '${PLUGIN_NAME}/helpers/import-route';${code}`.replace(\n /(lazyNR)(:\\s*)(\\(\\)\\s*=>\\s*import\\(([^)]+)\\))/gs,\n 'lazy$2()=>n($3)',\n );\n\n/**\n * Add possibility to export route components like FCRoute or FCCRoute\n * USAGE: { path: '/', lazyNR: () => import('./pages/home') }\n * @see FCRoute\n * @see FCCRoute\n * @constructor\n */\nfunction ViteNormalizeRouterPlugin(options: IPluginOptions = {}): Plugin {\n const { isSSR = false } = options;\n\n return {\n name: `${PLUGIN_NAME}-normalize-route`,\n transform: (code, id) => {\n const extName = extname(id).split('?')[0]!;\n\n if (\n id.includes('node_modules') ||\n !['.js', '.ts', '.tsx'].includes(extName) ||\n !isRoutesFile(code)\n ) {\n return;\n }\n\n return {\n code: isSSR ? normalizeRoutesSSR(code) : normalizeRoutes(code),\n map: { mappings: '' },\n };\n },\n };\n}\n\nexport default ViteNormalizeRouterPlugin;\n"],"names":["normalizeRoutesSSR","code","PLUGIN_NAME","replace","normalizeRoutes","ViteNormalizeRouterPlugin","options","isSSR","name","transform","id","extName","extname","split","includes","isRoutesFile","map","mappings"],"mappings":"0HAYA,MAAMA,EAAsBC,GAC1B,kBAAkBC,2BAAqCD,IAAOE,QAC5D,uDACA,sBAMEC,EAAmBH,GACvB,kBAAkBC,2BAAqCD,IAAOE,QAC5D,kDACA,mBAUJ,SAASE,EAA0BC,EAA0B,IAC3D,MAAMC,MAAEA,GAAQ,GAAUD,EAE1B,MAAO,CACLE,KAAM,GAAGN,oBACTO,UAAW,CAACR,EAAMS,KAChB,MAAMC,EAAUC,EAAQF,GAAIG,MAAM,KAAK,GAEvC,IACEH,EAAGI,SAAS,iBACX,CAAC,MAAO,MAAO,QAAQA,SAASH,IAChCI,EAAad,GAKhB,MAAO,CACLA,KAAMM,EAAQP,EAAmBC,GAAQG,EAAgBH,GACzDe,IAAK,CAAEC,SAAU,IAClB,EAGP"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { Request } from 'express';
|
|
2
|
+
import { TRouteObject } from "../interfaces/route-object.js";
|
|
2
3
|
import { IEntrypointOptions } from "../node/entry.js";
|
|
3
4
|
import { TRender } from "../node/render.js";
|
|
4
5
|
import ServerConfig from "./server-config.js";
|
|
5
6
|
interface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {
|
|
6
7
|
render: TRender;
|
|
8
|
+
routes: TRouteObject[];
|
|
9
|
+
abortDelay?: number;
|
|
7
10
|
onRequest?: IEntrypointOptions<TAppProps>['onRequest'];
|
|
8
11
|
onRouterReady?: IEntrypointOptions<TAppProps>['onRouterReady'];
|
|
9
12
|
onShellReady?: IEntrypointOptions<TAppProps>['onShellReady'];
|
|
@@ -35,11 +38,9 @@ declare class PrepareServer {
|
|
|
35
38
|
protected html: string;
|
|
36
39
|
/**
|
|
37
40
|
* @constructor
|
|
38
|
-
* @protected
|
|
39
41
|
*/
|
|
40
42
|
/**
|
|
41
43
|
* @constructor
|
|
42
|
-
* @protected
|
|
43
44
|
*/
|
|
44
45
|
protected constructor(config: ServerConfig);
|
|
45
46
|
/**
|
|
@@ -55,7 +56,7 @@ declare class PrepareServer {
|
|
|
55
56
|
/**
|
|
56
57
|
* Resolve and return entrypoint params
|
|
57
58
|
*/
|
|
58
|
-
loadEntrypoint(): Promise<IPrepareServerEntrypointLoadOut>;
|
|
59
|
+
loadEntrypoint(shouldInit?: boolean): Promise<IPrepareServerEntrypointLoadOut>;
|
|
59
60
|
/**
|
|
60
61
|
* Load and return html shell
|
|
61
62
|
*/
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import t from"fs";import e from"path";class
|
|
1
|
+
import t from"fs";import e from"node:process";import r from"path";import i from"chalk";class o{config;entrypoint;onServerCreated;html;constructor(t){this.config=t}static init(t){return new o(t)}async loadEntrypoint(t=!0){if(this.entrypoint&&this.config.isProd)return this.entrypoint;const{root:o,isProd:n,serverFile:s}=this.config.getParams(),a=r.resolve(`${o}/${s}`);let l;try{l=n?(await import(a)).default:(await this.config.getVite().ssrLoadModule(a,{fixStacktrace:!0})).default}catch(t){if(t.message.includes("Cannot find module")&&t.message.includes("/build/"))return this.config.getLogger().error(i.red(`Before starting the server, you need to create a build: ${i.yellow("ssr-boost build")}`)),e.exit(1);throw t}!t&&l.init&&delete l.init;const{render:c,init:d,routes:h,abortDelay:f}=l,{onServerCreated:g,...m}=await(d?.({config:this.config}))??{};return this.entrypoint={render:c,routes:h,abortDelay:f,...m},this.onServerCreated=g,this.entrypoint}async loadHtml(e){const{isProd:i,root:o,indexFile:n}=this.config.getParams();this.html&&i||(this.html=t.readFileSync(r.resolve(`${o}/${n}`),"utf-8"));let s=this.html;return i||(s=(await this.config.getVite().transformIndexHtml(e.originalUrl,this.html)).replace(/(<script.+)(>[\s\S]+injectIntoGlobalHook.+)/,"$1async$2")),s.split("\x3c!--ssr-outlet--\x3e")}async onAppCreated(){return await this.loadEntrypoint(),await(this.onServerCreated?.(this.config.getApp())),this}}export{o as default};
|
|
2
2
|
//# sourceMappingURL=prepare-server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prepare-server.js","sources":["../../src/services/prepare-server.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport type { Request } from 'express';\nimport type { IEntrypointOptions, IPrepareRenderOut } from '@node/entry';\nimport type { TRender } from '@node/render';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {\n render: TRender;\n onRequest?: IEntrypointOptions<TAppProps>['onRequest'];\n onRouterReady?: IEntrypointOptions<TAppProps>['onRouterReady'];\n onShellReady?: IEntrypointOptions<TAppProps>['onShellReady'];\n onShellError?: IEntrypointOptions<TAppProps>['onShellError'];\n onResponse?: IEntrypointOptions<TAppProps>['onResponse'];\n onError?: IEntrypointOptions<TAppProps>['onError'];\n getState?: IEntrypointOptions<TAppProps>['getState'];\n}\n\n/**\n * Load server entrypoint and template\n * DEV MODE: refresh entrypoint and template\n */\nclass PrepareServer {\n /**\n * Server configuration\n */\n protected readonly config: ServerConfig;\n\n /**\n * Entrypoint resolved params\n */\n protected entrypoint?: IPrepareServerEntrypointLoadOut;\n\n /**\n * Hook which calls after express server created\n */\n protected onServerCreated?: IEntrypointOptions['onServerCreated'];\n\n /**\n * Html shell\n */\n protected html: string;\n\n /**\n * @constructor\n
|
|
1
|
+
{"version":3,"file":"prepare-server.js","sources":["../../src/services/prepare-server.ts"],"sourcesContent":["import fs from 'fs';\nimport process from 'node:process';\nimport path from 'path';\nimport chalk from 'chalk';\nimport type { Request } from 'express';\nimport type { TRouteObject } from '@interfaces/route-object';\nimport type { IEntrypointOptions, IPrepareRenderOut } from '@node/entry';\nimport type { TRender } from '@node/render';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {\n render: TRender;\n routes: TRouteObject[];\n abortDelay?: number;\n onRequest?: IEntrypointOptions<TAppProps>['onRequest'];\n onRouterReady?: IEntrypointOptions<TAppProps>['onRouterReady'];\n onShellReady?: IEntrypointOptions<TAppProps>['onShellReady'];\n onShellError?: IEntrypointOptions<TAppProps>['onShellError'];\n onResponse?: IEntrypointOptions<TAppProps>['onResponse'];\n onError?: IEntrypointOptions<TAppProps>['onError'];\n getState?: IEntrypointOptions<TAppProps>['getState'];\n}\n\n/**\n * Load server entrypoint and template\n * DEV MODE: refresh entrypoint and template\n */\nclass PrepareServer {\n /**\n * Server configuration\n */\n protected readonly config: ServerConfig;\n\n /**\n * Entrypoint resolved params\n */\n protected entrypoint?: IPrepareServerEntrypointLoadOut;\n\n /**\n * Hook which calls after express server created\n */\n protected onServerCreated?: IEntrypointOptions['onServerCreated'];\n\n /**\n * Html shell\n */\n protected html: string;\n\n /**\n * @constructor\n */\n protected constructor(config: ServerConfig) {\n this.config = config;\n }\n\n /**\n * Init service\n */\n public static init(config: ServerConfig): PrepareServer {\n return new PrepareServer(config);\n }\n\n /**\n * Resolve and return entrypoint params\n */\n public async loadEntrypoint(shouldInit = true): Promise<IPrepareServerEntrypointLoadOut> {\n // load server entrypoint each time only in development mode (for fast refresh)\n if (this.entrypoint && this.config.isProd) {\n return this.entrypoint;\n }\n\n const { root, isProd, serverFile } = this.config.getParams();\n const entrypointPath = path.resolve(`${root}/${serverFile}`);\n\n let resolvedEntrypoint: IPrepareRenderOut;\n\n try {\n if (!isProd) {\n resolvedEntrypoint = (\n await this.config.getVite()!.ssrLoadModule(entrypointPath, {\n fixStacktrace: true,\n })\n ).default;\n } else {\n resolvedEntrypoint = (await import(entrypointPath)).default;\n }\n } catch (e) {\n if (e.message.includes('Cannot find module') && e.message.includes('/build/')) {\n this.config\n .getLogger()\n .error(\n chalk.red(\n `Before starting the server, you need to create a build: ${chalk.yellow(\n 'ssr-boost build',\n )}`,\n ),\n );\n\n return process.exit(1);\n }\n\n throw e;\n }\n\n if (!shouldInit && resolvedEntrypoint.init) {\n delete resolvedEntrypoint.init;\n }\n\n const { render, init, routes, abortDelay } = resolvedEntrypoint;\n const { onServerCreated, ...renderParams } =\n (await init?.({\n config: this.config,\n })) ?? {};\n\n this.entrypoint = {\n render,\n routes,\n abortDelay,\n ...renderParams,\n };\n this.onServerCreated = onServerCreated;\n\n return this.entrypoint;\n }\n\n /**\n * Load and return html shell\n */\n public async loadHtml(req: Request): Promise<[string, string]> {\n const { isProd, root, indexFile } = this.config.getParams();\n\n if (!this.html || !isProd) {\n this.html = fs.readFileSync(path.resolve(`${root}/${indexFile}`), 'utf-8');\n }\n\n let modifiedHtml = this.html;\n\n if (!isProd) {\n // Apply Vite HTML transforms. This injects the Vite HMR client,\n // and also applies HTML transforms from Vite plugins, e.g. global\n // preambles from @vitejs/plugin-react\n modifiedHtml = (await this.config.getVite()!.transformIndexHtml(req.originalUrl, this.html))\n // Make vite script 'async'\n .replace(/(<script.+)(>[\\s\\S]+injectIntoGlobalHook.+)/, '$1async$2');\n }\n\n return modifiedHtml.split('<!--ssr-outlet-->') as [string, string];\n }\n\n /**\n * Run server created hook\n */\n public async onAppCreated(): Promise<PrepareServer> {\n await this.loadEntrypoint();\n await this.onServerCreated?.(this.config.getApp()!);\n\n return this;\n }\n}\n\nexport default PrepareServer;\n"],"names":["PrepareServer","config","entrypoint","onServerCreated","html","constructor","this","static","async","shouldInit","isProd","root","serverFile","getParams","entrypointPath","path","resolve","resolvedEntrypoint","import","default","getVite","ssrLoadModule","fixStacktrace","e","message","includes","getLogger","error","chalk","red","yellow","process","exit","init","render","routes","abortDelay","renderParams","req","indexFile","fs","readFileSync","modifiedHtml","transformIndexHtml","originalUrl","replace","split","loadEntrypoint","getApp"],"mappings":"uFA2BA,MAAMA,EAIeC,OAKTC,WAKAC,gBAKAC,KAKVC,YAAsBJ,GACpBK,KAAKL,OAASA,CACf,CAKMM,YAAYN,GACjB,OAAO,IAAID,EAAcC,EAC1B,CAKMO,qBAAqBC,GAAa,GAEvC,GAAIH,KAAKJ,YAAcI,KAAKL,OAAOS,OACjC,OAAOJ,KAAKJ,WAGd,MAAMS,KAAEA,EAAID,OAAEA,EAAME,WAAEA,GAAeN,KAAKL,OAAOY,YAC3CC,EAAiBC,EAAKC,QAAQ,GAAGL,KAAQC,KAE/C,IAAIK,EAEJ,IAQIA,EAPGP,SAOyBQ,OAAOJ,IAAiBK,eAL5Cb,KAAKL,OAAOmB,UAAWC,cAAcP,EAAgB,CACzDQ,eAAe,KAEjBH,OAIL,CAAC,MAAOI,GACP,GAAIA,EAAEC,QAAQC,SAAS,uBAAyBF,EAAEC,QAAQC,SAAS,WAWjE,OAVAnB,KAAKL,OACFyB,YACAC,MACCC,EAAMC,IACJ,2DAA2DD,EAAME,OAC/D,uBAKDC,EAAQC,KAAK,GAGtB,MAAMT,CACP,EAEId,GAAcQ,EAAmBgB,aAC7BhB,EAAmBgB,KAG5B,MAAMC,OAAEA,EAAMD,KAAEA,EAAIE,OAAEA,EAAMC,WAAEA,GAAenB,GACvCd,gBAAEA,KAAoBkC,SACnBJ,IAAO,CACZhC,OAAQK,KAAKL,WACR,CAAA,EAUT,OARAK,KAAKJ,WAAa,CAChBgC,SACAC,SACAC,gBACGC,GAEL/B,KAAKH,gBAAkBA,EAEhBG,KAAKJ,UACb,CAKMM,eAAe8B,GACpB,MAAM5B,OAAEA,EAAMC,KAAEA,EAAI4B,UAAEA,GAAcjC,KAAKL,OAAOY,YAE3CP,KAAKF,MAASM,IACjBJ,KAAKF,KAAOoC,EAAGC,aAAa1B,EAAKC,QAAQ,GAAGL,KAAQ4B,KAAc,UAGpE,IAAIG,EAAepC,KAAKF,KAWxB,OATKM,IAIHgC,SAAsBpC,KAAKL,OAAOmB,UAAWuB,mBAAmBL,EAAIM,YAAatC,KAAKF,OAEnFyC,QAAQ,8CAA+C,cAGrDH,EAAaI,MAAM,0BAC3B,CAKMtC,qBAIL,aAHMF,KAAKyC,uBACLzC,KAAKH,kBAAkBG,KAAKL,OAAO+C,WAElC1C,IACR"}
|
|
@@ -6,6 +6,7 @@ interface IConfigOptions {
|
|
|
6
6
|
isHost?: boolean;
|
|
7
7
|
isOnlyClient?: boolean;
|
|
8
8
|
prodParams?: Partial<IConfigParams>;
|
|
9
|
+
mode?: string;
|
|
9
10
|
}
|
|
10
11
|
interface IConfigParams {
|
|
11
12
|
root: string;
|
|
@@ -15,7 +16,6 @@ interface IConfigParams {
|
|
|
15
16
|
isSPA: boolean;
|
|
16
17
|
indexFile: string;
|
|
17
18
|
serverFile: string;
|
|
18
|
-
abortDelay: number;
|
|
19
19
|
host: string;
|
|
20
20
|
port: number;
|
|
21
21
|
}
|
|
@@ -35,6 +35,10 @@ declare class ServerConfig {
|
|
|
35
35
|
* SPA mode
|
|
36
36
|
*/
|
|
37
37
|
readonly isSPA: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Env mode
|
|
40
|
+
*/
|
|
41
|
+
readonly mode: string;
|
|
38
42
|
/**
|
|
39
43
|
* Vite config - only for development
|
|
40
44
|
*/
|
|
@@ -61,7 +65,7 @@ declare class ServerConfig {
|
|
|
61
65
|
/**
|
|
62
66
|
* @constructor
|
|
63
67
|
*/
|
|
64
|
-
protected constructor({ isProd, isHost, isOnlyClient }: IConfigOptions, prodParams: Partial<IConfigParams>);
|
|
68
|
+
protected constructor({ isProd, isHost, isOnlyClient, mode }: IConfigOptions, prodParams: Partial<IConfigParams>);
|
|
65
69
|
/**
|
|
66
70
|
* Initialize service
|
|
67
71
|
*/
|
|
@@ -90,13 +94,6 @@ declare class ServerConfig {
|
|
|
90
94
|
* Set express server
|
|
91
95
|
*/
|
|
92
96
|
setApp(express: Express): void;
|
|
93
|
-
/**
|
|
94
|
-
* Set custom abort delay
|
|
95
|
-
*/
|
|
96
|
-
/**
|
|
97
|
-
* Set custom abort delay
|
|
98
|
-
*/
|
|
99
|
-
setAbortDelay(ms: number): void;
|
|
100
97
|
/**
|
|
101
98
|
* Return vite dev server
|
|
102
99
|
* NOTE: only on development mode
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import i from"node:path";import t from"../helpers/plugin-config.js";import r from"./logger.js";class s{isProd;isHost;isSPA;mode;vite;app;params;prodParams;logger;constructor({isProd:i=!1,isHost:t=!1,isOnlyClient:r=!1,mode:s="production"},e){this.isProd=i,this.isHost=t,this.isSPA=r,this.mode=s,this.prodParams={root:"./build",publicDir:"/client",indexFile:"/client/index.html",serverFile:"/server/server.js",host:"127.0.0.1",port:3e3,...e},this.makeParams()}static init(i={},t={}){return new s(i,t)}makeParams(){const t=this.getPluginConfig()??{},{config:s}=this.vite??{},e=s?.root??this.prodParams.root??"",o=s?.publicDir??this.prodParams.publicDir,a=new URL(import.meta.url),p=t.pluginPath??i.resolve(i.dirname(a.pathname),"../"),h=t.indexFile??this.prodParams.indexFile,n=t.serverFile??this.prodParams.serverFile,l="boolean"==typeof s?.server.host||this.isHost?"0.0.0.0":s?.server.host??this.prodParams.host,m=s?.server.port??(this.isProd?this.prodParams.port:5173);this.params={root:e,publicDir:o,pluginPath:p,indexFile:h,serverFile:n,host:l,port:m,isSPA:this.isSPA,isProd:this.isProd},this.logger=this.vite?.config.logger??new r}setVite(i){this.vite=i,this.makeParams()}setApp(i){this.app=i}getVite(){return this.vite}getApp(){return this.app}getPluginConfig(){return this.vite?t(this.vite.config):void 0}getParams(){return this.params}getLogger(){return this.logger}}export{s as default};
|
|
2
2
|
//# sourceMappingURL=server-config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-config.js","sources":["../../src/services/server-config.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Express } from 'express';\nimport type { Logger, ViteDevServer } from 'vite';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport DefaultLogger from '@services/logger';\n\ninterface IConfigOptions {\n isProd?: boolean;\n isHost?: boolean;\n isOnlyClient?: boolean; // SPA mode\n prodParams?: Partial<IConfigParams>;\n}\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
|
|
1
|
+
{"version":3,"file":"server-config.js","sources":["../../src/services/server-config.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Express } from 'express';\nimport type { Logger, ViteDevServer } from 'vite';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport DefaultLogger from '@services/logger';\n\ninterface IConfigOptions {\n isProd?: boolean;\n isHost?: boolean;\n isOnlyClient?: boolean; // SPA mode\n prodParams?: Partial<IConfigParams>;\n mode?: string;\n}\n\ninterface IConfigParams {\n root: string;\n publicDir: string;\n pluginPath: string;\n isProd: boolean;\n isSPA: boolean;\n indexFile: string;\n serverFile: string;\n host: string;\n port: number;\n}\n\n/**\n * Server config\n */\nclass ServerConfig {\n /**\n * Production build\n */\n public readonly isProd: boolean;\n\n /**\n * Server host mode\n */\n public readonly isHost: boolean;\n\n /**\n * SPA mode\n */\n public readonly isSPA: boolean;\n\n /**\n * Env mode\n */\n public readonly mode: string;\n\n /**\n * Vite config - only for development\n */\n protected vite?: ViteDevServer;\n\n /**\n * Express application\n */\n protected app?: Express;\n\n /**\n * Config params\n */\n protected params: IConfigParams;\n\n /**\n * Default production params\n */\n protected prodParams: Partial<IConfigParams>;\n\n /**\n * Vite logger for dev mode or console for production\n */\n protected logger: Logger;\n\n /**\n * @constructor\n */\n protected constructor(\n { isProd = false, isHost = false, isOnlyClient = false, mode = 'production' }: IConfigOptions,\n prodParams: Partial<IConfigParams>,\n ) {\n this.isProd = isProd;\n this.isHost = isHost;\n this.isSPA = isOnlyClient;\n this.mode = mode;\n this.prodParams = {\n root: './build',\n publicDir: '/client', // default for production,\n indexFile: '/client/index.html',\n serverFile: '/server/server.js',\n host: '127.0.0.1',\n port: 3000,\n ...prodParams,\n };\n\n this.makeParams();\n }\n\n /**\n * Initialize service\n */\n public static init(\n options: IConfigOptions = {},\n prodOptions: Partial<IConfigParams> = {},\n ): ServerConfig {\n return new ServerConfig(options, prodOptions);\n }\n\n /**\n * Make config params\n */\n protected makeParams(): void {\n const pluginConfig = (this.getPluginConfig() ?? {}) as Partial<IPluginConfig>;\n const { config } = this.vite ?? {};\n\n const root = config?.root ?? this.prodParams.root ?? '';\n const publicDir = config?.publicDir ?? this.prodParams.publicDir!;\n const dirInfo = new URL(import.meta.url);\n const pluginPath =\n pluginConfig.pluginPath ?? path.resolve(path.dirname(dirInfo.pathname), '../');\n const indexFile = pluginConfig.indexFile ?? this.prodParams.indexFile!;\n const serverFile = pluginConfig.serverFile ?? this.prodParams.serverFile!;\n const host =\n typeof config?.server.host === 'boolean' || this.isHost\n ? '0.0.0.0'\n : config?.server.host ?? this.prodParams.host!;\n const port = config?.server.port ?? (this.isProd ? this.prodParams.port! : 5173);\n\n this.params = {\n root,\n publicDir,\n pluginPath,\n indexFile,\n serverFile,\n host,\n port,\n isSPA: this.isSPA,\n isProd: this.isProd,\n };\n this.logger = this.vite?.config.logger ?? new DefaultLogger();\n }\n\n /**\n * Set vite server\n */\n public setVite(vite: ViteDevServer): void {\n this.vite = vite;\n\n this.makeParams();\n }\n\n /**\n * Set express server\n */\n public setApp(express: Express): void {\n this.app = express;\n }\n\n /**\n * Return vite dev server\n * NOTE: only on development mode\n */\n public getVite(): ViteDevServer | undefined {\n return this.vite;\n }\n\n /**\n * Return express server\n */\n public getApp(): Express | undefined {\n return this.app;\n }\n\n /**\n * return plugin config\n * NOTE: only on development mode\n */\n public getPluginConfig(): IPluginConfig | undefined {\n return this.vite ? getPluginConfig(this.vite.config) : undefined;\n }\n\n /**\n * Return config params\n */\n public getParams(): IConfigParams {\n return this.params;\n }\n\n /**\n * Get server logger\n */\n public getLogger(): Logger {\n return this.logger;\n }\n}\n\nexport default ServerConfig;\n"],"names":["ServerConfig","isProd","isHost","isSPA","mode","vite","app","params","prodParams","logger","constructor","isOnlyClient","this","root","publicDir","indexFile","serverFile","host","port","makeParams","static","options","prodOptions","pluginConfig","getPluginConfig","config","dirInfo","URL","url","pluginPath","path","resolve","dirname","pathname","server","DefaultLogger","setVite","setApp","express","getVite","getApp","undefined","getParams","getLogger"],"mappings":"+FA8BA,MAAMA,EAIYC,OAKAC,OAKAC,MAKAC,KAKNC,KAKAC,IAKAC,OAKAC,WAKAC,OAKVC,aACET,OAAEA,GAAS,EAAKC,OAAEA,GAAS,EAAKS,aAAEA,GAAe,EAAKP,KAAEA,EAAO,cAC/DI,GAEAI,KAAKX,OAASA,EACdW,KAAKV,OAASA,EACdU,KAAKT,MAAQQ,EACbC,KAAKR,KAAOA,EACZQ,KAAKJ,WAAa,CAChBK,KAAM,UACNC,UAAW,UACXC,UAAW,qBACXC,WAAY,oBACZC,KAAM,YACNC,KAAM,OACHV,GAGLI,KAAKO,YACN,CAKMC,YACLC,EAA0B,GAC1BC,EAAsC,CAAA,GAEtC,OAAO,IAAItB,EAAaqB,EAASC,EAClC,CAKSH,aACR,MAAMI,EAAgBX,KAAKY,mBAAqB,CAAE,GAC5CC,OAAEA,GAAWb,KAAKP,MAAQ,CAAA,EAE1BQ,EAAOY,GAAQZ,MAAQD,KAAKJ,WAAWK,MAAQ,GAC/CC,EAAYW,GAAQX,WAAaF,KAAKJ,WAAWM,UACjDY,EAAU,IAAIC,gBAAgBC,KAC9BC,EACJN,EAAaM,YAAcC,EAAKC,QAAQD,EAAKE,QAAQN,EAAQO,UAAW,OACpElB,EAAYQ,EAAaR,WAAaH,KAAKJ,WAAWO,UACtDC,EAAaO,EAAaP,YAAcJ,KAAKJ,WAAWQ,WACxDC,EAC2B,kBAAxBQ,GAAQS,OAAOjB,MAAsBL,KAAKV,OAC7C,UACAuB,GAAQS,OAAOjB,MAAQL,KAAKJ,WAAWS,KACvCC,EAAOO,GAAQS,OAAOhB,OAASN,KAAKX,OAASW,KAAKJ,WAAWU,KAAQ,MAE3EN,KAAKL,OAAS,CACZM,OACAC,YACAe,aACAd,YACAC,aACAC,OACAC,OACAf,MAAOS,KAAKT,MACZF,OAAQW,KAAKX,QAEfW,KAAKH,OAASG,KAAKP,MAAMoB,OAAOhB,QAAU,IAAI0B,CAC/C,CAKMC,QAAQ/B,GACbO,KAAKP,KAAOA,EAEZO,KAAKO,YACN,CAKMkB,OAAOC,GACZ1B,KAAKN,IAAMgC,CACZ,CAMMC,UACL,OAAO3B,KAAKP,IACb,CAKMmC,SACL,OAAO5B,KAAKN,GACb,CAMMkB,kBACL,OAAOZ,KAAKP,KAAOmB,EAAgBZ,KAAKP,KAAKoB,aAAUgB,CACxD,CAKMC,YACL,OAAO9B,KAAKL,MACb,CAKMoC,YACL,OAAO/B,KAAKH,MACb"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Socket } from 'node:net';
|
|
3
|
+
import { AgnosticDataRouteMatch } from '@remix-run/router/dist/utils';
|
|
4
|
+
import { RouteObject } from 'react-router-dom';
|
|
5
|
+
import { Alias } from 'vite';
|
|
6
|
+
import { IRequestContext } from "../node/render.js";
|
|
7
|
+
interface ISsrManifestParams {
|
|
8
|
+
alias?: Alias[];
|
|
9
|
+
buildDir?: string;
|
|
10
|
+
}
|
|
11
|
+
interface IManifest {
|
|
12
|
+
[path: string]: {
|
|
13
|
+
assets: string[];
|
|
14
|
+
css: string[];
|
|
15
|
+
file: string;
|
|
16
|
+
imports: string[];
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Working with SSR Manifest file
|
|
21
|
+
*/
|
|
22
|
+
declare class SsrManifest {
|
|
23
|
+
/**
|
|
24
|
+
* Singleton
|
|
25
|
+
*/
|
|
26
|
+
protected static instance: SsrManifest | null;
|
|
27
|
+
/**
|
|
28
|
+
* Project root path
|
|
29
|
+
*/
|
|
30
|
+
protected root: string;
|
|
31
|
+
/**
|
|
32
|
+
* Build dir
|
|
33
|
+
*/
|
|
34
|
+
protected buildDir?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Client manifest file name
|
|
37
|
+
*/
|
|
38
|
+
protected manifestName: string;
|
|
39
|
+
/**
|
|
40
|
+
* Assets manifest file name
|
|
41
|
+
*/
|
|
42
|
+
protected assetsManifest: string;
|
|
43
|
+
/**
|
|
44
|
+
* Vite resolve aliases
|
|
45
|
+
*/
|
|
46
|
+
protected alias?: Alias[];
|
|
47
|
+
/**
|
|
48
|
+
* Loaded assets manifest file
|
|
49
|
+
*/
|
|
50
|
+
protected routesAssets: Record<string, string[]> | null;
|
|
51
|
+
/**
|
|
52
|
+
* @constructor
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
55
|
+
* @constructor
|
|
56
|
+
*/
|
|
57
|
+
protected constructor(root: string, { buildDir, alias }?: ISsrManifestParams);
|
|
58
|
+
/**
|
|
59
|
+
* Get singleton instance
|
|
60
|
+
*/
|
|
61
|
+
/**
|
|
62
|
+
* Get singleton instance
|
|
63
|
+
*/
|
|
64
|
+
static get(root: string, params?: ISsrManifestParams): SsrManifest;
|
|
65
|
+
/**
|
|
66
|
+
* Get assets manifest file name
|
|
67
|
+
*/
|
|
68
|
+
/**
|
|
69
|
+
* Get assets manifest file name
|
|
70
|
+
*/
|
|
71
|
+
protected getAssetsManifestFile(): string;
|
|
72
|
+
/**
|
|
73
|
+
* Load client ssr manifest
|
|
74
|
+
*/
|
|
75
|
+
/**
|
|
76
|
+
* Load client ssr manifest
|
|
77
|
+
*/
|
|
78
|
+
protected loadClientManifest(): IManifest;
|
|
79
|
+
/**
|
|
80
|
+
* Load assets manifest
|
|
81
|
+
*/
|
|
82
|
+
/**
|
|
83
|
+
* Load assets manifest
|
|
84
|
+
*/
|
|
85
|
+
protected loadAssetsManifest(): Record<string, string[]>;
|
|
86
|
+
/**
|
|
87
|
+
* Recursive walk routes and return id's with route import path
|
|
88
|
+
*/
|
|
89
|
+
/**
|
|
90
|
+
* Recursive walk routes and return id's with route import path
|
|
91
|
+
*/
|
|
92
|
+
protected getRoutesIds(routes: RouteObject[], index?: string): Promise<Record<string, string>>;
|
|
93
|
+
/**
|
|
94
|
+
* Build routes manifest file
|
|
95
|
+
*/
|
|
96
|
+
/**
|
|
97
|
+
* Build routes manifest file
|
|
98
|
+
*/
|
|
99
|
+
buildRoutesManifest(shouldPreloadAssets: boolean): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Load aliases manifest
|
|
102
|
+
*/
|
|
103
|
+
/**
|
|
104
|
+
* Load aliases manifest
|
|
105
|
+
*/
|
|
106
|
+
protected getAliases(): Record<string, string>;
|
|
107
|
+
/**
|
|
108
|
+
* Return route postfix
|
|
109
|
+
*/
|
|
110
|
+
/**
|
|
111
|
+
* Return route postfix
|
|
112
|
+
*/
|
|
113
|
+
protected getRouteImportPostfix(): string[];
|
|
114
|
+
/**
|
|
115
|
+
* Normalized route path
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* Normalized route path
|
|
119
|
+
*/
|
|
120
|
+
protected normalizeRoutePath(routePath?: string): string | undefined;
|
|
121
|
+
/**
|
|
122
|
+
* Get provided route assets
|
|
123
|
+
*/
|
|
124
|
+
/**
|
|
125
|
+
* Get provided route assets
|
|
126
|
+
*/
|
|
127
|
+
getAssets(routes?: AgnosticDataRouteMatch[]): string[];
|
|
128
|
+
/**
|
|
129
|
+
* Get asset weight
|
|
130
|
+
*/
|
|
131
|
+
/**
|
|
132
|
+
* Get asset weight
|
|
133
|
+
*/
|
|
134
|
+
protected getAssetWeight(asset: string): number;
|
|
135
|
+
/**
|
|
136
|
+
* Get asset type
|
|
137
|
+
*/
|
|
138
|
+
/**
|
|
139
|
+
* Get asset type
|
|
140
|
+
*/
|
|
141
|
+
protected getAssetType(asset: string): string | null;
|
|
142
|
+
/**
|
|
143
|
+
* Write 103 Early Hits header
|
|
144
|
+
*/
|
|
145
|
+
/**
|
|
146
|
+
* Write 103 Early Hits header
|
|
147
|
+
*/
|
|
148
|
+
writeEarlyHits(assets: string[], socket: Socket): void;
|
|
149
|
+
/**
|
|
150
|
+
* Inject route assets to head html
|
|
151
|
+
*/
|
|
152
|
+
/**
|
|
153
|
+
* Inject route assets to head html
|
|
154
|
+
*/
|
|
155
|
+
injectAssets({ routerContext, html, res, hasEarlyHints }: IRequestContext): void;
|
|
156
|
+
}
|
|
157
|
+
export { SsrManifest as default };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import t from"node:fs";import s from"node:path";import e from"./prepare-server.js";import i from"./server-config.js";const r="\r\n";class n{static instance=null;root;buildDir;manifestName="manifest.json";assetsManifest="assets-manifest.json";alias;routesAssets=null;constructor(t,{buildDir:s,alias:e}={}){this.root=t,this.buildDir=s,this.alias=e}static get(t,s={}){return null===n.instance&&(n.instance=new n(t,s)),n.instance}getAssetsManifestFile(){return`${s.resolve(this.root,this.buildDir||"")}/server/${this.assetsManifest}`}loadClientManifest(){const e=s.resolve(this.root,`${this.buildDir||""}/client/${this.manifestName}`);if(!t.existsSync(e))return{};const i=JSON.parse(t.readFileSync(e,{encoding:"utf-8"}));return t.rmSync(e),i}loadAssetsManifest(){if(null!==this.routesAssets)return this.routesAssets;const s=this.getAssetsManifestFile();return t.existsSync(s)?(this.routesAssets=JSON.parse(t.readFileSync(s,{encoding:"utf-8"})),this.routesAssets):{}}async getRoutesIds(t,s){const e={};for(const i in t){const r=t[i],n=[s,i].filter(Boolean).join("-");if(r.lazy){const t=await r.lazy();e[n]=this.normalizeRoutePath(t?.pathId)}else r.children&&Object.assign(e,await this.getRoutesIds(r.children,n))}return e}async buildRoutesManifest(s){const r=i.init({isProd:!0}),n=e.init(r),a=this.loadClientManifest(),{routes:o}=await n.loadEntrypoint(!1),l=await this.getRoutesIds(o),c=this.getRouteImportPostfix(),h={};Object.entries(l).forEach((([t,e])=>{const i=c.find((t=>void 0!==a[`${e}${t}`])),r=a[`${e}${i||""}`],n=[...r?.assets??[],...r?.css??[],r.file,...(s?r?.imports??[]:[]).map((t=>a[t]?.file))].filter((t=>t&&this.getAssetType(t))).map((t=>`/${t}`));n&&(h[t]=n)})),t.writeFileSync(this.getAssetsManifestFile(),JSON.stringify(h,null,2),{encoding:"utf-8"})}getAliases(){const t={};return this.alias?.forEach((({find:s,replacement:e})=>{"string"==typeof s&&(t[s]=e)})),t}getRouteImportPostfix(){return["","/index"].map((t=>[".js",".ts",".tsx"].map((s=>`${t}${s}`)))).flat()}normalizeRoutePath(t){if(!t)return;let e="";if(t.startsWith("./")||t.startsWith("../"))e=s.resolve(this.root,t);else{const s=this.getAliases(),[i]=t.split("/");s[i]&&(e=t.replace(i,s[i]))}return e.replace(this.root,"").replace(/^\/|\/$/g,"")}getAssets(t){const s=t?.map((({route:t})=>t.id)).filter(Boolean)??[];if(!s.length)return[];const e=this.loadAssetsManifest();return s.map((t=>e[t])).filter(Boolean).flat()}getAssetWeight(t){switch(this.getAssetType(t)){case"style":return 1;case"script":return 2;default:return 3}}getAssetType(t){const s=t.split(".").at(-1)?.toLowerCase();switch(s){case"css":return"style";case"js":return"script";case"svg":case"jpg":case"jpeg":case"png":case"webp":case"gif":case"ico":return"image";case"ttf":case"otf":case"woff":case"woff2":return"font";default:return null}}writeEarlyHits(t,s){s.write(`HTTP/1.1 103 Early Hints${r}`),t.forEach((t=>{const e=this.getAssetType(t);e&&["style","script"].includes(e)&&s.write(`Link: <${t}>; rel=preload; as=${e}${r}`)})),s.write(r)}injectAssets({routerContext:t,html:s,res:e,hasEarlyHints:i=!0}){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};
|
|
2
|
+
//# sourceMappingURL=ssr-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr-manifest.js","sources":["../../src/services/ssr-manifest.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { Socket } from 'node:net';\nimport path from 'node:path';\nimport type { AgnosticDataRouteMatch } from '@remix-run/router/dist/utils';\nimport type { RouteObject } from 'react-router-dom';\nimport type { Alias } from 'vite';\nimport type { IRequestContext } from '@node/render';\nimport PrepareServer from '@services/prepare-server';\nimport ServerConfig from '@services/server-config';\n\ninterface ISsrManifestParams {\n alias?: Alias[];\n buildDir?: string;\n}\n\ninterface IManifest {\n [path: string]: {\n assets: string[];\n css: string[];\n file: string;\n imports: string[];\n };\n}\n\nconst CRLF = '\\r\\n';\n\n/**\n * Working with SSR Manifest file\n */\nclass SsrManifest {\n /**\n * Singleton\n */\n protected static instance: SsrManifest | null = null;\n\n /**\n * Project root path\n */\n protected root: string;\n\n /**\n * Build dir\n */\n protected buildDir?: string;\n\n /**\n * Client manifest file name\n */\n protected manifestName = 'manifest.json';\n\n /**\n * Assets manifest file name\n */\n protected assetsManifest = 'assets-manifest.json';\n\n /**\n * Vite resolve aliases\n */\n protected alias?: Alias[];\n\n /**\n * Loaded assets manifest file\n */\n protected routesAssets: Record<string, string[]> | null = null;\n\n /**\n * @constructor\n */\n protected constructor(root: string, { buildDir, alias }: ISsrManifestParams = {}) {\n this.root = root;\n this.buildDir = buildDir;\n this.alias = alias;\n }\n\n /**\n * Get singleton instance\n */\n public static get(root: string, params: ISsrManifestParams = {}): SsrManifest {\n if (SsrManifest.instance === null) {\n SsrManifest.instance = new SsrManifest(root, params);\n }\n\n return SsrManifest.instance;\n }\n\n /**\n * Get assets manifest file name\n */\n protected getAssetsManifestFile(): string {\n const outDir = path.resolve(this.root, this.buildDir || '');\n\n return `${outDir}/server/${this.assetsManifest}`;\n }\n\n /**\n * Load client ssr manifest\n */\n protected loadClientManifest(): IManifest {\n const clientSsrManifest = path.resolve(\n this.root,\n `${this.buildDir || ''}/client/${this.manifestName}`,\n );\n\n if (!fs.existsSync(clientSsrManifest)) {\n return {};\n }\n\n const result = JSON.parse(\n fs.readFileSync(clientSsrManifest, { encoding: 'utf-8' }),\n ) as IManifest;\n\n fs.rmSync(clientSsrManifest);\n\n return result;\n }\n\n /**\n * Load assets manifest\n */\n protected loadAssetsManifest(): Record<string, string[]> {\n if (this.routesAssets !== null) {\n return this.routesAssets;\n }\n\n const manifestFile = this.getAssetsManifestFile();\n\n if (!fs.existsSync(manifestFile)) {\n return {};\n }\n\n this.routesAssets = JSON.parse(fs.readFileSync(manifestFile, { encoding: 'utf-8' })) as Record<\n string,\n string[]\n >;\n\n return this.routesAssets;\n }\n\n /**\n * Recursive walk routes and return id's with route import path\n */\n protected async getRoutesIds(\n routes: RouteObject[],\n index?: string,\n ): Promise<Record<string, string>> {\n const result = {};\n\n for (const routeIndex in routes) {\n const route = routes[routeIndex];\n const routeId = [index, routeIndex].filter(Boolean).join('-');\n\n if (route.lazy) {\n const resolvedRoute = await route.lazy();\n\n result[routeId] = this.normalizeRoutePath(resolvedRoute?.['pathId'] as string);\n } else if (route.children) {\n Object.assign(result, await this.getRoutesIds(route.children, routeId));\n }\n }\n\n return result;\n }\n\n /**\n * Build routes manifest file\n */\n public async buildRoutesManifest(shouldPreloadAssets: boolean): Promise<void> {\n const serverConfig = ServerConfig.init({ isProd: true });\n const prepareServer = PrepareServer.init(serverConfig);\n const manifest = this.loadClientManifest();\n const { routes } = await prepareServer.loadEntrypoint(false);\n const routesPaths = await this.getRoutesIds(routes as RouteObject[]);\n const postfixes = this.getRouteImportPostfix();\n\n const result = {};\n\n // find route assets\n Object.entries(routesPaths).forEach(([routeId, routePath]) => {\n const routePostfix = postfixes.find((postfix) => {\n const filePath = `${routePath}${postfix}`;\n\n return manifest[filePath] !== undefined;\n });\n const routeFile = `${routePath}${routePostfix || ''}`;\n const routeMeta = manifest[routeFile];\n const routeAssets = [\n ...(routeMeta?.assets ?? []),\n ...(routeMeta?.css ?? []),\n routeMeta.file,\n ...(shouldPreloadAssets ? routeMeta?.imports ?? [] : []).map(\n (nestedAsset) => manifest[nestedAsset]?.file,\n ),\n ]\n .filter(\n (asset) =>\n // keep only js,css,image,fonts files\n asset && this.getAssetType(asset),\n )\n .map((asset) => `/${asset}`);\n\n if (routeAssets) {\n result[routeId] = routeAssets;\n }\n });\n\n fs.writeFileSync(this.getAssetsManifestFile(), JSON.stringify(result, null, 2), {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Load aliases manifest\n */\n protected getAliases(): Record<string, string> {\n const aliases = {};\n\n this.alias?.forEach(({ find, replacement }) => {\n if (typeof find !== 'string') {\n return;\n }\n\n aliases[find] = replacement;\n });\n\n return aliases;\n }\n\n /**\n * Return route postfix\n */\n protected getRouteImportPostfix(): string[] {\n return ['', '/index']\n .map((prefix) => ['.js', '.ts', '.tsx'].map((ext) => `${prefix}${ext}`))\n .flat();\n }\n\n /**\n * Normalized route path\n */\n protected normalizeRoutePath(routePath?: string): string | undefined {\n if (!routePath) {\n return;\n }\n\n let fullPath = '';\n\n // relative import\n if (routePath.startsWith('./') || routePath.startsWith('../')) {\n fullPath = path.resolve(this.root, routePath);\n } else {\n // alias import\n const aliases = this.getAliases();\n // get alias\n const [routeAlias] = routePath.split('/');\n\n if (aliases[routeAlias]) {\n fullPath = routePath.replace(routeAlias, aliases[routeAlias]);\n }\n }\n\n return fullPath.replace(this.root, '').replace(/^\\/|\\/$/g, '');\n }\n\n /**\n * Get provided route assets\n */\n public getAssets(routes?: AgnosticDataRouteMatch[]): string[] {\n const routeIds = routes?.map(({ route }) => route.id).filter(Boolean) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n const routesAssets = this.loadAssetsManifest();\n\n return routeIds\n .map((routeId) => routesAssets[routeId])\n .filter(Boolean)\n .flat();\n }\n\n /**\n * Get asset weight\n */\n protected getAssetWeight(asset: string): number {\n const type = this.getAssetType(asset);\n\n switch (type) {\n case 'style':\n return 1;\n\n case 'script':\n return 2;\n\n default:\n return 3;\n }\n }\n\n /**\n * Get asset type\n */\n protected getAssetType(asset: string): string | null {\n const ext = asset.split('.').at(-1)?.toLowerCase();\n\n switch (ext) {\n case 'css':\n return 'style';\n\n case 'js':\n return 'script';\n\n case 'svg':\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'webp':\n case 'gif':\n case 'ico':\n return 'image';\n\n case 'ttf':\n case 'otf':\n case 'woff':\n case 'woff2':\n return 'font';\n\n default:\n return null;\n }\n }\n\n /**\n * Write 103 Early Hits header\n */\n public writeEarlyHits(assets: string[], socket: Socket): void {\n socket.write(`HTTP/1.1 103 Early Hints${CRLF}`);\n assets.forEach((asset) => {\n const type = this.getAssetType(asset);\n\n if (!type || !['style', 'script'].includes(type)) {\n return;\n }\n\n socket.write(`Link: <${asset}>; rel=preload; as=${type}${CRLF}`);\n });\n socket.write(CRLF);\n }\n\n /**\n * Inject route assets to head html\n */\n public injectAssets({ routerContext, html, res, hasEarlyHints = true }: 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","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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"}
|