vue-server-renderer
Version:
server renderer for Vue 2.0
1,614 lines (1,583 loc) • 277 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var stream = require('stream');
var he = require('he');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var he__default = /*#__PURE__*/_interopDefaultLegacy(he);
const emptyObject = Object.freeze({});
const isArray = Array.isArray;
// These helpers produce better VM code in JS engines due to their
// explicitness and function inlining.
function isUndef(v) {
return v === undefined || v === null;
}
function isDef(v) {
return v !== undefined && v !== null;
}
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' ||
// $flow-disable-line
typeof value === 'symbol' ||
typeof value === 'boolean');
}
function isFunction(value) {
return typeof value === 'function';
}
/**
* Quick object check - this is primarily used to tell
* objects from primitive values when we know the value
* is a JSON-compliant type.
*/
function isObject(obj) {
return obj !== null && typeof obj === 'object';
}
/**
* Get the raw type string of a value, e.g., [object Object].
*/
const _toString = Object.prototype.toString;
function toRawType(value) {
return _toString.call(value).slice(8, -1);
}
/**
* 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) {
const n = parseFloat(String(val));
return n >= 0 && Math.floor(n) === n && isFinite(val);
}
function isPromise(val) {
return (isDef(val) &&
typeof val.then === 'function' &&
typeof val.catch === 'function');
}
/**
* Convert a value to a string that is actually rendered.
*/
function toString(val) {
return val == null
? ''
: Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
? JSON.stringify(val, replacer, 2)
: String(val);
}
function replacer(_key, val) {
// avoid circular deps from v3
if (val && val.__v_isRef) {
return val.value;
}
return val;
}
/**
* Convert an input value to a number for persistence.
* If the conversion fails, return original string.
*/
function toNumber(val) {
const n = parseFloat(val);
return isNaN(n) ? val : n;
}
/**
* Make a map and return a function for checking if a key
* is in that map.
*/
function makeMap(str, expectsLowerCase) {
const map = Object.create(null);
const list = str.split(',');
for (let i = 0; i < list.length; i++) {
map[list[i]] = true;
}
return expectsLowerCase ? val => map[val.toLowerCase()] : val => map[val];
}
/**
* Check if a tag is a built-in tag.
*/
const isBuiltInTag = makeMap('slot,component', true);
/**
* Check if an attribute is a reserved attribute.
*/
const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');
/**
* Check whether an object has the property.
*/
const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return hasOwnProperty.call(obj, key);
}
/**
* Create a cached version of a pure function.
*/
function cached(fn) {
const cache = Object.create(null);
return function cachedFn(str) {
const hit = cache[str];
return hit || (cache[str] = fn(str));
};
}
/**
* Camelize a hyphen-delimited string.
*/
const camelizeRE = /-(\w)/g;
const camelize = cached((str) => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
});
/**
* Capitalize a string.
*/
const capitalize = cached((str) => {
return str.charAt(0).toUpperCase() + str.slice(1);
});
/**
* Hyphenate a camelCase string.
*/
const hyphenateRE = /\B([A-Z])/g;
const hyphenate = cached((str) => {
return str.replace(hyphenateRE, '-$1').toLowerCase();
});
/**
* Mix properties into target object.
*/
function extend(to, _from) {
for (const key in _from) {
to[key] = _from[key];
}
return to;
}
/**
* Merge an Array of Objects into a single Object.
*/
function toObject(arr) {
const res = {};
for (let i = 0; i < arr.length; i++) {
if (arr[i]) {
extend(res, arr[i]);
}
}
return res;
}
/* eslint-disable no-unused-vars */
/**
* 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$1(a, b, c) { }
/**
* Always return false.
*/
const no = (a, b, c) => false;
/* eslint-enable no-unused-vars */
/**
* Return the same value.
*/
const identity = (_) => _;
/**
* Generate a string containing static keys from compiler modules.
*/
function genStaticKeys(modules) {
return modules
.reduce((keys, m) => keys.concat(m.staticKeys || []), [])
.join(',');
}
/**
* Check if two values are loosely equal - that is,
* if they are plain objects, do they have the same shape?
*/
function looseEqual(a, b) {
if (a === b)
return true;
const isObjectA = isObject(a);
const isObjectB = isObject(b);
if (isObjectA && isObjectB) {
try {
const isArrayA = Array.isArray(a);
const isArrayB = Array.isArray(b);
if (isArrayA && isArrayB) {
return (a.length === b.length &&
a.every((e, i) => {
return looseEqual(e, b[i]);
}));
}
else if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime();
}
else if (!isArrayA && !isArrayB) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
return (keysA.length === keysB.length &&
keysA.every(key => {
return looseEqual(a[key], b[key]);
}));
}
else {
/* istanbul ignore next */
return false;
}
}
catch (e) {
/* istanbul ignore next */
return false;
}
}
else if (!isObjectA && !isObjectB) {
return String(a) === String(b);
}
else {
return false;
}
}
/**
* Return the first index at which a loosely equal value can be
* found in the array (if value is a plain object, the array must
* contain an object of the same shape), or -1 if it is not present.
*/
function looseIndexOf(arr, val) {
for (let i = 0; i < arr.length; i++) {
if (looseEqual(arr[i], val))
return i;
}
return -1;
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#polyfill
function hasChanged(x, y) {
if (x === y) {
return x === 0 && 1 / x !== 1 / y;
}
else {
return x === x || y === y;
}
}
const isAttr = makeMap('accept,accept-charset,accesskey,action,align,alt,async,autocomplete,' +
'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,' +
'checked,cite,class,code,codebase,color,cols,colspan,content,' +
'contenteditable,contextmenu,controls,coords,data,datetime,default,' +
'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,for,' +
'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,' +
'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,' +
'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,' +
'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,' +
'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,' +
'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,' +
'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,' +
'target,title,usemap,value,width,wrap');
const unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/; // eslint-disable-line no-control-regex
const isSSRUnsafeAttr = (name) => {
return unsafeAttrCharRE.test(name);
};
/* istanbul ignore next */
const isRenderableAttr = (name) => {
return (isAttr(name) || name.indexOf('data-') === 0 || name.indexOf('aria-') === 0);
};
const propsToAttrMap = {
acceptCharset: 'accept-charset',
className: 'class',
htmlFor: 'for',
httpEquiv: 'http-equiv'
};
const ESC = {
'<': '<',
'>': '>',
'"': '"',
'&': '&'
};
function escape(s) {
return s.replace(/[<>"&]/g, escapeChar);
}
function escapeChar(a) {
return ESC[a] || a;
}
const noUnitNumericStyleProps = {
'animation-iteration-count': true,
'border-image-outset': true,
'border-image-slice': true,
'border-image-width': true,
'box-flex': true,
'box-flex-group': true,
'box-ordinal-group': true,
'column-count': true,
columns: true,
flex: true,
'flex-grow': true,
'flex-positive': true,
'flex-shrink': true,
'flex-negative': true,
'flex-order': true,
'grid-row': true,
'grid-row-end': true,
'grid-row-span': true,
'grid-row-start': true,
'grid-column': true,
'grid-column-end': true,
'grid-column-span': true,
'grid-column-start': true,
'font-weight': true,
'line-clamp': true,
'line-height': true,
opacity: true,
order: true,
orphans: true,
'tab-size': true,
widows: true,
'z-index': true,
zoom: true,
// SVG
'fill-opacity': true,
'flood-opacity': true,
'stop-opacity': true,
'stroke-dasharray': true,
'stroke-dashoffset': true,
'stroke-miterlimit': true,
'stroke-opacity': true,
'stroke-width': true
};
const isJS = (file) => /\.js(\?[^.]+)?$/.test(file);
const isCSS = (file) => /\.css(\?[^.]+)?$/.test(file);
function createPromiseCallback() {
let resolve, reject;
const promise = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
const cb = (err, res) => {
if (err)
return reject(err);
resolve(res || '');
};
return { promise, cb };
}
// these are reserved for web because they are directly compiled away
// during template compilation
makeMap('style,class');
// attributes that should be using props for binding
const acceptValue = makeMap('input,textarea,option,select,progress');
const mustUseProp = (tag, type, attr) => {
return ((attr === 'value' && acceptValue(tag) && type !== 'button') ||
(attr === 'selected' && tag === 'option') ||
(attr === 'checked' && tag === 'input') ||
(attr === 'muted' && tag === 'video'));
};
const isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck');
const isValidContentEditableValue = makeMap('events,caret,typing,plaintext-only');
const convertEnumeratedValue = (key, value) => {
return isFalsyAttrValue(value) || value === 'false'
? 'false'
: // allow arbitrary string value for contenteditable
key === 'contenteditable' && isValidContentEditableValue(value)
? value
: 'true';
};
const isBooleanAttr = makeMap('allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' +
'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,' +
'truespeed,typemustmatch,visible');
const isFalsyAttrValue = (val) => {
return val == null || val === false;
};
function renderAttrs$1(node) {
let attrs = node.data.attrs;
let res = '';
const opts = node.parent && node.parent.componentOptions;
if (isUndef(opts) || opts.Ctor.options.inheritAttrs !== false) {
let parent = node.parent;
while (isDef(parent)) {
// Stop fallthrough in case parent has inheritAttrs option set to false
if (parent.componentOptions &&
parent.componentOptions.Ctor.options.inheritAttrs === false) {
break;
}
if (isDef(parent.data) && isDef(parent.data.attrs)) {
attrs = extend(extend({}, attrs), parent.data.attrs);
}
parent = parent.parent;
}
}
if (isUndef(attrs)) {
return res;
}
for (const key in attrs) {
if (isSSRUnsafeAttr(key)) {
continue;
}
if (key === 'style') {
// leave it to the style module
continue;
}
res += renderAttr(key, attrs[key]);
}
return res;
}
function renderAttr(key, value) {
if (isBooleanAttr(key)) {
if (!isFalsyAttrValue(value)) {
return ` ${key}="${key}"`;
}
}
else if (isEnumeratedAttr(key)) {
return ` ${key}="${escape(convertEnumeratedValue(key, value))}"`;
}
else if (!isFalsyAttrValue(value)) {
return ` ${key}="${escape(String(value))}"`;
}
return '';
}
/**
* @internal
*/
class VNode {
constructor(tag, data, children, text, elm, context, componentOptions, asyncFactory) {
this.tag = tag;
this.data = data;
this.children = children;
this.text = text;
this.elm = elm;
this.ns = undefined;
this.context = context;
this.fnContext = undefined;
this.fnOptions = undefined;
this.fnScopeId = undefined;
this.key = data && data.key;
this.componentOptions = componentOptions;
this.componentInstance = undefined;
this.parent = undefined;
this.raw = false;
this.isStatic = false;
this.isRootInsert = true;
this.isComment = false;
this.isCloned = false;
this.isOnce = false;
this.asyncFactory = asyncFactory;
this.asyncMeta = undefined;
this.isAsyncPlaceholder = false;
}
// DEPRECATED: alias for componentInstance for backwards compat.
/* istanbul ignore next */
get child() {
return this.componentInstance;
}
}
const createEmptyVNode = (text = '') => {
const node = new VNode();
node.text = text;
node.isComment = true;
return node;
};
function createTextVNode(val) {
return new VNode(undefined, undefined, undefined, String(val));
}
// optimized shallow clone
// used for static nodes and slot nodes because they may be reused across
// multiple renders, cloning them avoids errors when DOM manipulations rely
// on their elm reference.
function cloneVNode(vnode) {
const cloned = new VNode(vnode.tag, vnode.data,
// #7975
// clone children array to avoid mutating original in case of cloning
// a child.
vnode.children && vnode.children.slice(), vnode.text, vnode.elm, vnode.context, vnode.componentOptions, vnode.asyncFactory);
cloned.ns = vnode.ns;
cloned.isStatic = vnode.isStatic;
cloned.key = vnode.key;
cloned.isComment = vnode.isComment;
cloned.fnContext = vnode.fnContext;
cloned.fnOptions = vnode.fnOptions;
cloned.fnScopeId = vnode.fnScopeId;
cloned.asyncMeta = vnode.asyncMeta;
cloned.isCloned = true;
return cloned;
}
function renderDOMProps$1(node) {
let props = node.data.domProps;
let res = '';
let parent = node.parent;
while (isDef(parent)) {
if (parent.data && parent.data.domProps) {
props = extend(extend({}, props), parent.data.domProps);
}
parent = parent.parent;
}
if (isUndef(props)) {
return res;
}
const attrs = node.data.attrs;
for (const key in props) {
if (key === 'innerHTML') {
setText(node, props[key], true);
}
else if (key === 'textContent') {
setText(node, props[key], false);
}
else if (key === 'value' && node.tag === 'textarea') {
setText(node, toString(props[key]), false);
}
else {
// $flow-disable-line (WTF?)
const attr = propsToAttrMap[key] || key.toLowerCase();
if (isRenderableAttr(attr) &&
// avoid rendering double-bound props/attrs twice
!(isDef(attrs) && isDef(attrs[attr]))) {
res += renderAttr(attr, props[key]);
}
}
}
return res;
}
function setText(node, text, raw) {
const child = new VNode(undefined, undefined, undefined, text);
child.raw = raw;
node.children = [child];
}
/**
* unicode letters used for parsing html tags, component names and property paths.
* using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname
* skipping \u10000-\uEFFFF due to it freezing up PhantomJS
*/
const unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/;
/**
* Define a property.
*/
function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
// can we use __proto__?
const hasProto = '__proto__' in {};
// Browser environment sniffing
const inBrowser = typeof window !== 'undefined';
const UA = inBrowser && window.navigator.userAgent.toLowerCase();
const isIE = UA && /msie|trident/.test(UA);
UA && UA.indexOf('msie 9.0') > 0;
const isEdge = UA && UA.indexOf('edge/') > 0;
UA && UA.indexOf('android') > 0;
UA && /iphone|ipad|ipod|ios/.test(UA);
UA && /chrome\/\d+/.test(UA) && !isEdge;
UA && /phantomjs/.test(UA);
UA && UA.match(/firefox\/(\d+)/);
// Firefox has a "watch" function on Object.prototype...
// @ts-expect-error firebox support
const nativeWatch = {}.watch;
let supportsPassive = false;
if (inBrowser) {
try {
const opts = {};
Object.defineProperty(opts, 'passive', {
get() {
/* istanbul ignore next */
supportsPassive = true;
}
}); // https://github.com/facebook/flow/issues/285
window.addEventListener('test-passive', null, opts);
}
catch (e) { }
}
// this needs to be lazy-evaled because vue may be required before
// vue-server-renderer can set VUE_ENV
let _isServer;
const isServerRendering = () => {
if (_isServer === undefined) {
/* istanbul ignore if */
if (!inBrowser && typeof global !== 'undefined') {
// detect presence of vue-server-renderer and avoid
// Webpack shimming the process
_isServer =
global['process'] && global['process'].env.VUE_ENV === 'server';
}
else {
_isServer = false;
}
}
return _isServer;
};
/* istanbul ignore next */
function isNative(Ctor) {
return typeof Ctor === 'function' && /native code/.test(Ctor.toString());
}
const hasSymbol = typeof Symbol !== 'undefined' &&
isNative(Symbol) &&
typeof Reflect !== 'undefined' &&
isNative(Reflect.ownKeys);
let _Set; // $flow-disable-line
/* istanbul ignore if */ if (typeof Set !== 'undefined' && isNative(Set)) {
// use native Set when available.
_Set = Set;
}
else {
// a non-standard Set polyfill that only works with primitive keys.
_Set = class Set {
constructor() {
this.set = Object.create(null);
}
has(key) {
return this.set[key] === true;
}
add(key) {
this.set[key] = true;
}
clear() {
this.set = Object.create(null);
}
};
}
const SSR_ATTR = 'data-server-rendered';
const ASSET_TYPES = ['component', 'directive', 'filter'];
const LIFECYCLE_HOOKS = [
'beforeCreate',
'created',
'beforeMount',
'mounted',
'beforeUpdate',
'updated',
'beforeDestroy',
'destroyed',
'activated',
'deactivated',
'errorCaptured',
'serverPrefetch',
'renderTracked',
'renderTriggered'
];
var config = {
/**
* Option merge strategies (used in core/util/options)
*/
// $flow-disable-line
optionMergeStrategies: Object.create(null),
/**
* Whether to suppress warnings.
*/
silent: false,
/**
* Show production mode tip message on boot?
*/
productionTip: true,
/**
* Whether to enable devtools
*/
devtools: true,
/**
* Whether to record perf
*/
performance: false,
/**
* Error handler for watcher errors
*/
errorHandler: null,
/**
* Warn handler for watcher warns
*/
warnHandler: null,
/**
* Ignore certain custom elements
*/
ignoredElements: [],
/**
* Custom user key aliases for v-on
*/
// $flow-disable-line
keyCodes: Object.create(null),
/**
* Check if a tag is reserved so that it cannot be registered as a
* component. This is platform-dependent and may be overwritten.
*/
isReservedTag: no,
/**
* Check if an attribute is reserved so that it cannot be used as a component
* prop. This is platform-dependent and may be overwritten.
*/
isReservedAttr: no,
/**
* Check if a tag is an unknown element.
* Platform-dependent.
*/
isUnknownElement: no,
/**
* Get the namespace of an element
*/
getTagNamespace: noop$1,
/**
* Parse the real tag name for the specific platform.
*/
parsePlatformTagName: identity,
/**
* Check if an attribute must be bound using property, e.g. value
* Platform-dependent.
*/
mustUseProp: no,
/**
* Perform updates asynchronously. Intended to be used by Vue Test Utils
* This will significantly reduce performance if set to false.
*/
async: true,
/**
* Exposed for legacy reasons
*/
_lifecycleHooks: LIFECYCLE_HOOKS
};
let currentInstance = null;
/**
* @internal
*/
function setCurrentInstance(vm = null) {
if (!vm)
currentInstance && currentInstance._scope.off();
currentInstance = vm;
vm && vm._scope.on();
}
/* not type checking this file because flow doesn't play well with Proxy */
{
makeMap('Infinity,undefined,NaN,isFinite,isNaN,' +
'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,' +
'require' // for Webpack/Browserify
);
const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy);
if (hasProxy) {
const isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact');
config.keyCodes = new Proxy(config.keyCodes, {
set(target, key, value) {
if (isBuiltInModifier(key)) {
warn$2(`Avoid overwriting built-in modifier in config.keyCodes: .${key}`);
return false;
}
else {
target[key] = value;
return true;
}
}
});
}
}
let uid = 0;
/**
* A dep is an observable that can have multiple
* directives subscribing to it.
* @internal
*/
class Dep {
constructor() {
// pending subs cleanup
this._pending = false;
this.id = uid++;
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
removeSub(sub) {
// #12696 deps with massive amount of subscribers are extremely slow to
// clean up in Chromium
// to workaround this, we unset the sub for now, and clear them on
// next scheduler flush.
this.subs[this.subs.indexOf(sub)] = null;
if (!this._pending) {
this._pending = true;
}
}
depend(info) {
if (Dep.target) {
Dep.target.addDep(this);
if (info && Dep.target.onTrack) {
Dep.target.onTrack(Object.assign({ effect: Dep.target }, info));
}
}
}
notify(info) {
// stabilize the subscriber list first
const subs = this.subs.filter(s => s);
for (let i = 0, l = subs.length; i < l; i++) {
const sub = subs[i];
if (info) {
sub.onTrigger &&
sub.onTrigger(Object.assign({ effect: subs[i] }, info));
}
sub.update();
}
}
}
// The current target watcher being evaluated.
// This is globally unique because only one watcher
// can be evaluated at a time.
Dep.target = null;
const targetStack = [];
function pushTarget(target) {
targetStack.push(target);
Dep.target = target;
}
function popTarget() {
targetStack.pop();
Dep.target = targetStack[targetStack.length - 1];
}
/*
* not type checking this file because flow doesn't play well with
* dynamically accessing methods on Array prototype
*/
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function (method) {
// cache original method
const original = arrayProto[method];
def(arrayMethods, method, function mutator(...args) {
const result = original.apply(this, args);
const ob = this.__ob__;
let 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({
type: "array mutation" /* TriggerOpTypes.ARRAY_MUTATION */,
target: this,
key: method
});
}
return result;
});
});
const arrayKeys = Object.getOwnPropertyNames(arrayMethods);
const NO_INITIAL_VALUE = {};
/**
* In some cases we may want to disable observation inside a component's
* update computation.
*/
let shouldObserve = true;
function toggleObserving(value) {
shouldObserve = value;
}
// ssr mock dep
const mockDep = {
notify: noop$1,
depend: noop$1,
addSub: noop$1,
removeSub: noop$1
};
/**
* Observer class that is attached to each observed
* object. Once attached, the observer converts the target
* object's property keys into getter/setters that
* collect dependencies and dispatch updates.
*/
class Observer {
constructor(value, shallow = false, mock = false) {
this.value = value;
this.shallow = shallow;
this.mock = mock;
// this.value = value
this.dep = mock ? mockDep : new Dep();
this.vmCount = 0;
def(value, '__ob__', this);
if (isArray(value)) {
if (!mock) {
if (hasProto) {
value.__proto__ = arrayMethods;
/* eslint-enable no-proto */
}
else {
for (let i = 0, l = arrayKeys.length; i < l; i++) {
const key = arrayKeys[i];
def(value, key, arrayMethods[key]);
}
}
}
if (!shallow) {
this.observeArray(value);
}
}
else {
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
const keys = Object.keys(value);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
defineReactive(value, key, NO_INITIAL_VALUE, undefined, shallow, mock);
}
}
}
/**
* Observe a list of Array items.
*/
observeArray(value) {
for (let i = 0, l = value.length; i < l; i++) {
observe(value[i], false, this.mock);
}
}
}
// helpers
/**
* 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, shallow, ssrMockReactivity) {
if (value && hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
return value.__ob__;
}
if (shouldObserve &&
(ssrMockReactivity || !isServerRendering()) &&
(isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value.__v_skip /* ReactiveFlags.SKIP */ &&
!isRef(value) &&
!(value instanceof VNode)) {
return new Observer(value, shallow, ssrMockReactivity);
}
}
/**
* Define a reactive property on an Object.
*/
function defineReactive(obj, key, val, customSetter, shallow, mock, observeEvenIfShallow = false) {
const dep = new Dep();
const property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return;
}
// cater for pre-defined getter/setters
const getter = property && property.get;
const setter = property && property.set;
if ((!getter || setter) &&
(val === NO_INITIAL_VALUE || arguments.length === 2)) {
val = obj[key];
}
let childOb = shallow ? val && val.__ob__ : observe(val, false, mock);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
if (Dep.target) {
{
dep.depend({
target: obj,
type: "get" /* TrackOpTypes.GET */,
key
});
}
if (childOb) {
childOb.dep.depend();
if (isArray(value)) {
dependArray(value);
}
}
}
return isRef(value) && !shallow ? value.value : value;
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val;
if (!hasChanged(value, newVal)) {
return;
}
if (customSetter) {
customSetter();
}
if (setter) {
setter.call(obj, newVal);
}
else if (getter) {
// #7981: for accessor properties without setter
return;
}
else if (!shallow && isRef(value) && !isRef(newVal)) {
value.value = newVal;
return;
}
else {
val = newVal;
}
childOb = shallow ? newVal && newVal.__ob__ : observe(newVal, false, mock);
{
dep.notify({
type: "set" /* TriggerOpTypes.SET */,
target: obj,
key,
newValue: newVal,
oldValue: value
});
}
}
});
return dep;
}
function set(target, key, val) {
if ((isUndef(target) || isPrimitive(target))) {
warn$2(`Cannot set reactive property on undefined, null, or primitive value: ${target}`);
}
if (isReadonly(target)) {
warn$2(`Set operation on key "${key}" failed: target is readonly.`);
return;
}
const ob = target.__ob__;
if (isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key);
target.splice(key, 1, val);
// when mocking for SSR, array methods are not hijacked
if (ob && !ob.shallow && ob.mock) {
observe(val, false, true);
}
return val;
}
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val;
}
if (target._isVue || (ob && ob.vmCount)) {
warn$2('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(ob.value, key, val, undefined, ob.shallow, ob.mock);
{
ob.dep.notify({
type: "add" /* TriggerOpTypes.ADD */,
target: target,
key,
newValue: val,
oldValue: undefined
});
}
return val;
}
/**
* Collect dependencies on array elements when the array is touched, since
* we cannot intercept array element access like property getters.
*/
function dependArray(value) {
for (let e, i = 0, l = value.length; i < l; i++) {
e = value[i];
if (e && e.__ob__) {
e.__ob__.dep.depend();
}
if (isArray(e)) {
dependArray(e);
}
}
}
function isReadonly(value) {
return !!(value && value.__v_isReadonly);
}
function isRef(r) {
return !!(r && r.__v_isRef === true);
}
const normalizeEvent = cached((name) => {
const passive = name.charAt(0) === '&';
name = passive ? name.slice(1) : name;
const once = name.charAt(0) === '~'; // Prefixed last, checked first
name = once ? name.slice(1) : name;
const capture = name.charAt(0) === '!';
name = capture ? name.slice(1) : name;
return {
name,
once,
capture,
passive
};
});
function createFnInvoker(fns, vm) {
function invoker() {
const fns = invoker.fns;
if (isArray(fns)) {
const cloned = fns.slice();
for (let i = 0; i < cloned.length; i++) {
invokeWithErrorHandling(cloned[i], null, arguments, vm, `v-on handler`);
}
}
else {
// return handler return value for single handlers
return invokeWithErrorHandling(fns, null, arguments, vm, `v-on handler`);
}
}
invoker.fns = fns;
return invoker;
}
function updateListeners(on, oldOn, add, remove, createOnceHandler, vm) {
let name, cur, old, event;
for (name in on) {
cur = on[name];
old = oldOn[name];
event = normalizeEvent(name);
if (isUndef(cur)) {
warn$2(`Invalid handler for event "${event.name}": got ` + String(cur), vm);
}
else if (isUndef(old)) {
if (isUndef(cur.fns)) {
cur = on[name] = createFnInvoker(cur, vm);
}
if (isTrue(event.once)) {
cur = on[name] = createOnceHandler(event.name, cur, event.capture);
}
add(event.name, cur, event.capture, event.passive, event.params);
}
else if (cur !== old) {
old.fns = cur;
on[name] = old;
}
}
for (name in oldOn) {
if (isUndef(on[name])) {
event = normalizeEvent(name);
remove(event.name, oldOn[name], event.capture);
}
}
}
function extractPropsFromVNodeData(data, Ctor, tag) {
// we are only extracting raw values here.
// validation and default values are handled in the child
// component itself.
const propOptions = Ctor.options.props;
if (isUndef(propOptions)) {
return;
}
const res = {};
const { attrs, props } = data;
if (isDef(attrs) || isDef(props)) {
for (const key in propOptions) {
const altKey = hyphenate(key);
{
const keyInLowerCase = key.toLowerCase();
if (key !== keyInLowerCase && attrs && hasOwn(attrs, keyInLowerCase)) {
tip(`Prop "${keyInLowerCase}" is passed to component ` +
`${formatComponentName(
// @ts-expect-error tag is string
tag || Ctor)}, but the declared prop name is` +
` "${key}". ` +
`Note that HTML attributes are case-insensitive and camelCased ` +
`props need to use their kebab-case equivalents when using in-DOM ` +
`templates. You should probably use "${altKey}" instead of "${key}".`);
}
}
checkProp(res, props, key, altKey, true) ||
checkProp(res, attrs, key, altKey, false);
}
}
return res;
}
function checkProp(res, hash, key, altKey, preserve) {
if (isDef(hash)) {
if (hasOwn(hash, key)) {
res[key] = hash[key];
if (!preserve) {
delete hash[key];
}
return true;
}
else if (hasOwn(hash, altKey)) {
res[key] = hash[altKey];
if (!preserve) {
delete hash[altKey];
}
return true;
}
}
return false;
}
// The template compiler attempts to minimize the need for normalization by
// statically analyzing the template at compile time.
//
// For plain HTML markup, normalization can be completely skipped because the
// generated render function is guaranteed to return Array<VNode>. There are
// two cases where extra normalization is needed:
// 1. When the children contains components - because a functional component
// may return an Array instead of a single root. In this case, just a simple
// normalization is needed - if any child is an Array, we flatten the whole
// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep
// because functional components already normalize their own children.
function simpleNormalizeChildren(children) {
for (let i = 0; i < children.length; i++) {
if (isArray(children[i])) {
return Array.prototype.concat.apply([], children);
}
}
return children;
}
// 2. When the children contains constructs that always generated nested Arrays,
// e.g. <template>, <slot>, v-for, or when the children is provided by user
// with hand-written render functions / JSX. In such cases a full normalization
// is needed to cater to all possible types of children values.
function normalizeChildren(children) {
return isPrimitive(children)
? [createTextVNode(children)]
: isArray(children)
? normalizeArrayChildren(children)
: undefined;
}
function isTextNode(node) {
return isDef(node) && isDef(node.text) && isFalse(node.isComment);
}
function normalizeArrayChildren(children, nestedIndex) {
const res = [];
let i, c, lastIndex, last;
for (i = 0; i < children.length; i++) {
c = children[i];
if (isUndef(c) || typeof c === 'boolean')
continue;
lastIndex = res.length - 1;
last = res[lastIndex];
// nested
if (isArray(c)) {
if (c.length > 0) {
c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`);
// merge adjacent text nodes
if (isTextNode(c[0]) && isTextNode(last)) {
res[lastIndex] = createTextVNode(last.text + c[0].text);
c.shift();
}
res.push.apply(res, c);
}
}
else if (isPrimitive(c)) {
if (isTextNode(last)) {
// merge adjacent text nodes
// this is necessary for SSR hydration because text nodes are
// essentially merged when rendered to HTML strings
res[lastIndex] = createTextVNode(last.text + c);
}
else if (c !== '') {
// convert primitive to vnode
res.push(createTextVNode(c));
}
}
else {
if (isTextNode(c) && isTextNode(last)) {
// merge adjacent text nodes
res[lastIndex] = createTextVNode(last.text + c.text);
}
else {
// default key for nested array children (likely generated by v-for)
if (isTrue(children._isVList) &&
isDef(c.tag) &&
isUndef(c.key) &&
isDef(nestedIndex)) {
c.key = `__vlist${nestedIndex}_${i}__`;
}
res.push(c);
}
}
}
return res;
}
const SIMPLE_NORMALIZE = 1;
const ALWAYS_NORMALIZE = 2;
// wrapper function for providing a more flexible interface
// without getting yelled at by flow
function createElement(context, tag, data, children, normalizationType, alwaysNormalize) {
if (isArray(data) || isPrimitive(data)) {
normalizationType = children;
children = data;
data = undefined;
}
if (isTrue(alwaysNormalize)) {
normalizationType = ALWAYS_NORMALIZE;
}
return _createElement(context, tag, data, children, normalizationType);
}
function _createElement(context, tag, data, children, normalizationType) {
if (isDef(data) && isDef(data.__ob__)) {
warn$2(`Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` + 'Always create fresh vnode data objects in each render!', context);
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 (isDef(data) && isDef(data.key) && !isPrimitive(data.key)) {
warn$2('Avoid using non-primitive value as key, ' +
'use string/number value instead.', context);
}
// support single function children as default scoped slot
if (isArray(children) && isFunction(children[0])) {
data = data || {};
data.scopedSlots = { default: children[0] };
children.length = 0;
}
if (normalizationType === ALWAYS_NORMALIZE) {
children = normalizeChildren(children);
}
else if (normalizationType === SIMPLE_NORMALIZE) {
children = simpleNormalizeChildren(children);
}
let vnode, ns;
if (typeof tag === 'string') {
let Ctor;
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag);
if ((!data || !data.pre) &&
isDef((Ctor = resolveAsset(context.$options, 'components', tag)))) {
// component
vnode = createComponent(Ctor, data, context, children, tag);
}
else {
// unknown or unlisted namespaced elements
// check at runtime because it may get assigned a namespace when its
// parent normalizes children
vnode = new VNode(tag, data, children, undefined, undefined, context);
}
}
else {
// direct component options / constructor
vnode = createComponent(tag, data, context, children);
}
if (isArray(vnode)) {
return vnode;
}
else if (isDef(vnode)) {
if (isDef(ns))
applyNS(vnode, ns);
if (isDef(data))
registerDeepBindings(data);
return vnode;
}
else {
return createEmptyVNode();
}
}
function applyNS(vnode, ns, force) {
vnode.ns = ns;
if (vnode.tag === 'foreignObject') {
// use default namespace inside foreignObject
ns = undefined;
force = true;
}
if (isDef(vnode.children)) {
for (let i = 0, l = vnode.children.length; i < l; i++) {
const child = vnode.children[i];
if (isDef(child.tag) &&
(isUndef(child.ns) || (isTrue(force) && child.tag !== 'svg'))) {
applyNS(child, ns, force);
}
}
}
}
// ref #5318
// necessary to ensure parent re-render when deep bindings like :style and
// :class are used on slot nodes
function registerDeepBindings(data) {
if (isObject(data.style)) {
traverse(data.style);
}
if (isObject(data.class)) {
traverse(data.class);
}
}
/**
* Runtime helper for rendering v-for lists.
*/
function renderList(val, render) {
let ret = null, i, l, keys, key;
if (isArray(val) || typeof val === 'string') {
ret = new Array(val.length);
for (i = 0, l = val.length; i < l; i++) {
ret[i] = render(val[i], i);
}
}
else if (typeof val === 'number') {
ret = new Array(val);
for (i = 0; i < val; i++) {
ret[i] = render(i + 1, i);
}
}
else if (isObject(val)) {
if (hasSymbol && val[Symbol.iterator]) {
ret = [];
const iterator = val[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
ret.push(render(result.value, ret.length));
result = iterator.next();
}
}
else {
keys = Object.keys(val);
ret = new Array(keys.length);
for (i = 0, l = keys.length; i < l; i++) {
key = keys[i];
ret[i] = render(val[key], key, i);
}
}
}
if (!isDef(ret)) {
ret = [];
}
ret._isVList = true;
return ret;
}
/**
* Runtime helper for rendering <slot>
*/
function renderSlot(name, fallbackRender, props, bindObject) {
const scopedSlotFn = this.$scopedSlots[name];
let nodes;
if (scopedSlotFn) {
// scoped slot
props = props || {};
if (bindObject) {
if (!isObject(bindObject)) {
warn$2('slot v-bind without argument expects an Object', this);
}
props = extend(extend({}, bindObject), props);
}
nodes =
scopedSlotFn(props) ||
(isFunction(fallbackRender) ? fallbackRender() : fallbackRender);
}
else {
nodes =
this.$slots[name] ||
(isFunction(fallbackRender) ? fallbackRender() : fallbackRender);
}
const target = props && props.slot;
if (target) {
return this.$createElement('template', { slot: target }, nodes);
}
else {
return nodes;
}
}
/**
* Runtime helper for resolving filters
*/
function resolveFilter(id) {
return resolveAsset(this.$options, 'filters', id, true) || identity;
}
function isKeyNotMatch(expect, actual) {
if (isArray(expect)) {
return expect.indexOf(actual) === -1;
}
else {
return expect !== actual;
}
}
/**
* Runtime helper for checking keyCodes from config.
* exposed as Vue.prototype._k
* passing in eventKeyName as last argument separately for backwards compat
*/
function checkKeyCodes(eventKeyCode, key, builtInKeyCode, eventKeyName, builtInKeyName) {
const mappedKeyCode = config.keyCodes[key] || builtInKeyCode;
if (builtInKeyName && eventKeyName && !config.keyCodes[key]) {
return isKeyNotMatch(builtInKeyName, eventKeyName);
}
else if (mappedKeyCode) {
return isKeyNotMatch(mappedKeyCode, eventKeyCode);
}
else if (eventKeyName) {
return hyphenate(eventKeyName) !== key;
}
return eventKeyCode === undefined;
}
/**
* Runtime helper for merging v-bind="object" into a VNode's data.
*/
function bindObjectProps(data, tag, value, asProp, isSync) {
if (value) {
if (!isObject(value)) {
warn$2('v-bind without argument expects an Object or Array value', this);
}
else {
if (isArray(value)) {
value = toObject(value);
}
let hash;
for (const key in value) {
if (key === 'class' || key === 'style' || isReservedAttribute(key)) {
hash = data;
}
else {
const type = data.attrs && data.attrs.type;
hash =
asProp || config.mustUseProp(tag, type, key)
? data.domProps || (data.domProps = {})
: data.attrs || (data.attrs = {});
}
const camelizedKey = camelize(key);
const hyphenatedKey = hyphenate(key);
if (!(camelizedKey in hash) && !(hyphenatedKey in hash)) {
hash[key] = value[key];
if (isSync) {
const on = data.on || (data.on = {});
on[`update:${key}`] = function ($event) {
value[key] = $event;
};
}
}
}
}
}
return data;
}
/**
* Runtime helper for rendering static trees.
*/
function renderStatic(index, isInFor) {
const cached = this._staticTrees || (this._staticTrees = []);
let tree = cached[index];
// if has already-rendered static tree and not inside v-for,
// we can reuse the same tree.
if (tree && !isInFor) {
return tree;
}
// otherwise, render a fresh tree.
tree = cached[index] = this.$options.staticRenderFns[index].call(this._renderProxy, this._c, this // for render fns generated for functional component templates
);
markStatic(tree, `__static__${index}`, false);
return tree;
}
/**
* Runtime helper for v-once.
* Effectively it means marking the node as static with a unique key.
*/
function markOnce(tree, index, key) {
markStatic(tree, `__once__${index}${key ? `_${key}` : ``}`, true);
return tree;
}
function markStatic(tree, key, isOnce) {
if (isArray(t