Skip to content

Commit

Permalink
fix(router): catch all routes should work now
Browse files Browse the repository at this point in the history
  • Loading branch information
evenchange4 committed Jun 4, 2024
1 parent 68b1f96 commit 15cde8e
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 0 deletions.
19 changes: 19 additions & 0 deletions packages/router/src/__tests__/pathRanking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,25 @@ describe('Path ranking', () => {
});
});

it('catchAll /* should be the last one', () => {
checkPathOrder(['/a', '/*']);
checkPathOrder(['/a/*', '/a', '/*']);

possibleOptions.forEach(options => {
checkPathOrder([['', options], '/*']);
checkPathOrder([['/', options], '/*']);
checkPathOrder([['/ab', options], '/*']);
checkPathOrder([['/:a', options], '/*']);
checkPathOrder([['/:a?', options], '/*']);
checkPathOrder([['/:a+', options], '/*']);
checkPathOrder([['/:a*', options], '/*']);
checkPathOrder([['/:a(\\d+)', options], '/*']);
checkPathOrder([['/:a(\\d+)?', options], '/*']);
checkPathOrder([['/:a(\\d+)+', options], '/*']);
checkPathOrder([['/:a(\\d+)*', options], '/*']);
});
});

it('handles sub segments', () => {
checkPathOrder([
'/a/_2_',
Expand Down
27 changes: 27 additions & 0 deletions packages/router/src/matchRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,32 @@ function flattenRoutes<T extends IRouteBaseObject>(
return branches;
}

/**
* 1. Remove layout branches.
* Only keep the page branches which the last route is a page.
* 2. Remove path ending with slash
*
* @param branches
* @returns branches
*/
function onlyPageBranches<T extends IRouteBaseObject>(
branches: IRouteBranch<T>[] = []
): IRouteBranch<T>[] {
// 1. remove layout branches
const newBranches = branches.filter(([_path, routes]) => {
const lastRoute = routes[routes.length - 1];
// @ts-expect-error todo
return lastRoute?.__componentSource__?.endsWith('page');
});

// 2. remove path ending with slash
newBranches.forEach(branch => {
branch[0] = branch[0].slice(0, -1);
});

return newBranches;
}

export function matchRoutes<T extends IRouteBaseObject>(
routes: T[],
location: string | PartialLocation,
Expand All @@ -127,6 +153,7 @@ export function matchRoutes<T extends IRouteBaseObject>(
}

let branches = flattenRoutes(routes);
branches = onlyPageBranches(branches);
branches = rankRouteBranches(branches);

let matches: IRouteMatch<T>[] | null = null;
Expand Down
52 changes: 52 additions & 0 deletions test/e2e/catch-all.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { AppCtx, Page, devFixture } from '../utils';

let ctx: AppCtx;
let page: Page;

jest.setTimeout(5 * 60 * 1000);

describe('catch-all', () => {
beforeAll(async () => {
ctx = await devFixture('catch-all');
});
afterAll(async () => {
await ctx.close();
});
afterEach(async () => {
await page.close();
});

test('should exact match home page', async () => {
page = await ctx.browser.page(ctx.url('/home'));
await page.waitForSelector('[id="home-page"]');
expect(await page.$text('[id="global-layout"]')).toBe('/layout.js');
expect(await page.$text('[id="home-page"]')).toBe('/home/page.js');
expect(await page.$text('[id="home-layout"]')).toBe('/home/layout.js');
});

test('should match catchAll', async () => {
page = await ctx.browser.page(ctx.url('/other'));
await page.waitForSelector('[id="catchAll-page"]');
expect(await page.$text('[id="global-layout"]')).toBe('/layout.js');
expect(await page.$text('[id="catchAll-page"]')).toBe('/$/page.js');
expect(await page.$text('[id="catchAll-layout"]')).toBe('/$/layout.js');
});

test('should match /:symbol/calc', async () => {
page = await ctx.browser.page(ctx.url('/symbol/calc'));
await page.waitForSelector('[id="calc-page"]');
expect(await page.$text('[id="global-layout"]')).toBe('/layout.js');
expect(await page.$text('[id="calc-page"]')).toBe('/$symbol/calc/page.js');
expect(await page.$text('[id="calc-layout"]')).toBe(
'/$symbol/calc/layout.js'
);
});

test('/symbol/calc2 should match catchAll', async () => {
page = await ctx.browser.page(ctx.url('/symbol/calc2'));
await page.waitForSelector('[id="catchAll-page"]');
expect(await page.$text('[id="global-layout"]')).toBe('/layout.js');
expect(await page.$text('[id="catchAll-page"]')).toBe('/$/page.js');
expect(await page.$text('[id="catchAll-layout"]')).toBe('/$/layout.js');
});
});
6 changes: 6 additions & 0 deletions test/fixtures/catch-all/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "fixture-catch-all",
"dependencies": {
"shuvi": "workspace:*"
}
}
3 changes: 3 additions & 0 deletions test/fixtures/catch-all/shuvi.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
ssr: true
};
10 changes: 10 additions & 0 deletions test/fixtures/catch-all/src/routes/$/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { RouterView } from '@shuvi/runtime';

const Layout = () => (
<div>
<div id="catchAll-layout">/$/layout.js</div>
<RouterView />
</div>
);

export default Layout;
7 changes: 7 additions & 0 deletions test/fixtures/catch-all/src/routes/$/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Index() {
return (
<>
<div id="catchAll-page">/$/page.js</div>
</>
);
}
10 changes: 10 additions & 0 deletions test/fixtures/catch-all/src/routes/$symbol/calc/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { RouterView } from '@shuvi/runtime';

const Layout = () => (
<div>
<div id="calc-layout">/$symbol/calc/layout.js</div>
<RouterView />
</div>
);

export default Layout;
7 changes: 7 additions & 0 deletions test/fixtures/catch-all/src/routes/$symbol/calc/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Index() {
return (
<>
<div id="calc-page">/$symbol/calc/page.js</div>
</>
);
}
10 changes: 10 additions & 0 deletions test/fixtures/catch-all/src/routes/home/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { RouterView } from '@shuvi/runtime';

const Layout = () => (
<div>
<div id="home-layout">/home/layout.js</div>
<RouterView />
</div>
);

export default Layout;
7 changes: 7 additions & 0 deletions test/fixtures/catch-all/src/routes/home/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Index() {
return (
<>
<div id="home-page">/home/page.js</div>
</>
);
}
10 changes: 10 additions & 0 deletions test/fixtures/catch-all/src/routes/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { RouterView } from '@shuvi/runtime';

const GlobalLayout = () => (
<div>
<div id="global-layout">/layout.js</div>
<RouterView />
</div>
);

export default GlobalLayout;

0 comments on commit 15cde8e

Please sign in to comment.