@bgscore/react-router
Version:
Automatic React Router generator for Vite with TypeScript support
2 lines (1 loc) • 7.52 kB
JavaScript
import t from"path";import e from"fs";import n from"fs/promises";function r(t,e,{baseUrl:n,authGuardDir:r,notFoundDir:o}){function a(t,o,s=!1){const i=" ".repeat(o),c=[];if(c.push(`${i}{`),t.isIndex?c.push(`${i} index: true,`):c.push(`${i} path: "${t.path}",`),t.element){let e=`<${t.element} />`;const n=t.imports.some((t=>"layout"===t.type));r&&!n&&(e=`<AuthGuard>${e}</AuthGuard>`),c.push(`${i} element: ${e},`)}const u=t.imports.find((t=>"component"===t.type));if(u&&e.find((t=>t.path===u.path))){const t=function(t,e){return t.replace(new RegExp(`^${e}/?`),"").replace(/\[(.+?)\]/g,((t,e)=>e)).replace(/\//g,"-").toLowerCase()}(u.path,n);c.push(`${i} id: "${t}",`),c.push(`${i} loader: async (...props) => {`),c.push(`${i} const mod = await import("${u.path}");`),c.push(`${i} const { errorElement, ...metadata } = mod.metadata ?? {};`),c.push(`${i} return {`),c.push(`${i} metadata,`),c.push(`${i} data: mod.metadata?.loader ? await mod.metadata.loader(...props) : undefined`),c.push(`${i} };`),c.push(`${i} },`),c.push(`${i} errorElement: (async () => {`),c.push(`${i} const mod = await import("${u.path}");`),c.push(`${i} const rawErrorElement = mod.metadata?.errorElement;`),c.push(`${i} if (React.isValidElement(rawErrorElement)) {`),c.push(`${i} return rawErrorElement;`),c.push(`${i} } else if (typeof rawErrorElement === 'function') {`),c.push(`${i} return React.createElement(React.Suspense, { fallback: React.createElement('div', null, '') }, React.createElement(rawErrorElement));`),c.push(`${i} } else {`),c.push(`${i} return null;`),c.push(`${i} }`),c.push(`${i} })(),`)}return t.children.length>0&&(c.push(`${i} children: [`),t.children.forEach(((e,n)=>{const r=a(e,o+4,!1);c.push(r+(n<t.children.length-1?",":""))})),c.push(`${i} ]`)),c.push(`${i}}`),c.join("\n")}let s=t.map((t=>a(t,2,!0))).join(",\n");return o&&(s+=',\n { path: "*", element: <NotFound /> }'),`[\n${s}\n]`}function o(e,n){return t.relative(n,e).split(t.sep).map((t=>{let e=t.replace(/\.tsx$/,"")?.trim();return e=e.replace(/\s+/g,"_").replace(/\[(\w+)\]/g,((t,e)=>e.charAt(0).toUpperCase()+e.slice(1))),"index"===e?"Index":e})).filter(Boolean).join("-").split(/[\/\-_]/).map((t=>t.charAt(0).toUpperCase()+t.slice(1))).join("")}function a(t){try{return e.readFileSync(t,"utf-8").includes("export const metadata")}catch{return!1}}function s(t){try{const n=e.readFileSync(t,"utf-8"),r=/export\s+const\s+metadata\s*:\s*.*?\s*=\s*({[\s\S]*?})?/,o=n.match(r);if(o){const t=o[1].replace(/(\n|\r|\t| )/g,"");return{independent:t.includes("independent:true"),layout:!t.includes("layout:false")}}return{independent:!1,layout:!0}}catch{return{independent:!1,layout:!0}}}function i(t,e){const n={...t[0],children:[]};let r=n;for(let e=1;e<t.length;e++){const n={...t[e],children:[]};r.children=[n],r=n}return r.children=[e],n}function c(t){return t.startsWith("[")&&t.endsWith("]")?`:${t.slice(1,-1)}`:t}function u(n,r,l,m,d){let p=[];const f=e.readdirSync(n).sort(((t,e)=>t===`${m}.tsx`?-1:e===`${m}.tsx`?1:t.localeCompare(e))),$=d.slice(),g=f.find((t=>t===`${m}.tsx`));let y=d.slice();if(g){const e=t.join(n,g),a=o(e,r),s=`${l}/${t.relative(r,e).replace(/\.tsx$/,"").replace(/\\/g,"/")}`,i={path:n===r?"/":c(t.basename(n)),element:a,children:[],isIndex:!1,imports:[{name:a,path:s,type:"layout"}]};y=y.concat(i)}else if(n!==r){const e={path:c(t.basename(n)),children:[],imports:[],isIndex:!1};y=y.concat(e)}for(const d of f){const f=t.join(n,d),g=e.statSync(f);if(g.isFile()&&".tsx"===t.extname(d)&&d!==`${m}.tsx`){const e=t.basename(d,".tsx"),u="index"===e,m=e.startsWith("[")&&e.endsWith("]"),g=u?"":m?`:${e.slice(1,-1)}`:e,h=s(f),{independent:x=!1,layout:w=!0}=h,E=o(f,r),D={path:g,isIndex:u,element:E,children:[],imports:[{name:E,path:`${l}/${t.relative(r,f).replace(/\.tsx$/,"").replace(/\\/g,"/")}`,type:"component",hasMetadata:a(f)}],isIndependent:x,metadata:h},I=!1===w&&y.length>$.length?$.concat({path:n===r?"/":c(t.basename(n)),children:[],imports:[],isIndex:!1}):y;if(x){const t=I.length?I[I.length-1]:null,e={path:I.map((t=>t.path)).filter((t=>"/"!==t)).join("/").replace(/\/+/g,"/")||(u?"":g),element:t?t.element:void 0,children:[D],imports:t?t.imports:[],isIndex:!1};p.push(e)}else{const t=I.length>0?i(I,D):D;p.push(t)}}else if(g.isDirectory()){const t=u(f,r,l,m,y);p=p.concat(t)}}return p}function l(t,e,n,r){return u(e,t,n,r,[])}function m(t){const e=new Map;for(const n of t){n.children=m(n.children);const t=n.path+"|"+(n.element||"");if(e.has(t)){const r=e.get(t);r.children=m(r.children.concat(n.children)),r.isIndex=r.isIndex||n.isIndex}else e.set(t,{...n})}return Array.from(e.values())}async function d(e){const{sourceDir:o="src/pages",outputDir:a="src/shared/routes",outputName:s="bgs-routes",baseUrl:i="pages",layoutName:c="_layout",authGuardDir:u,notFoundDir:d,minify:p=!0,logging:f=!0}=e||{};try{const e=t.resolve(process.cwd(),o),$=t.resolve(process.cwd(),a,`${s}.tsx`),g=l(e,e,i,c),{imports:y,lazyImports:h,metadataImports:x}=function(t){const e=new Map,n=new Map,r=new Map;return t.forEach((function t(o){o.imports.forEach((t=>{if("layout"===t.type)e.has(t.name)||e.set(t.name,{name:t.name,path:t.path});else if(n.has(t.name)||n.set(t.name,{name:t.name,path:t.path}),t.hasMetadata){const e=`metadata${t.name}`;r.set(t.path,{variableName:e,path:t.path})}})),o.children.forEach(t)})),{imports:Array.from(e.values()),lazyImports:Array.from(n.values()),metadataImports:Array.from(r.values())}}(g);let w=`\n// @ts-nocheck\nimport { createBrowserRouter } from "react-router-dom";\nimport React, { lazy } from "react";\n\n${[...y.map((t=>`import ${t.name} from "${t.path}";`)),...h.map((t=>`const ${t.name} = lazy(() => import("${t.path}"));`)),...u?[`import AuthGuard from "${u}";`]:[],...d?[`import NotFound from "${d}";`]:[]].join("\n")}\n\nexport const routes = ${r(m(g),x,{sourceDir:o,outputDir:a,outputName:s,baseUrl:i,layoutName:c,minify:p,logging:f,authGuardDir:u,notFoundDir:d})};\n\nconst router = createBrowserRouter(routes);\n\nexport default router;\n `.trim();p&&(w=`// @ts-nocheck \n${w.replace(/\/\/.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").replace(/[\n\r\t]/g,"").replace(/\s+/g," ")}`);let E=!0;try{await n.readFile($,"utf-8")===w&&(E=!1)}catch{}E&&(await n.mkdir(t.dirname($),{recursive:!0}),await n.writeFile($,w,"utf-8"))}catch(t){}}const p=t=>{const e=t?.sourceDir||"src/pages",n=function(){let n;return function(...r){clearTimeout(n),n=setTimeout((()=>{(n=>{n?.replace(/\\/g,"/")?.includes(e)&&d(t)})(...r)}),500)}}();return{name:"bgs-react-router",enforce:"pre",async configResolved(){await d(t)},async buildStart(){await d(t)},transform(t,n){if(n?.replace(/\\/g,"/")?.includes(e)){const r=n.endsWith(".tsx")||n.endsWith(".jsx")?o(n,e):"";return`\n (function() {\n const originalWarn = console.warn;\n console.warn = function(msg, ...args) {\n if (typeof msg === "string" && msg.includes("No \`HydrateFallback\` element")) {\n return;\n }\n originalWarn.apply(console, [msg, ...args]);\n };\n })();\n \n ${t}\n ${r?`window.__METADATA${r} = typeof metadata !== "undefined" ? metadata : {};`:""}\n `}},configureServer(t){n(e),t.watcher.on("add",n).on("change",n).on("unlink",n).on("addDir",n).on("unlinkDir",n)}}};export{p as default};