Skip to content

Commit

Permalink
Support optional segments in check routes CLI command (#774)
Browse files Browse the repository at this point in the history
* Add tests for missing-routes

* Support optional segments in missing-routes

* Changeset
  • Loading branch information
frandiox committed Apr 18, 2023
1 parent b098780 commit 2039a4a
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/bright-impalas-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/cli-hydrogen': patch
---

Fix the `check routes` command to match optional segments.
52 changes: 52 additions & 0 deletions packages/cli/src/lib/missing-routes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {describe, it, expect} from 'vitest';
import {findMissingRoutes} from './missing-routes.js';

const createRoute = (path: string) => ({
routes: {
'route-id': {
file: 'a/file',
id: 'route-id',
path,
},
},
});

describe('missing-routes', () => {
it('matches routes with dots', async () => {
const requiredRoutes = ['sitemap.xml'];

expect(findMissingRoutes({routes: {}}, requiredRoutes)).toHaveLength(1);
expect(
findMissingRoutes(createRoute('sitemap.xml'), requiredRoutes),
).toHaveLength(0);
});

it('matches routes with different parameter names', async () => {
const requiredRoutes = ['collections/:collectionHandle'];

expect(findMissingRoutes({routes: {}}, requiredRoutes)).toHaveLength(1);
expect(
findMissingRoutes(createRoute('collections/:param'), requiredRoutes),
).toHaveLength(0);
});

it('matches optional segments in different positions', async () => {
const requiredRoutes = ['collections/products'];
const validRoutes = [
'segment?/collections/products',
':segment?/collections/products',
'collections/segment?/products',
'collections/:segment?/products',
'collections/products/segment?',
'collections/products/:segment?',
];

expect(findMissingRoutes({routes: {}}, requiredRoutes)).toHaveLength(1);

for (const validRoute of validRoutes) {
expect(
findMissingRoutes(createRoute(validRoute), requiredRoutes),
).toHaveLength(0);
}
});
});
25 changes: 15 additions & 10 deletions packages/cli/src/lib/missing-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,17 @@ const REQUIRED_ROUTES = [
// 'opening_soon',
];

export function findMissingRoutes(config: RemixConfig) {
export function findMissingRoutes(
config: {routes: RemixConfig['routes']},
requiredRoutes = REQUIRED_ROUTES,
) {
const userRoutes = Object.values(config.routes);
const requiredRoutes = new Set(REQUIRED_ROUTES);
const missingRoutes = new Set(requiredRoutes);

for (const requiredRoute of requiredRoutes) {
for (const userRoute of userRoutes) {
if (!requiredRoute && !userRoute.path) {
requiredRoutes.delete(requiredRoute);
missingRoutes.delete(requiredRoute);
} else if (requiredRoute && userRoute.path) {
const currentRoute = {
path: userRoute.path,
Expand All @@ -67,21 +70,23 @@ export function findMissingRoutes(config: RemixConfig) {
currentRoute.parentId = parentRoute.parentId;
}

const optionalSegment = ':?[^\\/\\?]+\\?';
const reString =
// Starts with optional params
'^(:[^\\/\\?]+\\?\\/)?' +
// Escape dots and replace params with regex
requiredRoute.replaceAll('.', '\\.').replace(/:[^/]+/g, ':[^\\/]+') +
'$';
`^(${optionalSegment}\\/)?` + // Starts with an optional segment
requiredRoute
.replaceAll('.', '\\.') // Escape dots
.replace(/\//g, `\\/(${optionalSegment}\\/)?`) // Has optional segments in the middle
.replace(/:[^/)?]+/g, ':[^\\/]+') + // Replace params with regex
`(\\/${optionalSegment})?$`; // Ends with an optional segment

if (new RegExp(reString).test(currentRoute.path)) {
requiredRoutes.delete(requiredRoute);
missingRoutes.delete(requiredRoute);
}
}
}
}

return [...requiredRoutes];
return [...missingRoutes];
}

const LINE_LIMIT = 100;
Expand Down

0 comments on commit 2039a4a

Please sign in to comment.