Skip to content

Commit db5e07e

Browse files
lusarzeddyerburgh
authored andcommitted
refactor: refactor stub-components.js (#544)
1 parent 319ab1b commit db5e07e

File tree

4 files changed

+98
-154
lines changed

4 files changed

+98
-154
lines changed

packages/shared/compile-template.js

+7-8
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
import { compileToFunctions } from 'vue-template-compiler'
44

55
export function compileTemplate (component: Component) {
6-
if (component.components) {
7-
Object.keys(component.components).forEach((c) => {
8-
const cmp = component.components[c]
9-
if (!cmp.render) {
10-
compileTemplate(cmp)
11-
}
12-
})
13-
}
6+
Object.keys(component.components || {}).forEach((c) => {
7+
const cmp = component.components[c]
8+
if (!cmp.render) {
9+
compileTemplate(cmp)
10+
}
11+
})
12+
1413
if (component.extends) {
1514
compileTemplate(component.extends)
1615
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// @flow
2+
3+
import { throwError } from './util'
4+
import { compileToFunctions } from 'vue-template-compiler'
5+
6+
export function validateStubOptions (stubOptions: Array<string> | Object) {
7+
if (Array.isArray(stubOptions)) {
8+
if (containsNonStringItem(stubOptions)) {
9+
throwError('each item in an options.stubs array must be a string')
10+
}
11+
} else {
12+
if (containsInvalidOptions(stubOptions)) {
13+
throwError('options.stub values must be passed a string or component')
14+
}
15+
16+
if (necessaryCompileToFunctionsMissed(stubOptions)) {
17+
throwError('vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined')
18+
}
19+
}
20+
}
21+
22+
function containsNonStringItem (array: Array<string>): boolean {
23+
return array.some(name => typeof name !== 'string')
24+
}
25+
26+
function necessaryCompileToFunctionsMissed (stubOptions: Object): boolean {
27+
return Object.keys(stubOptions)
28+
.map(key => stubOptions[key])
29+
.some(stub => typeof stub === 'string') && !compileToFunctions
30+
}
31+
32+
function containsInvalidOptions (stubOptions: Object): boolean {
33+
return Object.keys(stubOptions)
34+
.map(key => stubOptions[key])
35+
.some(isInvalidStubOption)
36+
}
37+
38+
function isInvalidStubOption (stub): boolean {
39+
return !['string', 'boolean'].includes(typeof stub) && !isVueComponent(stub)
40+
}
41+
42+
function isVueComponent (cmp) {
43+
return cmp && (cmp.render || cmp.template || cmp.options)
44+
}

packages/shared/stub-components.js

+44-139
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,12 @@
11
// @flow
22

3-
import Vue from 'vue'
43
import { compileToFunctions } from 'vue-template-compiler'
5-
import { throwError } from './util'
6-
import { componentNeedsCompiling } from './validators'
7-
import { compileTemplate } from './compile-template'
8-
import { capitalize, camelize, hyphenate } from './util'
9-
10-
function isVueComponent (comp) {
11-
return comp && (comp.render || comp.template || comp.options)
12-
}
13-
14-
function isValidStub (stub: any) {
15-
return !!stub &&
16-
typeof stub === 'string' ||
17-
(stub === true) ||
18-
(isVueComponent(stub))
19-
}
20-
21-
function isRequiredComponent (name) {
22-
return name === 'KeepAlive' || name === 'Transition' || name === 'TransitionGroup'
23-
}
4+
import { validateStubOptions } from 'shared/stub-components-validate'
5+
import { componentNeedsCompiling } from 'shared/validators'
6+
import { compileTemplate } from 'shared/compile-template'
247

258
function getCoreProperties (component: Component): Object {
9+
if (!component) return {}
2610
return {
2711
attrs: component.attrs,
2812
name: component.name,
@@ -40,147 +24,68 @@ function getCoreProperties (component: Component): Object {
4024
functional: component.functional
4125
}
4226
}
43-
function createStubFromString (templateString: string, originalComponent: Component): Object {
44-
if (!compileToFunctions) {
45-
throwError('vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined')
46-
}
47-
48-
if (templateString.indexOf(hyphenate(originalComponent.name)) !== -1 ||
49-
templateString.indexOf(capitalize(originalComponent.name)) !== -1 ||
50-
templateString.indexOf(camelize(originalComponent.name)) !== -1) {
51-
throwError('options.stub cannot contain a circular reference')
52-
}
5327

28+
function createStubFromString (originalComponent: Component, template: string): Object {
5429
return {
5530
...getCoreProperties(originalComponent),
56-
...compileToFunctions(templateString)
31+
...compileToFunctions(template)
5732
}
5833
}
5934

60-
function createBlankStub (originalComponent: Component) {
35+
function createBlankStub (originalComponent: Component): Object {
6136
return {
6237
...getCoreProperties(originalComponent),
6338
render: h => h('')
6439
}
6540
}
6641

67-
export function createComponentStubs (originalComponents: Object = {}, stubs: Object): Object {
68-
const components = {}
69-
if (!stubs) {
70-
return components
71-
}
72-
if (Array.isArray(stubs)) {
73-
stubs.forEach(stub => {
74-
if (stub === false) {
75-
return
76-
}
42+
function createStubFromComponent (component: Component, name: string): Object {
43+
if (componentNeedsCompiling(component)) compileTemplate(component)
44+
return name ? { ...component, name } : component
45+
}
7746

78-
if (typeof stub !== 'string') {
79-
throwError('each item in an options.stubs array must be a string')
80-
}
81-
components[stub] = createBlankStub({})
82-
})
47+
function createStub (originalComponent: Component, stubValue): Object {
48+
if (stubValue === true) {
49+
return createBlankStub(originalComponent)
50+
} else if (typeof stubValue === 'string') {
51+
return createStubFromString(originalComponent, stubValue)
8352
} else {
84-
Object.keys(stubs).forEach(stub => {
85-
if (stubs[stub] === false) {
86-
return
87-
}
88-
if (!isValidStub(stubs[stub])) {
89-
throwError('options.stub values must be passed a string or component')
90-
}
91-
if (stubs[stub] === true) {
92-
components[stub] = createBlankStub({})
93-
return
94-
}
95-
96-
if (componentNeedsCompiling(stubs[stub])) {
97-
compileTemplate(stubs[stub])
98-
}
99-
100-
if (originalComponents[stub]) {
101-
// Remove cached constructor
102-
delete originalComponents[stub]._Ctor
103-
if (typeof stubs[stub] === 'string') {
104-
components[stub] = createStubFromString(stubs[stub], originalComponents[stub])
105-
} else {
106-
components[stub] = {
107-
...stubs[stub],
108-
name: originalComponents[stub].name
109-
}
110-
}
111-
} else {
112-
if (typeof stubs[stub] === 'string') {
113-
if (!compileToFunctions) {
114-
throwError('vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined')
115-
}
116-
components[stub] = {
117-
...compileToFunctions(stubs[stub])
118-
}
119-
} else {
120-
components[stub] = {
121-
...stubs[stub]
122-
}
123-
}
124-
}
125-
// ignoreElements does not exist in Vue 2.0.x
126-
if (Vue.config.ignoredElements) {
127-
Vue.config.ignoredElements.push(stub)
128-
}
129-
})
53+
return createStubFromComponent(stubValue, originalComponent && originalComponent.name)
13054
}
131-
return components
132-
}
133-
134-
function stubComponents (components: Object, stubbedComponents: Object) {
135-
Object.keys(components).forEach(component => {
136-
// Remove cached constructor
137-
delete components[component]._Ctor
138-
if (!components[component].name) {
139-
components[component].name = component
140-
}
141-
stubbedComponents[component] = createBlankStub(components[component])
142-
143-
// ignoreElements does not exist in Vue 2.0.x
144-
if (Vue.config.ignoredElements) {
145-
Vue.config.ignoredElements.push(component)
146-
}
147-
})
14855
}
14956

150-
export function createComponentStubsForAll (component: Component): Object {
151-
const stubbedComponents = {}
152-
153-
if (component.components) {
154-
stubComponents(component.components, stubbedComponents)
57+
function normalizeStubOptions (components: ?Object, stubOptions: ?Object | Array<string>): Object {
58+
if (!stubOptions) {
59+
stubOptions = Object.keys(components || {})
15560
}
156-
157-
let extended = component.extends
158-
159-
// Loop through extended component chains to stub all child components
160-
while (extended) {
161-
if (extended.components) {
162-
stubComponents(extended.components, stubbedComponents)
163-
}
164-
extended = extended.extends
61+
if (Array.isArray(stubOptions)) {
62+
stubOptions = stubOptions.reduce((object, name) => {
63+
object[name] = true
64+
return object
65+
}, {})
16566
}
67+
return stubOptions
68+
}
16669

167-
if (component.extendOptions && component.extendOptions.components) {
168-
stubComponents(component.extendOptions.components, stubbedComponents)
169-
}
70+
export function createComponentStubs (components: Object = {}, stubOptions: Object): Object {
71+
validateStubOptions(stubOptions)
72+
return createStubs(components, stubOptions)
73+
}
17074

171-
return stubbedComponents
75+
export function createComponentStubsForAll (component: Component, stubs: Object = {}): Object {
76+
if (!component) return stubs
77+
Object.assign(stubs, createStubs(component.components))
78+
return createComponentStubsForAll(component.extends || component.extendOptions, stubs)
17279
}
17380

174-
export function createComponentStubsForGlobals (instance: Component): Object {
175-
const components = {}
176-
Object.keys(instance.options.components).forEach((c) => {
177-
if (isRequiredComponent(c)) {
178-
return
179-
}
81+
export function createStubs (components: Object, stubOptions: ?Object | Array<string>): Object {
82+
const options: Object = normalizeStubOptions(components, stubOptions)
18083

181-
components[c] = createBlankStub(instance.options.components[c])
182-
delete instance.options.components[c]._Ctor // eslint-disable-line no-param-reassign
183-
delete components[c]._Ctor // eslint-disable-line no-param-reassign
184-
})
185-
return components
84+
return Object.keys(options)
85+
.filter(name => !['KeepAlive', 'Transition', 'TransitionGroup'].includes(name))
86+
.filter(name => options[name] !== false)
87+
.reduce((stubs, name) => {
88+
stubs[name] = createStub(components[name], options[name])
89+
return stubs
90+
}, {})
18691
}

packages/test-utils/src/shallow.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import mount from './mount'
66
import type VueWrapper from './vue-wrapper'
77
import {
88
createComponentStubsForAll,
9-
createComponentStubsForGlobals
9+
createStubs
1010
} from 'shared/stub-components'
1111
import { camelize,
1212
capitalize,
@@ -26,15 +26,11 @@ export default function shallow (
2626
delete component.components[hyphenate(component.name)]
2727
}
2828

29-
const stubbedComponents = createComponentStubsForAll(component)
30-
const stubbedGlobalComponents = createComponentStubsForGlobals(vue)
31-
3229
return mount(component, {
3330
...options,
3431
components: {
35-
// stubbed components are used instead of original components components
36-
...stubbedGlobalComponents,
37-
...stubbedComponents
32+
...createStubs(vue.options.components),
33+
...createComponentStubsForAll(component)
3834
}
3935
})
4036
}

0 commit comments

Comments
 (0)