Skip to content

Commit

Permalink
upgrade path-to-regexp
Browse files Browse the repository at this point in the history
  • Loading branch information
yyanwang committed Dec 24, 2024
1 parent 9f3cf10 commit 87618a7
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 11 deletions.
2 changes: 1 addition & 1 deletion packages/cmf-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"connected-react-router": "^6.9.3",
"history": "^5.3.0",
"lodash": "^4.17.21",
"path-to-regexp": "^3.3.0",
"path-to-regexp": "^8.2.0",
"prop-types": "^15.8.1",
"react-redux": "^7.2.9",
"react-router": "~6.3.0",
Expand Down
39 changes: 39 additions & 0 deletions packages/cmf-router/src/sagaRouter.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,42 @@ if the route change to `localhost/datasets/add`
the `datasetsSaga` will be restarted since it still match on `/datasets/add{/:connectionId}` route and that the parameter has changed from being a value to being absent.

the {/:connectionId} at the end of path means /connectionId is optional.

### Root Path Matching

The root path `/` has special matching behavior that's important to understand:

1. Exact root path matching:

```javascript
const routes = {
'/': function* rootSaga() {
yield take('SOMETHING');
},
};
```

- Only matches exactly `/`
- Does not match child routes like `/tasks` or `/users/123`
- This is because path-to-regexp treats the root path `/` differently than other routes - it won't do partial matching even when `exact` is false
- If you want `/` to match any path that starts with `/`, you need to use a wildcard pattern like `/{*path}`

2. Matching root and all child routes:

```javascript
const routes = {
'/{*path}': function* rootSaga({ path }) {
yield take('SOMETHING');
},
};
```

- Matches both root path `/` and all child routes
- For root path `/`, params will be empty `{}`
- For child routes, `params.path` will contain the remaining path:
- `/tasks``{ path: 'tasks' }`
- `/tasks/123``{ path: 'tasks/123' }`

This pattern is particularly useful when you need a saga to run for all routes in your application while still being able to access the current route path.

For more details about path matching and troubleshooting, see [path-to-regexp documentation](https://github.com/pillarjs/path-to-regexp#errors).
20 changes: 20 additions & 0 deletions packages/cmf-router/src/sagaRouter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,24 @@ describe('sagaRouter route and route params', () => {
expect(gen.next({ type: '@@router/LOCATION_CHANGE' }).value).toEqual(expectedCancelYield);
expect(gen.next().value).toEqual(spawn(routes['/matchingroute{/:optional}'], {}, true));
});

it('should start root path saga when on child route', () => {
const mockHistory = {
get location() {
return {
pathname: '/tasks',
};
},
};
const routes = {
'/{*path}': function* rootSaga() {
yield take('SOMETHING');
},
};
const gen = sagaRouter(mockHistory, routes);

// Root saga should be started with isExact=true for wildcard path
expect(gen.next().value).toEqual(spawn(routes['/{*path}'], { path: 'tasks' }, true));
expect(gen.next().value).toEqual(take('@@router/LOCATION_CHANGE'));
});
});
33 changes: 33 additions & 0 deletions packages/cmf/__tests__/matchPath.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ describe('matchPath', () => {
const pathname = '/';
const match = matchPath(pathname, path);
expect(match.url).toBe('/');
expect(match.isExact).toBe(true);
});
});

Expand All @@ -16,13 +17,15 @@ describe('matchPath', () => {
const pathname = '/somewhere';
const match = matchPath(pathname, path);
expect(match.url).toBe('/somewhere');
expect(match.isExact).toBe(true);
});

it('returns correct url at "/somewhere/else"', () => {
const path = '/somewhere';
const pathname = '/somewhere/else';
const match = matchPath(pathname, path);
expect(match.url).toBe('/somewhere');
expect(match.isExact).toBe(false);
});
});

Expand All @@ -34,6 +37,36 @@ describe('matchPath', () => {
const pathname = '/somewhere';
const match = matchPath(pathname, options);
expect(match.url).toBe('/somewhere');
expect(match.isExact).toBe(true);
});
});

describe('with path="/{*path}"', () => {
it('returns correct match at root "/"', () => {
const path = '/{*path}';
const pathname = '/';
const match = matchPath(pathname, path);
expect(match.url).toBe('/');
expect(match.params).toEqual({});
expect(match.isExact).toBe(true);
});

it('returns correct match and params for child route "/tasks"', () => {
const path = '/{*path}';
const pathname = '/tasks';
const match = matchPath(pathname, path);
expect(match.url).toBe('/tasks');
expect(match.params).toEqual({ path: 'tasks' });
expect(match.isExact).toBe(true);
});

it('returns correct match and params for nested route "/tasks/123"', () => {
const path = '/{*path}';
const pathname = '/tasks/123';
const match = matchPath(pathname, path);
expect(match.url).toBe('/tasks/123');
expect(match.params).toEqual({ path: 'tasks/123' });
expect(match.isExact).toBe(true);
});
});
});
2 changes: 1 addition & 1 deletion packages/cmf/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"invariant": "^2.2.4",
"lodash": "^4.17.21",
"nested-combine-reducers": "^1.2.2",
"path-to-regexp": "^3.3.0",
"path-to-regexp": "^8.2.0",
"prop-types": "^15.8.1",
"react-immutable-proptypes": "^2.2.0",
"react-redux": "^7.2.9",
Expand Down
7 changes: 3 additions & 4 deletions packages/cmf/src/matchPath.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Beware! Do not modify. Forked from react-router V4
* Will be available as a dependency
*/
import pathToRegexp from 'path-to-regexp';
import { pathToRegexp } from 'path-to-regexp';

const patternCache = {};
const cacheLimit = 10000;
Expand All @@ -14,9 +14,8 @@ const compilePath = (pattern, options) => {

if (cache[pattern]) return cache[pattern];

const keys = [];
const re = pathToRegexp(pattern, keys, options);
const compiledPattern = { re, keys };
const { regexp, keys } = pathToRegexp(pattern, options);
const compiledPattern = { re: regexp, keys };

if (cacheCount < cacheLimit) {
cache[pattern] = compiledPattern;
Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14953,16 +14953,16 @@ path-to-regexp@^2.2.1:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704"
integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==

path-to-regexp@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.3.0.tgz#f7f31d32e8518c2660862b644414b6d5c63a611b"
integrity sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==

path-to-regexp@^6.2.1, path-to-regexp@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4"
integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==

path-to-regexp@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4"
integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==

path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
Expand Down

0 comments on commit 87618a7

Please sign in to comment.