Guest User

genroutes

a guest
Sep 3rd, 2021
371
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { promises as fs } from 'fs';
  2. import path from 'path';
  3. import watch from 'node-watch';
  4.  
  5. const resolve = (p: string) => path.join(__dirname, '..', p);
  6.  
  7. interface RouteItem {
  8.   noExact?: boolean
  9.   path: string
  10.   component: any
  11.   children?: Array<RouteItem>
  12.   isLayout?: boolean
  13. }
  14.  
  15. const generateRouteFileContent = async () => {
  16.   const parseDir = async (base: string, dir: string): Promise<RouteItem | Array<RouteItem>> => {
  17.     const filePaths = await fs.readdir(dir);
  18.     const fileItems = (await Promise.all(
  19.       filePaths.map(async (fileName) => {
  20.         const filePath = path.join(dir, fileName);
  21.         const relativePath = filePath.replace(base, '').replace(/\\/g, '/');
  22.  
  23.         const stat = await fs.stat(filePath);
  24.         return {
  25.           filePath,
  26.           relativePath,
  27.           dir: stat.isDirectory(),
  28.         };
  29.       }),
  30.     )).filter((v) => v.dir || (v.relativePath.endsWith('.tsx') && !/\/helpers?/.test(v.relativePath)));
  31.  
  32.     const hasLayout = fileItems.some(
  33.       (v) => v.relativePath.endsWith('/_layout.tsx')
  34.         && !v.dir,
  35.     );
  36.  
  37.     const parsedRoutes = (await Promise.all(
  38.       fileItems.map(async (fileItem) => {
  39.         if (fileItem.dir) {
  40.           return parseDir(base, fileItem.filePath);
  41.         }
  42.  
  43.         const importPathBase = fileItem.relativePath
  44.           .replace(/\/index\.tsx$/, '')
  45.           .replace(/\/$/, '')
  46.           .replace(/\.tsx$/, '');
  47.         const importPath = `./pages${importPathBase}`;
  48.         const urlPath = importPathBase.replace(/\$(.+?)($|\/)/, ':$1$2') || '/';
  49.         const chunkNameBase = urlPath
  50.           .split('/')
  51.           .join('_')
  52.           .replace(/:/g, '')
  53.           .replace(/^_/, '')
  54.           .replace(/hub_app_/g, 'a_');
  55.         const chunkName = `p_${chunkNameBase}`;
  56.  
  57.         return {
  58.           isLayout: fileItem.relativePath.endsWith('/_layout.tsx') && !fileItem.dir,
  59.           path: urlPath,
  60.           component: `!!!() => import(/* webpackChunkName: '${chunkName}' */'${importPath}')!!!`,
  61.         };
  62.       }),
  63.     )).flatMap((v) => v);
  64.  
  65.     if (hasLayout) {
  66.       const layout = parsedRoutes.find((v) => v.isLayout)!;
  67.       const routesWithoutLayout = parsedRoutes.filter((v) => !v.isLayout);
  68.       routesWithoutLayout.forEach((v) => {
  69.         delete v.isLayout;
  70.       });
  71.  
  72.       return {
  73.         noExact: true,
  74.         path: layout.path.replace(/_layout$/, ''),
  75.         component: layout.component,
  76.         children: routesWithoutLayout,
  77.       };
  78.     }
  79.  
  80.     parsedRoutes.forEach((v) => {
  81.       delete v.isLayout;
  82.     });
  83.  
  84.     return parsedRoutes;
  85.   };
  86.  
  87.   const routeList = await parseDir(resolve('src/pages'), resolve('src/pages'));
  88.   const content = JSON
  89.     .stringify(routeList, null, 2)
  90.     .replace(/("!!!|!!!")/g, '');
  91.  
  92.   return `export default ${content}`;
  93. };
  94.  
  95. export const generateRoutes = async () => {
  96.   const routesFile = await generateRouteFileContent();
  97.   await fs.writeFile(resolve('src/routes.ts'), routesFile);
  98.   return routesFile;
  99. };
  100.  
  101. export const watchRoutes = async () => {
  102.   let routesFile = await generateRoutes();
  103.   let queue = false;
  104.   let running = false;
  105.  
  106.   const tryRun = () => {
  107.     queue = true;
  108.     if (running) {
  109.       return;
  110.     }
  111.     run();
  112.   };
  113.  
  114.   const run = async () => {
  115.     queue = false;
  116.     running = true;
  117.     const newRoutesFile = await generateRouteFileContent();
  118.  
  119.     if (routesFile !== newRoutesFile) {
  120.       routesFile = newRoutesFile;
  121.       await fs.writeFile(resolve('src/routes.ts'), routesFile).catch((err) => {
  122.         console.error(err);
  123.         process.exit(1);
  124.       });
  125.     }
  126.  
  127.     running = false;
  128.     if (queue) {
  129.       run();
  130.     }
  131.   };
  132.  
  133.   watch(
  134.     resolve('src/pages'),
  135.     { recursive: true },
  136.     tryRun,
  137.   );
  138. };
  139.  
  140. if (require.main === module) {
  141.   generateRoutes();
  142. }
RAW Paste Data