mirror of https://github.com/alibaba/ice.git
				
				
				
			Feat: define routes and ignore route files (#69)
* feat: support define routes * fix: test * fix: test * chore: undefined type * fix: conflict * chore: remove pages str from route id * fix: watch route change * fix: warn * fix: test * fix: test * chore: example * chore: add route-gen example * feat: add integration test * chore: test * chore: update config file * chore: remove pnpm cache * chore: test * chore: remove test:ci from ci workflow * chore: update build-scripts version * chore: build fixture * chore: remove devServer test * chore: buildFixture * feat: add vitest * chore: add ts-ignore * chore: node ci version * chore: comment bundle analyzer * chore: add test timeout * fix: lint * chore: remove threads * chore: set maxThreads and minThreads * chore: add maxConcurrency * chore: remove coverage * chore: threads * chore: set threads to false * fix: conflict * fix: comment
This commit is contained in:
		
							parent
							
								
									95d49c46d5
								
							
						
					
					
						commit
						d19f21e8b0
					
				| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					import { defineConfig } from '@ice/app';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineConfig({
 | 
				
			||||||
 | 
					  routes: {
 | 
				
			||||||
 | 
					    ignoreFiles: ['about.tsx', 'products.tsx'],
 | 
				
			||||||
 | 
					    defineRoutes: (route) => {
 | 
				
			||||||
 | 
					      route('/about-me', 'about.tsx');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      route('/', 'layout.tsx', () => {
 | 
				
			||||||
 | 
					        route('/product', 'products.tsx');
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "basic-project",
 | 
				
			||||||
 | 
					  "version": "1.0.0",
 | 
				
			||||||
 | 
					  "scripts": {
 | 
				
			||||||
 | 
					    "start": "ice start",
 | 
				
			||||||
 | 
					    "build": "ice build"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "description": "",
 | 
				
			||||||
 | 
					  "author": "",
 | 
				
			||||||
 | 
					  "license": "MIT",
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "@ice/app": "file:../../packages/ice",
 | 
				
			||||||
 | 
					    "@ice/runtime": "^1.0.0",
 | 
				
			||||||
 | 
					    "react": "^17.0.2",
 | 
				
			||||||
 | 
					    "react-dom": "^17.0.2"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "@types/react": "^17.0.39",
 | 
				
			||||||
 | 
					    "@types/react-dom": "^17.0.11",
 | 
				
			||||||
 | 
					    "browserslist": "^4.19.3",
 | 
				
			||||||
 | 
					    "regenerator-runtime": "^0.13.9"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					import { defineAppConfig } from 'ice';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineAppConfig({});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					/* eslint-disable react/self-closing-comp */
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { Meta, Title, Links, Main, Scripts } from 'ice';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Document(props) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <html lang="en">
 | 
				
			||||||
 | 
					      <head>
 | 
				
			||||||
 | 
					        <meta charSet="utf-8" />
 | 
				
			||||||
 | 
					        <meta name="description" content="ICE 3.0 Demo" />
 | 
				
			||||||
 | 
					        <meta name="viewport" content="width=device-width, initial-scale=1" />
 | 
				
			||||||
 | 
					        <Meta />
 | 
				
			||||||
 | 
					        <Title />
 | 
				
			||||||
 | 
					        <Links />
 | 
				
			||||||
 | 
					      </head>
 | 
				
			||||||
 | 
					      <body>
 | 
				
			||||||
 | 
					        <Main>
 | 
				
			||||||
 | 
					          {props.children}
 | 
				
			||||||
 | 
					        </Main>
 | 
				
			||||||
 | 
					        <Scripts />
 | 
				
			||||||
 | 
					      </body>
 | 
				
			||||||
 | 
					    </html>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Document;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					import * as React from 'react';
 | 
				
			||||||
 | 
					import { Link } from 'ice';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function About() {
 | 
				
			||||||
 | 
					  return <><h2>About</h2><Link to="/">home</Link></>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <h3>A page</h3>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <h3>B page</h3>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>Index</div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					import * as React from 'react';
 | 
				
			||||||
 | 
					import { Outlet, Link } from 'ice';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <h2>Dashboard</h2>
 | 
				
			||||||
 | 
					      <ul>
 | 
				
			||||||
 | 
					        <li><Link to="/dashboard/a">a</Link></li>
 | 
				
			||||||
 | 
					        <li><Link to="/dashboard/b">b</Link></li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					      <Outlet />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { useParams, Link } from 'ice';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function DetailId() {
 | 
				
			||||||
 | 
					  const params = useParams();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <h2>Detail id: {params.id}</h2>
 | 
				
			||||||
 | 
					      <Link to="/detail">Back to Detail</Link>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { Link } from 'ice';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Detail() {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <h2>Detail</h2>
 | 
				
			||||||
 | 
					      <ul>
 | 
				
			||||||
 | 
					        <li><Link to="/detail/join">join</Link></li>
 | 
				
			||||||
 | 
					        <li><Link to="/detail/dashboard">dashboard</Link></li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					import * as React from 'react';
 | 
				
			||||||
 | 
					import { Link } from 'ice';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Home() {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <h2>Home</h2>
 | 
				
			||||||
 | 
					      <ul>
 | 
				
			||||||
 | 
					        <li><Link to="/about-me">about</Link></li>
 | 
				
			||||||
 | 
					        <li><Link to="/detail">detail</Link></li>
 | 
				
			||||||
 | 
					        <li><Link to="/dashboard">dashboard</Link></li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					import * as React from 'react';
 | 
				
			||||||
 | 
					import { Outlet } from 'ice';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <h1>Layout</h1>
 | 
				
			||||||
 | 
					      <Outlet />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					import * as React from 'react';
 | 
				
			||||||
 | 
					import { Link } from 'ice';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Products() {
 | 
				
			||||||
 | 
					  return <><h2>Products Page</h2><Link to="/">home</Link></>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,32 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "compileOnSave": false,
 | 
				
			||||||
 | 
					  "buildOnSave": false,
 | 
				
			||||||
 | 
					  "compilerOptions": {
 | 
				
			||||||
 | 
					    "baseUrl": ".",
 | 
				
			||||||
 | 
					    "outDir": "build",
 | 
				
			||||||
 | 
					    "module": "esnext",
 | 
				
			||||||
 | 
					    "target": "es6",
 | 
				
			||||||
 | 
					    "jsx": "react",
 | 
				
			||||||
 | 
					    "moduleResolution": "node",
 | 
				
			||||||
 | 
					    "allowSyntheticDefaultImports": true,
 | 
				
			||||||
 | 
					    "lib": ["es6", "dom"],
 | 
				
			||||||
 | 
					    "sourceMap": true,
 | 
				
			||||||
 | 
					    "allowJs": true,
 | 
				
			||||||
 | 
					    "rootDir": "./",
 | 
				
			||||||
 | 
					    "forceConsistentCasingInFileNames": true,
 | 
				
			||||||
 | 
					    "noImplicitReturns": true,
 | 
				
			||||||
 | 
					    "noImplicitThis": true,
 | 
				
			||||||
 | 
					    "noImplicitAny": false,
 | 
				
			||||||
 | 
					    "importHelpers": true,
 | 
				
			||||||
 | 
					    "strictNullChecks": true,
 | 
				
			||||||
 | 
					    "suppressImplicitAnyIndexErrors": true,
 | 
				
			||||||
 | 
					    "noUnusedLocals": true,
 | 
				
			||||||
 | 
					    "skipLibCheck": true,
 | 
				
			||||||
 | 
					    "paths": {
 | 
				
			||||||
 | 
					      "@/*": ["./src/*"],
 | 
				
			||||||
 | 
					      "ice": [".ice"]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "include": ["src", ".ice", "ice.config.*"],
 | 
				
			||||||
 | 
					  "exclude": ["node_modules", "build", "public"]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,7 @@
 | 
				
			||||||
    "semver": "^7.3.5",
 | 
					    "semver": "^7.3.5",
 | 
				
			||||||
    "stylelint": "^14.3.0",
 | 
					    "stylelint": "^14.3.0",
 | 
				
			||||||
    "typescript": "^4.5.5",
 | 
					    "typescript": "^4.5.5",
 | 
				
			||||||
    "vitest": "^0.8.4"
 | 
					    "vitest": "^0.9.2"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "packageManager": "pnpm"
 | 
					  "packageManager": "pnpm"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,7 +33,7 @@
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@ice/types": "^1.0.0",
 | 
					    "@ice/types": "^1.0.0",
 | 
				
			||||||
    "build-scripts": "^2.0.0-15",
 | 
					    "build-scripts": "^2.0.0-16",
 | 
				
			||||||
    "webpack": "^5.69.1",
 | 
					    "webpack": "^5.69.1",
 | 
				
			||||||
    "webpack-dev-server": "^4.7.4"
 | 
					    "webpack-dev-server": "^4.7.4"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,7 +41,7 @@ function getEntry(rootDir: string) {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    runtime: ['react', 'react-dom', '@ice/runtime'],
 | 
					    runtime: ['react', 'react-dom', '@ice/runtime'],
 | 
				
			||||||
    index: {
 | 
					    main: {
 | 
				
			||||||
      import: [entryFile],
 | 
					      import: [entryFile],
 | 
				
			||||||
      dependOn: 'runtime',
 | 
					      dependOn: 'runtime',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,7 @@
 | 
				
			||||||
    "@ice/runtime": "^1.0.0",
 | 
					    "@ice/runtime": "^1.0.0",
 | 
				
			||||||
    "@ice/webpack-config": "^1.0.0",
 | 
					    "@ice/webpack-config": "^1.0.0",
 | 
				
			||||||
    "address": "^1.1.2",
 | 
					    "address": "^1.1.2",
 | 
				
			||||||
    "build-scripts": "^2.0.0-15",
 | 
					    "build-scripts": "^2.0.0-16",
 | 
				
			||||||
    "chalk": "^4.0.0",
 | 
					    "chalk": "^4.0.0",
 | 
				
			||||||
    "commander": "^9.0.0",
 | 
					    "commander": "^9.0.0",
 | 
				
			||||||
    "consola": "^2.15.3",
 | 
					    "consola": "^2.15.3",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,16 +32,9 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
 | 
				
			||||||
  const templateDir = path.join(__dirname, '../template/');
 | 
					  const templateDir = path.join(__dirname, '../template/');
 | 
				
			||||||
  const configFile = 'ice.config.(mts|mjs|ts|js|cjs|json)';
 | 
					  const configFile = 'ice.config.(mts|mjs|ts|js|cjs|json)';
 | 
				
			||||||
  const dataCache = new Map<string, string>();
 | 
					  const dataCache = new Map<string, string>();
 | 
				
			||||||
 | 
					 | 
				
			||||||
  const routesRenderData = generateRoutesInfo(rootDir);
 | 
					 | 
				
			||||||
  dataCache.set('routes', JSON.stringify(routesRenderData));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const generator = new Generator({
 | 
					  const generator = new Generator({
 | 
				
			||||||
    rootDir,
 | 
					    rootDir,
 | 
				
			||||||
    targetDir,
 | 
					    targetDir,
 | 
				
			||||||
    defaultRenderData: {
 | 
					 | 
				
			||||||
      ...routesRenderData,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    // add default template of ice
 | 
					    // add default template of ice
 | 
				
			||||||
    templates: [templateDir],
 | 
					    templates: [templateDir],
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
| 
						 | 
					@ -49,14 +42,6 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
 | 
				
			||||||
  const { addWatchEvent, removeWatchEvent } = createWatch({
 | 
					  const { addWatchEvent, removeWatchEvent } = createWatch({
 | 
				
			||||||
    watchDir: rootDir,
 | 
					    watchDir: rootDir,
 | 
				
			||||||
    command,
 | 
					    command,
 | 
				
			||||||
    watchEvents: getWatchEvents({
 | 
					 | 
				
			||||||
      generator,
 | 
					 | 
				
			||||||
      rootDir,
 | 
					 | 
				
			||||||
      targetDir,
 | 
					 | 
				
			||||||
      templateDir,
 | 
					 | 
				
			||||||
      configFile,
 | 
					 | 
				
			||||||
      cache: dataCache,
 | 
					 | 
				
			||||||
    }),
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const generatorAPI = {
 | 
					  const generatorAPI = {
 | 
				
			||||||
| 
						 | 
					@ -72,6 +57,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
 | 
				
			||||||
    addRenderFile: generator.addRenderFile,
 | 
					    addRenderFile: generator.addRenderFile,
 | 
				
			||||||
    addRenderTemplate: generator.addTemplateFiles,
 | 
					    addRenderTemplate: generator.addTemplateFiles,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const ctx = new Context<any, ExtendsPluginAPI>({
 | 
					  const ctx = new Context<any, ExtendsPluginAPI>({
 | 
				
			||||||
    rootDir,
 | 
					    rootDir,
 | 
				
			||||||
    command,
 | 
					    command,
 | 
				
			||||||
| 
						 | 
					@ -90,16 +76,32 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  await ctx.resolveConfig();
 | 
					  await ctx.resolveConfig();
 | 
				
			||||||
 | 
					  const { userConfig: { routes: routesConfig } } = ctx;
 | 
				
			||||||
 | 
					  const routesRenderData = generateRoutesInfo(rootDir, routesConfig);
 | 
				
			||||||
 | 
					  generator.modifyRenderData((renderData) => ({
 | 
				
			||||||
 | 
					    ...renderData,
 | 
				
			||||||
 | 
					    ...routesRenderData,
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					  dataCache.set('routes', JSON.stringify(routesRenderData.routeManifest));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const runtimeModules = getRuntimeModules(ctx.getAllPlugin());
 | 
					  const runtimeModules = getRuntimeModules(ctx.getAllPlugin());
 | 
				
			||||||
  generator.modifyRenderData((renderData) => ({
 | 
					  generator.modifyRenderData((renderData) => ({
 | 
				
			||||||
    ...renderData,
 | 
					    ...renderData,
 | 
				
			||||||
    runtimeModules,
 | 
					    runtimeModules,
 | 
				
			||||||
  }));
 | 
					  }));
 | 
				
			||||||
  await ctx.setup();
 | 
					  await ctx.setup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // render template before webpack compile
 | 
					  // render template before webpack compile
 | 
				
			||||||
  const renderStart = new Date().getTime();
 | 
					  const renderStart = new Date().getTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  generator.render();
 | 
					  generator.render();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addWatchEvent(
 | 
				
			||||||
 | 
					    ...getWatchEvents({ generator, targetDir, templateDir, cache: dataCache, ctx }),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  consola.debug('template render cost:', new Date().getTime() - renderStart);
 | 
					  consola.debug('template render cost:', new Date().getTime() - renderStart);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // define runtime env before get webpack config
 | 
					  // define runtime env before get webpack config
 | 
				
			||||||
  defineRuntimeEnv();
 | 
					  defineRuntimeEnv();
 | 
				
			||||||
  const compileIncludes = runtimeModules.map(({ name }) => `${name}/runtime`);
 | 
					  const compileIncludes = runtimeModules.map(({ name }) => `${name}/runtime`);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,32 +1,34 @@
 | 
				
			||||||
import * as path from 'path';
 | 
					import * as path from 'path';
 | 
				
			||||||
import consola from 'consola';
 | 
					import consola from 'consola';
 | 
				
			||||||
import type { WatchEvent } from '@ice/types/esm/plugin.js';
 | 
					import type { WatchEvent } from '@ice/types/esm/plugin.js';
 | 
				
			||||||
 | 
					import type { Context } from 'build-scripts';
 | 
				
			||||||
 | 
					import type { Config } from '@ice/types';
 | 
				
			||||||
import { generateRoutesInfo } from './routes.js';
 | 
					import { generateRoutesInfo } from './routes.js';
 | 
				
			||||||
import type Generator from './service/runtimeGenerator';
 | 
					import type Generator from './service/runtimeGenerator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Options {
 | 
					interface Options {
 | 
				
			||||||
  rootDir: string;
 | 
					 | 
				
			||||||
  targetDir: string;
 | 
					  targetDir: string;
 | 
				
			||||||
  templateDir: string;
 | 
					  templateDir: string;
 | 
				
			||||||
  configFile: string;
 | 
					 | 
				
			||||||
  generator: Generator;
 | 
					  generator: Generator;
 | 
				
			||||||
  cache: Map<string, string>;
 | 
					  cache: Map<string, string>;
 | 
				
			||||||
 | 
					  ctx: Context<Config>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getWatchEvents = (options: Options): WatchEvent[] => {
 | 
					const getWatchEvents = (options: Options): WatchEvent[] => {
 | 
				
			||||||
  const { rootDir, generator, targetDir, templateDir, configFile, cache } = options;
 | 
					  const { generator, targetDir, templateDir, cache, ctx } = options;
 | 
				
			||||||
 | 
					  const { userConfig: { routes: routesConfig }, configFile, rootDir } = ctx;
 | 
				
			||||||
  const watchRoutes: WatchEvent = [
 | 
					  const watchRoutes: WatchEvent = [
 | 
				
			||||||
    /src\/pages\/?[\w*-:.$]+$/,
 | 
					    /src\/pages\/?[\w*-:.$]+$/,
 | 
				
			||||||
    (eventName: string) => {
 | 
					    (eventName: string) => {
 | 
				
			||||||
      if (eventName === 'add' || eventName === 'unlink') {
 | 
					      if (eventName === 'add' || eventName === 'unlink') {
 | 
				
			||||||
        const routesRenderData = generateRoutesInfo(rootDir);
 | 
					        const routesRenderData = generateRoutesInfo(rootDir, routesConfig);
 | 
				
			||||||
        const stringifiedData = JSON.stringify(routesRenderData);
 | 
					        const stringifiedData = JSON.stringify(routesRenderData);
 | 
				
			||||||
        if (cache.get('routes') !== stringifiedData) {
 | 
					        if (cache.get('routes') !== stringifiedData) {
 | 
				
			||||||
          cache.set('routes', stringifiedData);
 | 
					          cache.set('routes', stringifiedData);
 | 
				
			||||||
          consola.debug('[event]', `routes data regenerated: ${stringifiedData}`);
 | 
					          consola.debug('[event]', `routes data regenerated: ${stringifiedData}`);
 | 
				
			||||||
          generator.renderFile(
 | 
					          generator.renderFile(
 | 
				
			||||||
            path.join(templateDir, 'routes.ts.ejs'),
 | 
					            path.join(templateDir, 'routes.ts.ejs'),
 | 
				
			||||||
            path.join(rootDir, targetDir, 'route.ts'),
 | 
					            path.join(rootDir, targetDir, 'routes.ts'),
 | 
				
			||||||
            routesRenderData,
 | 
					            routesRenderData,
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
          generator.renderFile(
 | 
					          generator.renderFile(
 | 
				
			||||||
| 
						 | 
					@ -38,6 +40,7 @@ const getWatchEvents = (options: Options): WatchEvent[] => {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const watchGlobalStyle: WatchEvent = [
 | 
					  const watchGlobalStyle: WatchEvent = [
 | 
				
			||||||
    /src\/global.(scss|less|css)/,
 | 
					    /src\/global.(scss|less|css)/,
 | 
				
			||||||
    (event: string, filePath: string) => {
 | 
					    (event: string, filePath: string) => {
 | 
				
			||||||
| 
						 | 
					@ -49,7 +52,7 @@ const getWatchEvents = (options: Options): WatchEvent[] => {
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const watchConfigFile: WatchEvent = [
 | 
					  const watchConfigFile: WatchEvent = [
 | 
				
			||||||
    new RegExp(configFile),
 | 
					    new RegExp((typeof configFile === 'string' ? [configFile] : configFile).join('|')),
 | 
				
			||||||
    (event: string, filePath: string) => {
 | 
					    (event: string, filePath: string) => {
 | 
				
			||||||
      if (event === 'change') {
 | 
					      if (event === 'change') {
 | 
				
			||||||
        consola.warn(`Found a change in ${path.basename(filePath)}. Restart the dev server to see the changes in effect.`);
 | 
					        consola.warn(`Found a change in ${path.basename(filePath)}. Restart the dev server to see the changes in effect.`);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,6 +112,10 @@ const userConfig = [
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    name: 'routes',
 | 
				
			||||||
 | 
					    validation: 'object',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const cliOptions = [
 | 
					const cliOptions = [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import * as fs from 'fs';
 | 
					 | 
				
			||||||
import * as path from 'path';
 | 
					import * as path from 'path';
 | 
				
			||||||
 | 
					import fse from 'fs-extra';
 | 
				
			||||||
import type { RouteItem } from '@ice/runtime';
 | 
					import type { RouteItem } from '@ice/runtime';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Options {
 | 
					interface Options {
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@ export default async function generateHTML(options: Options) {
 | 
				
			||||||
  } = options;
 | 
					  } = options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const serverEntry = await import(entry);
 | 
					  const serverEntry = await import(entry);
 | 
				
			||||||
  const routes = JSON.parse(fs.readFileSync(routeManifest, 'utf8'));
 | 
					  const routes = JSON.parse(fse.readFileSync(routeManifest, 'utf8'));
 | 
				
			||||||
  const paths = getPaths(routes);
 | 
					  const paths = getPaths(routes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (let i = 0, n = paths.length; i < n; i++) {
 | 
					  for (let i = 0, n = paths.length; i < n; i++) {
 | 
				
			||||||
| 
						 | 
					@ -40,7 +40,9 @@ export default async function generateHTML(options: Options) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const fileName = routePath === '/' ? 'index.html' : `${routePath}.html`;
 | 
					    const fileName = routePath === '/' ? 'index.html' : `${routePath}.html`;
 | 
				
			||||||
    fs.writeFileSync(path.join(outDir, fileName), html);
 | 
					    const contentPath = path.join(outDir, fileName);
 | 
				
			||||||
 | 
					    await fse.ensureFile(contentPath);
 | 
				
			||||||
 | 
					    await fse.writeFile(contentPath, html);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,14 +51,14 @@ export default async function generateHTML(options: Options) {
 | 
				
			||||||
 * @param routes
 | 
					 * @param routes
 | 
				
			||||||
 * @returns
 | 
					 * @returns
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function getPaths(routes: RouteItem[]): string[] {
 | 
					function getPaths(routes: RouteItem[], parentPath = ''): string[] {
 | 
				
			||||||
  let pathList = [];
 | 
					  let pathList = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  routes.forEach(route => {
 | 
					  routes.forEach(route => {
 | 
				
			||||||
    if (route.children) {
 | 
					    if (route.children) {
 | 
				
			||||||
      pathList = pathList.concat(getPaths(route.children));
 | 
					      pathList = pathList.concat(getPaths(route.children, route.path));
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      pathList.push(route.path || '/');
 | 
					      pathList.push(path.join('/', parentPath, route.path || ''));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,10 @@
 | 
				
			||||||
import * as path from 'path';
 | 
					import * as path from 'path';
 | 
				
			||||||
import { formatNestedRouteManifest, generateRouteManifest } from '@ice/route-manifest';
 | 
					import { formatNestedRouteManifest, generateRouteManifest } from '@ice/route-manifest';
 | 
				
			||||||
import type { NestedRouteManifest } from '@ice/route-manifest';
 | 
					import type { NestedRouteManifest } from '@ice/route-manifest';
 | 
				
			||||||
 | 
					import type { UserConfig } from '@ice/types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function generateRoutesInfo(rootDir: string) {
 | 
					export function generateRoutesInfo(rootDir: string, routesConfig: UserConfig['routes'] = {}) {
 | 
				
			||||||
  const routeManifest = generateRouteManifest(rootDir);
 | 
					  const routeManifest = generateRouteManifest(rootDir, routesConfig.ignoreFiles, routesConfig.defineRoutes);
 | 
				
			||||||
  const routes = formatNestedRouteManifest(routeManifest);
 | 
					  const routes = formatNestedRouteManifest(routeManifest);
 | 
				
			||||||
  const str = generateNestRoutesStr(routes);
 | 
					  const str = generateNestRoutesStr(routes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +24,7 @@ function generateNestRoutesStr(nestRouteManifest: NestedRouteManifest[]) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let str = `{
 | 
					    let str = `{
 | 
				
			||||||
      path: '${routePath || ''}',
 | 
					      path: '${routePath || ''}',
 | 
				
			||||||
      load: () => import(/* webpackChunkName: "${componentName}" */ '@/${componentFile}'),
 | 
					      load: () => import(/* webpackChunkName: "${componentName}" */ '@/pages/${componentFile}'),
 | 
				
			||||||
      componentName: '${componentName}',
 | 
					      componentName: '${componentName}',
 | 
				
			||||||
      index: ${index},
 | 
					      index: ${index},
 | 
				
			||||||
      id: '${id}',
 | 
					      id: '${id}',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@ const RENDER_WAIT = 150;
 | 
				
			||||||
interface Options {
 | 
					interface Options {
 | 
				
			||||||
  rootDir: string;
 | 
					  rootDir: string;
 | 
				
			||||||
  targetDir: string;
 | 
					  targetDir: string;
 | 
				
			||||||
  defaultRenderData: RenderData;
 | 
					  defaultRenderData?: RenderData;
 | 
				
			||||||
  templates?: (string | TemplateOptions)[];
 | 
					  templates?: (string | TemplateOptions)[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,7 +106,7 @@ export default class Generator {
 | 
				
			||||||
  private contentTypes: string[];
 | 
					  private contentTypes: string[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public constructor(options: Options) {
 | 
					  public constructor(options: Options) {
 | 
				
			||||||
    const { rootDir, targetDir, defaultRenderData, templates } = options;
 | 
					    const { rootDir, targetDir, defaultRenderData = {}, templates } = options;
 | 
				
			||||||
    this.rootDir = rootDir;
 | 
					    this.rootDir = rootDir;
 | 
				
			||||||
    this.targetDir = targetDir;
 | 
					    this.targetDir = targetDir;
 | 
				
			||||||
    this.renderData = defaultRenderData;
 | 
					    this.renderData = defaultRenderData;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,8 +29,8 @@ function createWatch(options: {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    watcher,
 | 
					    watcher,
 | 
				
			||||||
    addWatchEvent: ([pattern, action, name]: WatchEvent) => {
 | 
					    addWatchEvent: (...args: WatchEvent[]) => {
 | 
				
			||||||
      watchEvents.push([pattern, action, name]);
 | 
					      watchEvents.push(...args);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    removeWatchEvent: (name: string) => {
 | 
					    removeWatchEvent: (name: string) => {
 | 
				
			||||||
      const eventIndex = watchEvents.findIndex(([,,watchName]) => watchName === name);
 | 
					      const eventIndex = watchEvents.findIndex(([,,watchName]) => watchName === name);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,8 @@ import {
 | 
				
			||||||
  useAppContext,
 | 
					  useAppContext,
 | 
				
			||||||
  Link,
 | 
					  Link,
 | 
				
			||||||
  Outlet,
 | 
					  Outlet,
 | 
				
			||||||
 | 
					  useParams,
 | 
				
			||||||
 | 
					  useSearchParams,
 | 
				
			||||||
  Meta,
 | 
					  Meta,
 | 
				
			||||||
  Title,
 | 
					  Title,
 | 
				
			||||||
  Links,
 | 
					  Links,
 | 
				
			||||||
| 
						 | 
					@ -16,6 +18,8 @@ export {
 | 
				
			||||||
  useAppContext,
 | 
					  useAppContext,
 | 
				
			||||||
  Link,
 | 
					  Link,
 | 
				
			||||||
  Outlet,
 | 
					  Outlet,
 | 
				
			||||||
 | 
					  useParams,
 | 
				
			||||||
 | 
					  useSearchParams,
 | 
				
			||||||
  Meta,
 | 
					  Meta,
 | 
				
			||||||
  Title,
 | 
					  Title,
 | 
				
			||||||
  Links,
 | 
					  Links,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,9 +5,10 @@ import minimatch from 'minimatch';
 | 
				
			||||||
import { createRouteId, defineRoutes } from './routes.js';
 | 
					import { createRouteId, defineRoutes } from './routes.js';
 | 
				
			||||||
import type { RouteManifest, DefineRouteFunction, NestedRouteManifest } from './routes.js';
 | 
					import type { RouteManifest, DefineRouteFunction, NestedRouteManifest } from './routes.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export {
 | 
					export type {
 | 
				
			||||||
  RouteManifest,
 | 
					  RouteManifest,
 | 
				
			||||||
  NestedRouteManifest,
 | 
					  NestedRouteManifest,
 | 
				
			||||||
 | 
					  DefineRouteFunction,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const validRouteChar = ['-', '\\w', '/', ':', '*'];
 | 
					const validRouteChar = ['-', '\\w', '/', ':', '*'];
 | 
				
			||||||
| 
						 | 
					@ -26,14 +27,18 @@ export function isRouteModuleFile(filename: string): boolean {
 | 
				
			||||||
  return routeModuleExts.includes(path.extname(filename));
 | 
					  return routeModuleExts.includes(path.extname(filename));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function generateRouteManifest(rootDir: string) {
 | 
					export function generateRouteManifest(
 | 
				
			||||||
 | 
					  rootDir: string,
 | 
				
			||||||
 | 
					  ignoreFiles: string[] = [],
 | 
				
			||||||
 | 
					  defineExtraRoutes?: (defineRoute: DefineRouteFunction) => void,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
  const srcDir = path.join(rootDir, 'src');
 | 
					  const srcDir = path.join(rootDir, 'src');
 | 
				
			||||||
  const routeManifest: RouteManifest = {};
 | 
					  const routeManifest: RouteManifest = {};
 | 
				
			||||||
  // 2. find routes in `src/pages` directory
 | 
					  // 2. find routes in `src/pages` directory
 | 
				
			||||||
  if (fs.existsSync(path.resolve(srcDir, 'pages'))) {
 | 
					  if (fs.existsSync(path.resolve(srcDir, 'pages'))) {
 | 
				
			||||||
    const conventionalRoutes = defineConventionalRoutes(
 | 
					    const conventionalRoutes = defineConventionalRoutes(
 | 
				
			||||||
      rootDir,
 | 
					      rootDir,
 | 
				
			||||||
      [], // TODO: add ignoredFilePatterns defined in ice.config.js
 | 
					      ignoreFiles,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const key of Object.keys(conventionalRoutes)) {
 | 
					    for (const key of Object.keys(conventionalRoutes)) {
 | 
				
			||||||
| 
						 | 
					@ -44,7 +49,17 @@ export function generateRouteManifest(rootDir: string) {
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  // 3. add extra routes from user config
 | 
				
			||||||
 | 
					  if (defineExtraRoutes) {
 | 
				
			||||||
 | 
					    const extraRoutes = defineRoutes(defineExtraRoutes);
 | 
				
			||||||
 | 
					    for (const key of Object.keys(extraRoutes)) {
 | 
				
			||||||
 | 
					      const route = extraRoutes[key];
 | 
				
			||||||
 | 
					      routeManifest[route.id] = {
 | 
				
			||||||
 | 
					        ...route,
 | 
				
			||||||
 | 
					        parentId: route.parentId || undefined,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  return routeManifest;
 | 
					  return routeManifest;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,7 +81,6 @@ function defineConventionalRoutes(
 | 
				
			||||||
  ignoredFilePatterns?: string[],
 | 
					  ignoredFilePatterns?: string[],
 | 
				
			||||||
): RouteManifest {
 | 
					): RouteManifest {
 | 
				
			||||||
  const files: { [routeId: string]: string } = {};
 | 
					  const files: { [routeId: string]: string } = {};
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // 1. find all route components in src/pages
 | 
					  // 1. find all route components in src/pages
 | 
				
			||||||
  visitFiles(
 | 
					  visitFiles(
 | 
				
			||||||
    path.join(rootDir, 'src', 'pages'),
 | 
					    path.join(rootDir, 'src', 'pages'),
 | 
				
			||||||
| 
						 | 
					@ -78,10 +92,9 @@ function defineConventionalRoutes(
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const filePath = path.join('pages', file);
 | 
					 | 
				
			||||||
      if (isRouteModuleFile(file)) {
 | 
					      if (isRouteModuleFile(file)) {
 | 
				
			||||||
        let routeId = createRouteId(filePath);
 | 
					        let routeId = createRouteId(file);
 | 
				
			||||||
        files[routeId] = filePath;
 | 
					        files[routeId] = file;
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					@ -102,24 +115,25 @@ function defineConventionalRoutes(
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (let routeId of childRouteIds) {
 | 
					    for (let routeId of childRouteIds) {
 | 
				
			||||||
 | 
					      const parentRoutePath = removeLastLayoutStrFromId(parentId) || '';
 | 
				
			||||||
      const routePath: string | undefined = createRoutePath(
 | 
					      const routePath: string | undefined = createRoutePath(
 | 
				
			||||||
        routeId.slice((removeLayoutStrFromId(parentId) || 'pages').length),
 | 
					        // parentRoutePath = 'home', routeId = 'home/me', the new routeId is 'me'
 | 
				
			||||||
 | 
					        // in order to escape the child route path is absolute path
 | 
				
			||||||
 | 
					        routeId.slice(parentRoutePath.length + (parentRoutePath ? 1 : 0)),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
					      const routeFilePath = path.join('src', 'pages', files[routeId]);
 | 
				
			||||||
      if (RegExp(`[^${validRouteChar.join(',')}]`).test(routePath)) {
 | 
					      if (RegExp(`[^${validRouteChar.join(',')}]`).test(routePath)) {
 | 
				
			||||||
        throw new Error(`invalid character in '${routeId}'. Only support char: ${validRouteChar.join(', ')}`);
 | 
					        throw new Error(`invalid character in '${routeFilePath}'. Only support char: ${validRouteChar.join(', ')}`);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      const isIndexRoute = routeId.endsWith('/index');
 | 
					      const isIndexRoute = routeId === 'index' || routeId.endsWith('/index');
 | 
				
			||||||
      let fullPath = createRoutePath(routeId.slice('pages'.length + 1));
 | 
					      const fullPath = createRoutePath(routeId);
 | 
				
			||||||
      let uniqueRouteId = (fullPath || '') + (isIndexRoute ? '?index' : '');
 | 
					      const uniqueRouteId = (fullPath || '') + (isIndexRoute ? '?index' : '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (uniqueRouteId) {
 | 
					      if (uniqueRouteId) {
 | 
				
			||||||
        if (uniqueRoutes.has(uniqueRouteId)) {
 | 
					        if (uniqueRoutes.has(uniqueRouteId)) {
 | 
				
			||||||
          throw new Error(
 | 
					          throw new Error(
 | 
				
			||||||
            `Path ${JSON.stringify(fullPath)} defined by route ${JSON.stringify(
 | 
					            `Path ${JSON.stringify(fullPath)} defined by route ${JSON.stringify(routeFilePath)} 
 | 
				
			||||||
              routeId,
 | 
					            conflicts with route ${JSON.stringify(uniqueRoutes.get(uniqueRouteId))}`,
 | 
				
			||||||
            )} conflicts with route ${JSON.stringify(
 | 
					 | 
				
			||||||
              uniqueRoutes.get(uniqueRouteId),
 | 
					 | 
				
			||||||
            )}`,
 | 
					 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          uniqueRoutes.set(uniqueRouteId, routeId);
 | 
					          uniqueRoutes.set(uniqueRouteId, routeId);
 | 
				
			||||||
| 
						 | 
					@ -155,7 +169,7 @@ export function createRoutePath(routeId: string): string | undefined {
 | 
				
			||||||
  let result = '';
 | 
					  let result = '';
 | 
				
			||||||
  let rawSegmentBuffer = '';
 | 
					  let rawSegmentBuffer = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const partialRouteId = removeLayoutStrFromId(routeId);
 | 
					  const partialRouteId = removeLastLayoutStrFromId(routeId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (let i = 0; i < partialRouteId.length; i++) {
 | 
					  for (let i = 0; i < partialRouteId.length; i++) {
 | 
				
			||||||
    const char = partialRouteId.charAt(i);
 | 
					    const char = partialRouteId.charAt(i);
 | 
				
			||||||
| 
						 | 
					@ -196,7 +210,7 @@ function findParentRouteId(
 | 
				
			||||||
  return routeIds.find((id) => {
 | 
					  return routeIds.find((id) => {
 | 
				
			||||||
    // childRouteId is `pages/about` and id is `pages/layout` will match
 | 
					    // childRouteId is `pages/about` and id is `pages/layout` will match
 | 
				
			||||||
    // childRouteId is `pages/about/index` and id is `pages/about/layout` will match
 | 
					    // childRouteId is `pages/about/index` and id is `pages/about/layout` will match
 | 
				
			||||||
    return childRouteId !== id && id.endsWith('layout') && childRouteId.startsWith(`${id.slice(0, id.length - '/layout'.length)}`);
 | 
					    return childRouteId !== id && id.endsWith('layout') && childRouteId.startsWith(`${id.slice(0, id.length - 'layout'.length)}`);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -224,9 +238,12 @@ function visitFiles(
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * remove `/layout` str if the routeId has it
 | 
					 * remove `/layout` str if the routeId has it
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * /About/layout -> /About
 | 
					 * 'layout' -> ''
 | 
				
			||||||
 * /About/layout/index -> /About/layout/index
 | 
					 * 'About/layout' -> 'About'
 | 
				
			||||||
 | 
					 * 'About/layout/index' -> 'About/layout/index'
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function removeLayoutStrFromId(id?: string) {
 | 
					function removeLastLayoutStrFromId(id?: string) {
 | 
				
			||||||
  return id?.endsWith('/layout') ? id.slice(0, id.length - '/layout'.length) : id;
 | 
					  const layoutStrs = ['/layout', 'layout'];
 | 
				
			||||||
 | 
					  const currentLayoutStr = layoutStrs.find(layoutStr => id?.endsWith(layoutStr));
 | 
				
			||||||
 | 
					  return currentLayoutStr ? id.slice(0, id.length - currentLayoutStr.length) : id;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,9 +100,10 @@ export function defineRoutes(
 | 
				
			||||||
      // route(path, file, options)
 | 
					      // route(path, file, options)
 | 
				
			||||||
      options = optionsOrChildren || {};
 | 
					      options = optionsOrChildren || {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const id = createRouteId(file);
 | 
					    const id = createRouteId(file);
 | 
				
			||||||
    const route: ConfigRoute = {
 | 
					    const route: ConfigRoute = {
 | 
				
			||||||
      path: path || undefined,
 | 
					      path,
 | 
				
			||||||
      index: options.index ? true : undefined,
 | 
					      index: options.index ? true : undefined,
 | 
				
			||||||
      id,
 | 
					      id,
 | 
				
			||||||
      parentId:
 | 
					      parentId:
 | 
				
			||||||
| 
						 | 
					@ -144,6 +145,6 @@ function stripFileExtension(file: string) {
 | 
				
			||||||
function createComponentName(id: string) {
 | 
					function createComponentName(id: string) {
 | 
				
			||||||
  return id.replace('.', '/') // 'pages/home.news' -> pages/home/news
 | 
					  return id.replace('.', '/') // 'pages/home.news' -> pages/home/news
 | 
				
			||||||
    .split('/')
 | 
					    .split('/')
 | 
				
			||||||
    .map((item: string) => item[0].toUpperCase() + item.slice(1, item.length))
 | 
					    .map((item: string) => item.toLowerCase())
 | 
				
			||||||
    .join('');
 | 
					    .join('-');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3,70 +3,33 @@
 | 
				
			||||||
exports[`generateRouteManifest function > layout-routes 1`] = `
 | 
					exports[`generateRouteManifest function > layout-routes 1`] = `
 | 
				
			||||||
[
 | 
					[
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "componentName": "PagesBlogIndex",
 | 
					    "componentName": "blog-index",
 | 
				
			||||||
    "file": "pages/blog/index.tsx",
 | 
					    "file": "blog/index.tsx",
 | 
				
			||||||
    "id": "pages/blog/index",
 | 
					    "id": "blog/index",
 | 
				
			||||||
    "index": true,
 | 
					    "index": true,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": "/blog",
 | 
					    "path": "blog",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "componentName": "PagesBlog$id",
 | 
					    "componentName": "blog-$id",
 | 
				
			||||||
    "file": "pages/blog/$id.tsx",
 | 
					    "file": "blog/$id.tsx",
 | 
				
			||||||
    "id": "pages/blog/$id",
 | 
					    "id": "blog/$id",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": "/blog/:id",
 | 
					    "path": "blog/:id",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "componentName": "PagesAbout",
 | 
					    "componentName": "about",
 | 
				
			||||||
    "file": "pages/about.tsx",
 | 
					    "file": "about.tsx",
 | 
				
			||||||
    "id": "pages/about",
 | 
					    "id": "about",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": "/about",
 | 
					    "path": "about",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "componentName": "PagesIndex",
 | 
					    "componentName": "index",
 | 
				
			||||||
    "file": "pages/index.tsx",
 | 
					    "file": "index.tsx",
 | 
				
			||||||
    "id": "pages/index",
 | 
					    "id": "index",
 | 
				
			||||||
    "index": true,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": undefined,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
exports[`generateRouteManifest function layout-routes 1`] = `
 | 
					 | 
				
			||||||
Array [
 | 
					 | 
				
			||||||
  Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesBlogIndex",
 | 
					 | 
				
			||||||
    "file": "pages/blog/index.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/blog/index",
 | 
					 | 
				
			||||||
    "index": true,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": "/blog",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesBlog$id",
 | 
					 | 
				
			||||||
    "file": "pages/blog/$id.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/blog/$id",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": "/blog/:id",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesAbout",
 | 
					 | 
				
			||||||
    "file": "pages/about.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/about",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": "/about",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesIndex",
 | 
					 | 
				
			||||||
    "file": "pages/index.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/index",
 | 
					 | 
				
			||||||
    "index": true,
 | 
					    "index": true,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": undefined,
 | 
					    "path": undefined,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,42 +2,87 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports[`generateRouteManifest function > basic-routes 1`] = `
 | 
					exports[`generateRouteManifest function > basic-routes 1`] = `
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "pages/About/index": {
 | 
					  "About/index": {
 | 
				
			||||||
    "componentName": "PagesAboutIndex",
 | 
					    "componentName": "about-index",
 | 
				
			||||||
    "file": "pages/About/index.tsx",
 | 
					    "file": "About/index.tsx",
 | 
				
			||||||
    "id": "pages/About/index",
 | 
					    "id": "About/index",
 | 
				
			||||||
    "index": true,
 | 
					    "index": true,
 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					    "parentId": "layout",
 | 
				
			||||||
    "path": "/About",
 | 
					    "path": "About",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/About/me/index": {
 | 
					  "About/me/index": {
 | 
				
			||||||
    "componentName": "PagesAboutMeIndex",
 | 
					    "componentName": "about-me-index",
 | 
				
			||||||
    "file": "pages/About/me/index.tsx",
 | 
					    "file": "About/me/index.tsx",
 | 
				
			||||||
    "id": "pages/About/me/index",
 | 
					    "id": "About/me/index",
 | 
				
			||||||
    "index": true,
 | 
					    "index": true,
 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					    "parentId": "layout",
 | 
				
			||||||
    "path": "/About/me",
 | 
					    "path": "About/me",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/home": {
 | 
					  "home": {
 | 
				
			||||||
    "componentName": "PagesHome",
 | 
					    "componentName": "home",
 | 
				
			||||||
    "file": "pages/home.tsx",
 | 
					    "file": "home.tsx",
 | 
				
			||||||
    "id": "pages/home",
 | 
					    "id": "home",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					    "parentId": "layout",
 | 
				
			||||||
    "path": "/home",
 | 
					    "path": "home",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/index": {
 | 
					  "index": {
 | 
				
			||||||
    "componentName": "PagesIndex",
 | 
					    "componentName": "index",
 | 
				
			||||||
    "file": "pages/index.tsx",
 | 
					    "file": "index.tsx",
 | 
				
			||||||
    "id": "pages/index",
 | 
					    "id": "index",
 | 
				
			||||||
    "index": true,
 | 
					    "index": true,
 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					    "parentId": "layout",
 | 
				
			||||||
    "path": undefined,
 | 
					    "path": undefined,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/layout": {
 | 
					  "layout": {
 | 
				
			||||||
    "componentName": "PagesLayout",
 | 
					    "componentName": "layout",
 | 
				
			||||||
    "file": "pages/layout.tsx",
 | 
					    "file": "layout.tsx",
 | 
				
			||||||
    "id": "pages/layout",
 | 
					    "id": "layout",
 | 
				
			||||||
 | 
					    "index": undefined,
 | 
				
			||||||
 | 
					    "parentId": undefined,
 | 
				
			||||||
 | 
					    "path": undefined,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`generateRouteManifest function > define-extra-routes 1`] = `
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "About/index": {
 | 
				
			||||||
 | 
					    "componentName": "about-index",
 | 
				
			||||||
 | 
					    "file": "About/index.tsx",
 | 
				
			||||||
 | 
					    "id": "About/index",
 | 
				
			||||||
 | 
					    "index": undefined,
 | 
				
			||||||
 | 
					    "parentId": undefined,
 | 
				
			||||||
 | 
					    "path": "/about-me",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "About/me/index": {
 | 
				
			||||||
 | 
					    "componentName": "about-me-index",
 | 
				
			||||||
 | 
					    "file": "About/me/index.tsx",
 | 
				
			||||||
 | 
					    "id": "About/me/index",
 | 
				
			||||||
 | 
					    "index": true,
 | 
				
			||||||
 | 
					    "parentId": "layout",
 | 
				
			||||||
 | 
					    "path": "About/me",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "home": {
 | 
				
			||||||
 | 
					    "componentName": "home",
 | 
				
			||||||
 | 
					    "file": "home.tsx",
 | 
				
			||||||
 | 
					    "id": "home",
 | 
				
			||||||
 | 
					    "index": undefined,
 | 
				
			||||||
 | 
					    "parentId": "layout",
 | 
				
			||||||
 | 
					    "path": "home",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "index": {
 | 
				
			||||||
 | 
					    "componentName": "index",
 | 
				
			||||||
 | 
					    "file": "index.tsx",
 | 
				
			||||||
 | 
					    "id": "index",
 | 
				
			||||||
 | 
					    "index": true,
 | 
				
			||||||
 | 
					    "parentId": "layout",
 | 
				
			||||||
 | 
					    "path": undefined,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "layout": {
 | 
				
			||||||
 | 
					    "componentName": "layout",
 | 
				
			||||||
 | 
					    "file": "layout.tsx",
 | 
				
			||||||
 | 
					    "id": "layout",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": undefined,
 | 
					    "path": undefined,
 | 
				
			||||||
| 
						 | 
					@ -47,18 +92,18 @@ exports[`generateRouteManifest function > basic-routes 1`] = `
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports[`generateRouteManifest function > doc-delimeters-routes 1`] = `
 | 
					exports[`generateRouteManifest function > doc-delimeters-routes 1`] = `
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "pages/home.news": {
 | 
					  "home.news": {
 | 
				
			||||||
    "componentName": "PagesHomeNews",
 | 
					    "componentName": "home-news",
 | 
				
			||||||
    "file": "pages/home.news.tsx",
 | 
					    "file": "home.news.tsx",
 | 
				
			||||||
    "id": "pages/home.news",
 | 
					    "id": "home.news",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					    "parentId": "layout",
 | 
				
			||||||
    "path": "/home/news",
 | 
					    "path": "home/news",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/layout": {
 | 
					  "layout": {
 | 
				
			||||||
    "componentName": "PagesLayout",
 | 
					    "componentName": "layout",
 | 
				
			||||||
    "file": "pages/layout.tsx",
 | 
					    "file": "layout.tsx",
 | 
				
			||||||
    "id": "pages/layout",
 | 
					    "id": "layout",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": undefined,
 | 
					    "path": undefined,
 | 
				
			||||||
| 
						 | 
					@ -68,34 +113,34 @@ exports[`generateRouteManifest function > doc-delimeters-routes 1`] = `
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports[`generateRouteManifest function > dynamic-routes 1`] = `
 | 
					exports[`generateRouteManifest function > dynamic-routes 1`] = `
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "pages/about": {
 | 
					  "about": {
 | 
				
			||||||
    "componentName": "PagesAbout",
 | 
					    "componentName": "about",
 | 
				
			||||||
    "file": "pages/about.tsx",
 | 
					    "file": "about.tsx",
 | 
				
			||||||
    "id": "pages/about",
 | 
					    "id": "about",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": "/about",
 | 
					    "path": "about",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/blog/$id": {
 | 
					  "blog/$id": {
 | 
				
			||||||
    "componentName": "PagesBlog$id",
 | 
					    "componentName": "blog-$id",
 | 
				
			||||||
    "file": "pages/blog/$id.tsx",
 | 
					    "file": "blog/$id.tsx",
 | 
				
			||||||
    "id": "pages/blog/$id",
 | 
					    "id": "blog/$id",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": "/blog/:id",
 | 
					    "path": "blog/:id",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/blog/index": {
 | 
					  "blog/index": {
 | 
				
			||||||
    "componentName": "PagesBlogIndex",
 | 
					    "componentName": "blog-index",
 | 
				
			||||||
    "file": "pages/blog/index.tsx",
 | 
					    "file": "blog/index.tsx",
 | 
				
			||||||
    "id": "pages/blog/index",
 | 
					    "id": "blog/index",
 | 
				
			||||||
    "index": true,
 | 
					    "index": true,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": "/blog",
 | 
					    "path": "blog",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/index": {
 | 
					  "index": {
 | 
				
			||||||
    "componentName": "PagesIndex",
 | 
					    "componentName": "index",
 | 
				
			||||||
    "file": "pages/index.tsx",
 | 
					    "file": "index.tsx",
 | 
				
			||||||
    "id": "pages/index",
 | 
					    "id": "index",
 | 
				
			||||||
    "index": true,
 | 
					    "index": true,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": undefined,
 | 
					    "path": undefined,
 | 
				
			||||||
| 
						 | 
					@ -103,76 +148,113 @@ exports[`generateRouteManifest function > dynamic-routes 1`] = `
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports[`generateRouteManifest function > ignore-routes 1`] = `
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "About/me/index": {
 | 
				
			||||||
 | 
					    "componentName": "about-me-index",
 | 
				
			||||||
 | 
					    "file": "About/me/index.tsx",
 | 
				
			||||||
 | 
					    "id": "About/me/index",
 | 
				
			||||||
 | 
					    "index": true,
 | 
				
			||||||
 | 
					    "parentId": "layout",
 | 
				
			||||||
 | 
					    "path": "About/me",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "home": {
 | 
				
			||||||
 | 
					    "componentName": "home",
 | 
				
			||||||
 | 
					    "file": "home.tsx",
 | 
				
			||||||
 | 
					    "id": "home",
 | 
				
			||||||
 | 
					    "index": undefined,
 | 
				
			||||||
 | 
					    "parentId": "layout",
 | 
				
			||||||
 | 
					    "path": "home",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "index": {
 | 
				
			||||||
 | 
					    "componentName": "index",
 | 
				
			||||||
 | 
					    "file": "index.tsx",
 | 
				
			||||||
 | 
					    "id": "index",
 | 
				
			||||||
 | 
					    "index": true,
 | 
				
			||||||
 | 
					    "parentId": "layout",
 | 
				
			||||||
 | 
					    "path": undefined,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "layout": {
 | 
				
			||||||
 | 
					    "componentName": "layout",
 | 
				
			||||||
 | 
					    "file": "layout.tsx",
 | 
				
			||||||
 | 
					    "id": "layout",
 | 
				
			||||||
 | 
					    "index": undefined,
 | 
				
			||||||
 | 
					    "parentId": undefined,
 | 
				
			||||||
 | 
					    "path": undefined,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports[`generateRouteManifest function > layout-routes 1`] = `
 | 
					exports[`generateRouteManifest function > layout-routes 1`] = `
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "pages/about": {
 | 
					  "about": {
 | 
				
			||||||
    "componentName": "PagesAbout",
 | 
					    "componentName": "about",
 | 
				
			||||||
    "file": "pages/about.tsx",
 | 
					    "file": "about.tsx",
 | 
				
			||||||
    "id": "pages/about",
 | 
					    "id": "about",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					    "parentId": "layout",
 | 
				
			||||||
    "path": "/about",
 | 
					    "path": "about",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/blog/$id": {
 | 
					  "blog/$id": {
 | 
				
			||||||
    "componentName": "PagesBlog$id",
 | 
					    "componentName": "blog-$id",
 | 
				
			||||||
    "file": "pages/blog/$id.tsx",
 | 
					    "file": "blog/$id.tsx",
 | 
				
			||||||
    "id": "pages/blog/$id",
 | 
					    "id": "blog/$id",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": "pages/blog/layout",
 | 
					    "parentId": "blog/layout",
 | 
				
			||||||
    "path": "/:id",
 | 
					    "path": ":id",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/blog/index": {
 | 
					  "blog/index": {
 | 
				
			||||||
    "componentName": "PagesBlogIndex",
 | 
					    "componentName": "blog-index",
 | 
				
			||||||
    "file": "pages/blog/index.tsx",
 | 
					    "file": "blog/index.tsx",
 | 
				
			||||||
    "id": "pages/blog/index",
 | 
					    "id": "blog/index",
 | 
				
			||||||
    "index": true,
 | 
					    "index": true,
 | 
				
			||||||
    "parentId": "pages/blog/layout",
 | 
					    "parentId": "blog/layout",
 | 
				
			||||||
    "path": undefined,
 | 
					    "path": undefined,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/blog/layout": {
 | 
					  "blog/layout": {
 | 
				
			||||||
    "componentName": "PagesBlogLayout",
 | 
					    "componentName": "blog-layout",
 | 
				
			||||||
    "file": "pages/blog/layout.tsx",
 | 
					    "file": "blog/layout.tsx",
 | 
				
			||||||
    "id": "pages/blog/layout",
 | 
					    "id": "blog/layout",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					    "parentId": "layout",
 | 
				
			||||||
    "path": "/blog",
 | 
					    "path": "blog",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/home/index": {
 | 
					  "home/index": {
 | 
				
			||||||
    "componentName": "PagesHomeIndex",
 | 
					    "componentName": "home-index",
 | 
				
			||||||
    "file": "pages/home/index.tsx",
 | 
					    "file": "home/index.tsx",
 | 
				
			||||||
    "id": "pages/home/index",
 | 
					    "id": "home/index",
 | 
				
			||||||
    "index": true,
 | 
					    "index": true,
 | 
				
			||||||
    "parentId": "pages/home/layout",
 | 
					    "parentId": "home/layout",
 | 
				
			||||||
    "path": undefined,
 | 
					    "path": undefined,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/home/layout": {
 | 
					  "home/layout": {
 | 
				
			||||||
    "componentName": "PagesHomeLayout",
 | 
					    "componentName": "home-layout",
 | 
				
			||||||
    "file": "pages/home/layout.tsx",
 | 
					    "file": "home/layout.tsx",
 | 
				
			||||||
    "id": "pages/home/layout",
 | 
					    "id": "home/layout",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					    "parentId": "layout",
 | 
				
			||||||
    "path": "/home",
 | 
					    "path": "home",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/home/layout/index": {
 | 
					  "home/layout/index": {
 | 
				
			||||||
    "componentName": "PagesHomeLayoutIndex",
 | 
					    "componentName": "home-layout-index",
 | 
				
			||||||
    "file": "pages/home/layout/index.tsx",
 | 
					    "file": "home/layout/index.tsx",
 | 
				
			||||||
    "id": "pages/home/layout/index",
 | 
					    "id": "home/layout/index",
 | 
				
			||||||
    "index": true,
 | 
					    "index": true,
 | 
				
			||||||
    "parentId": "pages/home/layout",
 | 
					    "parentId": "home/layout",
 | 
				
			||||||
    "path": "/layout",
 | 
					    "path": "layout",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/index": {
 | 
					  "index": {
 | 
				
			||||||
    "componentName": "PagesIndex",
 | 
					    "componentName": "index",
 | 
				
			||||||
    "file": "pages/index.tsx",
 | 
					    "file": "index.tsx",
 | 
				
			||||||
    "id": "pages/index",
 | 
					    "id": "index",
 | 
				
			||||||
    "index": true,
 | 
					    "index": true,
 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					    "parentId": "layout",
 | 
				
			||||||
    "path": undefined,
 | 
					    "path": undefined,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/layout": {
 | 
					  "layout": {
 | 
				
			||||||
    "componentName": "PagesLayout",
 | 
					    "componentName": "layout",
 | 
				
			||||||
    "file": "pages/layout.tsx",
 | 
					    "file": "layout.tsx",
 | 
				
			||||||
    "id": "pages/layout",
 | 
					    "id": "layout",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": undefined,
 | 
					    "path": undefined,
 | 
				
			||||||
| 
						 | 
					@ -182,235 +264,26 @@ exports[`generateRouteManifest function > layout-routes 1`] = `
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports[`generateRouteManifest function > splat-routes 1`] = `
 | 
					exports[`generateRouteManifest function > splat-routes 1`] = `
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "pages/$": {
 | 
					  "$": {
 | 
				
			||||||
    "componentName": "Pages$",
 | 
					    "componentName": "$",
 | 
				
			||||||
    "file": "pages/$.tsx",
 | 
					    "file": "$.tsx",
 | 
				
			||||||
    "id": "pages/$",
 | 
					    "id": "$",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					    "parentId": "layout",
 | 
				
			||||||
    "path": "/*",
 | 
					    "path": "*",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/home": {
 | 
					  "home": {
 | 
				
			||||||
    "componentName": "PagesHome",
 | 
					    "componentName": "home",
 | 
				
			||||||
    "file": "pages/home.tsx",
 | 
					    "file": "home.tsx",
 | 
				
			||||||
    "id": "pages/home",
 | 
					    "id": "home",
 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					    "parentId": "layout",
 | 
				
			||||||
    "path": "/home",
 | 
					    "path": "home",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "pages/layout": {
 | 
					  "layout": {
 | 
				
			||||||
    "componentName": "PagesLayout",
 | 
					    "componentName": "layout",
 | 
				
			||||||
    "file": "pages/layout.tsx",
 | 
					    "file": "layout.tsx",
 | 
				
			||||||
    "id": "pages/layout",
 | 
					    "id": "layout",
 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": undefined,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
exports[`generateRouteManifest function basic-routes 1`] = `
 | 
					 | 
				
			||||||
Object {
 | 
					 | 
				
			||||||
  "pages/About/index": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesAboutIndex",
 | 
					 | 
				
			||||||
    "file": "pages/About/index.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/About/index",
 | 
					 | 
				
			||||||
    "index": true,
 | 
					 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					 | 
				
			||||||
    "path": "/About",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/About/me/index": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesAboutMeIndex",
 | 
					 | 
				
			||||||
    "file": "pages/About/me/index.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/About/me/index",
 | 
					 | 
				
			||||||
    "index": true,
 | 
					 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					 | 
				
			||||||
    "path": "/About/me",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/home": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesHome",
 | 
					 | 
				
			||||||
    "file": "pages/home.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/home",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					 | 
				
			||||||
    "path": "/home",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/index": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesIndex",
 | 
					 | 
				
			||||||
    "file": "pages/index.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/index",
 | 
					 | 
				
			||||||
    "index": true,
 | 
					 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					 | 
				
			||||||
    "path": undefined,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/layout": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesLayout",
 | 
					 | 
				
			||||||
    "file": "pages/layout.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/layout",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": undefined,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
exports[`generateRouteManifest function doc-delimeters-routes 1`] = `
 | 
					 | 
				
			||||||
Object {
 | 
					 | 
				
			||||||
  "pages/home.news": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesHomeNews",
 | 
					 | 
				
			||||||
    "file": "pages/home.news.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/home.news",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					 | 
				
			||||||
    "path": "/home/news",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/layout": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesLayout",
 | 
					 | 
				
			||||||
    "file": "pages/layout.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/layout",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": undefined,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
exports[`generateRouteManifest function dynamic-routes 1`] = `
 | 
					 | 
				
			||||||
Object {
 | 
					 | 
				
			||||||
  "pages/about": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesAbout",
 | 
					 | 
				
			||||||
    "file": "pages/about.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/about",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": "/about",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/blog/$id": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesBlog$id",
 | 
					 | 
				
			||||||
    "file": "pages/blog/$id.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/blog/$id",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": "/blog/:id",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/blog/index": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesBlogIndex",
 | 
					 | 
				
			||||||
    "file": "pages/blog/index.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/blog/index",
 | 
					 | 
				
			||||||
    "index": true,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": "/blog",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/index": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesIndex",
 | 
					 | 
				
			||||||
    "file": "pages/index.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/index",
 | 
					 | 
				
			||||||
    "index": true,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": undefined,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
exports[`generateRouteManifest function layout-routes 1`] = `
 | 
					 | 
				
			||||||
Object {
 | 
					 | 
				
			||||||
  "pages/about": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesAbout",
 | 
					 | 
				
			||||||
    "file": "pages/about.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/about",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					 | 
				
			||||||
    "path": "/about",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/blog/$id": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesBlog$id",
 | 
					 | 
				
			||||||
    "file": "pages/blog/$id.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/blog/$id",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": "pages/blog/layout",
 | 
					 | 
				
			||||||
    "path": "/:id",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/blog/index": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesBlogIndex",
 | 
					 | 
				
			||||||
    "file": "pages/blog/index.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/blog/index",
 | 
					 | 
				
			||||||
    "index": true,
 | 
					 | 
				
			||||||
    "parentId": "pages/blog/layout",
 | 
					 | 
				
			||||||
    "path": undefined,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/blog/layout": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesBlogLayout",
 | 
					 | 
				
			||||||
    "file": "pages/blog/layout.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/blog/layout",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					 | 
				
			||||||
    "path": "/blog",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/home/index": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesHomeIndex",
 | 
					 | 
				
			||||||
    "file": "pages/home/index.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/home/index",
 | 
					 | 
				
			||||||
    "index": true,
 | 
					 | 
				
			||||||
    "parentId": "pages/home/layout",
 | 
					 | 
				
			||||||
    "path": undefined,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/home/layout": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesHomeLayout",
 | 
					 | 
				
			||||||
    "file": "pages/home/layout.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/home/layout",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					 | 
				
			||||||
    "path": "/home",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/home/layout/index": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesHomeLayoutIndex",
 | 
					 | 
				
			||||||
    "file": "pages/home/layout/index.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/home/layout/index",
 | 
					 | 
				
			||||||
    "index": true,
 | 
					 | 
				
			||||||
    "parentId": "pages/home/layout",
 | 
					 | 
				
			||||||
    "path": "/layout",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/index": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesIndex",
 | 
					 | 
				
			||||||
    "file": "pages/index.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/index",
 | 
					 | 
				
			||||||
    "index": true,
 | 
					 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					 | 
				
			||||||
    "path": undefined,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/layout": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesLayout",
 | 
					 | 
				
			||||||
    "file": "pages/layout.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/layout",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": undefined,
 | 
					 | 
				
			||||||
    "path": undefined,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
exports[`generateRouteManifest function splat-routes 1`] = `
 | 
					 | 
				
			||||||
Object {
 | 
					 | 
				
			||||||
  "pages/$": Object {
 | 
					 | 
				
			||||||
    "componentName": "Pages$",
 | 
					 | 
				
			||||||
    "file": "pages/$.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/$",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					 | 
				
			||||||
    "path": "/*",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/home": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesHome",
 | 
					 | 
				
			||||||
    "file": "pages/home.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/home",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					 | 
				
			||||||
    "parentId": "pages/layout",
 | 
					 | 
				
			||||||
    "path": "/home",
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "pages/layout": Object {
 | 
					 | 
				
			||||||
    "componentName": "PagesLayout",
 | 
					 | 
				
			||||||
    "file": "pages/layout.tsx",
 | 
					 | 
				
			||||||
    "id": "pages/layout",
 | 
					 | 
				
			||||||
    "index": undefined,
 | 
					    "index": undefined,
 | 
				
			||||||
    "parentId": undefined,
 | 
					    "parentId": undefined,
 | 
				
			||||||
    "path": undefined,
 | 
					    "path": undefined,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,22 @@ describe('generateRouteManifest function', () => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  test('invalid-routes', () => {
 | 
					  test('invalid-routes', () => {
 | 
				
			||||||
    expect(() => generateRouteManifest(path.join(fixturesDir, 'invalid-routes'))).toThrow(`invalid character in 'pages/[a.pdf]'. Only support char: -, \\w, /`);
 | 
					    expect(() => generateRouteManifest(path.join(fixturesDir, 'invalid-routes'))).toThrow(`invalid character in 'src/pages/[a.pdf].tsx'. Only support char: -, \\w, /`);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('ignore-routes', () => {
 | 
				
			||||||
 | 
					    const routeManifest = generateRouteManifest(path.join(fixturesDir, 'basic-routes'), ['About/index.tsx']);
 | 
				
			||||||
 | 
					    expect(routeManifest).toMatchSnapshot();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('define-extra-routes', () => {
 | 
				
			||||||
 | 
					    const routeManifest = generateRouteManifest(
 | 
				
			||||||
 | 
					      path.join(fixturesDir, 'basic-routes'), 
 | 
				
			||||||
 | 
					      ['About/index.tsx'], 
 | 
				
			||||||
 | 
					      (defineRoute) => {
 | 
				
			||||||
 | 
					        defineRoute('/about-me', 'About/index.tsx');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    expect(routeManifest).toMatchSnapshot();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,8 @@
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Link,
 | 
					  Link,
 | 
				
			||||||
  Outlet,
 | 
					  Outlet,
 | 
				
			||||||
 | 
					  useParams,
 | 
				
			||||||
 | 
					  useSearchParams,
 | 
				
			||||||
} from 'react-router-dom';
 | 
					} from 'react-router-dom';
 | 
				
			||||||
import Runtime from './runtime.js';
 | 
					import Runtime from './runtime.js';
 | 
				
			||||||
import App from './App.js';
 | 
					import App from './App.js';
 | 
				
			||||||
| 
						 | 
					@ -14,7 +16,7 @@ import {
 | 
				
			||||||
  Scripts,
 | 
					  Scripts,
 | 
				
			||||||
  Main,
 | 
					  Main,
 | 
				
			||||||
} from './Document.js';
 | 
					} from './Document.js';
 | 
				
			||||||
import {
 | 
					import type {
 | 
				
			||||||
  RuntimePlugin,
 | 
					  RuntimePlugin,
 | 
				
			||||||
  AppContext,
 | 
					  AppContext,
 | 
				
			||||||
  AppConfig,
 | 
					  AppConfig,
 | 
				
			||||||
| 
						 | 
					@ -32,15 +34,20 @@ export {
 | 
				
			||||||
  runServerApp,
 | 
					  runServerApp,
 | 
				
			||||||
  renderDocument,
 | 
					  renderDocument,
 | 
				
			||||||
  useAppContext,
 | 
					  useAppContext,
 | 
				
			||||||
  Link,
 | 
					 | 
				
			||||||
  Outlet,
 | 
					 | 
				
			||||||
  Meta,
 | 
					  Meta,
 | 
				
			||||||
  Title,
 | 
					  Title,
 | 
				
			||||||
  Links,
 | 
					  Links,
 | 
				
			||||||
  Scripts,
 | 
					  Scripts,
 | 
				
			||||||
  Main,
 | 
					  Main,
 | 
				
			||||||
  defineAppConfig,
 | 
					  defineAppConfig,
 | 
				
			||||||
  // types
 | 
					  // react-router-dom API
 | 
				
			||||||
 | 
					  Link,
 | 
				
			||||||
 | 
					  Outlet,
 | 
				
			||||||
 | 
					  useParams,
 | 
				
			||||||
 | 
					  useSearchParams,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type {
 | 
				
			||||||
  RuntimePlugin,
 | 
					  RuntimePlugin,
 | 
				
			||||||
  AppContext,
 | 
					  AppContext,
 | 
				
			||||||
  AppConfig,
 | 
					  AppConfig,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,8 +107,8 @@ function BrowserEntry({ history, appContext, Document, ...rest }: BrowserEntryPr
 | 
				
			||||||
  useLayoutEffect(() => {
 | 
					  useLayoutEffect(() => {
 | 
				
			||||||
    history.listen(({ action, location }) => {
 | 
					    history.listen(({ action, location }) => {
 | 
				
			||||||
      const matches = matchRoutes(routes, location);
 | 
					      const matches = matchRoutes(routes, location);
 | 
				
			||||||
      if (!matches) {
 | 
					      if (!matches.length) {
 | 
				
			||||||
        throw new Error(`Routes not found in location ${location}.`);
 | 
					        throw new Error(`Routes not found in location ${location.pathname}.`);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      loadNextPage(matches, (pageData) => {
 | 
					      loadNextPage(matches, (pageData) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@
 | 
				
			||||||
  "bugs": "https://github.com/ice-lab/ice-next/issues",
 | 
					  "bugs": "https://github.com/ice-lab/ice-next/issues",
 | 
				
			||||||
  "homepage": "https://next.ice.work",
 | 
					  "homepage": "https://next.ice.work",
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "build-scripts": "^2.0.0-15",
 | 
					    "build-scripts": "^2.0.0-16",
 | 
				
			||||||
    "esbuild": "^0.14.23",
 | 
					    "esbuild": "^0.14.23",
 | 
				
			||||||
    "@ice/runtime": "^1.0.0",
 | 
					    "@ice/runtime": "^1.0.0",
 | 
				
			||||||
    "@ice/route-manifest": "^1.0.0",
 | 
					    "@ice/route-manifest": "^1.0.0",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					import type { DefineRouteFunction } from '@ice/route-manifest';
 | 
				
			||||||
import type { IPluginList } from 'build-scripts';
 | 
					import type { IPluginList } from 'build-scripts';
 | 
				
			||||||
import type { Config, ModifyWebpackConfig } from './config';
 | 
					import type { Config, ModifyWebpackConfig } from './config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,5 +13,9 @@ export interface UserConfig {
 | 
				
			||||||
  proxy?: Config['proxy'];
 | 
					  proxy?: Config['proxy'];
 | 
				
			||||||
  filename?: string;
 | 
					  filename?: string;
 | 
				
			||||||
  webpack?: ModifyWebpackConfig;
 | 
					  webpack?: ModifyWebpackConfig;
 | 
				
			||||||
 | 
					  routes?: {
 | 
				
			||||||
 | 
					    ignoreFiles?: string[];
 | 
				
			||||||
 | 
					    defineRoutes?: (defineRoute: DefineRouteFunction) => void;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
  plugins?: IPluginList;
 | 
					  plugins?: IPluginList;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										702
									
								
								pnpm-lock.yaml
								
								
								
								
							
							
						
						
									
										702
									
								
								pnpm-lock.yaml
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -16,7 +16,6 @@ describe(`build ${example}`, () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  test('open /', async () => {
 | 
					  test('open /', async () => {
 | 
				
			||||||
    await buildFixture(example);
 | 
					    await buildFixture(example);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    const res = await setupBrowser({ example });
 | 
					    const res = await setupBrowser({ example });
 | 
				
			||||||
    page = res.page;
 | 
					    page = res.page;
 | 
				
			||||||
    browser = res.browser;
 | 
					    browser = res.browser;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,107 @@
 | 
				
			||||||
 | 
					import { expect, test, describe, afterAll } from 'vitest';
 | 
				
			||||||
 | 
					import { buildFixture, setupBrowser } from '../utils/build';
 | 
				
			||||||
 | 
					import { startFixture, setupStartBrowser } from '../utils/start';
 | 
				
			||||||
 | 
					import { Page } from '../utils/browser';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const example = 'routes-generate';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe(`build ${example}`, () => {
 | 
				
			||||||
 | 
					  let page: Page = null;
 | 
				
			||||||
 | 
					  let browser = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('open /', async () => {
 | 
				
			||||||
 | 
					    await buildFixture(example);
 | 
				
			||||||
 | 
					    const res = await setupBrowser({ example });
 | 
				
			||||||
 | 
					    page = res.page;
 | 
				
			||||||
 | 
					    browser = res.browser;
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual(['Layout']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['Home']);
 | 
				
			||||||
 | 
					  }, 120000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('define extra routes', async () => {
 | 
				
			||||||
 | 
					    let res = await setupBrowser({ example, defaultHtml: 'about-me.html' });
 | 
				
			||||||
 | 
					    page = res.page;
 | 
				
			||||||
 | 
					    browser = res.browser;
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual([]);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['About']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res = await setupBrowser({ example, defaultHtml: 'product.html' });
 | 
				
			||||||
 | 
					    page = res.page;
 | 
				
			||||||
 | 
					    browser = res.browser;
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual(['Layout']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['Products Page']);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('page layout', async () => {
 | 
				
			||||||
 | 
					    let res = await setupBrowser({ example, defaultHtml: 'dashboard/a.html' });
 | 
				
			||||||
 | 
					    page = res.page;
 | 
				
			||||||
 | 
					    browser = res.browser;
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual(['Layout']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['Dashboard']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h3')).toStrictEqual(['A page']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res = await setupBrowser({ example, defaultHtml: 'dashboard/b.html' });
 | 
				
			||||||
 | 
					    page = res.page;
 | 
				
			||||||
 | 
					    browser = res.browser;
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual(['Layout']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['Dashboard']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h3')).toStrictEqual(['B page']);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // TODO: dynamic-routes test
 | 
				
			||||||
 | 
					  test.todo('dynamic routes', async () => {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  afterAll(async () => {
 | 
				
			||||||
 | 
					    await browser.close();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe(`start ${example}`, () => {
 | 
				
			||||||
 | 
					  let page: Page = null;
 | 
				
			||||||
 | 
					  let browser = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('setup devServer', async () => {
 | 
				
			||||||
 | 
					    const { devServer, port } = await startFixture(example);
 | 
				
			||||||
 | 
					    const res = await setupStartBrowser({ server: devServer, port });
 | 
				
			||||||
 | 
					    page = res.page;
 | 
				
			||||||
 | 
					    browser = res.browser;
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual(['Layout']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['Home']);
 | 
				
			||||||
 | 
					  }, 120000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('define extra routes', async () => {
 | 
				
			||||||
 | 
					    await page.push('about-me');
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual([]);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['About']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await page.push('product');
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual(['Layout']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['Products Page']);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('page layout', async () => {
 | 
				
			||||||
 | 
					    await page.push('dashboard/a');
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual(['Layout']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['Dashboard']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h3')).toStrictEqual(['A page']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await page.push('dashboard/b');
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual(['Layout']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['Dashboard']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h3')).toStrictEqual(['B page']);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('dynamic routes layout', async () => {
 | 
				
			||||||
 | 
					    await page.push('detail/a');
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual(['Layout']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['Detail id: a']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await page.push('detail/b');
 | 
				
			||||||
 | 
					    expect(await page.$$text('h1')).toStrictEqual(['Layout']);
 | 
				
			||||||
 | 
					    expect(await page.$$text('h2')).toStrictEqual(['Detail id: b']);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  afterAll(async () => {
 | 
				
			||||||
 | 
					    await browser.close();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,8 @@ export default defineConfig({
 | 
				
			||||||
    alias: { ...moduleNameMapper },
 | 
					    alias: { ...moduleNameMapper },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  test: {
 | 
					  test: {
 | 
				
			||||||
    // disable threads to avoid `Segmentation fault (core dumped)` error: https://github.com/vitest-dev/vitest/issues/317
 | 
					    // To avoid error `Segmentation fault (core dumped)` in CI environment, disable threads
 | 
				
			||||||
 | 
					    // ref: https://github.com/vitest-dev/vitest/issues/317
 | 
				
			||||||
    threads: false,
 | 
					    threads: false,
 | 
				
			||||||
    exclude: [
 | 
					    exclude: [
 | 
				
			||||||
      '**/node_modules/**',
 | 
					      '**/node_modules/**',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue