'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var shared = require('@vue/shared'); var parser = require('@babel/parser'); var sourceMap = require('source-map'); var estreeWalker = require('estree-walker'); function defaultOnError(error) { throw error; } function defaultOnWarn(msg) { console.warn(`[Vue warn] ${msg.message}`); } 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 /* ErrorCodes.ABRUPT_CLOSING_OF_EMPTY_COMMENT */]: 'Illegal comment.', [1 /* ErrorCodes.CDATA_IN_HTML_CONTENT */]: 'CDATA section is allowed only in XML context.', [2 /* ErrorCodes.DUPLICATE_ATTRIBUTE */]: 'Duplicate attribute.', [3 /* ErrorCodes.END_TAG_WITH_ATTRIBUTES */]: 'End tag cannot have attributes.', [4 /* ErrorCodes.END_TAG_WITH_TRAILING_SOLIDUS */]: "Illegal '/' in tags.", [5 /* ErrorCodes.EOF_BEFORE_TAG_NAME */]: 'Unexpected EOF in tag.', [6 /* ErrorCodes.EOF_IN_CDATA */]: 'Unexpected EOF in CDATA section.', [7 /* ErrorCodes.EOF_IN_COMMENT */]: 'Unexpected EOF in comment.', [8 /* ErrorCodes.EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT */]: 'Unexpected EOF in script.', [9 /* ErrorCodes.EOF_IN_TAG */]: 'Unexpected EOF in tag.', [10 /* ErrorCodes.INCORRECTLY_CLOSED_COMMENT */]: 'Incorrectly closed comment.', [11 /* ErrorCodes.INCORRECTLY_OPENED_COMMENT */]: 'Incorrectly opened comment.', [12 /* ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME */]: "Illegal tag name. Use '<' to print '<'.", [13 /* ErrorCodes.MISSING_ATTRIBUTE_VALUE */]: 'Attribute value was expected.', [14 /* ErrorCodes.MISSING_END_TAG_NAME */]: 'End tag name was expected.', [15 /* ErrorCodes.MISSING_WHITESPACE_BETWEEN_ATTRIBUTES */]: 'Whitespace was expected.', [16 /* ErrorCodes.NESTED_COMMENT */]: "Unexpected ' isRef(x) ? x.value = y : x = y const { right: rVal, operator } = parent; const rExp = rawExp.slice(rVal.start - 1, rVal.end - 1); const rExpString = stringifyExpression(processExpression(createSimpleExpression(rExp, false), context, false, false, knownIds)); return `${context.helperString(IS_REF)}(${raw})${context.isTS ? ` //@ts-ignore\n` : ``} ? ${raw}.value ${operator} ${rExpString} : ${raw}`; } else if (isUpdateArg) { // make id replace parent in the code range so the raw update operator // is removed id.start = parent.start; id.end = parent.end; const { prefix: isPrefix, operator } = parent; const prefix = isPrefix ? operator : ``; const postfix = isPrefix ? `` : operator; // let binding. // x++ --> isRef(a) ? a.value++ : a++ return `${context.helperString(IS_REF)}(${raw})${context.isTS ? ` //@ts-ignore\n` : ``} ? ${prefix}${raw}.value${postfix} : ${prefix}${raw}${postfix}`; } else if (isDestructureAssignment) { // TODO // let binding in a destructure assignment - it's very tricky to // handle both possible cases here without altering the original // structure of the code, so we just assume it's not a ref here // for now return raw; } else { return `${context.helperString(UNREF)}(${raw})`; } } else if (type === "props" /* BindingTypes.PROPS */) { // use __props which is generated by compileScript so in ts mode // it gets correct type return shared.genPropsAccessExp(raw); } else if (type === "props-aliased" /* BindingTypes.PROPS_ALIASED */) { // prop with a different local alias (from defineProps() destructure) return shared.genPropsAccessExp(bindingMetadata.__propsAliases[raw]); } } else { if (type && type.startsWith('setup')) { // setup bindings in non-inline mode return `$setup.${raw}`; } else if (type === "props-aliased" /* BindingTypes.PROPS_ALIASED */) { return `$props['${bindingMetadata.__propsAliases[raw]}']`; } else if (type) { return `$${type}.${raw}`; } } // fallback to ctx return `_ctx.${raw}`; }; // fast path if expression is a simple identifier. const rawExp = node.content; // bail constant on parens (function invocation) and dot (member access) const bailConstant = rawExp.indexOf(`(`) > -1 || rawExp.indexOf('.') > 0; if (isSimpleIdentifier(rawExp)) { const isScopeVarReference = context.identifiers[rawExp]; const isAllowedGlobal = shared.isGloballyWhitelisted(rawExp); const isLiteral = isLiteralWhitelisted(rawExp); if (!asParams && !isScopeVarReference && !isAllowedGlobal && !isLiteral) { // const bindings exposed from setup can be skipped for patching but // cannot be hoisted to module scope if (bindingMetadata[node.content] === "setup-const" /* BindingTypes.SETUP_CONST */) { node.constType = 1 /* ConstantTypes.CAN_SKIP_PATCH */; } node.content = rewriteIdentifier(rawExp); } else if (!isScopeVarReference) { if (isLiteral) { node.constType = 3 /* ConstantTypes.CAN_STRINGIFY */; } else { node.constType = 2 /* ConstantTypes.CAN_HOIST */; } } 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 }).program; } catch (e) { context.onError(createCompilerError(45 /* ErrorCodes.X_INVALID_EXPRESSION */, node.loc, undefined, e.message)); return node; } const ids = []; const parentStack = []; const knownIds = Object.create(context.identifiers); walkIdentifiers(ast, (node, parent, _, isReferenced, isLocal) => { if (isStaticPropertyKey(node, parent)) { return; } // v2 wrapped filter call if (node.name.startsWith('_filter_')) { return; } const needPrefix = isReferenced && canPrefix(node); if (needPrefix && !isLocal) { if (isStaticProperty(parent) && parent.shorthand) { node.prefix = `${node.name}: `; } node.name = rewriteIdentifier(node.name, parent, node); ids.push(node); } else { // The identifier is considered constant unless it's pointing to a // local scope variable (a v-for alias, or a v-slot prop) if (!(needPrefix && isLocal) && !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); } }, true, // invoke on ALL identifiers parentStack, knownIds); // 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 ? 3 /* ConstantTypes.CAN_STRINGIFY */ : 0 /* ConstantTypes.NOT_CONSTANT */)); 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.constType = bailConstant ? 0 /* ConstantTypes.NOT_CONSTANT */ : 3 /* ConstantTypes.CAN_STRINGIFY */; } ret.identifiers = Object.keys(knownIds); return ret; } function canPrefix(id) { // skip whitelisted globals if (shared.isGloballyWhitelisted(id.name)) { return false; } // special case for webpack compilation if (id.name === 'require') { return false; } return true; } function stringifyExpression(exp) { if (shared.isString(exp)) { return exp; } else if (exp.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */) { return exp.content; } else { return exp.children .map(stringifyExpression) .join(''); } } 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 /* NodeTypes.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. const parentCondition = getParentCondition(ifNode.codegenNode); 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(28 /* ErrorCodes.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 /* NodeTypes.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 /* NodeTypes.COMMENT */) { context.removeNode(sibling); comments.unshift(sibling); continue; } if (sibling && sibling.type === 2 /* NodeTypes.TEXT */ && !sibling.content.trim().length) { context.removeNode(sibling); continue; } if (sibling && sibling.type === 9 /* NodeTypes.IF */) { // Check if v-else was followed by v-else-if if (dir.name === 'else-if' && sibling.branches[sibling.branches.length - 1].condition === undefined) { context.onError(createCompilerError(30 /* ErrorCodes.X_V_ELSE_NO_ADJACENT_IF */, node.loc)); } // move the node to the if node's branches context.removeNode(); const branch = createIfBranch(node, dir); if (comments.length && // #3619 ignore comments if the v-if is direct child of !(context.parent && context.parent.type === 1 /* NodeTypes.ELEMENT */ && isBuiltInType(context.parent.tag, 'transition'))) { 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(29 /* ErrorCodes.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(30 /* ErrorCodes.X_V_ELSE_NO_ADJACENT_IF */, node.loc)); } break; } } } function createIfBranch(node, dir) { const isTemplateIf = node.tagType === 3 /* ElementTypes.TEMPLATE */; return { type: 10 /* NodeTypes.IF_BRANCH */, loc: node.loc, condition: dir.name === 'else' ? undefined : dir.exp, children: isTemplateIf && !findDir(node, 'for') ? node.children : [node], userKey: findProp(node, `key`), isTemplateIf }; } 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, 2 /* ConstantTypes.CAN_HOIST */)); const { children } = branch; const firstChild = children[0]; const needFragmentWrapper = children.length !== 1 || firstChild.type !== 1 /* NodeTypes.ELEMENT */; if (needFragmentWrapper) { if (children.length === 1 && firstChild.type === 11 /* NodeTypes.FOR */) { // optimize away nested fragments when child is a ForNode const vnodeCall = firstChild.codegenNode; injectProp(vnodeCall, keyProperty, context); return vnodeCall; } else { let patchFlag = 64 /* PatchFlags.STABLE_FRAGMENT */; let patchFlagText = shared.PatchFlagNames[64 /* PatchFlags.STABLE_FRAGMENT */]; // check if the fragment actually contains a single valid child with // the rest being comments if (!branch.isTemplateIf && children.filter(c => c.type !== 3 /* NodeTypes.COMMENT */).length === 1) { patchFlag |= 2048 /* PatchFlags.DEV_ROOT_FRAGMENT */; patchFlagText += `, ${shared.PatchFlagNames[2048 /* PatchFlags.DEV_ROOT_FRAGMENT */]}`; } return createVNodeCall(context, helper(FRAGMENT), createObjectExpression([keyProperty]), children, patchFlag + (` /* ${patchFlagText} */` ), undefined, undefined, true, false, false /* isComponent */, branch.loc); } } else { const ret = firstChild.codegenNode; const vnodeCall = getMemoedVNodeCall(ret); // Change createVNode to createBlock. if (vnodeCall.type === 13 /* NodeTypes.VNODE_CALL */) { makeBlock(vnodeCall, context); } // inject branch key injectProp(vnodeCall, keyProperty, context); return ret; } } function isSameKey(a, b) { if (!a || a.type !== b.type) { return false; } if (a.type === 6 /* NodeTypes.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 /* NodeTypes.SIMPLE_EXPRESSION */ || exp.isStatic !== branchExp.isStatic || exp.content !== branchExp.content) { return false; } } return true; } function getParentCondition(node) { while (true) { if (node.type === 19 /* NodeTypes.JS_CONDITIONAL_EXPRESSION */) { if (node.alternate.type === 19 /* NodeTypes.JS_CONDITIONAL_EXPRESSION */) { node = node.alternate; } else { return node; } } else if (node.type === 20 /* NodeTypes.JS_CACHE_EXPRESSION */) { node = node.value; } } } const transformFor = createStructuralDirectiveTransform('for', (node, dir, context) => { const { helper, removeHelper } = 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 isTemplate = isTemplateNode(node); const memo = findDir(node, 'memo'); const keyProp = findProp(node, `key`); const keyExp = keyProp && (keyProp.type === 6 /* NodeTypes.ATTRIBUTE */ ? createSimpleExpression(keyProp.value.content, true) : keyProp.exp); const keyProperty = keyProp ? createObjectProperty(`key`, keyExp) : null; if (isTemplate) { // #2085 / #5288 process :key and v-memo expressions need to be // processed on `