Skip to content

Commit

Permalink
refactor: refactor stub-components.js (#544)
Browse files Browse the repository at this point in the history
  • Loading branch information
lusarz authored and eddyerburgh committed Apr 24, 2018
1 parent 319ab1b commit db5e07e
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 154 deletions.
15 changes: 7 additions & 8 deletions packages/shared/compile-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
import { compileToFunctions } from 'vue-template-compiler'

export function compileTemplate (component: Component) {
if (component.components) {
Object.keys(component.components).forEach((c) => {
const cmp = component.components[c]
if (!cmp.render) {
compileTemplate(cmp)
}
})
}
Object.keys(component.components || {}).forEach((c) => {
const cmp = component.components[c]
if (!cmp.render) {
compileTemplate(cmp)
}
})

if (component.extends) {
compileTemplate(component.extends)
}
Expand Down
44 changes: 44 additions & 0 deletions packages/shared/stub-components-validate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// @flow

import { throwError } from './util'
import { compileToFunctions } from 'vue-template-compiler'

export function validateStubOptions (stubOptions: Array<string> | Object) {
if (Array.isArray(stubOptions)) {
if (containsNonStringItem(stubOptions)) {
throwError('each item in an options.stubs array must be a string')
}
} else {
if (containsInvalidOptions(stubOptions)) {
throwError('options.stub values must be passed a string or component')
}

if (necessaryCompileToFunctionsMissed(stubOptions)) {
throwError('vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined')
}
}
}

function containsNonStringItem (array: Array<string>): boolean {
return array.some(name => typeof name !== 'string')
}

function necessaryCompileToFunctionsMissed (stubOptions: Object): boolean {
return Object.keys(stubOptions)
.map(key => stubOptions[key])
.some(stub => typeof stub === 'string') && !compileToFunctions
}

function containsInvalidOptions (stubOptions: Object): boolean {
return Object.keys(stubOptions)
.map(key => stubOptions[key])
.some(isInvalidStubOption)
}

function isInvalidStubOption (stub): boolean {
return !['string', 'boolean'].includes(typeof stub) && !isVueComponent(stub)
}

function isVueComponent (cmp) {
return cmp && (cmp.render || cmp.template || cmp.options)
}
183 changes: 44 additions & 139 deletions packages/shared/stub-components.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,12 @@
// @flow

import Vue from 'vue'
import { compileToFunctions } from 'vue-template-compiler'
import { throwError } from './util'
import { componentNeedsCompiling } from './validators'
import { compileTemplate } from './compile-template'
import { capitalize, camelize, hyphenate } from './util'

function isVueComponent (comp) {
return comp && (comp.render || comp.template || comp.options)
}

function isValidStub (stub: any) {
return !!stub &&
typeof stub === 'string' ||
(stub === true) ||
(isVueComponent(stub))
}

function isRequiredComponent (name) {
return name === 'KeepAlive' || name === 'Transition' || name === 'TransitionGroup'
}
import { validateStubOptions } from 'shared/stub-components-validate'
import { componentNeedsCompiling } from 'shared/validators'
import { compileTemplate } from 'shared/compile-template'

function getCoreProperties (component: Component): Object {
if (!component) return {}
return {
attrs: component.attrs,
name: component.name,
Expand All @@ -40,147 +24,68 @@ function getCoreProperties (component: Component): Object {
functional: component.functional
}
}
function createStubFromString (templateString: string, originalComponent: Component): Object {
if (!compileToFunctions) {
throwError('vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined')
}

if (templateString.indexOf(hyphenate(originalComponent.name)) !== -1 ||
templateString.indexOf(capitalize(originalComponent.name)) !== -1 ||
templateString.indexOf(camelize(originalComponent.name)) !== -1) {
throwError('options.stub cannot contain a circular reference')
}

function createStubFromString (originalComponent: Component, template: string): Object {
return {
...getCoreProperties(originalComponent),
...compileToFunctions(templateString)
...compileToFunctions(template)
}
}

function createBlankStub (originalComponent: Component) {
function createBlankStub (originalComponent: Component): Object {
return {
...getCoreProperties(originalComponent),
render: h => h('')
}
}

export function createComponentStubs (originalComponents: Object = {}, stubs: Object): Object {
const components = {}
if (!stubs) {
return components
}
if (Array.isArray(stubs)) {
stubs.forEach(stub => {
if (stub === false) {
return
}
function createStubFromComponent (component: Component, name: string): Object {
if (componentNeedsCompiling(component)) compileTemplate(component)
return name ? { ...component, name } : component
}

if (typeof stub !== 'string') {
throwError('each item in an options.stubs array must be a string')
}
components[stub] = createBlankStub({})
})
function createStub (originalComponent: Component, stubValue): Object {
if (stubValue === true) {
return createBlankStub(originalComponent)
} else if (typeof stubValue === 'string') {
return createStubFromString(originalComponent, stubValue)
} else {
Object.keys(stubs).forEach(stub => {
if (stubs[stub] === false) {
return
}
if (!isValidStub(stubs[stub])) {
throwError('options.stub values must be passed a string or component')
}
if (stubs[stub] === true) {
components[stub] = createBlankStub({})
return
}

if (componentNeedsCompiling(stubs[stub])) {
compileTemplate(stubs[stub])
}

if (originalComponents[stub]) {
// Remove cached constructor
delete originalComponents[stub]._Ctor
if (typeof stubs[stub] === 'string') {
components[stub] = createStubFromString(stubs[stub], originalComponents[stub])
} else {
components[stub] = {
...stubs[stub],
name: originalComponents[stub].name
}
}
} else {
if (typeof stubs[stub] === 'string') {
if (!compileToFunctions) {
throwError('vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined')
}
components[stub] = {
...compileToFunctions(stubs[stub])
}
} else {
components[stub] = {
...stubs[stub]
}
}
}
// ignoreElements does not exist in Vue 2.0.x
if (Vue.config.ignoredElements) {
Vue.config.ignoredElements.push(stub)
}
})
return createStubFromComponent(stubValue, originalComponent && originalComponent.name)
}
return components
}

function stubComponents (components: Object, stubbedComponents: Object) {
Object.keys(components).forEach(component => {
// Remove cached constructor
delete components[component]._Ctor
if (!components[component].name) {
components[component].name = component
}
stubbedComponents[component] = createBlankStub(components[component])

// ignoreElements does not exist in Vue 2.0.x
if (Vue.config.ignoredElements) {
Vue.config.ignoredElements.push(component)
}
})
}

export function createComponentStubsForAll (component: Component): Object {
const stubbedComponents = {}

if (component.components) {
stubComponents(component.components, stubbedComponents)
function normalizeStubOptions (components: ?Object, stubOptions: ?Object | Array<string>): Object {
if (!stubOptions) {
stubOptions = Object.keys(components || {})
}

let extended = component.extends

// Loop through extended component chains to stub all child components
while (extended) {
if (extended.components) {
stubComponents(extended.components, stubbedComponents)
}
extended = extended.extends
if (Array.isArray(stubOptions)) {
stubOptions = stubOptions.reduce((object, name) => {
object[name] = true
return object
}, {})
}
return stubOptions
}

if (component.extendOptions && component.extendOptions.components) {
stubComponents(component.extendOptions.components, stubbedComponents)
}
export function createComponentStubs (components: Object = {}, stubOptions: Object): Object {
validateStubOptions(stubOptions)
return createStubs(components, stubOptions)
}

return stubbedComponents
export function createComponentStubsForAll (component: Component, stubs: Object = {}): Object {
if (!component) return stubs
Object.assign(stubs, createStubs(component.components))
return createComponentStubsForAll(component.extends || component.extendOptions, stubs)
}

export function createComponentStubsForGlobals (instance: Component): Object {
const components = {}
Object.keys(instance.options.components).forEach((c) => {
if (isRequiredComponent(c)) {
return
}
export function createStubs (components: Object, stubOptions: ?Object | Array<string>): Object {
const options: Object = normalizeStubOptions(components, stubOptions)

components[c] = createBlankStub(instance.options.components[c])
delete instance.options.components[c]._Ctor // eslint-disable-line no-param-reassign
delete components[c]._Ctor // eslint-disable-line no-param-reassign
})
return components
return Object.keys(options)
.filter(name => !['KeepAlive', 'Transition', 'TransitionGroup'].includes(name))
.filter(name => options[name] !== false)
.reduce((stubs, name) => {
stubs[name] = createStub(components[name], options[name])
return stubs
}, {})
}
10 changes: 3 additions & 7 deletions packages/test-utils/src/shallow.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import mount from './mount'
import type VueWrapper from './vue-wrapper'
import {
createComponentStubsForAll,
createComponentStubsForGlobals
createStubs
} from 'shared/stub-components'
import { camelize,
capitalize,
Expand All @@ -26,15 +26,11 @@ export default function shallow (
delete component.components[hyphenate(component.name)]
}

const stubbedComponents = createComponentStubsForAll(component)
const stubbedGlobalComponents = createComponentStubsForGlobals(vue)

return mount(component, {
...options,
components: {
// stubbed components are used instead of original components components
...stubbedGlobalComponents,
...stubbedComponents
...createStubs(vue.options.components),
...createComponentStubsForAll(component)
}
})
}

0 comments on commit db5e07e

Please sign in to comment.