diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 0c03779a886..5368b5481bc 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -124,15 +124,7 @@ The default test script will do the following: lint with ESLint -> type check wi ## Financial Contribution -As a pure community-driven project without major corporate backing, we also welcome financial contributions via Patreon and OpenCollective. - -- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou) -- [Become a backer or sponsor on OpenCollective](https://opencollective.com/vuejs) -- [One-time donation via PayPal or crypto-currencies](https://vuejs.org/support-vuejs/#One-time-Donations) - -### What's the difference between Patreon and OpenCollective funding? - -Funds donated via Patreon go directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses for core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform. +As a pure community-driven project without major corporate backing, we also welcome financial contributions via GitHub Sponsors and OpenCollective. Please consult the [Sponsor Page](https://vuejs.org/sponsor/) for more details. ## Credits diff --git a/BACKERS.md b/BACKERS.md index 38a9299edae..fa66d206698 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -1,434 +1,9 @@

Sponsors & Backers

-Vue.js is an MIT-licensed open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome [backers](https://github.com/vuejs/vue/blob/dev/BACKERS.md). If you'd like to join them, please consider: - -- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou). -- [Become a backer or sponsor on OpenCollective](https://opencollective.com/vuejs). -- [One-time donation via PayPal or crypto-currencies.](https://vuejs.org/support-vuejs/#One-time-Donations) - -#### What's the difference between Patreon and OpenCollective? - -Funds donated via Patreon go directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses for core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform. - -

- -

Special Sponsors

- - +Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of the awesome sponsors and backers listed in this file. If you'd like to join them, please consider [ sponsor Vue's development](https://vuejs.org/sponsor/).

- - + + sponsors

- - - -

Platinum via Patreon

- - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - -
- - - -

Platinum Sponsors (China)

- - - - - - -
- - - -
- - -

Platinum via OpenCollective

- - - - -

Gold via Patreon

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - -

Gold via OpenCollective

- - - - - - - - - - - - - -

Silver via Patreon

- -- Matt Mullenweg - - - - - - - - - - -
- - - - - - - - - - - -
- - -

Silver via OpenCollective

- - - - - -

Bronze via Patreon

- - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - -

Bronze via OpenCollective

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Generous Backers via Patreon ($50+)

- - -- Wasim Khamlichi -- errorrik -- Sean Ferguson -- Johnny Ray Austin -- Peter Imburg - - -

Backers via Patreon

- - -- Masahiro Tanaka -- Shawn Wildermuth -- Keisuke Kita -- Benjamin Listwon -- Lars Andreas Ness -- Kirk Lewis -- Wen-Tien Chang -- Stephen Michael Hartley -- Victor Tolbert -- username -- Luiz -- James J. Ye -- Barbara Liau -- Asaf Yishai -- Matsumoto Takamasa -- Guy Gavergun -- Matt Jones -- Niannian Modisette -- Bernhard E. Reiter -- Zoran Knezevic -- Jon Hobbs-Smith -- Pierre Vanhulst -- Haim Yulzari -- Anthony Estebe -- Jim Raden -- IMGNRY -- Guilherme S L de Souza -- Tyler -- Jordan Oroshiba -- Joe Gregory -- Marcos Moura -- Eric -- Jere Sjöroos -- Wakana Seki -- Donald Fischer -- David Ang -- Oskar Lindgren -- Jaeyoung Lee -- David Kaplan -- Philip John Basile -- Matt Sencenbaugh -- Chris Calo -- Juan Bermudez -- Hannes Kochniß -- Daniel Mattingley -- Elon Hung -- Andrew Willis -- Yusuke Kawabata -- 龙腾道 -- Peter Matkovsky -- Nick Dandakis -- Pierre Lebrun -- Ryan Brewer -- Bichinger Software & Consulting -- Alfonso Herrera -- Afif Sohaili -- Riki Fridrich -- Diana Bergholz -- 琚致远 -- Parham Saidi -- Allan McKernan -- Roy Segall -- Abhay -- Nobuhide Esaki -- Thomas Wiedemann -- Santi Sanchez Canals -- lucien144 -- Evgeny Zislis -- Zeth Odderskov -- Kirill Tyurin -- Jules -- Sunny Yuen -- wickedwei -- Dennis Zanetti -- Faizal Andyka -- Aleksander Figiel - - -

Backers via OpenCollective

- - diff --git a/README.md b/README.md index 68781a21c02..ed68ab8bbbc 100644 --- a/README.md +++ b/README.md @@ -7,244 +7,21 @@ Version License Chat -
- Build Status

-

Supporting Vue.js

+## This repo is for Vue 2 -Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome [backers](https://github.com/vuejs/vue/blob/dev/BACKERS.md). If you'd like to join them, please consider: +You are looking at the repository for Vue 2. The repo for Vue 3 is [vuejs/core](https://github.com/vuejs/core). -- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou). -- [Become a backer or sponsor on Open Collective](https://opencollective.com/vuejs). -- [One-time donation via PayPal or crypto-currencies.](https://vuejs.org/support-vuejs/#One-time-Donations) +## Sponsors -#### What's the difference between Patreon and OpenCollective? - -Funds donated via Patreon go directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses for core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform. - -

Special Sponsors

- +Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome [backers](https://github.com/vuejs/core/blob/main/BACKERS.md). If you'd like to join them, please consider [ sponsor Vue's development](https://vuejs.org/sponsor/).

- - + + sponsors

- - - -

Platinum Sponsors

- - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - -
- - - -

Platinum Sponsors (China)

- - - - - - -
- - - -
- - -

Gold Sponsors

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - -

Sponsors via Open Collective

- -

Platinum

- - - - -

Gold

- - - - - - --- @@ -254,20 +31,20 @@ Vue (pronounced `/vjuː/`, like view) is a **progressive framework** for buildin #### Browser Compatibility -Vue.js supports all browsers that are [ES5-compliant](http://kangax.github.io/compat-table/es5/) (IE8 and below are not supported). +Vue.js supports all browsers that are [ES5-compliant](https://kangax.github.io/compat-table/es5/) (IE8 and below are not supported). ## Ecosystem -| Project | Status | Description | -|---------|--------|-------------| -| [vue-router] | [![vue-router-status]][vue-router-package] | Single-page application routing | -| [vuex] | [![vuex-status]][vuex-package] | Large-scale state management | -| [vue-cli] | [![vue-cli-status]][vue-cli-package] | Project scaffolding | -| [vue-loader] | [![vue-loader-status]][vue-loader-package] | Single File Component (`*.vue` file) loader for webpack | -| [vue-server-renderer] | [![vue-server-renderer-status]][vue-server-renderer-package] | Server-side rendering support | -| [vue-class-component] | [![vue-class-component-status]][vue-class-component-package] | TypeScript decorator for a class-based API | -| [vue-rx] | [![vue-rx-status]][vue-rx-package] | RxJS integration | -| [vue-devtools] | [![vue-devtools-status]][vue-devtools-package] | Browser DevTools extension | +| Project | Status | Description | +| --------------------- | ------------------------------------------------------------ | ------------------------------------------------------- | +| [vue-router] | [![vue-router-status]][vue-router-package] | Single-page application routing | +| [vuex] | [![vuex-status]][vuex-package] | Large-scale state management | +| [vue-cli] | [![vue-cli-status]][vue-cli-package] | Project scaffolding | +| [vue-loader] | [![vue-loader-status]][vue-loader-package] | Single File Component (`*.vue` file) loader for webpack | +| [vue-server-renderer] | [![vue-server-renderer-status]][vue-server-renderer-package] | Server-side rendering support | +| [vue-class-component] | [![vue-class-component-status]][vue-class-component-package] | TypeScript decorator for a class-based API | +| [vue-rx] | [![vue-rx-status]][vue-rx-package] | RxJS integration | +| [vue-devtools] | [![vue-devtools-status]][vue-devtools-package] | Browser DevTools extension | [vue-router]: https://github.com/vuejs/vue-router [vuex]: https://github.com/vuejs/vuex @@ -276,8 +53,7 @@ Vue.js supports all browsers that are [ES5-compliant](http://kangax.github.io/co [vue-server-renderer]: https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer [vue-class-component]: https://github.com/vuejs/vue-class-component [vue-rx]: https://github.com/vuejs/vue-rx -[vue-devtools]: https://github.com/vuejs/vue-devtools - +[vue-devtools]: https://github.com/vuejs/vue-devtools [vue-router-status]: https://img.shields.io/npm/v/vue-router.svg [vuex-status]: https://img.shields.io/npm/v/vuex.svg [vue-cli-status]: https://img.shields.io/npm/v/@vue/cli.svg @@ -286,7 +62,6 @@ Vue.js supports all browsers that are [ES5-compliant](http://kangax.github.io/co [vue-class-component-status]: https://img.shields.io/npm/v/vue-class-component.svg [vue-rx-status]: https://img.shields.io/npm/v/vue-rx.svg [vue-devtools-status]: https://img.shields.io/chrome-web-store/v/nhdogjmejiglipccpnnnanhbledajbpd.svg - [vue-router-package]: https://npmjs.com/package/vue-router [vuex-package]: https://npmjs.com/package/vuex [vue-cli-package]: https://npmjs.com/package/@vue/cli @@ -302,7 +77,7 @@ To check out [live examples](https://vuejs.org/v2/examples/) and docs, visit [vu ## Questions -For questions and support please use [the official forum](http://forum.vuejs.org) or [community chat](https://chat.vuejs.org/). The issue list of this repo is **exclusively** for bug reports and feature requests. +For questions and support please use [the official forum](https://forum.vuejs.org) or [community chat](https://chat.vuejs.org/). The issue list of this repo is **exclusively** for bug reports and feature requests. ## Issues @@ -326,9 +101,8 @@ Thank you to all the people who already contributed to Vue! - ## License -[MIT](http://opensource.org/licenses/MIT) +[MIT](https://opensource.org/licenses/MIT) Copyright (c) 2013-present, Yuxi (Evan) You diff --git a/dist/vue.common.dev.js b/dist/vue.common.dev.js index 41f17e2f943..eb4648163b1 100644 --- a/dist/vue.common.dev.js +++ b/dist/vue.common.dev.js @@ -1,6 +1,6 @@ /*! - * Vue.js v2.6.12 - * (c) 2014-2020 Evan You + * Vue.js v2.6.14 + * (c) 2014-2021 Evan You * Released under the MIT License. */ 'use strict'; @@ -1700,13 +1700,14 @@ function assertProp ( type = [type]; } for (var i = 0; i < type.length && !valid; i++) { - var assertedType = assertType(value, type[i]); + var assertedType = assertType(value, type[i], vm); expectedTypes.push(assertedType.expectedType || ''); valid = assertedType.valid; } } - if (!valid) { + var haveExpectedTypes = expectedTypes.some(function (t) { return t; }); + if (!valid && haveExpectedTypes) { warn( getInvalidTypeMessage(name, value, expectedTypes), vm @@ -1724,9 +1725,9 @@ function assertProp ( } } -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; +var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol|BigInt)$/; -function assertType (value, type) { +function assertType (value, type, vm) { var valid; var expectedType = getType(type); if (simpleCheckRE.test(expectedType)) { @@ -1741,7 +1742,12 @@ function assertType (value, type) { } else if (expectedType === 'Array') { valid = Array.isArray(value); } else { - valid = value instanceof type; + try { + valid = value instanceof type; + } catch (e) { + warn('Invalid prop type: "' + String(type) + '" is not a constructor', vm); + valid = false; + } } return { valid: valid, @@ -1749,13 +1755,15 @@ function assertType (value, type) { } } +var functionTypeCheckRE = /^\s*function (\w+)/; + /** * Use function string name to check built-in types, * because a simple equality check will fail when running * across different vms / iframes. */ function getType (fn) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); + var match = fn && fn.toString().match(functionTypeCheckRE); return match ? match[1] : '' } @@ -1780,18 +1788,19 @@ function getInvalidTypeMessage (name, value, expectedTypes) { " Expected " + (expectedTypes.map(capitalize).join(', ')); var expectedType = expectedTypes[0]; var receivedType = toRawType(value); - var expectedValue = styleValue(value, expectedType); - var receivedValue = styleValue(value, receivedType); // check if we need to specify expected value - if (expectedTypes.length === 1 && - isExplicable(expectedType) && - !isBoolean(expectedType, receivedType)) { - message += " with value " + expectedValue; + if ( + expectedTypes.length === 1 && + isExplicable(expectedType) && + isExplicable(typeof value) && + !isBoolean(expectedType, receivedType) + ) { + message += " with value " + (styleValue(value, expectedType)); } message += ", got " + receivedType + " "; // check if we need to specify received value if (isExplicable(receivedType)) { - message += "with value " + receivedValue + "."; + message += "with value " + (styleValue(value, receivedType)) + "."; } return message } @@ -1806,9 +1815,9 @@ function styleValue (value, type) { } } +var EXPLICABLE_TYPES = ['string', 'number', 'boolean']; function isExplicable (value) { - var explicitTypes = ['string', 'number', 'boolean']; - return explicitTypes.some(function (elem) { return value.toLowerCase() === elem; }) + return EXPLICABLE_TYPES.some(function (elem) { return value.toLowerCase() === elem; }) } function isBoolean () { @@ -2035,7 +2044,7 @@ var initProxy; var allowedGlobals = makeMap( 'Infinity,undefined,NaN,isFinite,isNaN,' + 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + - 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + + 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,' + 'require' // for Webpack/Browserify ); @@ -2538,6 +2547,12 @@ function isWhitespace (node) { /* */ +function isAsyncPlaceholder (node) { + return node.isComment && node.asyncFactory +} + +/* */ + function normalizeScopedSlots ( slots, normalSlots, @@ -2594,9 +2609,10 @@ function normalizeScopedSlot(normalSlots, key, fn) { res = res && typeof res === 'object' && !Array.isArray(res) ? [res] // single vnode : normalizeChildren(res); + var vnode = res && res[0]; return res && ( - res.length === 0 || - (res.length === 1 && res[0].isComment) // #9658 + !vnode || + (res.length === 1 && vnode.isComment && !isAsyncPlaceholder(vnode)) // #9658, #10391 ) ? undefined : res }; @@ -2669,26 +2685,28 @@ function renderList ( */ function renderSlot ( name, - fallback, + fallbackRender, props, bindObject ) { var scopedSlotFn = this.$scopedSlots[name]; var nodes; - if (scopedSlotFn) { // scoped slot + if (scopedSlotFn) { + // scoped slot props = props || {}; if (bindObject) { if (!isObject(bindObject)) { - warn( - 'slot v-bind without argument expects an Object', - this - ); + warn('slot v-bind without argument expects an Object', this); } props = extend(extend({}, bindObject), props); } - nodes = scopedSlotFn(props) || fallback; + nodes = + scopedSlotFn(props) || + (typeof fallbackRender === 'function' ? fallbackRender() : fallbackRender); } else { - nodes = this.$slots[name] || fallback; + nodes = + this.$slots[name] || + (typeof fallbackRender === 'function' ? fallbackRender() : fallbackRender); } var target = props && props.slot; @@ -2738,6 +2756,7 @@ function checkKeyCodes ( } else if (eventKeyName) { return hyphenate(eventKeyName) !== key } + return eventKeyCode === undefined } /* */ @@ -3269,8 +3288,10 @@ function createComponent ( } function createComponentInstanceForVnode ( - vnode, // we know it's MountedComponentVNode but flow doesn't - parent // activeInstance in lifecycle state + // we know it's MountedComponentVNode but flow doesn't + vnode, + // activeInstance in lifecycle state + parent ) { var options = { _isComponent: true, @@ -3409,7 +3430,7 @@ function _createElement ( ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag); if (config.isReservedTag(tag)) { // platform built-in elements - if (isDef(data) && isDef(data.nativeOn)) { + if (isDef(data) && isDef(data.nativeOn) && data.tag !== 'component') { warn( ("The .native modifier for v-on is only valid on components but it was used on <" + tag + ">."), context @@ -3735,12 +3756,6 @@ function resolveAsyncComponent ( /* */ -function isAsyncPlaceholder (node) { - return node.isComment && node.asyncFactory -} - -/* */ - function getFirstComponentChild (children) { if (Array.isArray(children)) { for (var i = 0; i < children.length; i++) { @@ -4107,7 +4122,8 @@ function updateChildComponent ( var hasDynamicScopedSlot = !!( (newScopedSlots && !newScopedSlots.$stable) || (oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) || - (newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key) + (newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key) || + (!newScopedSlots && vm.$scopedSlots.$key) ); // Any static slot children from the parent may have changed during parent's @@ -4559,11 +4575,8 @@ Watcher.prototype.run = function run () { var oldValue = this.value; this.value = value; if (this.user) { - try { - this.cb.call(this.vm, value, oldValue); - } catch (e) { - handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); - } + var info = "callback for watcher \"" + (this.expression) + "\""; + invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info); } else { this.cb.call(this.vm, value, oldValue); } @@ -4785,6 +4798,8 @@ function initComputed (vm, computed) { warn(("The computed property \"" + key + "\" is already defined in data."), vm); } else if (vm.$options.props && key in vm.$options.props) { warn(("The computed property \"" + key + "\" is already defined as a prop."), vm); + } else if (vm.$options.methods && key in vm.$options.methods) { + warn(("The computed property \"" + key + "\" is already defined as a method."), vm); } } } @@ -4937,11 +4952,10 @@ function stateMixin (Vue) { options.user = true; var watcher = new Watcher(vm, expOrFn, cb, options); if (options.immediate) { - try { - cb.call(vm, watcher.value); - } catch (error) { - handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\"")); - } + var info = "callback for immediate watcher \"" + (watcher.expression) + "\""; + pushTarget(); + invokeWithErrorHandling(cb, vm, [watcher.value], vm, info); + popTarget(); } return function unwatchFn () { watcher.teardown(); @@ -5239,6 +5253,8 @@ function initAssetRegisters (Vue) { + + function getComponentName (opts) { return opts && (opts.Ctor.options.name || opts.tag) } @@ -5260,9 +5276,9 @@ function pruneCache (keepAliveInstance, filter) { var keys = keepAliveInstance.keys; var _vnode = keepAliveInstance._vnode; for (var key in cache) { - var cachedNode = cache[key]; - if (cachedNode) { - var name = getComponentName(cachedNode.componentOptions); + var entry = cache[key]; + if (entry) { + var name = entry.name; if (name && !filter(name)) { pruneCacheEntry(cache, key, keys, _vnode); } @@ -5276,9 +5292,9 @@ function pruneCacheEntry ( keys, current ) { - var cached$$1 = cache[key]; - if (cached$$1 && (!current || cached$$1.tag !== current.tag)) { - cached$$1.componentInstance.$destroy(); + var entry = cache[key]; + if (entry && (!current || entry.tag !== current.tag)) { + entry.componentInstance.$destroy(); } cache[key] = null; remove(keys, key); @@ -5296,6 +5312,32 @@ var KeepAlive = { max: [String, Number] }, + methods: { + cacheVNode: function cacheVNode() { + var ref = this; + var cache = ref.cache; + var keys = ref.keys; + var vnodeToCache = ref.vnodeToCache; + var keyToCache = ref.keyToCache; + if (vnodeToCache) { + var tag = vnodeToCache.tag; + var componentInstance = vnodeToCache.componentInstance; + var componentOptions = vnodeToCache.componentOptions; + cache[keyToCache] = { + name: getComponentName(componentOptions), + tag: tag, + componentInstance: componentInstance, + }; + keys.push(keyToCache); + // prune oldest entry + if (this.max && keys.length > parseInt(this.max)) { + pruneCacheEntry(cache, keys[0], keys, this._vnode); + } + this.vnodeToCache = null; + } + } + }, + created: function created () { this.cache = Object.create(null); this.keys = []; @@ -5310,6 +5352,7 @@ var KeepAlive = { mounted: function mounted () { var this$1 = this; + this.cacheVNode(); this.$watch('include', function (val) { pruneCache(this$1, function (name) { return matches(val, name); }); }); @@ -5318,6 +5361,10 @@ var KeepAlive = { }); }, + updated: function updated () { + this.cacheVNode(); + }, + render: function render () { var slot = this.$slots.default; var vnode = getFirstComponentChild(slot); @@ -5351,12 +5398,9 @@ var KeepAlive = { remove(keys, key); keys.push(key); } else { - cache[key] = vnode; - keys.push(key); - // prune oldest entry - if (this.max && keys.length > parseInt(this.max)) { - pruneCacheEntry(cache, keys[0], keys, this._vnode); - } + // delay setting the cache until update + this.vnodeToCache = vnode; + this.keyToCache = key; } vnode.data.keepAlive = true; @@ -5439,7 +5483,7 @@ Object.defineProperty(Vue, 'FunctionalRenderContext', { value: FunctionalRenderContext }); -Vue.version = '2.6.12'; +Vue.version = '2.6.14'; /* */ @@ -5476,7 +5520,7 @@ var isBooleanAttr = makeMap( 'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' + 'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' + 'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' + - 'required,reversed,scoped,seamless,selected,sortable,translate,' + + 'required,reversed,scoped,seamless,selected,sortable,' + 'truespeed,typemustmatch,visible' ); @@ -5600,7 +5644,7 @@ var isHTMLTag = makeMap( // contain child elements. var isSVG = makeMap( 'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' + - 'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' + + 'foreignobject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' + 'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view', true ); @@ -5805,7 +5849,8 @@ var hooks = ['create', 'activate', 'update', 'remove', 'destroy']; function sameVnode (a, b) { return ( - a.key === b.key && ( + a.key === b.key && + a.asyncFactory === b.asyncFactory && ( ( a.tag === b.tag && a.isComment === b.isComment && @@ -5813,7 +5858,6 @@ function sameVnode (a, b) { sameInputType(a, b) ) || ( isTrue(a.isAsyncPlaceholder) && - a.asyncFactory === b.asyncFactory && isUndef(b.asyncFactory.error) ) ) @@ -6701,7 +6745,7 @@ function updateAttrs (oldVnode, vnode) { cur = attrs[key]; old = oldAttrs[key]; if (old !== cur) { - setAttr(elm, key, cur); + setAttr(elm, key, cur, vnode.data.pre); } } // #4391: in IE9, setting type can reset value for input[type=radio] @@ -6721,8 +6765,8 @@ function updateAttrs (oldVnode, vnode) { } } -function setAttr (el, key, value) { - if (el.tagName.indexOf('-') > -1) { +function setAttr (el, key, value, isInPre) { + if (isInPre || el.tagName.indexOf('-') > -1) { baseSetAttr(el, key, value); } else if (isBooleanAttr(key)) { // set attribute for blank value @@ -9243,7 +9287,7 @@ var isNonPhrasingTag = makeMap( // Regular Expressions for parsing tags and attributes var attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; -var dynamicArgAttribute = /^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; +var dynamicArgAttribute = /^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; var ncname = "[a-zA-Z_][\\-\\.0-9_a-zA-Z" + (unicodeRegExp.source) + "]*"; var qnameCapture = "((?:" + ncname + "\\:)?" + ncname + ")"; var startTagOpen = new RegExp(("^<" + qnameCapture)); @@ -9548,7 +9592,7 @@ var modifierRE = /\.[^.\]]+(?=[^\]]*$)/g; var slotRE = /^v-slot(:|$)|^#/; var lineBreakRE = /[\r\n]/; -var whitespaceRE$1 = /\s+/g; +var whitespaceRE$1 = /[ \f\t\r\n]+/g; var invalidAttributeRE = /[\s"'<>\/=]/; @@ -9596,8 +9640,12 @@ function parse ( platformMustUseProp = options.mustUseProp || no; platformGetTagNamespace = options.getTagNamespace || no; var isReservedTag = options.isReservedTag || no; - maybeComponent = function (el) { return !!el.component || !isReservedTag(el.tag); }; - + maybeComponent = function (el) { return !!( + el.component || + el.attrsMap[':is'] || + el.attrsMap['v-bind:is'] || + !(el.attrsMap.is ? isReservedTag(el.attrsMap.is) : isReservedTag(el.tag)) + ); }; transforms = pluckModuleFunction(options.modules, 'transformNode'); preTransforms = pluckModuleFunction(options.modules, 'preTransformNode'); postTransforms = pluckModuleFunction(options.modules, 'postTransformNode'); @@ -10846,9 +10894,9 @@ function genHandler (handler) { code += genModifierCode; } var handlerCode = isMethodPath - ? ("return " + (handler.value) + "($event)") + ? ("return " + (handler.value) + ".apply(null, arguments)") : isFunctionExpression - ? ("return (" + (handler.value) + ")($event)") + ? ("return (" + (handler.value) + ").apply(null, arguments)") : isFunctionInvocation ? ("return " + (handler.value)) : handler.value; @@ -10934,7 +10982,8 @@ function generate ( options ) { var state = new CodegenState(options); - var code = ast ? genElement(ast, state) : '_c("div")'; + // fix #11483, Root level ' + } + } + }).$mount() + expect(vm.$el.nodeName).toBe('#comment') + expect('Templates should only be responsible for mapping the state').toHaveBeenWarned() + }) }) diff --git a/test/unit/features/directives/class.spec.js b/test/unit/features/directives/class.spec.js index 0ee3380ca9a..38a25988d2a 100644 --- a/test/unit/features/directives/class.spec.js +++ b/test/unit/features/directives/class.spec.js @@ -152,6 +152,39 @@ describe('Directive v-bind:class', () => { }).then(done) }) + // css static classes should only contain a single space in between, + // as all the text inside of classes is shipped as a JS string + // and this could lead to useless spacing in static classes + it('condenses whitespace in staticClass', done => { + const vm = new Vue({ + template: '
', + }).$mount() + expect(vm.$el.className).toBe('test1 test2 test3 test4 test5 test6') + done() + }) + + it('condenses whitespace in staticClass merge in a component', done => { + const vm = new Vue({ + template: ` + + + `, + data: { + componentClass1: 'componentClass1', + }, + components: { + component1: { + template: '
' + }, + } + }).$mount() + expect(vm.$el.className).toBe('test staticClass componentClass1') + vm.componentClass1 = 'c1' + waitForUpdate(() => { + expect(vm.$el.className).toBe('test staticClass c1') + }).then(done) + }) + // a vdom patch edge case where the user has several un-keyed elements of the // same tag next to each other, and toggling them. it('properly remove staticClass for toggling un-keyed children', done => { diff --git a/test/unit/features/directives/on.spec.js b/test/unit/features/directives/on.spec.js index 5e755a34f89..353d3df5372 100644 --- a/test/unit/features/directives/on.spec.js +++ b/test/unit/features/directives/on.spec.js @@ -31,7 +31,7 @@ describe('Directive v-on', () => { expect(event.type).toBe('click') }) - it('should bind event to a inline statement', () => { + it('should bind event to an inline statement', () => { vm = new Vue({ el, template: '
', @@ -218,7 +218,7 @@ describe('Directive v-on', () => { }) // ctrl, shift, alt, meta - it('should support system modifers', () => { + it('should support system modifiers', () => { vm = new Vue({ el, template: ` @@ -474,6 +474,20 @@ describe('Directive v-on', () => { expect(spy.calls.count()).toBe(0) }) + it('should not throw a warning if native modifier is used on a dynamic component', () => { + vm = new Vue({ + el, + template: ` + + `, + methods: { foo: spy }, + }) + + triggerEvent(vm.$el, 'click') + expect(`The .native modifier for v-on is only valid on components but it was used on
.`).not.toHaveBeenWarned() + expect(spy.calls.allArgs()).toEqual([['regular']]); // Regular @click should work for dynamic components resolved to native HTML elements. + }) + it('.once modifier should work with child components', () => { vm = new Vue({ el, @@ -962,6 +976,17 @@ describe('Directive v-on', () => { expect(value).toBe(1) }) + it('should not execute callback if modifiers are present', () => { + vm = new Vue({ + el, + template: '', + methods: { foo: spy } + }) + // simulating autocomplete event (Event object with type keyup but without keyCode) + triggerEvent(vm.$el, 'keyup') + expect(spy.calls.count()).toBe(0) + }) + describe('dynamic arguments', () => { it('basic', done => { const spy = jasmine.createSpy() diff --git a/test/unit/features/error-handling.spec.js b/test/unit/features/error-handling.spec.js index 7d0eaa512ab..7dd36dc9f1f 100644 --- a/test/unit/features/error-handling.spec.js +++ b/test/unit/features/error-handling.spec.js @@ -127,25 +127,25 @@ describe('Error handling', () => { }).then(done) }) - it('should recover from errors in user watcher callback', done => { - const vm = createTestInstance(components.userWatcherCallback) - vm.n++ - waitForUpdate(() => { - expect(`Error in callback for watcher "n"`).toHaveBeenWarned() - expect(`Error: userWatcherCallback`).toHaveBeenWarned() - }).thenWaitFor(next => { - assertBothInstancesActive(vm).end(next) - }).then(done) - }) + ;[ + ['userWatcherCallback', 'watcher'], + ['userImmediateWatcherCallback', 'immediate watcher'] + ].forEach(([type, description]) => { + it(`should recover from errors in user ${description} callback`, done => { + const vm = createTestInstance(components[type]) + assertBothInstancesActive(vm).then(() => { + expect(`Error in callback for ${description} "n"`).toHaveBeenWarned() + expect(`Error: ${type} error`).toHaveBeenWarned() + }).then(done) + }) - it('should recover from errors in user immediate watcher callback', done => { - const vm = createTestInstance(components.userImmediateWatcherCallback) - waitForUpdate(() => { - expect(`Error in callback for immediate watcher "n"`).toHaveBeenWarned() - expect(`Error: userImmediateWatcherCallback error`).toHaveBeenWarned() - }).thenWaitFor(next => { - assertBothInstancesActive(vm).end(next) - }).then(done) + it(`should recover from promise errors in user ${description} callback`, done => { + const vm = createTestInstance(components[`${type}Async`]) + assertBothInstancesActive(vm).then(() => { + expect(`Error in callback for ${description} "n" (Promise/async)`).toHaveBeenWarned() + expect(`Error: ${type} error`).toHaveBeenWarned() + }).then(done) + }) }) it('config.errorHandler should capture render errors', done => { @@ -359,6 +359,33 @@ function createErrorTestComponents () { } } + components.userWatcherCallbackAsync = { + props: ['n'], + watch: { + n () { + return Promise.reject(new Error('userWatcherCallback error')) + } + }, + render (h) { + return h('div', this.n) + } + } + + components.userImmediateWatcherCallbackAsync = { + props: ['n'], + watch: { + n: { + immediate: true, + handler () { + return Promise.reject(new Error('userImmediateWatcherCallback error')) + } + } + }, + render (h) { + return h('div', this.n) + } + } + // event errors components.event = { beforeCreate () { diff --git a/test/unit/features/filter/filter.spec.js b/test/unit/features/filter/filter.spec.js index 82c57a3d62e..854ea575064 100644 --- a/test/unit/features/filter/filter.spec.js +++ b/test/unit/features/filter/filter.spec.js @@ -194,4 +194,11 @@ describe('Filters', () => { it('support template string', () => { expect(parseFilters('`a | ${b}c` | d')).toBe('_f("d")(`a | ${b}c`)') }) + + it('bigint support', () => { + const vm = new Vue({ + template: `
{{ BigInt(BigInt(10000000)) + BigInt(2000000000n) * 3000000n }}
` + }).$mount() + expect(vm.$el.textContent).toBe('6000000010000000') + }) }) diff --git a/test/unit/features/global-api/set-delete.spec.js b/test/unit/features/global-api/set-delete.spec.js index d1c7f3d8709..59e75ae4dbe 100644 --- a/test/unit/features/global-api/set-delete.spec.js +++ b/test/unit/features/global-api/set-delete.spec.js @@ -14,7 +14,7 @@ describe('Global API: set/delete', () => { }).then(done) }) - it('should update a observing object', done => { + it('should update an observing object', done => { const vm = new Vue({ template: '
{{foo.x}}
', data: { foo: { x: 1 }} @@ -26,7 +26,7 @@ describe('Global API: set/delete', () => { }).then(done) }) - it('should update a observing array', done => { + it('should update an observing array', done => { const vm = new Vue({ template: '
{{k}}-{{v}}
', data: { list: ['a', 'b', 'c'] } diff --git a/test/unit/features/instance/methods-lifecycle.spec.js b/test/unit/features/instance/methods-lifecycle.spec.js index 1e424a5c9be..a2c5b86e920 100644 --- a/test/unit/features/instance/methods-lifecycle.spec.js +++ b/test/unit/features/instance/methods-lifecycle.spec.js @@ -53,6 +53,38 @@ describe('Instance methods lifecycle', () => { } }).$mount() }) + + it('Dep.target should be undefined during invocation of child immediate watcher', done => { + let calls = 0 + const childData = { a: 1 } + const parentUpdate = jasmine.createSpy() + new Vue({ + template: '
', + updated: parentUpdate, + components: { + myComponent: { + template: '
{{ a }}
', + data() { + return childData + }, + watch: { + anything: { + handler() { + ++calls + this.a + }, + immediate: true + } + } + } + } + }).$mount() + expect(calls).toBe(1) + childData.a++ + waitForUpdate(() => { + expect(parentUpdate).not.toHaveBeenCalled() + }).then(done) + }) }) describe('$destroy', () => { diff --git a/test/unit/features/options/computed.spec.js b/test/unit/features/options/computed.spec.js index edc20bad3bf..17cd9516640 100644 --- a/test/unit/features/options/computed.spec.js +++ b/test/unit/features/options/computed.spec.js @@ -206,6 +206,18 @@ describe('Options computed', () => { expect(`computed property "a" is already defined as a prop`).toHaveBeenWarned() }) + it('warn conflict with methods', () => { + new Vue({ + computed: { + a: () => 2 + }, + methods: { + a: () => {} + } + }) + expect(`computed property "a" is already defined as a method`).toHaveBeenWarned() + }) + it('rethrow computed error', () => { const vm = new Vue({ computed: { diff --git a/test/unit/features/options/errorCaptured.spec.js b/test/unit/features/options/errorCaptured.spec.js index 3da13b9ed38..0c1aba35b79 100644 --- a/test/unit/features/options/errorCaptured.spec.js +++ b/test/unit/features/options/errorCaptured.spec.js @@ -247,4 +247,152 @@ describe('Options errorCaptured', () => { expect(store.errors[0]).toEqual(new Error('render error')) }).then(done) }) + + it('should capture error from watcher', done => { + const spy = jasmine.createSpy() + + let child + let err + const Child = { + data () { + return { + foo: null + } + }, + watch: { + foo () { + err = new Error('userWatcherCallback error') + throw err + } + }, + created () { + child = this + }, + render () {} + } + + new Vue({ + errorCaptured: spy, + render: h => h(Child) + }).$mount() + + child.foo = 'bar' + + waitForUpdate(() => { + expect(spy).toHaveBeenCalledWith(err, child, 'callback for watcher "foo"') + expect(globalSpy).toHaveBeenCalledWith(err, child, 'callback for watcher "foo"') + }).then(done) + }) + + it('should capture promise error from watcher', done => { + const spy = jasmine.createSpy() + + let child + let err + const Child = { + data () { + return { + foo: null + } + }, + watch: { + foo () { + err = new Error('userWatcherCallback error') + return Promise.reject(err) + } + }, + created () { + child = this + }, + render () {} + } + + new Vue({ + errorCaptured: spy, + render: h => h(Child) + }).$mount() + + child.foo = 'bar' + + child.$nextTick(() => { + waitForUpdate(() => { + expect(spy).toHaveBeenCalledWith(err, child, 'callback for watcher "foo" (Promise/async)') + expect(globalSpy).toHaveBeenCalledWith(err, child, 'callback for watcher "foo" (Promise/async)') + }).then(done) + }) + }) + + it('should capture error from immediate watcher', done => { + const spy = jasmine.createSpy() + + let child + let err + const Child = { + data () { + return { + foo: 'foo' + } + }, + watch: { + foo: { + immediate: true, + handler () { + err = new Error('userImmediateWatcherCallback error') + throw err + } + } + }, + created () { + child = this + }, + render () {} + } + + new Vue({ + errorCaptured: spy, + render: h => h(Child) + }).$mount() + + waitForUpdate(() => { + expect(spy).toHaveBeenCalledWith(err, child, 'callback for immediate watcher "foo"') + expect(globalSpy).toHaveBeenCalledWith(err, child, 'callback for immediate watcher "foo"') + }).then(done) + }) + + it('should capture promise error from immediate watcher', done => { + const spy = jasmine.createSpy() + + let child + let err + const Child = { + data () { + return { + foo: 'foo' + } + }, + watch: { + foo: { + immediate: true, + handler () { + err = new Error('userImmediateWatcherCallback error') + return Promise.reject(err) + } + } + }, + created () { + child = this + }, + render () {} + } + + new Vue({ + errorCaptured: spy, + render: h => h(Child) + }).$mount() + + waitForUpdate(() => { + expect(spy).toHaveBeenCalledWith(err, child, 'callback for immediate watcher "foo" (Promise/async)') + expect(globalSpy).toHaveBeenCalledWith(err, child, 'callback for immediate watcher "foo" (Promise/async)') + }).then(done) + }) }) diff --git a/test/unit/features/options/props.spec.js b/test/unit/features/options/props.spec.js index b2bf482737b..2ada45a0553 100644 --- a/test/unit/features/options/props.spec.js +++ b/test/unit/features/options/props.spec.js @@ -252,6 +252,16 @@ describe('Options props', () => { expect('Expected String, Number, got Symbol').toHaveBeenWarned() }) } + + if (typeof BigInt !== 'undefined') { + /* global BigInt */ + it('bigint', () => { + makeInstance(BigInt(100), BigInt) + expect(console.error.calls.count()).toBe(0) + makeInstance({}, BigInt) + expect('Expected BigInt, got Object').toHaveBeenWarned() + }) + } it('custom constructor', () => { function Class () {} @@ -553,4 +563,20 @@ describe('Options props', () => { expect(vm.$refs.test.$props.booleanOrString).toBe(true) expect(vm.$refs.test.$props.stringOrBoolean).toBe('') }) + + it('should warn when a prop type is not a constructor', () => { + const vm = new Vue({ + template: '
{{a}}
', + props: { + a: { + type: 'String', + default: 'test' + } + } + }).$mount() + expect( + 'Invalid prop type: "String" is not a constructor' + ).toHaveBeenWarned() + }) + }) diff --git a/test/unit/modules/compiler/codegen.spec.js b/test/unit/modules/compiler/codegen.spec.js index d7faa204944..a31f751d64e 100644 --- a/test/unit/modules/compiler/codegen.spec.js +++ b/test/unit/modules/compiler/codegen.spec.js @@ -196,7 +196,7 @@ describe('codegen', () => { it('generate slot fallback content', () => { assertCodegen( '
hi
', - `with(this){return _c('div',[_t("default",[_c('div',[_v("hi")])])],2)}` + `with(this){return _c('div',[_t("default",function(){return [_c('div',[_v("hi")])]})],2)}` ) }) diff --git a/test/unit/modules/compiler/parser.spec.js b/test/unit/modules/compiler/parser.spec.js index 360bc11bbb6..b47de581396 100644 --- a/test/unit/modules/compiler/parser.spec.js +++ b/test/unit/modules/compiler/parser.spec.js @@ -865,6 +865,14 @@ describe('parser', () => { expect(ast.children[4].children[0].text).toBe('. Have fun! ') }) + it(`maintains   with whitespace: 'condense'`, () => { + const options = extend({}, condenseOptions) + const ast = parse(' ', options) + const code = ast.children[0] + expect(code.type).toBe(3) + expect(code.text).toBe('\xA0') + }) + it(`preserve whitespace in
 tag with whitespace: 'condense'`, function () {
     const options = extend({}, condenseOptions)
     const ast = parse('
  \nhi\n   
', options) @@ -901,4 +909,20 @@ describe('parser', () => { expect(ast.children[2].type).toBe(3) expect(ast.children[2].text).toBe('\ndef') }) + + // #10152 + it('not warn when scoped slot used inside of dynamic component on regular element', () => { + parse(` +
+
+
+
+
+ `, baseOptions) + expect('v-slot can only be used on components or