@lomray/vite-ssr-boost 4.0.0 → 6.0.0

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.
Files changed (54) hide show
  1. package/README.md +0 -3
  2. package/cli/helpers/keyboard-input.js.map +1 -1
  3. package/cli/helpers/vite-reset-cache.js.map +1 -1
  4. package/cli/run-amplify-build.js.map +1 -1
  5. package/cli/run-docker-build.js.map +1 -1
  6. package/cli/run-vercel-build.js.map +1 -1
  7. package/cli.js.map +1 -1
  8. package/components/navigate.js.map +1 -1
  9. package/components/render-client.d.ts +7 -0
  10. package/components/render-client.js +2 -0
  11. package/components/render-client.js.map +1 -0
  12. package/components/with-suspense.d.ts +1 -1
  13. package/components/with-suspense.js.map +1 -1
  14. package/constants/common.d.ts +2 -1
  15. package/constants/common.js +1 -1
  16. package/constants/common.js.map +1 -1
  17. package/helpers/dev-marker.js.map +1 -1
  18. package/helpers/get-server-state.js.map +1 -1
  19. package/helpers/import-route.d.ts +1 -1
  20. package/helpers/import-route.js +1 -1
  21. package/helpers/import-route.js.map +1 -1
  22. package/helpers/print-server-info.js.map +1 -1
  23. package/helpers/resolve-server-urls.js.map +1 -1
  24. package/helpers/serialize-errors.js.map +1 -1
  25. package/interfaces/route-object.d.ts +1 -0
  26. package/node/render.js.map +1 -1
  27. package/node/server.js.map +1 -1
  28. package/node/write-response.js.map +1 -1
  29. package/package.json +19 -18
  30. package/plugin.d.ts +0 -1
  31. package/plugin.js +1 -1
  32. package/plugin.js.map +1 -1
  33. package/plugins/handle-custom-entrypoint.js.map +1 -1
  34. package/plugins/make-aliases.js.map +1 -1
  35. package/plugins/normalize-route.d.ts +0 -3
  36. package/plugins/normalize-route.js +1 -1
  37. package/plugins/normalize-route.js.map +1 -1
  38. package/services/build.d.ts +1 -8
  39. package/services/build.js +1 -1
  40. package/services/build.js.map +1 -1
  41. package/services/logger.js.map +1 -1
  42. package/services/parse-routes.d.ts +5 -3
  43. package/services/parse-routes.js +1 -1
  44. package/services/parse-routes.js.map +1 -1
  45. package/services/path-normalize.js.map +1 -1
  46. package/services/prepare-server.js.map +1 -1
  47. package/services/server-api.js.map +1 -1
  48. package/services/server-config.js.map +1 -1
  49. package/services/ssr-manifest.d.ts +2 -9
  50. package/services/ssr-manifest.js +1 -1
  51. package/services/ssr-manifest.js.map +1 -1
  52. package/helpers/ssr-meta.d.ts +0 -18
  53. package/helpers/ssr-meta.js +0 -2
  54. package/helpers/ssr-meta.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lomray/vite-ssr-boost",
3
- "version": "4.0.0",
3
+ "version": "6.0.0",
4
4
  "description": "Vite plugin for create awesome SSR or SPA applications on React.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -30,50 +30,51 @@
30
30
  "lint:check": "eslint \"src/**/*.{ts,tsx,*.ts,*tsx}\"",
31
31
  "lint:format": "eslint --fix \"src/**/*.{ts,tsx,*.ts,*tsx}\"",
32
32
  "ts:check": "tsc --project ./tsconfig.json --skipLibCheck --noemit",
33
- "test": "vitest run"
33
+ "test": "vitest run",
34
+ "test:coverage": "vitest run --coverage"
34
35
  },
35
36
  "dependencies": {
36
- "chalk": "^5.3.0",
37
+ "chalk": "^5.4.1",
37
38
  "commander": "^12.1.0",
38
39
  "compression": "^1.7.5",
39
- "express": "^4.21.1",
40
+ "express": "^4.21.2",
40
41
  "hoist-non-react-statics": "^3.3.2",
41
42
  "json5": "^2.2.3"
42
43
  },
43
44
  "devDependencies": {
44
- "@commitlint/cli": "^19.5.0",
45
- "@commitlint/config-conventional": "^19.5.0",
45
+ "@commitlint/cli": "^19.6.1",
46
+ "@commitlint/config-conventional": "^19.6.0",
46
47
  "@lomray/eslint-config-react": "^5.0.6",
47
48
  "@lomray/prettier-config": "^2.0.1",
48
49
  "@rollup/plugin-terser": "^0.4.4",
49
- "@testing-library/react": "^15.0.7",
50
+ "@testing-library/react": "^16.1.0",
50
51
  "@types/babel__generator": "^7.6.8",
51
52
  "@types/babel__traverse": "^7.20.6",
52
- "@types/chai": "^5.0.0",
53
+ "@types/chai": "^5.0.1",
53
54
  "@types/compression": "^1.7.5",
54
- "@types/hoist-non-react-statics": "^3.3.5",
55
+ "@types/hoist-non-react-statics": "^3.3.6",
55
56
  "@types/react-dom": "^18.3.0",
56
57
  "@types/sinon": "^17.0.3",
57
58
  "@types/sinon-chai": "^4.0.0",
58
- "@vitest/coverage-v8": "^2.1.6",
59
+ "@vitest/coverage-v8": "^2.1.8",
59
60
  "@zerollup/ts-transform-paths": "^1.7.18",
60
- "chai": "^5.1.1",
61
+ "chai": "^5.1.2",
61
62
  "eslint": "^8.57.0",
62
- "husky": "^9.1.6",
63
- "jsdom": "^24.0.0",
64
- "lint-staged": "^15.2.10",
65
- "prettier": "^3.3.3",
66
- "rollup": "^4.24.0",
63
+ "husky": "^9.1.7",
64
+ "jsdom": "^26.0.0",
65
+ "lint-staged": "^15.3.0",
66
+ "prettier": "^3.4.2",
67
+ "rollup": "^4.30.1",
67
68
  "rollup-plugin-copy": "^3.5.0",
68
69
  "rollup-plugin-folder-input": "^1.0.1",
69
70
  "rollup-plugin-peer-deps-external": "^2.2.4",
70
71
  "rollup-plugin-preserve-shebangs": "^0.2.0",
71
72
  "rollup-plugin-ts": "^3.4.5",
72
- "semantic-release": "^24.1.2",
73
+ "semantic-release": "^24.2.1",
73
74
  "sinon": "^19.0.2",
74
75
  "sinon-chai": "^4.0.0",
75
76
  "typescript": "^5.3.3",
76
- "vitest": "^2.1.6"
77
+ "vitest": "^2.1.8"
77
78
  },
78
79
  "peerDependencies": {
79
80
  "@babel/generator": ">=7.23.0",
package/plugin.d.ts CHANGED
@@ -8,7 +8,6 @@ interface IPluginOptions {
8
8
  serverFile?: string;
9
9
  clientFile?: string;
10
10
  routesPath?: string;
11
- routesParsing?: 'node' | 'babel';
12
11
  spaIndex?: boolean | ICreateSPAIndex;
13
12
  tsconfigAliases?: boolean | IMakeAliasesPluginOptions;
14
13
  customShortcuts?: {
package/plugin.js CHANGED
@@ -1,2 +1,2 @@
1
- import e from"node:path";import i from"node:process";import s from"./constants/cli-actions.js";import o from"./constants/plugin-name.js";import n from"./plugins/create-spa-index.js";import{getCurrentEntrypoint as t,ViteHandleCustomEntrypointPlugin as r}from"./plugins/handle-custom-entrypoint.js";import p from"./plugins/make-aliases.js";import a from"./plugins/normalize-route.js";const l={indexFile:"index.html",serverFile:"server.ts",clientFile:"client.ts",routesParsing:"babel",tsconfigAliases:!0,spaIndex:!1};function u(u={}){const m=new URL(import.meta.url),d=global.viteBoostAction||i.env.SSR_BOOST_ACTION,c={...l,...u},f=t(c.entrypoint??[]),S="1"===i.env.SSR_BOOST_IS_SSR||d===s.dev,g=d===s.build,h=[{name:o,enforce:"pre",pluginOptions:{...c,pluginPath:e.dirname(m.pathname),action:d,isDev:d===s.dev},config:(e,{isSsrBuild:i})=>(e.define={...e.define??{},__IS_SSR__:f?"ssr"===f.type:S},e.build={...e.build??{}},i?{...e,...g?{appType:"custom"}:{},publicDir:!1}:(S&&g&&(e.build.manifest=!0),e))}],{tsconfigAliases:b,routesPath:v,routesParsing:_,spaIndex:y}=c;return b&&h.push(p("boolean"==typeof b?void 0:b)),y&&h.push(n("boolean"==typeof y?void 0:y)),f&&h.push(r({entrypoint:f})),h.push(a({isSSR:S,isBuild:g,routesPath:v,isNodeParsing:"node"===_})),h}export{u as default};
1
+ import e from"node:path";import i from"node:process";import o from"./constants/cli-actions.js";import t from"./constants/plugin-name.js";import n from"./plugins/create-spa-index.js";import{getCurrentEntrypoint as s,ViteHandleCustomEntrypointPlugin as r}from"./plugins/handle-custom-entrypoint.js";import p from"./plugins/make-aliases.js";import a from"./plugins/normalize-route.js";const l={indexFile:"index.html",serverFile:"server.ts",clientFile:"client.ts",tsconfigAliases:!0,spaIndex:!1};function m(m={}){const u=new URL(import.meta.url),d=global.viteBoostAction||i.env.SSR_BOOST_ACTION,c={...l,...m},f=s(c.entrypoint??[]),S="1"===i.env.SSR_BOOST_IS_SSR||d===o.dev,g=d===o.build,h=[{name:t,enforce:"pre",pluginOptions:{...c,pluginPath:e.dirname(u.pathname),action:d,isDev:d===o.dev},config:(e,{isSsrBuild:i})=>(e.define={...e.define??{},__IS_SSR__:f?"ssr"===f.type:S},e.build={...e.build??{}},i?{...e,...g?{appType:"custom"}:{},publicDir:!1}:(S&&g&&(e.build.manifest=!0),e))}],{tsconfigAliases:v,routesPath:_,spaIndex:b}=c;return v&&h.push(p("boolean"==typeof v?void 0:v)),b&&h.push(n("boolean"==typeof b?void 0:b)),f&&h.push(r({entrypoint:f})),h.push(a({isSSR:S,isBuild:g,routesPath:_})),h}export{m 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 process from 'node:process';\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 ViteCreateSPAIndexPlugin from '@plugins/create-spa-index';\nimport type { IPluginOptions as ICreateSPAIndex } from '@plugins/create-spa-index';\nimport {\n ViteHandleCustomEntrypointPlugin,\n getCurrentEntrypoint,\n} from '@plugins/handle-custom-entrypoint';\nimport type { IPluginOptions as IMakeAliasesPluginOptions } from '@plugins/make-aliases';\nimport ViteMakeAliasesPlugin from '@plugins/make-aliases';\nimport ViteNormalizeRouterPlugin from '@plugins/normalize-route';\nimport type { IBuildEntrypoint } from '@services/build';\n\nexport interface IPluginOptions {\n // default: index.html\n indexFile?: string;\n // default: server.ts\n serverFile?: string;\n // default: client.ts\n clientFile?: string;\n // Path contains routes declaration files (need to detect route files). default: undefined, e.g.: /routes/\n routesPath?: string;\n // how parse routes\n // node - import routes file directly and walk through\n // babel - use babel travers to walk through and avoid import routes file\n // default: babel\n routesParsing?: 'node' | 'babel';\n // Create additional SPA entrypoint: index-spa.html\n // Can be used for service worker: createHandlerBoundToURL(\"index-spa.html\")\n spaIndex?: boolean | ICreateSPAIndex;\n // Read aliases from tsconfig\n tsconfigAliases?: boolean | IMakeAliasesPluginOptions;\n customShortcuts?: {\n key: string;\n description: string;\n action: (cliContext: ICliContext) => Promise<void> | void;\n isOnlyDev?: boolean;\n }[];\n // Additional entry points for build\n entrypoint?: IBuildEntrypoint[];\n}\n\nconst defaultOptions: IPluginOptions = {\n indexFile: 'index.html',\n serverFile: 'server.ts',\n clientFile: 'client.ts',\n routesParsing: 'babel',\n tsconfigAliases: true,\n spaIndex: false,\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 entrypointConfig = getCurrentEntrypoint(mergedOptions.entrypoint ?? []);\n const isSSR = process.env.SSR_BOOST_IS_SSR === '1' || action === CliActions.dev;\n const isBuild = action === CliActions.build;\n\n const plugins: Plugin[] = [\n {\n name: PLUGIN_NAME,\n enforce: 'pre',\n // @ts-ignore save custom options\n pluginOptions: {\n ...mergedOptions,\n pluginPath: path.dirname(dirInfo.pathname),\n action,\n isDev: action === CliActions.dev,\n },\n\n config(config, { isSsrBuild }) {\n config.define = {\n ...(config.define ?? {}),\n __IS_SSR__: entrypointConfig ? entrypointConfig.type === 'ssr' : isSSR,\n };\n\n config.build = {\n ...(config.build ?? {}),\n };\n\n if (!isSsrBuild) {\n if (isSSR && isBuild) {\n config.build.manifest = true;\n }\n\n return config;\n }\n\n return {\n ...config,\n ...(isBuild ? { appType: 'custom' } : {}),\n publicDir: false,\n };\n },\n },\n ];\n\n const { tsconfigAliases, routesPath, routesParsing, spaIndex } = mergedOptions;\n\n if (tsconfigAliases) {\n plugins.push(\n ViteMakeAliasesPlugin(typeof tsconfigAliases === 'boolean' ? undefined : tsconfigAliases),\n );\n }\n\n if (spaIndex) {\n plugins.push(ViteCreateSPAIndexPlugin(typeof spaIndex === 'boolean' ? undefined : spaIndex));\n }\n\n if (entrypointConfig) {\n plugins.push(ViteHandleCustomEntrypointPlugin({ entrypoint: entrypointConfig }));\n }\n\n plugins.push(\n ViteNormalizeRouterPlugin({\n isSSR,\n isBuild,\n routesPath,\n isNodeParsing: routesParsing === 'node',\n }),\n );\n\n return plugins;\n}\n\nexport default ViteSsrBoostPlugin;\n"],"names":["defaultOptions","indexFile","serverFile","clientFile","routesParsing","tsconfigAliases","spaIndex","ViteSsrBoostPlugin","options","dirInfo","URL","url","action","global","viteBoostAction","process","env","SSR_BOOST_ACTION","mergedOptions","entrypointConfig","getCurrentEntrypoint","entrypoint","isSSR","SSR_BOOST_IS_SSR","CliActions","dev","isBuild","build","plugins","name","PLUGIN_NAME","enforce","pluginOptions","pluginPath","path","dirname","pathname","isDev","config","isSsrBuild","define","__IS_SSR__","type","appType","publicDir","manifest","routesPath","push","ViteMakeAliasesPlugin","undefined","ViteCreateSPAIndexPlugin","ViteHandleCustomEntrypointPlugin","ViteNormalizeRouterPlugin","isNodeParsing"],"mappings":"8XA8CA,MAAMA,EAAiC,CACrCC,UAAW,aACXC,WAAY,YACZC,WAAY,YACZC,cAAe,QACfC,iBAAiB,EACjBC,UAAU,GAOZ,SAASC,EAAmBC,EAA0B,IACpD,MAAMC,EAAU,IAAIC,gBAAgBC,KAC9BC,EAAUC,OAAOC,iBAAmBC,EAAQC,IAAIC,iBAChDC,EAAgC,IAAKlB,KAAmBQ,GACxDW,EAAmBC,EAAqBF,EAAcG,YAAc,IACpEC,EAAyC,MAAjCP,EAAQC,IAAIO,kBAA4BX,IAAWY,EAAWC,IACtEC,EAAUd,IAAWY,EAAWG,MAEhCC,EAAoB,CACxB,CACEC,KAAMC,EACNC,QAAS,MAETC,cAAe,IACVd,EACHe,WAAYC,EAAKC,QAAQ1B,EAAQ2B,UACjCxB,SACAyB,MAAOzB,IAAWY,EAAWC,KAG/Ba,OAAM,CAACA,GAAQC,WAAEA,MACfD,EAAOE,OAAS,IACVF,EAAOE,QAAU,GACrBC,WAAYtB,EAA6C,QAA1BA,EAAiBuB,KAAiBpB,GAGnEgB,EAAOX,MAAQ,IACTW,EAAOX,OAAS,IAGjBY,EAQE,IACFD,KACCZ,EAAU,CAAEiB,QAAS,UAAa,CAAA,EACtCC,WAAW,IAVPtB,GAASI,IACXY,EAAOX,MAAMkB,UAAW,GAGnBP,OAYTjC,gBAAEA,EAAeyC,WAAEA,EAAU1C,cAAEA,EAAaE,SAAEA,GAAaY,EAyBjE,OAvBIb,GACFuB,EAAQmB,KACNC,EAAiD,kBAApB3C,OAAgC4C,EAAY5C,IAIzEC,GACFsB,EAAQmB,KAAKG,EAA6C,kBAAb5C,OAAyB2C,EAAY3C,IAGhFa,GACFS,EAAQmB,KAAKI,EAAiC,CAAE9B,WAAYF,KAG9DS,EAAQmB,KACNK,EAA0B,CACxB9B,QACAI,UACAoB,aACAO,cAAiC,SAAlBjD,KAIZwB,CACT"}
1
+ {"version":3,"file":"plugin.js","sources":["../src/plugin.ts"],"sourcesContent":["import path from 'node:path';\nimport process from 'node:process';\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 ViteCreateSPAIndexPlugin from '@plugins/create-spa-index';\nimport type { IPluginOptions as ICreateSPAIndex } from '@plugins/create-spa-index';\nimport {\n ViteHandleCustomEntrypointPlugin,\n getCurrentEntrypoint,\n} from '@plugins/handle-custom-entrypoint';\nimport type { IPluginOptions as IMakeAliasesPluginOptions } from '@plugins/make-aliases';\nimport ViteMakeAliasesPlugin from '@plugins/make-aliases';\nimport ViteNormalizeRouterPlugin from '@plugins/normalize-route';\nimport type { IBuildEntrypoint } from '@services/build';\n\nexport interface IPluginOptions {\n // default: index.html\n indexFile?: string;\n // default: server.ts\n serverFile?: string;\n // default: client.ts\n clientFile?: string;\n // Path contains routes declaration files (need to detect route files). default: undefined, e.g.: /routes/\n routesPath?: string;\n // Create additional SPA entrypoint: index-spa.html\n // Can be used for service worker: createHandlerBoundToURL(\"index-spa.html\")\n spaIndex?: boolean | ICreateSPAIndex;\n // Read aliases from tsconfig\n tsconfigAliases?: boolean | IMakeAliasesPluginOptions;\n customShortcuts?: {\n key: string;\n description: string;\n action: (cliContext: ICliContext) => Promise<void> | void;\n isOnlyDev?: boolean;\n }[];\n // Additional entry points for build\n entrypoint?: IBuildEntrypoint[];\n}\n\nconst defaultOptions: IPluginOptions = {\n indexFile: 'index.html',\n serverFile: 'server.ts',\n clientFile: 'client.ts',\n tsconfigAliases: true,\n spaIndex: false,\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 entrypointConfig = getCurrentEntrypoint(mergedOptions.entrypoint ?? []);\n const isSSR = process.env.SSR_BOOST_IS_SSR === '1' || action === CliActions.dev;\n const isBuild = action === CliActions.build;\n\n const plugins: Plugin[] = [\n {\n name: PLUGIN_NAME,\n enforce: 'pre',\n // @ts-ignore save custom options\n pluginOptions: {\n ...mergedOptions,\n pluginPath: path.dirname(dirInfo.pathname),\n action,\n isDev: action === CliActions.dev,\n },\n\n config(config, { isSsrBuild }) {\n config.define = {\n ...(config.define ?? {}),\n __IS_SSR__: entrypointConfig ? entrypointConfig.type === 'ssr' : isSSR,\n };\n\n config.build = {\n ...(config.build ?? {}),\n };\n\n if (!isSsrBuild) {\n if (isSSR && isBuild) {\n config.build.manifest = true;\n }\n\n return config;\n }\n\n return {\n ...config,\n ...(isBuild ? { appType: 'custom' } : {}),\n publicDir: false,\n };\n },\n },\n ];\n\n const { tsconfigAliases, routesPath, spaIndex } = mergedOptions;\n\n if (tsconfigAliases) {\n plugins.push(\n ViteMakeAliasesPlugin(typeof tsconfigAliases === 'boolean' ? undefined : tsconfigAliases),\n );\n }\n\n if (spaIndex) {\n plugins.push(ViteCreateSPAIndexPlugin(typeof spaIndex === 'boolean' ? undefined : spaIndex));\n }\n\n if (entrypointConfig) {\n plugins.push(ViteHandleCustomEntrypointPlugin({ entrypoint: entrypointConfig }));\n }\n\n plugins.push(ViteNormalizeRouterPlugin({ isSSR, isBuild, routesPath }));\n\n return plugins;\n}\n\nexport default ViteSsrBoostPlugin;\n"],"names":["defaultOptions","indexFile","serverFile","clientFile","tsconfigAliases","spaIndex","ViteSsrBoostPlugin","options","dirInfo","URL","url","action","global","viteBoostAction","process","env","SSR_BOOST_ACTION","mergedOptions","entrypointConfig","getCurrentEntrypoint","entrypoint","isSSR","SSR_BOOST_IS_SSR","CliActions","dev","isBuild","build","plugins","name","PLUGIN_NAME","enforce","pluginOptions","pluginPath","path","dirname","pathname","isDev","config","isSsrBuild","define","__IS_SSR__","type","appType","publicDir","manifest","routesPath","push","ViteMakeAliasesPlugin","undefined","ViteCreateSPAIndexPlugin","ViteHandleCustomEntrypointPlugin","ViteNormalizeRouterPlugin"],"mappings":"8XAyCA,MAAMA,EAAiC,CACrCC,UAAW,aACXC,WAAY,YACZC,WAAY,YACZC,iBAAiB,EACjBC,UAAU,GAOZ,SAASC,EAAmBC,EAA0B,IACpD,MAAMC,EAAU,IAAIC,gBAAgBC,KAC9BC,EAAUC,OAAOC,iBAAmBC,EAAQC,IAAIC,iBAChDC,EAAgC,IAAKjB,KAAmBO,GACxDW,EAAmBC,EAAqBF,EAAcG,YAAc,IACpEC,EAAyC,MAAjCP,EAAQC,IAAIO,kBAA4BX,IAAWY,EAAWC,IACtEC,EAAUd,IAAWY,EAAWG,MAEhCC,EAAoB,CACxB,CACEC,KAAMC,EACNC,QAAS,MAETC,cAAe,IACVd,EACHe,WAAYC,EAAKC,QAAQ1B,EAAQ2B,UACjCxB,SACAyB,MAAOzB,IAAWY,EAAWC,KAG/Ba,OAAM,CAACA,GAAQC,WAAEA,MACfD,EAAOE,OAAS,IACVF,EAAOE,QAAU,GACrBC,WAAYtB,EAA6C,QAA1BA,EAAiBuB,KAAiBpB,GAGnEgB,EAAOX,MAAQ,IACTW,EAAOX,OAAS,IAGjBY,EAQE,IACFD,KACCZ,EAAU,CAAEiB,QAAS,UAAa,CAAA,EACtCC,WAAW,IAVPtB,GAASI,IACXY,EAAOX,MAAMkB,UAAW,GAGnBP,OAYTjC,gBAAEA,EAAeyC,WAAEA,EAAUxC,SAAEA,GAAaY,EAkBlD,OAhBIb,GACFuB,EAAQmB,KACNC,EAAiD,kBAApB3C,OAAgC4C,EAAY5C,IAIzEC,GACFsB,EAAQmB,KAAKG,EAA6C,kBAAb5C,OAAyB2C,EAAY3C,IAGhFa,GACFS,EAAQmB,KAAKI,EAAiC,CAAE9B,WAAYF,KAG9DS,EAAQmB,KAAKK,EAA0B,CAAE9B,QAAOI,UAASoB,gBAElDlB,CACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"handle-custom-entrypoint.js","sources":["../../src/plugins/handle-custom-entrypoint.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport type { Plugin } from 'vite';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport type { IBuildEntrypoint } from '@services/build';\n\nexport interface IPluginOptions {\n entrypoint: IBuildEntrypoint;\n}\n\nconst pluginName = `${PLUGIN_NAME}-handle-custom-entrypoint`;\n\n/**\n * Get current entrypoint name\n */\nconst getCurrentEntrypointName = (): string | undefined =>\n process.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME;\n\n/**\n * Set current entrypoint name\n */\nconst setCurrentEntrypointName = (name: string): void => {\n process.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME = name;\n};\n\n/**\n * Find current entrypoint by env\n */\nconst getCurrentEntrypoint = (\n entrypoint: IBuildEntrypoint[],\n currentEntrypointName = getCurrentEntrypointName(),\n): IBuildEntrypoint | null => {\n if (!entrypoint.length || !currentEntrypointName) {\n return null;\n }\n\n for (const entry of entrypoint) {\n if (entry.name === currentEntrypointName && !entry.serverFile) {\n return entry;\n }\n }\n\n return null;\n};\n\n/**\n * Replace entrypoint in html file\n */\nconst replaceEntrypoint = (code: string, originalPath: string, endpointPath: string): string => {\n const cleanOrigPath = originalPath.replace('./', '/');\n const cleanEndpointPath = endpointPath.replace('./', '/');\n\n return code.replace(cleanOrigPath, cleanEndpointPath);\n};\n\n/**\n * Return custom entrypoint instead default (index.html).\n *\n * E.g. for build multiple entrypoint\n * @constructor\n */\nfunction ViteHandleCustomEntrypointPlugin(options: IPluginOptions): Plugin {\n const { entrypoint } = options;\n let outPath = '';\n let origClientFile = '';\n\n return {\n name: pluginName,\n enforce: 'pre',\n /**\n * Apply only on build but not for SSR and only for custom entrypoint\n */\n apply(_, { isSsrBuild }): boolean {\n return !isSsrBuild && Boolean(entrypoint);\n },\n config(config) {\n const { indexFile } = entrypoint;\n const buildConfig = config.build ?? {};\n const indexFilePath = indexFile ? path.resolve(config.root ?? '', indexFile) : undefined;\n\n return {\n ...config,\n build: {\n ...buildConfig,\n rollupOptions: {\n ...(buildConfig.rollupOptions ?? {}),\n input: indexFilePath,\n },\n },\n };\n },\n configResolved(config) {\n const pluginConfig = config.plugins.find((plugin) => plugin.name === PLUGIN_NAME);\n\n outPath = path.resolve(config.root, config.build.outDir);\n // @ts-expect-error pluginOptions is custom param\n origClientFile = (pluginConfig.pluginOptions as Record<string, any>).clientFile as string;\n },\n transform(code, id) {\n if (id.endsWith('.html')) {\n const { clientFile } = entrypoint;\n\n if (clientFile) {\n return {\n code: replaceEntrypoint(code, origClientFile, clientFile),\n map: this.getCombinedSourcemap(),\n };\n }\n }\n\n return {\n code,\n map: this.getCombinedSourcemap(),\n };\n },\n /**\n * Development mode\n */\n transformIndexHtml(html, { originalUrl, server }): string {\n const { clientFile } = entrypoint;\n\n if (clientFile && server?.config.command === 'serve' && originalUrl?.endsWith('.html')) {\n return replaceEntrypoint(html, origClientFile, clientFile);\n }\n\n return html;\n },\n closeBundle() {\n const { indexFile } = entrypoint;\n\n if (!indexFile) {\n return;\n }\n\n const indexFilePath = path.resolve(outPath, path.basename(indexFile));\n\n if (fs.existsSync(indexFilePath)) {\n fs.renameSync(indexFilePath, path.resolve(outPath, 'index.html'));\n }\n },\n };\n}\n\nexport {\n ViteHandleCustomEntrypointPlugin,\n getCurrentEntrypoint,\n getCurrentEntrypointName,\n setCurrentEntrypointName,\n};\n"],"names":["pluginName","PLUGIN_NAME","getCurrentEntrypointName","process","env","SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME","setCurrentEntrypointName","name","getCurrentEntrypoint","entrypoint","currentEntrypointName","length","entry","serverFile","replaceEntrypoint","code","originalPath","endpointPath","cleanOrigPath","replace","cleanEndpointPath","ViteHandleCustomEntrypointPlugin","options","outPath","origClientFile","enforce","apply","_","isSsrBuild","Boolean","config","indexFile","buildConfig","build","indexFilePath","path","resolve","root","undefined","rollupOptions","input","configResolved","pluginConfig","plugins","find","plugin","outDir","pluginOptions","clientFile","transform","id","endsWith","map","this","getCombinedSourcemap","transformIndexHtml","html","originalUrl","server","command","closeBundle","basename","fs","existsSync","renameSync"],"mappings":"uHAWA,MAAMA,EAAa,GAAGC,6BAKhBC,EAA2B,IAC/BC,EAAQC,IAAIC,uCAKRC,EAA4BC,IAChCJ,EAAQC,IAAIC,uCAAyCE,CAAI,EAMrDC,EAAuB,CAC3BC,EACAC,EAAwBR,OAExB,IAAKO,EAAWE,SAAWD,EACzB,OAAO,KAGT,IAAK,MAAME,KAASH,EAClB,GAAIG,EAAML,OAASG,IAA0BE,EAAMC,WACjD,OAAOD,EAIX,OAAO,IAAI,EAMPE,EAAoB,CAACC,EAAcC,EAAsBC,KAC7D,MAAMC,EAAgBF,EAAaG,QAAQ,KAAM,KAC3CC,EAAoBH,EAAaE,QAAQ,KAAM,KAErD,OAAOJ,EAAKI,QAAQD,EAAeE,EAAkB,EASvD,SAASC,EAAiCC,GACxC,MAAMb,WAAEA,GAAea,EACvB,IAAIC,EAAU,GACVC,EAAiB,GAErB,MAAO,CACLjB,KAAMP,EACNyB,QAAS,MAITC,MAAK,CAACC,GAAGC,WAAEA,MACDA,GAAcC,QAAQpB,GAEhCqB,OAAOA,GACL,MAAMC,UAAEA,GAActB,EAChBuB,EAAcF,EAAOG,OAAS,GAC9BC,EAAgBH,EAAYI,EAAKC,QAAQN,EAAOO,MAAQ,GAAIN,QAAaO,EAE/E,MAAO,IACFR,EACHG,MAAO,IACFD,EACHO,cAAe,IACTP,EAAYO,eAAiB,GACjCC,MAAON,IAId,EACDO,eAAeX,GACb,MAAMY,EAAeZ,EAAOa,QAAQC,MAAMC,GAAWA,EAAOtC,OAASN,IAErEsB,EAAUY,EAAKC,QAAQN,EAAOO,KAAMP,EAAOG,MAAMa,QAEjDtB,EAAkBkB,EAAaK,cAAsCC,UACtE,EACDC,UAAUlC,EAAMmC,GACd,GAAIA,EAAGC,SAAS,SAAU,CACxB,MAAMH,WAAEA,GAAevC,EAEvB,GAAIuC,EACF,MAAO,CACLjC,KAAMD,EAAkBC,EAAMS,EAAgBwB,GAC9CI,IAAKC,KAAKC,uBAGf,CAED,MAAO,CACLvC,OACAqC,IAAKC,KAAKC,uBAEb,EAIDC,mBAAmBC,GAAMC,YAAEA,EAAWC,OAAEA,IACtC,MAAMV,WAAEA,GAAevC,EAEvB,OAAIuC,GAAyC,UAA3BU,GAAQ5B,OAAO6B,SAAuBF,GAAaN,SAAS,SACrErC,EAAkB0C,EAAMhC,EAAgBwB,GAG1CQ,CACR,EACDI,cACE,MAAM7B,UAAEA,GAActB,EAEtB,IAAKsB,EACH,OAGF,MAAMG,EAAgBC,EAAKC,QAAQb,EAASY,EAAK0B,SAAS9B,IAEtD+B,EAAGC,WAAW7B,IAChB4B,EAAGE,WAAW9B,EAAeC,EAAKC,QAAQb,EAAS,cAEtD,EAEL"}
1
+ {"version":3,"file":"handle-custom-entrypoint.js","sources":["../../src/plugins/handle-custom-entrypoint.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport type { Plugin } from 'vite';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport type { IBuildEntrypoint } from '@services/build';\n\nexport interface IPluginOptions {\n entrypoint: IBuildEntrypoint;\n}\n\nconst pluginName = `${PLUGIN_NAME}-handle-custom-entrypoint`;\n\n/**\n * Get current entrypoint name\n */\nconst getCurrentEntrypointName = (): string | undefined =>\n process.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME;\n\n/**\n * Set current entrypoint name\n */\nconst setCurrentEntrypointName = (name: string): void => {\n process.env.SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME = name;\n};\n\n/**\n * Find current entrypoint by env\n */\nconst getCurrentEntrypoint = (\n entrypoint: IBuildEntrypoint[],\n currentEntrypointName = getCurrentEntrypointName(),\n): IBuildEntrypoint | null => {\n if (!entrypoint.length || !currentEntrypointName) {\n return null;\n }\n\n for (const entry of entrypoint) {\n if (entry.name === currentEntrypointName && !entry.serverFile) {\n return entry;\n }\n }\n\n return null;\n};\n\n/**\n * Replace entrypoint in html file\n */\nconst replaceEntrypoint = (code: string, originalPath: string, endpointPath: string): string => {\n const cleanOrigPath = originalPath.replace('./', '/');\n const cleanEndpointPath = endpointPath.replace('./', '/');\n\n return code.replace(cleanOrigPath, cleanEndpointPath);\n};\n\n/**\n * Return custom entrypoint instead default (index.html).\n *\n * E.g. for build multiple entrypoint\n * @constructor\n */\nfunction ViteHandleCustomEntrypointPlugin(options: IPluginOptions): Plugin {\n const { entrypoint } = options;\n let outPath = '';\n let origClientFile = '';\n\n return {\n name: pluginName,\n enforce: 'pre',\n /**\n * Apply only on build but not for SSR and only for custom entrypoint\n */\n apply(_, { isSsrBuild }): boolean {\n return !isSsrBuild && Boolean(entrypoint);\n },\n config(config) {\n const { indexFile } = entrypoint;\n const buildConfig = config.build ?? {};\n const indexFilePath = indexFile ? path.resolve(config.root ?? '', indexFile) : undefined;\n\n return {\n ...config,\n build: {\n ...buildConfig,\n rollupOptions: {\n ...(buildConfig.rollupOptions ?? {}),\n input: indexFilePath,\n },\n },\n };\n },\n configResolved(config) {\n const pluginConfig = config.plugins.find((plugin) => plugin.name === PLUGIN_NAME);\n\n outPath = path.resolve(config.root, config.build.outDir);\n // @ts-expect-error pluginOptions is custom param\n origClientFile = (pluginConfig.pluginOptions as Record<string, any>).clientFile as string;\n },\n transform(code, id) {\n if (id.endsWith('.html')) {\n const { clientFile } = entrypoint;\n\n if (clientFile) {\n return {\n code: replaceEntrypoint(code, origClientFile, clientFile),\n map: this.getCombinedSourcemap(),\n };\n }\n }\n\n return {\n code,\n map: this.getCombinedSourcemap(),\n };\n },\n /**\n * Development mode\n */\n transformIndexHtml(html, { originalUrl, server }): string {\n const { clientFile } = entrypoint;\n\n if (clientFile && server?.config.command === 'serve' && originalUrl?.endsWith('.html')) {\n return replaceEntrypoint(html, origClientFile, clientFile);\n }\n\n return html;\n },\n closeBundle() {\n const { indexFile } = entrypoint;\n\n if (!indexFile) {\n return;\n }\n\n const indexFilePath = path.resolve(outPath, path.basename(indexFile));\n\n if (fs.existsSync(indexFilePath)) {\n fs.renameSync(indexFilePath, path.resolve(outPath, 'index.html'));\n }\n },\n };\n}\n\nexport {\n ViteHandleCustomEntrypointPlugin,\n getCurrentEntrypoint,\n getCurrentEntrypointName,\n setCurrentEntrypointName,\n};\n"],"names":["pluginName","PLUGIN_NAME","getCurrentEntrypointName","process","env","SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME","setCurrentEntrypointName","name","getCurrentEntrypoint","entrypoint","currentEntrypointName","length","entry","serverFile","replaceEntrypoint","code","originalPath","endpointPath","cleanOrigPath","replace","cleanEndpointPath","ViteHandleCustomEntrypointPlugin","options","outPath","origClientFile","enforce","apply","_","isSsrBuild","Boolean","config","indexFile","buildConfig","build","indexFilePath","path","resolve","root","undefined","rollupOptions","input","configResolved","pluginConfig","plugins","find","plugin","outDir","pluginOptions","clientFile","transform","id","endsWith","map","this","getCombinedSourcemap","transformIndexHtml","html","originalUrl","server","command","closeBundle","basename","fs","existsSync","renameSync"],"mappings":"uHAWA,MAAMA,EAAa,GAAGC,6BAKhBC,EAA2B,IAC/BC,EAAQC,IAAIC,uCAKRC,EAA4BC,IAChCJ,EAAQC,IAAIC,uCAAyCE,CAAI,EAMrDC,EAAuB,CAC3BC,EACAC,EAAwBR,OAExB,IAAKO,EAAWE,SAAWD,EACzB,OAAO,KAGT,IAAK,MAAME,KAASH,EAClB,GAAIG,EAAML,OAASG,IAA0BE,EAAMC,WACjD,OAAOD,EAIX,OAAO,IAAI,EAMPE,EAAoB,CAACC,EAAcC,EAAsBC,KAC7D,MAAMC,EAAgBF,EAAaG,QAAQ,KAAM,KAC3CC,EAAoBH,EAAaE,QAAQ,KAAM,KAErD,OAAOJ,EAAKI,QAAQD,EAAeE,EAAkB,EASvD,SAASC,EAAiCC,GACxC,MAAMb,WAAEA,GAAea,EACvB,IAAIC,EAAU,GACVC,EAAiB,GAErB,MAAO,CACLjB,KAAMP,EACNyB,QAAS,MAITC,MAAK,CAACC,GAAGC,WAAEA,MACDA,GAAcC,QAAQpB,GAEhCqB,OAAOA,GACL,MAAMC,UAAEA,GAActB,EAChBuB,EAAcF,EAAOG,OAAS,CAAE,EAChCC,EAAgBH,EAAYI,EAAKC,QAAQN,EAAOO,MAAQ,GAAIN,QAAaO,EAE/E,MAAO,IACFR,EACHG,MAAO,IACFD,EACHO,cAAe,IACTP,EAAYO,eAAiB,GACjCC,MAAON,IAId,EACDO,eAAeX,GACb,MAAMY,EAAeZ,EAAOa,QAAQC,MAAMC,GAAWA,EAAOtC,OAASN,IAErEsB,EAAUY,EAAKC,QAAQN,EAAOO,KAAMP,EAAOG,MAAMa,QAEjDtB,EAAkBkB,EAAaK,cAAsCC,UACtE,EACDC,UAAUlC,EAAMmC,GACd,GAAIA,EAAGC,SAAS,SAAU,CACxB,MAAMH,WAAEA,GAAevC,EAEvB,GAAIuC,EACF,MAAO,CACLjC,KAAMD,EAAkBC,EAAMS,EAAgBwB,GAC9CI,IAAKC,KAAKC,wBAKhB,MAAO,CACLvC,OACAqC,IAAKC,KAAKC,uBAEb,EAIDC,mBAAmBC,GAAMC,YAAEA,EAAWC,OAAEA,IACtC,MAAMV,WAAEA,GAAevC,EAEvB,OAAIuC,GAAyC,UAA3BU,GAAQ5B,OAAO6B,SAAuBF,GAAaN,SAAS,SACrErC,EAAkB0C,EAAMhC,EAAgBwB,GAG1CQ,CACR,EACDI,cACE,MAAM7B,UAAEA,GAActB,EAEtB,IAAKsB,EACH,OAGF,MAAMG,EAAgBC,EAAKC,QAAQb,EAASY,EAAK0B,SAAS9B,IAEtD+B,EAAGC,WAAW7B,IAChB4B,EAAGE,WAAW9B,EAAeC,EAAKC,QAAQb,EAAS,cAEtD,EAEL"}
@@ -1 +1 @@
1
- {"version":3,"file":"make-aliases.js","sources":["../../src/plugins/make-aliases.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport JSON5 from 'json5';\nimport type { Plugin } from 'vite';\n// import without aliases for use in vitest.config.ts\nimport PLUGIN_NAME from '../constants/plugin-name';\nimport ViteAliases from '../helpers/vite-aliases';\n\nexport interface IPluginOptions {\n root?: string; // default: cwd()\n tsconfig?: string; // default: tsconfig.json\n}\n\nconst pluginName = `${PLUGIN_NAME}-make-aliases`;\nconst cleanupAlias = (str: string): string => str.replace('/*', '');\n\n/**\n * Read tsconfig file and set vite aliases\n * @see PathNormalize.getAliases\n * @constructor\n */\nfunction ViteMakeAliasesPlugin(options: IPluginOptions = {}): Plugin {\n const { root, tsconfig } = options;\n const projectRoot = root ?? process.cwd();\n const tsconfigPath = path.resolve(projectRoot, tsconfig ?? 'tsconfig.json');\n const aliases: [string, string][] = [];\n\n if (!fs.existsSync(tsconfigPath)) {\n console.error(`${pluginName}: tsconfig not exist in \"${tsconfigPath}\"`);\n } else {\n const tsJson = JSON5.parse<{ compilerOptions?: { paths: Record<string, string[]> } }>(\n fs.readFileSync(tsconfigPath, { encoding: 'utf-8' }),\n );\n const paths = tsJson?.compilerOptions?.paths ?? {};\n\n Object.entries(paths).forEach(([alias, aliasPaths]) => {\n aliases.push([cleanupAlias(alias), cleanupAlias(aliasPaths[0])]);\n });\n }\n\n return {\n name: pluginName,\n config(config) {\n if (aliases.length) {\n const resolveConfig = config.resolve ?? {};\n const defaultAliases = resolveConfig.alias ?? [];\n const normalizedAliases = Array.isArray(defaultAliases)\n ? defaultAliases\n : Object.entries(defaultAliases).map(([find, val]) => ({\n find,\n replacement: val as string,\n }));\n\n normalizedAliases.push(...ViteAliases(aliases, `${projectRoot}/${config?.root ?? ''}`));\n\n config.resolve = {\n ...resolveConfig,\n alias: normalizedAliases,\n };\n }\n\n return config;\n },\n };\n}\n\nexport default ViteMakeAliasesPlugin;\n"],"names":["pluginName","PLUGIN_NAME","cleanupAlias","str","replace","ViteMakeAliasesPlugin","options","root","tsconfig","projectRoot","process","cwd","tsconfigPath","path","resolve","aliases","fs","existsSync","tsJson","JSON5","parse","readFileSync","encoding","paths","compilerOptions","Object","entries","forEach","alias","aliasPaths","push","console","error","name","config","length","resolveConfig","defaultAliases","normalizedAliases","Array","isArray","map","find","val","replacement","ViteAliases"],"mappings":"sLAcA,MAAMA,EAAa,GAAGC,iBAChBC,EAAgBC,GAAwBA,EAAIC,QAAQ,KAAM,IAOhE,SAASC,EAAsBC,EAA0B,IACvD,MAAMC,KAAEA,EAAIC,SAAEA,GAAaF,EACrBG,EAAcF,GAAQG,EAAQC,MAC9BC,EAAeC,EAAKC,QAAQL,EAAaD,GAAY,iBACrDO,EAA8B,GAEpC,GAAKC,EAAGC,WAAWL,GAEZ,CACL,MAAMM,EAASC,EAAMC,MACnBJ,EAAGK,aAAaT,EAAc,CAAEU,SAAU,WAEtCC,EAAQL,GAAQM,iBAAiBD,OAAS,CAAA,EAEhDE,OAAOC,QAAQH,GAAOI,SAAQ,EAAEC,EAAOC,MACrCd,EAAQe,KAAK,CAAC5B,EAAa0B,GAAQ1B,EAAa2B,EAAW,KAAK,GAEnE,MAVCE,QAAQC,MAAM,GAAGhC,6BAAsCY,MAYzD,MAAO,CACLqB,KAAMjC,EACNkC,OAAOA,GACL,GAAInB,EAAQoB,OAAQ,CAClB,MAAMC,EAAgBF,EAAOpB,SAAW,GAClCuB,EAAiBD,EAAcR,OAAS,GACxCU,EAAoBC,MAAMC,QAAQH,GACpCA,EACAZ,OAAOC,QAAQW,GAAgBI,KAAI,EAAEC,EAAMC,MAAU,CACnDD,OACAE,YAAaD,MAGnBL,EAAkBR,QAAQe,EAAY9B,EAAS,GAAGN,KAAeyB,GAAQ3B,MAAQ,OAEjF2B,EAAOpB,QAAU,IACZsB,EACHR,MAAOU,EAEV,CAED,OAAOJ,CACR,EAEL"}
1
+ {"version":3,"file":"make-aliases.js","sources":["../../src/plugins/make-aliases.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport JSON5 from 'json5';\nimport type { Plugin } from 'vite';\n// import without aliases for use in vitest.config.ts\nimport PLUGIN_NAME from '../constants/plugin-name';\nimport ViteAliases from '../helpers/vite-aliases';\n\nexport interface IPluginOptions {\n root?: string; // default: cwd()\n tsconfig?: string; // default: tsconfig.json\n}\n\nconst pluginName = `${PLUGIN_NAME}-make-aliases`;\nconst cleanupAlias = (str: string): string => str.replace('/*', '');\n\n/**\n * Read tsconfig file and set vite aliases\n * @see PathNormalize.getAliases\n * @constructor\n */\nfunction ViteMakeAliasesPlugin(options: IPluginOptions = {}): Plugin {\n const { root, tsconfig } = options;\n const projectRoot = root ?? process.cwd();\n const tsconfigPath = path.resolve(projectRoot, tsconfig ?? 'tsconfig.json');\n const aliases: [string, string][] = [];\n\n if (!fs.existsSync(tsconfigPath)) {\n console.error(`${pluginName}: tsconfig not exist in \"${tsconfigPath}\"`);\n } else {\n const tsJson = JSON5.parse<{ compilerOptions?: { paths: Record<string, string[]> } }>(\n fs.readFileSync(tsconfigPath, { encoding: 'utf-8' }),\n );\n const paths = tsJson?.compilerOptions?.paths ?? {};\n\n Object.entries(paths).forEach(([alias, aliasPaths]) => {\n aliases.push([cleanupAlias(alias), cleanupAlias(aliasPaths[0])]);\n });\n }\n\n return {\n name: pluginName,\n config(config) {\n if (aliases.length) {\n const resolveConfig = config.resolve ?? {};\n const defaultAliases = resolveConfig.alias ?? [];\n const normalizedAliases = Array.isArray(defaultAliases)\n ? defaultAliases\n : Object.entries(defaultAliases).map(([find, val]) => ({\n find,\n replacement: val as string,\n }));\n\n normalizedAliases.push(...ViteAliases(aliases, `${projectRoot}/${config?.root ?? ''}`));\n\n config.resolve = {\n ...resolveConfig,\n alias: normalizedAliases,\n };\n }\n\n return config;\n },\n };\n}\n\nexport default ViteMakeAliasesPlugin;\n"],"names":["pluginName","PLUGIN_NAME","cleanupAlias","str","replace","ViteMakeAliasesPlugin","options","root","tsconfig","projectRoot","process","cwd","tsconfigPath","path","resolve","aliases","fs","existsSync","tsJson","JSON5","parse","readFileSync","encoding","paths","compilerOptions","Object","entries","forEach","alias","aliasPaths","push","console","error","name","config","length","resolveConfig","defaultAliases","normalizedAliases","Array","isArray","map","find","val","replacement","ViteAliases"],"mappings":"sLAcA,MAAMA,EAAa,GAAGC,iBAChBC,EAAgBC,GAAwBA,EAAIC,QAAQ,KAAM,IAOhE,SAASC,EAAsBC,EAA0B,IACvD,MAAMC,KAAEA,EAAIC,SAAEA,GAAaF,EACrBG,EAAcF,GAAQG,EAAQC,MAC9BC,EAAeC,EAAKC,QAAQL,EAAaD,GAAY,iBACrDO,EAA8B,GAEpC,GAAKC,EAAGC,WAAWL,GAEZ,CACL,MAAMM,EAASC,EAAMC,MACnBJ,EAAGK,aAAaT,EAAc,CAAEU,SAAU,WAEtCC,EAAQL,GAAQM,iBAAiBD,OAAS,CAAE,EAElDE,OAAOC,QAAQH,GAAOI,SAAQ,EAAEC,EAAOC,MACrCd,EAAQe,KAAK,CAAC5B,EAAa0B,GAAQ1B,EAAa2B,EAAW,KAAK,SARlEE,QAAQC,MAAM,GAAGhC,6BAAsCY,MAYzD,MAAO,CACLqB,KAAMjC,EACNkC,OAAOA,GACL,GAAInB,EAAQoB,OAAQ,CAClB,MAAMC,EAAgBF,EAAOpB,SAAW,CAAE,EACpCuB,EAAiBD,EAAcR,OAAS,GACxCU,EAAoBC,MAAMC,QAAQH,GACpCA,EACAZ,OAAOC,QAAQW,GAAgBI,KAAI,EAAEC,EAAMC,MAAU,CACnDD,OACAE,YAAaD,MAGnBL,EAAkBR,QAAQe,EAAY9B,EAAS,GAAGN,KAAeyB,GAAQ3B,MAAQ,OAEjF2B,EAAOpB,QAAU,IACZsB,EACHR,MAAOU,GAIX,OAAOJ,CACR,EAEL"}
@@ -3,15 +3,12 @@ interface IPluginOptions {
3
3
  isSSR?: boolean;
4
4
  isBuild?: boolean;
5
5
  routesPath?: string;
6
- isNodeParsing?: boolean;
7
6
  }
8
7
  /**
9
8
  * Add possibility to export route components like FCRoute or FCCRoute
10
- * Add route path for generating manifest
11
9
  * USAGE: { path: '/', lazy: () => import('./pages/home') }
12
10
  * @see FCRoute
13
11
  * @see FCCRoute
14
- * @see SsrManifest.getAsyncRoutesIds
15
12
  * @see importRoute
16
13
  * @constructor
17
14
  */
@@ -1,2 +1,2 @@
1
- import{extname as e,resolve as r}from"node:path";import t from"../constants/plugin-name.js";import o from"../helpers/is-route-file.js";import{writeMeta as s}from"../helpers/ssr-meta.js";import i from"../services/parse-routes.js";const n=(e,r=!1)=>r?e:i.injectPathId(e),l=(e,r)=>{const o=e.replace(/(lazy)(:\s*)(\(\)\s*=>\s*import\(([^)]+)\))/gs,r?"lazy$2n($3,$4)":"lazy$2n($3)");return e!==o?`import n from '${t}/helpers/import-route';${o}`:e};function u(i={}){const{routesPath:u,isNodeParsing:m=!1,isSSR:p=!1,isBuild:a=!1}=i,c=new Map,d={root:"",buildDir:""};return{name:`${t}-normalize-route`,enforce:"pre",transform(r,t){const[s]=e(t).split("?"),i=!u||t.includes(u);if(!t.includes("node_modules")&&[".js",".mjs",".ts",".tsx"].includes(s)&&i&&o(r))return c.set(t,""),{code:l(n(r,a),p&&(m||!a)),map:{mappings:""}}},...m?{config(e,{isSsrBuild:r}){r||(d.root=e.root,d.buildDir=e.build.outDir)},generateBundle(e,r){for(const[e,t]of Object.entries(r))"chunk"===t.type&&Object.entries(t.modules).forEach((([r])=>{c.has(r)&&c.set(r,e)}))},writeBundle(){if(!d.root)return;const[e]=r(d.root,d.buildDir).split("/client");s(e,{routeFiles:Object.fromEntries(c)})}}:{}}}export{u as default};
1
+ import{extname as e}from"node:path";import s from"../constants/plugin-name.js";import o from"../helpers/is-route-file.js";import r from"../services/parse-routes.js";function t(t={}){const{routesPath:n,isSSR:i=!1,isBuild:m=!1}=t;return{name:`${s}-normalize-route`,enforce:"pre",transform(s,t){const[u]=e(t).split("?"),p=!n||t.includes(n);if(!t.includes("node_modules")&&[".js",".mjs",".ts",".tsx"].includes(u)&&p&&o(s))return{code:r.handleRoutes(s,i&&!m),map:{mappings:""}}}}}export{t as default};
2
2
  //# sourceMappingURL=normalize-route.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalize-route.js","sources":["../../src/plugins/normalize-route.ts"],"sourcesContent":["import { extname, resolve } from 'node:path';\nimport type { Plugin } from 'vite';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport isRoutesFile from '@helpers/is-route-file';\nimport { writeMeta } from '@helpers/ssr-meta';\nimport ParseRoutes from '@services/parse-routes';\n\nexport interface IPluginOptions {\n isSSR?: boolean;\n isBuild?: boolean;\n routesPath?: string;\n isNodeParsing?: boolean;\n}\n\n/**\n * Add pathId to route (where pathId - import string)\n * NOTE: only for dev mode\n */\nconst normalizeSyncRoutes = (code: string, isBuild = false): string => {\n if (isBuild) {\n return code;\n }\n\n return ParseRoutes.injectPathId(code);\n};\n\n/**\n * Add normalize wrapper to lazy imports for client build\n */\nconst normalizeAsyncRoutes = (code: string, hasPathId: boolean): string => {\n const modifiedCode = code.replace(\n /(lazy)(:\\s*)(\\(\\)\\s*=>\\s*import\\(([^)]+)\\))/gs,\n hasPathId ? 'lazy$2n($3,$4)' : 'lazy$2n($3)',\n );\n\n if (code !== modifiedCode) {\n return `import n from '${PLUGIN_NAME}/helpers/import-route';${modifiedCode}`;\n }\n\n return code;\n};\n\n/**\n * Add possibility to export route components like FCRoute or FCCRoute\n * Add route path for generating manifest\n * USAGE: { path: '/', lazy: () => import('./pages/home') }\n * @see FCRoute\n * @see FCCRoute\n * @see SsrManifest.getAsyncRoutesIds\n * @see importRoute\n * @constructor\n */\nfunction ViteNormalizeRouterPlugin(options: IPluginOptions = {}): Plugin {\n const { routesPath, isNodeParsing = false, isSSR = false, isBuild = false } = options;\n const routeFiles = new Map<string, string>();\n const cfg = { root: '', buildDir: '' };\n\n return {\n name: `${PLUGIN_NAME}-normalize-route`,\n enforce: 'pre',\n transform(code, id) {\n const [extName] = extname(id).split('?');\n const isRoutesPath = !routesPath || id.includes(routesPath);\n\n if (\n id.includes('node_modules') ||\n !['.js', '.mjs', '.ts', '.tsx'].includes(extName) ||\n !isRoutesPath ||\n !isRoutesFile(code)\n ) {\n return;\n }\n\n routeFiles.set(id, '');\n\n return {\n code: normalizeAsyncRoutes(\n // always add pathId to sync routes for development\n normalizeSyncRoutes(code, isBuild),\n // always add pathId to async routes for development or if it's node parsing mode\n isSSR && (isNodeParsing || !isBuild),\n ),\n map: { mappings: '' },\n };\n },\n ...(isNodeParsing\n ? {\n /**\n * Get build path\n */\n config(config, { isSsrBuild }): void {\n if (isSsrBuild) {\n return;\n }\n\n cfg.root = config.root!;\n cfg.buildDir = config.build!.outDir!;\n },\n /**\n * Get transformed route files\n */\n generateBundle(_, bundle) {\n for (const [fileName, chunk] of Object.entries(bundle)) {\n if (chunk.type === 'chunk') {\n Object.entries(chunk.modules).forEach(([modulePath]) => {\n if (routeFiles.has(modulePath)) {\n routeFiles.set(modulePath, fileName);\n }\n });\n }\n }\n },\n /**\n * Save metadata on for client build\n * @see config hook\n */\n writeBundle(): void {\n if (!cfg.root) {\n return;\n }\n\n const [buildDir] = resolve(cfg.root, cfg.buildDir).split('/client');\n\n writeMeta(buildDir, { routeFiles: Object.fromEntries(routeFiles) });\n },\n }\n : {}),\n };\n}\n\nexport default ViteNormalizeRouterPlugin;\n"],"names":["normalizeSyncRoutes","code","isBuild","ParseRoutes","injectPathId","normalizeAsyncRoutes","hasPathId","modifiedCode","replace","PLUGIN_NAME","ViteNormalizeRouterPlugin","options","routesPath","isNodeParsing","isSSR","routeFiles","Map","cfg","root","buildDir","name","enforce","transform","id","extName","extname","split","isRoutesPath","includes","isRoutesFile","set","map","mappings","config","isSsrBuild","build","outDir","generateBundle","_","bundle","fileName","chunk","Object","entries","type","modules","forEach","modulePath","has","writeBundle","resolve","writeMeta","fromEntries"],"mappings":"qOAkBA,MAAMA,EAAsB,CAACC,EAAcC,GAAU,IAC/CA,EACKD,EAGFE,EAAYC,aAAaH,GAM5BI,EAAuB,CAACJ,EAAcK,KAC1C,MAAMC,EAAeN,EAAKO,QACxB,gDACAF,EAAY,iBAAmB,eAGjC,OAAIL,IAASM,EACJ,kBAAkBE,2BAAqCF,IAGzDN,CAAI,EAab,SAASS,EAA0BC,EAA0B,IAC3D,MAAMC,WAAEA,EAAUC,cAAEA,GAAgB,EAAKC,MAAEA,GAAQ,EAAKZ,QAAEA,GAAU,GAAUS,EACxEI,EAAa,IAAIC,IACjBC,EAAM,CAAEC,KAAM,GAAIC,SAAU,IAElC,MAAO,CACLC,KAAM,GAAGX,oBACTY,QAAS,MACTC,UAAUrB,EAAMsB,GACd,MAAOC,GAAWC,EAAQF,GAAIG,MAAM,KAC9BC,GAAgBf,GAAcW,EAAGK,SAAShB,GAEhD,IACEW,EAAGK,SAAS,iBACX,CAAC,MAAO,OAAQ,MAAO,QAAQA,SAASJ,IACxCG,GACAE,EAAa5B,GAOhB,OAFAc,EAAWe,IAAIP,EAAI,IAEZ,CACLtB,KAAMI,EAEJL,EAAoBC,EAAMC,GAE1BY,IAAUD,IAAkBX,IAE9B6B,IAAK,CAAEC,SAAU,IAEpB,KACGnB,EACA,CAIEoB,OAAOA,GAAQC,WAAEA,IACXA,IAIJjB,EAAIC,KAAOe,EAAOf,KAClBD,EAAIE,SAAWc,EAAOE,MAAOC,OAC9B,EAIDC,eAAeC,EAAGC,GAChB,IAAK,MAAOC,EAAUC,KAAUC,OAAOC,QAAQJ,GAC1B,UAAfE,EAAMG,MACRF,OAAOC,QAAQF,EAAMI,SAASC,SAAQ,EAAEC,MAClChC,EAAWiC,IAAID,IACjBhC,EAAWe,IAAIiB,EAAYP,EAC5B,GAIR,EAKDS,cACE,IAAKhC,EAAIC,KACP,OAGF,MAAOC,GAAY+B,EAAQjC,EAAIC,KAAMD,EAAIE,UAAUO,MAAM,WAEzDyB,EAAUhC,EAAU,CAAEJ,WAAY2B,OAAOU,YAAYrC,IACtD,GAEH,GAER"}
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';\nimport ParseRoutes from '@services/parse-routes';\n\nexport interface IPluginOptions {\n isSSR?: boolean;\n isBuild?: boolean;\n routesPath?: string;\n}\n\n/**\n * Add possibility to export route components like FCRoute or FCCRoute\n * USAGE: { path: '/', lazy: () => import('./pages/home') }\n * @see FCRoute\n * @see FCCRoute\n * @see importRoute\n * @constructor\n */\nfunction ViteNormalizeRouterPlugin(options: IPluginOptions = {}): Plugin {\n const { routesPath, isSSR = false, isBuild = false } = options;\n\n return {\n name: `${PLUGIN_NAME}-normalize-route`,\n enforce: 'pre',\n transform(code, id) {\n const [extName] = extname(id).split('?');\n const isRoutesPath = !routesPath || id.includes(routesPath);\n\n if (\n id.includes('node_modules') ||\n !['.js', '.mjs', '.ts', '.tsx'].includes(extName) ||\n !isRoutesPath ||\n !isRoutesFile(code)\n ) {\n return;\n }\n\n return {\n code: ParseRoutes.handleRoutes(\n code,\n // always add pathId to routes for development\n isSSR && !isBuild,\n ),\n map: { mappings: '' },\n };\n },\n };\n}\n\nexport default ViteNormalizeRouterPlugin;\n"],"names":["ViteNormalizeRouterPlugin","options","routesPath","isSSR","isBuild","name","PLUGIN_NAME","enforce","transform","code","id","extName","extname","split","isRoutesPath","includes","isRoutesFile","ParseRoutes","handleRoutes","map","mappings"],"mappings":"qKAoBA,SAASA,EAA0BC,EAA0B,IAC3D,MAAMC,WAAEA,EAAUC,MAAEA,GAAQ,EAAKC,QAAEA,GAAU,GAAUH,EAEvD,MAAO,CACLI,KAAM,GAAGC,oBACTC,QAAS,MACTC,UAAUC,EAAMC,GACd,MAAOC,GAAWC,EAAQF,GAAIG,MAAM,KAC9BC,GAAgBZ,GAAcQ,EAAGK,SAASb,GAEhD,IACEQ,EAAGK,SAAS,iBACX,CAAC,MAAO,OAAQ,MAAO,QAAQA,SAASJ,IACxCG,GACAE,EAAaP,GAKhB,MAAO,CACLA,KAAMQ,EAAYC,aAChBT,EAEAN,IAAUC,GAEZe,IAAK,CAAEC,SAAU,IAEpB,EAEL"}
@@ -129,14 +129,7 @@ declare class Build {
129
129
  /**
130
130
  * Build assets manifest file
131
131
  */
132
- protected buildManifest(): Promise<void>;
133
- /**
134
- * Remove pathId from client route files
135
- */
136
- /**
137
- * Remove pathId from client route files
138
- */
139
- private cleanupClientRoutes;
132
+ protected buildManifest(): void;
140
133
  /**
141
134
  * Change general directive Disallow to Allow in robots.txt.
142
135
  */
package/services/build.js CHANGED
@@ -1,2 +1,2 @@
1
- import i from"node:child_process";import s from"node:fs";import t from"node:path";import e from"chalk";import{resolveConfig as o}from"vite";import n from"../cli/helpers/vite-reset-cache.js";import r from"../helpers/create-focus-only.js";import{createDevMarker as l}from"../helpers/dev-marker.js";import a from"../helpers/plugin-config.js";import c from"../helpers/process-stop.js";import{readMeta as u,removeMeta as d}from"../helpers/ssr-meta.js";import h from"./server-config.js";import p from"./ssr-manifest.js";class f{isProd;nodeEnv;buildDir;viteConfig;pluginConfig;params={mode:"",clientOptions:"",serverOptions:"",focusOnly:"app",isWatch:!1,isUnlockRobots:!1,isEject:!1,isServerless:!1,isNoWarnings:!1};abortController=null;runningBuild=[];hasPreviewModeExitListener=!1;constructor(i){this.params={...this.params,...i}}async makeConfig(){const{mode:i}=this.params;this.viteConfig=await o({},"build",i,"production"),this.pluginConfig=a(this.viteConfig),this.buildDir=t.resolve(this.viteConfig.root,this.viteConfig.build.outDir),this.nodeEnv=process.env.NODE_ENV||"production",this.isProd="production"===this.nodeEnv}clearBuildFolder(){s.existsSync(this.buildDir)&&s.rmSync(this.buildDir,{recursive:!0})}getIsProd(){return this.isProd}getNodeEnv(){return this.nodeEnv}getRunningBuildNames(){return this.runningBuild.map((({name:i})=>i))}promisifyProcess(i,s=!1){const t=new Promise(((t,e)=>{i.on("exit",(i=>{t(i)})),i.on("close",(i=>{t(i)})),i.on("error",(i=>{e(i)})),s&&i.stderr?.on("data",(i=>{const s=Buffer.from(i).toString();(s.includes("warning")||s.includes("WARNING"))&&t(1)}))}));return i.stdout?.pipe(process.stdout),i.stderr?.pipe(process.stderr),{promise:t,command:i}}async buildManifest(){console.info(e.blue(`Building routes manifest file: ${this.pluginConfig.routesParsing}`));const i="node"===this.pluginConfig.routesParsing,s=h.init({isProd:this.isProd,mode:this.params.mode},{root:this.viteConfig.root,clientFile:this.pluginConfig.clientFile});await p.get(s,{buildDir:this.viteConfig.build.outDir,viteAliases:this.viteConfig.resolve.alias,basename:this.viteConfig.base}).buildRoutesManifest(i),i&&this.cleanupClientRoutes()}cleanupClientRoutes(){const{routeFiles:i}=u(this.buildDir),t=new Set(Object.values(i??[]));t.size&&t.forEach((i=>{const t=`${this.buildDir}/client/${i}`;try{const i=s.readFileSync(t,{encoding:"utf-8"}).replace(/(lazy:.*?\((.*?)\)),\s?".*?"\)/g,"$1)");s.writeFileSync(t,i)}catch(i){console.log(`Failed cleanup client route ${t}:`,i)}}))}unlockRobots(){const i=`${this.buildDir}/client/robots.txt`;if(!s.existsSync(i))return void console.warn(`Failed to unlock robots.txt, file not exist: ${i}`);const t=s.readFileSync(i,{encoding:"utf-8"}).replace(/Disallow: \/$/m,"Allow: /");s.writeFileSync(i,t,{encoding:"utf-8"}),console.info(e.blue("\nrobots.txt unlocked."))}eject(){const i=`${this.buildDir}/server/start.js`;s.writeFileSync(i,"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\n\nconst VERSION = process.env.VERSION || \"1.0.0\";\nconst PORT = process.env.PORT || 3000;\nconst IS_HOST = process.env.IS_HOST || \"0\";\nconst ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\n\nawait runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\n",{encoding:"utf-8"})}createServerless(){const i=`${this.buildDir}/server/serverless.js`;s.writeFileSync(i,"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\n\nexport default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\n",{encoding:"utf-8"})}async spawnBuild(s,t,e={}){const{mode:o,isNoWarnings:n}=this.params,{focusOnly:l=this.params.focusOnly,shouldWait:a=!1,env:c={}}=e,u=o&&!t.includes("--mode")?`--mode ${o}`:"",d=this.promisifyProcess(i.spawn(`vite build ${t} ${u} --emptyOutDir`,{signal:this.abortController.signal,stdio:[process.stdin,"pipe","pipe"],shell:!0,env:{...process.env,...c,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:r(l).isOnlyClient()?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}),n);this.runningBuild.push({name:s,buildProcess:d}),a&&await this.waitLastBuild()}async waitLastBuild(){const i=this.runningBuild.at(-1);if(!i)return;const s=await i.buildProcess.promise;c(s,!0)}runPreviewMode(){this.hasPreviewModeExitListener||(process.on("exit",(()=>{this.abortController.abort()})),this.hasPreviewModeExitListener=!0);const{onFinish:i}=this.params;let s=this.runningBuild.length;const t=e=>{Buffer.from(e).toString().includes("built in")&&(s-=1,s||(this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.removeListener("data",t)})),l(this.isProd,this.viteConfig),i?.()))};this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.on("data",t)}))}async build(){await this.makeConfig(),await n(),this.clearBuildFolder();const{clientOptions:i,serverOptions:s,onFinish:t,isWatch:e,focusOnly:o,isEject:a,isServerless:c,isUnlockRobots:u}=this.params,{outDir:h}=this.viteConfig.build,p=r(o);this.abortController=new AbortController,this.runningBuild=[],p.isClient()&&await this.spawnBuild("client",`${i} --outDir ${h}/client`,{shouldWait:!e}),p.isServer()&&(await this.spawnBuild("server",`${s} --outDir ${h}/server --ssr ${this.pluginConfig.serverFile}`,{shouldWait:!e}),e||(await this.buildManifest(),a&&this.eject(),c&&this.createServerless()));const{entrypoint:f}=this.pluginConfig;if(f?.length&&p.isEntrypoint())for(const{name:i,type:s,serverFile:t,buildOptions:o=""}of f){const n=t&&"ssr"===s?`--ssr ${t}`:"";await this.spawnBuild(i,`${o} ${n} --outDir ${h}/${i}`,{shouldWait:!e,focusOnly:"ssr"===s?"server":"client",env:{SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME:i}})}e?this.runPreviewMode():(u&&this.unlockRobots(),l(this.isProd,this.viteConfig),d(this.buildDir),t?.())}}export{f as default};
1
+ import i from"node:child_process";import s from"node:fs";import t from"node:path";import e from"chalk";import{resolveConfig as o}from"vite";import n from"../cli/helpers/vite-reset-cache.js";import r from"../helpers/create-focus-only.js";import{createDevMarker as l}from"../helpers/dev-marker.js";import a from"../helpers/plugin-config.js";import u from"../helpers/process-stop.js";import c from"./server-config.js";import d from"./ssr-manifest.js";class h{isProd;nodeEnv;buildDir;viteConfig;pluginConfig;params={mode:"",clientOptions:"",serverOptions:"",focusOnly:"app",isWatch:!1,isUnlockRobots:!1,isEject:!1,isServerless:!1,isNoWarnings:!1};abortController=null;runningBuild=[];hasPreviewModeExitListener=!1;constructor(i){this.params={...this.params,...i}}async makeConfig(){const{mode:i}=this.params;this.viteConfig=await o({},"build",i,"production"),this.pluginConfig=a(this.viteConfig),this.buildDir=t.resolve(this.viteConfig.root,this.viteConfig.build.outDir),this.nodeEnv=process.env.NODE_ENV||"production",this.isProd="production"===this.nodeEnv}clearBuildFolder(){s.existsSync(this.buildDir)&&s.rmSync(this.buildDir,{recursive:!0})}getIsProd(){return this.isProd}getNodeEnv(){return this.nodeEnv}getRunningBuildNames(){return this.runningBuild.map((({name:i})=>i))}promisifyProcess(i,s=!1){const t=new Promise(((t,e)=>{i.on("exit",(i=>{t(i)})),i.on("close",(i=>{t(i)})),i.on("error",(i=>{e(i)})),s&&i.stderr?.on("data",(i=>{const s=Buffer.from(i).toString();(s.includes("warning")||s.includes("WARNING"))&&t(1)}))}));return i.stdout?.pipe(process.stdout),i.stderr?.pipe(process.stderr),{promise:t,command:i}}buildManifest(){console.info(e.blue("Building routes manifest file"));const i=c.init({isProd:this.isProd,mode:this.params.mode},{root:this.viteConfig.root,clientFile:this.pluginConfig.clientFile});d.get(i,{buildDir:this.viteConfig.build.outDir,viteAliases:this.viteConfig.resolve.alias,basename:this.viteConfig.base}).buildRoutesManifest()}unlockRobots(){const i=`${this.buildDir}/client/robots.txt`;if(!s.existsSync(i))return void console.warn(`Failed to unlock robots.txt, file not exist: ${i}`);const t=s.readFileSync(i,{encoding:"utf-8"}).replace(/Disallow: \/$/m,"Allow: /");s.writeFileSync(i,t,{encoding:"utf-8"}),console.info(e.blue("\nrobots.txt unlocked."))}eject(){const i=`${this.buildDir}/server/start.js`;s.writeFileSync(i,"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\n\nconst VERSION = process.env.VERSION || \"1.0.0\";\nconst PORT = process.env.PORT || 3000;\nconst IS_HOST = process.env.IS_HOST || \"0\";\nconst ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\n\nawait runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\n",{encoding:"utf-8"})}createServerless(){const i=`${this.buildDir}/server/serverless.js`;s.writeFileSync(i,"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\n\nexport default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\n",{encoding:"utf-8"})}async spawnBuild(s,t,e={}){const{mode:o,isNoWarnings:n}=this.params,{focusOnly:l=this.params.focusOnly,shouldWait:a=!1,env:u={}}=e,c=o&&!t.includes("--mode")?`--mode ${o}`:"",d=this.promisifyProcess(i.spawn(`vite build ${t} ${c} --emptyOutDir`,{signal:this.abortController.signal,stdio:[process.stdin,"pipe","pipe"],shell:!0,env:{...process.env,...u,FORCE_COLOR:"2",SSR_BOOST_IS_SSR:r(l).isOnlyClient()?"0":"1",SSR_BOOST_ACTION:global.viteBoostAction}}),n);this.runningBuild.push({name:s,buildProcess:d}),a&&await this.waitLastBuild()}async waitLastBuild(){const i=this.runningBuild.at(-1);if(!i)return;const s=await i.buildProcess.promise;u(s,!0)}runPreviewMode(){this.hasPreviewModeExitListener||(process.on("exit",(()=>{this.abortController.abort()})),this.hasPreviewModeExitListener=!0);const{onFinish:i}=this.params;let s=this.runningBuild.length;const t=e=>{Buffer.from(e).toString().includes("built in")&&(s-=1,s||(this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.removeListener("data",t)})),l(this.isProd,this.viteConfig),i?.()))};this.runningBuild.forEach((({buildProcess:i})=>{i.command.stdout?.on("data",t)}))}async build(){await this.makeConfig(),await n(),this.clearBuildFolder();const{clientOptions:i,serverOptions:s,onFinish:t,isWatch:e,focusOnly:o,isEject:a,isServerless:u,isUnlockRobots:c}=this.params,{outDir:d}=this.viteConfig.build,h=r(o);this.abortController=new AbortController,this.runningBuild=[],h.isClient()&&await this.spawnBuild("client",`${i} --outDir ${d}/client`,{shouldWait:!e}),h.isServer()&&(await this.spawnBuild("server",`${s} --outDir ${d}/server --ssr ${this.pluginConfig.serverFile}`,{shouldWait:!e}),e||(this.buildManifest(),a&&this.eject(),u&&this.createServerless()));const{entrypoint:p}=this.pluginConfig;if(p?.length&&h.isEntrypoint())for(const{name:i,type:s,serverFile:t,buildOptions:o=""}of p){const n=t&&"ssr"===s?`--ssr ${t}`:"";await this.spawnBuild(i,`${o} ${n} --outDir ${d}/${i}`,{shouldWait:!e,focusOnly:"ssr"===s?"server":"client",env:{SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME:i}})}e?this.runPreviewMode():(c&&this.unlockRobots(),l(this.isProd,this.viteConfig),t?.())}}export{h as default};
2
2
  //# sourceMappingURL=build.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sources":["../../src/services/build.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { ResolvedConfig } from 'vite';\nimport { resolveConfig } from 'vite';\nimport viteResetCache from '@cli/helpers/vite-reset-cache';\nimport createFocusOnly from '@helpers/create-focus-only';\nimport { createDevMarker } from '@helpers/dev-marker';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport processStop from '@helpers/process-stop';\nimport { readMeta, removeMeta } from '@helpers/ssr-meta';\nimport ServerConfig from '@services/server-config';\nimport SsrManifest from '@services/ssr-manifest';\n\nexport interface IBuildParams {\n mode: string;\n onFinish?: () => void;\n clientOptions?: string;\n serverOptions?: string;\n focusOnly?: 'all' | 'app' | 'client' | 'server' | 'entrypoint';\n isWatch?: boolean;\n isUnlockRobots?: boolean;\n isEject?: boolean;\n isServerless?: boolean;\n isNoWarnings?: boolean;\n}\n\ninterface IBuildProcess {\n promise: Promise<number | null | string>;\n command: childProcess.ChildProcess;\n}\n\ninterface ISpawnBuildParams {\n shouldWait?: boolean;\n focusOnly?: IBuildParams['focusOnly'];\n env?: Record<string, string>;\n}\n\nexport interface IBuildEntrypoint {\n // entrypoint name\n name: string;\n type: 'spa' | 'ssr';\n // custom index file, default: indexFile from plugin config\n indexFile?: string;\n // custom entry file for replace in indexFile, default: undefined (do nothing)\n clientFile?: string;\n // custom server file, indexFile and clientFile will be ignored\n serverFile?: string;\n // additional options for vite build command\n buildOptions?: string;\n}\n\n/**\n * Build service\n */\nclass Build {\n /**\n * Is production build\n */\n protected isProd: boolean;\n\n /**\n * Node environment\n */\n protected nodeEnv: string;\n\n /**\n * Build folder\n */\n protected buildDir: string;\n\n /**\n * Vite config\n */\n protected viteConfig: ResolvedConfig;\n\n /**\n * Plugin config\n */\n protected pluginConfig: IPluginConfig;\n\n /**\n * Build params\n */\n protected params: IBuildParams = {\n mode: '',\n clientOptions: '',\n serverOptions: '',\n focusOnly: 'app',\n isWatch: false,\n isUnlockRobots: false,\n isEject: false,\n isServerless: false,\n isNoWarnings: false,\n };\n\n /**\n * Abort controller for builds\n */\n protected abortController: AbortController | null = null;\n\n /**\n * Running builds\n */\n protected runningBuild: { name: string; buildProcess: IBuildProcess }[] = [];\n\n /**\n * Listener for preview has attached\n */\n protected hasPreviewModeExitListener = false;\n\n /**\n * @constructor\n */\n public constructor(params: IBuildParams) {\n this.params = { ...this.params, ...params };\n }\n\n /**\n * Make config\n */\n protected async makeConfig(): Promise<void> {\n const { mode } = this.params;\n\n this.viteConfig = await resolveConfig({}, 'build', mode, 'production');\n this.pluginConfig = getPluginConfig(this.viteConfig);\n this.buildDir = path.resolve(this.viteConfig.root, this.viteConfig.build.outDir);\n this.nodeEnv = process.env.NODE_ENV || 'production';\n this.isProd = this.nodeEnv === 'production';\n }\n\n /**\n * Clear build folder\n */\n public clearBuildFolder(): void {\n // clear build folder\n if (fs.existsSync(this.buildDir)) {\n fs.rmSync(this.buildDir, { recursive: true });\n }\n }\n\n /**\n * Return is prod indicator value\n */\n public getIsProd(): boolean {\n return this.isProd;\n }\n\n /**\n * Return node env value\n */\n public getNodeEnv(): string {\n return this.nodeEnv;\n }\n\n /**\n * Return build names\n */\n public getRunningBuildNames(): string[] {\n return this.runningBuild.map(({ name }) => name);\n }\n\n /**\n * Promisify spawn process\n */\n protected promisifyProcess(\n command: childProcess.ChildProcess,\n isRejectWarnings = false,\n ): IBuildProcess {\n const promise = new Promise<number | null | string>((resolve, reject): void => {\n command.on('exit', (code) => {\n resolve(code);\n });\n\n command.on('close', (code: number): void => {\n resolve(code);\n });\n\n command.on('error', (message: string): void => {\n reject(message);\n });\n\n if (isRejectWarnings) {\n command.stderr?.on('data', (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('warning') || msg.includes('WARNING')) {\n resolve(1);\n }\n });\n }\n });\n\n command.stdout?.pipe(process.stdout);\n command.stderr?.pipe(process.stderr);\n\n return { promise, command };\n }\n\n /**\n * Build assets manifest file\n */\n protected async buildManifest(): Promise<void> {\n console.info(chalk.blue(`Building routes manifest file: ${this.pluginConfig.routesParsing}`));\n\n const isNodeParsing = this.pluginConfig.routesParsing === 'node';\n const serverConfig = ServerConfig.init(\n { isProd: this.isProd, mode: this.params.mode },\n { root: this.viteConfig.root, clientFile: this.pluginConfig.clientFile },\n );\n\n await SsrManifest.get(serverConfig, {\n buildDir: this.viteConfig.build.outDir,\n viteAliases: this.viteConfig.resolve.alias,\n basename: this.viteConfig.base,\n }).buildRoutesManifest(isNodeParsing);\n\n if (isNodeParsing) {\n this.cleanupClientRoutes();\n }\n }\n\n /**\n * Remove pathId from client route files\n */\n private cleanupClientRoutes(): void {\n const { routeFiles } = readMeta(this.buildDir);\n const files = new Set(Object.values(routeFiles ?? []));\n\n if (!files.size) {\n return;\n }\n\n files.forEach((file) => {\n const filepath = `${this.buildDir}/client/${file}`;\n\n try {\n const result = fs\n .readFileSync(filepath, { encoding: 'utf-8' })\n .replace(/(lazy:.*?\\((.*?)\\)),\\s?\".*?\"\\)/g, '$1)');\n\n fs.writeFileSync(filepath, result);\n } catch (e) {\n console.log(`Failed cleanup client route ${filepath}:`, e);\n }\n });\n }\n\n /**\n * Change general directive Disallow to Allow in robots.txt.\n */\n protected unlockRobots(): void {\n const robotsFile = `${this.buildDir}/client/robots.txt`;\n\n if (!fs.existsSync(robotsFile)) {\n console.warn(`Failed to unlock robots.txt, file not exist: ${robotsFile}`);\n\n return;\n }\n\n const data = fs\n .readFileSync(robotsFile, { encoding: 'utf-8' })\n .replace(/Disallow: \\/$/m, 'Allow: /');\n\n fs.writeFileSync(robotsFile, data, { encoding: 'utf-8' });\n\n console.info(chalk.blue('\\nrobots.txt unlocked.'));\n }\n\n /**\n * Eject cli to run app via node\n */\n protected eject(): void {\n const entrypoint = `${this.buildDir}/server/start.js`;\n const script =\n \"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\\n\\n\" +\n 'const VERSION = process.env.VERSION || \"1.0.0\";\\n' +\n 'const PORT = process.env.PORT || 3000;\\n' +\n 'const IS_HOST = process.env.IS_HOST || \"0\";\\n' +\n 'const ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\\n\\n' +\n `await runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Create serverless entrypoint\n */\n protected createServerless(): void {\n const entrypoint = `${this.buildDir}/server/serverless.js`;\n const script =\n \"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\\n\\n\" +\n `export default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Build specified entrypoint\n */\n protected async spawnBuild(\n name: string,\n buildOptions: string,\n params: ISpawnBuildParams = {},\n ): Promise<void> {\n const { mode, isNoWarnings } = this.params;\n const { focusOnly = this.params.focusOnly, shouldWait = false, env = {} } = params;\n const modeOpt = mode && !buildOptions.includes('--mode') ? `--mode ${mode}` : '';\n\n const buildProcess = this.promisifyProcess(\n childProcess.spawn(`vite build ${buildOptions} ${modeOpt} --emptyOutDir`, {\n signal: this.abortController!.signal,\n stdio: [process.stdin, 'pipe', 'pipe'],\n shell: true,\n env: {\n ...process.env,\n ...env,\n FORCE_COLOR: '2',\n SSR_BOOST_IS_SSR: createFocusOnly(focusOnly).isOnlyClient() ? '0' : '1',\n SSR_BOOST_ACTION: global.viteBoostAction,\n },\n }),\n isNoWarnings,\n );\n\n this.runningBuild.push({ name, buildProcess });\n\n if (!shouldWait) {\n return;\n }\n\n await this.waitLastBuild();\n }\n\n /**\n * Wait latest build and stop process in case error\n */\n protected async waitLastBuild(): Promise<void> {\n const latestProcess = this.runningBuild.at(-1);\n\n if (!latestProcess) {\n return;\n }\n\n const exitCode = await latestProcess.buildProcess.promise;\n\n processStop(exitCode, true);\n }\n\n /**\n * Run preview mode\n */\n protected runPreviewMode(): void {\n if (!this.hasPreviewModeExitListener) {\n process.on('exit', () => {\n this.abortController!.abort();\n });\n\n this.hasPreviewModeExitListener = true;\n }\n\n const { onFinish } = this.params;\n let buildCount = this.runningBuild.length;\n\n /**\n * Detect finished builds for process\n */\n const listener = (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('built in')) {\n buildCount -= 1;\n\n if (!buildCount) {\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.removeListener('data', listener);\n });\n createDevMarker(this.isProd, this.viteConfig);\n onFinish?.();\n }\n }\n };\n\n /**\n * Listen output for call onFinish\n */\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.on('data', listener);\n });\n }\n\n /**\n * Run app build\n */\n public async build(): Promise<void> {\n await this.makeConfig();\n // this is required step - build with different env may cause problems\n await viteResetCache();\n this.clearBuildFolder();\n\n const {\n clientOptions,\n serverOptions,\n onFinish,\n isWatch,\n focusOnly,\n isEject,\n isServerless,\n isUnlockRobots,\n } = this.params;\n const { outDir } = this.viteConfig.build;\n const focus = createFocusOnly(focusOnly);\n\n this.abortController = new AbortController();\n this.runningBuild = [];\n\n if (focus.isClient()) {\n /**\n * Build client\n */\n await this.spawnBuild('client', `${clientOptions} --outDir ${outDir}/client`, {\n shouldWait: !isWatch,\n });\n }\n\n /**\n * Build server\n */\n if (focus.isServer()) {\n await this.spawnBuild(\n 'server',\n `${serverOptions} --outDir ${outDir}/server --ssr ${this.pluginConfig.serverFile}`,\n {\n shouldWait: !isWatch,\n },\n );\n\n if (!isWatch) {\n await this.buildManifest();\n\n if (isEject) {\n this.eject();\n }\n\n if (isServerless) {\n this.createServerless();\n }\n }\n }\n\n /**\n * Build additional entrypoint\n */\n const { entrypoint } = this.pluginConfig;\n\n if (entrypoint?.length && focus.isEntrypoint()) {\n for (const { name, type, serverFile, buildOptions = '' } of entrypoint) {\n const cliOptions = serverFile && type === 'ssr' ? `--ssr ${serverFile}` : '';\n\n await this.spawnBuild(name, `${buildOptions} ${cliOptions} --outDir ${outDir}/${name}`, {\n shouldWait: !isWatch,\n focusOnly: type === 'ssr' ? 'server' : 'client',\n env: {\n SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME: name,\n },\n });\n }\n }\n\n /**\n * Preview mode\n */\n if (isWatch) {\n this.runPreviewMode();\n\n return;\n }\n\n if (isUnlockRobots) {\n this.unlockRobots();\n }\n\n createDevMarker(this.isProd, this.viteConfig);\n removeMeta(this.buildDir);\n onFinish?.();\n }\n}\n\nexport default Build;\n"],"names":["Build","isProd","nodeEnv","buildDir","viteConfig","pluginConfig","params","mode","clientOptions","serverOptions","focusOnly","isWatch","isUnlockRobots","isEject","isServerless","isNoWarnings","abortController","runningBuild","hasPreviewModeExitListener","constructor","this","async","resolveConfig","getPluginConfig","path","resolve","root","build","outDir","process","env","NODE_ENV","clearBuildFolder","fs","existsSync","rmSync","recursive","getIsProd","getNodeEnv","getRunningBuildNames","map","name","promisifyProcess","command","isRejectWarnings","promise","Promise","reject","on","code","message","stderr","buff","msg","Buffer","from","toString","includes","stdout","pipe","console","info","chalk","blue","routesParsing","isNodeParsing","serverConfig","ServerConfig","init","clientFile","SsrManifest","get","viteAliases","alias","basename","base","buildRoutesManifest","cleanupClientRoutes","routeFiles","readMeta","files","Set","Object","values","size","forEach","file","filepath","result","readFileSync","encoding","replace","writeFileSync","e","log","unlockRobots","robotsFile","warn","data","eject","entrypoint","createServerless","buildOptions","shouldWait","modeOpt","buildProcess","childProcess","spawn","signal","stdio","stdin","shell","FORCE_COLOR","SSR_BOOST_IS_SSR","createFocusOnly","isOnlyClient","SSR_BOOST_ACTION","global","viteBoostAction","push","waitLastBuild","latestProcess","at","exitCode","processStop","runPreviewMode","abort","onFinish","buildCount","length","listener","removeListener","createDevMarker","makeConfig","viteResetCache","focus","AbortController","isClient","spawnBuild","isServer","serverFile","buildManifest","isEntrypoint","type","cliOptions","SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME","removeMeta"],"mappings":"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"}
1
+ {"version":3,"file":"build.js","sources":["../../src/services/build.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { ResolvedConfig } from 'vite';\nimport { resolveConfig } from 'vite';\nimport viteResetCache from '@cli/helpers/vite-reset-cache';\nimport createFocusOnly from '@helpers/create-focus-only';\nimport { createDevMarker } from '@helpers/dev-marker';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport processStop from '@helpers/process-stop';\nimport ServerConfig from '@services/server-config';\nimport SsrManifest from '@services/ssr-manifest';\n\nexport interface IBuildParams {\n mode: string;\n onFinish?: () => void;\n clientOptions?: string;\n serverOptions?: string;\n focusOnly?: 'all' | 'app' | 'client' | 'server' | 'entrypoint';\n isWatch?: boolean;\n isUnlockRobots?: boolean;\n isEject?: boolean;\n isServerless?: boolean;\n isNoWarnings?: boolean;\n}\n\ninterface IBuildProcess {\n promise: Promise<number | null | string>;\n command: childProcess.ChildProcess;\n}\n\ninterface ISpawnBuildParams {\n shouldWait?: boolean;\n focusOnly?: IBuildParams['focusOnly'];\n env?: Record<string, string>;\n}\n\nexport interface IBuildEntrypoint {\n // entrypoint name\n name: string;\n type: 'spa' | 'ssr';\n // custom index file, default: indexFile from plugin config\n indexFile?: string;\n // custom entry file for replace in indexFile, default: undefined (do nothing)\n clientFile?: string;\n // custom server file, indexFile and clientFile will be ignored\n serverFile?: string;\n // additional options for vite build command\n buildOptions?: string;\n}\n\n/**\n * Build service\n */\nclass Build {\n /**\n * Is production build\n */\n protected isProd: boolean;\n\n /**\n * Node environment\n */\n protected nodeEnv: string;\n\n /**\n * Build folder\n */\n protected buildDir: string;\n\n /**\n * Vite config\n */\n protected viteConfig: ResolvedConfig;\n\n /**\n * Plugin config\n */\n protected pluginConfig: IPluginConfig;\n\n /**\n * Build params\n */\n protected params: IBuildParams = {\n mode: '',\n clientOptions: '',\n serverOptions: '',\n focusOnly: 'app',\n isWatch: false,\n isUnlockRobots: false,\n isEject: false,\n isServerless: false,\n isNoWarnings: false,\n };\n\n /**\n * Abort controller for builds\n */\n protected abortController: AbortController | null = null;\n\n /**\n * Running builds\n */\n protected runningBuild: { name: string; buildProcess: IBuildProcess }[] = [];\n\n /**\n * Listener for preview has attached\n */\n protected hasPreviewModeExitListener = false;\n\n /**\n * @constructor\n */\n public constructor(params: IBuildParams) {\n this.params = { ...this.params, ...params };\n }\n\n /**\n * Make config\n */\n protected async makeConfig(): Promise<void> {\n const { mode } = this.params;\n\n this.viteConfig = await resolveConfig({}, 'build', mode, 'production');\n this.pluginConfig = getPluginConfig(this.viteConfig);\n this.buildDir = path.resolve(this.viteConfig.root, this.viteConfig.build.outDir);\n this.nodeEnv = process.env.NODE_ENV || 'production';\n this.isProd = this.nodeEnv === 'production';\n }\n\n /**\n * Clear build folder\n */\n public clearBuildFolder(): void {\n // clear build folder\n if (fs.existsSync(this.buildDir)) {\n fs.rmSync(this.buildDir, { recursive: true });\n }\n }\n\n /**\n * Return is prod indicator value\n */\n public getIsProd(): boolean {\n return this.isProd;\n }\n\n /**\n * Return node env value\n */\n public getNodeEnv(): string {\n return this.nodeEnv;\n }\n\n /**\n * Return build names\n */\n public getRunningBuildNames(): string[] {\n return this.runningBuild.map(({ name }) => name);\n }\n\n /**\n * Promisify spawn process\n */\n protected promisifyProcess(\n command: childProcess.ChildProcess,\n isRejectWarnings = false,\n ): IBuildProcess {\n const promise = new Promise<number | null | string>((resolve, reject): void => {\n command.on('exit', (code) => {\n resolve(code);\n });\n\n command.on('close', (code: number): void => {\n resolve(code);\n });\n\n command.on('error', (message: string): void => {\n reject(message);\n });\n\n if (isRejectWarnings) {\n command.stderr?.on('data', (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('warning') || msg.includes('WARNING')) {\n resolve(1);\n }\n });\n }\n });\n\n command.stdout?.pipe(process.stdout);\n command.stderr?.pipe(process.stderr);\n\n return { promise, command };\n }\n\n /**\n * Build assets manifest file\n */\n protected buildManifest(): void {\n console.info(chalk.blue('Building routes manifest file'));\n\n const serverConfig = ServerConfig.init(\n { isProd: this.isProd, mode: this.params.mode },\n { root: this.viteConfig.root, clientFile: this.pluginConfig.clientFile },\n );\n\n SsrManifest.get(serverConfig, {\n buildDir: this.viteConfig.build.outDir,\n viteAliases: this.viteConfig.resolve.alias,\n basename: this.viteConfig.base,\n }).buildRoutesManifest();\n }\n\n /**\n * Change general directive Disallow to Allow in robots.txt.\n */\n protected unlockRobots(): void {\n const robotsFile = `${this.buildDir}/client/robots.txt`;\n\n if (!fs.existsSync(robotsFile)) {\n console.warn(`Failed to unlock robots.txt, file not exist: ${robotsFile}`);\n\n return;\n }\n\n const data = fs\n .readFileSync(robotsFile, { encoding: 'utf-8' })\n .replace(/Disallow: \\/$/m, 'Allow: /');\n\n fs.writeFileSync(robotsFile, data, { encoding: 'utf-8' });\n\n console.info(chalk.blue('\\nrobots.txt unlocked.'));\n }\n\n /**\n * Eject cli to run app via node\n */\n protected eject(): void {\n const entrypoint = `${this.buildDir}/server/start.js`;\n const script =\n \"import runProd from '@lomray/vite-ssr-boost/cli/run-prod.js';\\n\\n\" +\n 'const VERSION = process.env.VERSION || \"1.0.0\";\\n' +\n 'const PORT = process.env.PORT || 3000;\\n' +\n 'const IS_HOST = process.env.IS_HOST || \"0\";\\n' +\n 'const ONLY_CLIENT = process.env.ONLY_CLIENT || \"0\";\\n\\n' +\n `await runProd({\n version: VERSION,\n isHost: IS_HOST === '1',\n isPrintInfo: true,\n port: PORT,\n onlyClient: ONLY_CLIENT === '1',\n });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Create serverless entrypoint\n */\n protected createServerless(): void {\n const entrypoint = `${this.buildDir}/server/serverless.js`;\n const script =\n \"import runServerless from '@lomray/vite-ssr-boost/cli/run-serverless.js';\\n\\n\" +\n `export default await runServerless({ version: process.env.VERSION || \"1.0.0\" });\\n`;\n\n fs.writeFileSync(entrypoint, script, {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Build specified entrypoint\n */\n protected async spawnBuild(\n name: string,\n buildOptions: string,\n params: ISpawnBuildParams = {},\n ): Promise<void> {\n const { mode, isNoWarnings } = this.params;\n const { focusOnly = this.params.focusOnly, shouldWait = false, env = {} } = params;\n const modeOpt = mode && !buildOptions.includes('--mode') ? `--mode ${mode}` : '';\n\n const buildProcess = this.promisifyProcess(\n childProcess.spawn(`vite build ${buildOptions} ${modeOpt} --emptyOutDir`, {\n signal: this.abortController!.signal,\n stdio: [process.stdin, 'pipe', 'pipe'],\n shell: true,\n env: {\n ...process.env,\n ...env,\n FORCE_COLOR: '2',\n SSR_BOOST_IS_SSR: createFocusOnly(focusOnly).isOnlyClient() ? '0' : '1',\n SSR_BOOST_ACTION: global.viteBoostAction,\n },\n }),\n isNoWarnings,\n );\n\n this.runningBuild.push({ name, buildProcess });\n\n if (!shouldWait) {\n return;\n }\n\n await this.waitLastBuild();\n }\n\n /**\n * Wait latest build and stop process in case error\n */\n protected async waitLastBuild(): Promise<void> {\n const latestProcess = this.runningBuild.at(-1);\n\n if (!latestProcess) {\n return;\n }\n\n const exitCode = await latestProcess.buildProcess.promise;\n\n processStop(exitCode, true);\n }\n\n /**\n * Run preview mode\n */\n protected runPreviewMode(): void {\n if (!this.hasPreviewModeExitListener) {\n process.on('exit', () => {\n this.abortController!.abort();\n });\n\n this.hasPreviewModeExitListener = true;\n }\n\n const { onFinish } = this.params;\n let buildCount = this.runningBuild.length;\n\n /**\n * Detect finished builds for process\n */\n const listener = (buff: Uint8Array): void => {\n const msg = Buffer.from(buff).toString();\n\n if (msg.includes('built in')) {\n buildCount -= 1;\n\n if (!buildCount) {\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.removeListener('data', listener);\n });\n createDevMarker(this.isProd, this.viteConfig);\n onFinish?.();\n }\n }\n };\n\n /**\n * Listen output for call onFinish\n */\n this.runningBuild.forEach(({ buildProcess }) => {\n buildProcess.command.stdout?.on('data', listener);\n });\n }\n\n /**\n * Run app build\n */\n public async build(): Promise<void> {\n await this.makeConfig();\n // this is required step - build with different env may cause problems\n await viteResetCache();\n this.clearBuildFolder();\n\n const {\n clientOptions,\n serverOptions,\n onFinish,\n isWatch,\n focusOnly,\n isEject,\n isServerless,\n isUnlockRobots,\n } = this.params;\n const { outDir } = this.viteConfig.build;\n const focus = createFocusOnly(focusOnly);\n\n this.abortController = new AbortController();\n this.runningBuild = [];\n\n if (focus.isClient()) {\n /**\n * Build client\n */\n await this.spawnBuild('client', `${clientOptions} --outDir ${outDir}/client`, {\n shouldWait: !isWatch,\n });\n }\n\n /**\n * Build server\n */\n if (focus.isServer()) {\n await this.spawnBuild(\n 'server',\n `${serverOptions} --outDir ${outDir}/server --ssr ${this.pluginConfig.serverFile}`,\n {\n shouldWait: !isWatch,\n },\n );\n\n if (!isWatch) {\n this.buildManifest();\n\n if (isEject) {\n this.eject();\n }\n\n if (isServerless) {\n this.createServerless();\n }\n }\n }\n\n /**\n * Build additional entrypoint\n */\n const { entrypoint } = this.pluginConfig;\n\n if (entrypoint?.length && focus.isEntrypoint()) {\n for (const { name, type, serverFile, buildOptions = '' } of entrypoint) {\n const cliOptions = serverFile && type === 'ssr' ? `--ssr ${serverFile}` : '';\n\n await this.spawnBuild(name, `${buildOptions} ${cliOptions} --outDir ${outDir}/${name}`, {\n shouldWait: !isWatch,\n focusOnly: type === 'ssr' ? 'server' : 'client',\n env: {\n SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME: name,\n },\n });\n }\n }\n\n /**\n * Preview mode\n */\n if (isWatch) {\n this.runPreviewMode();\n\n return;\n }\n\n if (isUnlockRobots) {\n this.unlockRobots();\n }\n\n createDevMarker(this.isProd, this.viteConfig);\n onFinish?.();\n }\n}\n\nexport default Build;\n"],"names":["Build","isProd","nodeEnv","buildDir","viteConfig","pluginConfig","params","mode","clientOptions","serverOptions","focusOnly","isWatch","isUnlockRobots","isEject","isServerless","isNoWarnings","abortController","runningBuild","hasPreviewModeExitListener","constructor","this","async","resolveConfig","getPluginConfig","path","resolve","root","build","outDir","process","env","NODE_ENV","clearBuildFolder","fs","existsSync","rmSync","recursive","getIsProd","getNodeEnv","getRunningBuildNames","map","name","promisifyProcess","command","isRejectWarnings","promise","Promise","reject","on","code","message","stderr","buff","msg","Buffer","from","toString","includes","stdout","pipe","buildManifest","console","info","chalk","blue","serverConfig","ServerConfig","init","clientFile","SsrManifest","get","viteAliases","alias","basename","base","buildRoutesManifest","unlockRobots","robotsFile","warn","data","readFileSync","encoding","replace","writeFileSync","eject","entrypoint","createServerless","buildOptions","shouldWait","modeOpt","buildProcess","childProcess","spawn","signal","stdio","stdin","shell","FORCE_COLOR","SSR_BOOST_IS_SSR","createFocusOnly","isOnlyClient","SSR_BOOST_ACTION","global","viteBoostAction","push","waitLastBuild","latestProcess","at","exitCode","processStop","runPreviewMode","abort","onFinish","buildCount","length","listener","forEach","removeListener","createDevMarker","makeConfig","viteResetCache","focus","AbortController","isClient","spawnBuild","isServer","serverFile","isEntrypoint","type","cliOptions","SSR_BOOST_CUSTOM_ENTRYPOINT_BUILD_NAME"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sources":["../../src/services/logger.ts"],"sourcesContent":["import type { LogErrorOptions, Logger as ViteLogger, LogOptions } from 'vite';\n\nconst LogLevels = {\n error: 1,\n warn: 2,\n info: 3,\n};\n\ninterface ILoggerOptions {\n logLevel?: number;\n logFilter?: (params: ILogParams) => boolean;\n}\n\ninterface ILogParams {\n level: keyof typeof LogLevels;\n msg?: string;\n options?: LogErrorOptions;\n}\n\n/**\n * Default production logger\n */\nclass Logger implements ViteLogger {\n /**\n * @inheritDoc\n */\n public hasWarned: boolean;\n\n /**\n * Current log level\n */\n public loglevel: number;\n\n /**\n * Custom log filter\n * Return 'true' to skip output\n */\n public logFilter: ILoggerOptions['logFilter'] | undefined;\n\n /**\n * @constructor\n */\n public constructor({ logFilter, logLevel = 3 }: ILoggerOptions = {}) {\n this.loglevel = logLevel;\n this.logFilter = logFilter;\n }\n\n /**\n * @inheritDoc\n */\n public clearScreen(): void {\n //\n }\n\n /**\n * Should we skip current log depends on current log level\n */\n protected shouldSkipLog(level: number): boolean {\n return level > this.loglevel;\n }\n\n /**\n * Common log\n */\n protected log(params: ILogParams): void {\n const { level, msg, options } = params;\n\n if (this.shouldSkipLog(LogLevels[level]) || this.logFilter?.(params)) {\n return;\n }\n\n const args = [msg, options?.error].filter(Boolean);\n\n console[level](...args);\n }\n\n /**\n * @inheritDoc\n */\n public error(msg: string, options?: LogErrorOptions): void {\n this.log({ msg, options, level: 'error' });\n }\n\n /**\n * @inheritDoc\n */\n public hasErrorLogged(): boolean {\n return false;\n }\n\n /**\n * @inheritDoc\n */\n public info(msg: string, options: LogOptions): void {\n this.log({ msg, options, level: 'info' });\n }\n\n /**\n * @inheritDoc\n */\n public warn(msg: string, options: LogOptions): void {\n this.log({ msg, options, level: 'warn' });\n }\n\n /**\n * @inheritDoc\n */\n public warnOnce(msg: string, options: LogOptions): void {\n this.warn(msg, options);\n }\n}\n\nexport default Logger;\n"],"names":["LogLevels","error","warn","info","Logger","hasWarned","loglevel","logFilter","constructor","logLevel","this","clearScreen","shouldSkipLog","level","log","params","msg","options","args","filter","Boolean","console","hasErrorLogged","warnOnce"],"mappings":"AAEA,MAAMA,EAAY,CAChBC,MAAO,EACPC,KAAM,EACNC,KAAM,GAiBR,MAAMC,EAIGC,UAKAC,SAMAC,UAKPC,aAAmBD,UAAEA,EAASE,SAAEA,EAAW,GAAsB,CAAA,GAC/DC,KAAKJ,SAAWG,EAChBC,KAAKH,UAAYA,CAClB,CAKMI,cAEN,CAKSC,cAAcC,GACtB,OAAOA,EAAQH,KAAKJ,QACrB,CAKSQ,IAAIC,GACZ,MAAMF,MAAEA,EAAKG,IAAEA,EAAGC,QAAEA,GAAYF,EAEhC,GAAIL,KAAKE,cAAcZ,EAAUa,KAAWH,KAAKH,YAAYQ,GAC3D,OAGF,MAAMG,EAAO,CAACF,EAAKC,GAAShB,OAAOkB,OAAOC,SAE1CC,QAAQR,MAAUK,EACnB,CAKMjB,MAAMe,EAAaC,GACxBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,SACjC,CAKMS,iBACL,OAAO,CACR,CAKMnB,KAAKa,EAAaC,GACvBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,QACjC,CAKMX,KAAKc,EAAaC,GACvBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,QACjC,CAKMU,SAASP,EAAaC,GAC3BP,KAAKR,KAAKc,EAAKC,EAChB"}
1
+ {"version":3,"file":"logger.js","sources":["../../src/services/logger.ts"],"sourcesContent":["import type { LogErrorOptions, Logger as ViteLogger, LogOptions } from 'vite';\n\nconst LogLevels = {\n error: 1,\n warn: 2,\n info: 3,\n};\n\ninterface ILoggerOptions {\n logLevel?: number;\n logFilter?: (params: ILogParams) => boolean;\n}\n\ninterface ILogParams {\n level: keyof typeof LogLevels;\n msg?: string;\n options?: LogErrorOptions;\n}\n\n/**\n * Default production logger\n */\nclass Logger implements ViteLogger {\n /**\n * @inheritDoc\n */\n public hasWarned: boolean;\n\n /**\n * Current log level\n */\n public loglevel: number;\n\n /**\n * Custom log filter\n * Return 'true' to skip output\n */\n public logFilter: ILoggerOptions['logFilter'] | undefined;\n\n /**\n * @constructor\n */\n public constructor({ logFilter, logLevel = 3 }: ILoggerOptions = {}) {\n this.loglevel = logLevel;\n this.logFilter = logFilter;\n }\n\n /**\n * @inheritDoc\n */\n public clearScreen(): void {\n //\n }\n\n /**\n * Should we skip current log depends on current log level\n */\n protected shouldSkipLog(level: number): boolean {\n return level > this.loglevel;\n }\n\n /**\n * Common log\n */\n protected log(params: ILogParams): void {\n const { level, msg, options } = params;\n\n if (this.shouldSkipLog(LogLevels[level]) || this.logFilter?.(params)) {\n return;\n }\n\n const args = [msg, options?.error].filter(Boolean);\n\n console[level](...args);\n }\n\n /**\n * @inheritDoc\n */\n public error(msg: string, options?: LogErrorOptions): void {\n this.log({ msg, options, level: 'error' });\n }\n\n /**\n * @inheritDoc\n */\n public hasErrorLogged(): boolean {\n return false;\n }\n\n /**\n * @inheritDoc\n */\n public info(msg: string, options: LogOptions): void {\n this.log({ msg, options, level: 'info' });\n }\n\n /**\n * @inheritDoc\n */\n public warn(msg: string, options: LogOptions): void {\n this.log({ msg, options, level: 'warn' });\n }\n\n /**\n * @inheritDoc\n */\n public warnOnce(msg: string, options: LogOptions): void {\n this.warn(msg, options);\n }\n}\n\nexport default Logger;\n"],"names":["LogLevels","error","warn","info","Logger","hasWarned","loglevel","logFilter","constructor","logLevel","this","clearScreen","shouldSkipLog","level","log","params","msg","options","args","filter","Boolean","console","hasErrorLogged","warnOnce"],"mappings":"AAEA,MAAMA,EAAY,CAChBC,MAAO,EACPC,KAAM,EACNC,KAAM,GAiBR,MAAMC,EAIGC,UAKAC,SAMAC,UAKPC,aAAmBD,UAAEA,EAASE,SAAEA,EAAW,GAAsB,CAAA,GAC/DC,KAAKJ,SAAWG,EAChBC,KAAKH,UAAYA,EAMZI,eAOGC,cAAcC,GACtB,OAAOA,EAAQH,KAAKJ,SAMZQ,IAAIC,GACZ,MAAMF,MAAEA,EAAKG,IAAEA,EAAGC,QAAEA,GAAYF,EAEhC,GAAIL,KAAKE,cAAcZ,EAAUa,KAAWH,KAAKH,YAAYQ,GAC3D,OAGF,MAAMG,EAAO,CAACF,EAAKC,GAAShB,OAAOkB,OAAOC,SAE1CC,QAAQR,MAAUK,GAMbjB,MAAMe,EAAaC,GACxBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,UAM3BS,iBACL,OAAO,EAMFnB,KAAKa,EAAaC,GACvBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,SAM3BX,KAAKc,EAAaC,GACvBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,SAM3BU,SAASP,EAAaC,GAC3BP,KAAKR,KAAKc,EAAKC"}
@@ -96,11 +96,13 @@ declare class ParseRoutes {
96
96
  */
97
97
  private static processRouteFileCode;
98
98
  /**
99
- * Inject pathId to sync routes
99
+ * Inject pathId to sync/async routes
100
+ * Add async wrapper (see import-routes)
100
101
  */
101
102
  /**
102
- * Inject pathId to sync routes
103
+ * Inject pathId to sync/async routes
104
+ * Add async wrapper (see import-routes)
103
105
  */
104
- static injectPathId(code: string): string;
106
+ static handleRoutes(code: string, shouldAddPathId: boolean): string;
105
107
  }
106
108
  export { ParseRoutes as default, TRoutesTree };
@@ -1,2 +1,2 @@
1
- import e from"fs";import{resolve as t}from"node:path";import r from"path";import n from"@babel/generator";import*as a from"@babel/parser";import o from"@babel/traverse";import{isObjectProperty as i,isIdentifier as s,isJSXElement as l,isJSXIdentifier as p,isArrayExpression as c,objectProperty as u,identifier as m,stringLiteral as f,isObjectExpression as d}from"@babel/types";import h from"./path-normalize.js";const y=n.default??n,v=o.default??o;class E{pathNormalize;config;constructor(e,t){this.config=e,this.pathNormalize=new h(e,t)}parse(){const{clientFile:e,root:r}=this.config.getParams(),n=t(r,e),a=this.findRoutesEntrypoint(n);if(!a?.routesPath)throw new Error(`Unable to find routes file import in ${e}`);const{routesPath:o,exportName:i}=a,s=this.resolveFilename(o,n);return this.recursiveBuildRoutesTree(s,i)}parseFile(t){try{const r=e.readFileSync(t,"utf-8");return a.parse(r,{sourceType:"module",plugins:["typescript","jsx"]})}catch(e){return null}}getImportPath(e,t){let r=null,n=null;return v(e,{ImportDeclaration(e){const a=e.node;a.specifiers.forEach((e=>{e.local.name===t&&(n="ImportDefaultSpecifier"===e.type?null:t,r=a.source.value)}))}}),{routesPath:r,exportName:n}}findRoutesDefinition(e,t){let r=t;if(v(e,{ExportNamedDeclaration({node:e}){!e.declaration&&e.specifiers.length>0&&e.specifiers.forEach((e=>{const n=e.exported.name;null===t&&"ExportSpecifier"===e.type?"default"===e.local.name&&(r=n):n===t&&(r=e.local.name)}))},ExportDefaultDeclaration({node:e}){null===t&&("Identifier"===e.declaration.type?r=e.declaration.name:"VariableDeclaration"===e.declaration.type&&(r=e.declaration.declarations[0].id.name))}}),r){let t=null;return v(e,{VariableDeclaration({node:e}){e.declarations.forEach((n=>{n.id.name===r&&(t=e)}))}}),t}return null}findRoutesEntrypoint(e){const t=this.parseFile(e);let r=null;return t?(v(t,{CallExpression({node:e}){"entryClient"===e.callee.name&&e.arguments.length>=2&&"Identifier"===e.arguments[1].type&&(r=e.arguments[1].name)}}),this.getImportPath(t,r)):r}resolveFilename(e,t){let n=e;(e.startsWith("./")||e.startsWith("../"))&&t&&(n=r.resolve(r.dirname(t),e));const a=this.pathNormalize.getAppPath(n,!0);return this.pathNormalize.findAppFile(a)}parseRoutesArray(e,t,r){const n=[];return e.forEach(((e,a)=>{if("ObjectExpression"===e.type){const o={index:a,import:"",children:[]};e.properties.forEach((e=>{const n=e;if("children"===n.key.name&&"ArrayExpression"===n.value.type&&(o.children=this.parseRoutesArray(n.value.elements,t,r)),"lazy"===n.key.name&&"ArrowFunctionExpression"===n.value.type){const e=n.value.body;if("CallExpression"===e.type&&"Import"===e.callee.type){const[t]=e.arguments;"StringLiteral"===t.type&&(o.import=t.value)}}if("Component"===n.key.name&&"Identifier"===n.value.type){const e=n.value.name,{path:r}=t[e]??{};r&&(o.import=r)}if("element"===n.key.name&&"JSXElement"===n.value.type){const e=n.value?.openingElement?.name?.name,{path:r}=t[e]??{};r&&(o.import=r)}if("children"===n.key.name&&"Identifier"===n.value.type){const e=n.value.name,{path:a,isDefault:i}=t[e]??{};if(a){const t=this.resolveFilename(a,r);t&&(o.children=this.recursiveBuildRoutesTree(t,i?null:e))}}})),(o.import||o.children.length>0)&&n.push(o)}})),n}static parseImportsMap(e){const t={};return v(e,{ImportDeclaration(e){const r=e.node;r.specifiers.forEach((e=>{t[e.local.name]={path:r.source.value,isDefault:"ImportDefaultSpecifier"===e.type}}))}}),t}recursiveBuildRoutesTree(e,t=null){if(!e)return[];const r=this.parseFile(e);if(!r)return[];const n=this.findRoutesDefinition(r,t),a=[];if(!n)return a;const o=E.parseImportsMap(r),i=n.declarations[0].init?.elements;return a.push(...this.parseRoutesArray(i,o,e)),a}static processRouteFileCode(e,t){e.node.properties.forEach((r=>{if(i(r)&&s(r.key)){if("element"===r.key.name||"Component"===r.key.name){let n="";l(r.value)&&p(r.value.openingElement.name)?n=r.value.openingElement.name.name:s(r.value)&&(n=r.value.name);const a=e.findParent((e=>c(e.node))),o=t[n]?.path;if(a&&o){const t=u(m("pathId"),f(o));e.node.properties.splice(e.node.properties.indexOf(r)+1,0,t)}}"children"===r.key.name&&c(r.value)&&r.value.elements.forEach((e=>{d(e)&&E.processRouteFileCode({node:e},t)}))}}))}static injectPathId(e){if(!e)return e;const t=a.parse(e,{sourceType:"module",plugins:["typescript","jsx"]});if(!t)return e;const r=E.parseImportsMap(t);return v(t,{ObjectExpression(e){E.processRouteFileCode(e,r)}}),y(t,{retainLines:!0}).code}}export{E as default};
1
+ import e from"fs";import{resolve as t}from"node:path";import r from"path";import n from"@babel/generator";import*as a from"@babel/parser";import i from"@babel/traverse";import{isObjectProperty as o,isIdentifier as l,isBooleanLiteral as s,booleanLiteral as p,isArrayExpression as u,objectProperty as c,identifier as m,stringLiteral as f,isJSXElement as d,isJSXIdentifier as h,isObjectExpression as y}from"@babel/types";import v from"../constants/plugin-name.js";import g from"./path-normalize.js";const E=n.default??n,x=i.default??i;class I{pathNormalize;config;constructor(e,t){this.config=e,this.pathNormalize=new g(e,t)}parse(){const{clientFile:e,root:r}=this.config.getParams(),n=t(r,e),a=this.findRoutesEntrypoint(n);if(!a?.routesPath)throw new Error(`Unable to find routes file import in ${e}`);const{routesPath:i,exportName:o}=a,l=this.resolveFilename(i,n);return this.recursiveBuildRoutesTree(l,o)}parseFile(t){try{const r=e.readFileSync(t,"utf-8");return a.parse(r,{sourceType:"module",plugins:["typescript","jsx"]})}catch(e){return null}}getImportPath(e,t){let r=null,n=null;return x(e,{ImportDeclaration(e){const a=e.node;a.specifiers.forEach((e=>{e.local.name===t&&(n="ImportDefaultSpecifier"===e.type?null:t,r=a.source.value)}))}}),{routesPath:r,exportName:n}}findRoutesDefinition(e,t){let r=t;if(x(e,{ExportNamedDeclaration({node:e}){!e.declaration&&e.specifiers.length>0&&e.specifiers.forEach((e=>{const n=e.exported.name;null===t&&"ExportSpecifier"===e.type?"default"===e.local.name&&(r=n):n===t&&(r=e.local.name)}))},ExportDefaultDeclaration({node:e}){null===t&&("Identifier"===e.declaration.type?r=e.declaration.name:"VariableDeclaration"===e.declaration.type&&(r=e.declaration.declarations[0].id.name))}}),r){let t=null;return x(e,{VariableDeclaration({node:e}){e.declarations.forEach((n=>{n.id.name===r&&(t=e)}))}}),t}return null}findRoutesEntrypoint(e){const t=this.parseFile(e);let r=null;return t?(x(t,{CallExpression({node:e}){"entryClient"===e.callee.name&&e.arguments.length>=2&&"Identifier"===e.arguments[1].type&&(r=e.arguments[1].name)}}),this.getImportPath(t,r)):r}resolveFilename(e,t){let n=e;(e.startsWith("./")||e.startsWith("../"))&&t&&(n=r.resolve(r.dirname(t),e));const a=this.pathNormalize.getAppPath(n,!0);return this.pathNormalize.findAppFile(a)}parseRoutesArray(e,t,r){const n=[];return e.forEach(((e,a)=>{if("ObjectExpression"===e.type){const i={index:a,import:"",children:[]};e.properties.forEach((e=>{const n=e;if("children"===n.key.name&&"ArrayExpression"===n.value.type&&(i.children=this.parseRoutesArray(n.value.elements,t,r)),"lazy"===n.key.name&&"ArrowFunctionExpression"===n.value.type){const e=n.value.body;if("CallExpression"===e.type&&"Import"===e.callee.type){const[t]=e.arguments;"StringLiteral"===t.type&&(i.import=t.value)}}if("Component"===n.key.name&&"Identifier"===n.value.type){const e=n.value.name,{path:r}=t[e]??{};r&&(i.import=r)}if("element"===n.key.name&&"JSXElement"===n.value.type){const e=n.value?.openingElement?.name?.name,{path:r}=t[e]??{};r&&(i.import=r)}if("children"===n.key.name&&"Identifier"===n.value.type){const e=n.value.name,{path:a,isDefault:o}=t[e]??{};if(a){const t=this.resolveFilename(a,r);t&&(i.children=this.recursiveBuildRoutesTree(t,o?null:e))}}})),(i.import||i.children.length>0)&&n.push(i)}})),n}static parseImportsMap(e){const t={};return x(e,{ImportDeclaration(e){const r=e.node;r.specifiers.forEach((e=>{t[e.local.name]={path:r.source.value,isDefault:"ImportDefaultSpecifier"===e.type}}))}}),t}recursiveBuildRoutesTree(e,t=null){if(!e)return[];const r=this.parseFile(e);if(!r)return[];const n=this.findRoutesDefinition(r,t),a=[];if(!n)return a;const i=I.parseImportsMap(r),o=n.declarations[0].init?.elements;return a.push(...this.parseRoutesArray(o,i,e)),a}static processRouteFileCode(e,t,r,n){e.node.properties.forEach((a=>{if(o(a)&&l(a.key)){if("lazy"===a.key.name&&"ArrowFunctionExpression"===a.value.type){const t=a.value.body,i=e.node.properties.findIndex((e=>o(e)&&l(e.key)&&s(e.value)&&"isOnlyClient"===e.key.name&&e.value.value));if(a.value={type:"CallExpression",callee:{type:"Identifier",name:"n"},arguments:-1!==i?[a.value,p(!0)]:[a.value]},-1!==i&&e.node.properties.splice(i,1),n(),"CallExpression"===t.type&&"Import"===t.callee.type){const[n]=t.arguments,i=e.findParent?.((e=>u(e.node)));if(i&&"StringLiteral"===n.type&&n.value&&r){const t=c(m("pathId"),f(n.value));e.node.properties.splice(e.node.properties.indexOf(a)+1,0,t)}}}if("element"===a.key.name||"Component"===a.key.name){let n="";d(a.value)&&h(a.value.openingElement.name)?n=a.value.openingElement.name.name:l(a.value)&&(n=a.value.name);const i=e.findParent?.((e=>u(e.node))),o=t[n]?.path;if(i&&o&&r){const t=c(m("pathId"),f(o));e.node.properties.splice(e.node.properties.indexOf(a)+1,0,t)}}"children"===a.key.name&&u(a.value)&&a.value.elements.forEach((e=>{y(e)&&I.processRouteFileCode({node:e},t,r,n)}))}}))}static handleRoutes(e,t){if(!e)return e;const r=a.parse(e,{sourceType:"module",plugins:["typescript","jsx"]});if(!r)return e;const n=I.parseImportsMap(r);let i=!1;return x(r,{ObjectExpression(e){I.processRouteFileCode(e,n,t,(()=>{i=!0}))}}),i&&r.program.body.unshift({type:"ImportDeclaration",specifiers:[{type:"ImportDefaultSpecifier",local:{type:"Identifier",name:"n"}}],source:{type:"StringLiteral",value:`${v}/helpers/import-route`}}),E(r,{retainLines:!0}).code}}export{I as default};
2
2
  //# sourceMappingURL=parse-routes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"parse-routes.js","sources":["../../src/services/parse-routes.ts"],"sourcesContent":["import fs from 'fs';\nimport { resolve } from 'node:path';\nimport path from 'path';\nimport babelGenerate from '@babel/generator';\nimport type * as GenerateTypes from '@babel/generator';\nimport * as parser from '@babel/parser';\nimport type { ParseResult } from '@babel/parser';\nimport babelTraverse from '@babel/traverse';\nimport type * as TraverseTypes from '@babel/traverse';\nimport type {\n CallExpression,\n File as BabelFile,\n VariableDeclaration,\n ObjectExpression,\n} from '@babel/types';\nimport {\n isObjectProperty,\n isIdentifier,\n identifier,\n stringLiteral,\n objectProperty,\n isArrayExpression,\n isJSXElement,\n isJSXIdentifier,\n isObjectExpression,\n} from '@babel/types';\nimport type { Alias } from 'vite';\nimport PathNormalize from '@services/path-normalize';\nimport type ServerConfig from '@services/server-config';\n//\n// @ts-expect-error known import problem\nconst generate = (babelGenerate.default ?? babelGenerate) as (typeof GenerateTypes)['default'];\n// @ts-expect-error known import problem\nconst traverse = (babelTraverse.default ?? babelTraverse) as (typeof TraverseTypes)['default'];\n\ninterface IPathImport {\n routesPath: string | null;\n exportName: string | null;\n}\n\ninterface IMapImports {\n [name: string]: {\n path: string;\n isDefault: boolean; // is default import?\n };\n}\n\nexport type TRoutesTree = {\n index: number;\n import: string;\n children: TRoutesTree[];\n};\n\n/**\n * Parse react router routes array\n */\nclass ParseRoutes {\n /**\n * Path normalize service\n */\n protected readonly pathNormalize: PathNormalize;\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * @constructor\n */\n constructor(config: ServerConfig, viteAliases?: Alias[]) {\n this.config = config;\n this.pathNormalize = new PathNormalize(config, viteAliases);\n }\n\n /**\n * Parse routes\n */\n public parse(): TRoutesTree[] {\n const { clientFile, root } = this.config.getParams();\n\n const clientEntrypoint = resolve(root, clientFile);\n const routesEntrypoint = this.findRoutesEntrypoint(clientEntrypoint);\n\n if (!routesEntrypoint?.routesPath) {\n throw new Error(`Unable to find routes file import in ${clientFile}`);\n }\n\n const { routesPath, exportName } = routesEntrypoint;\n const routeFilepath = this.resolveFilename(routesPath, clientEntrypoint);\n\n return this.recursiveBuildRoutesTree(routeFilepath, exportName);\n }\n\n /**\n * Parse file and return ast\n */\n private parseFile(filename: string): ParseResult<BabelFile> | null {\n try {\n const code = fs.readFileSync(filename, 'utf-8');\n\n return parser.parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n });\n } catch (e) {\n return null;\n }\n }\n\n /**\n * Find route import filepath\n */\n private getImportPath(\n ast: ParseResult<BabelFile>,\n importName: string | null,\n ): IPathImport | null {\n let routesPath: string | null = null;\n let exportName: string | null = null;\n\n traverse(ast, {\n ImportDeclaration(nodePath) {\n const importNode = nodePath.node;\n\n importNode.specifiers.forEach((specifier) => {\n if (specifier.local.name === importName) {\n exportName = specifier.type === 'ImportDefaultSpecifier' ? null : importName;\n routesPath = importNode.source.value;\n }\n });\n },\n });\n\n return {\n routesPath,\n exportName,\n };\n }\n\n /**\n * Find routes array inside code\n */\n private findRoutesDefinition(\n ast: ParseResult<BabelFile>,\n exportName: string | null,\n ): null | VariableDeclaration {\n let exportNameResolved = exportName;\n\n // noinspection JSUnusedGlobalSymbols\n traverse(ast, {\n ExportNamedDeclaration({ node }) {\n if (!node.declaration && node.specifiers.length > 0) {\n node.specifiers.forEach((specifier) => {\n // @ts-expect-error missing in types\n const exportedName = specifier.exported.name as string;\n\n if (exportName === null && specifier.type === 'ExportSpecifier') {\n if (specifier.local.name === 'default') {\n exportNameResolved = exportedName;\n }\n } else if (exportedName === exportName) {\n // @ts-expect-error missing in types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n exportNameResolved = specifier.local.name as string;\n }\n });\n }\n },\n ExportDefaultDeclaration({ node }) {\n if (exportName === null) {\n if (node.declaration.type === 'Identifier') {\n exportNameResolved = node.declaration.name;\n // @ts-expect-error missing in types\n } else if (node.declaration.type === 'VariableDeclaration') {\n // @ts-expect-error missing in types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n exportNameResolved = node.declaration.declarations[0].id.name as string;\n }\n }\n },\n });\n\n if (exportNameResolved) {\n let variableNode: VariableDeclaration | null = null;\n\n traverse(ast, {\n VariableDeclaration({ node }) {\n node.declarations.forEach((declaration) => {\n // @ts-expect-error missing in types\n if (declaration.id.name === exportNameResolved) {\n variableNode = node;\n }\n });\n },\n });\n\n return variableNode;\n }\n\n return null;\n }\n\n /**\n * Entrypoint routes file\n */\n private findRoutesEntrypoint(clientEntrypoint: string): IPathImport | null {\n const ast = this.parseFile(clientEntrypoint);\n\n let routesVariable: string | null = null;\n\n if (!ast) {\n return routesVariable;\n }\n\n traverse(ast, {\n CallExpression({ node }) {\n if (\n // @ts-expect-error missing in types\n node.callee.name === 'entryClient' &&\n node.arguments.length >= 2 &&\n node.arguments[1].type === 'Identifier'\n ) {\n routesVariable = node.arguments[1].name;\n }\n },\n });\n\n return this.getImportPath(ast, routesVariable);\n }\n\n /**\n * Resolve route filename import\n */\n private resolveFilename(filename: string, relativeFile?: string): string | null {\n let resolvedFilename = filename;\n\n if ((filename.startsWith('./') || filename.startsWith('../')) && relativeFile) {\n resolvedFilename = path.resolve(path.dirname(relativeFile), filename);\n }\n\n const filepath = this.pathNormalize.getAppPath(resolvedFilename, true);\n\n return this.pathNormalize.findAppFile(filepath!);\n }\n\n /**\n * Parse ast array routes objects\n */\n private parseRoutesArray(\n elements: TraverseTypes.Node[],\n importsMap: IMapImports,\n relativeFile: string,\n ): TRoutesTree[] {\n const results: TRoutesTree[] = [];\n\n elements.forEach((node, index) => {\n if (node.type === 'ObjectExpression') {\n const routeInfo: TRoutesTree = { index, import: '', children: [] };\n\n node.properties.forEach((prop) => {\n const objectProp = prop as {\n key: { name: string };\n value: { type: string; elements: TraverseTypes.Node[] };\n };\n\n if (objectProp.key.name === 'children' && objectProp.value.type === 'ArrayExpression') {\n routeInfo.children = this.parseRoutesArray(\n objectProp.value.elements,\n importsMap,\n relativeFile,\n );\n }\n\n // async routes\n if (\n objectProp.key.name === 'lazy' &&\n objectProp.value.type === 'ArrowFunctionExpression'\n ) {\n // @ts-expect-error incorrect types\n const importCall = objectProp.value.body as CallExpression;\n\n if (importCall.type === 'CallExpression' && importCall.callee.type === 'Import') {\n const [importArg] = importCall.arguments;\n\n if (importArg.type === 'StringLiteral') {\n routeInfo.import = importArg.value;\n }\n }\n }\n\n // static routes: Component\n if (objectProp.key.name === 'Component' && objectProp.value.type === 'Identifier') {\n // @ts-expect-error incorrect types\n const importName = objectProp.value.name as string;\n const { path: importPath } = importsMap[importName] ?? {};\n\n if (importPath) {\n routeInfo.import = importPath;\n }\n }\n\n // static routes: element\n if (objectProp.key.name === 'element' && objectProp.value.type === 'JSXElement') {\n // @ts-expect-error incorrect types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const importName = objectProp.value?.openingElement?.name?.name as string;\n const { path: importPath } = importsMap[importName] ?? {};\n\n if (importPath) {\n routeInfo.import = importPath;\n }\n }\n\n if (objectProp.key.name === 'children' && objectProp.value.type === 'Identifier') {\n // @ts-expect-error incorrect types\n const importName = objectProp.value.name as string;\n const { path: importPath, isDefault } = importsMap[importName] ?? {};\n\n if (importPath) {\n const childrenFilePath = this.resolveFilename(importPath, relativeFile);\n\n if (childrenFilePath) {\n routeInfo.children = this.recursiveBuildRoutesTree(\n childrenFilePath,\n isDefault ? null : importName,\n );\n }\n }\n }\n });\n\n if (routeInfo.import || routeInfo.children.length > 0) {\n results.push(routeInfo);\n }\n }\n });\n\n return results;\n }\n\n /**\n * Parse imports map from ast\n */\n private static parseImportsMap(ast: ParseResult<BabelFile>): IMapImports {\n const importsMap: IMapImports = {};\n\n traverse(ast, {\n ImportDeclaration(nodePath) {\n const importNode = nodePath.node;\n\n importNode.specifiers.forEach((specifier) => {\n importsMap[specifier.local.name] = {\n path: importNode.source.value,\n isDefault: specifier.type === 'ImportDefaultSpecifier',\n };\n });\n },\n });\n\n return importsMap;\n }\n\n /**\n * Recursive build routes tree with dynamic imports\n */\n private recursiveBuildRoutesTree(\n filename: string | null,\n exportName: string | null = null,\n ): TRoutesTree[] {\n if (!filename) {\n return [];\n }\n\n const ast = this.parseFile(filename);\n\n if (!ast) {\n return [];\n }\n\n const routesNode = this.findRoutesDefinition(ast, exportName);\n const results: TRoutesTree[] = [];\n\n if (!routesNode) {\n return results;\n }\n\n const importsMap = ParseRoutes.parseImportsMap(ast);\n\n // @ts-expect-error missing types\n const elements = routesNode.declarations[0].init?.elements as TraverseTypes.Node[];\n\n results.push(...this.parseRoutesArray(elements, importsMap, filename));\n\n return results;\n }\n\n /**\n * Add pathId to static routes\n */\n private static processRouteFileCode(\n nodePath: TraverseTypes.NodePath<ObjectExpression>,\n importsMap: IMapImports,\n ): void {\n nodePath.node.properties.forEach((property) => {\n if (isObjectProperty(property) && isIdentifier(property.key)) {\n if (property.key.name === 'element' || property.key.name === 'Component') {\n let componentName = '';\n\n if (isJSXElement(property.value) && isJSXIdentifier(property.value.openingElement.name)) {\n componentName = property.value.openingElement.name.name;\n } else if (isIdentifier(property.value)) {\n componentName = property.value.name;\n }\n\n const parent = nodePath.findParent((p) => isArrayExpression(p.node));\n const importName = importsMap[componentName]?.path;\n\n if (parent && importName) {\n const pathIdProperty = objectProperty(identifier('pathId'), stringLiteral(importName));\n\n // Insert the pathId property right after the element property\n nodePath.node.properties.splice(\n nodePath.node.properties.indexOf(property) + 1,\n 0,\n pathIdProperty,\n );\n }\n }\n\n if (property.key.name === 'children' && isArrayExpression(property.value)) {\n // Process each object in the children array recursively\n property.value.elements.forEach((element) => {\n if (isObjectExpression(element)) {\n ParseRoutes.processRouteFileCode(\n { node: element } as TraverseTypes.NodePath<ObjectExpression>,\n importsMap,\n );\n }\n });\n }\n }\n });\n }\n\n /**\n * Inject pathId to sync routes\n */\n public static injectPathId(code: string): string {\n if (!code) {\n return code;\n }\n\n const ast = parser.parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n });\n\n if (!ast) {\n return code;\n }\n\n const importsMap = ParseRoutes.parseImportsMap(ast);\n\n traverse(ast, {\n ObjectExpression(nodePath) {\n ParseRoutes.processRouteFileCode(nodePath, importsMap);\n },\n });\n\n return generate(ast, {\n retainLines: true,\n }).code;\n }\n}\n\nexport default ParseRoutes;\n"],"names":["generate","babelGenerate","default","traverse","babelTraverse","ParseRoutes","pathNormalize","config","constructor","viteAliases","this","PathNormalize","parse","clientFile","root","getParams","clientEntrypoint","resolve","routesEntrypoint","findRoutesEntrypoint","routesPath","Error","exportName","routeFilepath","resolveFilename","recursiveBuildRoutesTree","parseFile","filename","code","fs","readFileSync","parser","sourceType","plugins","e","getImportPath","ast","importName","ImportDeclaration","nodePath","importNode","node","specifiers","forEach","specifier","local","name","type","source","value","findRoutesDefinition","exportNameResolved","ExportNamedDeclaration","declaration","length","exportedName","exported","ExportDefaultDeclaration","declarations","id","variableNode","VariableDeclaration","routesVariable","CallExpression","callee","arguments","relativeFile","resolvedFilename","startsWith","path","dirname","filepath","getAppPath","findAppFile","parseRoutesArray","elements","importsMap","results","index","routeInfo","import","children","properties","prop","objectProp","key","importCall","body","importArg","importPath","openingElement","isDefault","childrenFilePath","push","static","routesNode","parseImportsMap","init","property","isObjectProperty","isIdentifier","componentName","isJSXElement","isJSXIdentifier","parent","findParent","p","isArrayExpression","pathIdProperty","objectProperty","identifier","stringLiteral","splice","indexOf","element","isObjectExpression","processRouteFileCode","ObjectExpression","retainLines"],"mappings":"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"}
1
+ {"version":3,"file":"parse-routes.js","sources":["../../src/services/parse-routes.ts"],"sourcesContent":["import fs from 'fs';\nimport { resolve } from 'node:path';\nimport path from 'path';\nimport babelGenerate from '@babel/generator';\nimport type * as GenerateTypes from '@babel/generator';\nimport * as parser from '@babel/parser';\nimport type { ParseResult } from '@babel/parser';\nimport babelTraverse from '@babel/traverse';\nimport type * as TraverseTypes from '@babel/traverse';\nimport type {\n CallExpression,\n File as BabelFile,\n VariableDeclaration,\n ObjectExpression,\n} from '@babel/types';\nimport {\n isObjectProperty,\n isIdentifier,\n identifier,\n isBooleanLiteral,\n stringLiteral,\n booleanLiteral,\n objectProperty,\n isArrayExpression,\n isJSXElement,\n isJSXIdentifier,\n isObjectExpression,\n} from '@babel/types';\nimport type { Alias } from 'vite';\nimport PLUGIN_NAME from '@constants/plugin-name';\nimport PathNormalize from '@services/path-normalize';\nimport type ServerConfig from '@services/server-config';\n//\n// @ts-expect-error known import problem\nconst generate = (babelGenerate.default ?? babelGenerate) as (typeof GenerateTypes)['default'];\n// @ts-expect-error known import problem\nconst traverse = (babelTraverse.default ?? babelTraverse) as (typeof TraverseTypes)['default'];\n\ninterface IPathImport {\n routesPath: string | null;\n exportName: string | null;\n}\n\ninterface IMapImports {\n [name: string]: {\n path: string;\n isDefault: boolean; // is default import?\n };\n}\n\nexport type TRoutesTree = {\n index: number;\n import: string;\n children: TRoutesTree[];\n};\n\n/**\n * Parse react router routes array\n */\nclass ParseRoutes {\n /**\n * Path normalize service\n */\n protected readonly pathNormalize: PathNormalize;\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * @constructor\n */\n constructor(config: ServerConfig, viteAliases?: Alias[]) {\n this.config = config;\n this.pathNormalize = new PathNormalize(config, viteAliases);\n }\n\n /**\n * Parse routes\n */\n public parse(): TRoutesTree[] {\n const { clientFile, root } = this.config.getParams();\n\n const clientEntrypoint = resolve(root, clientFile);\n const routesEntrypoint = this.findRoutesEntrypoint(clientEntrypoint);\n\n if (!routesEntrypoint?.routesPath) {\n throw new Error(`Unable to find routes file import in ${clientFile}`);\n }\n\n const { routesPath, exportName } = routesEntrypoint;\n const routeFilepath = this.resolveFilename(routesPath, clientEntrypoint);\n\n return this.recursiveBuildRoutesTree(routeFilepath, exportName);\n }\n\n /**\n * Parse file and return ast\n */\n private parseFile(filename: string): ParseResult<BabelFile> | null {\n try {\n const code = fs.readFileSync(filename, 'utf-8');\n\n return parser.parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n });\n } catch (e) {\n return null;\n }\n }\n\n /**\n * Find route import filepath\n */\n private getImportPath(\n ast: ParseResult<BabelFile>,\n importName: string | null,\n ): IPathImport | null {\n let routesPath: string | null = null;\n let exportName: string | null = null;\n\n traverse(ast, {\n ImportDeclaration(nodePath) {\n const importNode = nodePath.node;\n\n importNode.specifiers.forEach((specifier) => {\n if (specifier.local.name === importName) {\n exportName = specifier.type === 'ImportDefaultSpecifier' ? null : importName;\n routesPath = importNode.source.value;\n }\n });\n },\n });\n\n return {\n routesPath,\n exportName,\n };\n }\n\n /**\n * Find routes array inside code\n */\n private findRoutesDefinition(\n ast: ParseResult<BabelFile>,\n exportName: string | null,\n ): null | VariableDeclaration {\n let exportNameResolved = exportName;\n\n // noinspection JSUnusedGlobalSymbols\n traverse(ast, {\n ExportNamedDeclaration({ node }) {\n if (!node.declaration && node.specifiers.length > 0) {\n node.specifiers.forEach((specifier) => {\n // @ts-expect-error missing in types\n const exportedName = specifier.exported.name as string;\n\n if (exportName === null && specifier.type === 'ExportSpecifier') {\n if (specifier.local.name === 'default') {\n exportNameResolved = exportedName;\n }\n } else if (exportedName === exportName) {\n // @ts-expect-error missing in types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n exportNameResolved = specifier.local.name as string;\n }\n });\n }\n },\n ExportDefaultDeclaration({ node }) {\n if (exportName === null) {\n if (node.declaration.type === 'Identifier') {\n exportNameResolved = node.declaration.name;\n // @ts-expect-error missing in types\n } else if (node.declaration.type === 'VariableDeclaration') {\n // @ts-expect-error missing in types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n exportNameResolved = node.declaration.declarations[0].id.name as string;\n }\n }\n },\n });\n\n if (exportNameResolved) {\n let variableNode: VariableDeclaration | null = null;\n\n traverse(ast, {\n VariableDeclaration({ node }) {\n node.declarations.forEach((declaration) => {\n // @ts-expect-error missing in types\n if (declaration.id.name === exportNameResolved) {\n variableNode = node;\n }\n });\n },\n });\n\n return variableNode;\n }\n\n return null;\n }\n\n /**\n * Entrypoint routes file\n */\n private findRoutesEntrypoint(clientEntrypoint: string): IPathImport | null {\n const ast = this.parseFile(clientEntrypoint);\n\n let routesVariable: string | null = null;\n\n if (!ast) {\n return routesVariable;\n }\n\n traverse(ast, {\n CallExpression({ node }) {\n if (\n // @ts-expect-error missing in types\n node.callee.name === 'entryClient' &&\n node.arguments.length >= 2 &&\n node.arguments[1].type === 'Identifier'\n ) {\n routesVariable = node.arguments[1].name;\n }\n },\n });\n\n return this.getImportPath(ast, routesVariable);\n }\n\n /**\n * Resolve route filename import\n */\n private resolveFilename(filename: string, relativeFile?: string): string | null {\n let resolvedFilename = filename;\n\n if ((filename.startsWith('./') || filename.startsWith('../')) && relativeFile) {\n resolvedFilename = path.resolve(path.dirname(relativeFile), filename);\n }\n\n const filepath = this.pathNormalize.getAppPath(resolvedFilename, true);\n\n return this.pathNormalize.findAppFile(filepath!);\n }\n\n /**\n * Parse ast array routes objects\n */\n private parseRoutesArray(\n elements: TraverseTypes.Node[],\n importsMap: IMapImports,\n relativeFile: string,\n ): TRoutesTree[] {\n const results: TRoutesTree[] = [];\n\n elements.forEach((node, index) => {\n if (node.type === 'ObjectExpression') {\n const routeInfo: TRoutesTree = { index, import: '', children: [] };\n\n node.properties.forEach((prop) => {\n const objectProp = prop as {\n key: { name: string };\n value: { type: string; elements: TraverseTypes.Node[] };\n };\n\n if (objectProp.key.name === 'children' && objectProp.value.type === 'ArrayExpression') {\n routeInfo.children = this.parseRoutesArray(\n objectProp.value.elements,\n importsMap,\n relativeFile,\n );\n }\n\n // async routes\n if (\n objectProp.key.name === 'lazy' &&\n objectProp.value.type === 'ArrowFunctionExpression'\n ) {\n // @ts-expect-error incorrect types\n const importCall = objectProp.value.body as CallExpression;\n\n if (importCall.type === 'CallExpression' && importCall.callee.type === 'Import') {\n const [importArg] = importCall.arguments;\n\n if (importArg.type === 'StringLiteral') {\n routeInfo.import = importArg.value;\n }\n }\n }\n\n // static routes: Component\n if (objectProp.key.name === 'Component' && objectProp.value.type === 'Identifier') {\n // @ts-expect-error incorrect types\n const importName = objectProp.value.name as string;\n const { path: importPath } = importsMap[importName] ?? {};\n\n if (importPath) {\n routeInfo.import = importPath;\n }\n }\n\n // static routes: element\n if (objectProp.key.name === 'element' && objectProp.value.type === 'JSXElement') {\n // @ts-expect-error incorrect types\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const importName = objectProp.value?.openingElement?.name?.name as string;\n const { path: importPath } = importsMap[importName] ?? {};\n\n if (importPath) {\n routeInfo.import = importPath;\n }\n }\n\n if (objectProp.key.name === 'children' && objectProp.value.type === 'Identifier') {\n // @ts-expect-error incorrect types\n const importName = objectProp.value.name as string;\n const { path: importPath, isDefault } = importsMap[importName] ?? {};\n\n if (importPath) {\n const childrenFilePath = this.resolveFilename(importPath, relativeFile);\n\n if (childrenFilePath) {\n routeInfo.children = this.recursiveBuildRoutesTree(\n childrenFilePath,\n isDefault ? null : importName,\n );\n }\n }\n }\n });\n\n if (routeInfo.import || routeInfo.children.length > 0) {\n results.push(routeInfo);\n }\n }\n });\n\n return results;\n }\n\n /**\n * Parse imports map from ast\n */\n private static parseImportsMap(ast: ParseResult<BabelFile>): IMapImports {\n const importsMap: IMapImports = {};\n\n traverse(ast, {\n ImportDeclaration(nodePath) {\n const importNode = nodePath.node;\n\n importNode.specifiers.forEach((specifier) => {\n importsMap[specifier.local.name] = {\n path: importNode.source.value,\n isDefault: specifier.type === 'ImportDefaultSpecifier',\n };\n });\n },\n });\n\n return importsMap;\n }\n\n /**\n * Recursive build routes tree with dynamic imports\n */\n private recursiveBuildRoutesTree(\n filename: string | null,\n exportName: string | null = null,\n ): TRoutesTree[] {\n if (!filename) {\n return [];\n }\n\n const ast = this.parseFile(filename);\n\n if (!ast) {\n return [];\n }\n\n const routesNode = this.findRoutesDefinition(ast, exportName);\n const results: TRoutesTree[] = [];\n\n if (!routesNode) {\n return results;\n }\n\n const importsMap = ParseRoutes.parseImportsMap(ast);\n\n // @ts-expect-error missing types\n const elements = routesNode.declarations[0].init?.elements as TraverseTypes.Node[];\n\n results.push(...this.parseRoutesArray(elements, importsMap, filename));\n\n return results;\n }\n\n /**\n * Add pathId to static routes\n */\n private static processRouteFileCode(\n nodePath: TraverseTypes.NodePath<ObjectExpression>,\n importsMap: IMapImports,\n shouldAddPathId: boolean,\n addImportRouteWrapper: () => void,\n ): void {\n nodePath.node.properties.forEach((property) => {\n if (isObjectProperty(property) && isIdentifier(property.key)) {\n // async routes\n if (property.key.name === 'lazy' && property.value.type === 'ArrowFunctionExpression') {\n const importCall = property.value.body as CallExpression;\n const isOnlyClientPropIndex = nodePath.node.properties.findIndex(\n (p) =>\n isObjectProperty(p) &&\n isIdentifier(p.key) &&\n isBooleanLiteral(p.value) &&\n p.key.name === 'isOnlyClient' &&\n p.value.value,\n );\n\n /**\n * Wrap lazy import with:\n * @see importRoute\n */\n property.value = {\n type: 'CallExpression',\n callee: {\n type: 'Identifier',\n name: 'n',\n },\n arguments:\n isOnlyClientPropIndex !== -1\n ? [property.value, booleanLiteral(true)]\n : [property.value],\n };\n\n if (isOnlyClientPropIndex !== -1) {\n nodePath.node.properties.splice(isOnlyClientPropIndex, 1);\n }\n\n addImportRouteWrapper();\n\n if (importCall.type === 'CallExpression' && importCall.callee.type === 'Import') {\n const [importArg] = importCall.arguments;\n // current object has part of array (inside array)\n const parent = nodePath.findParent?.((p) => isArrayExpression(p.node));\n\n if (\n parent &&\n importArg.type === 'StringLiteral' &&\n importArg.value &&\n shouldAddPathId\n ) {\n const pathIdProperty = objectProperty(\n identifier('pathId'),\n stringLiteral(importArg.value),\n );\n\n // Insert the pathId property right after the element property\n nodePath.node.properties.splice(\n nodePath.node.properties.indexOf(property) + 1,\n 0,\n pathIdProperty,\n );\n }\n }\n }\n\n if (property.key.name === 'element' || property.key.name === 'Component') {\n let componentName = '';\n\n if (isJSXElement(property.value) && isJSXIdentifier(property.value.openingElement.name)) {\n componentName = property.value.openingElement.name.name;\n } else if (isIdentifier(property.value)) {\n componentName = property.value.name;\n }\n\n // current object has part of array (inside array)\n const parent = nodePath.findParent?.((p) => isArrayExpression(p.node));\n const importName = importsMap[componentName]?.path;\n\n if (parent && importName && shouldAddPathId) {\n const pathIdProperty = objectProperty(identifier('pathId'), stringLiteral(importName));\n\n // Insert the pathId property right after the element property\n nodePath.node.properties.splice(\n nodePath.node.properties.indexOf(property) + 1,\n 0,\n pathIdProperty,\n );\n }\n }\n\n if (property.key.name === 'children' && isArrayExpression(property.value)) {\n // Process each object in the children array recursively\n property.value.elements.forEach((element) => {\n if (isObjectExpression(element)) {\n ParseRoutes.processRouteFileCode(\n { node: element } as TraverseTypes.NodePath<ObjectExpression>,\n importsMap,\n shouldAddPathId,\n addImportRouteWrapper,\n );\n }\n });\n }\n }\n });\n }\n\n /**\n * Inject pathId to sync/async routes\n * Add async wrapper (see import-routes)\n */\n public static handleRoutes(code: string, shouldAddPathId: boolean): string {\n if (!code) {\n return code;\n }\n\n const ast = parser.parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n });\n\n if (!ast) {\n return code;\n }\n\n const importsMap = ParseRoutes.parseImportsMap(ast);\n let shouldAddRoutesImport = false;\n\n traverse(ast, {\n ObjectExpression(nodePath): void {\n ParseRoutes.processRouteFileCode(nodePath, importsMap, shouldAddPathId, () => {\n shouldAddRoutesImport = true;\n });\n },\n });\n\n if (shouldAddRoutesImport) {\n ast.program.body.unshift({\n type: 'ImportDeclaration',\n specifiers: [\n {\n type: 'ImportDefaultSpecifier',\n local: {\n type: 'Identifier',\n name: 'n',\n },\n },\n ],\n source: {\n type: 'StringLiteral',\n value: `${PLUGIN_NAME}/helpers/import-route`,\n },\n });\n }\n\n return generate(ast, {\n retainLines: true,\n }).code;\n }\n}\n\nexport default ParseRoutes;\n"],"names":["generate","babelGenerate","default","traverse","babelTraverse","ParseRoutes","pathNormalize","config","constructor","viteAliases","this","PathNormalize","parse","clientFile","root","getParams","clientEntrypoint","resolve","routesEntrypoint","findRoutesEntrypoint","routesPath","Error","exportName","routeFilepath","resolveFilename","recursiveBuildRoutesTree","parseFile","filename","code","fs","readFileSync","parser","sourceType","plugins","e","getImportPath","ast","importName","ImportDeclaration","nodePath","importNode","node","specifiers","forEach","specifier","local","name","type","source","value","findRoutesDefinition","exportNameResolved","ExportNamedDeclaration","declaration","length","exportedName","exported","ExportDefaultDeclaration","declarations","id","variableNode","VariableDeclaration","routesVariable","CallExpression","callee","arguments","relativeFile","resolvedFilename","startsWith","path","dirname","filepath","getAppPath","findAppFile","parseRoutesArray","elements","importsMap","results","index","routeInfo","import","children","properties","prop","objectProp","key","importCall","body","importArg","importPath","openingElement","isDefault","childrenFilePath","push","static","routesNode","parseImportsMap","init","shouldAddPathId","addImportRouteWrapper","property","isObjectProperty","isIdentifier","isOnlyClientPropIndex","findIndex","p","isBooleanLiteral","booleanLiteral","splice","parent","findParent","isArrayExpression","pathIdProperty","objectProperty","identifier","stringLiteral","indexOf","componentName","isJSXElement","isJSXIdentifier","element","isObjectExpression","processRouteFileCode","shouldAddRoutesImport","ObjectExpression","program","unshift","PLUGIN_NAME","retainLines"],"mappings":"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"}