From: Eduardo San Martin Morote Date: Mon, 1 Jul 2019 12:26:27 +0000 (+0200) Subject: refactor: matched components always contain a `components` object X-Git-Tag: v4.0.0-alpha.0~322 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=51782ca4ce03e62bfd5c754c91667770139e668f;p=thirdparty%2Fvuejs%2Frouter.git refactor: matched components always contain a `components` object --- diff --git a/__tests__/extractComponentsGuards.spec.js b/__tests__/extractComponentsGuards.spec.js index 0cc9863b..3b1663e6 100644 --- a/__tests__/extractComponentsGuards.spec.js +++ b/__tests__/extractComponentsGuards.spec.js @@ -1,6 +1,7 @@ // @ts-check require('./helper') const expect = require('expect') +const { normalizeRecord } = require('../src/matcher') const { extractComponentsGuards } = require('../src/utils') const { START_LOCATION_NORMALIZED } = require('../src/types') const { components } = require('./utils') @@ -66,12 +67,14 @@ beforeEach(() => { /** * - * @param {MatchedRouteRecord[]} components + * @param {Exclude[]} components */ async function checkGuards(components, n) { beforeRouteEnter.mockClear() + const ncomps = components.map(normalizeRecord) const guards = await extractComponentsGuards( - components, + // type is fine as we excluded RouteRecordRedirect in components argument + components.map(normalizeRecord), 'beforeRouteEnter', to, from diff --git a/__tests__/matcher.spec.js b/__tests__/matcher.spec.js index b0453cb6..d33afbc3 100644 --- a/__tests__/matcher.spec.js +++ b/__tests__/matcher.spec.js @@ -17,7 +17,7 @@ describe('Router Matcher', () => { * * @param {RouteRecord | RouteRecord[]} record Record or records we are testing the matcher against * @param {MatcherLocation} location location we want to reolve against - * @param {Partial} resolved Expected resolved location given by the matcher + * @param {Partial} resolved Expected resolved location given by the matcher * @param {MatcherLocationNormalized} [start] Optional currentLocation used when resolving */ function assertRecordMatch( @@ -49,6 +49,15 @@ describe('Router Matcher', () => { resolved.params = resolved.params || {} } + for (const matched of resolved.matched) { + if ('component' in matched) { + // @ts-ignore + matched.components = { default: matched.component } + // @ts-ignore + delete matched.component + } + } + const result = matcher.resolve( { ...targetLocation, @@ -646,9 +655,7 @@ describe('Router Matcher', () => { }, { ...NestedChildWithParam, - path: `${Foo.path}/${NestedWithParam.path}/${ - NestedChildWithParam.path - }`, + path: `${Foo.path}/${NestedWithParam.path}/${NestedChildWithParam.path}`, }, ], } @@ -677,9 +684,7 @@ describe('Router Matcher', () => { }, { ...NestedChildWithParam, - path: `${Foo.path}/${NestedWithParam.path}/${ - NestedChildWithParam.path - }`, + path: `${Foo.path}/${NestedWithParam.path}/${NestedChildWithParam.path}`, }, ], } diff --git a/__tests__/router-view.spec.js b/__tests__/router-view.spec.js index 075436b5..3b28aed3 100644 --- a/__tests__/router-view.spec.js +++ b/__tests__/router-view.spec.js @@ -3,32 +3,39 @@ * */ // @ts-check -require("./helper"); -const expect = require("expect"); -const { default: RouterView } = require("../src/components/View"); -const { components, isMocha } = require("./utils"); +require('./helper') +const expect = require('expect') +const { default: RouterView } = require('../src/components/View') +const { components, isMocha } = require('./utils') -describe("RouterView", () => { +/** @typedef {import('../src/types').RouteLocationNormalized} RouteLocationNormalized */ + +/** @type {Record} */ +const routes = { + root: { + fullPath: '/', + name: undefined, + path: '/', + query: {}, + params: {}, + hash: '', + meta: {}, + matched: [{ components: { default: components.Home }, path: '/' }], + }, +} + +describe('RouterView', () => { // skip these tests on mocha because @vue/test-utils // do not work correctly - if (isMocha()) return; - const { mount } = require("@vue/test-utils"); + if (isMocha()) return + const { mount } = require('@vue/test-utils') - it("displays current route component", async () => { + it('displays current route component', async () => { const wrapper = await mount(RouterView, { mocks: { - $route: { - fullPath: "/", - name: undefined, - path: "/", - query: {}, - params: {}, - hash: "", - meta: {}, - matched: [{ component: components.Home, path: "/", name: undefined }] - } - } - }); - expect(wrapper.html()).toMatchInlineSnapshot(`"
Home
"`); - }); -}); + $route: routes.root, + }, + }) + expect(wrapper.html()).toMatchInlineSnapshot(`"
Home
"`) + }) +}) diff --git a/src/components/View.ts b/src/components/View.ts index db04a5a7..ee887ec8 100644 --- a/src/components/View.ts +++ b/src/components/View.ts @@ -6,7 +6,14 @@ const View: Component = { name: 'RouterView', functional: true, - render(_, { children, parent, data }) { + props: { + name: { + type: String, + default: 'default', + }, + }, + + render(_, { children, parent, data, props }) { // used by devtools to display a router-view badge // @ts-ignore data.routerView = true @@ -23,7 +30,7 @@ const View: Component = { // render empty node if no matched route if (!matched) return h() - const component = matched.component + const component = matched.components[props.name] return h(component, data, children) }, diff --git a/src/matcher.ts b/src/matcher.ts index e8944a3b..8ce1a603 100644 --- a/src/matcher.ts +++ b/src/matcher.ts @@ -5,19 +5,42 @@ import { MatcherLocation, MatcherLocationNormalized, MatcherLocationRedirect, + // TODO: add it to matched + // MatchedRouteRecord, } from './types/index' import { NoRouteMatchError, InvalidRouteMatch } from './errors' +type NormalizedRouteRecord = Exclude // normalize component/components into components + interface RouteMatcher { re: RegExp resolve: (params?: RouteParams) => string - record: RouteRecord // TODO: NormalizedRouteRecord? + record: NormalizedRouteRecord parent: RouteMatcher | void // TODO: children so they can be removed // children: RouteMatcher[] keys: string[] } +/** + * Normalizes a RouteRecord into a MatchedRouteRecord. Creates a copy + * @param record + * @returns the normalized version + */ +export function normalizeRecord( + record: Readonly +): NormalizedRouteRecord { + if ('component' in record) { + const { component, ...rest } = record + // @ts-ignore + rest.components = { default: component } + return rest as NormalizedRouteRecord + } + + // otherwise just create a copy + return { ...record } +} + export class RouterMatcher { private matchers: RouteMatcher[] = [] @@ -34,7 +57,8 @@ export class RouterMatcher { const keys: pathToRegexp.Key[] = [] const options: pathToRegexp.RegExpOptions = {} - const recordCopy = { ...record } + const recordCopy = normalizeRecord(record) + if (parent) { // if the child isn't an absolute route if (record.path[0] !== '/') { @@ -108,9 +132,7 @@ export class RouterMatcher { const value = result[i + 1] if (!value) { throw new Error( - `Error parsing path "${ - location.path - }" when looking for param "${key}"` + `Error parsing path "${location.path}" when looking for param "${key}"` ) } params[key] = value diff --git a/src/types/index.ts b/src/types/index.ts index 2a002848..fd36a445 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -37,8 +37,12 @@ export type RouteLocation = | RouteQueryAndHash & LocationAsName & RouteLocationOptions | RouteQueryAndHash & LocationAsRelative & RouteLocationOptions -// exposed to the user in a very consistant way -export type MatchedRouteRecord = Exclude +// A matched record cannot be a redirection and must contain +// a normalized version of components with { default: Component } instead of `component` +export type MatchedRouteRecord = Exclude< + RouteRecord, + RouteRecordRedirect | RouteRecordSingleView +> export interface RouteLocationNormalized extends Required { @@ -114,6 +118,8 @@ interface RouteRecordSingleView extends RouteRecordCommon { interface RouteRecordMultipleViews extends RouteRecordCommon { components: Record> + // TODO: add tests + children?: RouteRecord[] } export type RouteRecord = diff --git a/src/utils/index.ts b/src/utils/index.ts index 48974b39..91aa3c50 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -14,8 +14,8 @@ export async function extractComponentsGuards( await Promise.all( matched.map(async record => { // TODO: cache async routes per record - if ('component' in record) { - const { component } = record + for (const name in record.components) { + const component = record.components[name] const resolvedComponent = await (typeof component === 'function' ? component() : component) @@ -24,18 +24,6 @@ export async function extractComponentsGuards( if (guard) { guards.push(guardToPromiseFn(guard, to, from)) } - } else { - for (const name in record.components) { - const component = record.components[name] - const resolvedComponent = await (typeof component === 'function' - ? component() - : component) - - const guard = resolvedComponent[guardType] - if (guard) { - guards.push(guardToPromiseFn(guard, to, from)) - } - } } }) )