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", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy