Skip to content

Commit

Permalink
refactor: extract validation logic (#1108)
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyerburgh authored Jan 20, 2019
1 parent b461900 commit 1f160df
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 162 deletions.
51 changes: 0 additions & 51 deletions packages/create-instance/create-functional-component.js

This file was deleted.

132 changes: 40 additions & 92 deletions packages/create-instance/create-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,58 @@ import { createSlotVNodes } from './create-slot-vnodes'
import addMocks from './add-mocks'
import { addEventLogger } from './log-events'
import { addStubs } from './add-stubs'
import { throwError } from 'shared/util'
import { VUE_VERSION } from 'shared/consts'
import {
compileTemplate,
compileTemplateForSlots
} from 'shared/compile-template'
import { compileTemplate } from 'shared/compile-template'
import extractInstanceOptions from './extract-instance-options'
import createFunctionalComponent from './create-functional-component'
import { componentNeedsCompiling, isPlainObject } from 'shared/validators'
import { validateSlots } from './validate-slots'
import {
componentNeedsCompiling,
isConstructor
} from 'shared/validators'
import createScopedSlots from './create-scoped-slots'
import { createStubsFromStubsObject } from './create-component-stubs'
import { patchCreateElement } from './patch-create-element'
import { isConstructor } from 'shared/validators'

function vueExtendUnsupportedOption (option: string) {
return `options.${option} is not supported for ` +
`components created with Vue.extend in Vue < 2.3. ` +
`You can set ${option} to false to mount the component.`
function createContext (options, scopedSlots) {
const on = {
...(options.context && options.context.on),
...options.listeners
}
return {
attrs: {
...options.attrs,
// pass as attrs so that inheritAttrs works correctly
// propsData should take precedence over attrs
...options.propsData
},
...(options.context || {}),
on,
scopedSlots
}
}

// these options aren't supported if Vue is version < 2.3
// for components using Vue.extend. This is due to a bug
// that means the mixins we use to add properties are not applied
// correctly
const UNSUPPORTED_VERSION_OPTIONS = [
'mocks',
'stubs',
'localVue'
]
function createChildren (vm, h, { slots, context }) {
const slotVNodes = slots
? createSlotVNodes(vm, slots)
: undefined
return (
context &&
context.children &&
context.children.map(x => (typeof x === 'function' ? x(h) : x))
) || slotVNodes
}

export default function createInstance (
component: Component,
options: Options,
_Vue: Component
): Component {
if (
VUE_VERSION < 2.3 && isConstructor(component)
) {
UNSUPPORTED_VERSION_OPTIONS.forEach((option) => {
if (options[option]) {
throwError(vueExtendUnsupportedOption(option))
}
})
}

let componentOptions = isConstructor(component)
const componentOptions = isConstructor(component)
? component.options
: component

// instance options are options that are passed to the
// root instance when it's instantiated
const instanceOptions = extractInstanceOptions(options)

const stubComponentsObject = createStubsFromStubsObject(
componentOptions.components,
// $FlowIgnore
Expand All @@ -69,81 +68,30 @@ export default function createInstance (
addStubs(_Vue, stubComponentsObject)
patchCreateElement(_Vue, stubComponentsObject, options.shouldProxy)

if (componentOptions.functional) {
componentOptions = createFunctionalComponent(
componentOptions,
options,
_Vue
)
} else if (options.context) {
throwError(
`mount.context can only be used when mounting a ` +
`functional component`
)
}

if (componentNeedsCompiling(componentOptions)) {
compileTemplate(componentOptions)
}

// used to identify extended component using constructor
componentOptions.$_vueTestUtils_original = component

// make sure all extends are based on this instance
componentOptions._base = _Vue

const Constructor = _Vue.extend(componentOptions).extend(instanceOptions)

// used to identify extended component using constructor
Constructor.options.$_vueTestUtils_original = component

if (options.slots) {
compileTemplateForSlots(options.slots)
// validate slots outside of the createSlots function so
// that we can throw an error without it being caught by
// the Vue error handler
// $FlowIgnore
validateSlots(options.slots)
}

// Objects are not resolved in extended components in Vue < 2.5
// https://github.com/vuejs/vue/issues/6436
if (
options.provide &&
typeof options.provide === 'object' &&
VUE_VERSION < 2.5
) {
const obj = { ...options.provide }
options.provide = () => obj
}

const scopedSlots = createScopedSlots(options.scopedSlots, _Vue)

if (options.parentComponent && !isPlainObject(options.parentComponent)) {
throwError(
`options.parentComponent should be a valid Vue component options object`
)
}

const parentComponentOptions = options.parentComponent || {}

parentComponentOptions.provide = options.provide
parentComponentOptions.$_doNotStubChildren = true

parentComponentOptions._isFunctionalContainer = componentOptions.functional
parentComponentOptions.render = function (h) {
const slots = options.slots
? createSlotVNodes(this, options.slots)
: undefined
return h(
Constructor,
{
ref: 'vm',
on: options.listeners,
attrs: {
...options.attrs,
// pass as attrs so that inheritAttrs works correctly
// propsData should take precedence over attrs
...options.propsData
},
scopedSlots
},
slots
createContext(options, scopedSlots),
createChildren(this, h, options)
)
}
const Parent = _Vue.extend(parentComponentOptions)
Expand Down
6 changes: 5 additions & 1 deletion packages/server-test-utils/src/renderToString.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createRenderer } from 'vue-server-renderer'
import { mergeOptions } from 'shared/merge-options'
import config from './config'
import testUtils from '@vue/test-utils'
import { validateOptions } from 'shared/validate-options'

Vue.config.productionTip = false
Vue.config.devtools = false
Expand All @@ -27,9 +28,12 @@ export default function renderToString (
throwError(`you cannot use attachToDocument with ` + `renderToString`)
}

const mergedOptions = mergeOptions(options, config)
validateOptions(mergedOptions, component)

const vm = createInstance(
component,
mergeOptions(options, config),
mergedOptions,
testUtils.createLocalVue(options.localVue)
)
let renderedString = ''
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/merge-options.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @flow
import { normalizeStubs } from './normalize'
import { normalizeStubs, normalizeProvide } from './normalize'

function getOption (option, config?: Object): any {
if (option === false) {
Expand All @@ -26,11 +26,11 @@ export function mergeOptions (options: Options, config: Config): Options {
const provide = ((getOption(options.provide, config.provide)): Object)
return {
...options,
provide: normalizeProvide(provide),
logModifiedComponents: config.logModifiedComponents,
stubs: getOption(normalizeStubs(options.stubs), config.stubs),
mocks,
methods,
provide,
sync: !!(options.sync || options.sync === undefined)
}
}
14 changes: 14 additions & 0 deletions packages/shared/normalize.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { isPlainObject } from './validators'
import { throwError } from './util'
import { VUE_VERSION } from './consts'

export function normalizeStubs (stubs = {}) {
if (stubs === false) {
Expand All @@ -19,3 +20,16 @@ export function normalizeStubs (stubs = {}) {
}
throwError('options.stubs must be an object or an Array')
}

export function normalizeProvide (provide) {
// Objects are not resolved in extended components in Vue < 2.5
// https://github.com/vuejs/vue/issues/6436
if (
typeof provide === 'object' &&
VUE_VERSION < 2.5
) {
const obj = { ...provide }
return () => obj
}
return provide
}
61 changes: 61 additions & 0 deletions packages/shared/validate-options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
isPlainObject,
isFunctionalComponent,
isConstructor
} from './validators'
import { VUE_VERSION } from './consts'
import { compileTemplateForSlots } from './compile-template'
import { throwError } from './util'
import { validateSlots } from './validate-slots'

function vueExtendUnsupportedOption (option) {
return `options.${option} is not supported for ` +
`components created with Vue.extend in Vue < 2.3. ` +
`You can set ${option} to false to mount the component.`
}
// these options aren't supported if Vue is version < 2.3
// for components using Vue.extend. This is due to a bug
// that means the mixins we use to add properties are not applied
// correctly
const UNSUPPORTED_VERSION_OPTIONS = [
'mocks',
'stubs',
'localVue'
]

export function validateOptions (options, component) {
if (options.parentComponent && !isPlainObject(options.parentComponent)) {
throwError(
`options.parentComponent should be a valid Vue component options object`
)
}

if (!isFunctionalComponent(component) && options.context) {
throwError(
`mount.context can only be used when mounting a functional component`
)
}

if (options.context && !isPlainObject(options.context)) {
throwError('mount.context must be an object')
}

if (
VUE_VERSION < 2.3 && isConstructor(component)
) {
UNSUPPORTED_VERSION_OPTIONS.forEach((option) => {
if (options[option]) {
throwError(vueExtendUnsupportedOption(option))
}
})
}

if (options.slots) {
compileTemplateForSlots(options.slots)
// validate slots outside of the createSlots function so
// that we can throw an error without it being caught by
// the Vue error handler
// $FlowIgnore
validateSlots(options.slots)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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

function isValidSlot (slot: any): boolean {
return (
Expand Down
10 changes: 10 additions & 0 deletions packages/shared/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ export function isComponentOptions (c: any) {
return typeof c === 'object' && (c.template || c.render)
}

export function isFunctionalComponent (c: any) {
if (!isVueComponent(c)) {
return false
}
if (isConstructor(c)) {
return c.options.functional
}
return c.functional
}

export function templateContainsComponent (
template: string,
name: string
Expand Down
Loading

0 comments on commit 1f160df

Please sign in to comment.