1import he from 'he'2import { parseHTML } from './html-parser'3import { parseText } from './text-parser'4import { parseFilters } from './filter-parser'5import { genAssignmentCode } from '../directives/model'6import { extend, cached, no, camelize, hyphenate } from 'shared/util'7import { isIE, isEdge, isServerRendering } from 'core/util/env'89import {10 addProp,11 addAttr,12 baseWarn,13 addHandler,14 addDirective,15 getBindingAttr,16 getAndRemoveAttr,17 getRawBindingAttr,18 pluckModuleFunction,19 getAndRemoveAttrByRegex20} from '../helpers'2122import {23 ASTAttr,24 ASTElement,25 ASTIfCondition,26 ASTNode,27 ASTText,28 CompilerOptions29} from 'types/compiler'3031export const onRE = /^@|^v-on:/32export const dirRE = process.env.VBIND_PROP_SHORTHAND33 ? /^v-|^@|^:|^\.|^#/34 : /^v-|^@|^:|^#/35export const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/36export const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/37const stripParensRE = /^\(|\)$/g38const dynamicArgRE = /^\[.*\]$/3940const argRE = /:(.*)$/41export const bindRE = /^:|^\.|^v-bind:/42const propBindRE = /^\./43const modifierRE = /\.[^.\]]+(?=[^\]]*$)/g4445export const slotRE = /^v-slot(:|$)|^#/4647const lineBreakRE = /[\r\n]/48const whitespaceRE = /[ \f\t\r\n]+/g4950const invalidAttributeRE = /[\s"'<>\/=]/5152const decodeHTMLCached = cached(he.decode)5354export const emptySlotScopeToken = `_empty_`5556// configurable state57export let warn: any58let delimiters59let transforms60let preTransforms61let postTransforms62let platformIsPreTag63let platformMustUseProp64let platformGetTagNamespace65let maybeComponent6667export function createASTElement(68 tag: string,69 attrs: Array<ASTAttr>,70 parent: ASTElement | void71): ASTElement {72 return {73 type: 1,74 tag,75 attrsList: attrs,76 attrsMap: makeAttrsMap(attrs),77 rawAttrsMap: {},78 parent,79 children: []80 }81}8283/**84 * Convert HTML string to AST.85 */86export function parse(template: string, options: CompilerOptions): ASTElement {87 warn = options.warn || baseWarn8889 platformIsPreTag = options.isPreTag || no90 platformMustUseProp = options.mustUseProp || no91 platformGetTagNamespace = options.getTagNamespace || no92 const isReservedTag = options.isReservedTag || no93 maybeComponent = (el: ASTElement) =>94 !!(95 el.component ||96 el.attrsMap[':is'] ||97 el.attrsMap['v-bind:is'] ||98 !(el.attrsMap.is ? isReservedTag(el.attrsMap.is) : isReservedTag(el.tag))99 )100 transforms = pluckModuleFunction(options.modules, 'transformNode')101 preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')102 postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')103104 delimiters = options.delimiters105106 const stack: any[] = []107 const preserveWhitespace = options.preserveWhitespace !== false108 const whitespaceOption = options.whitespace109 let root110 let currentParent111 let inVPre = false112 let inPre = false113 let warned = false114115 function warnOnce(msg, range) {116 if (!warned) {117 warned = true118 warn(msg, range)119 }120 }121122 function closeElement(element) {123 trimEndingWhitespace(element)124 if (!inVPre && !element.processed) {125 element = processElement(element, options)126 }127 // tree management128 if (!stack.length && element !== root) {129 // allow root elements with v-if, v-else-if and v-else130 if (root.if && (element.elseif || element.else)) {131 if (__DEV__) {132 checkRootConstraints(element)133 }134 addIfCondition(root, {135 exp: element.elseif,136 block: element137 })138 } else if (__DEV__) {139 warnOnce(140 `Component template should contain exactly one root element. ` +141 `If you are using v-if on multiple elements, ` +142 `use v-else-if to chain them instead.`,143 { start: element.start }144 )145 }146 }147 if (currentParent && !element.forbidden) {148 if (element.elseif || element.else) {149 processIfConditions(element, currentParent)150 } else {151 if (element.slotScope) {152 // scoped slot153 // keep it in the children list so that v-else(-if) conditions can154 // find it as the prev node.155 const name = element.slotTarget || '"default"'156 ;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[157 name158 ] = element159 }160 currentParent.children.push(element)161 element.parent = currentParent162 }163 }164165 // final children cleanup166 // filter out scoped slots167 element.children = element.children.filter(c => !c.slotScope)168 // remove trailing whitespace node again169 trimEndingWhitespace(element)170171 // check pre state172 if (element.pre) {173 inVPre = false174 }175 if (platformIsPreTag(element.tag)) {176 inPre = false177 }178 // apply post-transforms179 for (let i = 0; i < postTransforms.length; i++) {180 postTransforms[i](element, options)181 }182 }183184 function trimEndingWhitespace(el) {185 // remove trailing whitespace node186 if (!inPre) {187 let lastNode188 while (189 (lastNode = el.children[el.children.length - 1]) &&190 lastNode.type === 3 &&191 lastNode.text === ' '192 ) {193 el.children.pop()194 }195 }196 }197198 function checkRootConstraints(el) {199 if (el.tag === 'slot' || el.tag === 'template') {200 warnOnce(201 `Cannot use <${el.tag}> as component root element because it may ` +202 'contain multiple nodes.',203 { start: el.start }204 )205 }206 if (el.attrsMap.hasOwnProperty('v-for')) {207 warnOnce(208 'Cannot use v-for on stateful component root element because ' +209 'it renders multiple elements.',210 el.rawAttrsMap['v-for']211 )212 }213 }214215 parseHTML(template, {216 warn,217 expectHTML: options.expectHTML,218 isUnaryTag: options.isUnaryTag,219 canBeLeftOpenTag: options.canBeLeftOpenTag,220 shouldDecodeNewlines: options.shouldDecodeNewlines,221 shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,222 shouldKeepComment: options.comments,223 outputSourceRange: options.outputSourceRange,224 start(tag, attrs, unary, start, end) {225 // check namespace.226 // inherit parent ns if there is one227 const ns =228 (currentParent && currentParent.ns) || platformGetTagNamespace(tag)229230 // handle IE svg bug231 /* istanbul ignore if */232 if (isIE && ns === 'svg') {233 attrs = guardIESVGBug(attrs)234 }235236 let element: ASTElement = createASTElement(tag, attrs, currentParent)237 if (ns) {238 element.ns = ns239 }240241 if (__DEV__) {242 if (options.outputSourceRange) {243 element.start = start244 element.end = end245 element.rawAttrsMap = element.attrsList.reduce((cumulated, attr) => {246 cumulated[attr.name] = attr247 return cumulated248 }, {})249 }250 attrs.forEach(attr => {251 if (invalidAttributeRE.test(attr.name)) {252 warn(253 `Invalid dynamic argument expression: attribute names cannot contain ` +254 `spaces, quotes, <, >, / or =.`,255 options.outputSourceRange256 ? {257 start: attr.start! + attr.name.indexOf(`[`),258 end: attr.start! + attr.name.length259 }260 : undefined261 )262 }263 })264 }265266 if (isForbiddenTag(element) && !isServerRendering()) {267 element.forbidden = true268 __DEV__ &&269 warn(270 'Templates should only be responsible for mapping the state to the ' +271 'UI. Avoid placing tags with side-effects in your templates, such as ' +272 `<${tag}>` +273 ', as they will not be parsed.',274 { start: element.start }275 )276 }277278 // apply pre-transforms279 for (let i = 0; i < preTransforms.length; i++) {280 element = preTransforms[i](element, options) || element281 }282283 if (!inVPre) {284 processPre(element)285 if (element.pre) {286 inVPre = true287 }288 }289 if (platformIsPreTag(element.tag)) {290 inPre = true291 }292 if (inVPre) {293 processRawAttrs(element)294 } else if (!element.processed) {295 // structural directives296 processFor(element)297 processIf(element)298 processOnce(element)299 }300301 if (!root) {302 root = element303 if (__DEV__) {304 checkRootConstraints(root)305 }306 }307308 if (!unary) {309 currentParent = element310 stack.push(element)311 } else {312 closeElement(element)313 }314 },315316 end(tag, start, end) {317 const element = stack[stack.length - 1]318 // pop stack319 stack.length -= 1320 currentParent = stack[stack.length - 1]321 if (__DEV__ && options.outputSourceRange) {322 element.end = end323 }324 closeElement(element)325 },326327 chars(text: string, start?: number, end?: number) {328 if (!currentParent) {329 if (__DEV__) {330 if (text === template) {331 warnOnce(332 'Component template requires a root element, rather than just text.',333 { start }334 )335 } else if ((text = text.trim())) {336 warnOnce(`text "${text}" outside root element will be ignored.`, {337 start338 })339 }340 }341 return342 }343 // IE textarea placeholder bug344 /* istanbul ignore if */345 if (346 isIE &&347 currentParent.tag === 'textarea' &&348 currentParent.attrsMap.placeholder === text349 ) {350 return351 }352 const children = currentParent.children353 if (inPre || text.trim()) {354 text = isTextTag(currentParent)355 ? text356 : (decodeHTMLCached(text) as string)357 } else if (!children.length) {358 // remove the whitespace-only node right after an opening tag359 text = ''360 } else if (whitespaceOption) {361 if (whitespaceOption === 'condense') {362 // in condense mode, remove the whitespace node if it contains363 // line break, otherwise condense to a single space364 text = lineBreakRE.test(text) ? '' : ' '365 } else {366 text = ' '367 }368 } else {369 text = preserveWhitespace ? ' ' : ''370 }371 if (text) {372 if (!inPre && whitespaceOption === 'condense') {373 // condense consecutive whitespaces into single space374 text = text.replace(whitespaceRE, ' ')375 }376 let res377 let child: ASTNode | undefined378 if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) {379 child = {380 type: 2,381 expression: res.expression,382 tokens: res.tokens,383 text384 }385 } else if (386 text !== ' ' ||387 !children.length ||388 children[children.length - 1].text !== ' '389 ) {390 child = {391 type: 3,392 text393 }394 }395 if (child) {396 if (__DEV__ && options.outputSourceRange) {397 child.start = start398 child.end = end399 }400 children.push(child)401 }402 }403 },404 comment(text: string, start, end) {405 // adding anything as a sibling to the root node is forbidden406 // comments should still be allowed, but ignored407 if (currentParent) {408 const child: ASTText = {409 type: 3,410 text,411 isComment: true412 }413 if (__DEV__ && options.outputSourceRange) {414 child.start = start415 child.end = end416 }417 currentParent.children.push(child)418 }419 }420 })421 return root422}423424function processPre(el) {425 if (getAndRemoveAttr(el, 'v-pre') != null) {426 el.pre = true427 }428}429430function processRawAttrs(el) {431 const list = el.attrsList432 const len = list.length433 if (len) {434 const attrs: Array<ASTAttr> = (el.attrs = new Array(len))435 for (let i = 0; i < len; i++) {436 attrs[i] = {437 name: list[i].name,438 value: JSON.stringify(list[i].value)439 }440 if (list[i].start != null) {441 attrs[i].start = list[i].start442 attrs[i].end = list[i].end443 }444 }445 } else if (!el.pre) {446 // non root node in pre blocks with no attributes447 el.plain = true448 }449}450451export function processElement(element: ASTElement, options: CompilerOptions) {452 processKey(element)453454 // determine whether this is a plain element after455 // removing structural attributes456 element.plain =457 !element.key && !element.scopedSlots && !element.attrsList.length458459 processRef(element)460 processSlotContent(element)461 processSlotOutlet(element)462 processComponent(element)463 for (let i = 0; i < transforms.length; i++) {464 element = transforms[i](element, options) || element465 }466 processAttrs(element)467 return element468}469470function processKey(el) {471 const exp = getBindingAttr(el, 'key')472 if (exp) {473 if (__DEV__) {474 if (el.tag === 'template') {475 warn(476 `<template> cannot be keyed. Place the key on real elements instead.`,477 getRawBindingAttr(el, 'key')478 )479 }480 if (el.for) {481 const iterator = el.iterator2 || el.iterator1482 const parent = el.parent483 if (484 iterator &&485 iterator === exp &&486 parent &&487 parent.tag === 'transition-group'488 ) {489 warn(490 `Do not use v-for index as key on <transition-group> children, ` +491 `this is the same as not using keys.`,492 getRawBindingAttr(el, 'key'),493 true /* tip */494 )495 }496 }497 }498 el.key = exp499 }500}501502function processRef(el) {503 const ref = getBindingAttr(el, 'ref')504 if (ref) {505 el.ref = ref506 el.refInFor = checkInFor(el)507 }508}509510export function processFor(el: ASTElement) {511 let exp512 if ((exp = getAndRemoveAttr(el, 'v-for'))) {513 const res = parseFor(exp)514 if (res) {515 extend(el, res)516 } else if (__DEV__) {517 warn(`Invalid v-for expression: ${exp}`, el.rawAttrsMap['v-for'])518 }519 }520}521522type ForParseResult = {523 for: string524 alias: string525 iterator1?: string526 iterator2?: string527}528529export function parseFor(exp: string): ForParseResult | undefined {530 const inMatch = exp.match(forAliasRE)531 if (!inMatch) return532 const res: any = {}533 res.for = inMatch[2].trim()534 const alias = inMatch[1].trim().replace(stripParensRE, '')535 const iteratorMatch = alias.match(forIteratorRE)536 if (iteratorMatch) {537 res.alias = alias.replace(forIteratorRE, '').trim()538 res.iterator1 = iteratorMatch[1].trim()539 if (iteratorMatch[2]) {540 res.iterator2 = iteratorMatch[2].trim()541 }542 } else {543 res.alias = alias544 }545 return res546}547548function processIf(el) {549 const exp = getAndRemoveAttr(el, 'v-if')550 if (exp) {551 el.if = exp552 addIfCondition(el, {553 exp: exp,554 block: el555 })556 } else {557 if (getAndRemoveAttr(el, 'v-else') != null) {558 el.else = true559 }560 const elseif = getAndRemoveAttr(el, 'v-else-if')561 if (elseif) {562 el.elseif = elseif563 }564 }565}566567function processIfConditions(el, parent) {568 const prev = findPrevElement(parent.children)569 if (prev && prev.if) {570 addIfCondition(prev, {571 exp: el.elseif,572 block: el573 })574 } else if (__DEV__) {575 warn(576 `v-${el.elseif ? 'else-if="' + el.elseif + '"' : 'else'} ` +577 `used on element <${el.tag}> without corresponding v-if.`,578 el.rawAttrsMap[el.elseif ? 'v-else-if' : 'v-else']579 )580 }581}582583function findPrevElement(children: Array<any>): ASTElement | void {584 let i = children.length585 while (i--) {586 if (children[i].type === 1) {587 return children[i]588 } else {589 if (__DEV__ && children[i].text !== ' ') {590 warn(591 `text "${children[i].text.trim()}" between v-if and v-else(-if) ` +592 `will be ignored.`,593 children[i]594 )595 }596 children.pop()597 }598 }599}600601export function addIfCondition(el: ASTElement, condition: ASTIfCondition) {602 if (!el.ifConditions) {603 el.ifConditions = []604 }605 el.ifConditions.push(condition)606}607608function processOnce(el) {609 const once = getAndRemoveAttr(el, 'v-once')610 if (once != null) {611 el.once = true612 }613}614615// handle content being passed to a component as slot,616// e.g. <template slot="xxx">, <div slot-scope="xxx">617function processSlotContent(el) {618 let slotScope619 if (el.tag === 'template') {620 slotScope = getAndRemoveAttr(el, 'scope')621 /* istanbul ignore if */622 if (__DEV__ && slotScope) {623 warn(624 `the "scope" attribute for scoped slots have been deprecated and ` +625 `replaced by "slot-scope" since 2.5. The new "slot-scope" attribute ` +626 `can also be used on plain elements in addition to <template> to ` +627 `denote scoped slots.`,628 el.rawAttrsMap['scope'],629 true630 )631 }632 el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope')633 } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {634 /* istanbul ignore if */635 if (__DEV__ && el.attrsMap['v-for']) {636 warn(637 `Ambiguous combined usage of slot-scope and v-for on <${el.tag}> ` +638 `(v-for takes higher priority). Use a wrapper <template> for the ` +639 `scoped slot to make it clearer.`,640 el.rawAttrsMap['slot-scope'],641 true642 )643 }644 el.slotScope = slotScope645 }646647 // slot="xxx"648 const slotTarget = getBindingAttr(el, 'slot')649 if (slotTarget) {650 el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget651 el.slotTargetDynamic = !!(652 el.attrsMap[':slot'] || el.attrsMap['v-bind:slot']653 )654 // preserve slot as an attribute for native shadow DOM compat655 // only for non-scoped slots.656 if (el.tag !== 'template' && !el.slotScope) {657 addAttr(el, 'slot', slotTarget, getRawBindingAttr(el, 'slot'))658 }659 }660661 // 2.6 v-slot syntax662 if (process.env.NEW_SLOT_SYNTAX) {663 if (el.tag === 'template') {664 // v-slot on <template>665 const slotBinding = getAndRemoveAttrByRegex(el, slotRE)666 if (slotBinding) {667 if (__DEV__) {668 if (el.slotTarget || el.slotScope) {669 warn(`Unexpected mixed usage of different slot syntaxes.`, el)670 }671 if (el.parent && !maybeComponent(el.parent)) {672 warn(673 `<template v-slot> can only appear at the root level inside ` +674 `the receiving component`,675 el676 )677 }678 }679 const { name, dynamic } = getSlotName(slotBinding)680 el.slotTarget = name681 el.slotTargetDynamic = dynamic682 el.slotScope = slotBinding.value || emptySlotScopeToken // force it into a scoped slot for perf683 }684 } else {685 // v-slot on component, denotes default slot686 const slotBinding = getAndRemoveAttrByRegex(el, slotRE)687 if (slotBinding) {688 if (__DEV__) {689 if (!maybeComponent(el)) {690 warn(691 `v-slot can only be used on components or <template>.`,692 slotBinding693 )694 }695 if (el.slotScope || el.slotTarget) {696 warn(`Unexpected mixed usage of different slot syntaxes.`, el)697 }698 if (el.scopedSlots) {699 warn(700 `To avoid scope ambiguity, the default slot should also use ` +701 `<template> syntax when there are other named slots.`,702 slotBinding703 )704 }705 }706 // add the component's children to its default slot707 const slots = el.scopedSlots || (el.scopedSlots = {})708 const { name, dynamic } = getSlotName(slotBinding)709 const slotContainer = (slots[name] = createASTElement(710 'template',711 [],712 el713 ))714 slotContainer.slotTarget = name715 slotContainer.slotTargetDynamic = dynamic716 slotContainer.children = el.children.filter((c: any) => {717 if (!c.slotScope) {718 c.parent = slotContainer719 return true720 }721 })722 slotContainer.slotScope = slotBinding.value || emptySlotScopeToken723 // remove children as they are returned from scopedSlots now724 el.children = []725 // mark el non-plain so data gets generated726 el.plain = false727 }728 }729 }730}731732function getSlotName(binding) {733 let name = binding.name.replace(slotRE, '')734 if (!name) {735 if (binding.name[0] !== '#') {736 name = 'default'737 } else if (__DEV__) {738 warn(`v-slot shorthand syntax requires a slot name.`, binding)739 }740 }741 return dynamicArgRE.test(name)742 ? // dynamic [name]743 { name: name.slice(1, -1), dynamic: true }744 : // static name745 { name: `"${name}"`, dynamic: false }746}747748// handle <slot/> outlets749function processSlotOutlet(el) {750 if (el.tag === 'slot') {751 el.slotName = getBindingAttr(el, 'name')752 if (__DEV__ && el.key) {753 warn(754 `\`key\` does not work on <slot> because slots are abstract outlets ` +755 `and can possibly expand into multiple elements. ` +756 `Use the key on a wrapping element instead.`,757 getRawBindingAttr(el, 'key')758 )759 }760 }761}762763function processComponent(el) {764 let binding765 if ((binding = getBindingAttr(el, 'is'))) {766 el.component = binding767 }768 if (getAndRemoveAttr(el, 'inline-template') != null) {769 el.inlineTemplate = true770 }771}772773function processAttrs(el) {774 const list = el.attrsList775 let i, l, name, rawName, value, modifiers, syncGen, isDynamic776 for (i = 0, l = list.length; i < l; i++) {777 name = rawName = list[i].name778 value = list[i].value779 if (dirRE.test(name)) {780 // mark element as dynamic781 el.hasBindings = true782 // modifiers783 modifiers = parseModifiers(name.replace(dirRE, ''))784 // support .foo shorthand syntax for the .prop modifier785 if (process.env.VBIND_PROP_SHORTHAND && propBindRE.test(name)) {786 ;(modifiers || (modifiers = {})).prop = true787 name = `.` + name.slice(1).replace(modifierRE, '')788 } else if (modifiers) {789 name = name.replace(modifierRE, '')790 }791 if (bindRE.test(name)) {792 // v-bind793 name = name.replace(bindRE, '')794 value = parseFilters(value)795 isDynamic = dynamicArgRE.test(name)796 if (isDynamic) {797 name = name.slice(1, -1)798 }799 if (__DEV__ && value.trim().length === 0) {800 warn(801 `The value for a v-bind expression cannot be empty. Found in "v-bind:${name}"`802 )803 }804 if (modifiers) {805 if (modifiers.prop && !isDynamic) {806 name = camelize(name)807 if (name === 'innerHtml') name = 'innerHTML'808 }809 if (modifiers.camel && !isDynamic) {810 name = camelize(name)811 }812 if (modifiers.sync) {813 syncGen = genAssignmentCode(value, `$event`)814 if (!isDynamic) {815 addHandler(816 el,817 `update:${camelize(name)}`,818 syncGen,819 null,820 false,821 warn,822 list[i]823 )824 if (hyphenate(name) !== camelize(name)) {825 addHandler(826 el,827 `update:${hyphenate(name)}`,828 syncGen,829 null,830 false,831 warn,832 list[i]833 )834 }835 } else {836 // handler w/ dynamic event name837 addHandler(838 el,839 `"update:"+(${name})`,840 syncGen,841 null,842 false,843 warn,844 list[i],845 true // dynamic846 )847 }848 }849 }850 if (851 (modifiers && modifiers.prop) ||852 (!el.component && platformMustUseProp(el.tag, el.attrsMap.type, name))853 ) {854 addProp(el, name, value, list[i], isDynamic)855 } else {856 addAttr(el, name, value, list[i], isDynamic)857 }858 } else if (onRE.test(name)) {859 // v-on860 name = name.replace(onRE, '')861 isDynamic = dynamicArgRE.test(name)862 if (isDynamic) {863 name = name.slice(1, -1)864 }865 addHandler(el, name, value, modifiers, false, warn, list[i], isDynamic)866 } else {867 // normal directives868 name = name.replace(dirRE, '')869 // parse arg870 const argMatch = name.match(argRE)871 let arg = argMatch && argMatch[1]872 isDynamic = false873 if (arg) {874 name = name.slice(0, -(arg.length + 1))875 if (dynamicArgRE.test(arg)) {876 arg = arg.slice(1, -1)877 isDynamic = true878 }879 }880 addDirective(881 el,882 name,883 rawName,884 value,885 arg,886 isDynamic,887 modifiers,888 list[i]889 )890 if (__DEV__ && name === 'model') {891 checkForAliasModel(el, value)892 }893 }894 } else {895 // literal attribute896 if (__DEV__) {897 const res = parseText(value, delimiters)898 if (res) {899 warn(900 `${name}="${value}": ` +901 'Interpolation inside attributes has been removed. ' +902 'Use v-bind or the colon shorthand instead. For example, ' +903 'instead of <div id="{{ val }}">, use <div :id="val">.',904 list[i]905 )906 }907 }908 addAttr(el, name, JSON.stringify(value), list[i])909 // #6887 firefox doesn't update muted state if set via attribute910 // even immediately after element creation911 if (912 !el.component &&913 name === 'muted' &&914 platformMustUseProp(el.tag, el.attrsMap.type, name)915 ) {916 addProp(el, name, 'true', list[i])917 }918 }919 }920}921922function checkInFor(el: ASTElement): boolean {923 let parent: ASTElement | void = el924 while (parent) {925 if (parent.for !== undefined) {926 return true927 }928 parent = parent.parent929 }930 return false931}932933function parseModifiers(name: string): Object | void {934 const match = name.match(modifierRE)935 if (match) {936 const ret = {}937 match.forEach(m => {938 ret[m.slice(1)] = true939 })940 return ret941 }942}943944function makeAttrsMap(attrs: Array<Record<string, any>>): Record<string, any> {945 const map = {}946 for (let i = 0, l = attrs.length; i < l; i++) {947 if (__DEV__ && map[attrs[i].name] && !isIE && !isEdge) {948 warn('duplicate attribute: ' + attrs[i].name, attrs[i])949 }950 map[attrs[i].name] = attrs[i].value951 }952 return map953}954955// for script (e.g. type="x/template") or style, do not decode content956function isTextTag(el): boolean {957 return el.tag === 'script' || el.tag === 'style'958}959960function isForbiddenTag(el): boolean {961 return (962 el.tag === 'style' ||963 (el.tag === 'script' &&964 (!el.attrsMap.type || el.attrsMap.type === 'text/javascript'))965 )966}967968const ieNSBug = /^xmlns:NS\d+/969const ieNSPrefix = /^NS\d+:/970971/* istanbul ignore next */972function guardIESVGBug(attrs) {973 const res: any[] = []974 for (let i = 0; i < attrs.length; i++) {975 const attr = attrs[i]976 if (!ieNSBug.test(attr.name)) {977 attr.name = attr.name.replace(ieNSPrefix, '')978 res.push(attr)979 }980 }981 return res982}983984function checkForAliasModel(el, value) {985 let _el = el986 while (_el) {987 if (_el.for && _el.alias === value) {988 warn(989 `<${el.tag} v-model="${value}">: ` +990 `You are binding v-model directly to a v-for iteration alias. ` +991 `This will not be able to modify the v-for source array because ` +992 `writing to the alias is like modifying a function local variable. ` +993 `Consider using an array of objects and use v-model on an object property instead.`,994 el.rawAttrsMap['v-model']995 )996 }997 _el = _el.parent998 }999}
Findings
✓ No findings reported for this file.