Source: runtime-core.cjs.prod.js
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var reactivity = require('@vue/reactivity');
// Patch flags are optimization hints generated by the compiler.
// when a block with dynamicChildren is encountered during diff, the algorithm
// enters "optimized mode". In this mode, we know that the vdom is produced by
// a render function generated by the compiler, so the algorithm only needs to
// handle updates explicitly marked by these patch flags.
// runtime object for public consumption
const PublicPatchFlags = {
TEXT: 1 /* TEXT */,
CLASS: 2 /* CLASS */,
STYLE: 4 /* STYLE */,
PROPS: 8 /* PROPS */,
NEED_PATCH: 32 /* NEED_PATCH */,
FULL_PROPS: 16 /* FULL_PROPS */,
KEYED_FRAGMENT: 64 /* KEYED_FRAGMENT */,
UNKEYED_FRAGMENT: 128 /* UNKEYED_FRAGMENT */,
DYNAMIC_SLOTS: 256 /* DYNAMIC_SLOTS */,
BAIL: -1 /* BAIL */
};
const globalsWhitelist = new Set(('Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,' +
'decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,' +
'Object,Boolean,String,RegExp,Map,Set,JSON,Intl').split(','));
const EMPTY_OBJ = {};
const EMPTY_ARR = [];
const NOOP = () => { };
const extend = (a, b) => {
for (const key in b) {
a[key] = b[key];
}
return a;
};
const hasOwnProperty = Object.prototype.hasOwnProperty;
const hasOwn = (val, key) => hasOwnProperty.call(val, key);
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
const vnodeHooksRE = /^vnode/;
const isReservedProp = (key) => key === 'key' || key === 'ref' || vnodeHooksRE.test(key);
const camelizeRE = /-(\w)/g;
const camelize = (str) => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
};
const hyphenateRE = /\B([A-Z])/g;
const hyphenate = (str) => {
return str.replace(hyphenateRE, '-$1').toLowerCase();
};
const capitalize = (str) => {
return str.charAt(0).toUpperCase() + str.slice(1);
};
// implementation, close to no-op
function createComponent(options) {
return isFunction(options) ? { setup: options } : options;
}
function callWithErrorHandling(fn, instance, type, args) {
let res;
try {
res = args ? fn(...args) : fn();
}
catch (err) {
handleError(err, instance, type);
}
return res;
}
function callWithAsyncErrorHandling(fn, instance, type, args) {
const res = callWithErrorHandling(fn, instance, type, args);
if (res != null && !res._isVue && typeof res.then === 'function') {
res.catch((err) => {
handleError(err, instance, type);
});
}
return res;
}
function handleError(err, instance, type) {
const contextVNode = instance ? instance.vnode : null;
if (instance) {
let cur = instance.parent;
// the exposed instance is the render proxy to keep it consistent with 2.x
const exposedInstance = instance.renderProxy;
// in production the hook receives only the error code
const errorInfo = type;
while (cur) {
const errorCapturedHooks = cur.ec;
if (errorCapturedHooks !== null) {
for (let i = 0; i < errorCapturedHooks.length; i++) {
if (errorCapturedHooks[i](err, exposedInstance, errorInfo)) {
return;
}
}
}
cur = cur.parent;
}
// app-level handling
const appErrorHandler = instance.appContext.config.errorHandler;
if (appErrorHandler) {
callWithErrorHandling(appErrorHandler, null, 8 /* APP_ERROR_HANDLER */, [err, exposedInstance, errorInfo]);
return;
}
}
logError(err);
}
function logError(err, type, contextVNode) {
// default behavior is crash in prod & test, recover in dev.
// TODO we should probably make this configurable via `createApp`
{
throw err;
}
}
const queue = [];
const postFlushCbs = [];
const p = Promise.resolve();
let isFlushing = false;
function nextTick(fn) {
return fn ? p.then(fn) : p;
}
function queueJob(job) {
if (queue.indexOf(job) === -1) {
queue.push(job);
if (!isFlushing) {
nextTick(flushJobs);
}
}
}
function queuePostFlushCb(cb) {
if (Array.isArray(cb)) {
postFlushCbs.push.apply(postFlushCbs, cb);
}
else {
postFlushCbs.push(cb);
}
if (!isFlushing) {
nextTick(flushJobs);
}
}
const dedupe = (cbs) => Array.from(new Set(cbs));
function flushPostFlushCbs() {
if (postFlushCbs.length) {
const cbs = dedupe(postFlushCbs);
postFlushCbs.length = 0;
for (let i = 0; i < cbs.length; i++) {
cbs[i]();
}
}
}
function flushJobs(seenJobs) {
isFlushing = true;
let job;
while ((job = queue.shift())) {
try {
job();
}
catch (err) {
handleError(err, null, 10 /* SCHEDULER */);
}
}
flushPostFlushCbs();
isFlushing = false;
// some postFlushCb queued jobs!
// keep flushing until it drains.
if (queue.length) {
flushJobs();
}
}
const Fragment = Symbol();
const Text = Symbol();
const Comment = Symbol();
const Portal = Symbol();
const Suspense = Symbol();
// Since v-if and v-for are the two possible ways node structure can dynamically
// change, once we consider v-if branches and each v-for fragment a block, we
// can divide a template into nested blocks, and within each block the node
// structure would be stable. This allows us to skip most children diffing
// and only worry about the dynamic nodes (indicated by patch flags).
const blockStack = [];
// Open a block.
// This must be called before `createBlock`. It cannot be part of `createBlock`
// because the children of the block are evaluated before `createBlock` itself
// is called. The generated code typically looks like this:
//
// function render() {
// return (openBlock(),createBlock('div', null, [...]))
// }
//
// disableTracking is true when creating a fragment block, since a fragment
// always diffs its children.
function openBlock(disableTracking) {
blockStack.push(disableTracking ? null : []);
}
let shouldTrack = true;
// Create a block root vnode. Takes the same exact arguments as `createVNode`.
// A block root keeps track of dynamic nodes within the block in the
// `dynamicChildren` array.
function createBlock(type, props, children, patchFlag, dynamicProps) {
// avoid a block with optFlag tracking itself
shouldTrack = false;
const vnode = createVNode(type, props, children, patchFlag, dynamicProps);
shouldTrack = true;
const trackedNodes = blockStack.pop();
vnode.dynamicChildren =
trackedNodes && trackedNodes.length ? trackedNodes : EMPTY_ARR;
// a block is always going to be patched
trackDynamicNode(vnode);
return vnode;
}
function createVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null) {
// class & style normalization.
if (props !== null) {
// for reactive or proxy objects, we need to clone it to enable mutation.
if (reactivity.isReactive(props) || SetupProxySymbol in props) {
props = extend({}, props);
}
// class normalization only needed if the vnode isn't generated by
// compiler-optimized code
if (props.class != null && !(patchFlag & 2 /* CLASS */)) {
props.class = normalizeClass(props.class);
}
let { style } = props;
if (style != null) {
// reactive state objects need to be cloned since they are likely to be
// mutated
if (reactivity.isReactive(style) && !isArray(style)) {
style = extend({}, style);
}
props.style = normalizeStyle(style);
}
}
// encode the vnode type information into a bitmap
const shapeFlag = isString(type)
? 1 /* ELEMENT */
: isObject(type)
? 4 /* STATEFUL_COMPONENT */
: isFunction(type)
? 2 /* FUNCTIONAL_COMPONENT */
: 0;
const vnode = {
_isVNode: true,
type,
props,
key: (props && props.key) || null,
ref: (props && props.ref) || null,
children: null,
component: null,
suspense: null,
el: null,
anchor: null,
target: null,
shapeFlag,
patchFlag,
dynamicProps,
dynamicChildren: null,
appContext: null
};
normalizeChildren(vnode, children);
// presence of a patch flag indicates this node needs patching on updates.
// component nodes also should always be patched, because even if the
// component doesn't need to update, it needs to persist the instance on to
// the next vnode so that it can be properly unmounted later.
if (shouldTrack &&
(patchFlag ||
shapeFlag & 4 /* STATEFUL_COMPONENT */ ||
shapeFlag & 2 /* FUNCTIONAL_COMPONENT */)) {
trackDynamicNode(vnode);
}
return vnode;
}
function trackDynamicNode(vnode) {
const currentBlockDynamicNodes = blockStack[blockStack.length - 1];
if (currentBlockDynamicNodes != null) {
currentBlockDynamicNodes.push(vnode);
}
}
function cloneVNode(vnode) {
return {
_isVNode: true,
type: vnode.type,
props: vnode.props,
key: vnode.key,
ref: vnode.ref,
children: vnode.children,
target: vnode.target,
shapeFlag: vnode.shapeFlag,
patchFlag: vnode.patchFlag,
dynamicProps: vnode.dynamicProps,
dynamicChildren: vnode.dynamicChildren,
appContext: vnode.appContext,
// these should be set to null since they should only be present on
// mounted VNodes. If they are somehow not null, this means we have
// encountered an already-mounted vnode being used again.
component: null,
suspense: null,
el: null,
anchor: null
};
}
function normalizeVNode(child) {
if (child == null) {
// empty placeholder
return createVNode(Comment);
}
else if (isArray(child)) {
// fragment
return createVNode(Fragment, null, child);
}
else if (typeof child === 'object') {
// already vnode, this should be the most common since compiled templates
// always produce all-vnode children arrays
return child.el === null ? child : cloneVNode(child);
}
else {
// primitive types
return createVNode(Text, null, child + '');
}
}
function normalizeChildren(vnode, children) {
let type = 0;
if (children == null) {
children = null;
}
else if (isArray(children)) {
type = 16 /* ARRAY_CHILDREN */;
}
else if (typeof children === 'object') {
type = 32 /* SLOTS_CHILDREN */;
}
else if (isFunction(children)) {
children = { default: children };
type = 32 /* SLOTS_CHILDREN */;
}
else {
children = isString(children) ? children : children + '';
type = 8 /* TEXT_CHILDREN */;
}
vnode.children = children;
vnode.shapeFlag |= type;
}
function normalizeStyle(value) {
if (isArray(value)) {
const res = {};
for (let i = 0; i < value.length; i++) {
const normalized = normalizeStyle(value[i]);
if (normalized) {
for (const key in normalized) {
res[key] = normalized[key];
}
}
}
return res;
}
else if (isObject(value)) {
return value;
}
}
function normalizeClass(value) {
let res = '';
if (isString(value)) {
res = value;
}
else if (isArray(value)) {
for (let i = 0; i < value.length; i++) {
res += normalizeClass(value[i]) + ' ';
}
}
else if (isObject(value)) {
for (const name in value) {
if (value[name]) {
res += name + ' ';
}
}
}
return res.trim();
}
const handlersRE = /^on|^vnode/;
function mergeProps(...args) {
const ret = {};
extend(ret, args[0]);
for (let i = 1; i < args.length; i++) {
const toMerge = args[i];
for (const key in toMerge) {
if (key === 'class') {
ret.class = normalizeClass([ret.class, toMerge.class]);
}
else if (key === 'style') {
ret.style = normalizeStyle([ret.style, toMerge.style]);
}
else if (handlersRE.test(key)) {
// on*, vnode*
const existing = ret[key];
ret[key] = existing
? [].concat(existing, toMerge[key])
: toMerge[key];
}
else {
ret[key] = toMerge[key];
}
}
}
return ret;
}
function injectHook(type, hook, target) {
if (target) {
(target[type] || (target[type] = [])).push((...args) => {
if (target.isUnmounted) {
return;
}
// disable tracking inside all lifecycle hooks
// since they can potentially be called inside effects.
reactivity.pauseTracking();
// Set currentInstance during hook invocation.
// This assumes the hook does not synchronously trigger other hooks, which
// can only be false when the user does something really funky.
setCurrentInstance(target);
const res = callWithAsyncErrorHandling(hook, target, type, args);
setCurrentInstance(null);
reactivity.resumeTracking();
return res;
});
}
}
function onBeforeMount(hook, target = currentInstance) {
injectHook("bm" /* BEFORE_MOUNT */, hook, target);
}
function onMounted(hook, target = currentInstance) {
injectHook("m" /* MOUNTED */, hook, target);
}
function onBeforeUpdate(hook, target = currentInstance) {
injectHook("bu" /* BEFORE_UPDATE */, hook, target);
}
function onUpdated(hook, target = currentInstance) {
injectHook("u" /* UPDATED */, hook, target);
}
function onBeforeUnmount(hook, target = currentInstance) {
injectHook("bum" /* BEFORE_UNMOUNT */, hook, target);
}
function onUnmounted(hook, target = currentInstance) {
injectHook("um" /* UNMOUNTED */, hook, target);
}
function onRenderTriggered(hook, target = currentInstance) {
injectHook("rtg" /* RENDER_TRIGGERED */, hook, target);
}
function onRenderTracked(hook, target = currentInstance) {
injectHook("rtc" /* RENDER_TRACKED */, hook, target);
}
function onErrorCaptured(hook, target = currentInstance) {
injectHook("ec" /* ERROR_CAPTURED */, hook, target);
}
// mark the current rendering instance for asset resolution (e.g.
// resolveComponent, resolveDirective) during render
let currentRenderingInstance = null;
function renderComponentRoot(instance) {
const { type: Component, vnode, renderProxy, props, slots, attrs, emit } = instance;
let result;
currentRenderingInstance = instance;
try {
if (vnode.shapeFlag & 4 /* STATEFUL_COMPONENT */) {
result = normalizeVNode(instance.render.call(renderProxy));
}
else {
// functional
const render = Component;
result = normalizeVNode(render.length > 1
? render(props, {
attrs,
slots,
emit
})
: render(props, null));
}
}
catch (err) {
handleError(err, instance, 1 /* RENDER_FUNCTION */);
result = createVNode(Comment);
}
currentRenderingInstance = null;
return result;
}
function shouldUpdateComponent(prevVNode, nextVNode, optimized) {
const { props: prevProps, children: prevChildren } = prevVNode;
const { props: nextProps, children: nextChildren, patchFlag } = nextVNode;
if (patchFlag > 0) {
if (patchFlag & 256 /* DYNAMIC_SLOTS */) {
// slot content that references values that might have changed,
// e.g. in a v-for
return true;
}
if (patchFlag & 16 /* FULL_PROPS */) {
// presence of this flag indicates props are always non-null
return hasPropsChanged(prevProps, nextProps);
}
else if (patchFlag & 8 /* PROPS */) {
const dynamicProps = nextVNode.dynamicProps;
for (let i = 0; i < dynamicProps.length; i++) {
const key = dynamicProps[i];
if (nextProps[key] !== prevProps[key]) {
return true;
}
}
}
}
else if (!optimized) {
// this path is only taken by manually written render functions
// so presence of any children leads to a forced update
if (prevChildren != null || nextChildren != null) {
return true;
}
if (prevProps === nextProps) {
return false;
}
if (prevProps === null) {
return nextProps !== null;
}
if (nextProps === null) {
return prevProps !== null;
}
return hasPropsChanged(prevProps, nextProps);
}
return false;
}
function hasPropsChanged(prevProps, nextProps) {
const nextKeys = Object.keys(nextProps);
if (nextKeys.length !== Object.keys(prevProps).length) {
return true;
}
for (let i = 0; i < nextKeys.length; i++) {
const key = nextKeys[i];
if (nextProps[key] !== prevProps[key]) {
return true;
}
}
return false;
}
// resolve raw VNode data.
// - filter out reserved keys (key, ref, slots)
// - extract class and style into $attrs (to be merged onto child
// component root)
// - for the rest:
// - if has declared props: put declared ones in `props`, the rest in `attrs`
// - else: everything goes in `props`.
function resolveProps(instance, rawProps, _options) {
const hasDeclaredProps = _options != null;
const options = normalizePropsOptions(_options);
if (!rawProps && !hasDeclaredProps) {
return;
}
const props = {};
let attrs = void 0;
// update the instance propsProxy (passed to setup()) to trigger potential
// changes
const propsProxy = instance.propsProxy;
const setProp = propsProxy
? (key, val) => {
props[key] = val;
propsProxy[key] = val;
}
: (key, val) => {
props[key] = val;
};
// allow mutation of propsProxy (which is readonly by default)
reactivity.unlock();
if (rawProps != null) {
for (const key in rawProps) {
// key, ref are reserved
if (isReservedProp(key))
continue;
// any non-declared data are put into a separate `attrs` object
// for spreading
if (hasDeclaredProps && !hasOwn(options, key)) {
(attrs || (attrs = {}))[key] = rawProps[key];
}
else {
setProp(key, rawProps[key]);
}
}
}
// set default values, cast booleans & run validators
if (hasDeclaredProps) {
for (const key in options) {
let opt = options[key];
if (opt == null)
continue;
const isAbsent = !hasOwn(props, key);
const hasDefault = hasOwn(opt, 'default');
const currentValue = props[key];
// default values
if (hasDefault && currentValue === undefined) {
const defaultValue = opt.default;
setProp(key, isFunction(defaultValue) ? defaultValue() : defaultValue);
}
// boolean casting
if (opt["1" /* shouldCast */]) {
if (isAbsent && !hasDefault) {
setProp(key, false);
}
else if (opt["2" /* shouldCastTrue */] &&
(currentValue === '' || currentValue === hyphenate(key))) {
setProp(key, true);
}
}
}
}
else {
// if component has no declared props, $attrs === $props
attrs = props;
}
// in case of dynamic props, check if we need to delete keys from
// the props proxy
const { patchFlag } = instance.vnode;
if (propsProxy !== null &&
(patchFlag === 0 || patchFlag & 16 /* FULL_PROPS */)) {
const rawInitialProps = reactivity.toRaw(propsProxy);
for (const key in rawInitialProps) {
if (!hasOwn(props, key)) {
delete propsProxy[key];
}
}
}
// lock readonly
reactivity.lock();
instance.props = props;
instance.attrs = options
? attrs
: instance.props;
}
const normalizationMap = new WeakMap();
function normalizePropsOptions(raw) {
if (!raw) {
return null;
}
if (normalizationMap.has(raw)) {
return normalizationMap.get(raw);
}
const normalized = {};
normalizationMap.set(raw, normalized);
if (isArray(raw)) {
for (let i = 0; i < raw.length; i++) {
const normalizedKey = camelize(raw[i]);
if (normalizedKey[0] !== '$') {
normalized[normalizedKey] = EMPTY_OBJ;
}
}
}
else {
for (const key in raw) {
const normalizedKey = camelize(key);
if (normalizedKey[0] !== '$') {
const opt = raw[key];
const prop = (normalized[normalizedKey] =
isArray(opt) || isFunction(opt) ? { type: opt } : opt);
if (prop != null) {
const booleanIndex = getTypeIndex(Boolean, prop.type);
const stringIndex = getTypeIndex(String, prop.type);
prop["1" /* shouldCast */] = booleanIndex > -1;
prop["2" /* shouldCastTrue */] = booleanIndex < stringIndex;
}
}
}
}
return normalized;
}
// use function string name to check type constructors
// so that it works across vms / iframes.
function getType(ctor) {
const match = ctor && ctor.toString().match(/^\s*function (\w+)/);
return match ? match[1] : '';
}
function isSameType(a, b) {
return getType(a) === getType(b);
}
function getTypeIndex(type, expectedTypes) {
if (isArray(expectedTypes)) {
for (let i = 0, len = expectedTypes.length; i < len; i++) {
if (isSameType(expectedTypes[i], type)) {
return i;
}
}
}
else if (isObject(expectedTypes)) {
return isSameType(expectedTypes, type) ? 0 : -1;
}
return -1;
}
const normalizeSlotValue = (value) => isArray(value)
? value.map(normalizeVNode)
: [normalizeVNode(value)];
const normalizeSlot = (key, rawSlot) => (props) => {
return normalizeSlotValue(rawSlot(props));
};
function resolveSlots(instance, children) {
let slots;
if (instance.vnode.shapeFlag & 32 /* SLOTS_CHILDREN */) {
if (children._compiled) {
// pre-normalized slots object generated by compiler
slots = children;
}
else {
slots = {};
for (const key in children) {
let value = children[key];
if (isFunction(value)) {
slots[key] = normalizeSlot(key, value);
}
else if (value != null) {
value = normalizeSlotValue(value);
slots[key] = () => value;
}
}
}
}
else if (children !== null) {
const normalized = normalizeSlotValue(children);
slots = { default: () => normalized };
}
if (slots !== void 0) {
instance.slots = slots;
}
}
/**
Runtime helper for applying directives to a vnode. Example usage:
const comp = resolveComponent('comp')
const foo = resolveDirective('foo')
const bar = resolveDirective('bar')
return applyDirectives(h(comp), [
[foo, this.x],
[bar, this.y]
])
*/
const valueCache = new WeakMap();
function applyDirective(props, instance, directive, value, arg, modifiers) {
let valueCacheForDir = valueCache.get(directive);
if (!valueCacheForDir) {
valueCacheForDir = new WeakMap();
valueCache.set(directive, valueCacheForDir);
}
for (const key in directive) {
const hook = directive[key];
const hookKey = `vnode` + key[0].toUpperCase() + key.slice(1);
const vnodeHook = (vnode, prevVNode) => {
let oldValue;
if (prevVNode != null) {
oldValue = valueCacheForDir.get(prevVNode);
valueCacheForDir.delete(prevVNode);
}
valueCacheForDir.set(vnode, value);
hook(vnode.el, {
instance: instance.renderProxy,
value,
oldValue,
arg,
modifiers
}, vnode, prevVNode);
};
const existing = props[hookKey];
props[hookKey] = existing
? [].concat(existing, vnodeHook)
: vnodeHook;
}
}
function applyDirectives(vnode, directives) {
const instance = currentRenderingInstance;
if (instance !== null) {
vnode = cloneVNode(vnode);
vnode.props = vnode.props != null ? extend({}, vnode.props) : {};
for (let i = 0; i < directives.length; i++) {
applyDirective(vnode.props, instance, ...directives[i]);
}
}
return vnode;
}
function invokeDirectiveHook(hook, instance, vnode, prevVNode = null) {
const args = [vnode, prevVNode];
if (isArray(hook)) {
for (let i = 0; i < hook.length; i++) {
callWithAsyncErrorHandling(hook[i], instance, 7 /* DIRECTIVE_HOOK */, args);
}
}
else if (isFunction(hook)) {
callWithAsyncErrorHandling(hook, instance, 7 /* DIRECTIVE_HOOK */, args);
}
}
function createAppContext() {
return {
config: {
devtools: true,
performance: false,
errorHandler: undefined,
warnHandler: undefined
},
mixins: [],
components: {},
directives: {},
provides: {}
};
}
function createAppAPI(render) {
return function createApp() {
const context = createAppContext();
let isMounted = false;
const app = {
get config() {
return context.config;
},
set config(v) {
},
use(plugin) {
if (isFunction(plugin)) {
plugin(app);
}
else if (isFunction(plugin.install)) {
plugin.install(app);
}
return app;
},
mixin(mixin) {
context.mixins.push(mixin);
return app;
},
component(name, component) {
// TODO component name validation
if (!component) {
return context.components[name];
}
else {
context.components[name] = component;
return app;
}
},
directive(name, directive) {
// TODO directive name validation
if (!directive) {
return context.directives[name];
}
else {
context.directives[name] = directive;
return app;
}
},
mount(rootComponent, rootContainer, rootProps) {
if (!isMounted) {
const vnode = createVNode(rootComponent, rootProps);
// store app context on the root VNode.
// this will be set on the root instance on initial mount.
vnode.appContext = context;
render(vnode, rootContainer);
isMounted = true;
return vnode.component.renderProxy;
}
},
provide(key, value) {
context.provides[key] = value;
}
};
return app;
};
}
function createSuspenseBoundary(vnode, parent, parentComponent, container, hiddenContainer, anchor, isSVG, optimized) {
return {
vnode,
parent,
parentComponent,
isSVG,
optimized,
container,
hiddenContainer,
anchor,
deps: 0,
subTree: null,
fallbackTree: null,
isResolved: false,
isUnmounted: false,
effects: []
};
}
function normalizeSuspenseChildren(vnode) {
const { shapeFlag, children } = vnode;
if (shapeFlag & PublicShapeFlags.SLOTS_CHILDREN) {
const { default: d, fallback } = children;
return {
content: normalizeVNode(isFunction(d) ? d() : d),
fallback: normalizeVNode(isFunction(fallback) ? fallback() : fallback)
};
}
else {
return {
content: normalizeVNode(children),
fallback: normalizeVNode(null)
};
}
}
const prodEffectOptions = {
scheduler: queueJob
};
function isSameType$1(n1, n2) {
return n1.type === n2.type && n1.key === n2.key;
}
function invokeHooks(hooks, arg) {
for (let i = 0; i < hooks.length; i++) {
hooks[i](arg);
}
}
function queuePostRenderEffect(fn, suspense) {
if (suspense !== null && !suspense.isResolved) {
if (isArray(fn)) {
suspense.effects.push(...fn);
}
else {
suspense.effects.push(fn);
}
}
else {
queuePostFlushCb(fn);
}
}
/**
* The createRenderer function accepts two generic arguments:
* HostNode and HostElement, corresponding to Node and Element types in the
* host environment. For example, for runtime-dom, HostNode would be the DOM
* `Node` interface and HostElement would be the DOM `Element` interface.
*
* Custom renderers can pass in the platform specific types like this:
*
* ``` js
* const { render, createApp } = createRenderer<Node, Element>({
* patchProp,
* ...nodeOps
* })
* ```
*/
function createRenderer(options) {
const { insert: hostInsert, remove: hostRemove, patchProp: hostPatchProp, createElement: hostCreateElement, createText: hostCreateText, createComment: hostCreateComment, setText: hostSetText, setElementText: hostSetElementText, parentNode: hostParentNode, nextSibling: hostNextSibling, querySelector: hostQuerySelector } = options;
function patch(n1, // null means this is a mount
n2, container, anchor = null, parentComponent = null, parentSuspense = null, isSVG = false, optimized = false) {
// patching & not same type, unmount old tree
if (n1 != null && !isSameType$1(n1, n2)) {
anchor = getNextHostNode(n1);
unmount(n1, parentComponent, parentSuspense, true);
n1 = null;
}
const { type, shapeFlag } = n2;
switch (type) {
case Text:
processText(n1, n2, container, anchor);
break;
case Comment:
processCommentNode(n1, n2, container, anchor);
break;
case Fragment:
processFragment(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
break;
case Portal:
processPortal(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
break;
case Suspense:
{
processSuspense(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
break;
default:
if (shapeFlag & 1 /* ELEMENT */) {
processElement(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
else if (shapeFlag & 6 /* COMPONENT */) {
processComponent(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
}
}
function processText(n1, n2, container, anchor) {
if (n1 == null) {
hostInsert((n2.el = hostCreateText(n2.children)), container, anchor);
}
else {
const el = (n2.el = n1.el);
if (n2.children !== n1.children) {
hostSetText(el, n2.children);
}
}
}
function processCommentNode(n1, n2, container, anchor) {
if (n1 == null) {
hostInsert((n2.el = hostCreateComment(n2.children || '')), container, anchor);
}
else {
// there's no support for dynamic comments
n2.el = n1.el;
}
}
function processElement(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) {
if (n1 == null) {
mountElement(n2, container, anchor, parentComponent, parentSuspense, isSVG);
}
else {
patchElement(n1, n2, parentComponent, parentSuspense, isSVG, optimized);
}
if (n2.ref !== null && parentComponent !== null) {
setRef(n2.ref, n1 && n1.ref, parentComponent, n2.el);
}
}
function mountElement(vnode, container, anchor, parentComponent, parentSuspense, isSVG) {
const tag = vnode.type;
isSVG = isSVG || tag === 'svg';
const el = (vnode.el = hostCreateElement(tag, isSVG));
const { props, shapeFlag } = vnode;
if (props != null) {
for (const key in props) {
if (isReservedProp(key))
continue;
hostPatchProp(el, key, props[key], null, isSVG);
}
if (props.vnodeBeforeMount != null) {
invokeDirectiveHook(props.vnodeBeforeMount, parentComponent, vnode);
}
}
if (shapeFlag & 8 /* TEXT_CHILDREN */) {
hostSetElementText(el, vnode.children);
}
else if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
mountChildren(vnode.children, el, null, parentComponent, parentSuspense, isSVG);
}
hostInsert(el, container, anchor);
if (props != null && props.vnodeMounted != null) {
queuePostRenderEffect(() => {
invokeDirectiveHook(props.vnodeMounted, parentComponent, vnode);
}, parentSuspense);
}
}
function mountChildren(children, container, anchor, parentComponent, parentSuspense, isSVG, start = 0) {
for (let i = start; i < children.length; i++) {
const child = (children[i] = normalizeVNode(children[i]));
patch(null, child, container, anchor, parentComponent, parentSuspense, isSVG);
}
}
function patchElement(n1, n2, parentComponent, parentSuspense, isSVG, optimized) {
const el = (n2.el = n1.el);
const { patchFlag, dynamicChildren } = n2;
const oldProps = (n1 && n1.props) || EMPTY_OBJ;
const newProps = n2.props || EMPTY_OBJ;
if (newProps.vnodeBeforeUpdate != null) {
invokeDirectiveHook(newProps.vnodeBeforeUpdate, parentComponent, n2, n1);
}
if (patchFlag > 0) {
// the presence of a patchFlag means this element's render code was
// generated by the compiler and can take the fast path.
// in this path old node and new node are guaranteed to have the same shape
// (i.e. at the exact same position in the source template)
if (patchFlag & 16 /* FULL_PROPS */) {
// element props contain dynamic keys, full diff needed
patchProps(el, n2, oldProps, newProps, parentComponent, parentSuspense, isSVG);
}
else {
// class
// this flag is matched when the element has dynamic class bindings.
if (patchFlag & 2 /* CLASS */) {
if (oldProps.class !== newProps.class) {
hostPatchProp(el, 'class', newProps.class, null, isSVG);
}
}
// style
// this flag is matched when the element has dynamic style bindings
if (patchFlag & 4 /* STYLE */) {
hostPatchProp(el, 'style', newProps.style, oldProps.style, isSVG);
}
// props
// This flag is matched when the element has dynamic prop/attr bindings
// other than class and style. The keys of dynamic prop/attrs are saved for
// faster iteration.
// Note dynamic keys like :[foo]="bar" will cause this optimization to
// bail out and go through a full diff because we need to unset the old key
if (patchFlag & 8 /* PROPS */) {
// if the flag is present then dynamicProps must be non-null
const propsToUpdate = n2.dynamicProps;
for (let i = 0; i < propsToUpdate.length; i++) {
const key = propsToUpdate[i];
const prev = oldProps[key];
const next = newProps[key];
if (prev !== next) {
hostPatchProp(el, key, next, prev, isSVG, n1.children, parentComponent, parentSuspense, unmountChildren);
}
}
}
}
// text
// This flag is matched when the element has only dynamic text children.
// this flag is terminal (i.e. skips children diffing).
if (patchFlag & 1 /* TEXT */) {
if (n1.children !== n2.children) {
hostSetElementText(el, n2.children);
}
return; // terminal
}
}
else if (!optimized) {
// unoptimized, full diff
patchProps(el, n2, oldProps, newProps, parentComponent, parentSuspense, isSVG);
}
if (dynamicChildren != null) {
// children fast path
const oldDynamicChildren = n1.dynamicChildren;
for (let i = 0; i < dynamicChildren.length; i++) {
patch(oldDynamicChildren[i], dynamicChildren[i], el, null, parentComponent, parentSuspense, isSVG, true);
}
}
else if (!optimized) {
// full diff
patchChildren(n1, n2, el, null, parentComponent, parentSuspense, isSVG);
}
if (newProps.vnodeUpdated != null) {
queuePostRenderEffect(() => {
invokeDirectiveHook(newProps.vnodeUpdated, parentComponent, n2, n1);
}, parentSuspense);
}
}
function patchProps(el, vnode, oldProps, newProps, parentComponent, parentSuspense, isSVG) {
if (oldProps !== newProps) {
for (const key in newProps) {
if (isReservedProp(key))
continue;
const next = newProps[key];
const prev = oldProps[key];
if (next !== prev) {
hostPatchProp(el, key, next, prev, isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren);
}
}
if (oldProps !== EMPTY_OBJ) {
for (const key in oldProps) {
if (isReservedProp(key))
continue;
if (!(key in newProps)) {
hostPatchProp(el, key, null, null, isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren);
}
}
}
}
}
function processFragment(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) {
const fragmentStartAnchor = (n2.el = n1 ? n1.el : hostCreateComment(''));
const fragmentEndAnchor = (n2.anchor = n1
? n1.anchor
: hostCreateComment(''));
if (n1 == null) {
hostInsert(fragmentStartAnchor, container, anchor);
hostInsert(fragmentEndAnchor, container, anchor);
// a fragment can only have array children
// since they are either generated by the compiler, or implicitly created
// from arrays.
mountChildren(n2.children, container, fragmentEndAnchor, parentComponent, parentSuspense, isSVG);
}
else {
patchChildren(n1, n2, container, fragmentEndAnchor, parentComponent, parentSuspense, isSVG, optimized);
}
}
function processPortal(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) {
const targetSelector = n2.props && n2.props.target;
const { patchFlag, shapeFlag, children } = n2;
if (n1 == null) {
const target = (n2.target = isString(targetSelector)
? hostQuerySelector(targetSelector)
: null);
if (target != null) {
if (shapeFlag & 8 /* TEXT_CHILDREN */) {
hostSetElementText(target, children);
}
else if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
mountChildren(children, target, null, parentComponent, parentSuspense, isSVG);
}
}
}
else {
// update content
const target = (n2.target = n1.target);
if (patchFlag === 1 /* TEXT */) {
hostSetElementText(target, children);
}
else if (!optimized) {
patchChildren(n1, n2, target, null, parentComponent, parentSuspense, isSVG);
}
// target changed
if (targetSelector !== (n1.props && n1.props.target)) {
const nextTarget = (n2.target = isString(targetSelector)
? hostQuerySelector(targetSelector)
: null);
if (nextTarget != null) {
// move content
if (shapeFlag & 8 /* TEXT_CHILDREN */) {
hostSetElementText(target, '');
hostSetElementText(nextTarget, children);
}
else if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
for (let i = 0; i < children.length; i++) {
move(children[i], nextTarget, null);
}
}
}
}
}
// insert an empty node as the placeholder for the portal
processCommentNode(n1, n2, container, anchor);
}
function processSuspense(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) {
if (n1 == null) {
mountSuspense(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
else {
patchSuspense(n1, n2, container, anchor, parentComponent, isSVG, optimized);
}
}
function mountSuspense(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) {
const hiddenContainer = hostCreateElement('div');
const suspense = (n2.suspense = createSuspenseBoundary(n2, parentSuspense, parentComponent, container, hiddenContainer, anchor, isSVG, optimized));
const { content, fallback } = normalizeSuspenseChildren(n2);
suspense.subTree = content;
suspense.fallbackTree = fallback;
// start mounting the content subtree in an off-dom container
patch(null, content, hiddenContainer, null, parentComponent, suspense, isSVG, optimized);
// now check if we have encountered any async deps
if (suspense.deps > 0) {
// mount the fallback tree
patch(null, fallback, container, anchor, parentComponent, null, // fallback tree will not have suspense context
isSVG, optimized);
n2.el = fallback.el;
}
else {
// Suspense has no async deps. Just resolve.
resolveSuspense(suspense);
}
}
function patchSuspense(n1, n2, container, anchor, parentComponent, isSVG, optimized) {
const suspense = (n2.suspense = n1.suspense);
suspense.vnode = n2;
const { content, fallback } = normalizeSuspenseChildren(n2);
const oldSubTree = suspense.subTree;
const oldFallbackTree = suspense.fallbackTree;
if (!suspense.isResolved) {
patch(oldSubTree, content, suspense.hiddenContainer, null, parentComponent, suspense, isSVG, optimized);
if (suspense.deps > 0) {
// still pending. patch the fallback tree.
patch(oldFallbackTree, fallback, container, anchor, parentComponent, null, // fallback tree will not have suspense context
isSVG, optimized);
n2.el = fallback.el;
}
// If deps somehow becomes 0 after the patch it means the patch caused an
// async dep component to unmount and removed its dep. It will cause the
// suspense to resolve and we don't need to do anything here.
}
else {
// just normal patch inner content as a fragment
patch(oldSubTree, content, container, anchor, parentComponent, suspense, isSVG, optimized);
n2.el = content.el;
}
suspense.subTree = content;
suspense.fallbackTree = fallback;
}
function resolveSuspense(suspense) {
const { vnode, subTree, fallbackTree, effects, parentComponent, container } = suspense;
// this is initial anchor on mount
let { anchor } = suspense;
// unmount fallback tree
if (fallbackTree.el) {
// if the fallback tree was mounted, it may have been moved
// as part of a parent suspense. get the latest anchor for insertion
anchor = getNextHostNode(fallbackTree);
unmount(fallbackTree, parentComponent, suspense, true);
}
// move content from off-dom container to actual container
move(subTree, container, anchor);
const el = (vnode.el = subTree.el);
// suspense as the root node of a component...
if (parentComponent && parentComponent.subTree === vnode) {
parentComponent.vnode.el = el;
updateHOCHostEl(parentComponent, el);
}
// check if there is a pending parent suspense
let parent = suspense.parent;
let hasUnresolvedAncestor = false;
while (parent) {
if (!parent.isResolved) {
// found a pending parent suspense, merge buffered post jobs
// into that parent
parent.effects.push(...effects);
hasUnresolvedAncestor = true;
break;
}
parent = parent.parent;
}
// no pending parent suspense, flush all jobs
if (!hasUnresolvedAncestor) {
queuePostFlushCb(effects);
}
suspense.isResolved = true;
// invoke @resolve event
const onResolve = vnode.props && vnode.props.onResolve;
if (isFunction(onResolve)) {
onResolve();
}
}
function restartSuspense(suspense) {
suspense.isResolved = false;
const { vnode, subTree, fallbackTree, parentComponent, container, hiddenContainer, isSVG, optimized } = suspense;
// move content tree back to the off-dom container
const anchor = getNextHostNode(subTree);
move(subTree, hiddenContainer, null);
// remount the fallback tree
patch(null, fallbackTree, container, anchor, parentComponent, null, // fallback tree will not have suspense context
isSVG, optimized);
const el = (vnode.el = fallbackTree.el);
// suspense as the root node of a component...
if (parentComponent && parentComponent.subTree === vnode) {
parentComponent.vnode.el = el;
updateHOCHostEl(parentComponent, el);
}
// invoke @suspense event
const onSuspense = vnode.props && vnode.props.onSuspense;
if (isFunction(onSuspense)) {
onSuspense();
}
}
function processComponent(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) {
if (n1 == null) {
mountComponent(n2, container, anchor, parentComponent, parentSuspense, isSVG);
}
else {
const instance = (n2.component = n1.component);
if (shouldUpdateComponent(n1, n2, optimized)) {
if (
instance.asyncDep &&
!instance.asyncResolved) {
updateComponentPreRender(instance, n2);
return;
}
else {
// normal update
instance.next = n2;
// instance.update is the reactive effect runner.
instance.update();
}
}
else {
// no update needed. just copy over properties
n2.component = n1.component;
n2.el = n1.el;
}
}
if (n2.ref !== null && parentComponent !== null) {
setRef(n2.ref, n1 && n1.ref, parentComponent, n2.component.renderProxy);
}
}
function mountComponent(initialVNode, container, anchor, parentComponent, parentSuspense, isSVG) {
const instance = (initialVNode.component = createComponentInstance(initialVNode, parentComponent));
// resolve props and slots for setup context
const propsOptions = initialVNode.type.props;
resolveProps(instance, initialVNode.props, propsOptions);
resolveSlots(instance, initialVNode.children);
// setup stateful logic
if (initialVNode.shapeFlag & 4 /* STATEFUL_COMPONENT */) {
setupStatefulComponent(instance, parentSuspense);
}
// setup() is async. This component relies on async logic to be resolved
// before proceeding
if ( instance.asyncDep) {
if (!parentSuspense) {
// TODO handle this properly
throw new Error('Async component without a suspense boundary!');
}
// parent suspense already resolved, need to re-suspense
// use queueJob so it's handled synchronously after patching the current
// suspense tree
if (parentSuspense.isResolved) {
queueJob(() => {
restartSuspense(parentSuspense);
});
}
parentSuspense.deps++;
instance.asyncDep
.catch(err => {
handleError(err, instance, 0 /* SETUP_FUNCTION */);
})
.then(asyncSetupResult => {
// component may be unmounted before resolve
if (!instance.isUnmounted && !parentSuspense.isUnmounted) {
retryAsyncComponent(instance, asyncSetupResult, parentSuspense, isSVG);
}
});
// give it a placeholder
const placeholder = (instance.subTree = createVNode(Comment));
processCommentNode(null, placeholder, container, anchor);
initialVNode.el = placeholder.el;
return;
}
setupRenderEffect(instance, parentSuspense, initialVNode, container, anchor, isSVG);
}
function retryAsyncComponent(instance, asyncSetupResult, parentSuspense, isSVG) {
parentSuspense.deps--;
// retry from this component
instance.asyncResolved = true;
const { vnode } = instance;
handleSetupResult(instance, asyncSetupResult, parentSuspense);
setupRenderEffect(instance, parentSuspense, vnode,
// component may have been moved before resolve
hostParentNode(instance.subTree.el), getNextHostNode(instance.subTree), isSVG);
updateHOCHostEl(instance, vnode.el);
if (parentSuspense.deps === 0) {
resolveSuspense(parentSuspense);
}
}
function setupRenderEffect(instance, parentSuspense, initialVNode, container, anchor, isSVG) {
// create reactive effect for rendering
let mounted = false;
instance.update = reactivity.effect(function componentEffect() {
if (!mounted) {
const subTree = (instance.subTree = renderComponentRoot(instance));
// beforeMount hook
if (instance.bm !== null) {
invokeHooks(instance.bm);
}
patch(null, subTree, container, anchor, instance, parentSuspense, isSVG);
initialVNode.el = subTree.el;
// mounted hook
if (instance.m !== null) {
queuePostRenderEffect(instance.m, parentSuspense);
}
mounted = true;
}
else {
// updateComponent
// This is triggered by mutation of component's own state (next: null)
// OR parent calling processComponent (next: HostVNode)
const { next } = instance;
if (next !== null) {
updateComponentPreRender(instance, next);
}
const prevTree = instance.subTree;
const nextTree = (instance.subTree = renderComponentRoot(instance));
// beforeUpdate hook
if (instance.bu !== null) {
invokeHooks(instance.bu);
}
// reset refs
// only needed if previous patch had refs
if (instance.refs !== EMPTY_OBJ) {
instance.refs = {};
}
patch(prevTree, nextTree,
// parent may have changed if it's in a portal
hostParentNode(prevTree.el),
// anchor may have changed if it's in a fragment
getNextHostNode(prevTree), instance, parentSuspense, isSVG);
instance.vnode.el = nextTree.el;
if (next === null) {
// self-triggered update. In case of HOC, update parent component
// vnode el. HOC is indicated by parent instance's subTree pointing
// to child component's vnode
updateHOCHostEl(instance, nextTree.el);
}
// updated hook
if (instance.u !== null) {
queuePostRenderEffect(instance.u, parentSuspense);
}
}
}, prodEffectOptions);
}
function updateComponentPreRender(instance, nextVNode) {
nextVNode.component = instance;
instance.vnode = nextVNode;
instance.next = null;
resolveProps(instance, nextVNode.props, nextVNode.type.props);
resolveSlots(instance, nextVNode.children);
}
function updateHOCHostEl({ vnode, parent }, el) {
while (parent && parent.subTree === vnode) {
(vnode = parent.vnode).el = el;
parent = parent.parent;
}
}
function patchChildren(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized = false) {
const c1 = n1 && n1.children;
const prevShapeFlag = n1 ? n1.shapeFlag : 0;
const c2 = n2.children;
const { patchFlag, shapeFlag } = n2;
if (patchFlag === -1 /* BAIL */) {
optimized = false;
}
// fast path
if (patchFlag > 0) {
if (patchFlag & 64 /* KEYED_FRAGMENT */) {
// this could be either fully-keyed or mixed (some keyed some not)
// presence of patchFlag means children are guaranteed to be arrays
patchKeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
return;
}
else if (patchFlag & 128 /* UNKEYED_FRAGMENT */) {
// unkeyed
patchUnkeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
return;
}
}
// children has 3 possibilities: text, array or no children.
if (shapeFlag & 8 /* TEXT_CHILDREN */) {
// text children fast path
if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
unmountChildren(c1, parentComponent, parentSuspense);
}
if (c2 !== c1) {
hostSetElementText(container, c2);
}
}
else {
if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
// prev children was array
if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
// two arrays, cannot assume anything, do full diff
patchKeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
else {
// no new children, just unmount old
unmountChildren(c1, parentComponent, parentSuspense, true);
}
}
else {
// prev children was text OR null
// new children is array OR null
if (prevShapeFlag & 8 /* TEXT_CHILDREN */) {
hostSetElementText(container, '');
}
// mount new if array
if (shapeFlag & 16 /* ARRAY_CHILDREN