refactor: option merging + extract helper functions

This commit is contained in:
Evan You 2018-10-16 15:47:51 -04:00
parent 7bc28a6e61
commit 149d82d618
32 changed files with 412 additions and 295 deletions

View File

@ -3,7 +3,6 @@ import {
Component, Component,
render, render,
nodeOps, nodeOps,
ComponentInstance,
observable, observable,
nextTick nextTick
} from '@vue/renderer-test' } from '@vue/renderer-test'
@ -41,7 +40,7 @@ describe('Parent chain management', () => {
} }
const root = nodeOps.createElement('div') const root = nodeOps.createElement('div')
const parent = render(h(Parent), root) as ComponentInstance const parent = render(h(Parent), root) as Component
expect(child.$parent).toBe(parent) expect(child.$parent).toBe(parent)
expect(child.$root).toBe(parent) expect(child.$root).toBe(parent)
@ -100,7 +99,7 @@ describe('Parent chain management', () => {
} }
const root = nodeOps.createElement('div') const root = nodeOps.createElement('div')
const parent = render(h(Parent), root) as ComponentInstance const parent = render(h(Parent), root) as Component
expect(child.$parent).toBe(parent) expect(child.$parent).toBe(parent)
expect(child.$root).toBe(parent) expect(child.$root).toBe(parent)

View File

@ -1,4 +1,4 @@
import { EMPTY_OBJ, NOOP } from './utils' import { EMPTY_OBJ, NOOP, isArray } from '@vue/shared'
import { VNode, Slots, RenderNode, MountedVNode } from './vdom' import { VNode, Slots, RenderNode, MountedVNode } from './vdom'
import { import {
Data, Data,
@ -44,30 +44,30 @@ interface PublicInstanceMethods {
$emit(name: string, ...payload: any[]): this $emit(name: string, ...payload: any[]): this
} }
interface APIMethods<P, D> { export interface APIMethods<P = {}, D = {}> {
data?(): Partial<D> data(): Partial<D>
render(props: Readonly<P>, slots: Slots, attrs: Data, parentVNode: VNode): any render(props: Readonly<P>, slots: Slots, attrs: Data, parentVNode: VNode): any
} }
interface LifecycleMethods { export interface LifecycleMethods {
beforeCreate?(): void beforeCreate(): void
created?(): void created(): void
beforeMount?(): void beforeMount(): void
mounted?(): void mounted(): void
beforeUpdate?(vnode: VNode): void beforeUpdate(vnode: VNode): void
updated?(vnode: VNode): void updated(vnode: VNode): void
beforeUnmount?(): void beforeUnmount(): void
unmounted?(): void unmounted(): void
errorCaptured?(): ( errorCaptured(): (
err: Error, err: Error,
type: ErrorTypes, type: ErrorTypes,
instance: ComponentInstance | null, instance: ComponentInstance | null,
vnode: VNode vnode: VNode
) => boolean | void ) => boolean | void
activated?(): void activated(): void
deactivated?(): void deactivated(): void
renderTracked?(e: DebuggerEvent): void renderTracked(e: DebuggerEvent): void
renderTriggered?(e: DebuggerEvent): void renderTriggered(e: DebuggerEvent): void
} }
export interface ComponentClass extends ComponentClassOptions { export interface ComponentClass extends ComponentClassOptions {
@ -88,9 +88,10 @@ export type ComponentType = ComponentClass | FunctionalComponent
// It extends InternalComponent with mounted instance properties. // It extends InternalComponent with mounted instance properties.
export interface ComponentInstance<P = {}, D = {}> export interface ComponentInstance<P = {}, D = {}>
extends InternalComponent, extends InternalComponent,
APIMethods<P, D>, Partial<APIMethods<P, D>>,
LifecycleMethods { Partial<LifecycleMethods> {
constructor: ComponentClass constructor: ComponentClass
render: APIMethods<P, D>['render']
$vnode: MountedVNode $vnode: MountedVNode
$data: D $data: D
@ -157,7 +158,7 @@ class InternalComponent implements PublicInstanceMethods {
// eventEmitter interface // eventEmitter interface
$on(event: string, fn: Function): this { $on(event: string, fn: Function): this {
if (Array.isArray(event)) { if (isArray(event)) {
for (let i = 0; i < event.length; i++) { for (let i = 0; i < event.length; i++) {
this.$on(event[i], fn) this.$on(event[i], fn)
} }
@ -181,7 +182,7 @@ class InternalComponent implements PublicInstanceMethods {
if (this._events) { if (this._events) {
if (!event && !fn) { if (!event && !fn) {
this._events = null this._events = null
} else if (Array.isArray(event)) { } else if (isArray(event)) {
for (let i = 0; i < event.length; i++) { for (let i = 0; i < event.length; i++) {
this.$off(event[i], fn) this.$off(event[i], fn)
} }
@ -223,7 +224,7 @@ class InternalComponent implements PublicInstanceMethods {
function invokeListeners(value: Function | Function[], payload: any[]) { function invokeListeners(value: Function | Function[], payload: any[]) {
// TODO handle error // TODO handle error
if (Array.isArray(value)) { if (isArray(value)) {
for (let i = 0; i < value.length; i++) { for (let i = 0; i < value.length; i++) {
value[i](...payload) value[i](...payload)
} }

View File

@ -1,4 +1,4 @@
import { NOOP } from './utils' import { NOOP, isFunction } from '@vue/shared'
import { computed, stop, ComputedGetter } from '@vue/observer' import { computed, stop, ComputedGetter } from '@vue/observer'
import { ComponentInstance } from './component' import { ComponentInstance } from './component'
import { ComponentComputedOptions } from './componentOptions' import { ComponentComputedOptions } from './componentOptions'
@ -17,7 +17,7 @@ export function initializeComputed(
const proxy = instance.$proxy const proxy = instance.$proxy
for (const key in computedOptions) { for (const key in computedOptions) {
const option = computedOptions[key] const option = computedOptions[key]
const getter = typeof option === 'function' ? option : option.get || NOOP const getter = isFunction(option) ? option : option.get || NOOP
handles[key] = computed(getter, proxy) handles[key] = computed(getter, proxy)
} }
} }

View File

@ -1,5 +1,12 @@
import { ComponentInstance } from './component' import {
ComponentInstance,
ComponentClass,
APIMethods,
LifecycleMethods
} from './component'
import { Slots } from './vdom' import { Slots } from './vdom'
import { isArray, isObject, isFunction } from '@vue/shared'
import { normalizePropsOptions } from './componentProps'
export type Data = Record<string, any> export type Data = Record<string, any>
@ -73,3 +80,100 @@ export interface WatchOptions {
deep?: boolean deep?: boolean
immediate?: boolean immediate?: boolean
} }
type ReservedKeys = { [K in keyof (APIMethods & LifecycleMethods)]: 1 }
export const reservedMethods: ReservedKeys = {
data: 1,
render: 1,
beforeCreate: 1,
created: 1,
beforeMount: 1,
mounted: 1,
beforeUpdate: 1,
updated: 1,
beforeUnmount: 1,
unmounted: 1,
errorCaptured: 1,
activated: 1,
deactivated: 1,
renderTracked: 1,
renderTriggered: 1
}
// This is called in the base component constructor and the return value is
// set on the instance as $options.
export function resolveComponentOptionsFromClass(
Component: ComponentClass
): ComponentOptions {
if (Component.options) {
return Component.options
}
const staticDescriptors = Object.getOwnPropertyDescriptors(Component)
const options = {} as any
for (const key in staticDescriptors) {
const { enumerable, get, value } = staticDescriptors[key]
if (enumerable || get) {
options[key] = get ? get() : value
}
}
const instanceDescriptors = Object.getOwnPropertyDescriptors(
Component.prototype
)
for (const key in instanceDescriptors) {
const { get, value } = instanceDescriptors[key]
if (get) {
// computed properties
;(options.computed || (options.computed = {}))[key] = get
// there's no need to do anything for the setter
// as it's already defined on the prototype
} else if (isFunction(value)) {
if (key in reservedMethods) {
options[key] = value
} else {
;(options.methods || (options.methods = {}))[key] = value
}
}
}
options.props = normalizePropsOptions(options.props)
Component.options = options
return options
}
export function mergeComponentOptions(to: any, from: any): ComponentOptions {
const res: any = Object.assign({}, to)
if (from.mixins) {
from.mixins.forEach((mixin: any) => {
from = mergeComponentOptions(from, mixin)
})
}
for (const key in from) {
const value = from[key]
const existing = res[key]
if (isFunction(value) && isFunction(existing)) {
if (key === 'data') {
// for data we need to merge the returned value
res[key] = function() {
return Object.assign(existing(), value())
}
} else if (/^render|^errorCaptured/.test(key)) {
// render, renderTracked, renderTriggered & errorCaptured
// are never merged
res[key] = value
} else {
// merge lifecycle hooks
res[key] = function(...args: any[]) {
existing.call(this, ...args)
value.call(this, ...args)
}
}
} else if (isArray(value) && isArray(existing)) {
res[key] = existing.concat(value)
} else if (isObject(value) && isObject(existing)) {
res[key] = Object.assign({}, existing, value)
} else {
res[key] = value
}
}
return res
}

View File

@ -2,19 +2,40 @@ import { immutable, unwrap, lock, unlock } from '@vue/observer'
import { ComponentInstance } from './component' import { ComponentInstance } from './component'
import { import {
Data, Data,
ComponentPropsOptions,
PropOptions, PropOptions,
Prop, Prop,
PropType PropType,
ComponentPropsOptions
} from './componentOptions' } from './componentOptions'
import { EMPTY_OBJ, camelize, hyphenate, capitalize } from './utils' import {
EMPTY_OBJ,
camelize,
hyphenate,
capitalize,
isString,
isFunction,
isArray,
isObject
} from '@vue/shared'
import { warn } from './warning' import { warn } from './warning'
const EMPTY_PROPS = { props: EMPTY_OBJ } const EMPTY_PROPS = { props: EMPTY_OBJ }
const enum BooleanFlags {
shouldCast = '1',
shouldCastTrue = '2'
}
type NormalizedProp = PropOptions & {
[BooleanFlags.shouldCast]?: boolean
[BooleanFlags.shouldCastTrue]?: boolean
}
type NormalizedPropsOptions = Record<string, NormalizedProp>
export function initializeProps( export function initializeProps(
instance: ComponentInstance, instance: ComponentInstance,
options: ComponentPropsOptions | undefined, options: NormalizedPropsOptions | undefined,
data: Data | null data: Data | null
) { ) {
const { props, attrs } = resolveProps(data, options) const { props, attrs } = resolveProps(data, options)
@ -31,13 +52,13 @@ export function initializeProps(
// - else: everything goes in `props`. // - else: everything goes in `props`.
export function resolveProps( export function resolveProps(
rawData: any, rawData: any,
rawOptions: ComponentPropsOptions | void _options: NormalizedPropsOptions | void
): { props: Data; attrs?: Data } { ): { props: Data; attrs?: Data } {
const hasDeclaredProps = rawOptions !== void 0 const hasDeclaredProps = _options !== void 0
const options = _options as NormalizedPropsOptions
if (!rawData && !hasDeclaredProps) { if (!rawData && !hasDeclaredProps) {
return EMPTY_PROPS return EMPTY_PROPS
} }
const options = normalizePropsOptions(rawOptions) as NormalizedPropsOptions
const props: any = {} const props: any = {}
let attrs: any = void 0 let attrs: any = void 0
if (rawData != null) { if (rawData != null) {
@ -66,8 +87,7 @@ export function resolveProps(
// default values // default values
if (hasDefault && currentValue === void 0) { if (hasDefault && currentValue === void 0) {
const defaultValue = opt.default const defaultValue = opt.default
props[key] = props[key] = isFunction(defaultValue) ? defaultValue() : defaultValue
typeof defaultValue === 'function' ? defaultValue() : defaultValue
} }
// boolean casting // boolean casting
if (opt[BooleanFlags.shouldCast]) { if (opt[BooleanFlags.shouldCast]) {
@ -129,58 +149,34 @@ export function updateProps(instance: ComponentInstance, nextData: Data) {
} }
} }
const enum BooleanFlags { export function normalizePropsOptions(
shouldCast = '1',
shouldCastTrue = '2'
}
type NormalizedProp = PropOptions & {
[BooleanFlags.shouldCast]?: boolean
[BooleanFlags.shouldCastTrue]?: boolean
}
type NormalizedPropsOptions = Record<string, NormalizedProp>
const normalizationCache = new WeakMap<
ComponentPropsOptions,
NormalizedPropsOptions
>()
function normalizePropsOptions(
raw: ComponentPropsOptions | void raw: ComponentPropsOptions | void
): NormalizedPropsOptions { ): NormalizedPropsOptions | void {
if (!raw) { if (!raw) {
return EMPTY_OBJ return
}
const hit = normalizationCache.get(raw)
if (hit) {
return hit
} }
const normalized: NormalizedPropsOptions = {} const normalized: NormalizedPropsOptions = {}
if (Array.isArray(raw)) { if (isArray(raw)) {
for (let i = 0; i < raw.length; i++) { for (let i = 0; i < raw.length; i++) {
if (__DEV__ && typeof raw !== 'string') { if (__DEV__ && !isString(raw[i])) {
warn(`props must be strings when using array syntax.`) warn(`props must be strings when using array syntax.`, raw[i])
} }
normalized[camelize(raw[i])] = EMPTY_OBJ normalized[camelize(raw[i])] = EMPTY_OBJ
} }
} else { } else {
if (__DEV__ && typeof raw !== 'object') { if (__DEV__ && !isObject(raw)) {
warn(`invalid props options`, raw) warn(`invalid props options`, raw)
} }
for (const key in raw) { for (const key in raw) {
const opt = raw[key] const opt = raw[key]
const prop = (normalized[camelize(key)] = const prop = (normalized[camelize(key)] =
Array.isArray(opt) || typeof opt === 'function' isArray(opt) || isFunction(opt) ? { type: opt } : opt) as NormalizedProp
? { type: opt }
: opt) as NormalizedProp
const booleanIndex = getTypeIndex(Boolean, prop.type) const booleanIndex = getTypeIndex(Boolean, prop.type)
const stringIndex = getTypeIndex(String, prop.type) const stringIndex = getTypeIndex(String, prop.type)
prop[BooleanFlags.shouldCast] = booleanIndex > -1 prop[BooleanFlags.shouldCast] = booleanIndex > -1
prop[BooleanFlags.shouldCastTrue] = booleanIndex < stringIndex prop[BooleanFlags.shouldCastTrue] = booleanIndex < stringIndex
} }
} }
normalizationCache.set(raw, normalized)
return normalized return normalized
} }
@ -199,13 +195,13 @@ function getTypeIndex(
type: Prop<any>, type: Prop<any>,
expectedTypes: PropType<any> | void | null | true expectedTypes: PropType<any> | void | null | true
): number { ): number {
if (Array.isArray(expectedTypes)) { if (isArray(expectedTypes)) {
for (let i = 0, len = expectedTypes.length; i < len; i++) { for (let i = 0, len = expectedTypes.length; i < len; i++) {
if (isSameType(expectedTypes[i], type)) { if (isSameType(expectedTypes[i], type)) {
return i return i
} }
} }
} else if (expectedTypes != null && typeof expectedTypes === 'object') { } else if (isObject(expectedTypes)) {
return isSameType(expectedTypes, type) ? 0 : -1 return isSameType(expectedTypes, type) ? 0 : -1
} }
return -1 return -1
@ -235,7 +231,7 @@ function validateProp(
// type check // type check
if (type != null && type !== true) { if (type != null && type !== true) {
let isValid = false let isValid = false
const types = Array.isArray(type) ? type : [type] const types = isArray(type) ? type : [type]
const expectedTypes = [] const expectedTypes = []
// value is valid as long as one of the specified types match // value is valid as long as one of the specified types match
for (let i = 0; i < types.length && !isValid; i++) { for (let i = 0; i < types.length && !isValid; i++) {
@ -269,7 +265,7 @@ function assertType(value: any, type: Prop<any>): AssertionResult {
} else if (expectedType === 'Object') { } else if (expectedType === 'Object') {
valid = toRawType(value) === 'Object' valid = toRawType(value) === 'Object'
} else if (expectedType === 'Array') { } else if (expectedType === 'Array') {
valid = Array.isArray(value) valid = isArray(value)
} else { } else {
valid = value instanceof type valid = value instanceof type
} }

View File

@ -1,4 +1,5 @@
import { ComponentInstance } from './component' import { ComponentInstance } from './component'
import { isString, isFunction } from '@vue/shared'
const bindCache = new WeakMap() const bindCache = new WeakMap()
@ -41,7 +42,7 @@ const renderProxyHandlers = {
// TODO warn non-present property // TODO warn non-present property
} }
const value = Reflect.get(target, key, receiver) const value = Reflect.get(target, key, receiver)
if (typeof value === 'function') { if (isFunction(value)) {
// auto bind // auto bind
return getBoundMethod(value, target, receiver) return getBoundMethod(value, target, receiver)
} else { } else {
@ -56,7 +57,7 @@ const renderProxyHandlers = {
receiver: any receiver: any
): boolean { ): boolean {
if (__DEV__) { if (__DEV__) {
if (typeof key === 'string' && key[0] === '$') { if (isString(key) && key[0] === '$') {
// TODO warn setting immutable properties // TODO warn setting immutable properties
return false return false
} }

View File

@ -1,4 +1,3 @@
// import { EMPTY_OBJ } from './utils'
import { ComponentInstance } from './component' import { ComponentInstance } from './component'
import { observable } from '@vue/observer' import { observable } from '@vue/observer'

View File

@ -1,5 +1,5 @@
import { VNodeFlags } from './flags' import { VNodeFlags } from './flags'
import { EMPTY_OBJ } from './utils' import { EMPTY_OBJ, isArray, isFunction, isObject } from '@vue/shared'
import { h } from './h' import { h } from './h'
import { VNode, MountedVNode, createFragment } from './vdom' import { VNode, MountedVNode, createFragment } from './vdom'
import { import {
@ -13,7 +13,10 @@ import { initializeState } from './componentState'
import { initializeProps, resolveProps } from './componentProps' import { initializeProps, resolveProps } from './componentProps'
import { initializeComputed, teardownComputed } from './componentComputed' import { initializeComputed, teardownComputed } from './componentComputed'
import { initializeWatch, teardownWatch } from './componentWatch' import { initializeWatch, teardownWatch } from './componentWatch'
import { ComponentOptions } from './componentOptions' import {
ComponentOptions,
resolveComponentOptionsFromClass
} from './componentOptions'
import { createRenderProxy } from './componentProxy' import { createRenderProxy } from './componentProxy'
import { handleError, ErrorTypes } from './errorHandling' import { handleError, ErrorTypes } from './errorHandling'
import { warn } from './warning' import { warn } from './warning'
@ -58,7 +61,7 @@ export function initializeComponentInstance(instance: ComponentInstance) {
) )
} }
instance.$options = resolveComponentOptions(instance.constructor) instance.$options = resolveComponentOptionsFromClass(instance.constructor)
instance.$parentVNode = currentVNode as MountedVNode instance.$parentVNode = currentVNode as MountedVNode
// renderProxy // renderProxy
@ -143,9 +146,9 @@ function normalizeComponentRoot(
): VNode { ): VNode {
if (vnode == null) { if (vnode == null) {
vnode = createTextVNode('') vnode = createTextVNode('')
} else if (typeof vnode !== 'object') { } else if (!isObject(vnode)) {
vnode = createTextVNode(vnode + '') vnode = createTextVNode(vnode + '')
} else if (Array.isArray(vnode)) { } else if (isArray(vnode)) {
if (vnode.length === 1) { if (vnode.length === 1) {
vnode = normalizeComponentRoot(vnode[0], componentVNode) vnode = normalizeComponentRoot(vnode[0], componentVNode)
} else { } else {
@ -158,13 +161,13 @@ function normalizeComponentRoot(
(flags & VNodeFlags.COMPONENT || flags & VNodeFlags.ELEMENT) (flags & VNodeFlags.COMPONENT || flags & VNodeFlags.ELEMENT)
) { ) {
if (el) { if (el) {
vnode = cloneVNode(vnode) vnode = cloneVNode(vnode as VNode)
} }
if (flags & VNodeFlags.COMPONENT) { if (flags & VNodeFlags.COMPONENT) {
vnode.parentVNode = componentVNode vnode.parentVNode = componentVNode
} }
} else if (el) { } else if (el) {
vnode = cloneVNode(vnode) vnode = cloneVNode(vnode as VNode)
} }
} }
return vnode return vnode
@ -209,7 +212,7 @@ export function createComponentClassFromOptions(
// name -> displayName // name -> displayName
if (key === 'name') { if (key === 'name') {
options.displayName = options.name options.displayName = options.name
} else if (typeof value === 'function') { } else if (isFunction(value)) {
// lifecycle hook / data / render // lifecycle hook / data / render
if (__COMPAT__) { if (__COMPAT__) {
if (key === 'render') { if (key === 'render') {
@ -229,7 +232,7 @@ export function createComponentClassFromOptions(
} else if (key === 'computed') { } else if (key === 'computed') {
for (const computedKey in value) { for (const computedKey in value) {
const computed = value[computedKey] const computed = value[computedKey]
const isGet = typeof computed === 'function' const isGet = isFunction(computed)
Object.defineProperty(proto, computedKey, { Object.defineProperty(proto, computedKey, {
configurable: true, configurable: true,
get: isGet ? computed : computed.get, get: isGet ? computed : computed.get,
@ -250,37 +253,3 @@ export function createComponentClassFromOptions(
} }
return AnonymousComponent as ComponentClass return AnonymousComponent as ComponentClass
} }
// This is called in the base component constructor and the return value is
// set on the instance as $options.
export function resolveComponentOptions(
Component: ComponentClass
): ComponentOptions {
if (Component.options) {
return Component.options
}
const staticDescriptors = Object.getOwnPropertyDescriptors(Component)
const options = {} as any
for (const key in staticDescriptors) {
const { enumerable, get, value } = staticDescriptors[key]
if (enumerable || get) {
options[key] = get ? get() : value
}
}
const instanceDescriptors = Object.getOwnPropertyDescriptors(
Component.prototype
)
for (const key in instanceDescriptors) {
const { get, value } = instanceDescriptors[key]
if (get) {
// computed properties
;(options.computed || (options.computed = {}))[key] = get
// there's no need to do anything for the setter
// as it's already defined on the prototype
} else if (typeof value === 'function') {
;(options.methods || (options.methods = {}))[key] = value
}
}
Component.options = options
return options
}

View File

@ -1,4 +1,11 @@
import { EMPTY_OBJ, NOOP } from './utils' import {
EMPTY_OBJ,
NOOP,
isFunction,
isArray,
isString,
isObject
} from '@vue/shared'
import { ComponentInstance } from './component' import { ComponentInstance } from './component'
import { ComponentWatchOptions, WatchOptions } from './componentOptions' import { ComponentWatchOptions, WatchOptions } from './componentOptions'
import { autorun, stop } from '@vue/observer' import { autorun, stop } from '@vue/observer'
@ -13,11 +20,11 @@ export function initializeWatch(
if (options !== void 0) { if (options !== void 0) {
for (const key in options) { for (const key in options) {
const opt = options[key] const opt = options[key]
if (Array.isArray(opt)) { if (isArray(opt)) {
opt.forEach(o => setupWatcher(instance, key, o)) opt.forEach(o => setupWatcher(instance, key, o))
} else if (typeof opt === 'function') { } else if (isFunction(opt)) {
setupWatcher(instance, key, opt) setupWatcher(instance, key, opt)
} else if (typeof opt === 'string') { } else if (isString(opt)) {
setupWatcher(instance, key, (instance as any)[opt]) setupWatcher(instance, key, (instance as any)[opt])
} else if (opt.handler) { } else if (opt.handler) {
setupWatcher(instance, key, opt.handler, opt) setupWatcher(instance, key, opt.handler, opt)
@ -35,10 +42,9 @@ export function setupWatcher(
const handles = instance._watchHandles || (instance._watchHandles = new Set()) const handles = instance._watchHandles || (instance._watchHandles = new Set())
const proxy = instance.$proxy const proxy = instance.$proxy
const rawGetter = const rawGetter = isString(keyOrFn)
typeof keyOrFn === 'string' ? parseDotPath(keyOrFn, proxy)
? parseDotPath(keyOrFn, proxy) : () => keyOrFn.call(proxy)
: () => keyOrFn.call(proxy)
if (__DEV__ && rawGetter === NOOP) { if (__DEV__ && rawGetter === NOOP) {
warn( warn(
@ -116,11 +122,11 @@ function parseDotPath(path: string, ctx: any): Function {
} }
function traverse(value: any, seen: Set<any> = new Set()) { function traverse(value: any, seen: Set<any> = new Set()) {
if (value === null || typeof value !== 'object' || seen.has(value)) { if (!isObject(value) || seen.has(value)) {
return return
} }
seen.add(value) seen.add(value)
if (Array.isArray(value)) { if (isArray(value)) {
for (let i = 0; i < value.length; i++) { for (let i = 0; i < value.length; i++) {
traverse(value[i], seen) traverse(value[i], seen)
} }

View File

@ -1,7 +1,7 @@
import { autorun, stop } from '@vue/observer' import { autorun, stop } from '@vue/observer'
import { queueJob } from '@vue/scheduler' import { queueJob } from '@vue/scheduler'
import { VNodeFlags, ChildrenFlags } from './flags' import { VNodeFlags, ChildrenFlags } from './flags'
import { EMPTY_OBJ, reservedPropRE, lis } from './utils' import { EMPTY_OBJ, reservedPropRE, isString } from '@vue/shared'
import { import {
VNode, VNode,
MountedVNode, MountedVNode,
@ -297,7 +297,7 @@ export function createRenderer(options: RendererOptions) {
contextVNode: MountedVNode | null contextVNode: MountedVNode | null
) { ) {
const { tag, children, childFlags, ref } = vnode const { tag, children, childFlags, ref } = vnode
const target = typeof tag === 'string' ? platformQuerySelector(tag) : tag const target = isString(tag) ? platformQuerySelector(tag) : tag
if (__DEV__ && !target) { if (__DEV__ && !target) {
// TODO warn poartal target not found // TODO warn poartal target not found
@ -1410,3 +1410,49 @@ export function createRenderer(options: RendererOptions) {
return { render } return { render }
} }
// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
export function lis(arr: number[]): number[] {
const p = arr.slice()
const result = [0]
let i
let j
let u
let v
let c
const len = arr.length
for (i = 0; i < len; i++) {
const arrI = arr[i]
if (arrI !== 0) {
j = result[result.length - 1]
if (arr[j] < arrI) {
p[i] = j
result.push(i)
continue
}
u = 0
v = result.length - 1
while (u < v) {
c = ((u + v) / 2) | 0
if (arr[result[c]] < arrI) {
u = c + 1
} else {
v = c
}
}
if (arrI < arr[result[u]]) {
if (u > 0) {
p[i] = result[u - 1]
}
result[u] = i
}
}
}
u = result.length
v = result[u - 1]
while (u-- > 0) {
result[u] = v
v = p[v]
}
return result
}

View File

@ -14,6 +14,7 @@ import {
} from './vdom' } from './vdom'
import { isObservable } from '@vue/observer' import { isObservable } from '@vue/observer'
import { warn } from './warning' import { warn } from './warning'
import { isString, isArray, isFunction, isObject } from '@vue/shared'
export const Fragment = Symbol() export const Fragment = Symbol()
export const Portal = Symbol() export const Portal = Symbol()
@ -98,10 +99,7 @@ interface createElement extends VNodeFactories {
} }
export const h = ((tag: ElementType, data?: any, children?: any): VNode => { export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
if ( if (isArray(data) || !isObject(data) || data._isVNode) {
Array.isArray(data) ||
(data != null && (typeof data !== 'object' || data._isVNode))
) {
children = data children = data
data = null data = null
} }
@ -133,7 +131,7 @@ export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
} }
} }
if (typeof tag === 'string') { if (isString(tag)) {
// element // element
return createElementVNode( return createElementVNode(
tag, tag,
@ -160,10 +158,7 @@ export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
ref ref
) )
} else { } else {
if ( if (__DEV__ && !isFunction(tag) && !isObject(tag)) {
__DEV__ &&
(!tag || (typeof tag !== 'function' && typeof tag !== 'object'))
) {
warn('Invalid component passed to h(): ', tag) warn('Invalid component passed to h(): ', tag)
} }
// component // component

View File

@ -10,21 +10,26 @@ export * from '@vue/observer'
// Scheduler API // Scheduler API
export { nextTick } from '@vue/scheduler' export { nextTick } from '@vue/scheduler'
// Internal API
export {
createComponentInstance,
createComponentClassFromOptions
} from './componentUtils'
// Optional APIs // Optional APIs
// these are imported on-demand and can be tree-shaken // these are imported on-demand and can be tree-shaken
export { applyDirectives } from './optional/directive' export { applyDirectives } from './optional/directive'
export { Provide, Inject } from './optional/context' export { Provide, Inject } from './optional/context'
export { createAsyncComponent } from './optional/asyncComponent' export { createAsyncComponent } from './optional/asyncComponent'
export { KeepAlive } from './optional/keepAlive' export { KeepAlive } from './optional/keepAlive'
export { mixins } from './optional/mixin'
// flags & types // flags & types
export { ComponentType, ComponentClass, FunctionalComponent } from './component' export { ComponentType, ComponentClass, FunctionalComponent } from './component'
export * from './componentOptions'
export { VNodeFlags, ChildrenFlags } from './flags' export { VNodeFlags, ChildrenFlags } from './flags'
export { VNode, Slots } from './vdom' export { VNode, Slots } from './vdom'
// Internal API, for libraries or renderers that need to perform low level work
export {
reservedMethods,
resolveComponentOptionsFromClass,
mergeComponentOptions
} from './componentOptions'
export {
createComponentInstance,
createComponentClassFromOptions
} from './componentUtils'

View File

@ -2,6 +2,7 @@ import { ChildrenFlags } from '../flags'
import { createComponentVNode, Slots } from '../vdom' import { createComponentVNode, Slots } from '../vdom'
import { Component, ComponentType, ComponentClass } from '../component' import { Component, ComponentType, ComponentClass } from '../component'
import { unwrap } from '@vue/observer' import { unwrap } from '@vue/observer'
import { isFunction } from '@vue/shared'
interface AsyncComponentFactory { interface AsyncComponentFactory {
(): Promise<ComponentType> (): Promise<ComponentType>
@ -21,7 +22,7 @@ type AsyncComponentOptions = AsyncComponentFactory | AsyncComponentFullOptions
export function createAsyncComponent( export function createAsyncComponent(
options: AsyncComponentOptions options: AsyncComponentOptions
): ComponentClass { ): ComponentClass {
if (typeof options === 'function') { if (isFunction(options)) {
options = { factory: options } options = { factory: options }
} }

View File

@ -15,7 +15,7 @@ return applyDirectives(
import { VNode, cloneVNode, VNodeData } from '../vdom' import { VNode, cloneVNode, VNodeData } from '../vdom'
import { ComponentInstance } from '../component' import { ComponentInstance } from '../component'
import { EMPTY_OBJ } from '../utils' import { EMPTY_OBJ } from '@vue/shared'
interface DirectiveBinding { interface DirectiveBinding {
instance: ComponentInstance instance: ComponentInstance

View File

@ -2,6 +2,7 @@ import { Component, ComponentClass, ComponentInstance } from '../component'
import { VNode, Slots, cloneVNode } from '../vdom' import { VNode, Slots, cloneVNode } from '../vdom'
import { VNodeFlags } from '../flags' import { VNodeFlags } from '../flags'
import { warn } from '../warning' import { warn } from '../warning'
import { isString, isArray } from '@vue/shared'
type MatchPattern = string | RegExp | string[] | RegExp[] type MatchPattern = string | RegExp | string[] | RegExp[]
@ -119,9 +120,9 @@ function getName(comp: ComponentClass): string | void {
} }
function matches(pattern: MatchPattern, name: string): boolean { function matches(pattern: MatchPattern, name: string): boolean {
if (Array.isArray(pattern)) { if (isArray(pattern)) {
return (pattern as any).some((p: string | RegExp) => matches(p, name)) return (pattern as any).some((p: string | RegExp) => matches(p, name))
} else if (typeof pattern === 'string') { } else if (isString(pattern)) {
return pattern.split(',').indexOf(name) > -1 return pattern.split(',').indexOf(name) > -1
} else if (pattern.test) { } else if (pattern.test) {
return pattern.test(name) return pattern.test(name)

View File

@ -1,4 +1,12 @@
import { Component } from '../component' import { Component } from '../component'
import { createComponentClassFromOptions } from '../componentUtils'
import {
ComponentOptions,
resolveComponentOptionsFromClass,
mergeComponentOptions
} from '../componentOptions'
import { normalizePropsOptions } from '../componentProps'
import { isFunction } from '@vue/shared'
interface ComponentConstructor<This = Component> { interface ComponentConstructor<This = Component> {
new (): This new (): This
@ -25,7 +33,19 @@ export function mixins<
V = ExtractInstance<T> V = ExtractInstance<T>
>(...args: T): ComponentConstructorWithMixins<V> >(...args: T): ComponentConstructorWithMixins<V>
export function mixins(...args: any[]): any { export function mixins(...args: any[]): any {
// TODO let options: ComponentOptions = {}
args.forEach(mixin => {
if (isFunction(mixin)) {
options = mergeComponentOptions(
options,
resolveComponentOptionsFromClass(mixin)
)
} else {
mixin.props = normalizePropsOptions(mixin.props)
options = mergeComponentOptions(options, mixin)
}
})
return createComponentClassFromOptions(options)
} }
/* Example usage /* Example usage

View File

@ -1,105 +0,0 @@
export const EMPTY_OBJ: { readonly [key: string]: any } = Object.freeze({})
export const NOOP = () => {}
export const onRE = /^on/
export const vnodeHookRE = /^vnode/
export const handlersRE = /^on|^vnode/
export const reservedPropRE = /^(?:key|ref|slots)$|^vnode/
export function normalizeStyle(
value: any
): Record<string, string | number> | void {
if (Array.isArray(value)) {
const res: Record<string, string | number> = {}
for (let i = 0; i < value.length; i++) {
const normalized = normalizeStyle(value[i])
if (normalized) {
for (const key in normalized) {
res[key] = normalized[key]
}
}
}
return res
} else if (value && typeof value === 'object') {
return value
}
}
export function normalizeClass(value: any): string {
let res = ''
if (typeof value === 'string') {
res = value
} else if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
res += normalizeClass(value[i]) + ' '
}
} else if (typeof value === 'object') {
for (const name in value) {
if (value[name]) {
res += name + ' '
}
}
}
return res.trim()
}
const camelizeRE = /-(\w)/g
export const camelize = (str: string): string => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
}
const hyphenateRE = /\B([A-Z])/g
export const hyphenate = (str: string): string => {
return str.replace(hyphenateRE, '-$1').toLowerCase()
}
export const capitalize = (str: string): string => {
return str.charAt(0).toUpperCase() + str.slice(1)
}
// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
export function lis(arr: number[]): number[] {
const p = arr.slice()
const result = [0]
let i
let j
let u
let v
let c
const len = arr.length
for (i = 0; i < len; i++) {
const arrI = arr[i]
if (arrI !== 0) {
j = result[result.length - 1]
if (arr[j] < arrI) {
p[i] = j
result.push(i)
continue
}
u = 0
v = result.length - 1
while (u < v) {
c = ((u + v) / 2) | 0
if (arr[result[c]] < arrI) {
u = c + 1
} else {
v = c
}
}
if (arrI < arr[result[u]]) {
if (u > 0) {
p[i] = result[u - 1]
}
result[u] = i
}
}
}
u = result.length
v = result[u - 1]
while (u-- > 0) {
result[u] = v
v = p[v]
}
return result
}

View File

@ -5,7 +5,14 @@ import {
} from './component' } from './component'
import { VNodeFlags, ChildrenFlags } from './flags' import { VNodeFlags, ChildrenFlags } from './flags'
import { createComponentClassFromOptions } from './componentUtils' import { createComponentClassFromOptions } from './componentUtils'
import { normalizeClass, normalizeStyle, handlersRE, EMPTY_OBJ } from './utils' import {
handlersRE,
EMPTY_OBJ,
isObject,
isArray,
isFunction,
isString
} from '@vue/shared'
import { RawChildrenType, RawSlots } from './h' import { RawChildrenType, RawSlots } from './h'
// Vue core is platform agnostic, so we are not using Element for "DOM" nodes. // Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
@ -98,15 +105,6 @@ export function createVNode(
return vnode return vnode
} }
function normalizeClassAndStyle(data: VNodeData) {
if (data.class != null) {
data.class = normalizeClass(data.class)
}
if (data.style != null) {
data.style = normalizeStyle(data.style)
}
}
export function createElementVNode( export function createElementVNode(
tag: string, tag: string,
data: VNodeData | null, data: VNodeData | null,
@ -122,6 +120,50 @@ export function createElementVNode(
return createVNode(flags, tag, data, children, childFlags, key, ref, null) return createVNode(flags, tag, data, children, childFlags, key, ref, null)
} }
function normalizeClassAndStyle(data: VNodeData) {
if (data.class != null) {
data.class = normalizeClass(data.class)
}
if (data.style != null) {
data.style = normalizeStyle(data.style)
}
}
function normalizeStyle(value: any): Record<string, string | number> | void {
if (isArray(value)) {
const res: Record<string, string | number> = {}
for (let i = 0; i < value.length; i++) {
const normalized = normalizeStyle(value[i])
if (normalized) {
for (const key in normalized) {
res[key] = normalized[key]
}
}
}
return res
} else if (isObject(value)) {
return value
}
}
function normalizeClass(value: any): string {
let res = ''
if (isString(value)) {
res = value
} else if (isArray(value)) {
for (let i = 0; i < value.length; i++) {
res += normalizeClass(value[i]) + ' '
}
} else if (isObject(value)) {
for (const name in value) {
if (value[name]) {
res += name + ' '
}
}
}
return res.trim()
}
export function createComponentVNode( export function createComponentVNode(
comp: any, comp: any,
data: VNodeData | null, data: VNodeData | null,
@ -134,8 +176,7 @@ export function createComponentVNode(
let flags: VNodeFlags let flags: VNodeFlags
// flags // flags
const compType = typeof comp if (isObject(comp)) {
if (compType === 'object') {
if (comp.functional) { if (comp.functional) {
// object literal functional // object literal functional
flags = VNodeFlags.COMPONENT_FUNCTIONAL flags = VNodeFlags.COMPONENT_FUNCTIONAL
@ -155,7 +196,7 @@ export function createComponentVNode(
} }
} else { } else {
// assumes comp is function here now // assumes comp is function here now
if (__DEV__ && compType !== 'function') { if (__DEV__ && !isFunction(comp)) {
// TODO warn invalid comp value in dev // TODO warn invalid comp value in dev
} }
if (comp.prototype && comp.prototype.render) { if (comp.prototype && comp.prototype.render) {
@ -178,14 +219,13 @@ export function createComponentVNode(
? ChildrenFlags.DYNAMIC_SLOTS ? ChildrenFlags.DYNAMIC_SLOTS
: ChildrenFlags.NO_CHILDREN : ChildrenFlags.NO_CHILDREN
if (children != null) { if (children != null) {
const childrenType = typeof children if (isFunction(children)) {
if (childrenType === 'function') {
// function as children // function as children
slots = { default: children } slots = { default: children }
} else if (Array.isArray(children) || (children as any)._isVNode) { } else if (isArray(children) || (children as any)._isVNode) {
// direct vnode children // direct vnode children
slots = { default: () => children } slots = { default: () => children }
} else if (typeof children === 'object') { } else if (isObject(children)) {
// slot object as children // slot object as children
slots = children slots = children
} }
@ -313,7 +353,7 @@ export function cloneVNode(vnode: VNode, extraData?: VNodeData): VNode {
function normalizeChildren(vnode: VNode, children: any) { function normalizeChildren(vnode: VNode, children: any) {
let childFlags let childFlags
if (Array.isArray(children)) { if (isArray(children)) {
const { length } = children const { length } = children
if (length === 0) { if (length === 0) {
childFlags = ChildrenFlags.NO_CHILDREN childFlags = ChildrenFlags.NO_CHILDREN
@ -356,7 +396,7 @@ export function normalizeVNodes(
newChild = createTextVNode('') newChild = createTextVNode('')
} else if (child._isVNode) { } else if (child._isVNode) {
newChild = child.el ? cloneVNode(child) : child newChild = child.el ? cloneVNode(child) : child
} else if (Array.isArray(child)) { } else if (isArray(child)) {
normalizeVNodes(child, newChildren, currentPrefix + i + '|') normalizeVNodes(child, newChildren, currentPrefix + i + '|')
} else { } else {
newChild = createTextVNode(child + '') newChild = createTextVNode(child + '')
@ -386,7 +426,7 @@ function normalizeSlots(slots: { [name: string]: any }): Slots {
function normalizeSlot(value: any): VNode[] { function normalizeSlot(value: any): VNode[] {
if (value == null) { if (value == null) {
return [createTextVNode('')] return [createTextVNode('')]
} else if (Array.isArray(value)) { } else if (isArray(value)) {
return normalizeVNodes(value) return normalizeVNodes(value)
} else if (value._isVNode) { } else if (value._isVNode) {
return [value] return [value]

View File

@ -1,5 +1,5 @@
import { ComponentType, ComponentClass, FunctionalComponent } from './component' import { ComponentType, ComponentClass, FunctionalComponent } from './component'
import { EMPTY_OBJ } from './utils' import { EMPTY_OBJ, isString } from '@vue/shared'
import { VNode } from './vdom' import { VNode } from './vdom'
import { Data } from './componentOptions' import { Data } from './componentOptions'
@ -119,7 +119,7 @@ function formatProps(props: Data) {
const res = [] const res = []
for (const key in props) { for (const key in props) {
const value = props[key] const value = props[key]
if (typeof value === 'string') { if (isString(value)) {
res.push(`${key}=${JSON.stringify(value)}`) res.push(`${key}=${JSON.stringify(value)}`)
} else { } else {
res.push(`${key}=`, value) res.push(`${key}=`, value)

View File

@ -2,6 +2,7 @@ import { observable, immutable, unwrap } from './index'
import { OperationTypes } from './operations' import { OperationTypes } from './operations'
import { track, trigger } from './autorun' import { track, trigger } from './autorun'
import { LOCKED } from './lock' import { LOCKED } from './lock'
import { isObject } from '@vue/shared'
const hasOwnProperty = Object.prototype.hasOwnProperty const hasOwnProperty = Object.prototype.hasOwnProperty
@ -18,7 +19,7 @@ function createGetter(isImmutable: boolean) {
return res return res
} }
track(target, OperationTypes.GET, key) track(target, OperationTypes.GET, key)
return res !== null && typeof res === 'object' return isObject(res)
? isImmutable ? isImmutable
? // need to lazy access immutable and observable here to avoid ? // need to lazy access immutable and observable here to avoid
// circular dependency // circular dependency

View File

@ -2,8 +2,8 @@ import { unwrap, observable, immutable } from './index'
import { track, trigger } from './autorun' import { track, trigger } from './autorun'
import { OperationTypes } from './operations' import { OperationTypes } from './operations'
import { LOCKED } from './lock' import { LOCKED } from './lock'
import { isObject } from '@vue/shared'
const isObject = (value: any) => value !== null && typeof value === 'object'
const toObservable = (value: any) => const toObservable = (value: any) =>
isObject(value) ? observable(value) : value isObject(value) ? observable(value) : value
const toImmutable = (value: any) => (isObject(value) ? immutable(value) : value) const toImmutable = (value: any) => (isObject(value) ? immutable(value) : value)

View File

@ -1,3 +1,4 @@
import { isObject, EMPTY_OBJ } from '@vue/shared'
import { mutableHandlers, immutableHandlers } from './baseHandlers' import { mutableHandlers, immutableHandlers } from './baseHandlers'
import { import {
@ -28,7 +29,6 @@ export { OperationTypes } from './operations'
export { computed, ComputedGetter } from './computed' export { computed, ComputedGetter } from './computed'
export { lock, unlock } from './lock' export { lock, unlock } from './lock'
const EMPTY_OBJ = {}
const collectionTypes: Set<any> = new Set([Set, Map, WeakMap, WeakSet]) const collectionTypes: Set<any> = new Set([Set, Map, WeakMap, WeakSet])
const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/ const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/
@ -83,7 +83,7 @@ function createObservable(
baseHandlers: ProxyHandler<any>, baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any> collectionHandlers: ProxyHandler<any>
) { ) {
if (target === null || typeof target !== 'object') { if (!isObject(target)) {
if (__DEV__) { if (__DEV__) {
console.warn(`value is not observable: ${String(target)}`) console.warn(`value is not observable: ${String(target)}`)
} }

View File

@ -1,4 +1,4 @@
import { createRenderer, VNode, ComponentInstance } from '@vue/core' import { createRenderer, VNode, Component } from '@vue/core'
import { nodeOps } from './nodeOps' import { nodeOps } from './nodeOps'
import { patchData } from './patchData' import { patchData } from './patchData'
import { teardownVNode } from './teardownVNode' import { teardownVNode } from './teardownVNode'
@ -12,7 +12,7 @@ const { render: _render } = createRenderer({
type publicRender = ( type publicRender = (
node: VNode | null, node: VNode | null,
container: HTMLElement container: HTMLElement
) => ComponentInstance | null ) => Component | null
export const render = _render as publicRender export const render = _render as publicRender
// re-export everything from core // re-export everything from core

View File

@ -1,3 +1,5 @@
import { isString } from '@vue/shared'
// style properties that should NOT have "px" added when numeric // style properties that should NOT have "px" added when numeric
const nonNumericRE = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i const nonNumericRE = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i
@ -5,7 +7,7 @@ export function patchStyle(el: any, prev: any, next: any, data: any) {
const { style } = el const { style } = el
if (!next) { if (!next) {
el.removeAttribute('style') el.removeAttribute('style')
} else if (typeof next === 'string') { } else if (isString(next)) {
style.cssText = next style.cssText = next
} else { } else {
for (const key in next) { for (const key in next) {
@ -15,7 +17,7 @@ export function patchStyle(el: any, prev: any, next: any, data: any) {
} }
style[key] = value style[key] = value
} }
if (prev && typeof prev !== 'string') { if (prev && !isString(prev)) {
for (const key in prev) { for (const key in prev) {
if (!next[key]) { if (!next[key]) {
style[key] = '' style[key] = ''

View File

@ -1,4 +1,4 @@
import { createRenderer, VNode, ComponentInstance } from '@vue/core' import { createRenderer, VNode, Component } from '@vue/core'
import { nodeOps, TestElement } from './nodeOps' import { nodeOps, TestElement } from './nodeOps'
import { patchData } from './patchData' import { patchData } from './patchData'
@ -10,7 +10,7 @@ const { render: _render } = createRenderer({
type publicRender = ( type publicRender = (
node: VNode | null, node: VNode | null,
container: TestElement container: TestElement
) => ComponentInstance | null ) => Component | null
export const render = _render as publicRender export const render = _render as publicRender
export { serialize } from './serialize' export { serialize } from './serialize'

View File

@ -0,0 +1 @@
# @vue/shared

View File

@ -0,0 +1,4 @@
{
"name": "@vue/shared",
"private": true
}

View File

@ -0,0 +1,29 @@
export const EMPTY_OBJ: { readonly [key: string]: any } = Object.freeze({})
export const NOOP = () => {}
export const onRE = /^on/
export const vnodeHookRE = /^vnode/
export const handlersRE = /^on|^vnode/
export const reservedPropRE = /^(?:key|ref|slots)$|^vnode/
export const isArray = Array.isArray
export const isFunction = (val: any): val is Function =>
typeof val === 'function'
export const isString = (val: any): val is string => typeof val === 'string'
export const isObject = (val: any): val is Record<any, any> =>
val !== null && typeof val === 'object'
const camelizeRE = /-(\w)/g
export const camelize = (str: string): string => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
}
const hyphenateRE = /\B([A-Z])/g
export const hyphenate = (str: string): string => {
return str.replace(hyphenateRE, '-$1').toLowerCase()
}
export const capitalize = (str: string): string => {
return str.charAt(0).toUpperCase() + str.slice(1)
}

View File

@ -16,7 +16,7 @@ class Vue {
// convert it to a class // convert it to a class
const Component = createComponentClassFromOptions(options || {}) const Component = createComponentClassFromOptions(options || {})
const vnode = h(Component) const vnode = h(Component)
const instance = createComponentInstance(vnode, Component, null) const instance = createComponentInstance(vnode, Component)
function mount(el: any) { function mount(el: any) {
const dom = typeof el === 'string' ? document.querySelector(el) : el const dom = typeof el === 'string' ? document.querySelector(el) : el

View File

@ -95,12 +95,13 @@ function createConfig(output, plugins = []) {
// during a single build. // during a single build.
hasTSChecked = true hasTSChecked = true
const externals = Object.keys(aliasOptions).filter(p => p !== '@vue/shared')
return { return {
input: resolve(`src/index.ts`), input: resolve(`src/index.ts`),
// Global and Browser ESM builds inlines everything so that they can be // Global and Browser ESM builds inlines everything so that they can be
// used alone. // used alone.
external: external: isGlobalBuild || isBrowserESMBuild ? [] : externals,
isGlobalBuild || isBrowserESMBuild ? [] : Object.keys(aliasOptions),
plugins: [ plugins: [
tsPlugin, tsPlugin,
aliasPlugin, aliasPlugin,

View File

@ -1,8 +1,8 @@
const fs = require('fs') const fs = require('fs')
const targets = exports.targets = fs.readdirSync('packages').filter(f => { const targets = (exports.targets = fs.readdirSync('packages').filter(f => {
return fs.statSync(`packages/${f}`).isDirectory() return f !== 'shared' && fs.statSync(`packages/${f}`).isDirectory()
}) }))
exports.fuzzyMatchTarget = partialTarget => { exports.fuzzyMatchTarget = partialTarget => {
const matched = [] const matched = []

View File

@ -19,6 +19,7 @@
], ],
"rootDir": ".", "rootDir": ".",
"paths": { "paths": {
"@vue/shared": ["packages/shared/src"],
"@vue/core": ["packages/core/src"], "@vue/core": ["packages/core/src"],
"@vue/observer": ["packages/observer/src"], "@vue/observer": ["packages/observer/src"],
"@vue/scheduler": ["packages/scheduler/src"], "@vue/scheduler": ["packages/scheduler/src"],