From 5b5a88fef8340acdf6a3ed7eb5037da877c74d16 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 26 Feb 2017 18:23:47 -0500 Subject: [PATCH 01/18] refactor TypeScript typings to use ES style exports --- types/index.d.ts | 62 ++++++++++++++++----------------- types/test/augmentation-test.ts | 2 +- types/test/options-test.ts | 2 +- types/test/plugin-test.ts | 2 +- types/test/vue-test.ts | 2 +- 5 files changed, 34 insertions(+), 36 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index 28350fcfdbe..a9a5b9a7fe0 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,37 +1,35 @@ -import * as V from "./vue"; -import * as Options from "./options"; -import * as Plugin from "./plugin"; -import * as VNode from "./vnode"; +import { Vue } from "./vue"; -// `Vue` in `export = Vue` must be a namespace -// All available types are exported via this namespace -declare namespace Vue { - export type CreateElement = V.CreateElement; +export default Vue; - export type Component = Options.Component; - export type AsyncComponent = Options.AsyncComponent; - export type ComponentOptions = Options.ComponentOptions; - export type FunctionalComponentOptions = Options.FunctionalComponentOptions; - export type RenderContext = Options.RenderContext; - export type PropOptions = Options.PropOptions; - export type ComputedOptions = Options.ComputedOptions; - export type WatchHandler = Options.WatchHandler; - export type WatchOptions = Options.WatchOptions; - export type DirectiveFunction = Options.DirectiveFunction; - export type DirectiveOptions = Options.DirectiveOptions; +export { + CreateElement +} from "./vue"; - export type PluginFunction = Plugin.PluginFunction; - export type PluginObject = Plugin.PluginObject; +export { + Component, + AsyncComponent, + ComponentOptions, + FunctionalComponentOptions, + RenderContext, + PropOptions, + ComputedOptions, + WatchHandler, + WatchOptions, + DirectiveFunction, + DirectiveOptions +} from "./options"; - export type VNodeChildren = VNode.VNodeChildren; - export type VNodeChildrenArrayContents = VNode.VNodeChildrenArrayContents; - export type VNode = VNode.VNode; - export type VNodeComponentOptions = VNode.VNodeComponentOptions; - export type VNodeData = VNode.VNodeData; - export type VNodeDirective = VNode.VNodeDirective; -} +export { + PluginFunction, + PluginObject +} from "./plugin"; -// TS cannot merge imported class with namespace, declare a subclass to bypass -declare class Vue extends V.Vue {} - -export = Vue; +export { + VNodeChildren, + VNodeChildrenArrayContents, + VNode, + VNodeComponentOptions, + VNodeData, + VNodeDirective +} from "./vnode"; diff --git a/types/test/augmentation-test.ts b/types/test/augmentation-test.ts index dd569052be4..a1915c39711 100644 --- a/types/test/augmentation-test.ts +++ b/types/test/augmentation-test.ts @@ -1,4 +1,4 @@ -import Vue = require("../index"); +import Vue from "../index"; declare module "../vue" { // add instance property and method diff --git a/types/test/options-test.ts b/types/test/options-test.ts index 61173c8408c..ab56d378135 100644 --- a/types/test/options-test.ts +++ b/types/test/options-test.ts @@ -1,4 +1,4 @@ -import Vue = require("../index"); +import Vue from "../index"; import { ComponentOptions, FunctionalComponentOptions } from "../index"; interface Component extends Vue { diff --git a/types/test/plugin-test.ts b/types/test/plugin-test.ts index eb1a0d12afa..698864434c6 100644 --- a/types/test/plugin-test.ts +++ b/types/test/plugin-test.ts @@ -1,4 +1,4 @@ -import Vue = require("../index"); +import Vue from "../index"; import { PluginFunction, PluginObject } from "../index"; class Option { diff --git a/types/test/vue-test.ts b/types/test/vue-test.ts index e2f6f38c300..e254ce28bc1 100644 --- a/types/test/vue-test.ts +++ b/types/test/vue-test.ts @@ -1,4 +1,4 @@ -import Vue = require("../index"); +import Vue from "../index"; class Test extends Vue { a: number; From 385a74463bba9b76c9f2cfd6452ee40590a9d56f Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 8 Mar 2017 14:55:37 -0800 Subject: [PATCH 02/18] Allow functions in 'methods' & 'computed' to view themselves, as well as members from 'data' and the Vue instance. --- types/options.d.ts | 69 +++++++++++++++++------------- types/test/augmentation-test.ts | 10 ++--- types/tsconfig.json | 8 ++++ types/vue.d.ts | 74 ++++++++++++++++++--------------- 4 files changed, 93 insertions(+), 68 deletions(-) create mode 100644 types/tsconfig.json diff --git a/types/options.d.ts b/types/options.d.ts index de1b20d995d..6fd1a83ec69 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -1,40 +1,50 @@ -import { Vue, CreateElement } from "./vue"; +import { Vue, CreateElement, AnyVue } from "./vue"; import { VNode, VNodeData, VNodeDirective } from "./vnode"; type Constructor = { new (...args: any[]): any; } -export type Component = typeof Vue | ComponentOptions | FunctionalComponentOptions; -export type AsyncComponent = ( - resolve: (component: Component) => void, +export type Component = typeof Vue | ComponentOptions | FunctionalComponentOptions; +export type AsyncComponent = ( + resolve: (component: Component) => void, reject: (reason?: any) => void -) => Promise | Component | void; +) => Promise> | Component | void; + +/** + * When the `Computed` type parameter on `ComponentOptions` is inferred, + * it should have a property with the return type of every get-accessor. + * Since there isn't a way to query for the return type of a function, we allow TypeScript + * to infer from the shape of `Accessors` and work backwards. + */ +export type Accessors = { + [K in keyof T]: (() => T[K]) | ComputedOptions +} -export interface ComponentOptions { - data?: Object | ((this: V) => Object); +export interface ComponentOptions { + data?: Data | (() => Data); props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] }; propsData?: Object; - computed?: { [key: string]: ((this: V) => any) | ComputedOptions }; - methods?: { [key: string]: (this: V, ...args: any[]) => any }; - watch?: { [key: string]: ({ handler: WatchHandler } & WatchOptions) | WatchHandler | string }; + computed?: Accessors; + methods?: Methods; + watch?: { [key: string]: ({ handler: WatchHandler } & WatchOptions) | WatchHandler | string }; el?: Element | String; template?: string; - render?(this: V, createElement: CreateElement): VNode; + render?(createElement: CreateElement): VNode; renderError?: (h: () => VNode, err: Error) => VNode; staticRenderFns?: ((createElement: CreateElement) => VNode)[]; - beforeCreate?(this: V): void; - created?(this: V): void; - beforeDestroy?(this: V): void; - destroyed?(this: V): void; - beforeMount?(this: V): void; - mounted?(this: V): void; - beforeUpdate?(this: V): void; - updated?(this: V): void; - activated?(this: V): void; - deactivated?(this: V): void; + beforeCreate?(): void; + created?(): void; + beforeDestroy?(): void; + destroyed?(): void; + beforeMount?(): void; + mounted?(): void; + beforeUpdate?(): void; + updated?(): void; + activated?(): void; + deactivated?(): void; directives?: { [key: string]: DirectiveOptions | DirectiveFunction }; components?: { [key: string]: Component | AsyncComponent }; @@ -49,10 +59,11 @@ export interface ComponentOptions { event?: string; }; - parent?: Vue; - mixins?: (ComponentOptions | typeof Vue)[]; + parent?: AnyVue; + mixins?: (ComponentOptions | typeof Vue)[]; name?: string; - extends?: ComponentOptions | typeof Vue; + // TODO: support properly inferred 'extends' + extends?: ComponentOptions | typeof Vue; delimiters?: [string, string]; } @@ -68,7 +79,7 @@ export interface RenderContext { children: VNode[]; slots(): any; data: VNodeData; - parent: Vue; + parent: AnyVue; } export interface PropOptions { @@ -78,13 +89,13 @@ export interface PropOptions { validator?(value: any): boolean; } -export interface ComputedOptions { - get?(this: V): any; - set?(this: V, value: any): void; +export interface ComputedOptions { + get?(): T; + set?(value: T): void; cache?: boolean; } -export type WatchHandler = (this: V, val: T, oldVal: T) => void; +export type WatchHandler = (val: T, oldVal: T) => void; export interface WatchOptions { deep?: boolean; diff --git a/types/test/augmentation-test.ts b/types/test/augmentation-test.ts index a1915c39711..b234450f728 100644 --- a/types/test/augmentation-test.ts +++ b/types/test/augmentation-test.ts @@ -2,21 +2,21 @@ import Vue from "../index"; declare module "../vue" { // add instance property and method - interface Vue { + interface Vue { $instanceProperty: string; $instanceMethod(): void; } // add static property and method - namespace Vue { - const staticProperty: string; - function staticMethod(): void; + interface VueConstructor { + staticProperty: string; + staticMethod(): void; } } // augment ComponentOptions declare module "../options" { - interface ComponentOptions { + interface ComponentOptions { foo?: string; } } diff --git a/types/tsconfig.json b/types/tsconfig.json new file mode 100644 index 00000000000..9fac90b3f45 --- /dev/null +++ b/types/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "strict": true, + "lib": [ + "es2015", "dom" + ] + } +} \ No newline at end of file diff --git a/types/vue.d.ts b/types/vue.d.ts index 7b5fdc23423..bd83e1a7087 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -6,7 +6,7 @@ import { WatchOptions, WatchHandler, DirectiveOptions, - DirectiveFunction + DirectiveFunction, } from "./options"; import { VNode, VNodeData, VNodeChildren, ScopedSlot } from "./vnode"; import { PluginFunction, PluginObject } from "./plugin"; @@ -28,17 +28,17 @@ export type CreateElement = { (tag: AsyncComponent, data?: VNodeData, children?: VNodeChildren): VNode; } -export declare class Vue { - - constructor(options?: ComponentOptions); +interface AnyVue extends Vue { +} - $data: Object; +export interface Vue { + $data: Data; readonly $el: HTMLElement; - readonly $options: ComponentOptions; - readonly $parent: Vue; - readonly $root: Vue; - readonly $children: Vue[]; - readonly $refs: { [key: string]: Vue | Element | Vue[] | Element[]}; + readonly $options: ComponentOptions; + readonly $parent: AnyVue; + readonly $root: AnyVue; + readonly $children: AnyVue[]; + readonly $refs: { [key: string]: AnyVue | Element | AnyVue[] | Element[] }; readonly $slots: { [key: string]: VNode[] }; readonly $scopedSlots: { [key: string]: ScopedSlot }; readonly $isServer: boolean; @@ -51,12 +51,12 @@ export declare class Vue { $delete: typeof Vue.delete; $watch( expOrFn: string, - callback: WatchHandler, + callback: WatchHandler, options?: WatchOptions ): (() => void); $watch( expOrFn: (this: this) => T, - callback: WatchHandler, + callback: WatchHandler, options?: WatchOptions ): (() => void); $on(event: string | string[], callback: Function): this; @@ -66,36 +66,42 @@ export declare class Vue { $nextTick(callback: (this: this) => void): void; $nextTick(): Promise; $createElement: CreateElement; +} - static config: { - silent: boolean; - optionMergeStrategies: any; - devtools: boolean; - productionTip: boolean; - performance: boolean; - errorHandler(err: Error, vm: Vue, info: string): void; - ignoredElements: string[]; - keyCodes: { [key: string]: number }; - } +export interface VueConstructor { + new (options?: ComponentOptions, Computed> & ThisType): Data & Methods & Computed & Vue; - static extend(options: ComponentOptions | FunctionalComponentOptions): typeof Vue; - static nextTick(callback: () => void, context?: any[]): void; - static nextTick(): Promise - static set(object: Object, key: string, value: T): T; - static set(array: T[], key: number, value: T): T; - static delete(object: Object, key: string): void; + extend(this: V, options: ComponentOptions | FunctionalComponentOptions): ((...args: any[]) => Vue) & V; + nextTick(callback: () => void, context?: any[]): void; + nextTick(): Promise + set(object: Object, key: string, value: T): T; + set(array: T[], key: number, value: T): T; + delete(object: Object, key: string): void; - static directive( + directive( id: string, definition?: DirectiveOptions | DirectiveFunction ): DirectiveOptions; - static filter(id: string, definition?: Function): Function; - static component(id: string, definition?: Component | AsyncComponent): typeof Vue; + filter(id: string, definition?: Function): Function; + component(id: string, definition?: Component | AsyncComponent): typeof Vue; - static use(plugin: PluginObject | PluginFunction, options?: T): void; - static mixin(mixin: typeof Vue | ComponentOptions): void; - static compile(template: string): { + use(plugin: PluginObject | PluginFunction, options?: T): void; + mixin(mixin: typeof Vue | ComponentOptions): void; + compile(template: string): { render(createElement: typeof Vue.prototype.$createElement): VNode; staticRenderFns: (() => VNode)[]; }; + + config: { + silent: boolean; + optionMergeStrategies: any; + devtools: boolean; + productionTip: boolean; + performance: boolean; + errorHandler(err: Error, vm: AnyVue, info: string): void; + ignoredElements: string[]; + keyCodes: { [key: string]: number }; + } } + +export const Vue: VueConstructor; \ No newline at end of file From 8cd5b9cde328124130211a151191a6ca0aaa712c Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 16 Mar 2017 13:24:56 -0700 Subject: [PATCH 03/18] Got 'new Vue(...)', 'Vue.extend(...)', and 'Vue.component(...)' working. --- types/options.d.ts | 79 ++++++++++++++++++++++++++++++++++++++-------- types/vnode.d.ts | 6 ++-- types/vue.d.ts | 48 ++++++++++++++++++++-------- 3 files changed, 103 insertions(+), 30 deletions(-) diff --git a/types/options.d.ts b/types/options.d.ts index 6fd1a83ec69..da93715779d 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -5,11 +5,20 @@ type Constructor = { new (...args: any[]): any; } -export type Component = typeof Vue | ComponentOptions | FunctionalComponentOptions; -export type AsyncComponent = ( - resolve: (component: Component) => void, +export type Component = + typeof Vue | + FunctionalOrStandardComponentOptions; + +export type AsyncComponent = ( + resolve: (component: Component) => void, + reject: (reason?: any) => void +) => Promise> | Component | void; + +export type MyAsyncComponent = ( + resolve: (component: Component) => void, reject: (reason?: any) => void -) => Promise> | Component | void; +) => Promise> | Component | void; + /** * When the `Computed` type parameter on `ComponentOptions` is inferred, @@ -21,9 +30,49 @@ export type Accessors = { [K in keyof T]: (() => T[K]) | ComputedOptions } -export interface ComponentOptions { +/** + * A general type that + * + * - Describes (non-functional) component options in Vue. + * - Gives the appropriate type to `this` in each method in objects of this type. + * + * Use this only if the following two types become too cumbersome. + */ +export type ThisTypedComponentOptions>> = + object & + ComponentOptions> & + ThisType & Instance>; + +/** + * A specialized version of `ThisTypedComponentOptions`. + * This type should be used when a parameter type only contains an array of strings for its `props` value. + */ +export type ThisTypedComponentOptionsWithArrayProps> = + object & + ComponentOptions & + ThisType & Instance>; + +/** + * A specialized version of `ThisTypedComponentOptions`. + * This type should be used when a parameter type only contains an object mapped to `PropOptions` for its `props` value. + */ +export type ThisTypedComponentOptionsWithRecordProps> = + object & + ComponentOptions & + ThisType & Instance>; + +/** + * A helper type that describes options for either functional or non-functional components. + * Useful for `Vue.extend` and `Vue.component`. + */ +export type FunctionalOrStandardComponentOptions = + ThisTypedComponentOptions | + FunctionalComponentOptions, Record>; + + +export interface ComponentOptions { data?: Data | (() => Data); - props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] }; + props?: Props; propsData?: Object; computed?: Accessors; methods?: Methods; @@ -47,7 +96,7 @@ export interface ComponentOptions { deactivated?(): void; directives?: { [key: string]: DirectiveOptions | DirectiveFunction }; - components?: { [key: string]: Component | AsyncComponent }; + components?: { [key: string]: Component | AsyncComponent }; transitions?: { [key: string]: Object }; filters?: { [key: string]: Function }; @@ -60,32 +109,34 @@ export interface ComponentOptions { }; parent?: AnyVue; - mixins?: (ComponentOptions | typeof Vue)[]; + mixins?: (ComponentOptions | typeof Vue)[]; name?: string; // TODO: support properly inferred 'extends' - extends?: ComponentOptions | typeof Vue; + extends?: ComponentOptions | typeof Vue; delimiters?: [string, string]; } -export interface FunctionalComponentOptions { +export interface FunctionalComponentOptions { props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] }; functional: boolean; - render(this: never, createElement: CreateElement, context: RenderContext): VNode; + render(this: never, createElement: CreateElement, context: RenderContext): VNode; name?: string; } -export interface RenderContext { - props: any; +export interface RenderContext { + props: Props; children: VNode[]; slots(): any; data: VNodeData; parent: AnyVue; } +export type PropValidator = PropOptions | Constructor | Constructor[]; + export interface PropOptions { type?: Constructor | Constructor[] | null; required?: boolean; - default?: any; + default?: string | number | boolean | null | undefined | (() => object); validator?(value: any): boolean; } diff --git a/types/vnode.d.ts b/types/vnode.d.ts index ae72065f9b8..3262fcdb46f 100644 --- a/types/vnode.d.ts +++ b/types/vnode.d.ts @@ -1,4 +1,4 @@ -import { Vue } from "./vue"; +import { Vue, AnyVue } from "./vue"; export type ScopedSlot = (props: any) => VNodeChildrenArrayContents | string; @@ -14,10 +14,10 @@ export interface VNode { text?: string; elm?: Node; ns?: string; - context?: Vue; + context?: AnyVue; key?: string | number; componentOptions?: VNodeComponentOptions; - componentInstance?: Vue; + componentInstance?: AnyVue; parent?: VNode; raw?: boolean; isStatic?: boolean; diff --git a/types/vue.d.ts b/types/vue.d.ts index bd83e1a7087..6a483f983af 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -7,6 +7,10 @@ import { WatchHandler, DirectiveOptions, DirectiveFunction, + PropValidator, + ThisTypedComponentOptionsWithArrayProps, + ThisTypedComponentOptionsWithRecordProps, + MyAsyncComponent, } from "./options"; import { VNode, VNodeData, VNodeChildren, ScopedSlot } from "./vnode"; import { PluginFunction, PluginObject } from "./plugin"; @@ -20,21 +24,22 @@ export type CreateElement = { (tag: string, data?: VNodeData, children?: VNodeChildren): VNode; // component constructor or options - (tag: Component, children: VNodeChildren): VNode; - (tag: Component, data?: VNodeData, children?: VNodeChildren): VNode; + (tag: Component, children: VNodeChildren): VNode; + (tag: Component, data?: VNodeData, children?: VNodeChildren): VNode; // async component - (tag: AsyncComponent, children: VNodeChildren): VNode; - (tag: AsyncComponent, data?: VNodeData, children?: VNodeChildren): VNode; + (tag: AsyncComponent, children: VNodeChildren): VNode; + (tag: AsyncComponent, data?: VNodeData, children?: VNodeChildren): VNode; } -interface AnyVue extends Vue { -} +export interface AnyVue extends Vue {} + +export interface MinVue extends Vue {} -export interface Vue { +export interface Vue { $data: Data; readonly $el: HTMLElement; - readonly $options: ComponentOptions; + readonly $options: ComponentOptions; readonly $parent: AnyVue; readonly $root: AnyVue; readonly $children: AnyVue[]; @@ -42,7 +47,7 @@ export interface Vue { readonly $slots: { [key: string]: VNode[] }; readonly $scopedSlots: { [key: string]: ScopedSlot }; readonly $isServer: boolean; - readonly $props: any; + readonly $props: Props; $mount(elementOrSelector?: Element | String, hydrating?: boolean): this; $forceUpdate(): void; @@ -68,10 +73,19 @@ export interface Vue { $createElement: CreateElement; } +export type CombinedVueInstance = Data & Methods & Computed & Props & Vue +export type ExtendedVue = + (new (...args: any[]) => CombinedVueInstance) & + Constructor; + export interface VueConstructor { - new (options?: ComponentOptions, Computed> & ThisType): Data & Methods & Computed & Vue; + new (options?: ThisTypedComponentOptionsWithArrayProps): CombinedVueInstance>; + new >(options?: ThisTypedComponentOptionsWithRecordProps): CombinedVueInstance>; + + extend(this: VC, options: FunctionalComponentOptions>): ExtendedVue>; + extend(this: VC, options: ThisTypedComponentOptionsWithArrayProps): ExtendedVue>; + extend>(this: VC, options?: ThisTypedComponentOptionsWithRecordProps): ExtendedVue>; - extend(this: V, options: ComponentOptions | FunctionalComponentOptions): ((...args: any[]) => Vue) & V; nextTick(callback: () => void, context?: any[]): void; nextTick(): Promise set(object: Object, key: string, value: T): T; @@ -83,10 +97,18 @@ export interface VueConstructor { definition?: DirectiveOptions | DirectiveFunction ): DirectiveOptions; filter(id: string, definition?: Function): Function; - component(id: string, definition?: Component | AsyncComponent): typeof Vue; + + component(id: string): VueConstructor; + component(id: string, constructor: VC): VC; + component(this: VC, id: string, definition: AsyncComponent): ExtendedVue>; + component(this: VC, id: string, definition: FunctionalComponentOptions>): ExtendedVue>; + component(this: VC, id: string, definition: ThisTypedComponentOptionsWithArrayProps): ExtendedVue>; + component>(this: VC, id: string, definition?: ThisTypedComponentOptionsWithRecordProps): ExtendedVue>; + //component(this: VC, id: string, definition: Component): ExtendedVue>; + //component(id: string, definition: Component | AsyncComponent): typeof Vue; use(plugin: PluginObject | PluginFunction, options?: T): void; - mixin(mixin: typeof Vue | ComponentOptions): void; + mixin(mixin: typeof Vue | ComponentOptions): void; compile(template: string): { render(createElement: typeof Vue.prototype.$createElement): VNode; staticRenderFns: (() => VNode)[]; From 540a38fb21adb7a7bc394c65e23e6cffb36cd867 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 27 Mar 2017 14:58:02 -0700 Subject: [PATCH 04/18] Made it so that any 'data' function can only access 'props' and base Vue instance members. --- types/options.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/types/options.d.ts b/types/options.d.ts index da93715779d..62d62371bd2 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -40,7 +40,7 @@ export type Accessors = { */ export type ThisTypedComponentOptions>> = object & - ComponentOptions> & + ComponentOptions & AnyVue) => Data), Methods, Computed, PropNames[] | Record> & ThisType & Instance>; /** @@ -49,7 +49,7 @@ export type ThisTypedComponentOptions> = object & - ComponentOptions & + ComponentOptions & AnyVue) => Data), Methods, Computed, PropNames[]> & ThisType & Instance>; /** @@ -58,7 +58,7 @@ export type ThisTypedComponentOptionsWithArrayProps> = object & - ComponentOptions & + ComponentOptions & AnyVue) => Data), Methods, Computed, Props> & ThisType & Instance>; /** @@ -71,7 +71,7 @@ export type FunctionalOrStandardComponentOptions { - data?: Data | (() => Data); + data?: Data; props?: Props; propsData?: Object; computed?: Accessors; From f34f4f6c4d278437a9d7eedc9d52c0fb59851980 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 1 Jun 2017 14:49:44 -0700 Subject: [PATCH 05/18] Improved defaults, fixed overloads and types for functional components, got rid of AnyVue. --- types/options.d.ts | 44 ++++++++++++++++++-------------------------- types/vnode.d.ts | 4 ++-- types/vue.d.ts | 28 ++++++++++++---------------- 3 files changed, 32 insertions(+), 44 deletions(-) diff --git a/types/options.d.ts b/types/options.d.ts index 62d62371bd2..ef1b42577d3 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -1,4 +1,4 @@ -import { Vue, CreateElement, AnyVue } from "./vue"; +import { Vue, CreateElement, CombinedVueInstance } from "./vue"; import { VNode, VNodeData, VNodeDirective } from "./vnode"; type Constructor = { @@ -14,12 +14,6 @@ export type AsyncComponent = reject: (reason?: any) => void ) => Promise> | Component | void; -export type MyAsyncComponent = ( - resolve: (component: Component) => void, - reject: (reason?: any) => void -) => Promise> | Component | void; - - /** * When the `Computed` type parameter on `ComponentOptions` is inferred, * it should have a property with the return type of every get-accessor. @@ -31,35 +25,33 @@ export type Accessors = { } /** - * A general type that - * - * - Describes (non-functional) component options in Vue. - * - Gives the appropriate type to `this` in each method in objects of this type. + * A general type that describes non-functional component options in Vue. * - * Use this only if the following two types become too cumbersome. + * While `ThisTypedComponentOptionsWithArrayProps` and `ThisTypedComponentOptionsWithRecordProps` will + * lead to more accurate inferences, you can use this if the two are too cumbersome. */ -export type ThisTypedComponentOptions>> = +export type ThisTypedComponentOptions = object & - ComponentOptions & AnyVue) => Data), Methods, Computed, PropNames[] | Record> & - ThisType & Instance>; + ComponentOptions & Vue) => Data), Methods, Computed, PropNames[] | Record> & + ThisType>>; /** * A specialized version of `ThisTypedComponentOptions`. * This type should be used when a parameter type only contains an array of strings for its `props` value. */ -export type ThisTypedComponentOptionsWithArrayProps> = +export type ThisTypedComponentOptionsWithArrayProps = object & - ComponentOptions & AnyVue) => Data), Methods, Computed, PropNames[]> & - ThisType & Instance>; + ComponentOptions & Vue) => Data), Methods, Computed, PropNames[]> & + ThisType>>; /** * A specialized version of `ThisTypedComponentOptions`. * This type should be used when a parameter type only contains an object mapped to `PropOptions` for its `props` value. */ -export type ThisTypedComponentOptionsWithRecordProps> = +export type ThisTypedComponentOptionsWithRecordProps = object & - ComponentOptions & AnyVue) => Data), Methods, Computed, Props> & - ThisType & Instance>; + ComponentOptions & Vue) => Data), Methods, Computed, Props> & + ThisType>; /** * A helper type that describes options for either functional or non-functional components. @@ -108,7 +100,7 @@ export interface ComponentOptions { event?: string; }; - parent?: AnyVue; + parent?: Vue; mixins?: (ComponentOptions | typeof Vue)[]; name?: string; // TODO: support properly inferred 'extends' @@ -116,10 +108,10 @@ export interface ComponentOptions { delimiters?: [string, string]; } -export interface FunctionalComponentOptions { - props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] }; +export interface FunctionalComponentOptions { + props?: Props; functional: boolean; - render(this: never, createElement: CreateElement, context: RenderContext): VNode; + render(this: undefined, createElement: CreateElement, context: RenderContext): VNode; name?: string; } @@ -128,7 +120,7 @@ export interface RenderContext { children: VNode[]; slots(): any; data: VNodeData; - parent: AnyVue; + parent: Vue; } export type PropValidator = PropOptions | Constructor | Constructor[]; diff --git a/types/vnode.d.ts b/types/vnode.d.ts index 3262fcdb46f..40eb1c0c589 100644 --- a/types/vnode.d.ts +++ b/types/vnode.d.ts @@ -14,10 +14,10 @@ export interface VNode { text?: string; elm?: Node; ns?: string; - context?: AnyVue; + context?: Vue; key?: string | number; componentOptions?: VNodeComponentOptions; - componentInstance?: AnyVue; + componentInstance?: Vue; parent?: VNode; raw?: boolean; isStatic?: boolean; diff --git a/types/vue.d.ts b/types/vue.d.ts index 6a483f983af..ef61ffb9a30 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -3,14 +3,14 @@ import { AsyncComponent, ComponentOptions, FunctionalComponentOptions, - WatchOptions, + WatchOptionsWithHandler, WatchHandler, DirectiveOptions, DirectiveFunction, PropValidator, ThisTypedComponentOptionsWithArrayProps, ThisTypedComponentOptionsWithRecordProps, - MyAsyncComponent, + WatchOptions, } from "./options"; import { VNode, VNodeData, VNodeChildren, ScopedSlot } from "./vnode"; import { PluginFunction, PluginObject } from "./plugin"; @@ -32,18 +32,14 @@ export type CreateElement = { (tag: AsyncComponent, data?: VNodeData, children?: VNodeChildren): VNode; } -export interface AnyVue extends Vue {} - -export interface MinVue extends Vue {} - -export interface Vue { +export interface Vue { $data: Data; readonly $el: HTMLElement; readonly $options: ComponentOptions; - readonly $parent: AnyVue; - readonly $root: AnyVue; - readonly $children: AnyVue[]; - readonly $refs: { [key: string]: AnyVue | Element | AnyVue[] | Element[] }; + readonly $parent: Vue; + readonly $root: Vue; + readonly $children: Vue[]; + readonly $refs: { [key: string]: Vue | Element | Vue[] | Element[] }; readonly $slots: { [key: string]: VNode[] }; readonly $scopedSlots: { [key: string]: ScopedSlot }; readonly $isServer: boolean; @@ -82,7 +78,8 @@ export interface VueConstructor { new (options?: ThisTypedComponentOptionsWithArrayProps): CombinedVueInstance>; new >(options?: ThisTypedComponentOptionsWithRecordProps): CombinedVueInstance>; - extend(this: VC, options: FunctionalComponentOptions>): ExtendedVue>; + extend(this: VC, definition: FunctionalComponentOptions>): ExtendedVue>; + extend>(this: VC, definition: FunctionalComponentOptions>): ExtendedVue>; extend(this: VC, options: ThisTypedComponentOptionsWithArrayProps): ExtendedVue>; extend>(this: VC, options?: ThisTypedComponentOptionsWithRecordProps): ExtendedVue>; @@ -101,11 +98,10 @@ export interface VueConstructor { component(id: string): VueConstructor; component(id: string, constructor: VC): VC; component(this: VC, id: string, definition: AsyncComponent): ExtendedVue>; - component(this: VC, id: string, definition: FunctionalComponentOptions>): ExtendedVue>; + component(this: VC, id: string, definition: FunctionalComponentOptions>): ExtendedVue>; + component>(this: VC, id: string, definition: FunctionalComponentOptions>): ExtendedVue>; component(this: VC, id: string, definition: ThisTypedComponentOptionsWithArrayProps): ExtendedVue>; component>(this: VC, id: string, definition?: ThisTypedComponentOptionsWithRecordProps): ExtendedVue>; - //component(this: VC, id: string, definition: Component): ExtendedVue>; - //component(id: string, definition: Component | AsyncComponent): typeof Vue; use(plugin: PluginObject | PluginFunction, options?: T): void; mixin(mixin: typeof Vue | ComponentOptions): void; @@ -120,7 +116,7 @@ export interface VueConstructor { devtools: boolean; productionTip: boolean; performance: boolean; - errorHandler(err: Error, vm: AnyVue, info: string): void; + errorHandler(err: Error, vm: Vue, info: string): void; ignoredElements: string[]; keyCodes: { [key: string]: number }; } From b1f40ce08ad58defe71605ae47b63efb8d49478e Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 1 Jun 2017 15:36:16 -0700 Subject: [PATCH 06/18] Condensed declaration of 'watch'. --- types/index.d.ts | 1 + types/options.d.ts | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index a9a5b9a7fe0..da58517f42f 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -16,6 +16,7 @@ export { ComputedOptions, WatchHandler, WatchOptions, + WatchOptionsWithHandler, DirectiveFunction, DirectiveOptions } from "./options"; diff --git a/types/options.d.ts b/types/options.d.ts index ef1b42577d3..4d6df98e33c 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -68,7 +68,7 @@ export interface ComponentOptions { propsData?: Object; computed?: Accessors; methods?: Methods; - watch?: { [key: string]: ({ handler: WatchHandler } & WatchOptions) | WatchHandler | string }; + watch?: Record | WatchHandler | string>; el?: Element | String; template?: string; @@ -145,6 +145,10 @@ export interface WatchOptions { immediate?: boolean; } +export interface WatchOptionsWithHandler extends WatchOptions { + handler: WatchHandler; +} + export type DirectiveFunction = ( el: HTMLElement, binding: VNodeDirective, From 355ff757e572df8549e7cb07a309fb4ef957db68 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 1 Jun 2017 16:06:45 -0700 Subject: [PATCH 07/18] Added two tests for 'extend'. --- types/test/vue-test.ts | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/types/test/vue-test.ts b/types/test/vue-test.ts index e254ce28bc1..226b4869cd1 100644 --- a/types/test/vue-test.ts +++ b/types/test/vue-test.ts @@ -1,6 +1,6 @@ import Vue from "../index"; -class Test extends Vue { +class Test extends Vue { a: number; testProperties() { @@ -17,9 +17,9 @@ class Test extends Vue { // test property reification $refs: { - vue: Vue, + vue: Vue, element: HTMLInputElement, - vues: Vue[], + vues: Vue[], elements: HTMLInputElement[] } testReification() { @@ -81,9 +81,41 @@ class Test extends Vue { this.directive("", {bind() {}}); this.filter("", (value: number) => value); this.component("", { data: () => ({}) }); - this.component("", { functional: true }); + this.component("", { functional: true, render(h) { return h("div", "hello!") } }); this.use; this.mixin(Test); this.compile("
{{ message }}
"); } } + +const HelloWorldComponent = Vue.extend({ + props: ["name"], + data() { + return { + message: "Hello " + this.name, + } + }, + computed: { + shouted(): string { + return this.message.toUpperCase(); + } + }, + methods: { + getMoreExcited() { + this.message += "!"; + } + }, + watch: { + message(a: string) { + console.log(`Message ${this.message} was changed!`); + } + } +}); + +const FunctionalHelloWorldComponent = Vue.extend({ + functional: true, + props: ["name"], + render(createElement, ctxt) { + return createElement("div", "Hello " + ctxt.props.name) + } +}) \ No newline at end of file From bc5400783f1cf6f0082f7e6c6fd7290d238fc9b6 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 1 Jun 2017 16:29:10 -0700 Subject: [PATCH 08/18] .\types\options.d.ts --- types/options.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/options.d.ts b/types/options.d.ts index 4d6df98e33c..53fc8dc8884 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -87,7 +87,7 @@ export interface ComponentOptions { activated?(): void; deactivated?(): void; - directives?: { [key: string]: DirectiveOptions | DirectiveFunction }; + directives?: { [key: string]: DirectiveFunction | DirectiveOptions }; components?: { [key: string]: Component | AsyncComponent }; transitions?: { [key: string]: Object }; filters?: { [key: string]: Function }; From e7ea5bb9c06d2253e156cc60499a0dbd60f56bb0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 1 Jun 2017 16:33:28 -0700 Subject: [PATCH 09/18] Updated tests, tighted strictness. --- types/test/augmentation-test.ts | 2 +- types/test/options-test.ts | 27 ++++++++++++++------------- types/test/tsconfig.json | 3 +-- types/tsconfig.json | 5 ++++- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/types/test/augmentation-test.ts b/types/test/augmentation-test.ts index b234450f728..3af0c20cb7b 100644 --- a/types/test/augmentation-test.ts +++ b/types/test/augmentation-test.ts @@ -16,7 +16,7 @@ declare module "../vue" { // augment ComponentOptions declare module "../options" { - interface ComponentOptions { + interface ComponentOptions { foo?: string; } } diff --git a/types/test/options-test.ts b/types/test/options-test.ts index ab56d378135..a97e4995fa0 100644 --- a/types/test/options-test.ts +++ b/types/test/options-test.ts @@ -1,5 +1,6 @@ import Vue from "../index"; import { ComponentOptions, FunctionalComponentOptions } from "../index"; +import { CreateElement } from "../vue"; interface Component extends Vue { a: number; @@ -19,7 +20,7 @@ Vue.component('component', { type: String, default: 0, required: true, - validator(value) { + validator(value: number) { return value > 0; } } @@ -91,18 +92,18 @@ Vue.component('component', { createElement(), createElement("div", "message"), createElement(Vue.component("component")), - createElement({} as ComponentOptions), + createElement({} as ComponentOptions), createElement({ functional: true }), createElement(() => Vue.component("component")), - createElement(() => ( {} as ComponentOptions )), + createElement(() => ( {} as ComponentOptions )), createElement(() => { return new Promise((resolve) => { - resolve({} as ComponentOptions); + resolve({} as ComponentOptions); }) }), createElement((resolve, reject) => { - resolve({} as ComponentOptions); + resolve({} as ComponentOptions); reject(); }), @@ -147,7 +148,7 @@ Vue.component('component', { }, components: { a: Vue.component(""), - b: {} as ComponentOptions + b: {} as ComponentOptions }, transitions: {}, filters: { @@ -156,11 +157,11 @@ Vue.component('component', { } }, parent: new Vue, - mixins: [Vue.component(""), ({} as ComponentOptions)], + mixins: [Vue.component(""), ({} as ComponentOptions)], name: "Component", - extends: {} as ComponentOptions, + extends: {} as ComponentOptions, delimiters: ["${", "}"] -} as ComponentOptions); +}); Vue.component('component-with-scoped-slot', { render (h) { @@ -183,15 +184,15 @@ Vue.component('component-with-scoped-slot', { }, components: { child: { - render (h) { + render (this: Vue, h: CreateElement) { return h('div', [ this.$scopedSlots['default']({ msg: 'hi' }), this.$scopedSlots['item']({ msg: 'hello' }) ]) } - } as ComponentOptions + } } -} as ComponentOptions) +}) Vue.component('functional-component', { props: ['prop'], @@ -204,7 +205,7 @@ Vue.component('functional-component', { context.parent; return createElement("div", {}, context.children); } -} as FunctionalComponentOptions); +}); Vue.component("async-component", (resolve, reject) => { setTimeout(() => { diff --git a/types/test/tsconfig.json b/types/test/tsconfig.json index 68aca8c1f1e..06ac613a030 100644 --- a/types/test/tsconfig.json +++ b/types/test/tsconfig.json @@ -8,8 +8,7 @@ "es2015.core" ], "module": "commonjs", - "noImplicitAny": true, - "strictNullChecks": true, + "strict": true, "noEmit": true }, "files": [ diff --git a/types/tsconfig.json b/types/tsconfig.json index 9fac90b3f45..dc2f0455c71 100644 --- a/types/tsconfig.json +++ b/types/tsconfig.json @@ -4,5 +4,8 @@ "lib": [ "es2015", "dom" ] - } + }, + "include": [ + "./*.ts" + ] } \ No newline at end of file From d78d14b832b2e42d44bdcb3a3f89d18f8e9845dc Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 14 Jun 2017 15:20:06 -0700 Subject: [PATCH 10/18] Made the Vue instance non-generic, made readonly, augmented tests. --- types/test/augmentation-test.ts | 14 ++++++++++++-- types/test/vue-test.ts | 4 ++-- types/vnode.d.ts | 2 +- types/vue.d.ts | 10 +++++----- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/types/test/augmentation-test.ts b/types/test/augmentation-test.ts index 3af0c20cb7b..50acc17bbfe 100644 --- a/types/test/augmentation-test.ts +++ b/types/test/augmentation-test.ts @@ -2,7 +2,7 @@ import Vue from "../index"; declare module "../vue" { // add instance property and method - interface Vue { + interface Vue { $instanceProperty: string; $instanceMethod(): void; } @@ -22,10 +22,20 @@ declare module "../options" { } const vm = new Vue({ + props: ["bar"], data: { a: true }, - foo: "foo" + methods: { + foo() { + this.a = false; + } + }, + computed: { + BAR(): string { + return this.bar.toUpperCase(); + } + } }); vm.$instanceProperty; diff --git a/types/test/vue-test.ts b/types/test/vue-test.ts index c507492fcb7..dec78cfdd27 100644 --- a/types/test/vue-test.ts +++ b/types/test/vue-test.ts @@ -18,9 +18,9 @@ class Test extends Vue { // test property reification $refs: { - vue: Vue, + vue: Vue, element: HTMLInputElement, - vues: Vue[], + vues: Vue[], elements: HTMLInputElement[] } testReification() { diff --git a/types/vnode.d.ts b/types/vnode.d.ts index 40eb1c0c589..ae72065f9b8 100644 --- a/types/vnode.d.ts +++ b/types/vnode.d.ts @@ -1,4 +1,4 @@ -import { Vue, AnyVue } from "./vue"; +import { Vue } from "./vue"; export type ScopedSlot = (props: any) => VNodeChildrenArrayContents | string; diff --git a/types/vue.d.ts b/types/vue.d.ts index 2ed784da362..59b94cec12b 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -32,10 +32,9 @@ export type CreateElement = { (tag: AsyncComponent, data?: VNodeData, children?: VNodeChildren): VNode; } -export interface Vue { - $data: Data; +export interface Vue { readonly $el: HTMLElement; - readonly $options: ComponentOptions; + readonly $options: ComponentOptions; readonly $parent: Vue; readonly $root: Vue; readonly $children: Vue[]; @@ -43,7 +42,8 @@ export interface Vue; + readonly $props: Record; readonly $ssrContext: any; $mount(elementOrSelector?: Element | String, hydrating?: boolean): this; @@ -70,7 +70,7 @@ export interface Vue = Data & Methods & Computed & Props & Vue +export type CombinedVueInstance = Data & Methods & Computed & Props & Vue; export type ExtendedVue = (new (...args: any[]) => CombinedVueInstance) & Constructor; From fc83771a488e9754a5a8c8d760e9a94fff5a9e87 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 14 Jun 2017 15:30:21 -0700 Subject: [PATCH 11/18] Make it possible to extend Vue without type arguments. --- types/test/vue-test.ts | 2 +- types/vue.d.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/types/test/vue-test.ts b/types/test/vue-test.ts index dec78cfdd27..19ffd5559fd 100644 --- a/types/test/vue-test.ts +++ b/types/test/vue-test.ts @@ -1,6 +1,6 @@ import Vue from "../index"; -class Test extends Vue { +class Test extends Vue { a: number; testProperties() { diff --git a/types/vue.d.ts b/types/vue.d.ts index 59b94cec12b..39f9658ae24 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -76,8 +76,8 @@ export type ExtendedVue(options?: ThisTypedComponentOptionsWithArrayProps): CombinedVueInstance>; - new >(options?: ThisTypedComponentOptionsWithRecordProps): CombinedVueInstance>; + new (options?: ThisTypedComponentOptionsWithArrayProps): CombinedVueInstance>; + new = {}>(options?: ThisTypedComponentOptionsWithRecordProps): CombinedVueInstance>; extend(this: VC, definition: FunctionalComponentOptions>): ExtendedVue>; extend>(this: VC, definition: FunctionalComponentOptions>): ExtendedVue>; From a50c838dea039fbbccb2b1efe548c788d3566a66 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 14 Jun 2017 15:30:51 -0700 Subject: [PATCH 12/18] Removed 'ThisTypedComponentOptions'. --- types/options.d.ts | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/types/options.d.ts b/types/options.d.ts index c7457fc3d78..83618da3533 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -25,19 +25,7 @@ export type Accessors = { } /** - * A general type that describes non-functional component options in Vue. - * - * While `ThisTypedComponentOptionsWithArrayProps` and `ThisTypedComponentOptionsWithRecordProps` will - * lead to more accurate inferences, you can use this if the two are too cumbersome. - */ -export type ThisTypedComponentOptions = - object & - ComponentOptions & Vue) => Data), Methods, Computed, PropNames[] | Record> & - ThisType>>; - -/** - * A specialized version of `ThisTypedComponentOptions`. - * This type should be used when a parameter type only contains an array of strings for its `props` value. + * This type should be used when an array of strings is used for a component's `props` value. */ export type ThisTypedComponentOptionsWithArrayProps = object & @@ -45,8 +33,7 @@ export type ThisTypedComponentOptionsWithArrayProps>>; /** - * A specialized version of `ThisTypedComponentOptions`. - * This type should be used when a parameter type only contains an object mapped to `PropOptions` for its `props` value. + * This type should be used when an object mapped to `PropOptions` is used for a component's `props` value. */ export type ThisTypedComponentOptionsWithRecordProps = object & @@ -58,7 +45,8 @@ export type ThisTypedComponentOptionsWithRecordProps = - ThisTypedComponentOptions | + ThisTypedComponentOptionsWithArrayProps | + ThisTypedComponentOptionsWithRecordProps> | FunctionalComponentOptions, Record>; From 33a106c6949306548c18f2990825aedad17cf0bc Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 14 Jun 2017 23:52:30 -0700 Subject: [PATCH 13/18] Upgraded dependency on TypeScript. --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 8221f9e18bc..115d0ba454e 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "rollup-watch": "^4.0.0", "selenium-server": "^2.53.1", "serialize-javascript": "^1.3.0", - "typescript": "^2.3.4", + "typescript": "2.5.0-dev.20170615", "uglify-js": "^3.0.15", "webpack": "^2.6.1", "weex-js-runtime": "^0.20.5", diff --git a/yarn.lock b/yarn.lock index f02c8fab64e..ad7cff1b40a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4820,9 +4820,9 @@ typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@^2.3.4: - version "2.4.0" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.0.tgz#aef5a8d404beba36ad339abf079ddddfffba86dd" +typescript@2.5.0-dev.20170615: + version "2.5.0-dev.20170615" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.0-dev.20170615.tgz#30babe46483cb1ec742c397e2c9910532cb145a4" uglify-js@^2.6, uglify-js@^2.8.27: version "2.8.28" From 0f586db6987675eda7e4e34aad261ac5bbdc8a54 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 15 Jun 2017 16:30:52 -0700 Subject: [PATCH 14/18] Added test by @ktsn. --- types/test/vue-test.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/types/test/vue-test.ts b/types/test/vue-test.ts index deb2a1811d4..2425dc70d09 100644 --- a/types/test/vue-test.ts +++ b/types/test/vue-test.ts @@ -126,4 +126,28 @@ const FunctionalHelloWorldComponent = Vue.extend({ render(createElement, ctxt) { return createElement("div", "Hello " + ctxt.props.name) } -}) +}); + +const Parent = Vue.extend({ + data() { + return { greeting: 'Hello' } + } +}); + +const Child = Parent.extend({ + methods: { + foo() { + console.log(this.greeting.toLowerCase()); + } + } +}); + +const GrandChild = Child.extend({ + computed: { + lower(): string { + return this.greeting.toLowerCase(); + } + } +}); + +new GrandChild().lower.toUpperCase(); \ No newline at end of file From 1092efe6070da2052a8df97a802c9434436eef1e Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 15 Jun 2017 16:37:23 -0700 Subject: [PATCH 15/18] Removed unnecessary mixin constructors, made 'VueConstructor' generic. --- types/options.d.ts | 16 ++++++++-------- types/vue.d.ts | 30 ++++++++++++++---------------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/types/options.d.ts b/types/options.d.ts index f67ad07c854..37bdbf24f30 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -27,18 +27,18 @@ export type Accessors = { /** * This type should be used when an array of strings is used for a component's `props` value. */ -export type ThisTypedComponentOptionsWithArrayProps = +export type ThisTypedComponentOptionsWithArrayProps = object & - ComponentOptions & Vue) => Data), Methods, Computed, PropNames[]> & - ThisType>>; + ComponentOptions & Instance) => Data), Methods, Computed, PropNames[]> & + ThisType>>; /** * This type should be used when an object mapped to `PropOptions` is used for a component's `props` value. */ -export type ThisTypedComponentOptionsWithRecordProps = +export type ThisTypedComponentOptionsWithRecordProps = object & - ComponentOptions & Vue) => Data), Methods, Computed, Props> & - ThisType>; + ComponentOptions & Instance) => Data), Methods, Computed, Props> & + ThisType>; /** * A helper type that describes options for either functional or non-functional components. @@ -46,8 +46,8 @@ export type ThisTypedComponentOptionsWithRecordProps = | FunctionalComponentOptions, Record> - | ThisTypedComponentOptionsWithArrayProps - | ThisTypedComponentOptionsWithRecordProps>; + | ThisTypedComponentOptionsWithArrayProps + | ThisTypedComponentOptionsWithRecordProps>; export interface ComponentOptions { diff --git a/types/vue.d.ts b/types/vue.d.ts index 2b74ff3f1bd..4881b0e7e37 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -70,19 +70,17 @@ export interface Vue { $createElement: CreateElement; } -export type CombinedVueInstance = Data & Methods & Computed & Props & Vue; -export type ExtendedVue = - (new (...args: any[]) => CombinedVueInstance) & - Constructor; +export type CombinedVueInstance = Instance & Data & Methods & Computed & Props; +export type ExtendedVue = VueConstructor & Vue>; -export interface VueConstructor { - new (options?: ThisTypedComponentOptionsWithArrayProps): CombinedVueInstance>; - new = {}>(options?: ThisTypedComponentOptionsWithRecordProps): CombinedVueInstance>; +export interface VueConstructor { + new (options?: ThisTypedComponentOptionsWithArrayProps): CombinedVueInstance>; + new = {}>(options?: ThisTypedComponentOptionsWithRecordProps): CombinedVueInstance>; - extend(this: VC, definition: FunctionalComponentOptions>): ExtendedVue>; - extend>(this: VC, definition: FunctionalComponentOptions>): ExtendedVue>; - extend(this: VC, options: ThisTypedComponentOptionsWithArrayProps): ExtendedVue>; - extend>(this: VC, options?: ThisTypedComponentOptionsWithRecordProps): ExtendedVue>; + extend(definition: FunctionalComponentOptions>): ExtendedVue>; + extend>(definition: FunctionalComponentOptions>): ExtendedVue>; + extend(options: ThisTypedComponentOptionsWithArrayProps): ExtendedVue>; + extend>(options?: ThisTypedComponentOptionsWithRecordProps): ExtendedVue>; nextTick(callback: () => void, context?: any[]): void; nextTick(): Promise @@ -99,11 +97,11 @@ export interface VueConstructor { component(id: string): VueConstructor; component(id: string, constructor: VC): VC; - component(this: VC, id: string, definition: AsyncComponent): ExtendedVue>; - component(this: VC, id: string, definition: FunctionalComponentOptions>): ExtendedVue>; - component>(this: VC, id: string, definition: FunctionalComponentOptions>): ExtendedVue>; - component(this: VC, id: string, definition: ThisTypedComponentOptionsWithArrayProps): ExtendedVue>; - component>(this: VC, id: string, definition?: ThisTypedComponentOptionsWithRecordProps): ExtendedVue>; + component(id: string, definition: AsyncComponent): ExtendedVue>; + component(id: string, definition: FunctionalComponentOptions>): ExtendedVue>; + component>(id: string, definition: FunctionalComponentOptions>): ExtendedVue>; + component(id: string, definition: ThisTypedComponentOptionsWithArrayProps): ExtendedVue>; + component>(id: string, definition?: ThisTypedComponentOptionsWithRecordProps): ExtendedVue>; use(plugin: PluginObject | PluginFunction, options?: T): void; mixin(mixin: typeof Vue | ComponentOptions): void; From c628103ca1ec4d0798c67412f6c458055a2073c8 Mon Sep 17 00:00:00 2001 From: Hanks Date: Mon, 24 Jul 2017 19:32:38 +0800 Subject: [PATCH 16/18] [release] weex-vue-framework@2.4.2-weex.1 (#6196) * build(release weex): ignore the file path of entries * [release] weex-vue-framework@2.4.2-weex.1 --- build/release-weex.sh | 1 - packages/weex-template-compiler/build.js | 1146 ++++++++++++++---- packages/weex-template-compiler/package.json | 2 +- packages/weex-vue-framework/factory.js | 692 ++++++++--- packages/weex-vue-framework/index.js | 287 +++-- packages/weex-vue-framework/package.json | 2 +- 6 files changed, 1610 insertions(+), 520 deletions(-) diff --git a/build/release-weex.sh b/build/release-weex.sh index 77ce1d0abf6..3bb5412dd4f 100644 --- a/build/release-weex.sh +++ b/build/release-weex.sh @@ -31,7 +31,6 @@ if [[ $REPLY =~ ^[Yy]$ ]]; then cd - # commit - git add src/entries/weex* git add packages/weex* git commit -m "[release] weex-vue-framework@$NEXT_VERSION" fi diff --git a/packages/weex-template-compiler/build.js b/packages/weex-template-compiler/build.js index 9f3fc688040..e590d002c7d 100644 --- a/packages/weex-template-compiler/build.js +++ b/packages/weex-template-compiler/build.js @@ -2,7 +2,9 @@ Object.defineProperty(exports, '__esModule', { value: true }); -var he = require('he'); +function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } + +var he = _interopDefault(require('he')); /* */ @@ -14,6 +16,8 @@ var he = require('he'); + + /** * Check if value is primitive */ @@ -24,15 +28,29 @@ var he = require('he'); * Objects from primitive values when we know the value * is a JSON-compliant type. */ +function isObject (obj) { + return obj !== null && typeof obj === 'object' +} +var _toString = Object.prototype.toString; /** * Strict object type check. Only returns true * for plain JavaScript objects. */ +function isPlainObject (obj) { + return _toString.call(obj) === '[object Object]' +} +/** + * Check if val is a valid array index. + */ +function isValidArrayIndex (val) { + var n = parseFloat(val); + return n >= 0 && Math.floor(n) === n && isFinite(val) +} /** * Convert a value to a string that is actually rendered. @@ -69,11 +87,29 @@ function makeMap ( var isBuiltInTag = makeMap('slot,component', true); /** - * Remove an item from an array + * Check if a attribute is a reserved attribute. */ +var isReservedAttribute = makeMap('key,ref,slot,is'); +/** + * Remove an item from an array + */ +function remove (arr, item) { + if (arr.length) { + var index = arr.indexOf(item); + if (index > -1) { + return arr.splice(index, 1) + } + } +} - +/** + * Check whether the object has the property. + */ +var hasOwnProperty = Object.prototype.hasOwnProperty; +function hasOwn (obj, key) { + return hasOwnProperty.call(obj, key) +} /** * Create a cached version of a pure function. @@ -128,13 +164,15 @@ function extend (to, _from) { /** * Perform no operation. + * Stubbing args to make Flow happy without leaving useless transpiled code + * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/) */ -function noop () {} +function noop (a, b, c) {} /** * Always return false. */ -var no = function () { return false; }; +var no = function (a, b, c) { return false; }; /** * Return same value @@ -243,6 +281,10 @@ var decodingMap = { var encodedAttr = /&(?:lt|gt|quot|amp);/g; var encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10);/g; +// #5992 +var isIgnoreNewlineTag = makeMap('pre,textarea', true); +var shouldIgnoreFirstNewline = function (tag, html) { return tag && isIgnoreNewlineTag(tag) && html[0] === '\n'; }; + function decodeAttr (value, shouldDecodeNewlines) { var re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr; return value.replace(re, function (match) { return decodingMap[match]; }) @@ -266,6 +308,9 @@ function parseHTML (html, options) { var commentEnd = html.indexOf('-->'); if (commentEnd >= 0) { + if (options.shouldKeepComment) { + options.comment(html.substring(4, commentEnd)); + } advance(commentEnd + 3); continue } @@ -301,24 +346,27 @@ function parseHTML (html, options) { var startTagMatch = parseStartTag(); if (startTagMatch) { handleStartTag(startTagMatch); + if (shouldIgnoreFirstNewline(lastTag, html)) { + advance(1); + } continue } } - var text = (void 0), rest$1 = (void 0), next = (void 0); + var text = (void 0), rest = (void 0), next = (void 0); if (textEnd >= 0) { - rest$1 = html.slice(textEnd); + rest = html.slice(textEnd); while ( - !endTag.test(rest$1) && - !startTagOpen.test(rest$1) && - !comment.test(rest$1) && - !conditionalComment.test(rest$1) + !endTag.test(rest) && + !startTagOpen.test(rest) && + !comment.test(rest) && + !conditionalComment.test(rest) ) { // < in plain text, be forgiving and treat it as text - next = rest$1.indexOf('<', 1); + next = rest.indexOf('<', 1); if (next < 0) { break } textEnd += next; - rest$1 = html.slice(textEnd); + rest = html.slice(textEnd); } text = html.substring(0, textEnd); advance(textEnd); @@ -333,23 +381,26 @@ function parseHTML (html, options) { options.chars(text); } } else { + var endTagLength = 0; var stackedTag = lastTag.toLowerCase(); var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(]*>)', 'i')); - var endTagLength = 0; - var rest = html.replace(reStackedTag, function (all, text, endTag) { + var rest$1 = html.replace(reStackedTag, function (all, text, endTag) { endTagLength = endTag.length; if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') { text = text .replace(//g, '$1') .replace(//g, '$1'); } + if (shouldIgnoreFirstNewline(stackedTag, text)) { + text = text.slice(1); + } if (options.chars) { options.chars(text); } return '' }); - index += html.length - rest.length; - html = rest; + index += html.length - rest$1.length; + html = rest$1; parseEndTag(stackedTag, index - endTagLength, index); } @@ -406,7 +457,7 @@ function parseHTML (html, options) { } } - var unary = isUnaryTag$$1(tagName) || tagName === 'html' && lastTag === 'head' || !!unarySlash; + var unary = isUnaryTag$$1(tagName) || !!unarySlash; var l = match.attrs.length; var attrs = new Array(l); @@ -463,8 +514,9 @@ function parseHTML (html, options) { // Close all the open elements, up the stack for (var i = stack.length - 1; i >= pos; i--) { if (process.env.NODE_ENV !== 'production' && - (i > pos || !tagName) && - options.warn) { + (i > pos || !tagName) && + options.warn + ) { options.warn( ("tag <" + (stack[i].tag) + "> has no matching end tag.") ); @@ -674,10 +726,7 @@ function genAssignmentCode ( if (modelRs.idx === null) { return (value + "=" + assignment) } else { - return "var $$exp = " + (modelRs.exp) + ", $$idx = " + (modelRs.idx) + ";" + - "if (!Array.isArray($$exp)){" + - value + "=" + assignment + "}" + - "else{$$exp.splice($$idx, 1, " + assignment + ")}" + return ("$set(" + (modelRs.exp) + ", " + (modelRs.idx) + ", " + assignment + ")") } } @@ -770,6 +819,12 @@ function parseString (chr) { } } +var ASSET_TYPES = [ + 'component', + 'directive', + 'filter' +]; + var LIFECYCLE_HOOKS = [ 'beforeCreate', 'created', @@ -816,6 +871,11 @@ var config = ({ */ errorHandler: null, + /** + * Warn handler for watcher warns + */ + warnHandler: null, + /** * Ignore certain custom elements */ @@ -866,9 +926,11 @@ var config = ({ _lifecycleHooks: LIFECYCLE_HOOKS }); +/* */ + var warn$1 = noop; var tip = noop; -var formatComponentName; +var formatComponentName = (null); // work around flow check if (process.env.NODE_ENV !== 'production') { var hasConsole = typeof console !== 'undefined'; @@ -878,10 +940,12 @@ if (process.env.NODE_ENV !== 'production') { .replace(/[-_]/g, ''); }; warn$1 = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.error("[Vue warn]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); + var trace = vm ? generateComponentTrace(vm) : ''; + + if (config.warnHandler) { + config.warnHandler.call(null, msg, vm, trace); + } else if (hasConsole && (!config.silent)) { + console.error(("[Vue warn]: " + msg + trace)); } }; @@ -957,6 +1021,8 @@ if (process.env.NODE_ENV !== 'production') { }; } +/* */ + function handleError (err, vm, info) { if (config.errorHandler) { config.errorHandler.call(null, err, vm, info); @@ -977,7 +1043,7 @@ function handleError (err, vm, info) { /* globals MutationObserver */ // can we use __proto__? - +var hasProto = '__proto__' in {}; // Browser environment sniffing var inBrowser = typeof window !== 'undefined'; @@ -989,6 +1055,9 @@ var isAndroid = UA && UA.indexOf('android') > 0; var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; +// Firefix has a "watch" function on Object.prototype... +var nativeWatch = ({}).watch; + var supportsPassive = false; if (inBrowser) { try { @@ -998,7 +1067,7 @@ if (inBrowser) { /* istanbul ignore next */ supportsPassive = true; } - } )); // https://github.com/facebook/flow/issues/285 + })); // https://github.com/facebook/flow/issues/285 window.addEventListener('test-passive', null, opts); } catch (e) {} } @@ -1292,12 +1361,15 @@ function parse ( options ) { warn = options.warn || baseWarn; - platformGetTagNamespace = options.getTagNamespace || no; - platformMustUseProp = options.mustUseProp || no; + platformIsPreTag = options.isPreTag || no; - preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); + platformMustUseProp = options.mustUseProp || no; + platformGetTagNamespace = options.getTagNamespace || no; + transforms = pluckModuleFunction(options.modules, 'transformNode'); + preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); postTransforms = pluckModuleFunction(options.modules, 'postTransformNode'); + delimiters = options.delimiters; var stack = []; @@ -1331,6 +1403,7 @@ function parse ( isUnaryTag: options.isUnaryTag, canBeLeftOpenTag: options.canBeLeftOpenTag, shouldDecodeNewlines: options.shouldDecodeNewlines, + shouldKeepComment: options.comments, start: function start (tag, attrs, unary) { // check namespace. // inherit parent ns if there is one @@ -1489,8 +1562,9 @@ function parse ( // IE textarea placeholder bug /* istanbul ignore if */ if (isIE && - currentParent.tag === 'textarea' && - currentParent.attrsMap.placeholder === text) { + currentParent.tag === 'textarea' && + currentParent.attrsMap.placeholder === text + ) { return } var children = currentParent.children; @@ -1513,6 +1587,13 @@ function parse ( }); } } + }, + comment: function comment (text) { + currentParent.children.push({ + type: 3, + text: text, + isComment: true + }); } }); return root @@ -1714,7 +1795,9 @@ function processAttrs (el) { ); } } - if (isProp || platformMustUseProp(el.tag, el.attrsMap.type, name)) { + if (isProp || ( + !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) + )) { addProp(el, name, value); } else { addAttr(el, name, value); @@ -1889,6 +1972,15 @@ function markStatic (node) { node.static = false; } } + if (node.ifConditions) { + for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { + var block = node.ifConditions[i$1].block; + markStatic(block); + if (!block.static) { + node.static = false; + } + } + } } } @@ -1915,17 +2007,13 @@ function markStaticRoots (node, isInFor) { } } if (node.ifConditions) { - walkThroughConditionsBlocks(node.ifConditions, isInFor); + for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { + markStaticRoots(node.ifConditions[i$1].block, isInFor); + } } } } -function walkThroughConditionsBlocks (conditionBlocks, isInFor) { - for (var i = 1, len = conditionBlocks.length; i < len; i++) { - markStaticRoots(conditionBlocks[i].block, isInFor); - } -} - function isStatic (node) { if (node.type === 2) { // expression return false @@ -1994,17 +2082,17 @@ var modifierCode = { function genHandlers ( events, - native, + isNative, warn ) { - var res = native ? 'nativeOn:{' : 'on:{'; + var res = isNative ? 'nativeOn:{' : 'on:{'; for (var name in events) { var handler = events[name]; // #5330: warn click.right, since right clicks do not actually fire click events. if (process.env.NODE_ENV !== 'production' && - name === 'click' && - handler && handler.modifiers && handler.modifiers.right - ) { + name === 'click' && + handler && handler.modifiers && handler.modifiers.right + ) { warn( "Use \"contextmenu\" instead of \"click.right\" since right clicks " + "do not actually fire \"click\" events." @@ -2080,99 +2168,639 @@ function genFilterCode (key) { /* */ +var emptyObject = Object.freeze({}); + +/** + * Check if a string starts with $ or _ + */ + + +/** + * Define a property. + */ +function def (obj, key, val, enumerable) { + Object.defineProperty(obj, key, { + value: val, + enumerable: !!enumerable, + writable: true, + configurable: true + }); +} + +/* */ + + +var uid = 0; + +/** + * A dep is an observable that can have multiple + * directives subscribing to it. + */ +var Dep = function Dep () { + this.id = uid++; + this.subs = []; +}; + +Dep.prototype.addSub = function addSub (sub) { + this.subs.push(sub); +}; + +Dep.prototype.removeSub = function removeSub (sub) { + remove(this.subs, sub); +}; + +Dep.prototype.depend = function depend () { + if (Dep.target) { + Dep.target.addDep(this); + } +}; + +Dep.prototype.notify = function notify () { + // stabilize the subscriber list first + var subs = this.subs.slice(); + for (var i = 0, l = subs.length; i < l; i++) { + subs[i].update(); + } +}; + +// the current target watcher being evaluated. +// this is globally unique because there could be only one +// watcher being evaluated at any time. +Dep.target = null; + +/* + * not type checking this file because flow doesn't play well with + * dynamically accessing methods on Array prototype + */ + +var arrayProto = Array.prototype; +var arrayMethods = Object.create(arrayProto);[ + 'push', + 'pop', + 'shift', + 'unshift', + 'splice', + 'sort', + 'reverse' +] +.forEach(function (method) { + // cache original method + var original = arrayProto[method]; + def(arrayMethods, method, function mutator () { + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + + var result = original.apply(this, args); + var ob = this.__ob__; + var inserted; + switch (method) { + case 'push': + case 'unshift': + inserted = args; + break + case 'splice': + inserted = args.slice(2); + break + } + if (inserted) { ob.observeArray(inserted); } + // notify change + ob.dep.notify(); + return result + }); +}); + +/* */ + +var arrayKeys = Object.getOwnPropertyNames(arrayMethods); + +/** + * By default, when a reactive property is set, the new value is + * also converted to become reactive. However when passing down props, + * we don't want to force conversion because the value may be a nested value + * under a frozen data structure. Converting it would defeat the optimization. + */ +var observerState = { + shouldConvert: true +}; + +/** + * Observer class that are attached to each observed + * object. Once attached, the observer converts target + * object's property keys into getter/setters that + * collect dependencies and dispatches updates. + */ +var Observer = function Observer (value) { + this.value = value; + this.dep = new Dep(); + this.vmCount = 0; + def(value, '__ob__', this); + if (Array.isArray(value)) { + var augment = hasProto + ? protoAugment + : copyAugment; + augment(value, arrayMethods, arrayKeys); + this.observeArray(value); + } else { + this.walk(value); + } +}; + +/** + * Walk through each property and convert them into + * getter/setters. This method should only be called when + * value type is Object. + */ +Observer.prototype.walk = function walk (obj) { + var keys = Object.keys(obj); + for (var i = 0; i < keys.length; i++) { + defineReactive$$1(obj, keys[i], obj[keys[i]]); + } +}; + +/** + * Observe a list of Array items. + */ +Observer.prototype.observeArray = function observeArray (items) { + for (var i = 0, l = items.length; i < l; i++) { + observe(items[i]); + } +}; + +// helpers + +/** + * Augment an target Object or Array by intercepting + * the prototype chain using __proto__ + */ +function protoAugment (target, src, keys) { + /* eslint-disable no-proto */ + target.__proto__ = src; + /* eslint-enable no-proto */ +} + +/** + * Augment an target Object or Array by defining + * hidden properties. + */ +/* istanbul ignore next */ +function copyAugment (target, src, keys) { + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + def(target, key, src[key]); + } +} + +/** + * Attempt to create an observer instance for a value, + * returns the new observer if successfully observed, + * or the existing observer if the value already has one. + */ +function observe (value, asRootData) { + if (!isObject(value)) { + return + } + var ob; + if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { + ob = value.__ob__; + } else if ( + observerState.shouldConvert && + !isServerRendering() && + (Array.isArray(value) || isPlainObject(value)) && + Object.isExtensible(value) && + !value._isVue + ) { + ob = new Observer(value); + } + if (asRootData && ob) { + ob.vmCount++; + } + return ob +} + +/** + * Define a reactive property on an Object. + */ +function defineReactive$$1 ( + obj, + key, + val, + customSetter, + shallow +) { + var dep = new Dep(); + + var property = Object.getOwnPropertyDescriptor(obj, key); + if (property && property.configurable === false) { + return + } + + // cater for pre-defined getter/setters + var getter = property && property.get; + var setter = property && property.set; + + var childOb = !shallow && observe(val); + Object.defineProperty(obj, key, { + enumerable: true, + configurable: true, + get: function reactiveGetter () { + var value = getter ? getter.call(obj) : val; + if (Dep.target) { + dep.depend(); + if (childOb) { + childOb.dep.depend(); + } + if (Array.isArray(value)) { + dependArray(value); + } + } + return value + }, + set: function reactiveSetter (newVal) { + var value = getter ? getter.call(obj) : val; + /* eslint-disable no-self-compare */ + if (newVal === value || (newVal !== newVal && value !== value)) { + return + } + /* eslint-enable no-self-compare */ + if (process.env.NODE_ENV !== 'production' && customSetter) { + customSetter(); + } + if (setter) { + setter.call(obj, newVal); + } else { + val = newVal; + } + childOb = !shallow && observe(newVal); + dep.notify(); + } + }); +} + +/** + * Set a property on an object. Adds the new property and + * triggers change notification if the property doesn't + * already exist. + */ +function set (target, key, val) { + if (Array.isArray(target) && isValidArrayIndex(key)) { + target.length = Math.max(target.length, key); + target.splice(key, 1, val); + return val + } + if (hasOwn(target, key)) { + target[key] = val; + return val + } + var ob = (target).__ob__; + if (target._isVue || (ob && ob.vmCount)) { + process.env.NODE_ENV !== 'production' && warn$1( + 'Avoid adding reactive properties to a Vue instance or its root $data ' + + 'at runtime - declare it upfront in the data option.' + ); + return val + } + if (!ob) { + target[key] = val; + return val + } + defineReactive$$1(ob.value, key, val); + ob.dep.notify(); + return val +} + +/** + * Delete a property and trigger change if necessary. + */ + + +/** + * Collect dependencies on array elements when the array is touched, since + * we cannot intercept array element access like property getters. + */ +function dependArray (value) { + for (var e = (void 0), i = 0, l = value.length; i < l; i++) { + e = value[i]; + e && e.__ob__ && e.__ob__.dep.depend(); + if (Array.isArray(e)) { + dependArray(e); + } + } +} + +/* */ + +/** + * Option overwriting strategies are functions that handle + * how to merge a parent option value and a child option + * value into the final value. + */ +var strats = config.optionMergeStrategies; + +/** + * Options with restrictions + */ +if (process.env.NODE_ENV !== 'production') { + strats.el = strats.propsData = function (parent, child, vm, key) { + if (!vm) { + warn$1( + "option \"" + key + "\" can only be used during instance " + + 'creation with the `new` keyword.' + ); + } + return defaultStrat(parent, child) + }; +} + +/** + * Helper that recursively merges two data objects together. + */ +function mergeData (to, from) { + if (!from) { return to } + var key, toVal, fromVal; + var keys = Object.keys(from); + for (var i = 0; i < keys.length; i++) { + key = keys[i]; + toVal = to[key]; + fromVal = from[key]; + if (!hasOwn(to, key)) { + set(to, key, fromVal); + } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { + mergeData(toVal, fromVal); + } + } + return to +} + +/** + * Data + */ +function mergeDataOrFn ( + parentVal, + childVal, + vm +) { + if (!vm) { + // in a Vue.extend merge, both should be functions + if (!childVal) { + return parentVal + } + if (!parentVal) { + return childVal + } + // when parentVal & childVal are both present, + // we need to return a function that returns the + // merged result of both functions... no need to + // check if parentVal is a function here because + // it has to be a function to pass previous merges. + return function mergedDataFn () { + return mergeData( + typeof childVal === 'function' ? childVal.call(this) : childVal, + typeof parentVal === 'function' ? parentVal.call(this) : parentVal + ) + } + } else if (parentVal || childVal) { + return function mergedInstanceDataFn () { + // instance merge + var instanceData = typeof childVal === 'function' + ? childVal.call(vm) + : childVal; + var defaultData = typeof parentVal === 'function' + ? parentVal.call(vm) + : undefined; + if (instanceData) { + return mergeData(instanceData, defaultData) + } else { + return defaultData + } + } + } +} + +strats.data = function ( + parentVal, + childVal, + vm +) { + if (!vm) { + if (childVal && typeof childVal !== 'function') { + process.env.NODE_ENV !== 'production' && warn$1( + 'The "data" option should be a function ' + + 'that returns a per-instance value in component ' + + 'definitions.', + vm + ); + + return parentVal + } + return mergeDataOrFn.call(this, parentVal, childVal) + } + + return mergeDataOrFn(parentVal, childVal, vm) +}; + +/** + * Hooks and props are merged as arrays. + */ +function mergeHook ( + parentVal, + childVal +) { + return childVal + ? parentVal + ? parentVal.concat(childVal) + : Array.isArray(childVal) + ? childVal + : [childVal] + : parentVal +} + +LIFECYCLE_HOOKS.forEach(function (hook) { + strats[hook] = mergeHook; +}); + +/** + * Assets + * + * When a vm is present (instance creation), we need to do + * a three-way merge between constructor options, instance + * options and parent options. + */ +function mergeAssets (parentVal, childVal) { + var res = Object.create(parentVal || null); + return childVal + ? extend(res, childVal) + : res +} + +ASSET_TYPES.forEach(function (type) { + strats[type + 's'] = mergeAssets; +}); + +/** + * Watchers. + * + * Watchers hashes should not overwrite one + * another, so we merge them as arrays. + */ +strats.watch = function (parentVal, childVal) { + // work around Firefox's Object.prototype.watch... + if (parentVal === nativeWatch) { parentVal = undefined; } + if (childVal === nativeWatch) { childVal = undefined; } + /* istanbul ignore if */ + if (!childVal) { return Object.create(parentVal || null) } + if (!parentVal) { return childVal } + var ret = {}; + extend(ret, parentVal); + for (var key in childVal) { + var parent = ret[key]; + var child = childVal[key]; + if (parent && !Array.isArray(parent)) { + parent = [parent]; + } + ret[key] = parent + ? parent.concat(child) + : Array.isArray(child) ? child : [child]; + } + return ret +}; + +/** + * Other object hashes. + */ +strats.props = +strats.methods = +strats.inject = +strats.computed = function (parentVal, childVal) { + if (!parentVal) { return childVal } + var ret = Object.create(null); + extend(ret, parentVal); + if (childVal) { extend(ret, childVal); } + return ret +}; +strats.provide = mergeDataOrFn; + +/** + * Default strategy. + */ +var defaultStrat = function (parentVal, childVal) { + return childVal === undefined + ? parentVal + : childVal +}; + +/** + * Merge two option objects into a new one. + * Core utility used in both instantiation and inheritance. + */ + + +/** + * Resolve an asset. + * This function is used because child instances need access + * to assets defined in its ancestor chain. + */ + +/* */ + +/* */ + +/* */ + +function on (el, dir) { + if (process.env.NODE_ENV !== 'production' && dir.modifiers) { + warn$1("v-on without argument does not support modifiers."); + } + el.wrapListeners = function (code) { return ("_g(" + code + "," + (dir.value) + ")"); }; +} + +/* */ + function bind$1 (el, dir) { el.wrapData = function (code) { - return ("_b(" + code + ",'" + (el.tag) + "'," + (dir.value) + (dir.modifiers && dir.modifiers.prop ? ',true' : '') + ")") + return ("_b(" + code + ",'" + (el.tag) + "'," + (dir.value) + "," + (dir.modifiers && dir.modifiers.prop ? 'true' : 'false') + (dir.modifiers && dir.modifiers.sync ? ',true' : '') + ")") }; } /* */ var baseDirectives = { + on: on, bind: bind$1, cloak: noop }; /* */ -// configurable state -var warn$2; -var transforms$1; -var dataGenFns; -var platformDirectives; -var isPlatformReservedTag$1; -var staticRenderFns; -var onceCount; -var currentOptions; +var CodegenState = function CodegenState (options) { + this.options = options; + this.warn = options.warn || baseWarn; + this.transforms = pluckModuleFunction(options.modules, 'transformCode'); + this.dataGenFns = pluckModuleFunction(options.modules, 'genData'); + this.directives = extend(extend({}, baseDirectives), options.directives); + var isReservedTag = options.isReservedTag || no; + this.maybeComponent = function (el) { return !isReservedTag(el.tag); }; + this.onceId = 0; + this.staticRenderFns = []; +}; + + function generate ( ast, options ) { - // save previous staticRenderFns so generate calls can be nested - var prevStaticRenderFns = staticRenderFns; - var currentStaticRenderFns = staticRenderFns = []; - var prevOnceCount = onceCount; - onceCount = 0; - currentOptions = options; - warn$2 = options.warn || baseWarn; - transforms$1 = pluckModuleFunction(options.modules, 'transformCode'); - dataGenFns = pluckModuleFunction(options.modules, 'genData'); - platformDirectives = options.directives || {}; - isPlatformReservedTag$1 = options.isReservedTag || no; - var code = ast ? genElement(ast) : '_c("div")'; - staticRenderFns = prevStaticRenderFns; - onceCount = prevOnceCount; + var state = new CodegenState(options); + var code = ast ? genElement(ast, state) : '_c("div")'; return { render: ("with(this){return " + code + "}"), - staticRenderFns: currentStaticRenderFns + staticRenderFns: state.staticRenderFns } } -function genElement (el) { +function genElement (el, state) { if (el.staticRoot && !el.staticProcessed) { - return genStatic(el) + return genStatic(el, state) } else if (el.once && !el.onceProcessed) { - return genOnce(el) + return genOnce(el, state) } else if (el.for && !el.forProcessed) { - return genFor(el) + return genFor(el, state) } else if (el.if && !el.ifProcessed) { - return genIf(el) + return genIf(el, state) } else if (el.tag === 'template' && !el.slotTarget) { - return genChildren(el) || 'void 0' + return genChildren(el, state) || 'void 0' } else if (el.tag === 'slot') { - return genSlot(el) + return genSlot(el, state) } else { // component or element var code; if (el.component) { - code = genComponent(el.component, el); + code = genComponent(el.component, el, state); } else { - var data = el.plain ? undefined : genData(el); + var data = el.plain ? undefined : genData(el, state); - var children = el.inlineTemplate ? null : genChildren(el, true); + var children = el.inlineTemplate ? null : genChildren(el, state, true); code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")"; } // module transforms - for (var i = 0; i < transforms$1.length; i++) { - code = transforms$1[i](el, code); + for (var i = 0; i < state.transforms.length; i++) { + code = state.transforms[i](el, code); } return code } } // hoist static sub-trees out -function genStatic (el) { +function genStatic (el, state) { el.staticProcessed = true; - staticRenderFns.push(("with(this){return " + (genElement(el)) + "}")); - return ("_m(" + (staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")") + state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}")); + return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")") } // v-once -function genOnce (el) { +function genOnce (el, state) { el.onceProcessed = true; if (el.if && !el.ifProcessed) { - return genIf(el) + return genIf(el, state) } else if (el.staticInFor) { var key = ''; var parent = el.parent; @@ -2184,51 +2812,72 @@ function genOnce (el) { parent = parent.parent; } if (!key) { - process.env.NODE_ENV !== 'production' && warn$2( + process.env.NODE_ENV !== 'production' && state.warn( "v-once can only be used inside v-for that is keyed. " ); - return genElement(el) + return genElement(el, state) } - return ("_o(" + (genElement(el)) + "," + (onceCount++) + (key ? ("," + key) : "") + ")") + return ("_o(" + (genElement(el, state)) + "," + (state.onceId++) + (key ? ("," + key) : "") + ")") } else { - return genStatic(el) + return genStatic(el, state) } } -function genIf (el) { +function genIf ( + el, + state, + altGen, + altEmpty +) { el.ifProcessed = true; // avoid recursion - return genIfConditions(el.ifConditions.slice()) + return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) } -function genIfConditions (conditions) { +function genIfConditions ( + conditions, + state, + altGen, + altEmpty +) { if (!conditions.length) { - return '_e()' + return altEmpty || '_e()' } var condition = conditions.shift(); if (condition.exp) { - return ("(" + (condition.exp) + ")?" + (genTernaryExp(condition.block)) + ":" + (genIfConditions(conditions))) + return ("(" + (condition.exp) + ")?" + (genTernaryExp(condition.block)) + ":" + (genIfConditions(conditions, state, altGen, altEmpty))) } else { return ("" + (genTernaryExp(condition.block))) } // v-if with v-once should generate code like (a)?_m(0):_m(1) function genTernaryExp (el) { - return el.once ? genOnce(el) : genElement(el) + return altGen + ? altGen(el, state) + : el.once + ? genOnce(el, state) + : genElement(el, state) } } -function genFor (el) { +function genFor ( + el, + state, + altGen, + altHelper +) { var exp = el.for; var alias = el.alias; var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; - if ( - process.env.NODE_ENV !== 'production' && - maybeComponent(el) && el.tag !== 'slot' && el.tag !== 'template' && !el.key + if (process.env.NODE_ENV !== 'production' && + state.maybeComponent(el) && + el.tag !== 'slot' && + el.tag !== 'template' && + !el.key ) { - warn$2( + state.warn( "<" + (el.tag) + " v-for=\"" + alias + " in " + exp + "\">: component lists rendered with " + "v-for should have explicit keys. " + "See https://vuejs.org/guide/list.html#key for more info.", @@ -2237,18 +2886,18 @@ function genFor (el) { } el.forProcessed = true; // avoid recursion - return "_l((" + exp + ")," + + return (altHelper || '_l') + "((" + exp + ")," + "function(" + alias + iterator1 + iterator2 + "){" + - "return " + (genElement(el)) + + "return " + ((altGen || genElement)(el, state)) + '})' } -function genData (el) { +function genData (el, state) { var data = '{'; // directives first. // directives may mutate the el's other properties before they are generated. - var dirs = genDirectives(el); + var dirs = genDirectives(el, state); if (dirs) { data += dirs + ','; } // key @@ -2271,8 +2920,8 @@ function genData (el) { data += "tag:\"" + (el.tag) + "\","; } // module data generation functions - for (var i = 0; i < dataGenFns.length; i++) { - data += dataGenFns[i](el); + for (var i = 0; i < state.dataGenFns.length; i++) { + data += state.dataGenFns[i](el); } // attributes if (el.attrs) { @@ -2284,10 +2933,10 @@ function genData (el) { } // event handlers if (el.events) { - data += (genHandlers(el.events, false, warn$2)) + ","; + data += (genHandlers(el.events, false, state.warn)) + ","; } if (el.nativeEvents) { - data += (genHandlers(el.nativeEvents, true, warn$2)) + ","; + data += (genHandlers(el.nativeEvents, true, state.warn)) + ","; } // slot target if (el.slotTarget) { @@ -2295,7 +2944,7 @@ function genData (el) { } // scoped slots if (el.scopedSlots) { - data += (genScopedSlots(el.scopedSlots)) + ","; + data += (genScopedSlots(el.scopedSlots, state)) + ","; } // component v-model if (el.model) { @@ -2303,7 +2952,7 @@ function genData (el) { } // inline-template if (el.inlineTemplate) { - var inlineTemplate = genInlineTemplate(el); + var inlineTemplate = genInlineTemplate(el, state); if (inlineTemplate) { data += inlineTemplate + ","; } @@ -2313,10 +2962,14 @@ function genData (el) { if (el.wrapData) { data = el.wrapData(data); } + // v-on data wrap + if (el.wrapListeners) { + data = el.wrapListeners(data); + } return data } -function genDirectives (el) { +function genDirectives (el, state) { var dirs = el.directives; if (!dirs) { return } var res = 'directives:['; @@ -2325,11 +2978,11 @@ function genDirectives (el) { for (i = 0, l = dirs.length; i < l; i++) { dir = dirs[i]; needRuntime = true; - var gen = platformDirectives[dir.name] || baseDirectives[dir.name]; + var gen = state.directives[dir.name]; if (gen) { // compile-time directive that manipulates AST. // returns true if it also needs a runtime counterpart. - needRuntime = !!gen(el, dir, warn$2); + needRuntime = !!gen(el, dir, state.warn); } if (needRuntime) { hasRuntime = true; @@ -2341,43 +2994,81 @@ function genDirectives (el) { } } -function genInlineTemplate (el) { +function genInlineTemplate (el, state) { var ast = el.children[0]; if (process.env.NODE_ENV !== 'production' && ( el.children.length > 1 || ast.type !== 1 )) { - warn$2('Inline-template components must have exactly one child element.'); + state.warn('Inline-template components must have exactly one child element.'); } if (ast.type === 1) { - var inlineRenderFns = generate(ast, currentOptions); + var inlineRenderFns = generate(ast, state.options); return ("inlineTemplate:{render:function(){" + (inlineRenderFns.render) + "},staticRenderFns:[" + (inlineRenderFns.staticRenderFns.map(function (code) { return ("function(){" + code + "}"); }).join(',')) + "]}") } } -function genScopedSlots (slots) { - return ("scopedSlots:_u([" + (Object.keys(slots).map(function (key) { return genScopedSlot(key, slots[key]); }).join(',')) + "])") +function genScopedSlots ( + slots, + state +) { + return ("scopedSlots:_u([" + (Object.keys(slots).map(function (key) { + return genScopedSlot(key, slots[key], state) + }).join(',')) + "])") } -function genScopedSlot (key, el) { - return "[" + key + ",function(" + (String(el.attrsMap.scope)) + "){" + +function genScopedSlot ( + key, + el, + state +) { + if (el.for && !el.forProcessed) { + return genForScopedSlot(key, el, state) + } + return "{key:" + key + ",fn:function(" + (String(el.attrsMap.scope)) + "){" + "return " + (el.tag === 'template' - ? genChildren(el) || 'void 0' - : genElement(el)) + "}]" + ? genChildren(el, state) || 'void 0' + : genElement(el, state)) + "}}" } -function genChildren (el, checkSkip) { +function genForScopedSlot ( + key, + el, + state +) { + var exp = el.for; + var alias = el.alias; + var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : ''; + var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : ''; + el.forProcessed = true; // avoid recursion + return "_l((" + exp + ")," + + "function(" + alias + iterator1 + iterator2 + "){" + + "return " + (genScopedSlot(key, el, state)) + + '})' +} + +function genChildren ( + el, + state, + checkSkip, + altGenElement, + altGenNode +) { var children = el.children; if (children.length) { var el$1 = children[0]; // optimize single v-for if (children.length === 1 && - el$1.for && - el$1.tag !== 'template' && - el$1.tag !== 'slot') { - return genElement(el$1) + el$1.for && + el$1.tag !== 'template' && + el$1.tag !== 'slot' + ) { + return (altGenElement || genElement)(el$1, state) } - var normalizationType = checkSkip ? getNormalizationType(children) : 0; - return ("[" + (children.map(genNode).join(',')) + "]" + (normalizationType ? ("," + normalizationType) : '')) + var normalizationType = checkSkip + ? getNormalizationType(children, state.maybeComponent) + : 0; + var gen = altGenNode || genNode; + return ("[" + (children.map(function (c) { return gen(c, state); }).join(',')) + "]" + (normalizationType ? ("," + normalizationType) : '')) } } @@ -2385,7 +3076,10 @@ function genChildren (el, checkSkip) { // 0: no normalization needed // 1: simple normalization needed (possible 1-level deep nested array) // 2: full normalization needed -function getNormalizationType (children) { +function getNormalizationType ( + children, + maybeComponent +) { var res = 0; for (var i = 0; i < children.length; i++) { var el = children[i]; @@ -2409,13 +3103,11 @@ function needsNormalization (el) { return el.for !== undefined || el.tag === 'template' || el.tag === 'slot' } -function maybeComponent (el) { - return !isPlatformReservedTag$1(el.tag) -} - -function genNode (node) { +function genNode (node, state) { if (node.type === 1) { - return genElement(node) + return genElement(node, state) + } if (node.type === 3 && node.isComment) { + return genComment(node) } else { return genText(node) } @@ -2427,9 +3119,13 @@ function genText (text) { : transformSpecialNewlines(JSON.stringify(text.text))) + ")") } -function genSlot (el) { +function genComment (comment) { + return ("_e(" + (JSON.stringify(comment.text)) + ")") +} + +function genSlot (el, state) { var slotName = el.slotName || '"default"'; - var children = genChildren(el); + var children = genChildren(el, state); var res = "_t(" + slotName + (children ? ("," + children) : ''); var attrs = el.attrs && ("{" + (el.attrs.map(function (a) { return ((camelize(a.name)) + ":" + (a.value)); }).join(',')) + "}"); var bind$$1 = el.attrsMap['v-bind']; @@ -2446,9 +3142,13 @@ function genSlot (el) { } // componentName is el.component, take it as argument to shun flow's pessimistic refinement -function genComponent (componentName, el) { - var children = el.inlineTemplate ? null : genChildren(el, true); - return ("_c(" + componentName + "," + (genData(el)) + (children ? ("," + children) : '') + ")") +function genComponent ( + componentName, + el, + state +) { + var children = el.inlineTemplate ? null : genChildren(el, state, true); + return ("_c(" + componentName + "," + (genData(el, state)) + (children ? ("," + children) : '') + ")") } function genProps (props) { @@ -2566,21 +3266,7 @@ function checkExpression (exp, text, errors) { /* */ -function baseCompile ( - template, - options -) { - var ast = parse(template.trim(), options); - optimize(ast, options); - var code = generate(ast, options); - return { - ast: ast, - render: code.render, - staticRenderFns: code.staticRenderFns - } -} - -function makeFunction (code, errors) { +function createFunction (code, errors) { try { return new Function(code) } catch (err) { @@ -2589,50 +3275,10 @@ function makeFunction (code, errors) { } } -function createCompiler (baseOptions) { - var functionCompileCache = Object.create(null); - - function compile ( - template, - options - ) { - var finalOptions = Object.create(baseOptions); - var errors = []; - var tips = []; - finalOptions.warn = function (msg, tip$$1) { - (tip$$1 ? tips : errors).push(msg); - }; - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = (baseOptions.modules || []).concat(options.modules); - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ); - } - // copy other options - for (var key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key]; - } - } - } - - var compiled = baseCompile(template, finalOptions); - if (process.env.NODE_ENV !== 'production') { - errors.push.apply(errors, detectErrors(compiled.ast)); - } - compiled.errors = errors; - compiled.tips = tips; - return compiled - } +function createCompileToFunctionFn (compile) { + var cache = Object.create(null); - function compileToFunctions ( + return function compileToFunctions ( template, options, vm @@ -2661,8 +3307,8 @@ function createCompiler (baseOptions) { var key = options.delimiters ? String(options.delimiters) + template : template; - if (functionCompileCache[key]) { - return functionCompileCache[key] + if (cache[key]) { + return cache[key] } // compile @@ -2685,12 +3331,10 @@ function createCompiler (baseOptions) { // turn code into functions var res = {}; var fnGenErrors = []; - res.render = makeFunction(compiled.render, fnGenErrors); - var l = compiled.staticRenderFns.length; - res.staticRenderFns = new Array(l); - for (var i = 0; i < l; i++) { - res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i], fnGenErrors); - } + res.render = createFunction(compiled.render, fnGenErrors); + res.staticRenderFns = compiled.staticRenderFns.map(function (code) { + return createFunction(code, fnGenErrors) + }); // check function generation errors. // this should only happen if there is a bug in the compiler itself. @@ -2711,17 +3355,83 @@ function createCompiler (baseOptions) { } } - return (functionCompileCache[key] = res) + return (cache[key] = res) } +} - return { - compile: compile, - compileToFunctions: compileToFunctions +/* */ + +function createCompilerCreator (baseCompile) { + return function createCompiler (baseOptions) { + function compile ( + template, + options + ) { + var finalOptions = Object.create(baseOptions); + var errors = []; + var tips = []; + finalOptions.warn = function (msg, tip) { + (tip ? tips : errors).push(msg); + }; + + if (options) { + // merge custom modules + if (options.modules) { + finalOptions.modules = + (baseOptions.modules || []).concat(options.modules); + } + // merge custom directives + if (options.directives) { + finalOptions.directives = extend( + Object.create(baseOptions.directives), + options.directives + ); + } + // copy other options + for (var key in options) { + if (key !== 'modules' && key !== 'directives') { + finalOptions[key] = options[key]; + } + } + } + + var compiled = baseCompile(template, finalOptions); + if (process.env.NODE_ENV !== 'production') { + errors.push.apply(errors, detectErrors(compiled.ast)); + } + compiled.errors = errors; + compiled.tips = tips; + return compiled + } + + return { + compile: compile, + compileToFunctions: createCompileToFunctionFn(compile) + } } } /* */ +// `createCompilerCreator` allows creating compilers that use alternative +// parser/optimizer/codegen, e.g the SSR optimizing compiler. +// Here we just export a default compiler using the default parts. +var createCompiler = createCompilerCreator(function baseCompile ( + template, + options +) { + var ast = parse(template.trim(), options); + optimize(ast, options); + var code = generate(ast, options); + return { + ast: ast, + render: code.render, + staticRenderFns: code.staticRenderFns + } +}); + +/* */ + function transformNode (el, options) { var warn = options.warn || baseWarn; var staticClass = getAndRemoveAttr(el, 'class'); @@ -2860,7 +3570,7 @@ var style = { var normalize$1 = cached(camelize); -function normalizeKeyName (str) { +function normalizeKeyName (str) { if (str.match(/^v\-/)) { return str.replace(/(v-[a-z\-]+\:)([a-z\-]+)$/i, function ($, directive, prop) { return directive + normalize$1(prop) diff --git a/packages/weex-template-compiler/package.json b/packages/weex-template-compiler/package.json index 08693d4861e..19c493a216e 100644 --- a/packages/weex-template-compiler/package.json +++ b/packages/weex-template-compiler/package.json @@ -1,6 +1,6 @@ { "name": "weex-template-compiler", - "version": "2.1.9-weex.1", + "version": "2.4.2-weex.1", "description": "Weex template compiler for Vue 2.0", "main": "index.js", "repository": { diff --git a/packages/weex-vue-framework/factory.js b/packages/weex-vue-framework/factory.js index 70ed93943a6..155b38a2db4 100644 --- a/packages/weex-vue-framework/factory.js +++ b/packages/weex-vue-framework/factory.js @@ -18,11 +18,19 @@ function isTrue (v) { return v === true } +function isFalse (v) { + return v === false +} + /** * Check if value is primitive */ function isPrimitive (value) { - return typeof value === 'string' || typeof value === 'number' + return ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) } /** @@ -34,24 +42,32 @@ function isObject (obj) { return obj !== null && typeof obj === 'object' } -var toString = Object.prototype.toString; +var _toString = Object.prototype.toString; /** * Strict object type check. Only returns true * for plain JavaScript objects. */ function isPlainObject (obj) { - return toString.call(obj) === '[object Object]' + return _toString.call(obj) === '[object Object]' } function isRegExp (v) { - return toString.call(v) === '[object RegExp]' + return _toString.call(v) === '[object RegExp]' +} + +/** + * Check if val is a valid array index. + */ +function isValidArrayIndex (val) { + var n = parseFloat(val); + return n >= 0 && Math.floor(n) === n && isFinite(val) } /** * Convert a value to a string that is actually rendered. */ -function _toString (val) { +function toString (val) { return val == null ? '' : typeof val === 'object' @@ -91,6 +107,11 @@ function makeMap ( */ var isBuiltInTag = makeMap('slot,component', true); +/** + * Check if a attribute is a reserved attribute. + */ +var isReservedAttribute = makeMap('key,ref,slot,is'); + /** * Remove an item from an array */ @@ -203,13 +224,15 @@ function toObject (arr) { /** * Perform no operation. + * Stubbing args to make Flow happy without leaving useless transpiled code + * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/) */ -function noop () {} +function noop (a, b, c) {} /** * Always return false. */ -var no = function () { return false; }; +var no = function (a, b, c) { return false; }; /** * Return same value @@ -226,14 +249,30 @@ var identity = function (_) { return _; }; * if they are plain objects, do they have the same shape? */ function looseEqual (a, b) { + if (a === b) { return true } var isObjectA = isObject(a); var isObjectB = isObject(b); if (isObjectA && isObjectB) { try { - return JSON.stringify(a) === JSON.stringify(b) + var isArrayA = Array.isArray(a); + var isArrayB = Array.isArray(b); + if (isArrayA && isArrayB) { + return a.length === b.length && a.every(function (e, i) { + return looseEqual(e, b[i]) + }) + } else if (!isArrayA && !isArrayB) { + var keysA = Object.keys(a); + var keysB = Object.keys(b); + return keysA.length === keysB.length && keysA.every(function (key) { + return looseEqual(a[key], b[key]) + }) + } else { + /* istanbul ignore next */ + return false + } } catch (e) { - // possible circular reference - return a === b + /* istanbul ignore next */ + return false } } else if (!isObjectA && !isObjectB) { return String(a) === String(b) @@ -316,6 +355,11 @@ var config = ({ */ errorHandler: null, + /** + * Warn handler for watcher warns + */ + warnHandler: null, + /** * Ignore certain custom elements */ @@ -408,9 +452,11 @@ function parsePath (path) { } } +/* */ + var warn = noop; var tip = noop; -var formatComponentName; +var formatComponentName = (null); // work around flow check if (process.env.NODE_ENV !== 'production') { var hasConsole = typeof console !== 'undefined'; @@ -420,10 +466,12 @@ if (process.env.NODE_ENV !== 'production') { .replace(/[-_]/g, ''); }; warn = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.error("[Vue warn]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); + var trace = vm ? generateComponentTrace(vm) : ''; + + if (config.warnHandler) { + config.warnHandler.call(null, msg, vm, trace); + } else if (hasConsole && (!config.silent)) { + console.error(("[Vue warn]: " + msg + trace)); } }; @@ -499,6 +547,8 @@ if (process.env.NODE_ENV !== 'production') { }; } +/* */ + function handleError (err, vm, info) { if (config.errorHandler) { config.errorHandler.call(null, err, vm, info); @@ -531,6 +581,9 @@ var isAndroid = UA && UA.indexOf('android') > 0; var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; +// Firefix has a "watch" function on Object.prototype... +var nativeWatch = ({}).watch; + var supportsPassive = false; if (inBrowser) { try { @@ -540,7 +593,7 @@ if (inBrowser) { /* istanbul ignore next */ supportsPassive = true; } - } )); // https://github.com/facebook/flow/issues/285 + })); // https://github.com/facebook/flow/issues/285 window.addEventListener('test-passive', null, opts); } catch (e) {} } @@ -755,22 +808,14 @@ var arrayMethods = Object.create(arrayProto);[ // cache original method var original = arrayProto[method]; def(arrayMethods, method, function mutator () { - var arguments$1 = arguments; + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; - // avoid leaking arguments: - // http://jsperf.com/closure-with-arguments - var i = arguments.length; - var args = new Array(i); - while (i--) { - args[i] = arguments$1[i]; - } var result = original.apply(this, args); var ob = this.__ob__; var inserted; switch (method) { case 'push': - inserted = args; - break case 'unshift': inserted = args; break @@ -796,8 +841,7 @@ var arrayKeys = Object.getOwnPropertyNames(arrayMethods); * under a frozen data structure. Converting it would defeat the optimization. */ var observerState = { - shouldConvert: true, - isSettingProps: false + shouldConvert: true }; /** @@ -849,7 +893,7 @@ Observer.prototype.observeArray = function observeArray (items) { * Augment an target Object or Array by intercepting * the prototype chain using __proto__ */ -function protoAugment (target, src) { +function protoAugment (target, src, keys) { /* eslint-disable no-proto */ target.__proto__ = src; /* eslint-enable no-proto */ @@ -901,7 +945,8 @@ function defineReactive$$1 ( obj, key, val, - customSetter + customSetter, + shallow ) { var dep = new Dep(); @@ -914,7 +959,7 @@ function defineReactive$$1 ( var getter = property && property.get; var setter = property && property.set; - var childOb = observe(val); + var childOb = !shallow && observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, @@ -946,7 +991,7 @@ function defineReactive$$1 ( } else { val = newVal; } - childOb = observe(newVal); + childOb = !shallow && observe(newVal); dep.notify(); } }); @@ -958,7 +1003,7 @@ function defineReactive$$1 ( * already exist. */ function set (target, key, val) { - if (Array.isArray(target) && typeof key === 'number') { + if (Array.isArray(target) && isValidArrayIndex(key)) { target.length = Math.max(target.length, key); target.splice(key, 1, val); return val @@ -967,7 +1012,7 @@ function set (target, key, val) { target[key] = val; return val } - var ob = (target ).__ob__; + var ob = (target).__ob__; if (target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV !== 'production' && warn( 'Avoid adding reactive properties to a Vue instance or its root $data ' + @@ -988,11 +1033,11 @@ function set (target, key, val) { * Delete a property and trigger change if necessary. */ function del (target, key) { - if (Array.isArray(target) && typeof key === 'number') { + if (Array.isArray(target) && isValidArrayIndex(key)) { target.splice(key, 1); return } - var ob = (target ).__ob__; + var ob = (target).__ob__; if (target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV !== 'production' && warn( 'Avoid deleting properties on a Vue instance or its root $data ' + @@ -1071,7 +1116,7 @@ function mergeData (to, from) { /** * Data */ -strats.data = function ( +function mergeDataOrFn ( parentVal, childVal, vm @@ -1081,15 +1126,6 @@ strats.data = function ( if (!childVal) { return parentVal } - if (typeof childVal !== 'function') { - process.env.NODE_ENV !== 'production' && warn( - 'The "data" option should be a function ' + - 'that returns a per-instance value in component ' + - 'definitions.', - vm - ); - return parentVal - } if (!parentVal) { return childVal } @@ -1100,8 +1136,8 @@ strats.data = function ( // it has to be a function to pass previous merges. return function mergedDataFn () { return mergeData( - childVal.call(this), - parentVal.call(this) + typeof childVal === 'function' ? childVal.call(this) : childVal, + typeof parentVal === 'function' ? parentVal.call(this) : parentVal ) } } else if (parentVal || childVal) { @@ -1120,6 +1156,28 @@ strats.data = function ( } } } +} + +strats.data = function ( + parentVal, + childVal, + vm +) { + if (!vm) { + if (childVal && typeof childVal !== 'function') { + process.env.NODE_ENV !== 'production' && warn( + 'The "data" option should be a function ' + + 'that returns a per-instance value in component ' + + 'definitions.', + vm + ); + + return parentVal + } + return mergeDataOrFn.call(this, parentVal, childVal) + } + + return mergeDataOrFn(parentVal, childVal, vm) }; /** @@ -1167,6 +1225,9 @@ ASSET_TYPES.forEach(function (type) { * another, so we merge them as arrays. */ strats.watch = function (parentVal, childVal) { + // work around Firefox's Object.prototype.watch... + if (parentVal === nativeWatch) { parentVal = undefined; } + if (childVal === nativeWatch) { childVal = undefined; } /* istanbul ignore if */ if (!childVal) { return Object.create(parentVal || null) } if (!parentVal) { return childVal } @@ -1180,7 +1241,7 @@ strats.watch = function (parentVal, childVal) { } ret[key] = parent ? parent.concat(child) - : [child]; + : Array.isArray(child) ? child : [child]; } return ret }; @@ -1190,14 +1251,15 @@ strats.watch = function (parentVal, childVal) { */ strats.props = strats.methods = +strats.inject = strats.computed = function (parentVal, childVal) { - if (!childVal) { return Object.create(parentVal || null) } if (!parentVal) { return childVal } var ret = Object.create(null); extend(ret, parentVal); - extend(ret, childVal); + if (childVal) { extend(ret, childVal); } return ret }; +strats.provide = mergeDataOrFn; /** * Default strategy. @@ -1255,6 +1317,19 @@ function normalizeProps (options) { options.props = res; } +/** + * Normalize all injections into Object-based format + */ +function normalizeInject (options) { + var inject = options.inject; + if (Array.isArray(inject)) { + var normalized = options.inject = {}; + for (var i = 0; i < inject.length; i++) { + normalized[inject[i]] = inject[i]; + } + } +} + /** * Normalize raw function directives into object format. */ @@ -1288,6 +1363,7 @@ function mergeOptions ( } normalizeProps(child); + normalizeInject(child); normalizeDirectives(child); var extendsFrom = child.extends; if (extendsFrom) { @@ -1405,7 +1481,8 @@ function getPropDefaultValue (vm, prop, key) { // return previous default value to avoid unnecessary watcher trigger if (vm && vm.$options.propsData && vm.$options.propsData[key] === undefined && - vm._props[key] !== undefined) { + vm._props[key] !== undefined + ) { return vm._props[key] } // call factory function for non-Function types @@ -1511,6 +1588,8 @@ function isType (type, fn) { return false } +/* */ + /* not type checking this file because flow doesn't play well with Proxy */ var initProxy; @@ -1617,7 +1696,8 @@ var VNode = function VNode ( text, elm, context, - componentOptions + componentOptions, + asyncFactory ) { this.tag = tag; this.data = data; @@ -1637,6 +1717,9 @@ var VNode = function VNode ( this.isComment = false; this.isCloned = false; this.isOnce = false; + this.asyncFactory = asyncFactory; + this.asyncMeta = undefined; + this.isAsyncPlaceholder = false; }; var prototypeAccessors = { child: {} }; @@ -1649,9 +1732,11 @@ prototypeAccessors.child.get = function () { Object.defineProperties( VNode.prototype, prototypeAccessors ); -var createEmptyVNode = function () { +var createEmptyVNode = function (text) { + if ( text === void 0 ) text = ''; + var node = new VNode(); - node.text = ''; + node.text = text; node.isComment = true; return node }; @@ -1672,11 +1757,13 @@ function cloneVNode (vnode) { vnode.text, vnode.elm, vnode.context, - vnode.componentOptions + vnode.componentOptions, + vnode.asyncFactory ); cloned.ns = vnode.ns; cloned.isStatic = vnode.isStatic; cloned.key = vnode.key; + cloned.isComment = vnode.isComment; cloned.isCloned = true; return cloned } @@ -1713,8 +1800,9 @@ function createFnInvoker (fns) { var fns = invoker.fns; if (Array.isArray(fns)) { - for (var i = 0; i < fns.length; i++) { - fns[i].apply(null, arguments$1); + var cloned = fns.slice(); + for (var i = 0; i < cloned.length; i++) { + cloned[i].apply(null, arguments$1); } } else { // return handler return value for single handlers @@ -1895,6 +1983,10 @@ function normalizeChildren (children) { : undefined } +function isTextNode (node) { + return isDef(node) && isDef(node.text) && isFalse(node.isComment) +} + function normalizeArrayChildren (children, nestedIndex) { var res = []; var i, c, last; @@ -1906,19 +1998,26 @@ function normalizeArrayChildren (children, nestedIndex) { if (Array.isArray(c)) { res.push.apply(res, normalizeArrayChildren(c, ((nestedIndex || '') + "_" + i))); } else if (isPrimitive(c)) { - if (isDef(last) && isDef(last.text)) { + if (isTextNode(last)) { + // merge adjacent text nodes + // this is necessary for SSR hydration because text nodes are + // essentially merged when rendered to HTML strings (last).text += String(c); } else if (c !== '') { // convert primitive to vnode res.push(createTextVNode(c)); } } else { - if (isDef(c.text) && isDef(last) && isDef(last.text)) { + if (isTextNode(c) && isTextNode(last)) { + // merge adjacent text nodes res[res.length - 1] = createTextVNode(last.text + c.text); } else { // default key for nested array children (likely generated by v-for) - if (isDef(c.tag) && isUndef(c.key) && isDef(nestedIndex)) { - c.key = "__vlist" + ((nestedIndex)) + "_" + i + "__"; + if (isTrue(children._isVList) && + isDef(c.tag) && + isUndef(c.key) && + isDef(nestedIndex)) { + c.key = "__vlist" + nestedIndex + "_" + i + "__"; } res.push(c); } @@ -1930,11 +2029,27 @@ function normalizeArrayChildren (children, nestedIndex) { /* */ function ensureCtor (comp, base) { + if (comp.__esModule && comp.default) { + comp = comp.default; + } return isObject(comp) ? base.extend(comp) : comp } +function createAsyncPlaceholder ( + factory, + data, + context, + children, + tag +) { + var node = createEmptyVNode(); + node.asyncFactory = factory; + node.asyncMeta = { data: data, context: context, children: children, tag: tag }; + return node +} + function resolveAsyncComponent ( factory, baseCtor, @@ -2017,11 +2132,13 @@ function resolveAsyncComponent ( if (isDef(res.timeout)) { setTimeout(function () { - reject( - process.env.NODE_ENV !== 'production' - ? ("timeout (" + (res.timeout) + "ms)") - : null - ); + if (isUndef(factory.resolved)) { + reject( + process.env.NODE_ENV !== 'production' + ? ("timeout (" + (res.timeout) + "ms)") + : null + ); + } }, res.timeout); } } @@ -2174,7 +2291,11 @@ function eventsMixin (Vue) { cbs = cbs.length > 1 ? toArray(cbs) : cbs; var args = toArray(arguments, 1); for (var i = 0, l = cbs.length; i < l; i++) { - cbs[i].apply(vm, args); + try { + cbs[i].apply(vm, args); + } catch (e) { + handleError(e, vm, ("event handler for \"" + event + "\"")); + } } } return vm @@ -2200,7 +2321,8 @@ function resolveSlots ( // named slots should only be respected if the vnode was rendered in the // same context. if ((child.context === context || child.functionalContext === context) && - child.data && child.data.slot != null) { + child.data && child.data.slot != null + ) { var name = child.data.slot; var slot = (slots[name] || (slots[name] = [])); if (child.tag === 'template') { @@ -2224,11 +2346,16 @@ function isWhitespace (node) { } function resolveScopedSlots ( - fns + fns, // see flow/vnode + res ) { - var res = {}; + res = res || {}; for (var i = 0; i < fns.length; i++) { - res[fns[i][0]] = fns[i][1]; + if (Array.isArray(fns[i])) { + resolveScopedSlots(fns[i], res); + } else { + res[fns[i].key] = fns[i].fn; + } } return res } @@ -2236,6 +2363,7 @@ function resolveScopedSlots ( /* */ var activeInstance = null; +var isUpdatingChildComponent = false; function initLifecycle (vm) { var options = vm.$options; @@ -2283,6 +2411,9 @@ function lifecycleMixin (Vue) { vm.$options._parentElm, vm.$options._refElm ); + // no need for the ref nodes after initial patch + // this prevents keeping a detached DOM tree in memory (#5851) + vm.$options._parentElm = vm.$options._refElm = null; } else { // updates vm.$el = vm.__patch__(prevVnode, vnode); @@ -2347,8 +2478,6 @@ function lifecycleMixin (Vue) { if (vm.$el) { vm.$el.__vue__ = null; } - // remove reference to DOM nodes (prevents leak) - vm.$options._parentElm = vm.$options._refElm = null; }; } @@ -2424,6 +2553,10 @@ function updateChildComponent ( parentVnode, renderChildren ) { + if (process.env.NODE_ENV !== 'production') { + isUpdatingChildComponent = true; + } + // determine whether component has slot children // we need to do this before overwriting $options._renderChildren var hasChildren = !!( @@ -2435,17 +2568,21 @@ function updateChildComponent ( vm.$options._parentVnode = parentVnode; vm.$vnode = parentVnode; // update vm's placeholder node without re-render + if (vm._vnode) { // update child tree's parent vm._vnode.parent = parentVnode; } vm.$options._renderChildren = renderChildren; + // update $attrs and $listensers hash + // these are also reactive so they may trigger child update if the child + // used them during render + vm.$attrs = parentVnode.data && parentVnode.data.attrs; + vm.$listeners = listeners; + // update props if (propsData && vm.$options.props) { observerState.shouldConvert = false; - if (process.env.NODE_ENV !== 'production') { - observerState.isSettingProps = true; - } var props = vm._props; var propKeys = vm.$options._propKeys || []; for (var i = 0; i < propKeys.length; i++) { @@ -2453,12 +2590,10 @@ function updateChildComponent ( props[key] = validateProp(key, vm.$options.props, propsData, vm); } observerState.shouldConvert = true; - if (process.env.NODE_ENV !== 'production') { - observerState.isSettingProps = false; - } // keep a copy of raw propsData vm.$options.propsData = propsData; } + // update listeners if (listeners) { var oldListeners = vm.$options._parentListeners; @@ -2470,6 +2605,10 @@ function updateChildComponent ( vm.$slots = resolveSlots(renderChildren, parentVnode.context); vm.$forceUpdate(); } + + if (process.env.NODE_ENV !== 'production') { + isUpdatingChildComponent = false; + } } function isInInactiveTree (vm) { @@ -2546,7 +2685,7 @@ var index = 0; * Reset the scheduler's state. */ function resetSchedulerState () { - queue.length = activatedChildren.length = 0; + index = queue.length = activatedChildren.length = 0; has = {}; if (process.env.NODE_ENV !== 'production') { circular = {}; @@ -2603,7 +2742,7 @@ function flushSchedulerQueue () { // call component updated and activated hooks callActivatedHooks(activatedQueue); - callUpdateHooks(updatedQueue); + callUpdatedHooks(updatedQueue); // devtool hook /* istanbul ignore if */ @@ -2612,7 +2751,7 @@ function flushSchedulerQueue () { } } -function callUpdateHooks (queue) { +function callUpdatedHooks (queue) { var i = queue.length; while (i--) { var watcher = queue[i]; @@ -2656,10 +2795,10 @@ function queueWatcher (watcher) { // if already flushing, splice the watcher based on its id // if already past its id, it will be run next immediately. var i = queue.length - 1; - while (i >= 0 && queue[i].id > watcher.id) { + while (i > index && queue[i].id > watcher.id) { i--; } - queue.splice(Math.max(i, index) + 1, 0, watcher); + queue.splice(i + 1, 0, watcher); } // queue the flush if (!waiting) { @@ -2733,22 +2872,23 @@ Watcher.prototype.get = function get () { pushTarget(this); var value; var vm = this.vm; - if (this.user) { - try { - value = this.getter.call(vm, vm); - } catch (e) { + try { + value = this.getter.call(vm, vm); + } catch (e) { + if (this.user) { handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\"")); + } else { + throw e } - } else { - value = this.getter.call(vm, vm); - } - // "touch" every property so they are all tracked as - // dependencies for deep watching - if (this.deep) { - traverse(value); + } finally { + // "touch" every property so they are all tracked as + // dependencies for deep watching + if (this.deep) { + traverse(value); + } + popTarget(); + this.cleanupDeps(); } - popTarget(); - this.cleanupDeps(); return value }; @@ -2941,14 +3081,20 @@ function initState (vm) { observe(vm._data = {}, true /* asRootData */); } if (opts.computed) { initComputed(vm, opts.computed); } - if (opts.watch) { initWatch(vm, opts.watch); } + if (opts.watch && opts.watch !== nativeWatch) { + initWatch(vm, opts.watch); + } } -var isReservedProp = { - key: 1, - ref: 1, - slot: 1 -}; +function checkOptionType (vm, name) { + var option = vm.$options[name]; + if (!isPlainObject(option)) { + warn( + ("component option \"" + name + "\" should be an object."), + vm + ); + } +} function initProps (vm, propsOptions) { var propsData = vm.$options.propsData || {}; @@ -2964,14 +3110,14 @@ function initProps (vm, propsOptions) { var value = validateProp(key, propsOptions, propsData, vm); /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { - if (isReservedProp[key] || config.isReservedAttr(key)) { + if (isReservedAttribute(key) || config.isReservedAttr(key)) { warn( ("\"" + key + "\" is a reserved attribute and cannot be used as component prop."), vm ); } defineReactive$$1(props, key, value, function () { - if (vm.$parent && !observerState.isSettingProps) { + if (vm.$parent && !isUpdatingChildComponent) { warn( "Avoid mutating a prop directly since the value will be " + "overwritten whenever the parent component re-renders. " + @@ -3012,16 +3158,26 @@ function initData (vm) { // proxy data on instance var keys = Object.keys(data); var props = vm.$options.props; + var methods = vm.$options.methods; var i = keys.length; while (i--) { - if (props && hasOwn(props, keys[i])) { + var key = keys[i]; + if (process.env.NODE_ENV !== 'production') { + if (methods && hasOwn(methods, key)) { + warn( + ("method \"" + key + "\" has already been defined as a data property."), + vm + ); + } + } + if (props && hasOwn(props, key)) { process.env.NODE_ENV !== 'production' && warn( - "The data property \"" + (keys[i]) + "\" is already declared as a prop. " + + "The data property \"" + key + "\" is already declared as a prop. " + "Use prop default value instead.", vm ); - } else if (!isReserved(keys[i])) { - proxy(vm, "_data", keys[i]); + } else if (!isReserved(key)) { + proxy(vm, "_data", key); } } // observe data @@ -3040,22 +3196,20 @@ function getData (data, vm) { var computedWatcherOptions = { lazy: true }; function initComputed (vm, computed) { + process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'computed'); var watchers = vm._computedWatchers = Object.create(null); for (var key in computed) { var userDef = computed[key]; var getter = typeof userDef === 'function' ? userDef : userDef.get; - if (process.env.NODE_ENV !== 'production') { - if (getter === undefined) { - warn( - ("No getter function has been defined for computed property \"" + key + "\"."), - vm - ); - getter = noop; - } + if (process.env.NODE_ENV !== 'production' && getter == null) { + warn( + ("Getter is missing for computed property \"" + key + "\"."), + vm + ); } // create internal watcher for the computed property. - watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions); + watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions); // component-defined computed properties are already defined on the // component prototype. We only need to define computed properties defined @@ -3086,6 +3240,15 @@ function defineComputed (target, key, userDef) { ? userDef.set : noop; } + if (process.env.NODE_ENV !== 'production' && + sharedPropertyDefinition.set === noop) { + sharedPropertyDefinition.set = function () { + warn( + ("Computed property \"" + key + "\" was assigned to but it has no setter."), + this + ); + }; + } Object.defineProperty(target, key, sharedPropertyDefinition); } @@ -3105,6 +3268,7 @@ function createComputedGetter (key) { } function initMethods (vm, methods) { + process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'methods'); var props = vm.$options.props; for (var key in methods) { vm[key] = methods[key] == null ? noop : bind(methods[key], vm); @@ -3127,6 +3291,7 @@ function initMethods (vm, methods) { } function initWatch (vm, watch) { + process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'watch'); for (var key in watch) { var handler = watch[key]; if (Array.isArray(handler)) { @@ -3139,8 +3304,12 @@ function initWatch (vm, watch) { } } -function createWatcher (vm, key, handler) { - var options; +function createWatcher ( + vm, + keyOrFn, + handler, + options +) { if (isPlainObject(handler)) { options = handler; handler = handler.handler; @@ -3148,7 +3317,7 @@ function createWatcher (vm, key, handler) { if (typeof handler === 'string') { handler = vm[handler]; } - vm.$watch(key, handler, options); + return vm.$watch(keyOrFn, handler, options) } function stateMixin (Vue) { @@ -3183,6 +3352,9 @@ function stateMixin (Vue) { options ) { var vm = this; + if (isPlainObject(cb)) { + return createWatcher(vm, expOrFn, cb, options) + } options = options || {}; options.user = true; var watcher = new Watcher(vm, expOrFn, cb, options); @@ -3209,6 +3381,7 @@ function initProvide (vm) { function initInjections (vm) { var result = resolveInject(vm.$options.inject, vm); if (result) { + observerState.shouldConvert = false; Object.keys(result).forEach(function (key) { /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { @@ -3224,24 +3397,21 @@ function initInjections (vm) { defineReactive$$1(vm, key, result[key]); } }); + observerState.shouldConvert = true; } } function resolveInject (inject, vm) { if (inject) { // inject is :any because flow is not smart enough to figure out cached - // isArray here - var isArray = Array.isArray(inject); var result = Object.create(null); - var keys = isArray - ? inject - : hasSymbol + var keys = hasSymbol ? Reflect.ownKeys(inject) : Object.keys(inject); for (var i = 0; i < keys.length; i++) { var key = keys[i]; - var provideKey = isArray ? key : inject[key]; + var provideKey = inject[key]; var source = vm; while (source) { if (source._provided && provideKey in source._provided) { @@ -3250,6 +3420,9 @@ function resolveInject (inject, vm) { } source = source.$parent; } + if (process.env.NODE_ENV !== 'production' && !source) { + warn(("Injection \"" + key + "\" not found"), vm); + } } return result } @@ -3268,7 +3441,7 @@ function createFunctionalComponent ( var propOptions = Ctor.options.props; if (isDef(propOptions)) { for (var key in propOptions) { - props[key] = validateProp(key, propOptions, propsData); + props[key] = validateProp(key, propOptions, propsData || {}); } } else { if (isDef(data.attrs)) { mergeProps(props, data.attrs); } @@ -3289,6 +3462,7 @@ function createFunctionalComponent ( }); if (vnode instanceof VNode) { vnode.functionalContext = context; + vnode.functionalOptions = Ctor.options; if (data.slot) { (vnode.data || (vnode.data = {})).slot = data.slot; } @@ -3402,21 +3576,30 @@ function createComponent ( } // async component + var asyncFactory; if (isUndef(Ctor.cid)) { - Ctor = resolveAsyncComponent(Ctor, baseCtor, context); + asyncFactory = Ctor; + Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context); if (Ctor === undefined) { - // return nothing if this is indeed an async component - // wait for the callback to trigger parent update. - return + // return a placeholder node for async component, which is rendered + // as a comment node but preserves all the raw information for the node. + // the information will be used for async server-rendering and hydration. + return createAsyncPlaceholder( + asyncFactory, + data, + context, + children, + tag + ) } } + data = data || {}; + // resolve constructor options in case global mixins are applied after // component constructor creation resolveConstructorOptions(Ctor); - data = data || {}; - // transform component v-model data into props & events if (isDef(data.model)) { transformModel(Ctor.options, data); @@ -3434,12 +3617,19 @@ function createComponent ( // child component listeners instead of DOM listeners var listeners = data.on; // replace with listeners with .native modifier + // so it gets processed during parent component patch. data.on = data.nativeOn; if (isTrue(Ctor.options.abstract)) { // abstract components do not keep anything - // other than props & listeners + // other than props & listeners & slot + + // work around flow + var slot = data.slot; data = {}; + if (slot) { + data.slot = slot; + } } // merge component management hooks onto the placeholder node @@ -3450,7 +3640,8 @@ function createComponent ( var vnode = new VNode( ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')), data, undefined, undefined, undefined, context, - { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children } + { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, + asyncFactory ); return vnode } @@ -3555,13 +3746,28 @@ function _createElement ( ); return createEmptyVNode() } + // object syntax in v-bind + if (isDef(data) && isDef(data.is)) { + tag = data.is; + } if (!tag) { // in case of component :is set to falsy value return createEmptyVNode() } + // warn against non-primitive key + if (process.env.NODE_ENV !== 'production' && + isDef(data) && isDef(data.key) && !isPrimitive(data.key) + ) { + warn( + 'Avoid using non-primitive value as key, ' + + 'use string/number value instead.', + context + ); + } // support single function children as default scoped slot if (Array.isArray(children) && - typeof children[0] === 'function') { + typeof children[0] === 'function' + ) { data = data || {}; data.scopedSlots = { default: children[0] }; children.length = 0; @@ -3597,7 +3803,7 @@ function _createElement ( // direct component options / constructor vnode = createComponent(tag, data, context, children); } - if (vnode !== undefined) { + if (isDef(vnode)) { if (ns) { applyNS(vnode, ns); } return vnode } else { @@ -3611,7 +3817,7 @@ function applyNS (vnode, ns) { // use default namespace inside foreignObject return } - if (Array.isArray(vnode.children)) { + if (isDef(vnode.children)) { for (var i = 0, l = vnode.children.length; i < l; i++) { var child = vnode.children[i]; if (isDef(child.tag) && isUndef(child.ns)) { @@ -3649,6 +3855,9 @@ function renderList ( ret[i] = render(val[key], key, i); } } + if (isDef(ret)) { + (ret)._isVList = true; + } return ret } @@ -3667,7 +3876,7 @@ function renderSlot ( if (scopedSlotFn) { // scoped slot props = props || {}; if (bindObject) { - extend(props, bindObject); + props = extend(extend({}, bindObject), props); } return scopedSlotFn(props) || fallback } else { @@ -3721,7 +3930,8 @@ function bindObjectProps ( data, tag, value, - asProp + asProp, + isSync ) { if (value) { if (!isObject(value)) { @@ -3734,8 +3944,12 @@ function bindObjectProps ( value = toObject(value); } var hash; - for (var key in value) { - if (key === 'class' || key === 'style') { + var loop = function ( key ) { + if ( + key === 'class' || + key === 'style' || + isReservedAttribute(key) + ) { hash = data; } else { var type = data.attrs && data.attrs.type; @@ -3745,8 +3959,17 @@ function bindObjectProps ( } if (!(key in hash)) { hash[key] = value[key]; + + if (isSync) { + var on = data.on || (data.on = {}); + on[("update:" + key)] = function ($event) { + value[key] = $event; + }; + } } - } + }; + + for (var key in value) loop( key ); } } return data @@ -3813,6 +4036,27 @@ function markStaticNode (node, key, isOnce) { /* */ +function bindObjectListeners (data, value) { + if (value) { + if (!isPlainObject(value)) { + process.env.NODE_ENV !== 'production' && warn( + 'v-on without argument expects an Object value', + this + ); + } else { + var on = data.on = data.on ? extend({}, data.on) : {}; + for (var key in value) { + var existing = on[key]; + var ours = value[key]; + on[key] = existing ? [].concat(ours, existing) : ours; + } + } + } + return data +} + +/* */ + function initRender (vm) { vm._vnode = null; // the root of the child tree vm._staticTrees = null; @@ -3828,6 +4072,22 @@ function initRender (vm) { // normalization is always applied for the public version, used in // user-written render functions. vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); }; + + // $attrs & $listeners are exposed for easier HOC creation. + // they need to be reactive so that HOCs using them are always updated + var parentData = parentVnode && parentVnode.data; + /* istanbul ignore else */ + if (process.env.NODE_ENV !== 'production') { + defineReactive$$1(vm, '$attrs', parentData && parentData.attrs, function () { + !isUpdatingChildComponent && warn("$attrs is readonly.", vm); + }, true); + defineReactive$$1(vm, '$listeners', vm.$options._parentListeners, function () { + !isUpdatingChildComponent && warn("$listeners is readonly.", vm); + }, true); + } else { + defineReactive$$1(vm, '$attrs', parentData && parentData.attrs, null, true); + defineReactive$$1(vm, '$listeners', vm.$options._parentListeners, null, true); + } } function renderMixin (Vue) { @@ -3895,7 +4155,7 @@ function renderMixin (Vue) { // code size. Vue.prototype._o = markOnce; Vue.prototype._n = toNumber; - Vue.prototype._s = _toString; + Vue.prototype._s = toString; Vue.prototype._l = renderList; Vue.prototype._t = renderSlot; Vue.prototype._q = looseEqual; @@ -3907,6 +4167,7 @@ function renderMixin (Vue) { Vue.prototype._v = createTextVNode; Vue.prototype._e = createEmptyVNode; Vue.prototype._u = resolveScopedSlots; + Vue.prototype._g = bindObjectListeners; } /* */ @@ -4048,7 +4309,8 @@ function dedupe (latest, extended, sealed) { function Vue$2 (options) { if (process.env.NODE_ENV !== 'production' && - !(this instanceof Vue$2)) { + !(this instanceof Vue$2) + ) { warn('Vue is a constructor and should be called with the `new` keyword'); } this._init(options); @@ -4064,10 +4326,11 @@ renderMixin(Vue$2); function initUse (Vue) { Vue.use = function (plugin) { - /* istanbul ignore if */ - if (plugin.installed) { - return + var installedPlugins = (this._installedPlugins || (this._installedPlugins = [])); + if (installedPlugins.indexOf(plugin) > -1) { + return this } + // additional parameters var args = toArray(arguments, 1); args.unshift(this); @@ -4076,7 +4339,7 @@ function initUse (Vue) { } else if (typeof plugin === 'function') { plugin.apply(null, args); } - plugin.installed = true; + installedPlugins.push(plugin); return this }; } @@ -4086,6 +4349,7 @@ function initUse (Vue) { function initMixin$1 (Vue) { Vue.mixin = function (mixin) { this.options = mergeOptions(this.options, mixin); + return this }; } @@ -4226,14 +4490,16 @@ function initAssetRegisters (Vue) { /* */ -var patternTypes = [String, RegExp]; +var patternTypes = [String, RegExp, Array]; function getComponentName (opts) { return opts && (opts.Ctor.options.name || opts.tag) } function matches (pattern, name) { - if (typeof pattern === 'string') { + if (Array.isArray(pattern)) { + return pattern.indexOf(name) > -1 + } else if (typeof pattern === 'string') { return pattern.split(',').indexOf(name) > -1 } else if (isRegExp(pattern)) { return pattern.test(name) @@ -4377,7 +4643,14 @@ Object.defineProperty(Vue$2.prototype, '$isServer', { get: isServerRendering }); -Vue$2.version = '2.3.0-beta.1'; +Object.defineProperty(Vue$2.prototype, '$ssrContext', { + get: function get () { + /* istanbul ignore next */ + return this.$vnode && this.$vnode.ssrContext + } +}); + +Vue$2.version = '2.4.2'; /* globals renderer */ // renderer is injected by weex factory wrapper @@ -4508,10 +4781,11 @@ function registerRef (vnode, isRemoval) { } } else { if (vnode.data.refInFor) { - if (Array.isArray(refs[key]) && refs[key].indexOf(ref) < 0) { - refs[key].push(ref); - } else { + if (!Array.isArray(refs[key])) { refs[key] = [ref]; + } else if (refs[key].indexOf(ref) < 0) { + // $flow-disable-line + refs[key].push(ref); } } else { refs[key] = ref; @@ -4539,11 +4813,18 @@ var hooks = ['create', 'activate', 'update', 'remove', 'destroy']; function sameVnode (a, b) { return ( - a.key === b.key && - a.tag === b.tag && - a.isComment === b.isComment && - isDef(a.data) === isDef(b.data) && - sameInputType(a, b) + a.key === b.key && ( + ( + a.tag === b.tag && + a.isComment === b.isComment && + isDef(a.data) === isDef(b.data) && + sameInputType(a, b) + ) || ( + isTrue(a.isAsyncPlaceholder) && + a.asyncFactory === b.asyncFactory && + isUndef(b.asyncFactory.error) + ) + ) ) } @@ -4696,6 +4977,7 @@ function createPatchFunction (backend) { function initComponent (vnode, insertedVnodeQueue) { if (isDef(vnode.data.pendingInsert)) { insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert); + vnode.data.pendingInsert = null; } vnode.elm = vnode.componentInstance.$el; if (isPatchable(vnode)) { @@ -4732,11 +5014,11 @@ function createPatchFunction (backend) { insert(parentElm, vnode.elm, refElm); } - function insert (parent, elm, ref) { + function insert (parent, elm, ref$$1) { if (isDef(parent)) { - if (isDef(ref)) { - if (ref.parentNode === parent) { - nodeOps.insertBefore(parent, elm, ref); + if (isDef(ref$$1)) { + if (ref$$1.parentNode === parent) { + nodeOps.insertBefore(parent, elm, ref$$1); } } else { nodeOps.appendChild(parent, elm); @@ -4786,8 +5068,9 @@ function createPatchFunction (backend) { } // for slot content they should also get the scopeId from the host instance. if (isDef(i = activeInstance) && - i !== vnode.context && - isDef(i = i.$options._scopeId)) { + i !== vnode.context && + isDef(i = i.$options._scopeId) + ) { nodeOps.setAttribute(vnode.elm, i, ''); } } @@ -4912,7 +5195,7 @@ function createPatchFunction (backend) { if (sameVnode(elmToMove, newStartVnode)) { patchVnode(elmToMove, newStartVnode, insertedVnodeQueue); oldCh[idxInOld] = undefined; - canMove && nodeOps.insertBefore(parentElm, newStartVnode.elm, oldStartVnode.elm); + canMove && nodeOps.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm); newStartVnode = newCh[++newStartIdx]; } else { // same key but different element. treat as new element @@ -4934,24 +5217,37 @@ function createPatchFunction (backend) { if (oldVnode === vnode) { return } + + var elm = vnode.elm = oldVnode.elm; + + if (isTrue(oldVnode.isAsyncPlaceholder)) { + if (isDef(vnode.asyncFactory.resolved)) { + hydrate(oldVnode.elm, vnode, insertedVnodeQueue); + } else { + vnode.isAsyncPlaceholder = true; + } + return + } + // reuse element for static trees. // note we only do this if the vnode is cloned - // if the new node is not cloned it means the render functions have been // reset by the hot-reload-api and we need to do a proper re-render. if (isTrue(vnode.isStatic) && - isTrue(oldVnode.isStatic) && - vnode.key === oldVnode.key && - (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))) { - vnode.elm = oldVnode.elm; + isTrue(oldVnode.isStatic) && + vnode.key === oldVnode.key && + (isTrue(vnode.isCloned) || isTrue(vnode.isOnce)) + ) { vnode.componentInstance = oldVnode.componentInstance; return } + var i; var data = vnode.data; if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { i(oldVnode, vnode); } - var elm = vnode.elm = oldVnode.elm; + var oldCh = oldVnode.children; var ch = vnode.children; if (isDef(data) && isPatchable(vnode)) { @@ -4996,6 +5292,11 @@ function createPatchFunction (backend) { // Note: this is a browser-only function so we can assume elms are DOM nodes. function hydrate (elm, vnode, insertedVnodeQueue) { + if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) { + vnode.elm = elm; + vnode.isAsyncPlaceholder = true; + return true + } if (process.env.NODE_ENV !== 'production') { if (!assertNodeMatch(elm, vnode)) { return false @@ -5032,8 +5333,9 @@ function createPatchFunction (backend) { // longer than the virtual children list. if (!childrenMatch || childNode) { if (process.env.NODE_ENV !== 'production' && - typeof console !== 'undefined' && - !bailed) { + typeof console !== 'undefined' && + !bailed + ) { bailed = true; console.warn('Parent: ', elm); console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children); @@ -5313,8 +5615,10 @@ function updateClass (oldVnode, vnode) { var data = vnode.data; var oldData = oldVnode.data; - if (!data.staticClass && !data.class && - (!oldData || (!oldData.staticClass && !oldData.class))) { + if (!data.staticClass && + !data.class && + (!oldData || (!oldData.staticClass && !oldData.class)) + ) { return } @@ -5632,9 +5936,10 @@ function enter (_, vnode) { var parent = el.parentNode; var pendingNode = parent && parent._pending && parent._pending[vnode.key]; if (pendingNode && - pendingNode.context === vnode.context && - pendingNode.tag === vnode.tag && - pendingNode.elm._leaveCb) { + pendingNode.context === vnode.context && + pendingNode.tag === vnode.tag && + pendingNode.elm._leaveCb + ) { pendingNode.elm._leaveCb(); } enterHook && enterHook(el, cb); @@ -5893,6 +6198,10 @@ function isSameChild (child, oldChild) { return oldChild.key === child.key && oldChild.tag === child.tag } +function isAsyncPlaceholder (node) { + return node.isComment && node.asyncFactory +} + var Transition$1 = { name: 'transition', props: transitionProps, @@ -5901,13 +6210,13 @@ var Transition$1 = { render: function render (h) { var this$1 = this; - var children = this.$slots.default; + var children = this.$options._renderChildren; if (!children) { return } // filter out text nodes (possible whitespaces) - children = children.filter(function (c) { return c.tag; }); + children = children.filter(function (c) { return c.tag || isAsyncPlaceholder(c); }); /* istanbul ignore if */ if (!children.length) { return @@ -5926,7 +6235,8 @@ var Transition$1 = { // warn invalid mode if (process.env.NODE_ENV !== 'production' && - mode && mode !== 'in-out' && mode !== 'out-in') { + mode && mode !== 'in-out' && mode !== 'out-in' + ) { warn( 'invalid mode: ' + mode, this.$parent @@ -5958,7 +6268,9 @@ var Transition$1 = { // during entering. var id = "__transition-" + (this._uid) + "-"; child.key = child.key == null - ? id + child.tag + ? child.isComment + ? id + 'comment' + : id + child.tag : isPrimitive(child.key) ? (String(child.key).indexOf(id) === 0 ? child.key : id + child.key) : child.key; @@ -5973,7 +6285,12 @@ var Transition$1 = { child.data.show = true; } - if (oldChild && oldChild.data && !isSameChild(child, oldChild)) { + if ( + oldChild && + oldChild.data && + !isSameChild(child, oldChild) && + !isAsyncPlaceholder(oldChild) + ) { // replace old child transition data with fresh one // important for dynamic transitions! var oldData = oldChild && (oldChild.data.transition = extend({}, data)); @@ -5987,6 +6304,9 @@ var Transition$1 = { }); return placeholder(h, rawChild) } else if (mode === 'in-out') { + if (isAsyncPlaceholder(child)) { + return oldRawChild + } var delayedLeave; var performLeave = function () { delayedLeave(); }; mergeVNodeHook(data, 'afterEnter', performLeave); diff --git a/packages/weex-vue-framework/index.js b/packages/weex-vue-framework/index.js index 70b16298f7f..a05c5f54084 100644 --- a/packages/weex-vue-framework/index.js +++ b/packages/weex-vue-framework/index.js @@ -34,7 +34,7 @@ function init (cfg) { renderer.Document = cfg.Document; renderer.Element = cfg.Element; renderer.Comment = cfg.Comment; - renderer.sendTasks = cfg.sendTasks; + renderer.compileBundle = cfg.compileBundle; } /** @@ -47,7 +47,7 @@ function reset () { delete renderer.Document; delete renderer.Element; delete renderer.Comment; - delete renderer.sendTasks; + delete renderer.compileBundle; } /** @@ -82,18 +82,9 @@ function createInstance ( // Virtual-DOM object. var document = new renderer.Document(instanceId, config.bundleUrl); - // All function/callback of parameters before sent to native - // will be converted as an id. So `callbacks` is used to store - // these real functions. When a callback invoked and won't be - // called again, it should be removed from here automatically. - var callbacks = []; - - // The latest callback id, incremental. - var callbackId = 1; - var instance = instances[instanceId] = { instanceId: instanceId, config: config, data: data, - document: document, callbacks: callbacks, callbackId: callbackId + document: document }; // Prepare native module getter and HTML5 Timer APIs. @@ -104,6 +95,7 @@ function createInstance ( var weexInstanceVar = { config: config, document: document, + supports: supports, requireModule: moduleGetter }; Object.freeze(weexInstanceVar); @@ -118,11 +110,16 @@ function createInstance ( weex: weexInstanceVar, // deprecated __weex_require_module__: weexInstanceVar.requireModule // eslint-disable-line - }, timerAPIs); - callFunction(instanceVars, appCode); + }, timerAPIs, env.services); + + if (!callFunctionNative(instanceVars, appCode)) { + // If failed to compile functionBody on native side, + // fallback to 'callFunction()'. + callFunction(instanceVars, appCode); + } // Send `createFinish` signal to native. - renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'createFinish', args: [] }], -1); + instance.document.taskCenter.send('dom', { action: 'createFinish' }, []); } /** @@ -133,6 +130,7 @@ function createInstance ( function destroyInstance (instanceId) { var instance = instances[instanceId]; if (instance && instance.app instanceof instance.Vue) { + instance.document.destroy(); instance.app.$destroy(); } delete instances[instanceId]; @@ -154,7 +152,7 @@ function refreshInstance (instanceId, data) { instance.Vue.set(instance.app, key, data[key]); } // Finally `refreshFinish` signal needed. - renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'refreshFinish', args: [] }], -1); + instance.document.taskCenter.send('dom', { action: 'refreshFinish' }, []); } /** @@ -169,49 +167,57 @@ function getRoot (instanceId) { return instance.app.$el.toJSON() } +var jsHandlers = { + fireEvent: function (id) { + var args = [], len = arguments.length - 1; + while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; + + return fireEvent.apply(void 0, [ instances[id] ].concat( args )) + }, + callback: function (id) { + var args = [], len = arguments.length - 1; + while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; + + return callback.apply(void 0, [ instances[id] ].concat( args )) + } +}; + +function fireEvent (instance, nodeId, type, e, domChanges) { + var el = instance.document.getRef(nodeId); + if (el) { + return instance.document.fireEvent(el, type, e, domChanges) + } + return new Error(("invalid element reference \"" + nodeId + "\"")) +} + +function callback (instance, callbackId, data, ifKeepAlive) { + var result = instance.document.taskCenter.callback(callbackId, data, ifKeepAlive); + instance.document.taskCenter.send('dom', { action: 'updateFinish' }, []); + return result +} + /** - * Receive tasks from native. Generally there are two types of tasks: - * 1. `fireEvent`: an device actions or user actions from native. - * 2. `callback`: invoke function which sent to native as a parameter before. - * @param {string} instanceId - * @param {array} tasks + * Accept calls from native (event or callback). + * + * @param {string} id + * @param {array} tasks list with `method` and `args` */ -function receiveTasks (instanceId, tasks) { - var instance = instances[instanceId]; - if (!instance || !(instance.app instanceof instance.Vue)) { - return new Error(("receiveTasks: instance " + instanceId + " not found!")) - } - var callbacks = instance.callbacks; - var document = instance.document; - tasks.forEach(function (task) { - // `fireEvent` case: find the event target and fire. - if (task.method === 'fireEvent') { - var ref = task.args; - var nodeId = ref[0]; - var type = ref[1]; - var e = ref[2]; - var domChanges = ref[3]; - var el = document.getRef(nodeId); - document.fireEvent(el, type, e, domChanges); - } - // `callback` case: find the callback by id and call it. - if (task.method === 'callback') { - var ref$1 = task.args; - var callbackId = ref$1[0]; - var data = ref$1[1]; - var ifKeepAlive = ref$1[2]; - var callback = callbacks[callbackId]; - if (typeof callback === 'function') { - callback(data); - // Remove the callback from `callbacks` if it won't called again. - if (typeof ifKeepAlive === 'undefined' || ifKeepAlive === false) { - callbacks[callbackId] = undefined; - } +function receiveTasks (id, tasks) { + var instance = instances[id]; + if (instance && Array.isArray(tasks)) { + var results = []; + tasks.forEach(function (task) { + var handler = jsHandlers[task.method]; + var args = [].concat( task.args ); + /* istanbul ignore else */ + if (typeof handler === 'function') { + args.unshift(id); + results.push(handler.apply(void 0, args)); } - } - }); - // Finally `updateFinish` signal needed. - renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'updateFinish', args: [] }], -1); + }); + return results + } + return new Error(("invalid instance id \"" + id + "\" or tasks")) } /** @@ -235,6 +241,18 @@ function registerModules (newModules) { for (var name in newModules) loop( name ); } +/** + * Check whether the module or the method has been registered. + * @param {String} module name + * @param {String} method name (optional) + */ +function isRegisteredModule (name, method) { + if (typeof method === 'string') { + return !!(modules[name] && modules[name][method]) + } + return !!modules[name] +} + /** * Register native components information. * @param {array} newComponents @@ -254,6 +272,35 @@ function registerComponents (newComponents) { } } +/** + * Check whether the component has been registered. + * @param {String} component name + */ +function isRegisteredComponent (name) { + return !!components[name] +} + +/** + * Detects whether Weex supports specific features. + * @param {String} condition + */ +function supports (condition) { + if (typeof condition !== 'string') { return null } + + var res = condition.match(/^@(\w+)\/(\w+)(\.(\w+))?$/i); + if (res) { + var type = res[1]; + var name = res[2]; + var method = res[4]; + switch (type) { + case 'module': return isRegisteredModule(name, method) + case 'component': return isRegisteredComponent(name) + } + } + + return null +} + /** * Create a fresh instance of Vue for each Weex instance. */ @@ -314,9 +361,7 @@ function createVueModuleInstance (instanceId, moduleGetter) { * Generate native module getter. Each native module has several * methods to call. And all the behaviors is instance-related. So * this getter will return a set of methods which additionally - * send current instance id to native when called. Also the args - * will be normalized into "safe" value. For example function arg - * will be converted into a callback id. + * send current instance id to native when called. * @param {string} instanceId * @return {function} */ @@ -326,15 +371,23 @@ function genModuleGetter (instanceId) { var nativeModule = modules[name] || []; var output = {}; var loop = function ( methodName ) { - output[methodName] = function () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var finalArgs = args.map(function (value) { - return normalize(value, instance) - }); - renderer.sendTasks(instanceId + '', [{ module: name, method: methodName, args: finalArgs }], -1); - }; + Object.defineProperty(output, methodName, { + enumerable: true, + configurable: true, + get: function proxyGetter () { + return function () { + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + + return instance.document.taskCenter.send('module', { module: name, method: methodName }, args) + } + }, + set: function proxySetter (val) { + if (typeof val === 'function') { + return instance.document.taskCenter.send('module', { module: name, method: methodName }, [val]) + } + } + }); }; for (var methodName in nativeModule) loop( methodName ); @@ -362,8 +415,9 @@ function getInstanceTimer (instanceId, moduleGetter) { var handler = function () { args[0].apply(args, args.slice(2)); }; + timer.setTimeout(handler, args[1]); - return instance.callbackId.toString() + return instance.document.taskCenter.callbackManager.lastCallbackId.toString() }, setInterval: function () { var args = [], len = arguments.length; @@ -372,8 +426,9 @@ function getInstanceTimer (instanceId, moduleGetter) { var handler = function () { args[0].apply(args, args.slice(2)); }; + timer.setInterval(handler, args[1]); - return instance.callbackId.toString() + return instance.document.taskCenter.callbackManager.lastCallbackId.toString() }, clearTimeout: function (n) { timer.clearTimeout(n); @@ -405,52 +460,55 @@ function callFunction (globalObjects, body) { } /** - * Convert all type of values into "safe" format to send to native. - * 1. A `function` will be converted into callback id. - * 2. An `Element` object will be converted into `ref`. - * The `instance` param is used to generate callback id and store - * function if necessary. - * @param {any} v - * @param {object} instance - * @return {any} + * Call a new function generated on the V8 native side. + * + * This function helps speed up bundle compiling. Normally, the V8 + * engine needs to download, parse, and compile a bundle on every + * visit. If 'compileBundle()' is available on native side, + * the downloding, parsing, and compiling steps would be skipped. + * @param {object} globalObjects + * @param {string} body + * @return {boolean} */ -function normalize (v, instance) { - var type = typof(v); - - switch (type) { - case 'undefined': - case 'null': - return '' - case 'regexp': - return v.toString() - case 'date': - return v.toISOString() - case 'number': - case 'string': - case 'boolean': - case 'array': - case 'object': - if (v instanceof renderer.Element) { - return v.ref - } - return v - case 'function': - instance.callbacks[++instance.callbackId] = v; - return instance.callbackId.toString() - default: - return JSON.stringify(v) +function callFunctionNative (globalObjects, body) { + if (typeof renderer.compileBundle !== 'function') { + return false } -} -/** - * Get the exact type of an object by `toString()`. For example call - * `toString()` on an array will be returned `[object Array]`. - * @param {any} v - * @return {string} - */ -function typof (v) { - var s = Object.prototype.toString.call(v); - return s.substring(8, s.length - 1).toLowerCase() + var fn = void 0; + var isNativeCompileOk = false; + var script = '(function ('; + var globalKeys = []; + var globalValues = []; + for (var key in globalObjects) { + globalKeys.push(key); + globalValues.push(globalObjects[key]); + } + for (var i = 0; i < globalKeys.length - 1; ++i) { + script += globalKeys[i]; + script += ','; + } + script += globalKeys[globalKeys.length - 1]; + script += ') {'; + script += body; + script += '} )'; + + try { + var weex = globalObjects.weex || {}; + var config = weex.config || {}; + fn = renderer.compileBundle(script, + config.bundleUrl, + config.bundleDigest, + config.codeCachePath); + if (fn && typeof fn === 'function') { + fn.apply(void 0, globalValues); + isNativeCompileOk = true; + } + } catch (e) { + console.error(e); + } + + return isNativeCompileOk } exports.init = init; @@ -461,4 +519,7 @@ exports.refreshInstance = refreshInstance; exports.getRoot = getRoot; exports.receiveTasks = receiveTasks; exports.registerModules = registerModules; +exports.isRegisteredModule = isRegisteredModule; exports.registerComponents = registerComponents; +exports.isRegisteredComponent = isRegisteredComponent; +exports.supports = supports; diff --git a/packages/weex-vue-framework/package.json b/packages/weex-vue-framework/package.json index ed5f69be00e..71bd1d65c88 100644 --- a/packages/weex-vue-framework/package.json +++ b/packages/weex-vue-framework/package.json @@ -1,6 +1,6 @@ { "name": "weex-vue-framework", - "version": "2.1.9-weex.1", + "version": "2.4.2-weex.1", "description": "Vue 2.0 Framework for Weex", "main": "index.js", "repository": { From f7ebfa3e3564528e8bf406bda9fb2a9a848ea569 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 15 Aug 2017 18:18:07 -0700 Subject: [PATCH 17/18] Props -> Record --- types/options.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/options.d.ts b/types/options.d.ts index efa680da15f..4953bc70a5e 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -38,7 +38,7 @@ export type ThisTypedComponentOptionsWithArrayProps = object & ComponentOptions & Instance) => Data), Methods, Computed, Props> & - ThisType>; + ThisType>>; /** * A helper type that describes options for either functional or non-functional components. From bb0ff307f3109635ec714d1d8392c58a03cc453a Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 16 Aug 2017 10:40:21 -0700 Subject: [PATCH 18/18] Update TypeScript devDependency. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c88ec5e81a4..1db63ddbdfe 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "selenium-server": "^2.53.1", "serialize-javascript": "^1.3.0", "shelljs": "^0.7.8", - "typescript": "2.5.0-dev.20170615", + "typescript": "^2.4.2", "uglify-js": "^3.0.15", "webpack": "^2.6.1", "weex-js-runtime": "^0.20.5",