From 5e9942b007a65ddc70a935c48176332550af2d3f Mon Sep 17 00:00:00 2001 From: Marcelo Botega Fontana Date: Mon, 22 May 2023 18:15:44 +0200 Subject: [PATCH] fix(utils): respect patterns within paths when sorting routes (#20669) --- packages/utils/src/route.js | 11 +- .../test/__snapshots__/route.test.js.snap | 150 ++++++++++++++---- packages/utils/test/route.test.js | 107 ++++++++++++- test/dev/dynamic-routes.test.js | 28 ++-- 4 files changed, 245 insertions(+), 51 deletions(-) diff --git a/packages/utils/src/route.js b/packages/utils/src/route.js index 9c22e59692..e3fbc8cb86 100644 --- a/packages/utils/src/route.js +++ b/packages/utils/src/route.js @@ -97,7 +97,7 @@ function cleanChildrenRoutes (routes, isChild = false, routeNameSplitter = '-', return routes } -const DYNAMIC_ROUTE_REGEX = /^\/([:*])/ +const DYNAMIC_ROUTE_REGEX = /[:*]/ export const sortRoutes = function sortRoutes (routes) { routes.sort((a, b) => { @@ -126,13 +126,13 @@ export const sortRoutes = function sortRoutes (routes) { if (res !== 0) { break } - y = _a[i] === '*' ? 2 : _a[i].includes(':') ? 1 : 0 - z = _b[i] === '*' ? 2 : _b[i].includes(':') ? 1 : 0 + y = _a[i] === '*' ? 3 : _a[i].includes(':') ? 2 : _a[i].includes('*') ? 1 : 0 + z = _b[i] === '*' ? 3 : _b[i].includes(':') ? 2 : _b[i].includes('*') ? 1 : 0 res = y - z // If a.length >= b.length if (i === _b.length - 1 && res === 0) { // unless * found sort by level, then alphabetically - res = _a[i] === '*' + res = _a[i].includes('*') ? -1 : ( _a.length === _b.length ? a.path.localeCompare(b.path) : (_a.length - _b.length) @@ -142,7 +142,7 @@ export const sortRoutes = function sortRoutes (routes) { if (res === 0) { // unless * found sort by level, then alphabetically - res = _a[i - 1] === '*' && _b[i] + res = _a[i - 1].includes('*') && _b[i] ? 1 : ( _a.length === _b.length ? a.path.localeCompare(b.path) : (_a.length - _b.length) @@ -248,7 +248,6 @@ const getRoutePathExtension = (key) => { if (key.startsWith('_')) { return `:${key.substr(1)}` } - return key } diff --git a/packages/utils/test/__snapshots__/route.test.js.snap b/packages/utils/test/__snapshots__/route.test.js.snap index 9160477785..a60ee0fabe 100644 --- a/packages/utils/test/__snapshots__/route.test.js.snap +++ b/packages/utils/test/__snapshots__/route.test.js.snap @@ -26,6 +26,12 @@ exports[`util: route util: route create createRoutes should allow snake case rou "name": "parent-child-test", "path": "/parent/child/test", }, + { + "chunkName": "pages/index", + "component": "/some/nuxt/app/pages/index.vue", + "name": "index", + "path": "/", + }, { "chunkName": "pages/another_route/_id", "component": "/some/nuxt/app/pages/another_route/_id.vue", @@ -44,12 +50,6 @@ exports[`util: route util: route create createRoutes should allow snake case rou "name": "parent-all", "path": "/parent/*", }, - { - "chunkName": "pages/index", - "component": "/some/nuxt/app/pages/index.vue", - "name": "index", - "path": "/", - }, { "chunkName": "pages/_param", "component": "/some/nuxt/app/pages/_param.vue", @@ -85,6 +85,12 @@ exports[`util: route util: route create createRoutes should allow snake case rou "name": "parent-child-test", "path": "/parent/child/test", }, + { + "chunkName": "pages/index", + "component": "\\\\\\\\some\\\\nuxt\\\\app\\\\pages\\\\index.vue", + "name": "index", + "path": "/", + }, { "chunkName": "pages/another_route/_id", "component": "\\\\\\\\some\\\\nuxt\\\\app\\\\pages\\\\another_route\\\\_id.vue", @@ -103,12 +109,6 @@ exports[`util: route util: route create createRoutes should allow snake case rou "name": "parent-all", "path": "/parent/*", }, - { - "chunkName": "pages/index", - "component": "\\\\\\\\some\\\\nuxt\\\\app\\\\pages\\\\index.vue", - "name": "index", - "path": "/", - }, { "chunkName": "pages/_param", "component": "\\\\\\\\some\\\\nuxt\\\\app\\\\pages\\\\_param.vue", @@ -156,6 +156,15 @@ exports[`util: route util: route create createRoutes should enforce trailing sla "strict": true, }, }, + { + "chunkName": "pages/index", + "component": "/some/nuxt/app/pages/index.vue", + "name": "index", + "path": "/", + "pathToRegexpOptions": { + "strict": true, + }, + }, { "chunkName": "pages/another_route/_id", "component": "/some/nuxt/app/pages/another_route/_id.vue", @@ -183,15 +192,6 @@ exports[`util: route util: route create createRoutes should enforce trailing sla "strict": true, }, }, - { - "chunkName": "pages/index", - "component": "/some/nuxt/app/pages/index.vue", - "name": "index", - "path": "/", - "pathToRegexpOptions": { - "strict": true, - }, - }, { "chunkName": "pages/_param", "component": "/some/nuxt/app/pages/_param.vue", @@ -242,6 +242,15 @@ exports[`util: route util: route create createRoutes should remove trailing slas "strict": true, }, }, + { + "chunkName": "pages/index", + "component": "/some/nuxt/app/pages/index.vue", + "name": "index", + "path": "/", + "pathToRegexpOptions": { + "strict": true, + }, + }, { "chunkName": "pages/another_route/_id", "component": "/some/nuxt/app/pages/another_route/_id.vue", @@ -269,15 +278,6 @@ exports[`util: route util: route create createRoutes should remove trailing slas "strict": true, }, }, - { - "chunkName": "pages/index", - "component": "/some/nuxt/app/pages/index.vue", - "name": "index", - "path": "/", - "pathToRegexpOptions": { - "strict": true, - }, - }, { "chunkName": "pages/_param", "component": "/some/nuxt/app/pages/_param.vue", @@ -289,3 +289,93 @@ exports[`util: route util: route create createRoutes should remove trailing slas }, ] `; + +exports[`util: route util: route sortRoutes sortRoutes should sort routes 1`] = ` +[ + { + "chunkName": "pages/de/index", + "component": "/some/nuxt/app/pages/de/index.vue", + "name": "de", + "path": "/de", + }, + { + "chunkName": "pages/de_", + "component": "/some/nuxt/app/pages/de_.vue", + "name": "de_", + "path": "/de_", + }, + { + "chunkName": "pages/parent/index", + "component": "/some/nuxt/app/pages/parent/index.vue", + "name": "parent", + "path": "/parent", + }, + { + "chunkName": "pages/snake_case_route", + "component": "/some/nuxt/app/pages/snake_case_route.vue", + "name": "snake_case_route", + "path": "/snake_case_route", + }, + { + "children": [ + { + "chunkName": "pages/another_route/rout_", + "component": "/some/nuxt/app/pages/another_route/rout_.vue", + "name": "another_route-rout_", + "path": "", + }, + ], + "chunkName": "pages/another_route/rout_", + "component": "/some/nuxt/app/pages/another_route/rout_.vue", + "path": "/another_route/rout_", + }, + { + "chunkName": "pages/parent/child/index", + "component": "/some/nuxt/app/pages/parent/child/index.vue", + "name": "parent-child", + "path": "/parent/child", + }, + { + "chunkName": "pages/parent/child/test", + "component": "/some/nuxt/app/pages/parent/child/test.vue", + "name": "parent-child-test", + "path": "/parent/child/test", + }, + { + "chunkName": "pages/index", + "component": "/some/nuxt/app/pages/index.vue", + "name": "index", + "path": "/", + }, + { + "chunkName": "pages/another_route/_id", + "component": "/some/nuxt/app/pages/another_route/_id.vue", + "name": "another_route-id", + "path": "/another_route/:id?", + }, + { + "chunkName": "pages/subpage/_param", + "component": "/some/nuxt/app/pages/subpage/_param.vue", + "name": "subpage-param", + "path": "/subpage/:param?", + }, + { + "chunkName": "pages/parent/_", + "component": "/some/nuxt/app/pages/parent/_.vue", + "name": "parent-all", + "path": "/parent/*", + }, + { + "chunkName": "pages/_param", + "component": "/some/nuxt/app/pages/_param.vue", + "name": "param", + "path": "/:param", + }, + { + "chunkName": "pages/_", + "component": "/some/nuxt/app/pages/_.vue", + "name": "all", + "path": "/*", + }, +] +`; diff --git a/packages/utils/test/route.test.js b/packages/utils/test/route.test.js index 9aee50ff0a..a5a9b95848 100644 --- a/packages/utils/test/route.test.js +++ b/packages/utils/test/route.test.js @@ -1,4 +1,4 @@ -import { flatRoutes, createRoutes, guardDir, promisifyRoute } from '../src/route' +import { flatRoutes, createRoutes, guardDir, promisifyRoute, sortRoutes } from '../src/route' describe('util: route', () => { test('should flat route with path', () => { @@ -238,4 +238,109 @@ describe('util: route', () => { expect(routesResult).toMatchSnapshot() }) }) + + describe('util: route sortRoutes', () => { + const files = [ + 'pages/_param.vue', + 'pages/de/index.vue', + 'pages/index.vue', + 'pages/_.vue', + 'pages/another_route/rout_.vue', + 'pages/snake_case_route.vue', + 'pages/subpage/_param.vue', + 'pages/de_.vue', + 'pages/parent/index.vue', + 'pages/parent/_.vue', + 'pages/another_route/_id.vue', + 'pages/another_route/rout_.vue', + 'pages/parent/child/index.vue', + 'pages/parent/child/test.vue' + ] + const srcDir = '/some/nuxt/app' + const pagesDir = 'pages' + + test.posix('sortRoutes should sort routes', () => { + const routesResult = createRoutes({ files, srcDir, pagesDir }) + expect(routesResult).toMatchSnapshot() + }) + test('Should sortRoutes with extendRoutes using *', () => { + const routes = [ + { path: '/poetry' }, + { path: '/reports' }, + { path: '*' }, + { path: '/de/about' }, + { path: '/' }, + { path: '/about' }, + { path: '/de' }, + { path: '/tech' }, + { path: '/de/tech' }, + { path: '/de*' }, + { path: '/:post' }, + { path: '/de/:post' }, + { path: '/de/reports' }, + { path: '/de/poetry' } + ] + + sortRoutes(routes) + + expect(routes).toEqual( + [ + { path: '/about' }, + { path: '/de' }, + { path: '/poetry' }, + { path: '/reports' }, + { path: '/tech' }, + { path: '/de/about' }, + { path: '/de/poetry' }, + { path: '/de/reports' }, + { path: '/de/tech' }, + { path: '/' }, + { path: '/de/:post' }, + { path: '/de*' }, + { path: '/:post' }, + { path: '*' } + ] + ) + }) + + test('Should sortRoutes with extendRoutes using /*', () => { + const routes = [ + { path: '/poetry' }, + { path: '/reports' }, + { path: '/*' }, + { path: '/de/about' }, + { path: '/about' }, + { path: '/de' }, + { path: '/tech' }, + { path: '/de/tech' }, + { path: '/de/*' }, + { path: '/' }, + { path: '/:post' }, + { path: '/de/:post' }, + { path: '/de/reports' }, + { path: '/de/poetry' } + ] + + sortRoutes(routes) + + expect(routes).toEqual( + [ + { path: '/about' }, + { path: '/de' }, + { path: '/poetry' }, + { path: '/reports' }, + { path: '/tech' }, + { path: '/de/about' }, + { path: '/de/poetry' }, + { path: '/de/reports' }, + { path: '/de/tech' }, + { path: '/' }, + { path: '/de/:post' }, + { path: '/de/*' }, + { path: '/:post' }, + { path: '/*' } + ] + ) + }) + }) }) diff --git a/test/dev/dynamic-routes.test.js b/test/dev/dynamic-routes.test.js index d86d540718..a3d36398bc 100644 --- a/test/dev/dynamic-routes.test.js +++ b/test/dev/dynamic-routes.test.js @@ -54,23 +54,23 @@ describe('dynamic routes', () => { // pages/test/songs/toto.vue expect(routes[5].path).toBe('/test/songs/toto') expect(routes[5].name).toBe('test-songs-toto') - // pages/test/projects/_category.vue - expect(routes[6].path).toBe('/test/projects/:category') - expect(routes[6].name).toBe('test-projects-category') - // pages/test/songs/_id.vue - expect(routes[7].path).toBe('/test/songs/:id?') - expect(routes[7].name).toBe('test-songs-id') - // pages/users/_id.vue - expect(routes[8].path).toBe('/users/:id?') - expect(routes[8].name).toBe('users-id') - // pages/test/_.vue - expect(routes[9].path).toBe('/test/*') - expect(routes[9].name).toBe('test-all') // pages/index.vue - expect(routes[10].path).toBe('/') - expect(routes[10].name).toBe('index') + expect(routes[6].path).toBe('/') + expect(routes[6].name).toBe('index') + // pages/test/projects/_category.vue + expect(routes[7].path).toBe('/test/projects/:category') + expect(routes[7].name).toBe('test-projects-category') + // pages/test/songs/_id.vue + expect(routes[8].path).toBe('/test/songs/:id?') + expect(routes[8].name).toBe('test-songs-id') + // pages/users/_id.vue + expect(routes[9].path).toBe('/users/:id?') + expect(routes[9].name).toBe('users-id') + // pages/test/_.vue + expect(routes[10].path).toBe('/test/*') + expect(routes[10].name).toBe('test-all') // pages/_slug.vue expect(routes[11].path).toBe('/:slug') expect(routes[11].name).toBe('slug')