File: D:/HostingSpaces/SBogers10/shop.komma.nl/node_modules/@vue/compiler-core/dist/compiler-core.cjs.js
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var shared = require('@vue/shared');
var sourceMap = require('source-map');
var parser = require('@babel/parser');
var estreeWalker = require('estree-walker');
function defaultOnError(error) {
throw error;
}
function createCompilerError(code, loc, messages, additionalMessage) {
const msg = (messages || errorMessages)[code] + (additionalMessage || ``)
;
const error = new SyntaxError(String(msg));
error.code = code;
error.loc = loc;
return error;
}
const errorMessages = {
// parse errors
[0 /* ABRUPT_CLOSING_OF_EMPTY_COMMENT */]: 'Illegal comment.',
[1 /* CDATA_IN_HTML_CONTENT */]: 'CDATA section is allowed only in XML context.',
[2 /* DUPLICATE_ATTRIBUTE */]: 'Duplicate attribute.',
[3 /* END_TAG_WITH_ATTRIBUTES */]: 'End tag cannot have attributes.',
[4 /* END_TAG_WITH_TRAILING_SOLIDUS */]: "Illegal '/' in tags.",
[5 /* EOF_BEFORE_TAG_NAME */]: 'Unexpected EOF in tag.',
[6 /* EOF_IN_CDATA */]: 'Unexpected EOF in CDATA section.',
[7 /* EOF_IN_COMMENT */]: 'Unexpected EOF in comment.',
[8 /* EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT */]: 'Unexpected EOF in script.',
[9 /* EOF_IN_TAG */]: 'Unexpected EOF in tag.',
[10 /* INCORRECTLY_CLOSED_COMMENT */]: 'Incorrectly closed comment.',
[11 /* INCORRECTLY_OPENED_COMMENT */]: 'Incorrectly opened comment.',
[12 /* INVALID_FIRST_CHARACTER_OF_TAG_NAME */]: "Illegal tag name. Use '<' to print '<'.",
[13 /* MISSING_ATTRIBUTE_VALUE */]: 'Attribute value was expected.',
[14 /* MISSING_END_TAG_NAME */]: 'End tag name was expected.',
[15 /* MISSING_WHITESPACE_BETWEEN_ATTRIBUTES */]: 'Whitespace was expected.',
[16 /* NESTED_COMMENT */]: "Unexpected '<!--' in comment.",
[17 /* UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME */]: 'Attribute name cannot contain U+0022 ("), U+0027 (\'), and U+003C (<).',
[18 /* UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE */]: 'Unquoted attribute value cannot contain U+0022 ("), U+0027 (\'), U+003C (<), U+003D (=), and U+0060 (`).',
[19 /* UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME */]: "Attribute name cannot start with '='.",
[21 /* UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME */]: "'<?' is allowed only in XML context.",
[22 /* UNEXPECTED_SOLIDUS_IN_TAG */]: "Illegal '/' in tags.",
// Vue-specific parse errors
[23 /* X_INVALID_END_TAG */]: 'Invalid end tag.',
[24 /* X_MISSING_END_TAG */]: 'Element is missing end tag.',
[25 /* X_MISSING_INTERPOLATION_END */]: 'Interpolation end sign was not found.',
[26 /* X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END */]: 'End bracket for dynamic directive argument was not found. ' +
'Note that dynamic directive argument cannot contain spaces.',
// transform errors
[27 /* X_V_IF_NO_EXPRESSION */]: `v-if/v-else-if is missing expression.`,
[28 /* X_V_IF_SAME_KEY */]: `v-if/else branches must use unique keys.`,
[29 /* X_V_ELSE_NO_ADJACENT_IF */]: `v-else/v-else-if has no adjacent v-if.`,
[30 /* X_V_FOR_NO_EXPRESSION */]: `v-for is missing expression.`,
[31 /* X_V_FOR_MALFORMED_EXPRESSION */]: `v-for has invalid expression.`,
[32 /* X_V_FOR_TEMPLATE_KEY_PLACEMENT */]: `<template v-for> key should be placed on the <template> tag.`,
[33 /* X_V_BIND_NO_EXPRESSION */]: `v-bind is missing expression.`,
[34 /* X_V_ON_NO_EXPRESSION */]: `v-on is missing expression.`,
[35 /* X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET */]: `Unexpected custom directive on <slot> outlet.`,
[36 /* X_V_SLOT_MIXED_SLOT_USAGE */]: `Mixed v-slot usage on both the component and nested <template>.` +
`When there are multiple named slots, all slots should use <template> ` +
`syntax to avoid scope ambiguity.`,
[37 /* X_V_SLOT_DUPLICATE_SLOT_NAMES */]: `Duplicate slot names found. `,
[38 /* X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN */]: `Extraneous children found when component already has explicitly named ` +
`default slot. These children will be ignored.`,
[39 /* X_V_SLOT_MISPLACED */]: `v-slot can only be used on components or <template> tags.`,
[40 /* X_V_MODEL_NO_EXPRESSION */]: `v-model is missing expression.`,
[41 /* X_V_MODEL_MALFORMED_EXPRESSION */]: `v-model value must be a valid JavaScript member expression.`,
[42 /* X_V_MODEL_ON_SCOPE_VARIABLE */]: `v-model cannot be used on v-for or v-slot scope variables because they are not writable.`,
[43 /* X_INVALID_EXPRESSION */]: `Error parsing JavaScript expression: `,
[44 /* X_KEEP_ALIVE_INVALID_CHILDREN */]: `<KeepAlive> expects exactly one child component.`,
// generic errors
[45 /* X_PREFIX_ID_NOT_SUPPORTED */]: `"prefixIdentifiers" option is not supported in this build of compiler.`,
[46 /* X_MODULE_MODE_NOT_SUPPORTED */]: `ES module mode is not supported in this build of compiler.`,
[47 /* X_CACHE_HANDLER_NOT_SUPPORTED */]: `"cacheHandlers" option is only supported when the "prefixIdentifiers" option is enabled.`,
[48 /* X_SCOPE_ID_NOT_SUPPORTED */]: `"scopeId" option is only supported in module mode.`
};
const FRAGMENT = Symbol( `Fragment` );
const TELEPORT = Symbol( `Teleport` );
const SUSPENSE = Symbol( `Suspense` );
const KEEP_ALIVE = Symbol( `KeepAlive` );
const BASE_TRANSITION = Symbol( `BaseTransition` );
const OPEN_BLOCK = Symbol( `openBlock` );
const CREATE_BLOCK = Symbol( `createBlock` );
const CREATE_VNODE = Symbol( `createVNode` );
const CREATE_COMMENT = Symbol( `createCommentVNode` );
const CREATE_TEXT = Symbol( `createTextVNode` );
const CREATE_STATIC = Symbol( `createStaticVNode` );
const RESOLVE_COMPONENT = Symbol( `resolveComponent` );
const RESOLVE_DYNAMIC_COMPONENT = Symbol( `resolveDynamicComponent` );
const RESOLVE_DIRECTIVE = Symbol( `resolveDirective` );
const WITH_DIRECTIVES = Symbol( `withDirectives` );
const RENDER_LIST = Symbol( `renderList` );
const RENDER_SLOT = Symbol( `renderSlot` );
const CREATE_SLOTS = Symbol( `createSlots` );
const TO_DISPLAY_STRING = Symbol( `toDisplayString` );
const MERGE_PROPS = Symbol( `mergeProps` );
const TO_HANDLERS = Symbol( `toHandlers` );
const CAMELIZE = Symbol( `camelize` );
const CAPITALIZE = Symbol( `capitalize` );
const SET_BLOCK_TRACKING = Symbol( `setBlockTracking` );
const PUSH_SCOPE_ID = Symbol( `pushScopeId` );
const POP_SCOPE_ID = Symbol( `popScopeId` );
const WITH_SCOPE_ID = Symbol( `withScopeId` );
const WITH_CTX = Symbol( `withCtx` );
// Name mapping for runtime helpers that need to be imported from 'vue' in
// generated code. Make sure these are correctly exported in the runtime!
// Using `any` here because TS doesn't allow symbols as index type.
const helperNameMap = {
[FRAGMENT]: `Fragment`,
[TELEPORT]: `Teleport`,
[SUSPENSE]: `Suspense`,
[KEEP_ALIVE]: `KeepAlive`,
[BASE_TRANSITION]: `BaseTransition`,
[OPEN_BLOCK]: `openBlock`,
[CREATE_BLOCK]: `createBlock`,
[CREATE_VNODE]: `createVNode`,
[CREATE_COMMENT]: `createCommentVNode`,
[CREATE_TEXT]: `createTextVNode`,
[CREATE_STATIC]: `createStaticVNode`,
[RESOLVE_COMPONENT]: `resolveComponent`,
[RESOLVE_DYNAMIC_COMPONENT]: `resolveDynamicComponent`,
[RESOLVE_DIRECTIVE]: `resolveDirective`,
[WITH_DIRECTIVES]: `withDirectives`,
[RENDER_LIST]: `renderList`,
[RENDER_SLOT]: `renderSlot`,
[CREATE_SLOTS]: `createSlots`,
[TO_DISPLAY_STRING]: `toDisplayString`,
[MERGE_PROPS]: `mergeProps`,
[TO_HANDLERS]: `toHandlers`,
[CAMELIZE]: `camelize`,
[CAPITALIZE]: `capitalize`,
[SET_BLOCK_TRACKING]: `setBlockTracking`,
[PUSH_SCOPE_ID]: `pushScopeId`,
[POP_SCOPE_ID]: `popScopeId`,
[WITH_SCOPE_ID]: `withScopeId`,
[WITH_CTX]: `withCtx`
};
function registerRuntimeHelpers(helpers) {
Object.getOwnPropertySymbols(helpers).forEach(s => {
helperNameMap[s] = helpers[s];
});
}
// AST Utilities ---------------------------------------------------------------
// Some expressions, e.g. sequence and conditional expressions, are never
// associated with template nodes, so their source locations are just a stub.
// Container types like CompoundExpression also don't need a real location.
const locStub = {
source: '',
start: { line: 1, column: 1, offset: 0 },
end: { line: 1, column: 1, offset: 0 }
};
function createRoot(children, loc = locStub) {
return {
type: 0 /* ROOT */,
children,
helpers: [],
components: [],
directives: [],
hoists: [],
imports: [],
cached: 0,
temps: 0,
codegenNode: undefined,
loc
};
}
function createVNodeCall(context, tag, props, children, patchFlag, dynamicProps, directives, isBlock = false, disableTracking = false, loc = locStub) {
if (context) {
if (isBlock) {
context.helper(OPEN_BLOCK);
context.helper(CREATE_BLOCK);
}
else {
context.helper(CREATE_VNODE);
}
if (directives) {
context.helper(WITH_DIRECTIVES);
}
}
return {
type: 13 /* VNODE_CALL */,
tag,
props,
children,
patchFlag,
dynamicProps,
directives,
isBlock,
disableTracking,
loc
};
}
function createArrayExpression(elements, loc = locStub) {
return {
type: 17 /* JS_ARRAY_EXPRESSION */,
loc,
elements
};
}
function createObjectExpression(properties, loc = locStub) {
return {
type: 15 /* JS_OBJECT_EXPRESSION */,
loc,
properties
};
}
function createObjectProperty(key, value) {
return {
type: 16 /* JS_PROPERTY */,
loc: locStub,
key: shared.isString(key) ? createSimpleExpression(key, true) : key,
value
};
}
function createSimpleExpression(content, isStatic, loc = locStub, isConstant = false) {
return {
type: 4 /* SIMPLE_EXPRESSION */,
loc,
isConstant,
content,
isStatic
};
}
function createInterpolation(content, loc) {
return {
type: 5 /* INTERPOLATION */,
loc,
content: shared.isString(content)
? createSimpleExpression(content, false, loc)
: content
};
}
function createCompoundExpression(children, loc = locStub) {
return {
type: 8 /* COMPOUND_EXPRESSION */,
loc,
children
};
}
function createCallExpression(callee, args = [], loc = locStub) {
return {
type: 14 /* JS_CALL_EXPRESSION */,
loc,
callee,
arguments: args
};
}
function createFunctionExpression(params, returns = undefined, newline = false, isSlot = false, loc = locStub) {
return {
type: 18 /* JS_FUNCTION_EXPRESSION */,
params,
returns,
newline,
isSlot,
loc
};
}
function createConditionalExpression(test, consequent, alternate, newline = true) {
return {
type: 19 /* JS_CONDITIONAL_EXPRESSION */,
test,
consequent,
alternate,
newline,
loc: locStub
};
}
function createCacheExpression(index, value, isVNode = false) {
return {
type: 20 /* JS_CACHE_EXPRESSION */,
index,
value,
isVNode,
loc: locStub
};
}
function createBlockStatement(body) {
return {
type: 21 /* JS_BLOCK_STATEMENT */,
body,
loc: locStub
};
}
function createTemplateLiteral(elements) {
return {
type: 22 /* JS_TEMPLATE_LITERAL */,
elements,
loc: locStub
};
}
function createIfStatement(test, consequent, alternate) {
return {
type: 23 /* JS_IF_STATEMENT */,
test,
consequent,
alternate,
loc: locStub
};
}
function createAssignmentExpression(left, right) {
return {
type: 24 /* JS_ASSIGNMENT_EXPRESSION */,
left,
right,
loc: locStub
};
}
function createSequenceExpression(expressions) {
return {
type: 25 /* JS_SEQUENCE_EXPRESSION */,
expressions,
loc: locStub
};
}
function createReturnStatement(returns) {
return {
type: 26 /* JS_RETURN_STATEMENT */,
returns,
loc: locStub
};
}
const isStaticExp = (p) => p.type === 4 /* SIMPLE_EXPRESSION */ && p.isStatic;
const isBuiltInType = (tag, expected) => tag === expected || tag === shared.hyphenate(expected);
function isCoreComponent(tag) {
if (isBuiltInType(tag, 'Teleport')) {
return TELEPORT;
}
else if (isBuiltInType(tag, 'Suspense')) {
return SUSPENSE;
}
else if (isBuiltInType(tag, 'KeepAlive')) {
return KEEP_ALIVE;
}
else if (isBuiltInType(tag, 'BaseTransition')) {
return BASE_TRANSITION;
}
}
const nonIdentifierRE = /^\d|[^\$\w]/;
const isSimpleIdentifier = (name) => !nonIdentifierRE.test(name);
const memberExpRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\[[^\]]+\])*$/;
const isMemberExpression = (path) => {
if (!path)
return false;
return memberExpRE.test(path.trim());
};
function getInnerRange(loc, offset, length) {
const source = loc.source.substr(offset, length);
const newLoc = {
source,
start: advancePositionWithClone(loc.start, loc.source, offset),
end: loc.end
};
if (length != null) {
newLoc.end = advancePositionWithClone(loc.start, loc.source, offset + length);
}
return newLoc;
}
function advancePositionWithClone(pos, source, numberOfCharacters = source.length) {
return advancePositionWithMutation(shared.extend({}, pos), source, numberOfCharacters);
}
// advance by mutation without cloning (for performance reasons), since this
// gets called a lot in the parser
function advancePositionWithMutation(pos, source, numberOfCharacters = source.length) {
let linesCount = 0;
let lastNewLinePos = -1;
for (let i = 0; i < numberOfCharacters; i++) {
if (source.charCodeAt(i) === 10 /* newline char code */) {
linesCount++;
lastNewLinePos = i;
}
}
pos.offset += numberOfCharacters;
pos.line += linesCount;
pos.column =
lastNewLinePos === -1
? pos.column + numberOfCharacters
: numberOfCharacters - lastNewLinePos;
return pos;
}
function assert(condition, msg) {
/* istanbul ignore if */
if (!condition) {
throw new Error(msg || `unexpected compiler condition`);
}
}
function findDir(node, name, allowEmpty = false) {
for (let i = 0; i < node.props.length; i++) {
const p = node.props[i];
if (p.type === 7 /* DIRECTIVE */ &&
(allowEmpty || p.exp) &&
(shared.isString(name) ? p.name === name : name.test(p.name))) {
return p;
}
}
}
function findProp(node, name, dynamicOnly = false, allowEmpty = false) {
for (let i = 0; i < node.props.length; i++) {
const p = node.props[i];
if (p.type === 6 /* ATTRIBUTE */) {
if (dynamicOnly)
continue;
if (p.name === name && (p.value || allowEmpty)) {
return p;
}
}
else if (p.name === 'bind' &&
(p.exp || allowEmpty) &&
isBindKey(p.arg, name)) {
return p;
}
}
}
function isBindKey(arg, name) {
return !!(arg && isStaticExp(arg) && arg.content === name);
}
function hasDynamicKeyVBind(node) {
return node.props.some(p => p.type === 7 /* DIRECTIVE */ &&
p.name === 'bind' &&
(!p.arg || // v-bind="obj"
p.arg.type !== 4 /* SIMPLE_EXPRESSION */ || // v-bind:[_ctx.foo]
!p.arg.isStatic) // v-bind:[foo]
);
}
function isText(node) {
return node.type === 5 /* INTERPOLATION */ || node.type === 2 /* TEXT */;
}
function isVSlot(p) {
return p.type === 7 /* DIRECTIVE */ && p.name === 'slot';
}
function isTemplateNode(node) {
return (node.type === 1 /* ELEMENT */ && node.tagType === 3 /* TEMPLATE */);
}
function isSlotOutlet(node) {
return node.type === 1 /* ELEMENT */ && node.tagType === 2 /* SLOT */;
}
function injectProp(node, prop, context) {
let propsWithInjection;
const props = node.type === 13 /* VNODE_CALL */ ? node.props : node.arguments[2];
if (props == null || shared.isString(props)) {
propsWithInjection = createObjectExpression([prop]);
}
else if (props.type === 14 /* JS_CALL_EXPRESSION */) {
// merged props... add ours
// only inject key to object literal if it's the first argument so that
// if doesn't override user provided keys
const first = props.arguments[0];
if (!shared.isString(first) && first.type === 15 /* JS_OBJECT_EXPRESSION */) {
first.properties.unshift(prop);
}
else {
props.arguments.unshift(createObjectExpression([prop]));
}
propsWithInjection = props;
}
else if (props.type === 15 /* JS_OBJECT_EXPRESSION */) {
let alreadyExists = false;
// check existing key to avoid overriding user provided keys
if (prop.key.type === 4 /* SIMPLE_EXPRESSION */) {
const propKeyName = prop.key.content;
alreadyExists = props.properties.some(p => p.key.type === 4 /* SIMPLE_EXPRESSION */ &&
p.key.content === propKeyName);
}
if (!alreadyExists) {
props.properties.unshift(prop);
}
propsWithInjection = props;
}
else {
// single v-bind with expression, return a merged replacement
propsWithInjection = createCallExpression(context.helper(MERGE_PROPS), [
createObjectExpression([prop]),
props
]);
}
if (node.type === 13 /* VNODE_CALL */) {
node.props = propsWithInjection;
}
else {
node.arguments[2] = propsWithInjection;
}
}
function toValidAssetId(name, type) {
return `_${type}_${name.replace(/[^\w]/g, '_')}`;
}
// Check if a node contains expressions that reference current context scope ids
function hasScopeRef(node, ids) {
if (!node || Object.keys(ids).length === 0) {
return false;
}
switch (node.type) {
case 1 /* ELEMENT */:
for (let i = 0; i < node.props.length; i++) {
const p = node.props[i];
if (p.type === 7 /* DIRECTIVE */ &&
(hasScopeRef(p.arg, ids) || hasScopeRef(p.exp, ids))) {
return true;
}
}
return node.children.some(c => hasScopeRef(c, ids));
case 11 /* FOR */:
if (hasScopeRef(node.source, ids)) {
return true;
}
return node.children.some(c => hasScopeRef(c, ids));
case 9 /* IF */:
return node.branches.some(b => hasScopeRef(b, ids));
case 10 /* IF_BRANCH */:
if (hasScopeRef(node.condition, ids)) {
return true;
}
return node.children.some(c => hasScopeRef(c, ids));
case 4 /* SIMPLE_EXPRESSION */:
return (!node.isStatic &&
isSimpleIdentifier(node.content) &&
!!ids[node.content]);
case 8 /* COMPOUND_EXPRESSION */:
return node.children.some(c => shared.isObject(c) && hasScopeRef(c, ids));
case 5 /* INTERPOLATION */:
case 12 /* TEXT_CALL */:
return hasScopeRef(node.content, ids);
case 2 /* TEXT */:
case 3 /* COMMENT */:
return false;
default:
return false;
}
}
// The default decoder only provides escapes for characters reserved as part of
// the template syntax, and is only used if the custom renderer did not provide
// a platform-specific decoder.
const decodeRE = /&(gt|lt|amp|apos|quot);/g;
const decodeMap = {
gt: '>',
lt: '<',
amp: '&',
apos: "'",
quot: '"'
};
const defaultParserOptions = {
delimiters: [`{{`, `}}`],
getNamespace: () => 0 /* HTML */,
getTextMode: () => 0 /* DATA */,
isVoidTag: shared.NO,
isPreTag: shared.NO,
isCustomElement: shared.NO,
decodeEntities: (rawText) => rawText.replace(decodeRE, (_, p1) => decodeMap[p1]),
onError: defaultOnError,
comments: false
};
function baseParse(content, options = {}) {
const context = createParserContext(content, options);
const start = getCursor(context);
return createRoot(parseChildren(context, 0 /* DATA */, []), getSelection(context, start));
}
function createParserContext(content, rawOptions) {
const options = shared.extend({}, defaultParserOptions);
for (const key in rawOptions) {
// @ts-ignore
options[key] = rawOptions[key] || defaultParserOptions[key];
}
return {
options,
column: 1,
line: 1,
offset: 0,
originalSource: content,
source: content,
inPre: false,
inVPre: false
};
}
function parseChildren(context, mode, ancestors) {
const parent = last(ancestors);
const ns = parent ? parent.ns : 0 /* HTML */;
const nodes = [];
while (!isEnd(context, mode, ancestors)) {
const s = context.source;
let node = undefined;
if (mode === 0 /* DATA */ || mode === 1 /* RCDATA */) {
if (!context.inVPre && startsWith(s, context.options.delimiters[0])) {
// '{{'
node = parseInterpolation(context, mode);
}
else if (mode === 0 /* DATA */ && s[0] === '<') {
// https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state
if (s.length === 1) {
emitError(context, 5 /* EOF_BEFORE_TAG_NAME */, 1);
}
else if (s[1] === '!') {
// https://html.spec.whatwg.org/multipage/parsing.html#markup-declaration-open-state
if (startsWith(s, '<!--')) {
node = parseComment(context);
}
else if (startsWith(s, '<!DOCTYPE')) {
// Ignore DOCTYPE by a limitation.
node = parseBogusComment(context);
}
else if (startsWith(s, '<![CDATA[')) {
if (ns !== 0 /* HTML */) {
node = parseCDATA(context, ancestors);
}
else {
emitError(context, 1 /* CDATA_IN_HTML_CONTENT */);
node = parseBogusComment(context);
}
}
else {
emitError(context, 11 /* INCORRECTLY_OPENED_COMMENT */);
node = parseBogusComment(context);
}
}
else if (s[1] === '/') {
// https://html.spec.whatwg.org/multipage/parsing.html#end-tag-open-state
if (s.length === 2) {
emitError(context, 5 /* EOF_BEFORE_TAG_NAME */, 2);
}
else if (s[2] === '>') {
emitError(context, 14 /* MISSING_END_TAG_NAME */, 2);
advanceBy(context, 3);
continue;
}
else if (/[a-z]/i.test(s[2])) {
emitError(context, 23 /* X_INVALID_END_TAG */);
parseTag(context, 1 /* End */, parent);
continue;
}
else {
emitError(context, 12 /* INVALID_FIRST_CHARACTER_OF_TAG_NAME */, 2);
node = parseBogusComment(context);
}
}
else if (/[a-z]/i.test(s[1])) {
node = parseElement(context, ancestors);
}
else if (s[1] === '?') {
emitError(context, 21 /* UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME */, 1);
node = parseBogusComment(context);
}
else {
emitError(context, 12 /* INVALID_FIRST_CHARACTER_OF_TAG_NAME */, 1);
}
}
}
if (!node) {
node = parseText(context, mode);
}
if (shared.isArray(node)) {
for (let i = 0; i < node.length; i++) {
pushNode(nodes, node[i]);
}
}
else {
pushNode(nodes, node);
}
}
// Whitespace management for more efficient output
// (same as v2 whitespace: 'condense')
let removedWhitespace = false;
if (mode !== 2 /* RAWTEXT */) {
if (!context.inPre) {
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.type === 2 /* TEXT */) {
if (!/[^\t\r\n\f ]/.test(node.content)) {
const prev = nodes[i - 1];
const next = nodes[i + 1];
// If:
// - the whitespace is the first or last node, or:
// - the whitespace is adjacent to a comment, or:
// - the whitespace is between two elements AND contains newline
// Then the whitespace is ignored.
if (!prev ||
!next ||
prev.type === 3 /* COMMENT */ ||
next.type === 3 /* COMMENT */ ||
(prev.type === 1 /* ELEMENT */ &&
next.type === 1 /* ELEMENT */ &&
/[\r\n]/.test(node.content))) {
removedWhitespace = true;
nodes[i] = null;
}
else {
// Otherwise, condensed consecutive whitespace inside the text
// down to a single space
node.content = ' ';
}
}
else {
node.content = node.content.replace(/[\t\r\n\f ]+/g, ' ');
}
}
}
}
else if (parent && context.options.isPreTag(parent.tag)) {
// remove leading newline per html spec
// https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element
const first = nodes[0];
if (first && first.type === 2 /* TEXT */) {
first.content = first.content.replace(/^\r?\n/, '');
}
}
}
return removedWhitespace ? nodes.filter(Boolean) : nodes;
}
function pushNode(nodes, node) {
if (node.type === 2 /* TEXT */) {
const prev = last(nodes);
// Merge if both this and the previous node are text and those are
// consecutive. This happens for cases like "a < b".
if (prev &&
prev.type === 2 /* TEXT */ &&
prev.loc.end.offset === node.loc.start.offset) {
prev.content += node.content;
prev.loc.end = node.loc.end;
prev.loc.source += node.loc.source;
return;
}
}
nodes.push(node);
}
function parseCDATA(context, ancestors) {
advanceBy(context, 9);
const nodes = parseChildren(context, 3 /* CDATA */, ancestors);
if (context.source.length === 0) {
emitError(context, 6 /* EOF_IN_CDATA */);
}
else {
advanceBy(context, 3);
}
return nodes;
}
function parseComment(context) {
const start = getCursor(context);
let content;
// Regular comment.
const match = /--(\!)?>/.exec(context.source);
if (!match) {
content = context.source.slice(4);
advanceBy(context, context.source.length);
emitError(context, 7 /* EOF_IN_COMMENT */);
}
else {
if (match.index <= 3) {
emitError(context, 0 /* ABRUPT_CLOSING_OF_EMPTY_COMMENT */);
}
if (match[1]) {
emitError(context, 10 /* INCORRECTLY_CLOSED_COMMENT */);
}
content = context.source.slice(4, match.index);
// Advancing with reporting nested comments.
const s = context.source.slice(0, match.index);
let prevIndex = 1, nestedIndex = 0;
while ((nestedIndex = s.indexOf('<!--', prevIndex)) !== -1) {
advanceBy(context, nestedIndex - prevIndex + 1);
if (nestedIndex + 4 < s.length) {
emitError(context, 16 /* NESTED_COMMENT */);
}
prevIndex = nestedIndex + 1;
}
advanceBy(context, match.index + match[0].length - prevIndex + 1);
}
return {
type: 3 /* COMMENT */,
content,
loc: getSelection(context, start)
};
}
function parseBogusComment(context) {
const start = getCursor(context);
const contentStart = context.source[1] === '?' ? 1 : 2;
let content;
const closeIndex = context.source.indexOf('>');
if (closeIndex === -1) {
content = context.source.slice(contentStart);
advanceBy(context, context.source.length);
}
else {
content = context.source.slice(contentStart, closeIndex);
advanceBy(context, closeIndex + 1);
}
return {
type: 3 /* COMMENT */,
content,
loc: getSelection(context, start)
};
}
function parseElement(context, ancestors) {
// Start tag.
const wasInPre = context.inPre;
const wasInVPre = context.inVPre;
const parent = last(ancestors);
const element = parseTag(context, 0 /* Start */, parent);
const isPreBoundary = context.inPre && !wasInPre;
const isVPreBoundary = context.inVPre && !wasInVPre;
if (element.isSelfClosing || context.options.isVoidTag(element.tag)) {
return element;
}
// Children.
ancestors.push(element);
const mode = context.options.getTextMode(element, parent);
const children = parseChildren(context, mode, ancestors);
ancestors.pop();
element.children = children;
// End tag.
if (startsWithEndTagOpen(context.source, element.tag)) {
parseTag(context, 1 /* End */, parent);
}
else {
emitError(context, 24 /* X_MISSING_END_TAG */, 0, element.loc.start);
if (context.source.length === 0 && element.tag.toLowerCase() === 'script') {
const first = children[0];
if (first && startsWith(first.loc.source, '<!--')) {
emitError(context, 8 /* EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT */);
}
}
}
element.loc = getSelection(context, element.loc.start);
if (isPreBoundary) {
context.inPre = false;
}
if (isVPreBoundary) {
context.inVPre = false;
}
return element;
}
const isSpecialTemplateDirective = /*#__PURE__*/ shared.makeMap(`if,else,else-if,for,slot`);
/**
* Parse a tag (E.g. `<div id=a>`) with that type (start tag or end tag).
*/
function parseTag(context, type, parent) {
// Tag open.
const start = getCursor(context);
const match = /^<\/?([a-z][^\t\r\n\f />]*)/i.exec(context.source);
const tag = match[1];
const ns = context.options.getNamespace(tag, parent);
advanceBy(context, match[0].length);
advanceSpaces(context);
// save current state in case we need to re-parse attributes with v-pre
const cursor = getCursor(context);
const currentSource = context.source;
// Attributes.
let props = parseAttributes(context, type);
// check <pre> tag
if (context.options.isPreTag(tag)) {
context.inPre = true;
}
// check v-pre
if (!context.inVPre &&
props.some(p => p.type === 7 /* DIRECTIVE */ && p.name === 'pre')) {
context.inVPre = true;
// reset context
shared.extend(context, cursor);
context.source = currentSource;
// re-parse attrs and filter out v-pre itself
props = parseAttributes(context, type).filter(p => p.name !== 'v-pre');
}
// Tag close.
let isSelfClosing = false;
if (context.source.length === 0) {
emitError(context, 9 /* EOF_IN_TAG */);
}
else {
isSelfClosing = startsWith(context.source, '/>');
if (type === 1 /* End */ && isSelfClosing) {
emitError(context, 4 /* END_TAG_WITH_TRAILING_SOLIDUS */);
}
advanceBy(context, isSelfClosing ? 2 : 1);
}
let tagType = 0 /* ELEMENT */;
const options = context.options;
if (!context.inVPre && !options.isCustomElement(tag)) {
const hasVIs = props.some(p => p.type === 7 /* DIRECTIVE */ && p.name === 'is');
if (options.isNativeTag && !hasVIs) {
if (!options.isNativeTag(tag))
tagType = 1 /* COMPONENT */;
}
else if (hasVIs ||
isCoreComponent(tag) ||
(options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
/^[A-Z]/.test(tag) ||
tag === 'component') {
tagType = 1 /* COMPONENT */;
}
if (tag === 'slot') {
tagType = 2 /* SLOT */;
}
else if (tag === 'template' &&
props.some(p => {
return (p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name));
})) {
tagType = 3 /* TEMPLATE */;
}
}
return {
type: 1 /* ELEMENT */,
ns,
tag,
tagType,
props,
isSelfClosing,
children: [],
loc: getSelection(context, start),
codegenNode: undefined // to be created during transform phase
};
}
function parseAttributes(context, type) {
const props = [];
const attributeNames = new Set();
while (context.source.length > 0 &&
!startsWith(context.source, '>') &&
!startsWith(context.source, '/>')) {
if (startsWith(context.source, '/')) {
emitError(context, 22 /* UNEXPECTED_SOLIDUS_IN_TAG */);
advanceBy(context, 1);
advanceSpaces(context);
continue;
}
if (type === 1 /* End */) {
emitError(context, 3 /* END_TAG_WITH_ATTRIBUTES */);
}
const attr = parseAttribute(context, attributeNames);
if (type === 0 /* Start */) {
props.push(attr);
}
if (/^[^\t\r\n\f />]/.test(context.source)) {
emitError(context, 15 /* MISSING_WHITESPACE_BETWEEN_ATTRIBUTES */);
}
advanceSpaces(context);
}
return props;
}
function parseAttribute(context, nameSet) {
// Name.
const start = getCursor(context);
const match = /^[^\t\r\n\f />][^\t\r\n\f />=]*/.exec(context.source);
const name = match[0];
if (nameSet.has(name)) {
emitError(context, 2 /* DUPLICATE_ATTRIBUTE */);
}
nameSet.add(name);
if (name[0] === '=') {
emitError(context, 19 /* UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME */);
}
{
const pattern = /["'<]/g;
let m;
while ((m = pattern.exec(name))) {
emitError(context, 17 /* UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME */, m.index);
}
}
advanceBy(context, name.length);
// Value
let value = undefined;
if (/^[\t\r\n\f ]*=/.test(context.source)) {
advanceSpaces(context);
advanceBy(context, 1);
advanceSpaces(context);
value = parseAttributeValue(context);
if (!value) {
emitError(context, 13 /* MISSING_ATTRIBUTE_VALUE */);
}
}
const loc = getSelection(context, start);
if (!context.inVPre && /^(v-|:|@|#)/.test(name)) {
const match = /(?:^v-([a-z0-9-]+))?(?:(?::|^@|^#)(\[[^\]]+\]|[^\.]+))?(.+)?$/i.exec(name);
const dirName = match[1] ||
(startsWith(name, ':') ? 'bind' : startsWith(name, '@') ? 'on' : 'slot');
let arg;
if (match[2]) {
const isSlot = dirName === 'slot';
const startOffset = name.indexOf(match[2]);
const loc = getSelection(context, getNewPosition(context, start, startOffset), getNewPosition(context, start, startOffset + match[2].length + ((isSlot && match[3]) || '').length));
let content = match[2];
let isStatic = true;
if (content.startsWith('[')) {
isStatic = false;
if (!content.endsWith(']')) {
emitError(context, 26 /* X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END */);
}
content = content.substr(1, content.length - 2);
}
else if (isSlot) {
// #1241 special case for v-slot: vuetify relies extensively on slot
// names containing dots. v-slot doesn't have any modifiers and Vue 2.x
// supports such usage so we are keeping it consistent with 2.x.
content += match[3] || '';
}
arg = {
type: 4 /* SIMPLE_EXPRESSION */,
content,
isStatic,
isConstant: isStatic,
loc
};
}
if (value && value.isQuoted) {
const valueLoc = value.loc;
valueLoc.start.offset++;
valueLoc.start.column++;
valueLoc.end = advancePositionWithClone(valueLoc.start, value.content);
valueLoc.source = valueLoc.source.slice(1, -1);
}
return {
type: 7 /* DIRECTIVE */,
name: dirName,
exp: value && {
type: 4 /* SIMPLE_EXPRESSION */,
content: value.content,
isStatic: false,
// Treat as non-constant by default. This can be potentially set to
// true by `transformExpression` to make it eligible for hoisting.
isConstant: false,
loc: value.loc
},
arg,
modifiers: match[3] ? match[3].substr(1).split('.') : [],
loc
};
}
return {
type: 6 /* ATTRIBUTE */,
name,
value: value && {
type: 2 /* TEXT */,
content: value.content,
loc: value.loc
},
loc
};
}
function parseAttributeValue(context) {
const start = getCursor(context);
let content;
const quote = context.source[0];
const isQuoted = quote === `"` || quote === `'`;
if (isQuoted) {
// Quoted value.
advanceBy(context, 1);
const endIndex = context.source.indexOf(quote);
if (endIndex === -1) {
content = parseTextData(context, context.source.length, 4 /* ATTRIBUTE_VALUE */);
}
else {
content = parseTextData(context, endIndex, 4 /* ATTRIBUTE_VALUE */);
advanceBy(context, 1);
}
}
else {
// Unquoted
const match = /^[^\t\r\n\f >]+/.exec(context.source);
if (!match) {
return undefined;
}
const unexpectedChars = /["'<=`]/g;
let m;
while ((m = unexpectedChars.exec(match[0]))) {
emitError(context, 18 /* UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE */, m.index);
}
content = parseTextData(context, match[0].length, 4 /* ATTRIBUTE_VALUE */);
}
return { content, isQuoted, loc: getSelection(context, start) };
}
function parseInterpolation(context, mode) {
const [open, close] = context.options.delimiters;
const closeIndex = context.source.indexOf(close, open.length);
if (closeIndex === -1) {
emitError(context, 25 /* X_MISSING_INTERPOLATION_END */);
return undefined;
}
const start = getCursor(context);
advanceBy(context, open.length);
const innerStart = getCursor(context);
const innerEnd = getCursor(context);
const rawContentLength = closeIndex - open.length;
const rawContent = context.source.slice(0, rawContentLength);
const preTrimContent = parseTextData(context, rawContentLength, mode);
const content = preTrimContent.trim();
const startOffset = preTrimContent.indexOf(content);
if (startOffset > 0) {
advancePositionWithMutation(innerStart, rawContent, startOffset);
}
const endOffset = rawContentLength - (preTrimContent.length - content.length - startOffset);
advancePositionWithMutation(innerEnd, rawContent, endOffset);
advanceBy(context, close.length);
return {
type: 5 /* INTERPOLATION */,
content: {
type: 4 /* SIMPLE_EXPRESSION */,
isStatic: false,
// Set `isConstant` to false by default and will decide in transformExpression
isConstant: false,
content,
loc: getSelection(context, innerStart, innerEnd)
},
loc: getSelection(context, start)
};
}
function parseText(context, mode) {
const endTokens = ['<', context.options.delimiters[0]];
if (mode === 3 /* CDATA */) {
endTokens.push(']]>');
}
let endIndex = context.source.length;
for (let i = 0; i < endTokens.length; i++) {
const index = context.source.indexOf(endTokens[i], 1);
if (index !== -1 && endIndex > index) {
endIndex = index;
}
}
const start = getCursor(context);
const content = parseTextData(context, endIndex, mode);
return {
type: 2 /* TEXT */,
content,
loc: getSelection(context, start)
};
}
/**
* Get text data with a given length from the current location.
* This translates HTML entities in the text data.
*/
function parseTextData(context, length, mode) {
const rawText = context.source.slice(0, length);
advanceBy(context, length);
if (mode === 2 /* RAWTEXT */ ||
mode === 3 /* CDATA */ ||
rawText.indexOf('&') === -1) {
return rawText;
}
else {
// DATA or RCDATA containing "&"". Entity decoding required.
return context.options.decodeEntities(rawText, mode === 4 /* ATTRIBUTE_VALUE */);
}
}
function getCursor(context) {
const { column, line, offset } = context;
return { column, line, offset };
}
function getSelection(context, start, end) {
end = end || getCursor(context);
return {
start,
end,
source: context.originalSource.slice(start.offset, end.offset)
};
}
function last(xs) {
return xs[xs.length - 1];
}
function startsWith(source, searchString) {
return source.startsWith(searchString);
}
function advanceBy(context, numberOfCharacters) {
const { source } = context;
advancePositionWithMutation(context, source, numberOfCharacters);
context.source = source.slice(numberOfCharacters);
}
function advanceSpaces(context) {
const match = /^[\t\r\n\f ]+/.exec(context.source);
if (match) {
advanceBy(context, match[0].length);
}
}
function getNewPosition(context, start, numberOfCharacters) {
return advancePositionWithClone(start, context.originalSource.slice(start.offset, numberOfCharacters), numberOfCharacters);
}
function emitError(context, code, offset, loc = getCursor(context)) {
if (offset) {
loc.offset += offset;
loc.column += offset;
}
context.options.onError(createCompilerError(code, {
start: loc,
end: loc,
source: ''
}));
}
function isEnd(context, mode, ancestors) {
const s = context.source;
switch (mode) {
case 0 /* DATA */:
if (startsWith(s, '</')) {
//TODO: probably bad performance
for (let i = ancestors.length - 1; i >= 0; --i) {
if (startsWithEndTagOpen(s, ancestors[i].tag)) {
return true;
}
}
}
break;
case 1 /* RCDATA */:
case 2 /* RAWTEXT */: {
const parent = last(ancestors);
if (parent && startsWithEndTagOpen(s, parent.tag)) {
return true;
}
break;
}
case 3 /* CDATA */:
if (startsWith(s, ']]>')) {
return true;
}
break;
}
return !s;
}
function startsWithEndTagOpen(source, tag) {
return (startsWith(source, '</') &&
source.substr(2, tag.length).toLowerCase() === tag.toLowerCase() &&
/[\t\r\n\f />]/.test(source[2 + tag.length] || '>'));
}
function hoistStatic(root, context) {
walk(root, context, new Map(),
// Root node is unfortunately non-hoistable due to potential parent
// fallthrough attributes.
isSingleElementRoot(root, root.children[0]));
}
function isSingleElementRoot(root, child) {
const { children } = root;
return (children.length === 1 &&
child.type === 1 /* ELEMENT */ &&
!isSlotOutlet(child));
}
function walk(node, context, resultCache, doNotHoistNode = false) {
let hasHoistedNode = false;
// Some transforms, e.g. transformAssetUrls from @vue/compiler-sfc, replaces
// static bindings with expressions. These expressions are guaranteed to be
// constant so they are still eligible for hoisting, but they are only
// available at runtime and therefore cannot be evaluated ahead of time.
// This is only a concern for pre-stringification (via transformHoist by
// @vue/compiler-dom), but doing it here allows us to perform only one full
// walk of the AST and allow `stringifyStatic` to stop walking as soon as its
// stringficiation threshold is met.
let hasRuntimeConstant = false;
const { children } = node;
for (let i = 0; i < children.length; i++) {
const child = children[i];
// only plain elements & text calls are eligible for hoisting.
if (child.type === 1 /* ELEMENT */ &&
child.tagType === 0 /* ELEMENT */) {
let staticType;
if (!doNotHoistNode &&
(staticType = getStaticType(child, resultCache)) > 0) {
if (staticType === 2 /* HAS_RUNTIME_CONSTANT */) {
hasRuntimeConstant = true;
}
child.codegenNode.patchFlag =
-1 /* HOISTED */ + ( ` /* HOISTED */` );
child.codegenNode = context.hoist(child.codegenNode);
hasHoistedNode = true;
continue;
}
else {
// node may contain dynamic children, but its props may be eligible for
// hoisting.
const codegenNode = child.codegenNode;
if (codegenNode.type === 13 /* VNODE_CALL */) {
const flag = getPatchFlag(codegenNode);
if ((!flag ||
flag === 512 /* NEED_PATCH */ ||
flag === 1 /* TEXT */) &&
!hasNonHoistableProps(child)) {
const props = getNodeProps(child);
if (props) {
codegenNode.props = context.hoist(props);
}
}
}
}
}
else if (child.type === 12 /* TEXT_CALL */) {
const staticType = getStaticType(child.content, resultCache);
if (staticType > 0) {
if (staticType === 2 /* HAS_RUNTIME_CONSTANT */) {
hasRuntimeConstant = true;
}
child.codegenNode = context.hoist(child.codegenNode);
hasHoistedNode = true;
}
}
// walk further
if (child.type === 1 /* ELEMENT */) {
walk(child, context, resultCache);
}
else if (child.type === 11 /* FOR */) {
// Do not hoist v-for single child because it has to be a block
walk(child, context, resultCache, child.children.length === 1);
}
else if (child.type === 9 /* IF */) {
for (let i = 0; i < child.branches.length; i++) {
// Do not hoist v-if single child because it has to be a block
walk(child.branches[i], context, resultCache, child.branches[i].children.length === 1);
}
}
}
if (!hasRuntimeConstant && hasHoistedNode && context.transformHoist) {
context.transformHoist(children, context, node);
}
}
function getStaticType(node, resultCache = new Map()) {
switch (node.type) {
case 1 /* ELEMENT */:
if (node.tagType !== 0 /* ELEMENT */) {
return 0 /* NOT_STATIC */;
}
const cached = resultCache.get(node);
if (cached !== undefined) {
return cached;
}
const codegenNode = node.codegenNode;
if (codegenNode.type !== 13 /* VNODE_CALL */) {
return 0 /* NOT_STATIC */;
}
const flag = getPatchFlag(codegenNode);
if (!flag && !hasNonHoistableProps(node)) {
// element self is static. check its children.
let returnType = 1 /* FULL_STATIC */;
for (let i = 0; i < node.children.length; i++) {
const childType = getStaticType(node.children[i], resultCache);
if (childType === 0 /* NOT_STATIC */) {
resultCache.set(node, 0 /* NOT_STATIC */);
return 0 /* NOT_STATIC */;
}
else if (childType === 2 /* HAS_RUNTIME_CONSTANT */) {
returnType = 2 /* HAS_RUNTIME_CONSTANT */;
}
}
// check if any of the props contain runtime constants
if (returnType !== 2 /* HAS_RUNTIME_CONSTANT */) {
for (let i = 0; i < node.props.length; i++) {
const p = node.props[i];
if (p.type === 7 /* DIRECTIVE */ &&
p.name === 'bind' &&
p.exp &&
(p.exp.type === 8 /* COMPOUND_EXPRESSION */ ||
p.exp.isRuntimeConstant)) {
returnType = 2 /* HAS_RUNTIME_CONSTANT */;
}
}
}
// only svg/foreignObject could be block here, however if they are
// stati then they don't need to be blocks since there will be no
// nested updates.
if (codegenNode.isBlock) {
codegenNode.isBlock = false;
}
resultCache.set(node, returnType);
return returnType;
}
else {
resultCache.set(node, 0 /* NOT_STATIC */);
return 0 /* NOT_STATIC */;
}
case 2 /* TEXT */:
case 3 /* COMMENT */:
return 1 /* FULL_STATIC */;
case 9 /* IF */:
case 11 /* FOR */:
case 10 /* IF_BRANCH */:
return 0 /* NOT_STATIC */;
case 5 /* INTERPOLATION */:
case 12 /* TEXT_CALL */:
return getStaticType(node.content, resultCache);
case 4 /* SIMPLE_EXPRESSION */:
return node.isConstant
? node.isRuntimeConstant
? 2 /* HAS_RUNTIME_CONSTANT */
: 1 /* FULL_STATIC */
: 0 /* NOT_STATIC */;
case 8 /* COMPOUND_EXPRESSION */:
let returnType = 1 /* FULL_STATIC */;
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
if (shared.isString(child) || shared.isSymbol(child)) {
continue;
}
const childType = getStaticType(child, resultCache);
if (childType === 0 /* NOT_STATIC */) {
return 0 /* NOT_STATIC */;
}
else if (childType === 2 /* HAS_RUNTIME_CONSTANT */) {
returnType = 2 /* HAS_RUNTIME_CONSTANT */;
}
}
return returnType;
default:
return 0 /* NOT_STATIC */;
}
}
/**
* Even for a node with no patch flag, it is possible for it to contain
* non-hoistable expressions that refers to scope variables, e.g. compiler
* injected keys or cached event handlers. Therefore we need to always check the
* codegenNode's props to be sure.
*/
function hasNonHoistableProps(node) {
const props = getNodeProps(node);
if (props && props.type === 15 /* JS_OBJECT_EXPRESSION */) {
const { properties } = props;
for (let i = 0; i < properties.length; i++) {
const { key, value } = properties[i];
if (key.type !== 4 /* SIMPLE_EXPRESSION */ ||
!key.isStatic ||
(value.type !== 4 /* SIMPLE_EXPRESSION */ ||
(!value.isStatic && !value.isConstant))) {
return true;
}
}
}
return false;
}
function getNodeProps(node) {
const codegenNode = node.codegenNode;
if (codegenNode.type === 13 /* VNODE_CALL */) {
return codegenNode.props;
}
}
function getPatchFlag(node) {
const flag = node.patchFlag;
return flag ? parseInt(flag, 10) : undefined;
}
function createTransformContext(root, { prefixIdentifiers = false, hoistStatic = false, cacheHandlers = false, nodeTransforms = [], directiveTransforms = {}, transformHoist = null, isBuiltInComponent = shared.NOOP, isCustomElement = shared.NOOP, expressionPlugins = [], scopeId = null, ssr = false, ssrCssVars = ``, bindingMetadata = {}, onError = defaultOnError }) {
const context = {
// options
prefixIdentifiers,
hoistStatic,
cacheHandlers,
nodeTransforms,
directiveTransforms,
transformHoist,
isBuiltInComponent,
isCustomElement,
expressionPlugins,
scopeId,
ssr,
ssrCssVars,
bindingMetadata,
onError,
// state
root,
helpers: new Set(),
components: new Set(),
directives: new Set(),
hoists: [],
imports: new Set(),
temps: 0,
cached: 0,
identifiers: Object.create(null),
scopes: {
vFor: 0,
vSlot: 0,
vPre: 0,
vOnce: 0
},
parent: null,
currentNode: root,
childIndex: 0,
// methods
helper(name) {
context.helpers.add(name);
return name;
},
helperString(name) {
return `_${helperNameMap[context.helper(name)]}`;
},
replaceNode(node) {
/* istanbul ignore if */
{
if (!context.currentNode) {
throw new Error(`Node being replaced is already removed.`);
}
if (!context.parent) {
throw new Error(`Cannot replace root node.`);
}
}
context.parent.children[context.childIndex] = context.currentNode = node;
},
removeNode(node) {
if ( !context.parent) {
throw new Error(`Cannot remove root node.`);
}
const list = context.parent.children;
const removalIndex = node
? list.indexOf(node)
: context.currentNode
? context.childIndex
: -1;
/* istanbul ignore if */
if ( removalIndex < 0) {
throw new Error(`node being removed is not a child of current parent`);
}
if (!node || node === context.currentNode) {
// current node removed
context.currentNode = null;
context.onNodeRemoved();
}
else {
// sibling node removed
if (context.childIndex > removalIndex) {
context.childIndex--;
context.onNodeRemoved();
}
}
context.parent.children.splice(removalIndex, 1);
},
onNodeRemoved: () => { },
addIdentifiers(exp) {
// identifier tracking only happens in non-browser builds.
{
if (shared.isString(exp)) {
addId(exp);
}
else if (exp.identifiers) {
exp.identifiers.forEach(addId);
}
else if (exp.type === 4 /* SIMPLE_EXPRESSION */) {
addId(exp.content);
}
}
},
removeIdentifiers(exp) {
{
if (shared.isString(exp)) {
removeId(exp);
}
else if (exp.identifiers) {
exp.identifiers.forEach(removeId);
}
else if (exp.type === 4 /* SIMPLE_EXPRESSION */) {
removeId(exp.content);
}
}
},
hoist(exp) {
context.hoists.push(exp);
const identifier = createSimpleExpression(`_hoisted_${context.hoists.length}`, false, exp.loc, true);
identifier.hoisted = exp;
return identifier;
},
cache(exp, isVNode = false) {
return createCacheExpression(++context.cached, exp, isVNode);
}
};
function addId(id) {
const { identifiers } = context;
if (identifiers[id] === undefined) {
identifiers[id] = 0;
}
identifiers[id]++;
}
function removeId(id) {
context.identifiers[id]--;
}
return context;
}
function transform(root, options) {
const context = createTransformContext(root, options);
traverseNode(root, context);
if (options.hoistStatic) {
hoistStatic(root, context);
}
if (!options.ssr) {
createRootCodegen(root, context);
}
// finalize meta information
root.helpers = [...context.helpers];
root.components = [...context.components];
root.directives = [...context.directives];
root.imports = [...context.imports];
root.hoists = context.hoists;
root.temps = context.temps;
root.cached = context.cached;
}
function createRootCodegen(root, context) {
const { helper } = context;
const { children } = root;
if (children.length === 1) {
const child = children[0];
// if the single child is an element, turn it into a block.
if (isSingleElementRoot(root, child) && child.codegenNode) {
// single element root is never hoisted so codegenNode will never be
// SimpleExpressionNode
const codegenNode = child.codegenNode;
if (codegenNode.type === 13 /* VNODE_CALL */) {
codegenNode.isBlock = true;
helper(OPEN_BLOCK);
helper(CREATE_BLOCK);
}
root.codegenNode = codegenNode;
}
else {
// - single <slot/>, IfNode, ForNode: already blocks.
// - single text node: always patched.
// root codegen falls through via genNode()
root.codegenNode = child;
}
}
else if (children.length > 1) {
// root has multiple nodes - return a fragment block.
root.codegenNode = createVNodeCall(context, helper(FRAGMENT), undefined, root.children, `${64 /* STABLE_FRAGMENT */} /* ${shared.PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`, undefined, undefined, true);
}
else ;
}
function traverseChildren(parent, context) {
let i = 0;
const nodeRemoved = () => {
i--;
};
for (; i < parent.children.length; i++) {
const child = parent.children[i];
if (shared.isString(child))
continue;
context.parent = parent;
context.childIndex = i;
context.onNodeRemoved = nodeRemoved;
traverseNode(child, context);
}
}
function traverseNode(node, context) {
context.currentNode = node;
// apply transform plugins
const { nodeTransforms } = context;
const exitFns = [];
for (let i = 0; i < nodeTransforms.length; i++) {
const onExit = nodeTransforms[i](node, context);
if (onExit) {
if (shared.isArray(onExit)) {
exitFns.push(...onExit);
}
else {
exitFns.push(onExit);
}
}
if (!context.currentNode) {
// node was removed
return;
}
else {
// node may have been replaced
node = context.currentNode;
}
}
switch (node.type) {
case 3 /* COMMENT */:
if (!context.ssr) {
// inject import for the Comment symbol, which is needed for creating
// comment nodes with `createVNode`
context.helper(CREATE_COMMENT);
}
break;
case 5 /* INTERPOLATION */:
// no need to traverse, but we need to inject toString helper
if (!context.ssr) {
context.helper(TO_DISPLAY_STRING);
}
break;
// for container types, further traverse downwards
case 9 /* IF */:
for (let i = 0; i < node.branches.length; i++) {
traverseNode(node.branches[i], context);
}
break;
case 10 /* IF_BRANCH */:
case 11 /* FOR */:
case 1 /* ELEMENT */:
case 0 /* ROOT */:
traverseChildren(node, context);
break;
}
// exit transforms
context.currentNode = node;
let i = exitFns.length;
while (i--) {
exitFns[i]();
}
}
function createStructuralDirectiveTransform(name, fn) {
const matches = shared.isString(name)
? (n) => n === name
: (n) => name.test(n);
return (node, context) => {
if (node.type === 1 /* ELEMENT */) {
const { props } = node;
// structural directive transforms are not concerned with slots
// as they are handled separately in vSlot.ts
if (node.tagType === 3 /* TEMPLATE */ && props.some(isVSlot)) {
return;
}
const exitFns = [];
for (let i = 0; i < props.length; i++) {
const prop = props[i];
if (prop.type === 7 /* DIRECTIVE */ && matches(prop.name)) {
// structural directives are removed to avoid infinite recursion
// also we remove them *before* applying so that it can further
// traverse itself in case it moves the node around
props.splice(i, 1);
i--;
const onExit = fn(node, prop, context);
if (onExit)
exitFns.push(onExit);
}
}
return exitFns;
}
};
}
const PURE_ANNOTATION = `/*#__PURE__*/`;
function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode === 'module', sourceMap: sourceMap$1 = false, filename = `template.vue.html`, scopeId = null, optimizeImports = false, runtimeGlobalName = `Vue`, runtimeModuleName = `vue`, ssr = false }) {
const context = {
mode,
prefixIdentifiers,
sourceMap: sourceMap$1,
filename,
scopeId,
optimizeImports,
runtimeGlobalName,
runtimeModuleName,
ssr,
source: ast.loc.source,
code: ``,
column: 1,
line: 1,
offset: 0,
indentLevel: 0,
pure: false,
map: undefined,
helper(key) {
return `_${helperNameMap[key]}`;
},
push(code, node) {
context.code += code;
if ( context.map) {
if (node) {
let name;
if (node.type === 4 /* SIMPLE_EXPRESSION */ && !node.isStatic) {
const content = node.content.replace(/^_ctx\./, '');
if (content !== node.content && isSimpleIdentifier(content)) {
name = content;
}
}
addMapping(node.loc.start, name);
}
advancePositionWithMutation(context, code);
if (node && node.loc !== locStub) {
addMapping(node.loc.end);
}
}
},
indent() {
newline(++context.indentLevel);
},
deindent(withoutNewLine = false) {
if (withoutNewLine) {
--context.indentLevel;
}
else {
newline(--context.indentLevel);
}
},
newline() {
newline(context.indentLevel);
}
};
function newline(n) {
context.push('\n' + ` `.repeat(n));
}
function addMapping(loc, name) {
context.map.addMapping({
name,
source: context.filename,
original: {
line: loc.line,
column: loc.column - 1 // source-map column is 0 based
},
generated: {
line: context.line,
column: context.column - 1
}
});
}
if ( sourceMap$1) {
// lazy require source-map implementation, only in non-browser builds
context.map = new sourceMap.SourceMapGenerator();
context.map.setSourceContent(filename, context.source);
}
return context;
}
function generate(ast, options = {}) {
const context = createCodegenContext(ast, options);
if (options.onContextCreated)
options.onContextCreated(context);
const { mode, push, prefixIdentifiers, indent, deindent, newline, scopeId, ssr } = context;
const hasHelpers = ast.helpers.length > 0;
const useWithBlock = !prefixIdentifiers && mode !== 'module';
const genScopeId = scopeId != null && mode === 'module';
// preambles
if ( mode === 'module') {
genModulePreamble(ast, context, genScopeId);
}
else {
genFunctionPreamble(ast, context);
}
// binding optimizations
const optimizeSources = options.bindingMetadata
? `, $props, $setup, $data, $options`
: ``;
// enter render function
if (!ssr) {
if (genScopeId) {
push(`const render = ${PURE_ANNOTATION}_withId(`);
}
push(`function render(_ctx, _cache${optimizeSources}) {`);
}
else {
if (genScopeId) {
push(`const ssrRender = ${PURE_ANNOTATION}_withId(`);
}
push(`function ssrRender(_ctx, _push, _parent, _attrs${optimizeSources}) {`);
}
indent();
if (useWithBlock) {
push(`with (_ctx) {`);
indent();
// function mode const declarations should be inside with block
// also they should be renamed to avoid collision with user properties
if (hasHelpers) {
push(`const { ${ast.helpers
.map(s => `${helperNameMap[s]}: _${helperNameMap[s]}`)
.join(', ')} } = _Vue`);
push(`\n`);
newline();
}
}
// generate asset resolution statements
if (ast.components.length) {
genAssets(ast.components, 'component', context);
if (ast.directives.length || ast.temps > 0) {
newline();
}
}
if (ast.directives.length) {
genAssets(ast.directives, 'directive', context);
if (ast.temps > 0) {
newline();
}
}
if (ast.temps > 0) {
push(`let `);
for (let i = 0; i < ast.temps; i++) {
push(`${i > 0 ? `, ` : ``}_temp${i}`);
}
}
if (ast.components.length || ast.directives.length || ast.temps) {
push(`\n`);
newline();
}
// generate the VNode tree expression
if (!ssr) {
push(`return `);
}
if (ast.codegenNode) {
genNode(ast.codegenNode, context);
}
else {
push(`null`);
}
if (useWithBlock) {
deindent();
push(`}`);
}
deindent();
push(`}`);
if (genScopeId) {
push(`)`);
}
return {
ast,
code: context.code,
// SourceMapGenerator does have toJSON() method but it's not in the types
map: context.map ? context.map.toJSON() : undefined
};
}
function genFunctionPreamble(ast, context) {
const { ssr, prefixIdentifiers, push, newline, runtimeModuleName, runtimeGlobalName } = context;
const VueBinding = ssr
? `require(${JSON.stringify(runtimeModuleName)})`
: runtimeGlobalName;
const aliasHelper = (s) => `${helperNameMap[s]}: _${helperNameMap[s]}`;
// Generate const declaration for helpers
// In prefix mode, we place the const declaration at top so it's done
// only once; But if we not prefixing, we place the declaration inside the
// with block so it doesn't incur the `in` check cost for every helper access.
if (ast.helpers.length > 0) {
if ( prefixIdentifiers) {
push(`const { ${ast.helpers.map(aliasHelper).join(', ')} } = ${VueBinding}\n`);
}
else {
// "with" mode.
// save Vue in a separate variable to avoid collision
push(`const _Vue = ${VueBinding}\n`);
// in "with" mode, helpers are declared inside the with block to avoid
// has check cost, but hoists are lifted out of the function - we need
// to provide the helper here.
if (ast.hoists.length) {
const staticHelpers = [
CREATE_VNODE,
CREATE_COMMENT,
CREATE_TEXT,
CREATE_STATIC
]
.filter(helper => ast.helpers.includes(helper))
.map(aliasHelper)
.join(', ');
push(`const { ${staticHelpers} } = _Vue\n`);
}
}
}
// generate variables for ssr helpers
if ( ast.ssrHelpers && ast.ssrHelpers.length) {
// ssr guarantees prefixIdentifier: true
push(`const { ${ast.ssrHelpers
.map(aliasHelper)
.join(', ')} } = require("@vue/server-renderer")\n`);
}
genHoists(ast.hoists, context);
newline();
push(`return `);
}
function genModulePreamble(ast, context, genScopeId) {
const { push, helper, newline, scopeId, optimizeImports, runtimeModuleName } = context;
if (genScopeId) {
ast.helpers.push(WITH_SCOPE_ID);
if (ast.hoists.length) {
ast.helpers.push(PUSH_SCOPE_ID, POP_SCOPE_ID);
}
}
// generate import statements for helpers
if (ast.helpers.length) {
if (optimizeImports) {
// when bundled with webpack with code-split, calling an import binding
// as a function leads to it being wrapped with `Object(a.b)` or `(0,a.b)`,
// incurring both payload size increase and potential perf overhead.
// therefore we assign the imports to variables (which is a constant ~50b
// cost per-component instead of scaling with template size)
push(`import { ${ast.helpers
.map(s => helperNameMap[s])
.join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`);
push(`\n// Binding optimization for webpack code-split\nconst ${ast.helpers
.map(s => `_${helperNameMap[s]} = ${helperNameMap[s]}`)
.join(', ')}\n`);
}
else {
push(`import { ${ast.helpers
.map(s => `${helperNameMap[s]} as _${helperNameMap[s]}`)
.join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`);
}
}
if (ast.ssrHelpers && ast.ssrHelpers.length) {
push(`import { ${ast.ssrHelpers
.map(s => `${helperNameMap[s]} as _${helperNameMap[s]}`)
.join(', ')} } from "@vue/server-renderer"\n`);
}
if (ast.imports.length) {
genImports(ast.imports, context);
newline();
}
if (genScopeId) {
push(`const _withId = ${PURE_ANNOTATION}${helper(WITH_SCOPE_ID)}("${scopeId}")`);
newline();
}
genHoists(ast.hoists, context);
newline();
push(`export `);
}
function genAssets(assets, type, { helper, push, newline }) {
const resolver = helper(type === 'component' ? RESOLVE_COMPONENT : RESOLVE_DIRECTIVE);
for (let i = 0; i < assets.length; i++) {
const id = assets[i];
push(`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)})`);
if (i < assets.length - 1) {
newline();
}
}
}
function genHoists(hoists, context) {
if (!hoists.length) {
return;
}
context.pure = true;
const { push, newline, helper, scopeId, mode } = context;
const genScopeId = scopeId != null && mode !== 'function';
newline();
// push scope Id before initializing hoisted vnodes so that these vnodes
// get the proper scopeId as well.
if (genScopeId) {
push(`${helper(PUSH_SCOPE_ID)}("${scopeId}")`);
newline();
}
hoists.forEach((exp, i) => {
if (exp) {
push(`const _hoisted_${i + 1} = `);
genNode(exp, context);
newline();
}
});
if (genScopeId) {
push(`${helper(POP_SCOPE_ID)}()`);
newline();
}
context.pure = false;
}
function genImports(importsOptions, context) {
if (!importsOptions.length) {
return;
}
importsOptions.forEach(imports => {
context.push(`import `);
genNode(imports.exp, context);
context.push(` from '${imports.path}'`);
context.newline();
});
}
function isText$1(n) {
return (shared.isString(n) ||
n.type === 4 /* SIMPLE_EXPRESSION */ ||
n.type === 2 /* TEXT */ ||
n.type === 5 /* INTERPOLATION */ ||
n.type === 8 /* COMPOUND_EXPRESSION */);
}
function genNodeListAsArray(nodes, context) {
const multilines = nodes.length > 3 ||
( nodes.some(n => shared.isArray(n) || !isText$1(n)));
context.push(`[`);
multilines && context.indent();
genNodeList(nodes, context, multilines);
multilines && context.deindent();
context.push(`]`);
}
function genNodeList(nodes, context, multilines = false, comma = true) {
const { push, newline } = context;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (shared.isString(node)) {
push(node);
}
else if (shared.isArray(node)) {
genNodeListAsArray(node, context);
}
else {
genNode(node, context);
}
if (i < nodes.length - 1) {
if (multilines) {
comma && push(',');
newline();
}
else {
comma && push(', ');
}
}
}
}
function genNode(node, context) {
if (shared.isString(node)) {
context.push(node);
return;
}
if (shared.isSymbol(node)) {
context.push(context.helper(node));
return;
}
switch (node.type) {
case 1 /* ELEMENT */:
case 9 /* IF */:
case 11 /* FOR */:
assert(node.codegenNode != null, `Codegen node is missing for element/if/for node. ` +
`Apply appropriate transforms first.`);
genNode(node.codegenNode, context);
break;
case 2 /* TEXT */:
genText(node, context);
break;
case 4 /* SIMPLE_EXPRESSION */:
genExpression(node, context);
break;
case 5 /* INTERPOLATION */:
genInterpolation(node, context);
break;
case 12 /* TEXT_CALL */:
genNode(node.codegenNode, context);
break;
case 8 /* COMPOUND_EXPRESSION */:
genCompoundExpression(node, context);
break;
case 3 /* COMMENT */:
genComment(node, context);
break;
case 13 /* VNODE_CALL */:
genVNodeCall(node, context);
break;
case 14 /* JS_CALL_EXPRESSION */:
genCallExpression(node, context);
break;
case 15 /* JS_OBJECT_EXPRESSION */:
genObjectExpression(node, context);
break;
case 17 /* JS_ARRAY_EXPRESSION */:
genArrayExpression(node, context);
break;
case 18 /* JS_FUNCTION_EXPRESSION */:
genFunctionExpression(node, context);
break;
case 19 /* JS_CONDITIONAL_EXPRESSION */:
genConditionalExpression(node, context);
break;
case 20 /* JS_CACHE_EXPRESSION */:
genCacheExpression(node, context);
break;
// SSR only types
case 21 /* JS_BLOCK_STATEMENT */:
genNodeList(node.body, context, true, false);
break;
case 22 /* JS_TEMPLATE_LITERAL */:
genTemplateLiteral(node, context);
break;
case 23 /* JS_IF_STATEMENT */:
genIfStatement(node, context);
break;
case 24 /* JS_ASSIGNMENT_EXPRESSION */:
genAssignmentExpression(node, context);
break;
case 25 /* JS_SEQUENCE_EXPRESSION */:
genSequenceExpression(node, context);
break;
case 26 /* JS_RETURN_STATEMENT */:
genReturnStatement(node, context);
break;
/* istanbul ignore next */
case 10 /* IF_BRANCH */:
// noop
break;
default:
{
assert(false, `unhandled codegen node type: ${node.type}`);
// make sure we exhaust all possible types
const exhaustiveCheck = node;
return exhaustiveCheck;
}
}
}
function genText(node, context) {
context.push(JSON.stringify(node.content), node);
}
function genExpression(node, context) {
const { content, isStatic } = node;
context.push(isStatic ? JSON.stringify(content) : content, node);
}
function genInterpolation(node, context) {
const { push, helper, pure } = context;
if (pure)
push(PURE_ANNOTATION);
push(`${helper(TO_DISPLAY_STRING)}(`);
genNode(node.content, context);
push(`)`);
}
function genCompoundExpression(node, context) {
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
if (shared.isString(child)) {
context.push(child);
}
else {
genNode(child, context);
}
}
}
function genExpressionAsPropertyKey(node, context) {
const { push } = context;
if (node.type === 8 /* COMPOUND_EXPRESSION */) {
push(`[`);
genCompoundExpression(node, context);
push(`]`);
}
else if (node.isStatic) {
// only quote keys if necessary
const text = isSimpleIdentifier(node.content)
? node.content
: JSON.stringify(node.content);
push(text, node);
}
else {
push(`[${node.content}]`, node);
}
}
function genComment(node, context) {
{
const { push, helper, pure } = context;
if (pure) {
push(PURE_ANNOTATION);
}
push(`${helper(CREATE_COMMENT)}(${JSON.stringify(node.content)})`, node);
}
}
function genVNodeCall(node, context) {
const { push, helper, pure } = context;
const { tag, props, children, patchFlag, dynamicProps, directives, isBlock, disableTracking } = node;
if (directives) {
push(helper(WITH_DIRECTIVES) + `(`);
}
if (isBlock) {
push(`(${helper(OPEN_BLOCK)}(${disableTracking ? `true` : ``}), `);
}
if (pure) {
push(PURE_ANNOTATION);
}
push(helper(isBlock ? CREATE_BLOCK : CREATE_VNODE) + `(`, node);
genNodeList(genNullableArgs([tag, props, children, patchFlag, dynamicProps]), context);
push(`)`);
if (isBlock) {
push(`)`);
}
if (directives) {
push(`, `);
genNode(directives, context);
push(`)`);
}
}
function genNullableArgs(args) {
let i = args.length;
while (i--) {
if (args[i] != null)
break;
}
return args.slice(0, i + 1).map(arg => arg || `null`);
}
// JavaScript
function genCallExpression(node, context) {
const { push, helper, pure } = context;
const callee = shared.isString(node.callee) ? node.callee : helper(node.callee);
if (pure) {
push(PURE_ANNOTATION);
}
push(callee + `(`, node);
genNodeList(node.arguments, context);
push(`)`);
}
function genObjectExpression(node, context) {
const { push, indent, deindent, newline } = context;
const { properties } = node;
if (!properties.length) {
push(`{}`, node);
return;
}
const multilines = properties.length > 1 ||
(
properties.some(p => p.value.type !== 4 /* SIMPLE_EXPRESSION */));
push(multilines ? `{` : `{ `);
multilines && indent();
for (let i = 0; i < properties.length; i++) {
const { key, value } = properties[i];
// key
genExpressionAsPropertyKey(key, context);
push(`: `);
// value
genNode(value, context);
if (i < properties.length - 1) {
// will only reach this if it's multilines
push(`,`);
newline();
}
}
multilines && deindent();
push(multilines ? `}` : ` }`);
}
function genArrayExpression(node, context) {
genNodeListAsArray(node.elements, context);
}
function genFunctionExpression(node, context) {
const { push, indent, deindent, scopeId, mode } = context;
const { params, returns, body, newline, isSlot } = node;
// slot functions also need to push scopeId before rendering its content
const genScopeId = isSlot && scopeId != null && mode !== 'function';
if (genScopeId) {
push(`_withId(`);
}
else if (isSlot) {
push(`_${helperNameMap[WITH_CTX]}(`);
}
push(`(`, node);
if (shared.isArray(params)) {
genNodeList(params, context);
}
else if (params) {
genNode(params, context);
}
push(`) => `);
if (newline || body) {
push(`{`);
indent();
}
if (returns) {
if (newline) {
push(`return `);
}
if (shared.isArray(returns)) {
genNodeListAsArray(returns, context);
}
else {
genNode(returns, context);
}
}
else if (body) {
genNode(body, context);
}
if (newline || body) {
deindent();
push(`}`);
}
if (genScopeId || isSlot) {
push(`)`);
}
}
function genConditionalExpression(node, context) {
const { test, consequent, alternate, newline: needNewline } = node;
const { push, indent, deindent, newline } = context;
if (test.type === 4 /* SIMPLE_EXPRESSION */) {
const needsParens = !isSimpleIdentifier(test.content);
needsParens && push(`(`);
genExpression(test, context);
needsParens && push(`)`);
}
else {
push(`(`);
genNode(test, context);
push(`)`);
}
needNewline && indent();
context.indentLevel++;
needNewline || push(` `);
push(`? `);
genNode(consequent, context);
context.indentLevel--;
needNewline && newline();
needNewline || push(` `);
push(`: `);
const isNested = alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */;
if (!isNested) {
context.indentLevel++;
}
genNode(alternate, context);
if (!isNested) {
context.indentLevel--;
}
needNewline && deindent(true /* without newline */);
}
function genCacheExpression(node, context) {
const { push, helper, indent, deindent, newline } = context;
push(`_cache[${node.index}] || (`);
if (node.isVNode) {
indent();
push(`${helper(SET_BLOCK_TRACKING)}(-1),`);
newline();
}
push(`_cache[${node.index}] = `);
genNode(node.value, context);
if (node.isVNode) {
push(`,`);
newline();
push(`${helper(SET_BLOCK_TRACKING)}(1),`);
newline();
push(`_cache[${node.index}]`);
deindent();
}
push(`)`);
}
function genTemplateLiteral(node, context) {
const { push, indent, deindent } = context;
push('`');
const l = node.elements.length;
const multilines = l > 3;
for (let i = 0; i < l; i++) {
const e = node.elements[i];
if (shared.isString(e)) {
push(e.replace(/(`|\$|\\)/g, '\\$1'));
}
else {
push('${');
if (multilines)
indent();
genNode(e, context);
if (multilines)
deindent();
push('}');
}
}
push('`');
}
function genIfStatement(node, context) {
const { push, indent, deindent } = context;
const { test, consequent, alternate } = node;
push(`if (`);
genNode(test, context);
push(`) {`);
indent();
genNode(consequent, context);
deindent();
push(`}`);
if (alternate) {
push(` else `);
if (alternate.type === 23 /* JS_IF_STATEMENT */) {
genIfStatement(alternate, context);
}
else {
push(`{`);
indent();
genNode(alternate, context);
deindent();
push(`}`);
}
}
}
function genAssignmentExpression(node, context) {
genNode(node.left, context);
context.push(` = `);
genNode(node.right, context);
}
function genSequenceExpression(node, context) {
context.push(`(`);
genNodeList(node.expressions, context);
context.push(`)`);
}
function genReturnStatement({ returns }, context) {
context.push(`return `);
if (shared.isArray(returns)) {
genNodeListAsArray(returns, context);
}
else {
genNode(returns, context);
}
}
const isLiteralWhitelisted = /*#__PURE__*/ shared.makeMap('true,false,null,this');
const transformExpression = (node, context) => {
if (node.type === 5 /* INTERPOLATION */) {
node.content = processExpression(node.content, context);
}
else if (node.type === 1 /* ELEMENT */) {
// handle directives on element
for (let i = 0; i < node.props.length; i++) {
const dir = node.props[i];
// do not process for v-on & v-for since they are special handled
if (dir.type === 7 /* DIRECTIVE */ && dir.name !== 'for') {
const exp = dir.exp;
const arg = dir.arg;
// do not process exp if this is v-on:arg - we need special handling
// for wrapping inline statements.
if (exp &&
exp.type === 4 /* SIMPLE_EXPRESSION */ &&
!(dir.name === 'on' && arg)) {
dir.exp = processExpression(exp, context,
// slot args must be processed as function params
dir.name === 'slot');
}
if (arg && arg.type === 4 /* SIMPLE_EXPRESSION */ && !arg.isStatic) {
dir.arg = processExpression(arg, context);
}
}
}
}
};
// Important: since this function uses Node.js only dependencies, it should
// always be used with a leading !false check so that it can be
// tree-shaken from the browser build.
function processExpression(node, context,
// some expressions like v-slot props & v-for aliases should be parsed as
// function params
asParams = false,
// v-on handler values may contain multiple statements
asRawStatements = false) {
if (!context.prefixIdentifiers || !node.content.trim()) {
return node;
}
const { bindingMetadata } = context;
const prefix = (raw) => {
const source = shared.hasOwn(bindingMetadata, raw)
? `$` + bindingMetadata[raw]
: `_ctx`;
return `${source}.${raw}`;
};
// fast path if expression is a simple identifier.
const rawExp = node.content;
// bail on parens to prevent any possible function invocations.
const bailConstant = rawExp.indexOf(`(`) > -1;
if (isSimpleIdentifier(rawExp)) {
if (!asParams &&
!context.identifiers[rawExp] &&
!shared.isGloballyWhitelisted(rawExp) &&
!isLiteralWhitelisted(rawExp)) {
node.content = prefix(rawExp);
}
else if (!context.identifiers[rawExp] && !bailConstant) {
// mark node constant for hoisting unless it's referring a scope variable
node.isConstant = true;
}
return node;
}
let ast;
// exp needs to be parsed differently:
// 1. Multiple inline statements (v-on, with presence of `;`): parse as raw
// exp, but make sure to pad with spaces for consistent ranges
// 2. Expressions: wrap with parens (for e.g. object expressions)
// 3. Function arguments (v-for, v-slot): place in a function argument position
const source = asRawStatements
? ` ${rawExp} `
: `(${rawExp})${asParams ? `=>{}` : ``}`;
try {
ast = parser.parse(source, {
plugins: [...context.expressionPlugins, ...shared.babelParserDefaultPlugins]
}).program;
}
catch (e) {
context.onError(createCompilerError(43 /* X_INVALID_EXPRESSION */, node.loc, undefined, e.message));
return node;
}
const ids = [];
const knownIds = Object.create(context.identifiers);
const isDuplicate = (node) => ids.some(id => id.start === node.start);
estreeWalker.walk(ast, {
enter(node, parent) {
if (node.type === 'Identifier') {
if (!isDuplicate(node)) {
const needPrefix = shouldPrefix(node, parent);
if (!knownIds[node.name] && needPrefix) {
if (isPropertyShorthand(node, parent)) {
// property shorthand like { foo }, we need to add the key since we
// rewrite the value
node.prefix = `${node.name}: `;
}
node.name = prefix(node.name);
ids.push(node);
}
else if (!isStaticPropertyKey(node, parent)) {
// The identifier is considered constant unless it's pointing to a
// scope variable (a v-for alias, or a v-slot prop)
if (!(needPrefix && knownIds[node.name]) && !bailConstant) {
node.isConstant = true;
}
// also generate sub-expressions for other identifiers for better
// source map support. (except for property keys which are static)
ids.push(node);
}
}
}
else if (isFunction(node)) {
// walk function expressions and add its arguments to known identifiers
// so that we don't prefix them
node.params.forEach(p => estreeWalker.walk(p, {
enter(child, parent) {
if (child.type === 'Identifier' &&
// do not record as scope variable if is a destructured key
!isStaticPropertyKey(child, parent) &&
// do not record if this is a default value
// assignment of a destructured variable
!(parent &&
parent.type === 'AssignmentPattern' &&
parent.right === child)) {
const { name } = child;
if (node.scopeIds && node.scopeIds.has(name)) {
return;
}
if (name in knownIds) {
knownIds[name]++;
}
else {
knownIds[name] = 1;
}
(node.scopeIds || (node.scopeIds = new Set())).add(name);
}
}
}));
}
},
leave(node) {
if (node !== ast.body[0].expression && node.scopeIds) {
node.scopeIds.forEach((id) => {
knownIds[id]--;
if (knownIds[id] === 0) {
delete knownIds[id];
}
});
}
}
});
// We break up the compound expression into an array of strings and sub
// expressions (for identifiers that have been prefixed). In codegen, if
// an ExpressionNode has the `.children` property, it will be used instead of
// `.content`.
const children = [];
ids.sort((a, b) => a.start - b.start);
ids.forEach((id, i) => {
// range is offset by -1 due to the wrapping parens when parsed
const start = id.start - 1;
const end = id.end - 1;
const last = ids[i - 1];
const leadingText = rawExp.slice(last ? last.end - 1 : 0, start);
if (leadingText.length || id.prefix) {
children.push(leadingText + (id.prefix || ``));
}
const source = rawExp.slice(start, end);
children.push(createSimpleExpression(id.name, false, {
source,
start: advancePositionWithClone(node.loc.start, source, start),
end: advancePositionWithClone(node.loc.start, source, end)
}, id.isConstant /* isConstant */));
if (i === ids.length - 1 && end < rawExp.length) {
children.push(rawExp.slice(end));
}
});
let ret;
if (children.length) {
ret = createCompoundExpression(children, node.loc);
}
else {
ret = node;
ret.isConstant = !bailConstant;
}
ret.identifiers = Object.keys(knownIds);
return ret;
}
const isFunction = (node) => {
return /Function(?:Expression|Declaration)$|Method$/.test(node.type);
};
const isStaticProperty = (node) => node &&
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
!node.computed;
const isPropertyShorthand = (node, parent) => {
return (isStaticProperty(parent) &&
parent.value === node &&
parent.key.type === 'Identifier' &&
parent.key.name === node.name &&
parent.key.start === node.start);
};
const isStaticPropertyKey = (node, parent) => isStaticProperty(parent) && parent.key === node;
function shouldPrefix(identifier, parent) {
if (!(isFunction(parent) &&
// not id of a FunctionDeclaration
(parent.id === identifier ||
// not a params of a function
parent.params.includes(identifier))) &&
// not a key of Property
!isStaticPropertyKey(identifier, parent) &&
// not a property of a MemberExpression
!((parent.type === 'MemberExpression' ||
parent.type === 'OptionalMemberExpression') &&
parent.property === identifier &&
!parent.computed) &&
// not in an Array destructure pattern
!(parent.type === 'ArrayPattern') &&
// skip whitelisted globals
!shared.isGloballyWhitelisted(identifier.name) &&
// special case for webpack compilation
identifier.name !== `require` &&
// is a special keyword but parsed as identifier
identifier.name !== `arguments`) {
return true;
}
}
const transformIf = createStructuralDirectiveTransform(/^(if|else|else-if)$/, (node, dir, context) => {
return processIf(node, dir, context, (ifNode, branch, isRoot) => {
// #1587: We need to dynamically increment the key based on the current
// node's sibling nodes, since chained v-if/else branches are
// rendered at the same depth
const siblings = context.parent.children;
let i = siblings.indexOf(ifNode);
let key = 0;
while (i-- >= 0) {
const sibling = siblings[i];
if (sibling && sibling.type === 9 /* IF */) {
key += sibling.branches.length;
}
}
// Exit callback. Complete the codegenNode when all children have been
// transformed.
return () => {
if (isRoot) {
ifNode.codegenNode = createCodegenNodeForBranch(branch, key, context);
}
else {
// attach this branch's codegen node to the v-if root.
let parentCondition = ifNode.codegenNode;
while (parentCondition.alternate.type ===
19 /* JS_CONDITIONAL_EXPRESSION */) {
parentCondition = parentCondition.alternate;
}
parentCondition.alternate = createCodegenNodeForBranch(branch, key + ifNode.branches.length - 1, context);
}
};
});
});
// target-agnostic transform used for both Client and SSR
function processIf(node, dir, context, processCodegen) {
if (dir.name !== 'else' &&
(!dir.exp || !dir.exp.content.trim())) {
const loc = dir.exp ? dir.exp.loc : node.loc;
context.onError(createCompilerError(27 /* X_V_IF_NO_EXPRESSION */, dir.loc));
dir.exp = createSimpleExpression(`true`, false, loc);
}
if ( context.prefixIdentifiers && dir.exp) {
// dir.exp can only be simple expression because vIf transform is applied
// before expression transform.
dir.exp = processExpression(dir.exp, context);
}
if (dir.name === 'if') {
const branch = createIfBranch(node, dir);
const ifNode = {
type: 9 /* IF */,
loc: node.loc,
branches: [branch]
};
context.replaceNode(ifNode);
if (processCodegen) {
return processCodegen(ifNode, branch, true);
}
}
else {
// locate the adjacent v-if
const siblings = context.parent.children;
const comments = [];
let i = siblings.indexOf(node);
while (i-- >= -1) {
const sibling = siblings[i];
if ( sibling && sibling.type === 3 /* COMMENT */) {
context.removeNode(sibling);
comments.unshift(sibling);
continue;
}
if (sibling && sibling.type === 9 /* IF */) {
// move the node to the if node's branches
context.removeNode();
const branch = createIfBranch(node, dir);
if ( comments.length) {
branch.children = [...comments, ...branch.children];
}
// check if user is forcing same key on different branches
{
const key = branch.userKey;
if (key) {
sibling.branches.forEach(({ userKey }) => {
if (isSameKey(userKey, key)) {
context.onError(createCompilerError(28 /* X_V_IF_SAME_KEY */, branch.userKey.loc));
}
});
}
}
sibling.branches.push(branch);
const onExit = processCodegen && processCodegen(sibling, branch, false);
// since the branch was removed, it will not be traversed.
// make sure to traverse here.
traverseNode(branch, context);
// call on exit
if (onExit)
onExit();
// make sure to reset currentNode after traversal to indicate this
// node has been removed.
context.currentNode = null;
}
else {
context.onError(createCompilerError(29 /* X_V_ELSE_NO_ADJACENT_IF */, node.loc));
}
break;
}
}
}
function createIfBranch(node, dir) {
return {
type: 10 /* IF_BRANCH */,
loc: node.loc,
condition: dir.name === 'else' ? undefined : dir.exp,
children: node.tagType === 3 /* TEMPLATE */ && !findDir(node, 'for')
? node.children
: [node],
userKey: findProp(node, `key`)
};
}
function createCodegenNodeForBranch(branch, keyIndex, context) {
if (branch.condition) {
return createConditionalExpression(branch.condition, createChildrenCodegenNode(branch, keyIndex, context),
// make sure to pass in asBlock: true so that the comment node call
// closes the current block.
createCallExpression(context.helper(CREATE_COMMENT), [
'"v-if"' ,
'true'
]));
}
else {
return createChildrenCodegenNode(branch, keyIndex, context);
}
}
function createChildrenCodegenNode(branch, keyIndex, context) {
const { helper } = context;
const keyProperty = createObjectProperty(`key`, createSimpleExpression(`${keyIndex}`, false, locStub, true));
const { children } = branch;
const firstChild = children[0];
const needFragmentWrapper = children.length !== 1 || firstChild.type !== 1 /* ELEMENT */;
if (needFragmentWrapper) {
if (children.length === 1 && firstChild.type === 11 /* FOR */) {
// optimize away nested fragments when child is a ForNode
const vnodeCall = firstChild.codegenNode;
injectProp(vnodeCall, keyProperty, context);
return vnodeCall;
}
else {
return createVNodeCall(context, helper(FRAGMENT), createObjectExpression([keyProperty]), children, `${64 /* STABLE_FRAGMENT */} /* ${shared.PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`, undefined, undefined, true, false, branch.loc);
}
}
else {
const vnodeCall = firstChild
.codegenNode;
// Change createVNode to createBlock.
if (vnodeCall.type === 13 /* VNODE_CALL */) {
vnodeCall.isBlock = true;
helper(OPEN_BLOCK);
helper(CREATE_BLOCK);
}
// inject branch key
injectProp(vnodeCall, keyProperty, context);
return vnodeCall;
}
}
function isSameKey(a, b) {
if (!a || a.type !== b.type) {
return false;
}
if (a.type === 6 /* ATTRIBUTE */) {
if (a.value.content !== b.value.content) {
return false;
}
}
else {
// directive
const exp = a.exp;
const branchExp = b.exp;
if (exp.type !== branchExp.type) {
return false;
}
if (exp.type !== 4 /* SIMPLE_EXPRESSION */ ||
(exp.isStatic !== branchExp.isStatic ||
exp.content !== branchExp.content)) {
return false;
}
}
return true;
}
const transformFor = createStructuralDirectiveTransform('for', (node, dir, context) => {
const { helper } = context;
return processFor(node, dir, context, forNode => {
// create the loop render function expression now, and add the
// iterator on exit after all children have been traversed
const renderExp = createCallExpression(helper(RENDER_LIST), [
forNode.source
]);
const keyProp = findProp(node, `key`);
const keyProperty = keyProp
? createObjectProperty(`key`, keyProp.type === 6 /* ATTRIBUTE */
? createSimpleExpression(keyProp.value.content, true)
: keyProp.exp)
: null;
if ( context.prefixIdentifiers && keyProperty) {
// #2085 process :key expression needs to be processed in order for it
// to behave consistently for <template v-for> and <div v-for>.
// In the case of `<template v-for>`, the node is discarded and never
// traversed so its key expression won't be processed by the normal
// transforms.
keyProperty.value = processExpression(keyProperty.value, context);
}
const isStableFragment = forNode.source.type === 4 /* SIMPLE_EXPRESSION */ &&
forNode.source.isConstant;
const fragmentFlag = isStableFragment
? 64 /* STABLE_FRAGMENT */
: keyProp
? 128 /* KEYED_FRAGMENT */
: 256 /* UNKEYED_FRAGMENT */;
forNode.codegenNode = createVNodeCall(context, helper(FRAGMENT), undefined, renderExp, `${fragmentFlag} /* ${shared.PatchFlagNames[fragmentFlag]} */`, undefined, undefined, true /* isBlock */, !isStableFragment /* disableTracking */, node.loc);
return () => {
// finish the codegen now that all children have been traversed
let childBlock;
const isTemplate = isTemplateNode(node);
const { children } = forNode;
// check <template v-for> key placement
if ( isTemplate) {
node.children.some(c => {
if (c.type === 1 /* ELEMENT */) {
const key = findProp(c, 'key');
if (key) {
context.onError(createCompilerError(32 /* X_V_FOR_TEMPLATE_KEY_PLACEMENT */, key.loc));
return true;
}
}
});
}
const needFragmentWrapper = children.length !== 1 || children[0].type !== 1 /* ELEMENT */;
const slotOutlet = isSlotOutlet(node)
? node
: isTemplate &&
node.children.length === 1 &&
isSlotOutlet(node.children[0])
? node.children[0] // api-extractor somehow fails to infer this
: null;
if (slotOutlet) {
// <slot v-for="..."> or <template v-for="..."><slot/></template>
childBlock = slotOutlet.codegenNode;
if (isTemplate && keyProperty) {
// <template v-for="..." :key="..."><slot/></template>
// we need to inject the key to the renderSlot() call.
// the props for renderSlot is passed as the 3rd argument.
injectProp(childBlock, keyProperty, context);
}
}
else if (needFragmentWrapper) {
// <template v-for="..."> with text or multi-elements
// should generate a fragment block for each loop
childBlock = createVNodeCall(context, helper(FRAGMENT), keyProperty ? createObjectExpression([keyProperty]) : undefined, node.children, `${64 /* STABLE_FRAGMENT */} /* ${shared.PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`, undefined, undefined, true);
}
else {
// Normal element v-for. Directly use the child's codegenNode
// but mark it as a block.
childBlock = children[0]
.codegenNode;
if (isTemplate && keyProperty) {
injectProp(childBlock, keyProperty, context);
}
childBlock.isBlock = !isStableFragment;
if (childBlock.isBlock) {
helper(OPEN_BLOCK);
helper(CREATE_BLOCK);
}
}
renderExp.arguments.push(createFunctionExpression(createForLoopParams(forNode.parseResult), childBlock, true /* force newline */));
};
});
});
// target-agnostic transform used for both Client and SSR
function processFor(node, dir, context, processCodegen) {
if (!dir.exp) {
context.onError(createCompilerError(30 /* X_V_FOR_NO_EXPRESSION */, dir.loc));
return;
}
const parseResult = parseForExpression(
// can only be simple expression because vFor transform is applied
// before expression transform.
dir.exp, context);
if (!parseResult) {
context.onError(createCompilerError(31 /* X_V_FOR_MALFORMED_EXPRESSION */, dir.loc));
return;
}
const { addIdentifiers, removeIdentifiers, scopes } = context;
const { source, value, key, index } = parseResult;
const forNode = {
type: 11 /* FOR */,
loc: dir.loc,
source,
valueAlias: value,
keyAlias: key,
objectIndexAlias: index,
parseResult,
children: isTemplateNode(node) ? node.children : [node]
};
context.replaceNode(forNode);
// bookkeeping
scopes.vFor++;
if ( context.prefixIdentifiers) {
// scope management
// inject identifiers to context
value && addIdentifiers(value);
key && addIdentifiers(key);
index && addIdentifiers(index);
}
const onExit = processCodegen && processCodegen(forNode);
return () => {
scopes.vFor--;
if ( context.prefixIdentifiers) {
value && removeIdentifiers(value);
key && removeIdentifiers(key);
index && removeIdentifiers(index);
}
if (onExit)
onExit();
};
}
const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/;
// This regex doesn't cover the case if key or index aliases have destructuring,
// but those do not make sense in the first place, so this works in practice.
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;
const stripParensRE = /^\(|\)$/g;
function parseForExpression(input, context) {
const loc = input.loc;
const exp = input.content;
const inMatch = exp.match(forAliasRE);
if (!inMatch)
return;
const [, LHS, RHS] = inMatch;
const result = {
source: createAliasExpression(loc, RHS.trim(), exp.indexOf(RHS, LHS.length)),
value: undefined,
key: undefined,
index: undefined
};
if ( context.prefixIdentifiers) {
result.source = processExpression(result.source, context);
}
let valueContent = LHS.trim()
.replace(stripParensRE, '')
.trim();
const trimmedOffset = LHS.indexOf(valueContent);
const iteratorMatch = valueContent.match(forIteratorRE);
if (iteratorMatch) {
valueContent = valueContent.replace(forIteratorRE, '').trim();
const keyContent = iteratorMatch[1].trim();
let keyOffset;
if (keyContent) {
keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length);
result.key = createAliasExpression(loc, keyContent, keyOffset);
if ( context.prefixIdentifiers) {
result.key = processExpression(result.key, context, true);
}
}
if (iteratorMatch[2]) {
const indexContent = iteratorMatch[2].trim();
if (indexContent) {
result.index = createAliasExpression(loc, indexContent, exp.indexOf(indexContent, result.key
? keyOffset + keyContent.length
: trimmedOffset + valueContent.length));
if ( context.prefixIdentifiers) {
result.index = processExpression(result.index, context, true);
}
}
}
}
if (valueContent) {
result.value = createAliasExpression(loc, valueContent, trimmedOffset);
if ( context.prefixIdentifiers) {
result.value = processExpression(result.value, context, true);
}
}
return result;
}
function createAliasExpression(range, content, offset) {
return createSimpleExpression(content, false, getInnerRange(range, offset, content.length));
}
function createForLoopParams({ value, key, index }) {
const params = [];
if (value) {
params.push(value);
}
if (key) {
if (!value) {
params.push(createSimpleExpression(`_`, false));
}
params.push(key);
}
if (index) {
if (!key) {
if (!value) {
params.push(createSimpleExpression(`_`, false));
}
params.push(createSimpleExpression(`__`, false));
}
params.push(index);
}
return params;
}
const defaultFallback = createSimpleExpression(`undefined`, false);
// A NodeTransform that:
// 1. Tracks scope identifiers for scoped slots so that they don't get prefixed
// by transformExpression. This is only applied in non-browser builds with
// { prefixIdentifiers: true }.
// 2. Track v-slot depths so that we know a slot is inside another slot.
// Note the exit callback is executed before buildSlots() on the same node,
// so only nested slots see positive numbers.
const trackSlotScopes = (node, context) => {
if (node.type === 1 /* ELEMENT */ &&
(node.tagType === 1 /* COMPONENT */ ||
node.tagType === 3 /* TEMPLATE */)) {
// We are only checking non-empty v-slot here
// since we only care about slots that introduce scope variables.
const vSlot = findDir(node, 'slot');
if (vSlot) {
const slotProps = vSlot.exp;
if ( context.prefixIdentifiers) {
slotProps && context.addIdentifiers(slotProps);
}
context.scopes.vSlot++;
return () => {
if ( context.prefixIdentifiers) {
slotProps && context.removeIdentifiers(slotProps);
}
context.scopes.vSlot--;
};
}
}
};
// A NodeTransform that tracks scope identifiers for scoped slots with v-for.
// This transform is only applied in non-browser builds with { prefixIdentifiers: true }
const trackVForSlotScopes = (node, context) => {
let vFor;
if (isTemplateNode(node) &&
node.props.some(isVSlot) &&
(vFor = findDir(node, 'for'))) {
const result = (vFor.parseResult = parseForExpression(vFor.exp, context));
if (result) {
const { value, key, index } = result;
const { addIdentifiers, removeIdentifiers } = context;
value && addIdentifiers(value);
key && addIdentifiers(key);
index && addIdentifiers(index);
return () => {
value && removeIdentifiers(value);
key && removeIdentifiers(key);
index && removeIdentifiers(index);
};
}
}
};
const buildClientSlotFn = (props, children, loc) => createFunctionExpression(props, children, false /* newline */, true /* isSlot */, children.length ? children[0].loc : loc);
// Instead of being a DirectiveTransform, v-slot processing is called during
// transformElement to build the slots object for a component.
function buildSlots(node, context, buildSlotFn = buildClientSlotFn) {
context.helper(WITH_CTX);
const { children, loc } = node;
const slotsProperties = [];
const dynamicSlots = [];
const buildDefaultSlotProperty = (props, children) => createObjectProperty(`default`, buildSlotFn(props, children, loc));
// If the slot is inside a v-for or another v-slot, force it to be dynamic
// since it likely uses a scope variable.
let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0;
// with `prefixIdentifiers: true`, this can be further optimized to make
// it dynamic only when the slot actually uses the scope variables.
if ( !context.ssr && context.prefixIdentifiers) {
hasDynamicSlots = hasScopeRef(node, context.identifiers);
}
// 1. Check for slot with slotProps on component itself.
// <Comp v-slot="{ prop }"/>
const onComponentSlot = findDir(node, 'slot', true);
if (onComponentSlot) {
const { arg, exp } = onComponentSlot;
if (arg && !isStaticExp(arg)) {
hasDynamicSlots = true;
}
slotsProperties.push(createObjectProperty(arg || createSimpleExpression('default', true), buildSlotFn(exp, children, loc)));
}
// 2. Iterate through children and check for template slots
// <template v-slot:foo="{ prop }">
let hasTemplateSlots = false;
let hasNamedDefaultSlot = false;
const implicitDefaultChildren = [];
const seenSlotNames = new Set();
for (let i = 0; i < children.length; i++) {
const slotElement = children[i];
let slotDir;
if (!isTemplateNode(slotElement) ||
!(slotDir = findDir(slotElement, 'slot', true))) {
// not a <template v-slot>, skip.
if (slotElement.type !== 3 /* COMMENT */) {
implicitDefaultChildren.push(slotElement);
}
continue;
}
if (onComponentSlot) {
// already has on-component slot - this is incorrect usage.
context.onError(createCompilerError(36 /* X_V_SLOT_MIXED_SLOT_USAGE */, slotDir.loc));
break;
}
hasTemplateSlots = true;
const { children: slotChildren, loc: slotLoc } = slotElement;
const { arg: slotName = createSimpleExpression(`default`, true), exp: slotProps, loc: dirLoc } = slotDir;
// check if name is dynamic.
let staticSlotName;
if (isStaticExp(slotName)) {
staticSlotName = slotName ? slotName.content : `default`;
}
else {
hasDynamicSlots = true;
}
const slotFunction = buildSlotFn(slotProps, slotChildren, slotLoc);
// check if this slot is conditional (v-if/v-for)
let vIf;
let vElse;
let vFor;
if ((vIf = findDir(slotElement, 'if'))) {
hasDynamicSlots = true;
dynamicSlots.push(createConditionalExpression(vIf.exp, buildDynamicSlot(slotName, slotFunction), defaultFallback));
}
else if ((vElse = findDir(slotElement, /^else(-if)?$/, true /* allowEmpty */))) {
// find adjacent v-if
let j = i;
let prev;
while (j--) {
prev = children[j];
if (prev.type !== 3 /* COMMENT */) {
break;
}
}
if (prev && isTemplateNode(prev) && findDir(prev, 'if')) {
// remove node
children.splice(i, 1);
i--;
// attach this slot to previous conditional
let conditional = dynamicSlots[dynamicSlots.length - 1];
while (conditional.alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {
conditional = conditional.alternate;
}
conditional.alternate = vElse.exp
? createConditionalExpression(vElse.exp, buildDynamicSlot(slotName, slotFunction), defaultFallback)
: buildDynamicSlot(slotName, slotFunction);
}
else {
context.onError(createCompilerError(29 /* X_V_ELSE_NO_ADJACENT_IF */, vElse.loc));
}
}
else if ((vFor = findDir(slotElement, 'for'))) {
hasDynamicSlots = true;
const parseResult = vFor.parseResult ||
parseForExpression(vFor.exp, context);
if (parseResult) {
// Render the dynamic slots as an array and add it to the createSlot()
// args. The runtime knows how to handle it appropriately.
dynamicSlots.push(createCallExpression(context.helper(RENDER_LIST), [
parseResult.source,
createFunctionExpression(createForLoopParams(parseResult), buildDynamicSlot(slotName, slotFunction), true /* force newline */)
]));
}
else {
context.onError(createCompilerError(31 /* X_V_FOR_MALFORMED_EXPRESSION */, vFor.loc));
}
}
else {
// check duplicate static names
if (staticSlotName) {
if (seenSlotNames.has(staticSlotName)) {
context.onError(createCompilerError(37 /* X_V_SLOT_DUPLICATE_SLOT_NAMES */, dirLoc));
continue;
}
seenSlotNames.add(staticSlotName);
if (staticSlotName === 'default') {
hasNamedDefaultSlot = true;
}
}
slotsProperties.push(createObjectProperty(slotName, slotFunction));
}
}
if (!onComponentSlot) {
if (!hasTemplateSlots) {
// implicit default slot (on component)
slotsProperties.push(buildDefaultSlotProperty(undefined, children));
}
else if (implicitDefaultChildren.length) {
// implicit default slot (mixed with named slots)
if (hasNamedDefaultSlot) {
context.onError(createCompilerError(38 /* X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN */, implicitDefaultChildren[0].loc));
}
else {
slotsProperties.push(buildDefaultSlotProperty(undefined, implicitDefaultChildren));
}
}
}
const slotFlag = hasDynamicSlots
? 2 /* DYNAMIC */
: hasForwardedSlots(node.children)
? 3 /* FORWARDED */
: 1 /* STABLE */;
let slots = createObjectExpression(slotsProperties.concat(createObjectProperty(`_`,
// 2 = compiled but dynamic = can skip normalization, but must run diff
// 1 = compiled and static = can skip normalization AND diff as optimized
createSimpleExpression('' + slotFlag, false))), loc);
if (dynamicSlots.length) {
slots = createCallExpression(context.helper(CREATE_SLOTS), [
slots,
createArrayExpression(dynamicSlots)
]);
}
return {
slots,
hasDynamicSlots
};
}
function buildDynamicSlot(name, fn) {
return createObjectExpression([
createObjectProperty(`name`, name),
createObjectProperty(`fn`, fn)
]);
}
function hasForwardedSlots(children) {
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child.type === 1 /* ELEMENT */) {
if (child.tagType === 2 /* SLOT */ ||
(child.tagType === 0 /* ELEMENT */ &&
hasForwardedSlots(child.children))) {
return true;
}
}
}
return false;
}
// some directive transforms (e.g. v-model) may return a symbol for runtime
// import, which should be used instead of a resolveDirective call.
const directiveImportMap = new WeakMap();
// generate a JavaScript AST for this element's codegen
const transformElement = (node, context) => {
if (!(node.type === 1 /* ELEMENT */ &&
(node.tagType === 0 /* ELEMENT */ ||
node.tagType === 1 /* COMPONENT */))) {
return;
}
// perform the work on exit, after all child expressions have been
// processed and merged.
return function postTransformElement() {
const { tag, props } = node;
const isComponent = node.tagType === 1 /* COMPONENT */;
// The goal of the transform is to create a codegenNode implementing the
// VNodeCall interface.
const vnodeTag = isComponent
? resolveComponentType(node, context)
: `"${tag}"`;
const isDynamicComponent = shared.isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT;
let vnodeProps;
let vnodeChildren;
let vnodePatchFlag;
let patchFlag = 0;
let vnodeDynamicProps;
let dynamicPropNames;
let vnodeDirectives;
let shouldUseBlock =
// dynamic component may resolve to plain elements
isDynamicComponent ||
vnodeTag === TELEPORT ||
vnodeTag === SUSPENSE ||
(!isComponent &&
// <svg> and <foreignObject> must be forced into blocks so that block
// updates inside get proper isSVG flag at runtime. (#639, #643)
// This is technically web-specific, but splitting the logic out of core
// leads to too much unnecessary complexity.
(tag === 'svg' ||
tag === 'foreignObject' ||
// #938: elements with dynamic keys should be forced into blocks
findProp(node, 'key', true)));
// props
if (props.length > 0) {
const propsBuildResult = buildProps(node, context);
vnodeProps = propsBuildResult.props;
patchFlag = propsBuildResult.patchFlag;
dynamicPropNames = propsBuildResult.dynamicPropNames;
const directives = propsBuildResult.directives;
vnodeDirectives =
directives && directives.length
? createArrayExpression(directives.map(dir => buildDirectiveArgs(dir, context)))
: undefined;
}
// children
if (node.children.length > 0) {
if (vnodeTag === KEEP_ALIVE) {
// Although a built-in component, we compile KeepAlive with raw children
// instead of slot functions so that it can be used inside Transition
// or other Transition-wrapping HOCs.
// To ensure correct updates with block optimizations, we need to:
// 1. Force keep-alive into a block. This avoids its children being
// collected by a parent block.
shouldUseBlock = true;
// 2. Force keep-alive to always be updated, since it uses raw children.
patchFlag |= 1024 /* DYNAMIC_SLOTS */;
if ( node.children.length > 1) {
context.onError(createCompilerError(44 /* X_KEEP_ALIVE_INVALID_CHILDREN */, {
start: node.children[0].loc.start,
end: node.children[node.children.length - 1].loc.end,
source: ''
}));
}
}
const shouldBuildAsSlots = isComponent &&
// Teleport is not a real component and has dedicated runtime handling
vnodeTag !== TELEPORT &&
// explained above.
vnodeTag !== KEEP_ALIVE;
if (shouldBuildAsSlots) {
const { slots, hasDynamicSlots } = buildSlots(node, context);
vnodeChildren = slots;
if (hasDynamicSlots) {
patchFlag |= 1024 /* DYNAMIC_SLOTS */;
}
}
else if (node.children.length === 1 && vnodeTag !== TELEPORT) {
const child = node.children[0];
const type = child.type;
// check for dynamic text children
const hasDynamicTextChild = type === 5 /* INTERPOLATION */ ||
type === 8 /* COMPOUND_EXPRESSION */;
if (hasDynamicTextChild && !getStaticType(child)) {
patchFlag |= 1 /* TEXT */;
}
// pass directly if the only child is a text node
// (plain / interpolation / expression)
if (hasDynamicTextChild || type === 2 /* TEXT */) {
vnodeChildren = child;
}
else {
vnodeChildren = node.children;
}
}
else {
vnodeChildren = node.children;
}
}
// patchFlag & dynamicPropNames
if (patchFlag !== 0) {
{
if (patchFlag < 0) {
// special flags (negative and mutually exclusive)
vnodePatchFlag = patchFlag + ` /* ${shared.PatchFlagNames[patchFlag]} */`;
}
else {
// bitwise flags
const flagNames = Object.keys(shared.PatchFlagNames)
.map(Number)
.filter(n => n > 0 && patchFlag & n)
.map(n => shared.PatchFlagNames[n])
.join(`, `);
vnodePatchFlag = patchFlag + ` /* ${flagNames} */`;
}
}
if (dynamicPropNames && dynamicPropNames.length) {
vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames);
}
}
node.codegenNode = createVNodeCall(context, vnodeTag, vnodeProps, vnodeChildren, vnodePatchFlag, vnodeDynamicProps, vnodeDirectives, !!shouldUseBlock, false /* disableTracking */, node.loc);
};
};
function resolveComponentType(node, context, ssr = false) {
const { tag } = node;
// 1. dynamic component
const isProp = node.tag === 'component' ? findProp(node, 'is') : findDir(node, 'is');
if (isProp) {
const exp = isProp.type === 6 /* ATTRIBUTE */
? isProp.value && createSimpleExpression(isProp.value.content, true)
: isProp.exp;
if (exp) {
return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
exp
]);
}
}
// 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)
const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);
if (builtIn) {
// built-ins are simply fallthroughs / have special handling during ssr
// no we don't need to import their runtime equivalents
if (!ssr)
context.helper(builtIn);
return builtIn;
}
// 3. user component (from setup bindings)
if (context.bindingMetadata[tag] === 'setup') {
return `$setup[${JSON.stringify(tag)}]`;
}
// 4. user component (resolve)
context.helper(RESOLVE_COMPONENT);
context.components.add(tag);
return toValidAssetId(tag, `component`);
}
function buildProps(node, context, props = node.props, ssr = false) {
const { tag, loc: elementLoc } = node;
const isComponent = node.tagType === 1 /* COMPONENT */;
let properties = [];
const mergeArgs = [];
const runtimeDirectives = [];
// patchFlag analysis
let patchFlag = 0;
let hasRef = false;
let hasClassBinding = false;
let hasStyleBinding = false;
let hasHydrationEventBinding = false;
let hasDynamicKeys = false;
let hasVnodeHook = false;
const dynamicPropNames = [];
const analyzePatchFlag = ({ key, value }) => {
if (isStaticExp(key)) {
const name = key.content;
const isEventHandler = shared.isOn(name);
if (!isComponent &&
isEventHandler &&
// omit the flag for click handlers because hydration gives click
// dedicated fast path.
name.toLowerCase() !== 'onclick' &&
// omit v-model handlers
name !== 'onUpdate:modelValue' &&
// omit onVnodeXXX hooks
!shared.isReservedProp(name)) {
hasHydrationEventBinding = true;
}
if (isEventHandler && shared.isReservedProp(name)) {
hasVnodeHook = true;
}
if (value.type === 20 /* JS_CACHE_EXPRESSION */ ||
((value.type === 4 /* SIMPLE_EXPRESSION */ ||
value.type === 8 /* COMPOUND_EXPRESSION */) &&
getStaticType(value) > 0)) {
// skip if the prop is a cached handler or has constant value
return;
}
if (name === 'ref') {
hasRef = true;
}
else if (name === 'class' && !isComponent) {
hasClassBinding = true;
}
else if (name === 'style' && !isComponent) {
hasStyleBinding = true;
}
else if (name !== 'key' && !dynamicPropNames.includes(name)) {
dynamicPropNames.push(name);
}
}
else {
hasDynamicKeys = true;
}
};
for (let i = 0; i < props.length; i++) {
// static attribute
const prop = props[i];
if (prop.type === 6 /* ATTRIBUTE */) {
const { loc, name, value } = prop;
if (name === 'ref') {
hasRef = true;
}
// skip :is on <component>
if (name === 'is' && tag === 'component') {
continue;
}
properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', true, value ? value.loc : loc)));
}
else {
// directives
const { name, arg, exp, loc } = prop;
const isBind = name === 'bind';
const isOn = name === 'on';
// skip v-slot - it is handled by its dedicated transform.
if (name === 'slot') {
if (!isComponent) {
context.onError(createCompilerError(39 /* X_V_SLOT_MISPLACED */, loc));
}
continue;
}
// skip v-once - it is handled by its dedicated transform.
if (name === 'once') {
continue;
}
// skip v-is and :is on <component>
if (name === 'is' ||
(isBind && tag === 'component' && isBindKey(arg, 'is'))) {
continue;
}
// skip v-on in SSR compilation
if (isOn && ssr) {
continue;
}
// special case for v-bind and v-on with no argument
if (!arg && (isBind || isOn)) {
hasDynamicKeys = true;
if (exp) {
if (properties.length) {
mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));
properties = [];
}
if (isBind) {
mergeArgs.push(exp);
}
else {
// v-on="obj" -> toHandlers(obj)
mergeArgs.push({
type: 14 /* JS_CALL_EXPRESSION */,
loc,
callee: context.helper(TO_HANDLERS),
arguments: [exp]
});
}
}
else {
context.onError(createCompilerError(isBind
? 33 /* X_V_BIND_NO_EXPRESSION */
: 34 /* X_V_ON_NO_EXPRESSION */, loc));
}
continue;
}
const directiveTransform = context.directiveTransforms[name];
if (directiveTransform) {
// has built-in directive transform.
const { props, needRuntime } = directiveTransform(prop, node, context);
!ssr && props.forEach(analyzePatchFlag);
properties.push(...props);
if (needRuntime) {
runtimeDirectives.push(prop);
if (shared.isSymbol(needRuntime)) {
directiveImportMap.set(prop, needRuntime);
}
}
}
else {
// no built-in transform, this is a user custom directive.
runtimeDirectives.push(prop);
}
}
}
let propsExpression = undefined;
// has v-bind="object" or v-on="object", wrap with mergeProps
if (mergeArgs.length) {
if (properties.length) {
mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));
}
if (mergeArgs.length > 1) {
propsExpression = createCallExpression(context.helper(MERGE_PROPS), mergeArgs, elementLoc);
}
else {
// single v-bind with nothing else - no need for a mergeProps call
propsExpression = mergeArgs[0];
}
}
else if (properties.length) {
propsExpression = createObjectExpression(dedupeProperties(properties), elementLoc);
}
// patchFlag analysis
if (hasDynamicKeys) {
patchFlag |= 16 /* FULL_PROPS */;
}
else {
if (hasClassBinding) {
patchFlag |= 2 /* CLASS */;
}
if (hasStyleBinding) {
patchFlag |= 4 /* STYLE */;
}
if (dynamicPropNames.length) {
patchFlag |= 8 /* PROPS */;
}
if (hasHydrationEventBinding) {
patchFlag |= 32 /* HYDRATE_EVENTS */;
}
}
if ((patchFlag === 0 || patchFlag === 32 /* HYDRATE_EVENTS */) &&
(hasRef || hasVnodeHook || runtimeDirectives.length > 0)) {
patchFlag |= 512 /* NEED_PATCH */;
}
return {
props: propsExpression,
directives: runtimeDirectives,
patchFlag,
dynamicPropNames
};
}
// Dedupe props in an object literal.
// Literal duplicated attributes would have been warned during the parse phase,
// however, it's possible to encounter duplicated `onXXX` handlers with different
// modifiers. We also need to merge static and dynamic class / style attributes.
// - onXXX handlers / style: merge into array
// - class: merge into single expression with concatenation
function dedupeProperties(properties) {
const knownProps = new Map();
const deduped = [];
for (let i = 0; i < properties.length; i++) {
const prop = properties[i];
// dynamic keys are always allowed
if (prop.key.type === 8 /* COMPOUND_EXPRESSION */ || !prop.key.isStatic) {
deduped.push(prop);
continue;
}
const name = prop.key.content;
const existing = knownProps.get(name);
if (existing) {
if (name === 'style' || name === 'class' || name.startsWith('on')) {
mergeAsArray(existing, prop);
}
// unexpected duplicate, should have emitted error during parse
}
else {
knownProps.set(name, prop);
deduped.push(prop);
}
}
return deduped;
}
function mergeAsArray(existing, incoming) {
if (existing.value.type === 17 /* JS_ARRAY_EXPRESSION */) {
existing.value.elements.push(incoming.value);
}
else {
existing.value = createArrayExpression([existing.value, incoming.value], existing.loc);
}
}
function buildDirectiveArgs(dir, context) {
const dirArgs = [];
const runtime = directiveImportMap.get(dir);
if (runtime) {
dirArgs.push(context.helperString(runtime));
}
else {
// inject statement for resolving directive
context.helper(RESOLVE_DIRECTIVE);
context.directives.add(dir.name);
dirArgs.push(toValidAssetId(dir.name, `directive`));
}
const { loc } = dir;
if (dir.exp)
dirArgs.push(dir.exp);
if (dir.arg) {
if (!dir.exp) {
dirArgs.push(`void 0`);
}
dirArgs.push(dir.arg);
}
if (Object.keys(dir.modifiers).length) {
if (!dir.arg) {
if (!dir.exp) {
dirArgs.push(`void 0`);
}
dirArgs.push(`void 0`);
}
const trueExpression = createSimpleExpression(`true`, false, loc);
dirArgs.push(createObjectExpression(dir.modifiers.map(modifier => createObjectProperty(modifier, trueExpression)), loc));
}
return createArrayExpression(dirArgs, dir.loc);
}
function stringifyDynamicPropNames(props) {
let propsNamesString = `[`;
for (let i = 0, l = props.length; i < l; i++) {
propsNamesString += JSON.stringify(props[i]);
if (i < l - 1)
propsNamesString += ', ';
}
return propsNamesString + `]`;
}
const transformSlotOutlet = (node, context) => {
if (isSlotOutlet(node)) {
const { children, loc } = node;
const { slotName, slotProps } = processSlotOutlet(node, context);
const slotArgs = [
context.prefixIdentifiers ? `_ctx.$slots` : `$slots`,
slotName
];
if (slotProps) {
slotArgs.push(slotProps);
}
if (children.length) {
if (!slotProps) {
slotArgs.push(`{}`);
}
slotArgs.push(createFunctionExpression([], children, false, false, loc));
}
node.codegenNode = createCallExpression(context.helper(RENDER_SLOT), slotArgs, loc);
}
};
function processSlotOutlet(node, context) {
let slotName = `"default"`;
let slotProps = undefined;
// check for <slot name="xxx" OR :name="xxx" />
const name = findProp(node, 'name');
if (name) {
if (name.type === 6 /* ATTRIBUTE */ && name.value) {
// static name
slotName = JSON.stringify(name.value.content);
}
else if (name.type === 7 /* DIRECTIVE */ && name.exp) {
// dynamic name
slotName = name.exp;
}
}
const propsWithoutName = name
? node.props.filter(p => p !== name)
: node.props;
if (propsWithoutName.length > 0) {
const { props, directives } = buildProps(node, context, propsWithoutName);
slotProps = props;
if (directives.length) {
context.onError(createCompilerError(35 /* X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET */, directives[0].loc));
}
}
return {
slotName,
slotProps
};
}
const fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^\s*function(?:\s+[\w$]+)?\s*\(/;
const transformOn = (dir, node, context, augmentor) => {
const { loc, modifiers, arg } = dir;
if (!dir.exp && !modifiers.length) {
context.onError(createCompilerError(34 /* X_V_ON_NO_EXPRESSION */, loc));
}
let eventName;
if (arg.type === 4 /* SIMPLE_EXPRESSION */) {
if (arg.isStatic) {
const rawName = arg.content;
// for @vnode-xxx event listeners, auto convert it to camelCase
const normalizedName = rawName.startsWith(`vnode`)
? shared.capitalize(shared.camelize(rawName))
: shared.capitalize(rawName);
eventName = createSimpleExpression(`on${normalizedName}`, true, arg.loc);
}
else {
eventName = createCompoundExpression([
`"on" + ${context.helperString(CAPITALIZE)}(`,
arg,
`)`
]);
}
}
else {
// already a compound expression.
eventName = arg;
eventName.children.unshift(`"on" + ${context.helperString(CAPITALIZE)}(`);
eventName.children.push(`)`);
}
// handler processing
let exp = dir.exp;
if (exp && !exp.content.trim()) {
exp = undefined;
}
let isCacheable = context.cacheHandlers && !exp;
if (exp) {
const isMemberExp = isMemberExpression(exp.content);
const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content));
const hasMultipleStatements = exp.content.includes(`;`);
// process the expression since it's been skipped
if ( context.prefixIdentifiers) {
isInlineStatement && context.addIdentifiers(`$event`);
exp = processExpression(exp, context, false, hasMultipleStatements);
isInlineStatement && context.removeIdentifiers(`$event`);
// with scope analysis, the function is hoistable if it has no reference
// to scope variables.
isCacheable =
context.cacheHandlers &&
// #1541 bail if this is a member exp handler passed to a component -
// we need to use the original function to preserve arity,
// e.g. <transition> relies on checking cb.length to determine
// transition end handling. Inline function is ok since its arity
// is preserved even when cached.
!(isMemberExp && node.tagType === 1 /* COMPONENT */) &&
// bail if the function references closure variables (v-for, v-slot)
// it must be passed fresh to avoid stale values.
!hasScopeRef(exp, context.identifiers);
// If the expression is optimizable and is a member expression pointing
// to a function, turn it into invocation (and wrap in an arrow function
// below) so that it always accesses the latest value when called - thus
// avoiding the need to be patched.
if (isCacheable && isMemberExp) {
if (exp.type === 4 /* SIMPLE_EXPRESSION */) {
exp.content += `(...args)`;
}
else {
exp.children.push(`(...args)`);
}
}
}
if (isInlineStatement || (isCacheable && isMemberExp)) {
// wrap inline statement in a function expression
exp = createCompoundExpression([
`${isInlineStatement ? `$event` : `(...args)`} => ${hasMultipleStatements ? `{` : `(`}`,
exp,
hasMultipleStatements ? `}` : `)`
]);
}
}
let ret = {
props: [
createObjectProperty(eventName, exp || createSimpleExpression(`() => {}`, false, loc))
]
};
// apply extended compiler augmentor
if (augmentor) {
ret = augmentor(ret);
}
if (isCacheable) {
// cache handlers so that it's always the same handler being passed down.
// this avoids unnecessary re-renders when users use inline handlers on
// components.
ret.props[0].value = context.cache(ret.props[0].value);
}
return ret;
};
// v-bind without arg is handled directly in ./transformElements.ts due to it affecting
// codegen for the entire props object. This transform here is only for v-bind
// *with* args.
const transformBind = (dir, node, context) => {
const { exp, modifiers, loc } = dir;
const arg = dir.arg;
// .prop is no longer necessary due to new patch behavior
// .sync is replaced by v-model:arg
if (modifiers.includes('camel')) {
if (arg.type === 4 /* SIMPLE_EXPRESSION */) {
if (arg.isStatic) {
arg.content = shared.camelize(arg.content);
}
else {
arg.content = `${context.helperString(CAMELIZE)}(${arg.content})`;
}
}
else {
arg.children.unshift(`${context.helperString(CAMELIZE)}(`);
arg.children.push(`)`);
}
}
if (!exp ||
(exp.type === 4 /* SIMPLE_EXPRESSION */ && !exp.content.trim())) {
context.onError(createCompilerError(33 /* X_V_BIND_NO_EXPRESSION */, loc));
return {
props: [createObjectProperty(arg, createSimpleExpression('', true, loc))]
};
}
return {
props: [createObjectProperty(arg, exp)]
};
};
// Merge adjacent text nodes and expressions into a single expression
// e.g. <div>abc {{ d }} {{ e }}</div> should have a single expression node as child.
const transformText = (node, context) => {
if (node.type === 0 /* ROOT */ ||
node.type === 1 /* ELEMENT */ ||
node.type === 11 /* FOR */ ||
node.type === 10 /* IF_BRANCH */) {
// perform the transform on node exit so that all expressions have already
// been processed.
return () => {
const children = node.children;
let currentContainer = undefined;
let hasText = false;
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (isText(child)) {
hasText = true;
for (let j = i + 1; j < children.length; j++) {
const next = children[j];
if (isText(next)) {
if (!currentContainer) {
currentContainer = children[i] = {
type: 8 /* COMPOUND_EXPRESSION */,
loc: child.loc,
children: [child]
};
}
// merge adjacent text node into current
currentContainer.children.push(` + `, next);
children.splice(j, 1);
j--;
}
else {
currentContainer = undefined;
break;
}
}
}
}
if (!hasText ||
// if this is a plain element with a single text child, leave it
// as-is since the runtime has dedicated fast path for this by directly
// setting textContent of the element.
// for component root it's always normalized anyway.
(children.length === 1 &&
(node.type === 0 /* ROOT */ ||
(node.type === 1 /* ELEMENT */ &&
node.tagType === 0 /* ELEMENT */)))) {
return;
}
// pre-convert text nodes into createTextVNode(text) calls to avoid
// runtime normalization.
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (isText(child) || child.type === 8 /* COMPOUND_EXPRESSION */) {
const callArgs = [];
// createTextVNode defaults to single whitespace, so if it is a
// single space the code could be an empty call to save bytes.
if (child.type !== 2 /* TEXT */ || child.content !== ' ') {
callArgs.push(child);
}
// mark dynamic text with flag so it gets patched inside a block
if (!context.ssr && child.type !== 2 /* TEXT */) {
callArgs.push(`${1 /* TEXT */} /* ${shared.PatchFlagNames[1 /* TEXT */]} */`);
}
children[i] = {
type: 12 /* TEXT_CALL */,
content: child,
loc: child.loc,
codegenNode: createCallExpression(context.helper(CREATE_TEXT), callArgs)
};
}
}
};
}
};
const seen = new WeakSet();
const transformOnce = (node, context) => {
if (node.type === 1 /* ELEMENT */ && findDir(node, 'once', true)) {
if (seen.has(node)) {
return;
}
seen.add(node);
context.helper(SET_BLOCK_TRACKING);
return () => {
const cur = context.currentNode;
if (cur.codegenNode) {
cur.codegenNode = context.cache(cur.codegenNode, true /* isVNode */);
}
};
}
};
const transformModel = (dir, node, context) => {
const { exp, arg } = dir;
if (!exp) {
context.onError(createCompilerError(40 /* X_V_MODEL_NO_EXPRESSION */, dir.loc));
return createTransformProps();
}
const expString = exp.type === 4 /* SIMPLE_EXPRESSION */ ? exp.content : exp.loc.source;
if (!isMemberExpression(expString)) {
context.onError(createCompilerError(41 /* X_V_MODEL_MALFORMED_EXPRESSION */, exp.loc));
return createTransformProps();
}
if (
context.prefixIdentifiers &&
isSimpleIdentifier(expString) &&
context.identifiers[expString]) {
context.onError(createCompilerError(42 /* X_V_MODEL_ON_SCOPE_VARIABLE */, exp.loc));
return createTransformProps();
}
const propName = arg ? arg : createSimpleExpression('modelValue', true);
const eventName = arg
? isStaticExp(arg)
? `onUpdate:${arg.content}`
: createCompoundExpression(['"onUpdate:" + ', arg])
: `onUpdate:modelValue`;
const props = [
// modelValue: foo
createObjectProperty(propName, dir.exp),
// "onUpdate:modelValue": $event => (foo = $event)
createObjectProperty(eventName, createCompoundExpression([`$event => (`, exp, ` = $event)`]))
];
// cache v-model handler if applicable (when it doesn't refer any scope vars)
if (
context.prefixIdentifiers &&
context.cacheHandlers &&
!hasScopeRef(exp, context.identifiers)) {
props[1].value = context.cache(props[1].value);
}
// modelModifiers: { foo: true, "bar-baz": true }
if (dir.modifiers.length && node.tagType === 1 /* COMPONENT */) {
const modifiers = dir.modifiers
.map(m => (isSimpleIdentifier(m) ? m : JSON.stringify(m)) + `: true`)
.join(`, `);
const modifiersKey = arg
? isStaticExp(arg)
? `${arg.content}Modifiers`
: createCompoundExpression([arg, ' + "Modifiers"'])
: `modelModifiers`;
props.push(createObjectProperty(modifiersKey, createSimpleExpression(`{ ${modifiers} }`, false, dir.loc, true)));
}
return createTransformProps(props);
};
function createTransformProps(props = []) {
return { props };
}
function getBaseTransformPreset(prefixIdentifiers) {
return [
[
transformOnce,
transformIf,
transformFor,
...( prefixIdentifiers
? [
// order is important
trackVForSlotScopes,
transformExpression
]
: []),
transformSlotOutlet,
transformElement,
trackSlotScopes,
transformText
],
{
on: transformOn,
bind: transformBind,
model: transformModel
}
];
}
// we name it `baseCompile` so that higher order compilers like
// @vue/compiler-dom can export `compile` while re-exporting everything else.
function baseCompile(template, options = {}) {
const onError = options.onError || defaultOnError;
const isModuleMode = options.mode === 'module';
const prefixIdentifiers = (options.prefixIdentifiers === true || isModuleMode);
if (!prefixIdentifiers && options.cacheHandlers) {
onError(createCompilerError(47 /* X_CACHE_HANDLER_NOT_SUPPORTED */));
}
if (options.scopeId && !isModuleMode) {
onError(createCompilerError(48 /* X_SCOPE_ID_NOT_SUPPORTED */));
}
const ast = shared.isString(template) ? baseParse(template, options) : template;
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset(prefixIdentifiers);
transform(ast, shared.extend({}, options, {
prefixIdentifiers,
nodeTransforms: [
...nodeTransforms,
...(options.nodeTransforms || []) // user transforms
],
directiveTransforms: shared.extend({}, directiveTransforms, options.directiveTransforms || {} // user transforms
)
}));
return generate(ast, shared.extend({}, options, {
prefixIdentifiers
}));
}
const noopDirectiveTransform = () => ({ props: [] });
exports.generateCodeFrame = shared.generateCodeFrame;
exports.BASE_TRANSITION = BASE_TRANSITION;
exports.CAMELIZE = CAMELIZE;
exports.CAPITALIZE = CAPITALIZE;
exports.CREATE_BLOCK = CREATE_BLOCK;
exports.CREATE_COMMENT = CREATE_COMMENT;
exports.CREATE_SLOTS = CREATE_SLOTS;
exports.CREATE_STATIC = CREATE_STATIC;
exports.CREATE_TEXT = CREATE_TEXT;
exports.CREATE_VNODE = CREATE_VNODE;
exports.FRAGMENT = FRAGMENT;
exports.KEEP_ALIVE = KEEP_ALIVE;
exports.MERGE_PROPS = MERGE_PROPS;
exports.OPEN_BLOCK = OPEN_BLOCK;
exports.POP_SCOPE_ID = POP_SCOPE_ID;
exports.PUSH_SCOPE_ID = PUSH_SCOPE_ID;
exports.RENDER_LIST = RENDER_LIST;
exports.RENDER_SLOT = RENDER_SLOT;
exports.RESOLVE_COMPONENT = RESOLVE_COMPONENT;
exports.RESOLVE_DIRECTIVE = RESOLVE_DIRECTIVE;
exports.RESOLVE_DYNAMIC_COMPONENT = RESOLVE_DYNAMIC_COMPONENT;
exports.SET_BLOCK_TRACKING = SET_BLOCK_TRACKING;
exports.SUSPENSE = SUSPENSE;
exports.TELEPORT = TELEPORT;
exports.TO_DISPLAY_STRING = TO_DISPLAY_STRING;
exports.TO_HANDLERS = TO_HANDLERS;
exports.WITH_CTX = WITH_CTX;
exports.WITH_DIRECTIVES = WITH_DIRECTIVES;
exports.WITH_SCOPE_ID = WITH_SCOPE_ID;
exports.advancePositionWithClone = advancePositionWithClone;
exports.advancePositionWithMutation = advancePositionWithMutation;
exports.assert = assert;
exports.baseCompile = baseCompile;
exports.baseParse = baseParse;
exports.buildProps = buildProps;
exports.buildSlots = buildSlots;
exports.createArrayExpression = createArrayExpression;
exports.createAssignmentExpression = createAssignmentExpression;
exports.createBlockStatement = createBlockStatement;
exports.createCacheExpression = createCacheExpression;
exports.createCallExpression = createCallExpression;
exports.createCompilerError = createCompilerError;
exports.createCompoundExpression = createCompoundExpression;
exports.createConditionalExpression = createConditionalExpression;
exports.createForLoopParams = createForLoopParams;
exports.createFunctionExpression = createFunctionExpression;
exports.createIfStatement = createIfStatement;
exports.createInterpolation = createInterpolation;
exports.createObjectExpression = createObjectExpression;
exports.createObjectProperty = createObjectProperty;
exports.createReturnStatement = createReturnStatement;
exports.createRoot = createRoot;
exports.createSequenceExpression = createSequenceExpression;
exports.createSimpleExpression = createSimpleExpression;
exports.createStructuralDirectiveTransform = createStructuralDirectiveTransform;
exports.createTemplateLiteral = createTemplateLiteral;
exports.createTransformContext = createTransformContext;
exports.createVNodeCall = createVNodeCall;
exports.findDir = findDir;
exports.findProp = findProp;
exports.generate = generate;
exports.getBaseTransformPreset = getBaseTransformPreset;
exports.getInnerRange = getInnerRange;
exports.hasDynamicKeyVBind = hasDynamicKeyVBind;
exports.hasScopeRef = hasScopeRef;
exports.helperNameMap = helperNameMap;
exports.injectProp = injectProp;
exports.isBindKey = isBindKey;
exports.isBuiltInType = isBuiltInType;
exports.isCoreComponent = isCoreComponent;
exports.isMemberExpression = isMemberExpression;
exports.isSimpleIdentifier = isSimpleIdentifier;
exports.isSlotOutlet = isSlotOutlet;
exports.isStaticExp = isStaticExp;
exports.isTemplateNode = isTemplateNode;
exports.isText = isText;
exports.isVSlot = isVSlot;
exports.locStub = locStub;
exports.noopDirectiveTransform = noopDirectiveTransform;
exports.processExpression = processExpression;
exports.processFor = processFor;
exports.processIf = processIf;
exports.processSlotOutlet = processSlotOutlet;
exports.registerRuntimeHelpers = registerRuntimeHelpers;
exports.resolveComponentType = resolveComponentType;
exports.toValidAssetId = toValidAssetId;
exports.trackSlotScopes = trackSlotScopes;
exports.trackVForSlotScopes = trackVForSlotScopes;
exports.transform = transform;
exports.transformBind = transformBind;
exports.transformElement = transformElement;
exports.transformExpression = transformExpression;
exports.transformModel = transformModel;
exports.transformOn = transformOn;
exports.traverseNode = traverseNode;