diff --git a/src/components/ContentNode/Reference.vue b/src/components/ContentNode/Reference.vue index d9dbe19ae..b959691f9 100644 --- a/src/components/ContentNode/Reference.vue +++ b/src/components/ContentNode/Reference.vue @@ -70,7 +70,7 @@ export default { } = this.$router.resolve(url) || {}; // Resolved internal URLs don't have the "not found" route. - return name !== notFoundRouteName; + return !name.startsWith(notFoundRouteName); }, isSymbolReference() { return this.kind === 'symbol' && !this.hasInlineFormatting diff --git a/src/routes.js b/src/routes.js index 0c928ce37..11343bed5 100644 --- a/src/routes.js +++ b/src/routes.js @@ -16,7 +16,20 @@ import { import ServerError from 'theme/views/ServerError.vue'; import NotFound from 'theme/views/NotFound.vue'; -export default [ +export const fallbackRoutes = [ + { + path: '*', + name: notFoundRouteName, + component: NotFound, + }, + { + path: '*', // purposefully unreachable without a forced navigation + name: serverErrorRouteName, + component: ServerError, + }, +]; + +export const pagesRoutes = [ { path: '/tutorials/:id', name: 'tutorials-overview', @@ -38,14 +51,9 @@ export default [ /* webpackChunkName: "documentation-topic" */ 'theme/views/DocumentationTopic.vue' ), }, - { - path: '*', - name: notFoundRouteName, - component: NotFound, - }, - { - path: '*', // purposefully unreachable without a forced navigation - name: serverErrorRouteName, - component: ServerError, - }, +]; + +export default [ + ...pagesRoutes, + ...fallbackRoutes, ]; diff --git a/src/setup-utils/SwiftDocCRenderRouter.js b/src/setup-utils/SwiftDocCRenderRouter.js index 87ba2a4ac..26a70308d 100644 --- a/src/setup-utils/SwiftDocCRenderRouter.js +++ b/src/setup-utils/SwiftDocCRenderRouter.js @@ -9,25 +9,18 @@ */ import Router from 'vue-router'; -import { - notFoundRouteName, - serverErrorRouteName, -} from 'docc-render/constants/router'; import { saveScrollOnReload, restoreScrollOnReload, scrollBehavior, } from 'docc-render/utils/router-utils'; -import routes from 'docc-render/routes'; +import routes, { fallbackRoutes } from 'docc-render/routes'; import { baseUrl } from 'docc-render/utils/theme-settings'; import { addPrefixedRoutes } from 'docc-render/utils/route-utils'; const defaultRoutes = [ - ...routes, - ...addPrefixedRoutes(routes, [ - notFoundRouteName, - serverErrorRouteName, - ]), + ...addPrefixedRoutes(routes), + ...fallbackRoutes, ]; export default function createRouterInstance(routerConfig = {}) { diff --git a/src/utils/route-utils.js b/src/utils/route-utils.js index f543ca810..1ca94e09f 100644 --- a/src/utils/route-utils.js +++ b/src/utils/route-utils.js @@ -7,6 +7,7 @@ * See https://swift.org/LICENSE.txt for license information * See https://swift.org/CONTRIBUTORS.txt for Swift project authors */ +import { pathJoin } from 'docc-render/utils/assets'; const localEnvs = [ { pathPrefix: '/:locale?', nameSuffix: '-locale' }, @@ -26,7 +27,7 @@ export function addPrefixedRoutes(routes, skipRoutes = [], envs = localEnvs) { .filter(route => !skipRoutes.includes(route.name)) .map(route => ({ ...route, - path: current.pathPrefix + route.path, + path: pathJoin([current.pathPrefix, route.path]), name: route.name + current.nameSuffix, })), ), []); diff --git a/tests/unit/setup-utils/SwiftDocCRenderRouter.spec.js b/tests/unit/setup-utils/SwiftDocCRenderRouter.spec.js index 5d6df4b34..ecbc0abf1 100644 --- a/tests/unit/setup-utils/SwiftDocCRenderRouter.spec.js +++ b/tests/unit/setup-utils/SwiftDocCRenderRouter.spec.js @@ -118,8 +118,8 @@ describe('SwiftDocCRenderRouter', () => { const resolve = path => router.resolve(path).route; - it('resolves paths to the "tutorials-overview" route', () => { - const route = 'tutorials-overview'; + it('resolves paths to the "tutorials-overview-locale" route', () => { + const route = 'tutorials-overview-locale'; expect(resolve('/tutorials/foo').name).toBe(route); expect(resolve('/tutorials/bar').name).toBe(route); @@ -140,8 +140,8 @@ describe('SwiftDocCRenderRouter', () => { expect(resolve('/zh-CN/tutorials/foo/bar').name).not.toBe(route); }); - it('resolves paths to the "topic" route', () => { - const route = 'topic'; + it('resolves paths to the "topic-locale" route', () => { + const route = 'topic-locale'; expect(resolve('/tutorials/foo/bar').name).toBe(route); expect(resolve('/tutorials/foobar/baz').name).toBe(route); expect(resolve('/tutorials/documentation/foo').name).toBe(route); @@ -163,8 +163,8 @@ describe('SwiftDocCRenderRouter', () => { }); }); - it('resolves paths to the "documentation-topic" route', () => { - const route = 'documentation-topic'; + it('resolves paths to the "documentation-topic-locale" route', () => { + const route = 'documentation-topic-locale'; expect(resolve('/documentation/foo').name).toBe(route); expect(resolve('/documentation/bar').name).toBe(route); diff --git a/tests/unit/utils/route-utils.spec.js b/tests/unit/utils/route-utils.spec.js index f5ac2a011..343f3e4f8 100644 --- a/tests/unit/utils/route-utils.spec.js +++ b/tests/unit/utils/route-utils.spec.js @@ -50,4 +50,49 @@ describe('route-utils', () => { { meta: { b: 'b' }, name: 'b-foo', path: '/foo/bar/path/to/b' }, ]); }); + + it('handles routes with root path', () => { + const rootRoutes = [ + { name: 'home', path: '/', meta: { isHome: true } }, + ]; + + const result = addPrefixedRoutes(rootRoutes); + expect(result[0].path).toBe('/:locale?/'); + }); + + it('handles routes with trailing slashes', () => { + const trailingSlashRoutes = [ + { name: 'trailing', path: '/path/with/trailing/', meta: {} }, + ]; + + const result = addPrefixedRoutes(trailingSlashRoutes); + expect(result[0].path).toBe('/:locale?/path/with/trailing/'); + }); + + it('handles routes without trailing slashes', () => { + const trailingSlashRoutes = [ + { name: 'trailing', path: 'path/with/trailing', meta: {} }, + ]; + + const result = addPrefixedRoutes(trailingSlashRoutes); + expect(result[0].path).toBe('/:locale?/path/with/trailing'); + }); + + it('handles pathPrefix with trailing slash', () => { + const envWithTrailingSlash = [ + { pathPrefix: '/api/', nameSuffix: '-api' }, + ]; + + const result = addPrefixedRoutes([routes[0]], [], envWithTrailingSlash); + expect(result[0].path).toBe('/api/path/to/a'); + }); + + it('handles pathPrefix without leading slash', () => { + const envWithoutLeadingSlash = [ + { pathPrefix: 'api', nameSuffix: '-api' }, + ]; + + const result = addPrefixedRoutes([routes[0]], [], envWithoutLeadingSlash); + expect(result[0].path).toBe('api/path/to/a'); + }); });