]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: option merging + extract helper functions
authorEvan You <yyx990803@gmail.com>
Tue, 16 Oct 2018 19:47:51 +0000 (15:47 -0400)
committerEvan You <yyx990803@gmail.com>
Tue, 16 Oct 2018 19:47:51 +0000 (15:47 -0400)
32 files changed:
packages/core/__tests__/parentChain.spec.ts
packages/core/src/component.ts
packages/core/src/componentComputed.ts
packages/core/src/componentOptions.ts
packages/core/src/componentProps.ts
packages/core/src/componentProxy.ts
packages/core/src/componentState.ts
packages/core/src/componentUtils.ts
packages/core/src/componentWatch.ts
packages/core/src/createRenderer.ts
packages/core/src/h.ts
packages/core/src/index.ts
packages/core/src/optional/asyncComponent.ts
packages/core/src/optional/directive.ts
packages/core/src/optional/keepAlive.ts
packages/core/src/optional/mixin.ts
packages/core/src/utils.ts [deleted file]
packages/core/src/vdom.ts
packages/core/src/warning.ts
packages/observer/src/baseHandlers.ts
packages/observer/src/collectionHandlers.ts
packages/observer/src/index.ts
packages/renderer-dom/src/index.ts
packages/renderer-dom/src/modules/style.ts
packages/renderer-test/src/index.ts
packages/shared/README.md [new file with mode: 0644]
packages/shared/package.json [new file with mode: 0644]
packages/shared/src/index.ts [new file with mode: 0644]
packages/vue/src/index.ts
rollup.config.js
scripts/utils.js
tsconfig.json

index 93dd752896fb53bd8e4fcf6cd7f3023456ddf361..c34eaf7bcca515dcbb97fb2dedfd5dd3ac64cffe 100644 (file)
@@ -3,7 +3,6 @@ import {
   Component,
   render,
   nodeOps,
-  ComponentInstance,
   observable,
   nextTick
 } from '@vue/renderer-test'
@@ -41,7 +40,7 @@ describe('Parent chain management', () => {
     }
 
     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.$root).toBe(parent)
@@ -100,7 +99,7 @@ describe('Parent chain management', () => {
     }
 
     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.$root).toBe(parent)
index ee62fd22137fbd9262a9b110497282bde322e963..34631eec68a92c2a98937e464fc4f8294da0faa7 100644 (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 {
   Data,
@@ -44,30 +44,30 @@ interface PublicInstanceMethods {
   $emit(name: string, ...payload: any[]): this
 }
 
-interface APIMethods<P, D> {
-  data?(): Partial<D>
+export interface APIMethods<P = {}, D = {}> {
+  data(): Partial<D>
   render(props: Readonly<P>, slots: Slots, attrs: Data, parentVNode: VNode): any
 }
 
-interface LifecycleMethods {
-  beforeCreate?(): void
-  created?(): void
-  beforeMount?(): void
-  mounted?(): void
-  beforeUpdate?(vnode: VNode): void
-  updated?(vnode: VNode): void
-  beforeUnmount?(): void
-  unmounted?(): void
-  errorCaptured?(): (
+export interface LifecycleMethods {
+  beforeCreate(): void
+  created(): void
+  beforeMount(): void
+  mounted(): void
+  beforeUpdate(vnode: VNode): void
+  updated(vnode: VNode): void
+  beforeUnmount(): void
+  unmounted(): void
+  errorCaptured(): (
     err: Error,
     type: ErrorTypes,
     instance: ComponentInstance | null,
     vnode: VNode
   ) => boolean | void
-  activated?(): void
-  deactivated?(): void
-  renderTracked?(e: DebuggerEvent): void
-  renderTriggered?(e: DebuggerEvent): void
+  activated(): void
+  deactivated(): void
+  renderTracked(e: DebuggerEvent): void
+  renderTriggered(e: DebuggerEvent): void
 }
 
 export interface ComponentClass extends ComponentClassOptions {
@@ -88,9 +88,10 @@ export type ComponentType = ComponentClass | FunctionalComponent
 // It extends InternalComponent with mounted instance properties.
 export interface ComponentInstance<P = {}, D = {}>
   extends InternalComponent,
-    APIMethods<P, D>,
-    LifecycleMethods {
+    Partial<APIMethods<P, D>>,
+    Partial<LifecycleMethods> {
   constructor: ComponentClass
+  render: APIMethods<P, D>['render']
 
   $vnode: MountedVNode
   $data: D
@@ -157,7 +158,7 @@ class InternalComponent implements PublicInstanceMethods {
 
   // eventEmitter interface
   $on(event: string, fn: Function): this {
-    if (Array.isArray(event)) {
+    if (isArray(event)) {
       for (let i = 0; i < event.length; i++) {
         this.$on(event[i], fn)
       }
@@ -181,7 +182,7 @@ class InternalComponent implements PublicInstanceMethods {
     if (this._events) {
       if (!event && !fn) {
         this._events = null
-      } else if (Array.isArray(event)) {
+      } else if (isArray(event)) {
         for (let i = 0; i < event.length; i++) {
           this.$off(event[i], fn)
         }
@@ -223,7 +224,7 @@ class InternalComponent implements PublicInstanceMethods {
 
 function invokeListeners(value: Function | Function[], payload: any[]) {
   // TODO handle error
-  if (Array.isArray(value)) {
+  if (isArray(value)) {
     for (let i = 0; i < value.length; i++) {
       value[i](...payload)
     }
index 4f3e99c2bc259c358b50247b0d6eb5690e71b387..7e914a1c06b351a0343481fcc9c1bb434c8426ce 100644 (file)
@@ -1,4 +1,4 @@
-import { NOOP } from './utils'
+import { NOOP, isFunction } from '@vue/shared'
 import { computed, stop, ComputedGetter } from '@vue/observer'
 import { ComponentInstance } from './component'
 import { ComponentComputedOptions } from './componentOptions'
@@ -17,7 +17,7 @@ export function initializeComputed(
   const proxy = instance.$proxy
   for (const key in computedOptions) {
     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)
   }
 }
index bab8174599e467fffb3160d2a99548f02999cd61..2b033940a99b9d3c9e15599a1d96288935b4362b 100644 (file)
@@ -1,5 +1,12 @@
-import { ComponentInstance } from './component'
+import {
+  ComponentInstance,
+  ComponentClass,
+  APIMethods,
+  LifecycleMethods
+} from './component'
 import { Slots } from './vdom'
+import { isArray, isObject, isFunction } from '@vue/shared'
+import { normalizePropsOptions } from './componentProps'
 
 export type Data = Record<string, any>
 
@@ -73,3 +80,100 @@ export interface WatchOptions {
   deep?: 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
+}
index 9dae489c2638138ac2edc9e180ad31ad923e61eb..da1fa8254e1ba81241246629801679dec166213f 100644 (file)
@@ -2,19 +2,40 @@ import { immutable, unwrap, lock, unlock } from '@vue/observer'
 import { ComponentInstance } from './component'
 import {
   Data,
-  ComponentPropsOptions,
   PropOptions,
   Prop,
-  PropType
+  PropType,
+  ComponentPropsOptions
 } 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'
 
 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(
   instance: ComponentInstance,
-  options: ComponentPropsOptions | undefined,
+  options: NormalizedPropsOptions | undefined,
   data: Data | null
 ) {
   const { props, attrs } = resolveProps(data, options)
@@ -31,13 +52,13 @@ export function initializeProps(
 //   - else: everything goes in `props`.
 export function resolveProps(
   rawData: any,
-  rawOptions: ComponentPropsOptions | void
+  _options: NormalizedPropsOptions | void
 ): { props: Data; attrs?: Data } {
-  const hasDeclaredProps = rawOptions !== void 0
+  const hasDeclaredProps = _options !== void 0
+  const options = _options as NormalizedPropsOptions
   if (!rawData && !hasDeclaredProps) {
     return EMPTY_PROPS
   }
-  const options = normalizePropsOptions(rawOptions) as NormalizedPropsOptions
   const props: any = {}
   let attrs: any = void 0
   if (rawData != null) {
@@ -66,8 +87,7 @@ export function resolveProps(
       // default values
       if (hasDefault && currentValue === void 0) {
         const defaultValue = opt.default
-        props[key] =
-          typeof defaultValue === 'function' ? defaultValue() : defaultValue
+        props[key] = isFunction(defaultValue) ? defaultValue() : defaultValue
       }
       // boolean casting
       if (opt[BooleanFlags.shouldCast]) {
@@ -129,58 +149,34 @@ export function updateProps(instance: ComponentInstance, nextData: Data) {
   }
 }
 
-const enum BooleanFlags {
-  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(
+export function normalizePropsOptions(
   raw: ComponentPropsOptions | void
-): NormalizedPropsOptions {
+): NormalizedPropsOptions | void {
   if (!raw) {
-    return EMPTY_OBJ
-  }
-  const hit = normalizationCache.get(raw)
-  if (hit) {
-    return hit
+    return
   }
   const normalized: NormalizedPropsOptions = {}
-  if (Array.isArray(raw)) {
+  if (isArray(raw)) {
     for (let i = 0; i < raw.length; i++) {
-      if (__DEV__ && typeof raw !== 'string') {
-        warn(`props must be strings when using array syntax.`)
+      if (__DEV__ && !isString(raw[i])) {
+        warn(`props must be strings when using array syntax.`, raw[i])
       }
       normalized[camelize(raw[i])] = EMPTY_OBJ
     }
   } else {
-    if (__DEV__ && typeof raw !== 'object') {
+    if (__DEV__ && !isObject(raw)) {
       warn(`invalid props options`, raw)
     }
     for (const key in raw) {
       const opt = raw[key]
       const prop = (normalized[camelize(key)] =
-        Array.isArray(opt) || typeof opt === 'function'
-          ? { type: opt }
-          : opt) as NormalizedProp
+        isArray(opt) || isFunction(opt) ? { type: opt } : opt) as NormalizedProp
       const booleanIndex = getTypeIndex(Boolean, prop.type)
       const stringIndex = getTypeIndex(String, prop.type)
       prop[BooleanFlags.shouldCast] = booleanIndex > -1
       prop[BooleanFlags.shouldCastTrue] = booleanIndex < stringIndex
     }
   }
-  normalizationCache.set(raw, normalized)
   return normalized
 }
 
@@ -199,13 +195,13 @@ function getTypeIndex(
   type: Prop<any>,
   expectedTypes: PropType<any> | void | null | true
 ): number {
-  if (Array.isArray(expectedTypes)) {
+  if (isArray(expectedTypes)) {
     for (let i = 0, len = expectedTypes.length; i < len; i++) {
       if (isSameType(expectedTypes[i], type)) {
         return i
       }
     }
-  } else if (expectedTypes != null && typeof expectedTypes === 'object') {
+  } else if (isObject(expectedTypes)) {
     return isSameType(expectedTypes, type) ? 0 : -1
   }
   return -1
@@ -235,7 +231,7 @@ function validateProp(
   // type check
   if (type != null && type !== true) {
     let isValid = false
-    const types = Array.isArray(type) ? type : [type]
+    const types = isArray(type) ? type : [type]
     const expectedTypes = []
     // value is valid as long as one of the specified types match
     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') {
     valid = toRawType(value) === 'Object'
   } else if (expectedType === 'Array') {
-    valid = Array.isArray(value)
+    valid = isArray(value)
   } else {
     valid = value instanceof type
   }
index 33dc6ff5aff33c46c441565089893d7127ca498c..ba9945a1262cda3a8ed793b81d3b127b4ee3ceac 100644 (file)
@@ -1,4 +1,5 @@
 import { ComponentInstance } from './component'
+import { isString, isFunction } from '@vue/shared'
 
 const bindCache = new WeakMap()
 
@@ -41,7 +42,7 @@ const renderProxyHandlers = {
         // TODO warn non-present property
       }
       const value = Reflect.get(target, key, receiver)
-      if (typeof value === 'function') {
+      if (isFunction(value)) {
         // auto bind
         return getBoundMethod(value, target, receiver)
       } else {
@@ -56,7 +57,7 @@ const renderProxyHandlers = {
     receiver: any
   ): boolean {
     if (__DEV__) {
-      if (typeof key === 'string' && key[0] === '$') {
+      if (isString(key) && key[0] === '$') {
         // TODO warn setting immutable properties
         return false
       }
index 931794f42755d2503fc1a7713fcaaad9061c5576..5c1477ce6a7f8eb8c2deaac82966a3e4cfef558d 100644 (file)
@@ -1,4 +1,3 @@
-// import { EMPTY_OBJ } from './utils'
 import { ComponentInstance } from './component'
 import { observable } from '@vue/observer'
 
index b277c82280862ff0cd887e0f7568a5eb0b1d6926..3eb69f5a94ca9cf263659471a5808bb5ab949df8 100644 (file)
@@ -1,5 +1,5 @@
 import { VNodeFlags } from './flags'
-import { EMPTY_OBJ } from './utils'
+import { EMPTY_OBJ, isArray, isFunction, isObject } from '@vue/shared'
 import { h } from './h'
 import { VNode, MountedVNode, createFragment } from './vdom'
 import {
@@ -13,7 +13,10 @@ import { initializeState } from './componentState'
 import { initializeProps, resolveProps } from './componentProps'
 import { initializeComputed, teardownComputed } from './componentComputed'
 import { initializeWatch, teardownWatch } from './componentWatch'
-import { ComponentOptions } from './componentOptions'
+import {
+  ComponentOptions,
+  resolveComponentOptionsFromClass
+} from './componentOptions'
 import { createRenderProxy } from './componentProxy'
 import { handleError, ErrorTypes } from './errorHandling'
 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
 
   // renderProxy
@@ -143,9 +146,9 @@ function normalizeComponentRoot(
 ): VNode {
   if (vnode == null) {
     vnode = createTextVNode('')
-  } else if (typeof vnode !== 'object') {
+  } else if (!isObject(vnode)) {
     vnode = createTextVNode(vnode + '')
-  } else if (Array.isArray(vnode)) {
+  } else if (isArray(vnode)) {
     if (vnode.length === 1) {
       vnode = normalizeComponentRoot(vnode[0], componentVNode)
     } else {
@@ -158,13 +161,13 @@ function normalizeComponentRoot(
       (flags & VNodeFlags.COMPONENT || flags & VNodeFlags.ELEMENT)
     ) {
       if (el) {
-        vnode = cloneVNode(vnode)
+        vnode = cloneVNode(vnode as VNode)
       }
       if (flags & VNodeFlags.COMPONENT) {
         vnode.parentVNode = componentVNode
       }
     } else if (el) {
-      vnode = cloneVNode(vnode)
+      vnode = cloneVNode(vnode as VNode)
     }
   }
   return vnode
@@ -209,7 +212,7 @@ export function createComponentClassFromOptions(
     // name -> displayName
     if (key === 'name') {
       options.displayName = options.name
-    } else if (typeof value === 'function') {
+    } else if (isFunction(value)) {
       // lifecycle hook / data / render
       if (__COMPAT__) {
         if (key === 'render') {
@@ -229,7 +232,7 @@ export function createComponentClassFromOptions(
     } else if (key === 'computed') {
       for (const computedKey in value) {
         const computed = value[computedKey]
-        const isGet = typeof computed === 'function'
+        const isGet = isFunction(computed)
         Object.defineProperty(proto, computedKey, {
           configurable: true,
           get: isGet ? computed : computed.get,
@@ -250,37 +253,3 @@ export function createComponentClassFromOptions(
   }
   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
-}
index 79aa47e7ffe842f6870128cf4bbfcdfb46207b95..33ceb025a0db5f0111621f3a75918bf87ec12a23 100644 (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 { ComponentWatchOptions, WatchOptions } from './componentOptions'
 import { autorun, stop } from '@vue/observer'
@@ -13,11 +20,11 @@ export function initializeWatch(
   if (options !== void 0) {
     for (const key in options) {
       const opt = options[key]
-      if (Array.isArray(opt)) {
+      if (isArray(opt)) {
         opt.forEach(o => setupWatcher(instance, key, o))
-      } else if (typeof opt === 'function') {
+      } else if (isFunction(opt)) {
         setupWatcher(instance, key, opt)
-      } else if (typeof opt === 'string') {
+      } else if (isString(opt)) {
         setupWatcher(instance, key, (instance as any)[opt])
       } else if (opt.handler) {
         setupWatcher(instance, key, opt.handler, opt)
@@ -35,10 +42,9 @@ export function setupWatcher(
   const handles = instance._watchHandles || (instance._watchHandles = new Set())
   const proxy = instance.$proxy
 
-  const rawGetter =
-    typeof keyOrFn === 'string'
-      ? parseDotPath(keyOrFn, proxy)
-      : () => keyOrFn.call(proxy)
+  const rawGetter = isString(keyOrFn)
+    ? parseDotPath(keyOrFn, proxy)
+    : () => keyOrFn.call(proxy)
 
   if (__DEV__ && rawGetter === NOOP) {
     warn(
@@ -116,11 +122,11 @@ function parseDotPath(path: string, ctx: any): Function {
 }
 
 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
   }
   seen.add(value)
-  if (Array.isArray(value)) {
+  if (isArray(value)) {
     for (let i = 0; i < value.length; i++) {
       traverse(value[i], seen)
     }
index 0817cf68a0b021dd49d0c57b23d27667686effb1..8ffffca35a4127039d8f38566364d08c7ead7162 100644 (file)
@@ -1,7 +1,7 @@
 import { autorun, stop } from '@vue/observer'
 import { queueJob } from '@vue/scheduler'
 import { VNodeFlags, ChildrenFlags } from './flags'
-import { EMPTY_OBJ, reservedPropRE, lis } from './utils'
+import { EMPTY_OBJ, reservedPropRE, isString } from '@vue/shared'
 import {
   VNode,
   MountedVNode,
@@ -297,7 +297,7 @@ export function createRenderer(options: RendererOptions) {
     contextVNode: MountedVNode | null
   ) {
     const { tag, children, childFlags, ref } = vnode
-    const target = typeof tag === 'string' ? platformQuerySelector(tag) : tag
+    const target = isString(tag) ? platformQuerySelector(tag) : tag
 
     if (__DEV__ && !target) {
       // TODO warn poartal target not found
@@ -1410,3 +1410,49 @@ export function createRenderer(options: RendererOptions) {
 
   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
+}
index 9d4bcb977d59f1696794ff6cbd9f4a3ca56d7282..f7323f31ebbe1fbfc6da318d94e145910c07d61a 100644 (file)
@@ -14,6 +14,7 @@ import {
 } from './vdom'
 import { isObservable } from '@vue/observer'
 import { warn } from './warning'
+import { isString, isArray, isFunction, isObject } from '@vue/shared'
 
 export const Fragment = Symbol()
 export const Portal = Symbol()
@@ -98,10 +99,7 @@ interface createElement extends VNodeFactories {
 }
 
 export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
-  if (
-    Array.isArray(data) ||
-    (data != null && (typeof data !== 'object' || data._isVNode))
-  ) {
+  if (isArray(data) || !isObject(data) || data._isVNode) {
     children = data
     data = null
   }
@@ -133,7 +131,7 @@ export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
     }
   }
 
-  if (typeof tag === 'string') {
+  if (isString(tag)) {
     // element
     return createElementVNode(
       tag,
@@ -160,10 +158,7 @@ export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
       ref
     )
   } else {
-    if (
-      __DEV__ &&
-      (!tag || (typeof tag !== 'function' && typeof tag !== 'object'))
-    ) {
+    if (__DEV__ && !isFunction(tag) && !isObject(tag)) {
       warn('Invalid component passed to h(): ', tag)
     }
     // component
index 6c2d323c39974763823fcd949515d5ddf6a6fb0c..14b544de9d21f2c930b9bfe3722871bcfb5795b5 100644 (file)
@@ -10,21 +10,26 @@ export * from '@vue/observer'
 // Scheduler API
 export { nextTick } from '@vue/scheduler'
 
-// Internal API
-export {
-  createComponentInstance,
-  createComponentClassFromOptions
-} from './componentUtils'
-
 // Optional APIs
 // these are imported on-demand and can be tree-shaken
 export { applyDirectives } from './optional/directive'
 export { Provide, Inject } from './optional/context'
 export { createAsyncComponent } from './optional/asyncComponent'
 export { KeepAlive } from './optional/keepAlive'
+export { mixins } from './optional/mixin'
 
 // flags & types
 export { ComponentType, ComponentClass, FunctionalComponent } from './component'
-export * from './componentOptions'
 export { VNodeFlags, ChildrenFlags } from './flags'
 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'
index e0a6d0d4300340898cfd6a10e72dd383e1432d12..61920571ec40ae0eeae68f44b07f5f0ca004842b 100644 (file)
@@ -2,6 +2,7 @@ import { ChildrenFlags } from '../flags'
 import { createComponentVNode, Slots } from '../vdom'
 import { Component, ComponentType, ComponentClass } from '../component'
 import { unwrap } from '@vue/observer'
+import { isFunction } from '@vue/shared'
 
 interface AsyncComponentFactory {
   (): Promise<ComponentType>
@@ -21,7 +22,7 @@ type AsyncComponentOptions = AsyncComponentFactory | AsyncComponentFullOptions
 export function createAsyncComponent(
   options: AsyncComponentOptions
 ): ComponentClass {
-  if (typeof options === 'function') {
+  if (isFunction(options)) {
     options = { factory: options }
   }
 
index 7ee84bb7570ffd74bad2b9d5ed3538377180ff04..ba09cee68779556231413419983070d645619df3 100644 (file)
@@ -15,7 +15,7 @@ return applyDirectives(
 
 import { VNode, cloneVNode, VNodeData } from '../vdom'
 import { ComponentInstance } from '../component'
-import { EMPTY_OBJ } from '../utils'
+import { EMPTY_OBJ } from '@vue/shared'
 
 interface DirectiveBinding {
   instance: ComponentInstance
index 664b17770828003630d7040db47f19bf404f6822..fa973928d435de20c3ed8e100d127fff486de21b 100644 (file)
@@ -2,6 +2,7 @@ import { Component, ComponentClass, ComponentInstance } from '../component'
 import { VNode, Slots, cloneVNode } from '../vdom'
 import { VNodeFlags } from '../flags'
 import { warn } from '../warning'
+import { isString, isArray } from '@vue/shared'
 
 type MatchPattern = string | RegExp | string[] | RegExp[]
 
@@ -119,9 +120,9 @@ function getName(comp: ComponentClass): string | void {
 }
 
 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))
-  } else if (typeof pattern === 'string') {
+  } else if (isString(pattern)) {
     return pattern.split(',').indexOf(name) > -1
   } else if (pattern.test) {
     return pattern.test(name)
index 7b56416a56c6dc887a2ab7960d134c0f77e7b907..f6de28616642151e96a321446b27a67e2db48561 100644 (file)
@@ -1,4 +1,12 @@
 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> {
   new (): This
@@ -25,7 +33,19 @@ export function mixins<
   V = ExtractInstance<T>
 >(...args: T): ComponentConstructorWithMixins<V>
 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
diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts
deleted file mode 100644 (file)
index 701aa69..0000000
+++ /dev/null
@@ -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
-}
index 3db116df09e43b8e6330a046f892ad78dfccc1ba..dededddd432dc30f17949125725b125dbee8e5ce 100644 (file)
@@ -5,7 +5,14 @@ import {
 } from './component'
 import { VNodeFlags, ChildrenFlags } from './flags'
 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'
 
 // Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
@@ -98,15 +105,6 @@ export function createVNode(
   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(
   tag: string,
   data: VNodeData | null,
@@ -122,6 +120,50 @@ export function createElementVNode(
   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(
   comp: any,
   data: VNodeData | null,
@@ -134,8 +176,7 @@ export function createComponentVNode(
   let flags: VNodeFlags
 
   // flags
-  const compType = typeof comp
-  if (compType === 'object') {
+  if (isObject(comp)) {
     if (comp.functional) {
       // object literal functional
       flags = VNodeFlags.COMPONENT_FUNCTIONAL
@@ -155,7 +196,7 @@ export function createComponentVNode(
     }
   } else {
     // assumes comp is function here now
-    if (__DEV__ && compType !== 'function') {
+    if (__DEV__ && !isFunction(comp)) {
       // TODO warn invalid comp value in dev
     }
     if (comp.prototype && comp.prototype.render) {
@@ -178,14 +219,13 @@ export function createComponentVNode(
       ? ChildrenFlags.DYNAMIC_SLOTS
       : ChildrenFlags.NO_CHILDREN
     if (children != null) {
-      const childrenType = typeof children
-      if (childrenType === 'function') {
+      if (isFunction(children)) {
         // function as children
         slots = { default: children }
-      } else if (Array.isArray(children) || (children as any)._isVNode) {
+      } else if (isArray(children) || (children as any)._isVNode) {
         // direct vnode children
         slots = { default: () => children }
-      } else if (typeof children === 'object') {
+      } else if (isObject(children)) {
         // slot object as children
         slots = children
       }
@@ -313,7 +353,7 @@ export function cloneVNode(vnode: VNode, extraData?: VNodeData): VNode {
 
 function normalizeChildren(vnode: VNode, children: any) {
   let childFlags
-  if (Array.isArray(children)) {
+  if (isArray(children)) {
     const { length } = children
     if (length === 0) {
       childFlags = ChildrenFlags.NO_CHILDREN
@@ -356,7 +396,7 @@ export function normalizeVNodes(
       newChild = createTextVNode('')
     } else if (child._isVNode) {
       newChild = child.el ? cloneVNode(child) : child
-    } else if (Array.isArray(child)) {
+    } else if (isArray(child)) {
       normalizeVNodes(child, newChildren, currentPrefix + i + '|')
     } else {
       newChild = createTextVNode(child + '')
@@ -386,7 +426,7 @@ function normalizeSlots(slots: { [name: string]: any }): Slots {
 function normalizeSlot(value: any): VNode[] {
   if (value == null) {
     return [createTextVNode('')]
-  } else if (Array.isArray(value)) {
+  } else if (isArray(value)) {
     return normalizeVNodes(value)
   } else if (value._isVNode) {
     return [value]
index e7d1a814a1463af73f1d1ae3de4c8bcee833c181..7c5e73a31e2a38efba617e2b783ee181f22520cc 100644 (file)
@@ -1,5 +1,5 @@
 import { ComponentType, ComponentClass, FunctionalComponent } from './component'
-import { EMPTY_OBJ } from './utils'
+import { EMPTY_OBJ, isString } from '@vue/shared'
 import { VNode } from './vdom'
 import { Data } from './componentOptions'
 
@@ -119,7 +119,7 @@ function formatProps(props: Data) {
   const res = []
   for (const key in props) {
     const value = props[key]
-    if (typeof value === 'string') {
+    if (isString(value)) {
       res.push(`${key}=${JSON.stringify(value)}`)
     } else {
       res.push(`${key}=`, value)
index beb47e8e69bae794d3f6c1228c8c1dc24d61fdcc..0be06e3ac2c6954c294d8802e1a5db7b959c4e29 100644 (file)
@@ -2,6 +2,7 @@ import { observable, immutable, unwrap } from './index'
 import { OperationTypes } from './operations'
 import { track, trigger } from './autorun'
 import { LOCKED } from './lock'
+import { isObject } from '@vue/shared'
 
 const hasOwnProperty = Object.prototype.hasOwnProperty
 
@@ -18,7 +19,7 @@ function createGetter(isImmutable: boolean) {
       return res
     }
     track(target, OperationTypes.GET, key)
-    return res !== null && typeof res === 'object'
+    return isObject(res)
       ? isImmutable
         ? // need to lazy access immutable and observable here to avoid
           // circular dependency
index 4f0531d7f9a01c936189f70b21822e8a7e749aa1..8fd40a94cab4c17f09d5b1007a8b74bbe8d8bde9 100644 (file)
@@ -2,8 +2,8 @@ import { unwrap, observable, immutable } from './index'
 import { track, trigger } from './autorun'
 import { OperationTypes } from './operations'
 import { LOCKED } from './lock'
+import { isObject } from '@vue/shared'
 
-const isObject = (value: any) => value !== null && typeof value === 'object'
 const toObservable = (value: any) =>
   isObject(value) ? observable(value) : value
 const toImmutable = (value: any) => (isObject(value) ? immutable(value) : value)
index 7f8a9f5ab096e14c4d7597d71b79dc3dcfa0f839..836731ed43f0cc926555c96e3fa93e744bb784ea 100644 (file)
@@ -1,3 +1,4 @@
+import { isObject, EMPTY_OBJ } from '@vue/shared'
 import { mutableHandlers, immutableHandlers } from './baseHandlers'
 
 import {
@@ -28,7 +29,6 @@ export { OperationTypes } from './operations'
 export { computed, ComputedGetter } from './computed'
 export { lock, unlock } from './lock'
 
-const EMPTY_OBJ = {}
 const collectionTypes: Set<any> = new Set([Set, Map, WeakMap, WeakSet])
 const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/
 
@@ -83,7 +83,7 @@ function createObservable(
   baseHandlers: ProxyHandler<any>,
   collectionHandlers: ProxyHandler<any>
 ) {
-  if (target === null || typeof target !== 'object') {
+  if (!isObject(target)) {
     if (__DEV__) {
       console.warn(`value is not observable: ${String(target)}`)
     }
index b916242937db3c9b912ac490ef3ad21cba2cc421..4ddcd3c38ccdac7992cf79da6875728a9f3608f8 100644 (file)
@@ -1,4 +1,4 @@
-import { createRenderer, VNode, ComponentInstance } from '@vue/core'
+import { createRenderer, VNode, Component } from '@vue/core'
 import { nodeOps } from './nodeOps'
 import { patchData } from './patchData'
 import { teardownVNode } from './teardownVNode'
@@ -12,7 +12,7 @@ const { render: _render } = createRenderer({
 type publicRender = (
   node: VNode | null,
   container: HTMLElement
-) => ComponentInstance | null
+) => Component | null
 export const render = _render as publicRender
 
 // re-export everything from core
index f347064b4f8993a77a3e2f3cc342b142ed3f679f..e5c3e0ee9bf62a08bafbe75788bf4bc3c6fd9d93 100644 (file)
@@ -1,3 +1,5 @@
+import { isString } from '@vue/shared'
+
 // 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
 
@@ -5,7 +7,7 @@ export function patchStyle(el: any, prev: any, next: any, data: any) {
   const { style } = el
   if (!next) {
     el.removeAttribute('style')
-  } else if (typeof next === 'string') {
+  } else if (isString(next)) {
     style.cssText = next
   } else {
     for (const key in next) {
@@ -15,7 +17,7 @@ export function patchStyle(el: any, prev: any, next: any, data: any) {
       }
       style[key] = value
     }
-    if (prev && typeof prev !== 'string') {
+    if (prev && !isString(prev)) {
       for (const key in prev) {
         if (!next[key]) {
           style[key] = ''
index ba4063895cd68490215b38625b309ea83a1ba774..fda7490c926b5678552bf17bde503d5fd0764e7b 100644 (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 { patchData } from './patchData'
 
@@ -10,7 +10,7 @@ const { render: _render } = createRenderer({
 type publicRender = (
   node: VNode | null,
   container: TestElement
-) => ComponentInstance | null
+) => Component | null
 export const render = _render as publicRender
 
 export { serialize } from './serialize'
diff --git a/packages/shared/README.md b/packages/shared/README.md
new file mode 100644 (file)
index 0000000..6f4b137
--- /dev/null
@@ -0,0 +1 @@
+# @vue/shared
\ No newline at end of file
diff --git a/packages/shared/package.json b/packages/shared/package.json
new file mode 100644 (file)
index 0000000..5366801
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "name": "@vue/shared",
+  "private": true
+}
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
new file mode 100644 (file)
index 0000000..798f096
--- /dev/null
@@ -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)
+}
index 7bcae758689ba8adb4e1a15940ba690968c7c174..6ae92e1ee11d3b7b2314f7c80687ad3fd1042814 100644 (file)
@@ -16,7 +16,7 @@ class Vue {
     // convert it to a class
     const Component = createComponentClassFromOptions(options || {})
     const vnode = h(Component)
-    const instance = createComponentInstance(vnode, Component, null)
+    const instance = createComponentInstance(vnode, Component)
 
     function mount(el: any) {
       const dom = typeof el === 'string' ? document.querySelector(el) : el
index a7dd1924b872070b95376039cc880c778e3f7e65..4a31cbb88fabe3de355b5ad86df1ef27ca7c0fd9 100644 (file)
@@ -95,12 +95,13 @@ function createConfig(output, plugins = []) {
   // during a single build.
   hasTSChecked = true
 
+  const externals = Object.keys(aliasOptions).filter(p => p !== '@vue/shared')
+
   return {
     input: resolve(`src/index.ts`),
     // Global and Browser ESM builds inlines everything so that they can be
     // used alone.
-    external:
-      isGlobalBuild || isBrowserESMBuild ? [] : Object.keys(aliasOptions),
+    external: isGlobalBuild || isBrowserESMBuild ? [] : externals,
     plugins: [
       tsPlugin,
       aliasPlugin,
index aa1ca3dbd783bd01f7549208acc93ed35fb2a6e6..627ed740978cea306237a4a003048d3cdfb29a43 100644 (file)
@@ -1,8 +1,8 @@
 const fs = require('fs')
 
-const targets = exports.targets = fs.readdirSync('packages').filter(f => {
-  return fs.statSync(`packages/${f}`).isDirectory()
-})
+const targets = (exports.targets = fs.readdirSync('packages').filter(f => {
+  return f !== 'shared' && fs.statSync(`packages/${f}`).isDirectory()
+}))
 
 exports.fuzzyMatchTarget = partialTarget => {
   const matched = []
index 42ef91769259976158fba258d6d3757375ddc4b9..1815a49fc2a1ad793eab0e2e94cd74e276d91cb0 100644 (file)
@@ -19,6 +19,7 @@
     ],
     "rootDir": ".",
     "paths": {
+      "@vue/shared": ["packages/shared/src"],
       "@vue/core": ["packages/core/src"],
       "@vue/observer": ["packages/observer/src"],
       "@vue/scheduler": ["packages/scheduler/src"],