UNPKG

vue-svg-loader

Version:
1,278 lines (1,265 loc) 204 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var reactivity = require('@vue/reactivity'); var shared = require('@vue/shared'); const stack = []; function warn(msg, ...args) { // avoid props formatting or warn handler tracking deps that might be mutated // during patch, leading to infinite recursion. reactivity.pauseTracking(); const instance = stack.length ? stack[stack.length - 1].component : null; const appWarnHandler = instance && instance.appContext.config.warnHandler; const trace = getComponentTrace(); if (appWarnHandler) { callWithErrorHandling(appWarnHandler, instance, 11 /* APP_WARN_HANDLER */, [ msg + args.join(''), instance && instance.proxy, trace .map(({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>`) .join('\n'), trace ]); } else { const warnArgs = [`[Vue warn]: ${msg}`, ...args]; /* istanbul ignore if */ if (trace.length && // avoid spamming console during tests !false) { warnArgs.push(`\n`, ...formatTrace(trace)); } console.warn(...warnArgs); } reactivity.resetTracking(); } function getComponentTrace() { let currentVNode = stack[stack.length - 1]; if (!currentVNode) { return []; } // we can't just use the stack because it will be incomplete during updates // that did not start from the root. Re-construct the parent chain using // instance parent pointers. const normalizedStack = []; while (currentVNode) { const last = normalizedStack[0]; if (last && last.vnode === currentVNode) { last.recurseCount++; } else { normalizedStack.push({ vnode: currentVNode, recurseCount: 0 }); } const parentInstance = currentVNode.component && currentVNode.component.parent; currentVNode = parentInstance && parentInstance.vnode; } return normalizedStack; } /* istanbul ignore next */ function formatTrace(trace) { const logs = []; trace.forEach((entry, i) => { logs.push(...(i === 0 ? [] : [`\n`]), ...formatTraceEntry(entry)); }); return logs; } function formatTraceEntry({ vnode, recurseCount }) { const postfix = recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``; const isRoot = vnode.component ? vnode.component.parent == null : false; const open = ` at <${formatComponentName(vnode.component, vnode.type, isRoot)}`; const close = `>` + postfix; return vnode.props ? [open, ...formatProps(vnode.props), close] : [open + close]; } /* istanbul ignore next */ function formatProps(props) { const res = []; const keys = Object.keys(props); keys.slice(0, 3).forEach(key => { res.push(...formatProp(key, props[key])); }); if (keys.length > 3) { res.push(` ...`); } return res; } /* istanbul ignore next */ function formatProp(key, value, raw) { if (shared.isString(value)) { value = JSON.stringify(value); return raw ? value : [`${key}=${value}`]; } else if (typeof value === 'number' || typeof value === 'boolean' || value == null) { return raw ? value : [`${key}=${value}`]; } else if (reactivity.isRef(value)) { value = formatProp(key, reactivity.toRaw(value.value), true); return raw ? value : [`${key}=Ref<`, value, `>`]; } else if (shared.isFunction(value)) { return [`${key}=fn${value.name ? `<${value.name}>` : ``}`]; } else { value = reactivity.toRaw(value); return raw ? value : [`${key}=`, value]; } } 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) { if (shared.isFunction(fn)) { const res = callWithErrorHandling(fn, instance, type, args); if (res && shared.isPromise(res)) { res.catch(err => { handleError(err, instance, type); }); } return res; } const values = []; for (let i = 0; i < fn.length; i++) { values.push(callWithAsyncErrorHandling(fn[i], instance, type, args)); } return values; } 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.proxy; // in production the hook receives only the error code const errorInfo = type; while (cur) { const errorCapturedHooks = cur.ec; if (errorCapturedHooks) { 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, 10 /* APP_ERROR_HANDLER */, [err, exposedInstance, errorInfo]); return; } } logError(err); } function logError(err, type, contextVNode) { { // recover in prod to reduce the impact on end-user console.error(err); } } let isFlushing = false; let isFlushPending = false; const queue = []; let flushIndex = 0; const pendingPreFlushCbs = []; let activePreFlushCbs = null; let preFlushIndex = 0; const pendingPostFlushCbs = []; let activePostFlushCbs = null; let postFlushIndex = 0; const resolvedPromise = Promise.resolve(); let currentFlushPromise = null; let currentPreFlushParentJob = null; const RECURSION_LIMIT = 100; function nextTick(fn) { const p = currentFlushPromise || resolvedPromise; return fn ? p.then(fn) : p; } function queueJob(job) { // the dedupe search uses the startIndex argument of Array.includes() // by default the search index includes the current job that is being run // so it cannot recursively trigger itself again. // if the job is a watch() callback, the search will start with a +1 index to // allow it recursively trigger itself - it is the user's responsibility to // ensure it doesn't end up in an infinite loop. if ((!queue.length || !queue.includes(job, isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex)) && job !== currentPreFlushParentJob) { queue.push(job); queueFlush(); } } function queueFlush() { if (!isFlushing && !isFlushPending) { isFlushPending = true; currentFlushPromise = resolvedPromise.then(flushJobs); } } function invalidateJob(job) { const i = queue.indexOf(job); if (i > -1) { queue[i] = null; } } function queueCb(cb, activeQueue, pendingQueue, index) { if (!shared.isArray(cb)) { if (!activeQueue || !activeQueue.includes(cb, cb.allowRecurse ? index + 1 : index)) { pendingQueue.push(cb); } } else { // if cb is an array, it is a component lifecycle hook which can only be // triggered by a job, which is already deduped in the main queue, so // we can skip duplicate check here to improve perf pendingQueue.push(...cb); } queueFlush(); } function queuePreFlushCb(cb) { queueCb(cb, activePreFlushCbs, pendingPreFlushCbs, preFlushIndex); } function queuePostFlushCb(cb) { queueCb(cb, activePostFlushCbs, pendingPostFlushCbs, postFlushIndex); } function flushPreFlushCbs(seen, parentJob = null) { if (pendingPreFlushCbs.length) { currentPreFlushParentJob = parentJob; activePreFlushCbs = [...new Set(pendingPreFlushCbs)]; pendingPreFlushCbs.length = 0; for (preFlushIndex = 0; preFlushIndex < activePreFlushCbs.length; preFlushIndex++) { activePreFlushCbs[preFlushIndex](); } activePreFlushCbs = null; preFlushIndex = 0; currentPreFlushParentJob = null; // recursively flush until it drains flushPreFlushCbs(seen, parentJob); } } function flushPostFlushCbs(seen) { if (pendingPostFlushCbs.length) { activePostFlushCbs = [...new Set(pendingPostFlushCbs)]; pendingPostFlushCbs.length = 0; activePostFlushCbs.sort((a, b) => getId(a) - getId(b)); for (postFlushIndex = 0; postFlushIndex < activePostFlushCbs.length; postFlushIndex++) { activePostFlushCbs[postFlushIndex](); } activePostFlushCbs = null; postFlushIndex = 0; } } const getId = (job) => job.id == null ? Infinity : job.id; function flushJobs(seen) { isFlushPending = false; isFlushing = true; flushPreFlushCbs(seen); // Sort queue before flush. // This ensures that: // 1. Components are updated from parent to child. (because parent is always // created before the child so its render effect will have smaller // priority number) // 2. If a component is unmounted during a parent component's update, // its update can be skipped. // Jobs can never be null before flush starts, since they are only invalidated // during execution of another flushed job. queue.sort((a, b) => getId(a) - getId(b)); try { for (flushIndex = 0; flushIndex < queue.length; flushIndex++) { const job = queue[flushIndex]; if (job) { if (false) { checkRecursiveUpdates(seen, job); } callWithErrorHandling(job, null, 14 /* SCHEDULER */); } } } finally { flushIndex = 0; queue.length = 0; flushPostFlushCbs(); isFlushing = false; currentFlushPromise = null; // some postFlushCb queued jobs! // keep flushing until it drains. if (queue.length || pendingPostFlushCbs.length) { flushJobs(seen); } } } function checkRecursiveUpdates(seen, fn) { if (!seen.has(fn)) { seen.set(fn, 1); } else { const count = seen.get(fn); if (count > RECURSION_LIMIT) { throw new Error(`Maximum recursive updates exceeded. ` + `This means you have a reactive effect that is mutating its own ` + `dependencies and thus recursively triggering itself. Possible sources ` + `include component template, render function, updated hook or ` + `watcher source function.`); } else { seen.set(fn, count + 1); } } } // mark the current rendering instance for asset resolution (e.g. // resolveComponent, resolveDirective) during render let currentRenderingInstance = null; function setCurrentRenderingInstance(instance) { currentRenderingInstance = instance; } // dev only flag to track whether $attrs was used during render. // If $attrs was used during render then the warning for failed attrs // fallthrough can be suppressed. let accessedAttrs = false; function markAttrsAccessed() { accessedAttrs = true; } function renderComponentRoot(instance) { const { type: Component, parent, vnode, proxy, withProxy, props, slots, attrs, emit, render, renderCache, data, setupState, ctx } = instance; let result; currentRenderingInstance = instance; try { let fallthroughAttrs; if (vnode.shapeFlag & 4 /* STATEFUL_COMPONENT */) { // withProxy is a proxy with a different `has` trap only for // runtime-compiled render functions using `with` block. const proxyToUse = withProxy || proxy; result = normalizeVNode(render.call(proxyToUse, proxyToUse, renderCache, props, setupState, data, ctx)); fallthroughAttrs = attrs; } else { // functional const render = Component; // in dev, mark attrs accessed if optional props (attrs === props) if (false && attrs === props) { markAttrsAccessed(); } result = normalizeVNode(render.length > 1 ? render(props, false ? { get attrs() { markAttrsAccessed(); return attrs; }, slots, emit } : { attrs, slots, emit }) : render(props, null /* we know it doesn't need it */)); fallthroughAttrs = Component.props ? attrs : getFunctionalFallthrough(attrs); } // attr merging // in dev mode, comments are preserved, and it's possible for a template // to have comments along side the root element which makes it a fragment let root = result; let setRoot = undefined; if (false) { ; [root, setRoot] = getChildRoot(result); } if (Component.inheritAttrs !== false && fallthroughAttrs) { const keys = Object.keys(fallthroughAttrs); const { shapeFlag } = root; if (keys.length) { if (shapeFlag & 1 /* ELEMENT */ || shapeFlag & 6 /* COMPONENT */) { if (shapeFlag & 1 /* ELEMENT */ && keys.some(shared.isModelListener)) { // #1643, #1543 // component v-model listeners should only fallthrough for component // HOCs fallthroughAttrs = filterModelListeners(fallthroughAttrs); } root = cloneVNode(root, fallthroughAttrs); } else if (false && !accessedAttrs && root.type !== Comment) { const allAttrs = Object.keys(attrs); const eventAttrs = []; const extraAttrs = []; for (let i = 0, l = allAttrs.length; i < l; i++) { const key = allAttrs[i]; if (shared.isOn(key)) { // ignore v-model handlers when they fail to fallthrough if (!shared.isModelListener(key)) { // remove `on`, lowercase first letter to reflect event casing // accurately eventAttrs.push(key[2].toLowerCase() + key.slice(3)); } } else { extraAttrs.push(key); } } if (extraAttrs.length) { warn(`Extraneous non-props attributes (` + `${extraAttrs.join(', ')}) ` + `were passed to component but could not be automatically inherited ` + `because component renders fragment or text root nodes.`); } if (eventAttrs.length) { warn(`Extraneous non-emits event listeners (` + `${eventAttrs.join(', ')}) ` + `were passed to component but could not be automatically inherited ` + `because component renders fragment or text root nodes. ` + `If the listener is intended to be a component custom event listener only, ` + `declare it using the "emits" option.`); } } } } // inherit scopeId const scopeId = vnode.scopeId; // vite#536: if subtree root is created from parent slot if would already // have the correct scopeId, in this case adding the scopeId will cause // it to be removed if the original slot vnode is reused. const needScopeId = scopeId && root.scopeId !== scopeId; const treeOwnerId = parent && parent.type.__scopeId; const slotScopeId = treeOwnerId && treeOwnerId !== scopeId ? treeOwnerId + '-s' : null; if (needScopeId || slotScopeId) { const extras = {}; if (needScopeId) extras[scopeId] = ''; if (slotScopeId) extras[slotScopeId] = ''; root = cloneVNode(root, extras); } // inherit directives if (vnode.dirs) { if (false && !isElementRoot(root)) { warn(`Runtime directive used on component with non-element root node. ` + `The directives will not function as intended.`); } root.dirs = vnode.dirs; } // inherit transition data if (vnode.transition) { if (false && !isElementRoot(root)) { warn(`Component inside <Transition> renders non-element root node ` + `that cannot be animated.`); } root.transition = vnode.transition; } if (false && setRoot) { setRoot(root); } else { result = root; } } catch (err) { handleError(err, instance, 1 /* RENDER_FUNCTION */); result = createVNode(Comment); } currentRenderingInstance = null; return result; } /** * dev only */ const getChildRoot = (vnode) => { if (vnode.type !== Fragment) { return [vnode, undefined]; } const rawChildren = vnode.children; const dynamicChildren = vnode.dynamicChildren; const children = rawChildren.filter(child => { return !(isVNode(child) && child.type === Comment && child.children !== 'v-if'); }); if (children.length !== 1) { return [vnode, undefined]; } const childRoot = children[0]; const index = rawChildren.indexOf(childRoot); const dynamicIndex = dynamicChildren ? dynamicChildren.indexOf(childRoot) : -1; const setRoot = (updatedRoot) => { rawChildren[index] = updatedRoot; if (dynamicIndex > -1) { dynamicChildren[dynamicIndex] = updatedRoot; } else if (dynamicChildren && updatedRoot.patchFlag > 0) { dynamicChildren.push(updatedRoot); } }; return [normalizeVNode(childRoot), setRoot]; }; const getFunctionalFallthrough = (attrs) => { let res; for (const key in attrs) { if (key === 'class' || key === 'style' || shared.isOn(key)) { (res || (res = {}))[key] = attrs[key]; } } return res; }; const filterModelListeners = (attrs) => { const res = {}; for (const key in attrs) { if (!shared.isModelListener(key)) { res[key] = attrs[key]; } } return res; }; const isElementRoot = (vnode) => { return (vnode.shapeFlag & 6 /* COMPONENT */ || vnode.shapeFlag & 1 /* ELEMENT */ || vnode.type === Comment // potential v-if branch switch ); }; function shouldUpdateComponent(prevVNode, nextVNode, optimized) { const { props: prevProps, children: prevChildren } = prevVNode; const { props: nextProps, children: nextChildren, patchFlag } = nextVNode; // force child update for runtime directive or transition on component vnode. if (nextVNode.dirs || nextVNode.transition) { return true; } if (optimized && patchFlag > 0) { if (patchFlag & 1024 /* DYNAMIC_SLOTS */) { // slot content that references values that might have changed, // e.g. in a v-for return true; } if (patchFlag & 16 /* FULL_PROPS */) { if (!prevProps) { return !!nextProps; } // 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 { // this path is only taken by manually written render functions // so presence of any children leads to a forced update if (prevChildren || nextChildren) { if (!nextChildren || !nextChildren.$stable) { return true; } } if (prevProps === nextProps) { return false; } if (!prevProps) { return !!nextProps; } if (!nextProps) { return true; } 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; } function updateHOCHostEl({ vnode, parent }, el // HostNode ) { while (parent && parent.subTree === vnode) { (vnode = parent.vnode).el = el; parent = parent.parent; } } const isSuspense = (type) => type.__isSuspense; // Suspense exposes a component-like API, and is treated like a component // in the compiler, but internally it's a special built-in type that hooks // directly into the renderer. const SuspenseImpl = { // In order to make Suspense tree-shakable, we need to avoid importing it // directly in the renderer. The renderer checks for the __isSuspense flag // on a vnode's type and calls the `process` method, passing in renderer // internals. __isSuspense: true, process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, // platform-specific impl passed from renderer rendererInternals) { if (n1 == null) { mountSuspense(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, rendererInternals); } else { patchSuspense(n1, n2, container, anchor, parentComponent, isSVG, optimized, rendererInternals); } }, hydrate: hydrateSuspense }; // Force-casted public typing for h and TSX props inference const Suspense = ( SuspenseImpl ); function mountSuspense(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, rendererInternals) { const { p: patch, o: { createElement } } = rendererInternals; const hiddenContainer = createElement('div'); const suspense = (n2.suspense = createSuspenseBoundary(n2, parentSuspense, parentComponent, container, hiddenContainer, anchor, isSVG, optimized, rendererInternals)); // start mounting the content subtree in an off-dom container patch(null, suspense.subTree, 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, suspense.fallbackTree, container, anchor, parentComponent, null, // fallback tree will not have suspense context isSVG, optimized); n2.el = suspense.fallbackTree.el; } else { // Suspense has no async deps. Just resolve. suspense.resolve(); } } function patchSuspense(n1, n2, container, anchor, parentComponent, isSVG, optimized, { p: patch }) { 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 createSuspenseBoundary(vnode, parent, parentComponent, container, hiddenContainer, anchor, isSVG, optimized, rendererInternals, isHydrating = false) { const { p: patch, m: move, um: unmount, n: next, o: { parentNode } } = rendererInternals; const getCurrentTree = () => suspense.isResolved || suspense.isHydrating ? suspense.subTree : suspense.fallbackTree; const { content, fallback } = normalizeSuspenseChildren(vnode); const suspense = { vnode, parent, parentComponent, isSVG, optimized, container, hiddenContainer, anchor, deps: 0, subTree: content, fallbackTree: fallback, isHydrating, isResolved: false, isUnmounted: false, effects: [], resolve() { const { vnode, subTree, fallbackTree, effects, parentComponent, container } = suspense; if (suspense.isHydrating) { suspense.isHydrating = false; } else { // 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 = next(fallbackTree); unmount(fallbackTree, parentComponent, suspense, true); } // move content from off-dom container to actual container move(subTree, container, anchor, 0 /* ENTER */); } 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; suspense.effects = []; // invoke @resolve event const onResolve = vnode.props && vnode.props.onResolve; if (shared.isFunction(onResolve)) { onResolve(); } }, recede() { suspense.isResolved = false; const { vnode, subTree, fallbackTree, parentComponent, container, hiddenContainer, isSVG, optimized } = suspense; // move content tree back to the off-dom container const anchor = next(subTree); move(subTree, hiddenContainer, null, 1 /* LEAVE */); // 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 @recede event const onRecede = vnode.props && vnode.props.onRecede; if (shared.isFunction(onRecede)) { onRecede(); } }, move(container, anchor, type) { move(getCurrentTree(), container, anchor, type); suspense.container = container; }, next() { return next(getCurrentTree()); }, registerDep(instance, setupRenderEffect) { // suspense is already resolved, need to recede. // use queueJob so it's handled synchronously after patching the current // suspense tree if (suspense.isResolved) { queueJob(() => { suspense.recede(); }); } const hydratedEl = instance.vnode.el; suspense.deps++; instance .asyncDep.catch(err => { handleError(err, instance, 0 /* SETUP_FUNCTION */); }) .then(asyncSetupResult => { // retry when the setup() promise resolves. // component may have been unmounted before resolve. if (instance.isUnmounted || suspense.isUnmounted) { return; } suspense.deps--; // retry from this component instance.asyncResolved = true; const { vnode } = instance; handleSetupResult(instance, asyncSetupResult, false); if (hydratedEl) { // vnode may have been replaced if an update happened before the // async dep is resolved. vnode.el = hydratedEl; } setupRenderEffect(instance, vnode, // component may have been moved before resolve. // if this is not a hydration, instance.subTree will be the comment // placeholder. hydratedEl ? parentNode(hydratedEl) : parentNode(instance.subTree.el), // anchor will not be used if this is hydration, so only need to // consider the comment placeholder case. hydratedEl ? null : next(instance.subTree), suspense, isSVG, optimized); updateHOCHostEl(instance, vnode.el); if (suspense.deps === 0) { suspense.resolve(); } }); }, unmount(parentSuspense, doRemove) { suspense.isUnmounted = true; unmount(suspense.subTree, parentComponent, parentSuspense, doRemove); if (!suspense.isResolved) { unmount(suspense.fallbackTree, parentComponent, parentSuspense, doRemove); } } }; return suspense; } function hydrateSuspense(node, vnode, parentComponent, parentSuspense, isSVG, optimized, rendererInternals, hydrateNode) { /* eslint-disable no-restricted-globals */ const suspense = (vnode.suspense = createSuspenseBoundary(vnode, parentSuspense, parentComponent, node.parentNode, document.createElement('div'), null, isSVG, optimized, rendererInternals, true /* hydrating */)); // there are two possible scenarios for server-rendered suspense: // - success: ssr content should be fully resolved // - failure: ssr content should be the fallback branch. // however, on the client we don't really know if it has failed or not // attempt to hydrate the DOM assuming it has succeeded, but we still // need to construct a suspense boundary first const result = hydrateNode(node, suspense.subTree, parentComponent, suspense, optimized); if (suspense.deps === 0) { suspense.resolve(); } return result; /* eslint-enable no-restricted-globals */ } function normalizeSuspenseChildren(vnode) { const { shapeFlag, children } = vnode; if (shapeFlag & 32 /* SLOTS_CHILDREN */) { const { default: d, fallback } = children; return { content: normalizeVNode(shared.isFunction(d) ? d() : d), fallback: normalizeVNode(shared.isFunction(fallback) ? fallback() : fallback) }; } else { return { content: normalizeVNode(children), fallback: normalizeVNode(null) }; } } function queueEffectWithSuspense(fn, suspense) { if (suspense && !suspense.isResolved) { if (shared.isArray(fn)) { suspense.effects.push(...fn); } else { suspense.effects.push(fn); } } else { queuePostFlushCb(fn); } } let isRenderingCompiledSlot = 0; /** * Compiler runtime helper for rendering `<slot/>` * @private */ function renderSlot(slots, name, props = {}, // this is not a user-facing function, so the fallback is always generated by // the compiler and guaranteed to be a function returning an array fallback) { let slot = slots[name]; // a compiled slot disables block tracking by default to avoid manual // invocation interfering with template-based block tracking, but in // `renderSlot` we can be sure that it's template-based so we can force // enable it. isRenderingCompiledSlot++; const rendered = (openBlock(), createBlock(Fragment, { key: props.key }, slot ? slot(props) : fallback ? fallback() : [], slots._ === 1 /* STABLE */ ? 64 /* STABLE_FRAGMENT */ : -2 /* BAIL */)); isRenderingCompiledSlot--; return rendered; } /** * Wrap a slot function to memoize current rendering instance * @private */ function withCtx(fn, ctx = currentRenderingInstance) { if (!ctx) return fn; return function renderFnWithContext() { // If a user calls a compiled slot inside a template expression (#1745), it // can mess up block tracking, so by default we need to push a null block to // avoid that. This isn't necessary if rendering a compiled `<slot>`. if (!isRenderingCompiledSlot) { openBlock(true /* null block that disables tracking */); } const owner = currentRenderingInstance; setCurrentRenderingInstance(ctx); const res = fn.apply(null, arguments); setCurrentRenderingInstance(owner); if (!isRenderingCompiledSlot) { closeBlock(); } return res; }; } // SFC scoped style ID management. let currentScopeId = null; const scopeIdStack = []; /** * @private */ function pushScopeId(id) { scopeIdStack.push((currentScopeId = id)); } /** * @private */ function popScopeId() { scopeIdStack.pop(); currentScopeId = scopeIdStack[scopeIdStack.length - 1] || null; } /** * @private */ function withScopeId(id) { return ((fn) => withCtx(function () { pushScopeId(id); const res = fn.apply(this, arguments); popScopeId(); return res; })); } const isTeleport = (type) => type.__isTeleport; const isTeleportDisabled = (props) => props && (props.disabled || props.disabled === ''); const resolveTarget = (props, select) => { const targetSelector = props && props.to; if (shared.isString(targetSelector)) { if (!select) { return null; } else { const target = select(targetSelector); return target; } } else { return targetSelector; } }; const TeleportImpl = { __isTeleport: true, process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, internals) { const { mc: mountChildren, pc: patchChildren, pbc: patchBlockChildren, o: { insert, querySelector, createText, createComment } } = internals; const disabled = isTeleportDisabled(n2.props); const { shapeFlag, children } = n2; if (n1 == null) { // insert anchors in the main view const placeholder = (n2.el = createText('')); const mainAnchor = (n2.anchor = createText('')); insert(placeholder, container, anchor); insert(mainAnchor, container, anchor); const target = (n2.target = resolveTarget(n2.props, querySelector)); const targetAnchor = (n2.targetAnchor = createText('')); if (target) { insert(targetAnchor, target); } const mount = (container, anchor) => { // Teleport *always* has Array children. This is enforced in both the // compiler and vnode children normalization. if (shapeFlag & 16 /* ARRAY_CHILDREN */) { mountChildren(children, container, anchor, parentComponent, parentSuspense, isSVG, optimized); } }; if (disabled) { mount(container, mainAnchor); } else if (target) { mount(target, targetAnchor); } } else { // update content n2.el = n1.el; const mainAnchor = (n2.anchor = n1.anchor); const target = (n2.target = n1.target); const targetAnchor = (n2.targetAnchor = n1.targetAnchor); const wasDisabled = isTeleportDisabled(n1.props); const currentContainer = wasDisabled ? container : target; const currentAnchor = wasDisabled ? mainAnchor : targetAnchor; if (n2.dynamicChildren) { // fast path when the teleport happens to be a block root patchBlockChildren(n1.dynamicChildren, n2.dynamicChildren, currentContainer, parentComponent, parentSuspense, isSVG); // even in block tree mode we need to make sure all root-level nodes // in the teleport inherit previous DOM references so that they can // be moved in future patches. if (n2.shapeFlag & 16 /* ARRAY_CHILDREN */) { const oldChildren = n1.children; const children = n2.children; for (let i = 0; i < children.length; i++) { // only inherit for non-patched nodes (i.e. static ones) if (!children[i].el) { children[i].el = oldChildren[i].el; } } } } else if (!optimized) { patchChildren(n1, n2, currentContainer, currentAnchor, parentComponent, parentSuspense, isSVG); } if (disabled) { if (!wasDisabled) { // enabled -> disabled // move into main container moveTeleport(n2, container, mainAnchor, internals, 1 /* TOGGLE */); } } else { // target changed if ((n2.props && n2.props.to) !== (n1.props && n1.props.to)) { const nextTarget = (n2.target = resolveTarget(n2.props, querySelector)); if (nextTarget) { moveTeleport(n2, nextTarget, null, internals, 0 /* TARGET_CHANGE */); } } else if (wasDisabled) { // disabled -> enabled // move into teleport target moveTeleport(n2, target, targetAnchor, internals, 1 /* TOGGLE */); } } } }, remove(vnode, { r: remove, o: { remove: hostRemove } }) { const { shapeFlag, children, anchor } = vnode; hostRemove(anchor); if (shapeFlag & 16 /* ARRAY_CHILDREN */) { for (let i = 0; i < children.length; i++) { remove(children[i]); } } }, move: moveTeleport, hydrate: hydrateTeleport }; function moveTeleport(vnode, container, parentAnchor, { o: { insert }, m: move }, moveType = 2 /* REORDER */) { // move target anchor if this is a target change. if (moveType === 0 /* TARGET_CHANGE */) { insert(vnode.targetAnchor, container, parentAnchor); } const { el, anchor, shapeFlag, children, props } = vnode; const isReorder = moveType === 2 /* REORDER */; // move main view anchor if this is a re-order. if (isReorder) { insert(el, container, parentAnchor); } // if this is a re-order and teleport is enabled (content is in target) // do not move children. So the opposite is: only move children if this // is not a reorder, or the teleport is disabled if (!isReorder || isTeleportDisabled(props)) { // Teleport has either Array children or no children. if (shapeFlag & 16 /* ARRAY_CHILDREN */) { for (let i = 0; i < children.length; i++) { move(children[i], container, parentAnchor, 2 /* REORDER */); } } } // move main view anchor if this is a re-order. if (isReorder) { insert(anchor, container, parentAnchor); } } function hydrateTeleport(node, vnode, parentComponent, parentSuspense, optimized, { o: { nextSibling, parentNode, querySelector } }, hydrateChildren) { const target = (vnode.target = resolveTarget(vnode.props, querySelector)); if (target) { // if multiple teleports rendered to the same target element, we need to // pick up from where the last teleport finished instead of the first node const targetNode = target._lpa || target.firstChild; if (vnode.shapeFlag & 16 /* ARRAY_CHILDREN */) { if (isTeleportDisabled(vnode.props)) { vnode.anchor = hydrateChildren(nextSibling(node), vnode, parentNode(node), parentComponent, parentSuspense, optimized); vnode.targetAnchor = targetNode; } else { vnode.anchor = nextSibling(node); vnode.targetAnchor = hydrateChildren(targetNode, vnode, target, parentComponent, parentSuspense, optimized); } target._lpa = vnode.targetAnchor && nextSibling(vnode.targetAnchor); } } return vnode.anchor && nextSibling(vnode.anchor); } // Force-casted public typing for h and TSX props inference const Teleport = TeleportImpl; const COMPONENTS = 'components'; const DIRECTIVES = 'directives'; /** * @private */ function resolveComponent(name) { return resolveAsset(COMPONENTS, name) || name; } const NULL_DYNAMIC_COMPONENT = Symbol(); /** * @private */ function resolveDynamicComponent(component) { if (shared.isString(component)) { return resolveAsset(COMPONENTS, component, false) || component; } else { // invalid types will fallthrough to createVNode and raise warning return (component || NULL_DYNAMIC_COMPONENT); } } /** * @private */ function resolveDirective(name) { return resolveAsset(DIRECTIVES, name); } // implementation function resolveAsset(type, name, warnMissing = true) { const instance = currentRenderingInstance || currentInstance; if (instance) { const Component = instance.type; // self name has highest priority if (type === COMPONENTS) { const selfName = Component.displayName || Component.name; if (selfName && (selfName === name || selfName === shared.camelize(name) || selfName === shared.capitalize(shared.camelize(name)))) { return Component; } } const res = // local registration resolve(Component[type], name) || // global registration resolve(instance.appContext[type], name); return res; } } function resolve(registry, name) { return (registry && (registry[name] || registry[shared.camelize(name)] || registry[shared.capitalize(shared.camelize(name))])); } const Fragment = Symbol( undefined); const Text = Symbol( undefined); const Comment = Symbol( undefined); const Static = Symbol( undefined); // 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 = []; let currentBlock = null; /** * 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: * * ```js * function render() { * return (openBlock(),createBlock('div', null, [...])) * } * ``` * disableTracking is true when creating a v-for fragment block, since a v-for * fragment always diffs its children. * * @private */ function openBlock(disableTracking = false) { blockStack.push((currentBlock = disableTracking ? null : [])); } function closeBlock() { blockStack.pop(); currentBlock = blockStack[blockStack.length - 1] || null; } // Whether we should be tracking dynamic child nodes inside a block. // Only tracks when this value is > 0 // We are not using a simple boolean because this value may need to be // incremented/decremented by nested usage of v-once (see below) let shouldTrack = 1; /** * Block tracking sometimes needs to be disabled, for example during the * creation of a tree that needs to be cached by v-once. The compiler generates * code like this: * * ``` js * _cache[1] || ( * setBlockTracking(-1), * _cache[1] = createVNode(...), * setBlockTracking(1), * _cache[1] * ) * ``` * * @private */ function setBlockTracking(value) { shouldTrack += value; } /** * 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. * * @private */ function createBlock(type, props, children, patchFlag, dynamicProps) { const vnode = createVNode(type, props, children, patchFlag, dynamicProps, true /* isBlock: prevent a block from tracking itself */); // save current block children on the block vnode vnode.dynamicChildren = currentBlock || shared.EMPTY_ARR; // close block closeBlock(); // a block is always going to be patched, so track it as a child of its // parent block if (currentBlock) { currentBlock.push(vnode); } return vnode; } function isVNode(value) { return value ? value.__v_isVNode === true : false; } function isSameVNodeType(n1, n2) { return n1.type === n2.type && n1.key === n2.key; } /** * Internal API for registering an arguments transform for createVNode * used for creating stubs in the test-utils * It is *internal* but needs to be exposed for test-utils to pick up proper * typings */ function transformVNodeArgs(transformer) { } const InternalObjectKey = `__vInternal`; const normalizeKey = ({ key }) => key != null ? key : null; const normalizeRef = ({ ref }) => { return (ref != null ? shared.isArray(ref) ? ref : [currentRenderingInstance, ref] : null); }; const createVNode = ( _createVNode); function _createVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, isBlockNode = false) { if (!type || type === NULL_DYNAMIC_COMPONENT) { type = Comment