@lomray/vite-ssr-boost 2.7.0 → 2.8.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022-present, Lomray Software
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <h4 align='center'>Super easy framework based on Vite for create awesome SSR or SPA applications on React and React Router.</h4>
3
3
 
4
4
  <p align="center">
5
- <img src="./logo.png" alt="Vite SSR BOOST logo" width="250" height="250">
5
+ <img src="https://raw.githubusercontent.com/Lomray-Software/vite-ssr-boost/prod/logo.png" alt="Vite SSR BOOST logo" width="250" height="250">
6
6
  </p>
7
7
 
8
8
  ### Key features:
package/SECURITY.md ADDED
@@ -0,0 +1,26 @@
1
+ # Security Policy
2
+
3
+ ## Reporting Security Issues
4
+ If you discover a security vulnerability within our project, we appreciate your help in disclosing it to us in a responsible manner.
5
+
6
+ Please do not create a public GitHub issue for the security issue.
7
+
8
+ Instead, we kindly request you to report it via email to mikhail.yarmaliuk@lomray.com. This allows us to assess the risk and coordinate the fix before the vulnerability becomes public knowledge.
9
+
10
+ When reporting security issues, please provide the following information:
11
+
12
+ - Description of the issue
13
+ - Steps to reproduce the vulnerability
14
+ - Your contact information
15
+
16
+ We take all security reports seriously and will do our best to rapidly address the issue. We are grateful for your contributions to our security efforts, and we will acknowledge your report upon resolution.
17
+
18
+ ## Responsible Disclosure
19
+ We adhere to the principle of responsible disclosure. This means that if you discover a vulnerability, you agree to:
20
+
21
+ - Not disclose the issue publicly until we have had sufficient time to address it.
22
+ - Not exploit the issue for any malicious purpose.
23
+ - Not access or modify data without explicit permission from the owner.
24
+ We commit to handling your report with strict confidentiality and will keep you informed of the progress toward resolving the issue.
25
+
26
+ Thank you for helping us ensure the security and privacy.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lomray/vite-ssr-boost",
3
- "version": "2.7.0",
3
+ "version": "2.8.0-beta.2",
4
4
  "description": "Vite plugin for create awesome SSR or SPA applications on React.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -47,6 +47,7 @@
47
47
  "@lomray/prettier-config": "^2.0.1",
48
48
  "@rollup/plugin-terser": "^0.4.4",
49
49
  "@testing-library/react": "^15.0.7",
50
+ "@types/babel__generator": "^7.6.8",
50
51
  "@types/babel__traverse": "^7.20.6",
51
52
  "@types/chai": "^4.3.16",
52
53
  "@types/compression": "^1.7.5",
@@ -75,6 +76,7 @@
75
76
  "vitest": "^1.6.0"
76
77
  },
77
78
  "peerDependencies": {
79
+ "@babel/generator": ">=7.23.0",
78
80
  "@babel/parser": ">=7.23.0",
79
81
  "@babel/traverse": ">=7.23.0",
80
82
  "@types/express": ">=4.17.21",
@@ -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";const n=(e,r=!1)=>{if(r)return e;const t=[...e.matchAll(/import\s+(\w+)\sfrom\s+['"]([^'"]+)['"]/g)].reduce(((e,[,r,t])=>({[r]:t,...e})),{});return e.replace(/(.*?(?:Component|element):[\s<]*(\w+)[^,}]*),*/gs,((e,r,o)=>{const s=t?.[o];return s?`${r},pathId: '${s}',`:e}))},i=(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 l(l={}){const{routesPath:u,isNodeParsing:m=!1,isSSR:c=!1,isBuild:p=!1}=l,a=new Map,d={root:"",buildDir:""};return{name:`${t}-normalize-route`,enforce:"pre",transform(r,t){const[s]=e(t).split("?"),l=!u||t.includes(u);if(!t.includes("node_modules")&&[".js",".mjs",".ts",".tsx"].includes(s)&&l&&o(r))return a.set(t,""),{code:i(n(r,p),c&&(m||!p)),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])=>{a.has(r)&&a.set(r,e)}))},writeBundle(){if(!d.root)return;const[e]=r(d.root,d.buildDir).split("/client");s(e,{routeFiles:Object.fromEntries(a)})}}:{}}}export{l as default};
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};
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';\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 const imports: Record<string, string> = [\n ...code.matchAll(/import\\s+(\\w+)\\sfrom\\s+['\"]([^'\"]+)['\"]/g),\n ].reduce(\n (res, [, key, value]) => ({\n [key]: value,\n ...res,\n }),\n {},\n );\n\n return code.replace(\n /(.*?(?:Component|element):[\\s<]*(\\w+)[^,}]*),*/gs,\n (fullMatch, before: string, routeName: string) => {\n const importPath = imports?.[routeName];\n\n if (!importPath) {\n return fullMatch;\n }\n\n return `${before},pathId: '${importPath}',`;\n },\n );\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","imports","matchAll","reduce","res","key","value","replace","fullMatch","before","routeName","importPath","normalizeAsyncRoutes","hasPathId","modifiedCode","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":"0LAiBA,MAAMA,EAAsB,CAACC,EAAcC,GAAU,KACnD,GAAIA,EACF,OAAOD,EAGT,MAAME,EAAkC,IACnCF,EAAKG,SAAS,6CACjBC,QACA,CAACC,GAAK,CAAGC,EAAKC,MAAY,CACxBD,CAACA,GAAMC,KACJF,KAEL,CAAE,GAGJ,OAAOL,EAAKQ,QACV,oDACA,CAACC,EAAWC,EAAgBC,KAC1B,MAAMC,EAAaV,IAAUS,GAE7B,OAAKC,EAIE,GAAGF,cAAmBE,MAHpBH,CAGkC,GAE9C,EAMGI,EAAuB,CAACb,EAAcc,KAC1C,MAAMC,EAAef,EAAKQ,QACxB,gDACAM,EAAY,iBAAmB,eAGjC,OAAId,IAASe,EACJ,kBAAkBC,2BAAqCD,IAGzDf,CAAI,EAab,SAASiB,EAA0BC,EAA0B,IAC3D,MAAMC,WAAEA,EAAUC,cAAEA,GAAgB,EAAKC,MAAEA,GAAQ,EAAKpB,QAAEA,GAAU,GAAUiB,EACxEI,EAAa,IAAIC,IACjBC,EAAM,CAAEC,KAAM,GAAIC,SAAU,IAElC,MAAO,CACLC,KAAM,GAAGX,oBACTY,QAAS,MACTC,UAAU7B,EAAM8B,GACd,MAAOC,GAAWC,EAAQF,GAAIG,MAAM,KAC9BC,GAAgBf,GAAcW,EAAGK,SAAShB,GAEhD,IACEW,EAAGK,SAAS,iBACX,CAAC,MAAO,OAAQ,MAAO,QAAQA,SAASJ,IACxCG,GACAE,EAAapC,GAOhB,OAFAsB,EAAWe,IAAIP,EAAI,IAEZ,CACL9B,KAAMa,EAEJd,EAAoBC,EAAMC,GAE1BoB,IAAUD,IAAkBnB,IAE9BqC,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,CAAE,EAEV"}
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,CAAE,EAEV"}
@@ -74,6 +74,13 @@ declare class ParseRoutes {
74
74
  * Parse ast array routes objects
75
75
  */
76
76
  private parseRoutesArray;
77
+ /**
78
+ * Parse imports map from ast
79
+ */
80
+ /**
81
+ * Parse imports map from ast
82
+ */
83
+ private static parseImportsMap;
77
84
  /**
78
85
  * Recursive build routes tree with dynamic imports
79
86
  */
@@ -81,5 +88,19 @@ declare class ParseRoutes {
81
88
  * Recursive build routes tree with dynamic imports
82
89
  */
83
90
  private recursiveBuildRoutesTree;
91
+ /**
92
+ * Add pathId to static routes
93
+ */
94
+ /**
95
+ * Add pathId to static routes
96
+ */
97
+ private static processRouteFileCode;
98
+ /**
99
+ * Inject pathId to sync routes
100
+ */
101
+ /**
102
+ * Inject pathId to sync routes
103
+ */
104
+ static injectPathId(code: string): string;
84
105
  }
85
106
  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*as n from"@babel/parser";import a from"@babel/traverse";import i from"./path-normalize.js";const o=a.default??a;class l{pathNormalize;config;constructor(e,t){this.config=e,this.pathNormalize=new i(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 n.parse(r,{sourceType:"module",plugins:["typescript","jsx"]})}catch(e){return null}}getImportPath(e,t){let r=null,n=null;return o(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(o(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 o(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?(o(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}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={};o(r,{ImportDeclaration(e){const t=e.node;t.specifiers.forEach((e=>{i[e.local.name]={path:t.source.value,isDefault:"ImportDefaultSpecifier"===e.type}}))}});const l=n.declarations[0].init?.elements;return a.push(...this.parseRoutesArray(l,i,e)),a}}export{l 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 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};
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 * 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 { CallExpression, File as BabelFile, VariableDeclaration } 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 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 * 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: 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 // @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\nexport default ParseRoutes;\n"],"names":["traverse","babelTraverse","default","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","routesNode","init"],"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 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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"}