aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vue/src/compiler/parser
diff options
context:
space:
mode:
authorruki <waruqi@gmail.com>2018-11-08 00:38:48 +0800
committerruki <waruqi@gmail.com>2018-11-07 21:53:09 +0800
commit26105034da4fcce7ac883c899d781f016559310d (patch)
treec459a5dc4e3aa0972d9919033ece511ce76dd129 /node_modules/vue/src/compiler/parser
parent2c77f00f1a7ecb6c8192f9c16d3b2001b254a107 (diff)
downloadxmake-docs-26105034da4fcce7ac883c899d781f016559310d.tar.gz
xmake-docs-26105034da4fcce7ac883c899d781f016559310d.zip
switch to vuepress
Diffstat (limited to 'node_modules/vue/src/compiler/parser')
-rw-r--r--node_modules/vue/src/compiler/parser/entity-decoder.js11
-rw-r--r--node_modules/vue/src/compiler/parser/filter-parser.js97
-rw-r--r--node_modules/vue/src/compiler/parser/html-parser.js310
-rw-r--r--node_modules/vue/src/compiler/parser/index.js675
-rw-r--r--node_modules/vue/src/compiler/parser/text-parser.js53
5 files changed, 1146 insertions, 0 deletions
diff --git a/node_modules/vue/src/compiler/parser/entity-decoder.js b/node_modules/vue/src/compiler/parser/entity-decoder.js
new file mode 100644
index 00000000..2836294f
--- /dev/null
+++ b/node_modules/vue/src/compiler/parser/entity-decoder.js
@@ -0,0 +1,11 @@
+/* @flow */
+
+let decoder
+
+export default {
+ decode (html: string): string {
+ decoder = decoder || document.createElement('div')
+ decoder.innerHTML = html
+ return decoder.textContent
+ }
+}
diff --git a/node_modules/vue/src/compiler/parser/filter-parser.js b/node_modules/vue/src/compiler/parser/filter-parser.js
new file mode 100644
index 00000000..10939d3f
--- /dev/null
+++ b/node_modules/vue/src/compiler/parser/filter-parser.js
@@ -0,0 +1,97 @@
+/* @flow */
+
+const validDivisionCharRE = /[\w).+\-_$\]]/
+
+export function parseFilters (exp: string): string {
+ let inSingle = false
+ let inDouble = false
+ let inTemplateString = false
+ let inRegex = false
+ let curly = 0
+ let square = 0
+ let paren = 0
+ let lastFilterIndex = 0
+ let c, prev, i, expression, filters
+
+ for (i = 0; i < exp.length; i++) {
+ prev = c
+ c = exp.charCodeAt(i)
+ if (inSingle) {
+ if (c === 0x27 && prev !== 0x5C) inSingle = false
+ } else if (inDouble) {
+ if (c === 0x22 && prev !== 0x5C) inDouble = false
+ } else if (inTemplateString) {
+ if (c === 0x60 && prev !== 0x5C) inTemplateString = false
+ } else if (inRegex) {
+ if (c === 0x2f && prev !== 0x5C) inRegex = false
+ } else if (
+ c === 0x7C && // pipe
+ exp.charCodeAt(i + 1) !== 0x7C &&
+ exp.charCodeAt(i - 1) !== 0x7C &&
+ !curly && !square && !paren
+ ) {
+ if (expression === undefined) {
+ // first filter, end of expression
+ lastFilterIndex = i + 1
+ expression = exp.slice(0, i).trim()
+ } else {
+ pushFilter()
+ }
+ } else {
+ switch (c) {
+ case 0x22: inDouble = true; break // "
+ case 0x27: inSingle = true; break // '
+ case 0x60: inTemplateString = true; break // `
+ case 0x28: paren++; break // (
+ case 0x29: paren--; break // )
+ case 0x5B: square++; break // [
+ case 0x5D: square--; break // ]
+ case 0x7B: curly++; break // {
+ case 0x7D: curly--; break // }
+ }
+ if (c === 0x2f) { // /
+ let j = i - 1
+ let p
+ // find first non-whitespace prev char
+ for (; j >= 0; j--) {
+ p = exp.charAt(j)
+ if (p !== ' ') break
+ }
+ if (!p || !validDivisionCharRE.test(p)) {
+ inRegex = true
+ }
+ }
+ }
+ }
+
+ if (expression === undefined) {
+ expression = exp.slice(0, i).trim()
+ } else if (lastFilterIndex !== 0) {
+ pushFilter()
+ }
+
+ function pushFilter () {
+ (filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim())
+ lastFilterIndex = i + 1
+ }
+
+ if (filters) {
+ for (i = 0; i < filters.length; i++) {
+ expression = wrapFilter(expression, filters[i])
+ }
+ }
+
+ return expression
+}
+
+function wrapFilter (exp: string, filter: string): string {
+ const i = filter.indexOf('(')
+ if (i < 0) {
+ // _f: resolveFilter
+ return `_f("${filter}")(${exp})`
+ } else {
+ const name = filter.slice(0, i)
+ const args = filter.slice(i + 1)
+ return `_f("${name}")(${exp}${args !== ')' ? ',' + args : args}`
+ }
+}
diff --git a/node_modules/vue/src/compiler/parser/html-parser.js b/node_modules/vue/src/compiler/parser/html-parser.js
new file mode 100644
index 00000000..e2203613
--- /dev/null
+++ b/node_modules/vue/src/compiler/parser/html-parser.js
@@ -0,0 +1,310 @@
+/**
+ * Not type-checking this file because it's mostly vendor code.
+ */
+
+/*!
+ * HTML Parser By John Resig (ejohn.org)
+ * Modified by Juriy "kangax" Zaytsev
+ * Original code by Erik Arvidsson, Mozilla Public License
+ * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
+ */
+
+import { makeMap, no } from 'shared/util'
+import { isNonPhrasingTag } from 'web/compiler/util'
+
+// Regular Expressions for parsing tags and attributes
+const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
+// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName
+// but for Vue templates we can enforce a simple charset
+const ncname = '[a-zA-Z_][\\w\\-\\.]*'
+const qnameCapture = `((?:${ncname}\\:)?${ncname})`
+const startTagOpen = new RegExp(`^<${qnameCapture}`)
+const startTagClose = /^\s*(\/?)>/
+const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
+const doctype = /^<!DOCTYPE [^>]+>/i
+// #7298: escape - to avoid being pased as HTML comment when inlined in page
+const comment = /^<!\--/
+const conditionalComment = /^<!\[/
+
+let IS_REGEX_CAPTURING_BROKEN = false
+'x'.replace(/x(.)?/g, function (m, g) {
+ IS_REGEX_CAPTURING_BROKEN = g === ''
+})
+
+// Special Elements (can contain anything)
+export const isPlainTextElement = makeMap('script,style,textarea', true)
+const reCache = {}
+
+const decodingMap = {
+ '&lt;': '<',
+ '&gt;': '>',
+ '&quot;': '"',
+ '&amp;': '&',
+ '&#10;': '\n',
+ '&#9;': '\t'
+}
+const encodedAttr = /&(?:lt|gt|quot|amp);/g
+const encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10|#9);/g
+
+// #5992
+const isIgnoreNewlineTag = makeMap('pre,textarea', true)
+const shouldIgnoreFirstNewline = (tag, html) => tag && isIgnoreNewlineTag(tag) && html[0] === '\n'
+
+function decodeAttr (value, shouldDecodeNewlines) {
+ const re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr
+ return value.replace(re, match => decodingMap[match])
+}
+
+export function parseHTML (html, options) {
+ const stack = []
+ const expectHTML = options.expectHTML
+ const isUnaryTag = options.isUnaryTag || no
+ const canBeLeftOpenTag = options.canBeLeftOpenTag || no
+ let index = 0
+ let last, lastTag
+ while (html) {
+ last = html
+ // Make sure we're not in a plaintext content element like script/style
+ if (!lastTag || !isPlainTextElement(lastTag)) {
+ let textEnd = html.indexOf('<')
+ if (textEnd === 0) {
+ // Comment:
+ if (comment.test(html)) {
+ const commentEnd = html.indexOf('-->')
+
+ if (commentEnd >= 0) {
+ if (options.shouldKeepComment) {
+ options.comment(html.substring(4, commentEnd))
+ }
+ advance(commentEnd + 3)
+ continue
+ }
+ }
+
+ // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
+ if (conditionalComment.test(html)) {
+ const conditionalEnd = html.indexOf(']>')
+
+ if (conditionalEnd >= 0) {
+ advance(conditionalEnd + 2)
+ continue
+ }
+ }
+
+ // Doctype:
+ const doctypeMatch = html.match(doctype)
+ if (doctypeMatch) {
+ advance(doctypeMatch[0].length)
+ continue
+ }
+
+ // End tag:
+ const endTagMatch = html.match(endTag)
+ if (endTagMatch) {
+ const curIndex = index
+ advance(endTagMatch[0].length)
+ parseEndTag(endTagMatch[1], curIndex, index)
+ continue
+ }
+
+ // Start tag:
+ const startTagMatch = parseStartTag()
+ if (startTagMatch) {
+ handleStartTag(startTagMatch)
+ if (shouldIgnoreFirstNewline(lastTag, html)) {
+ advance(1)
+ }
+ continue
+ }
+ }
+
+ let text, rest, next
+ if (textEnd >= 0) {
+ rest = html.slice(textEnd)
+ while (
+ !endTag.test(rest) &&
+ !startTagOpen.test(rest) &&
+ !comment.test(rest) &&
+ !conditionalComment.test(rest)
+ ) {
+ // < in plain text, be forgiving and treat it as text
+ next = rest.indexOf('<', 1)
+ if (next < 0) break
+ textEnd += next
+ rest = html.slice(textEnd)
+ }
+ text = html.substring(0, textEnd)
+ advance(textEnd)
+ }
+
+ if (textEnd < 0) {
+ text = html
+ html = ''
+ }
+
+ if (options.chars && text) {
+ options.chars(text)
+ }
+ } else {
+ let endTagLength = 0
+ const stackedTag = lastTag.toLowerCase()
+ const reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i'))
+ const rest = html.replace(reStackedTag, function (all, text, endTag) {
+ endTagLength = endTag.length
+ if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') {
+ text = text
+ .replace(/<!\--([\s\S]*?)-->/g, '$1') // #7298
+ .replace(/<!\[CDATA\[([\s\S]*?)]]>/g, '$1')
+ }
+ if (shouldIgnoreFirstNewline(stackedTag, text)) {
+ text = text.slice(1)
+ }
+ if (options.chars) {
+ options.chars(text)
+ }
+ return ''
+ })
+ index += html.length - rest.length
+ html = rest
+ parseEndTag(stackedTag, index - endTagLength, index)
+ }
+
+ if (html === last) {
+ options.chars && options.chars(html)
+ if (process.env.NODE_ENV !== 'production' && !stack.length && options.warn) {
+ options.warn(`Mal-formatted tag at end of template: "${html}"`)
+ }
+ break
+ }
+ }
+
+ // Clean up any remaining tags
+ parseEndTag()
+
+ function advance (n) {
+ index += n
+ html = html.substring(n)
+ }
+
+ function parseStartTag () {
+ const start = html.match(startTagOpen)
+ if (start) {
+ const match = {
+ tagName: start[1],
+ attrs: [],
+ start: index
+ }
+ advance(start[0].length)
+ let end, attr
+ while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
+ advance(attr[0].length)
+ match.attrs.push(attr)
+ }
+ if (end) {
+ match.unarySlash = end[1]
+ advance(end[0].length)
+ match.end = index
+ return match
+ }
+ }
+ }
+
+ function handleStartTag (match) {
+ const tagName = match.tagName
+ const unarySlash = match.unarySlash
+
+ if (expectHTML) {
+ if (lastTag === 'p' && isNonPhrasingTag(tagName)) {
+ parseEndTag(lastTag)
+ }
+ if (canBeLeftOpenTag(tagName) && lastTag === tagName) {
+ parseEndTag(tagName)
+ }
+ }
+
+ const unary = isUnaryTag(tagName) || !!unarySlash
+
+ const l = match.attrs.length
+ const attrs = new Array(l)
+ for (let i = 0; i < l; i++) {
+ const args = match.attrs[i]
+ // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778
+ if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) {
+ if (args[3] === '') { delete args[3] }
+ if (args[4] === '') { delete args[4] }
+ if (args[5] === '') { delete args[5] }
+ }
+ const value = args[3] || args[4] || args[5] || ''
+ const shouldDecodeNewlines = tagName === 'a' && args[1] === 'href'
+ ? options.shouldDecodeNewlinesForHref
+ : options.shouldDecodeNewlines
+ attrs[i] = {
+ name: args[1],
+ value: decodeAttr(value, shouldDecodeNewlines)
+ }
+ }
+
+ if (!unary) {
+ stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs })
+ lastTag = tagName
+ }
+
+ if (options.start) {
+ options.start(tagName, attrs, unary, match.start, match.end)
+ }
+ }
+
+ function parseEndTag (tagName, start, end) {
+ let pos, lowerCasedTagName
+ if (start == null) start = index
+ if (end == null) end = index
+
+ if (tagName) {
+ lowerCasedTagName = tagName.toLowerCase()
+ }
+
+ // Find the closest opened tag of the same type
+ if (tagName) {
+ for (pos = stack.length - 1; pos >= 0; pos--) {
+ if (stack[pos].lowerCasedTag === lowerCasedTagName) {
+ break
+ }
+ }
+ } else {
+ // If no tag name is provided, clean shop
+ pos = 0
+ }
+
+ if (pos >= 0) {
+ // Close all the open elements, up the stack
+ for (let i = stack.length - 1; i >= pos; i--) {
+ if (process.env.NODE_ENV !== 'production' &&
+ (i > pos || !tagName) &&
+ options.warn
+ ) {
+ options.warn(
+ `tag <${stack[i].tag}> has no matching end tag.`
+ )
+ }
+ if (options.end) {
+ options.end(stack[i].tag, start, end)
+ }
+ }
+
+ // Remove the open elements from the stack
+ stack.length = pos
+ lastTag = pos && stack[pos - 1].tag
+ } else if (lowerCasedTagName === 'br') {
+ if (options.start) {
+ options.start(tagName, [], true, start, end)
+ }
+ } else if (lowerCasedTagName === 'p') {
+ if (options.start) {
+ options.start(tagName, [], false, start, end)
+ }
+ if (options.end) {
+ options.end(tagName, start, end)
+ }
+ }
+ }
+}
diff --git a/node_modules/vue/src/compiler/parser/index.js b/node_modules/vue/src/compiler/parser/index.js
new file mode 100644
index 00000000..a603b36b
--- /dev/null
+++ b/node_modules/vue/src/compiler/parser/index.js
@@ -0,0 +1,675 @@
+/* @flow */
+
+import he from 'he'
+import { parseHTML } from './html-parser'
+import { parseText } from './text-parser'
+import { parseFilters } from './filter-parser'
+import { genAssignmentCode } from '../directives/model'
+import { extend, cached, no, camelize } from 'shared/util'
+import { isIE, isEdge, isServerRendering } from 'core/util/env'
+
+import {
+ addProp,
+ addAttr,
+ baseWarn,
+ addHandler,
+ addDirective,
+ getBindingAttr,
+ getAndRemoveAttr,
+ pluckModuleFunction
+} from '../helpers'
+
+export const onRE = /^@|^v-on:/
+export const dirRE = /^v-|^@|^:/
+export const forAliasRE = /([^]*?)\s+(?:in|of)\s+([^]*)/
+export const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
+const stripParensRE = /^\(|\)$/g
+
+const argRE = /:(.*)$/
+export const bindRE = /^:|^v-bind:/
+const modifierRE = /\.[^.]+/g
+
+const decodeHTMLCached = cached(he.decode)
+
+// configurable state
+export let warn: any
+let delimiters
+let transforms
+let preTransforms
+let postTransforms
+let platformIsPreTag
+let platformMustUseProp
+let platformGetTagNamespace
+
+type Attr = { name: string; value: string };
+
+export function createASTElement (
+ tag: string,
+ attrs: Array<Attr>,
+ parent: ASTElement | void
+): ASTElement {
+ return {
+ type: 1,
+ tag,
+ attrsList: attrs,
+ attrsMap: makeAttrsMap(attrs),
+ parent,
+ children: []
+ }
+}
+
+/**
+ * Convert HTML string to AST.
+ */
+export function parse (
+ template: string,
+ options: CompilerOptions
+): ASTElement | void {
+ warn = options.warn || baseWarn
+
+ platformIsPreTag = options.isPreTag || no
+ platformMustUseProp = options.mustUseProp || no
+ platformGetTagNamespace = options.getTagNamespace || no
+
+ transforms = pluckModuleFunction(options.modules, 'transformNode')
+ preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
+ postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')
+
+ delimiters = options.delimiters
+
+ const stack = []
+ const preserveWhitespace = options.preserveWhitespace !== false
+ let root
+ let currentParent
+ let inVPre = false
+ let inPre = false
+ let warned = false
+
+ function warnOnce (msg) {
+ if (!warned) {
+ warned = true
+ warn(msg)
+ }
+ }
+
+ function closeElement (element) {
+ // check pre state
+ if (element.pre) {
+ inVPre = false
+ }
+ if (platformIsPreTag(element.tag)) {
+ inPre = false
+ }
+ // apply post-transforms
+ for (let i = 0; i < postTransforms.length; i++) {
+ postTransforms[i](element, options)
+ }
+ }
+
+ parseHTML(template, {
+ warn,
+ expectHTML: options.expectHTML,
+ isUnaryTag: options.isUnaryTag,
+ canBeLeftOpenTag: options.canBeLeftOpenTag,
+ shouldDecodeNewlines: options.shouldDecodeNewlines,
+ shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
+ shouldKeepComment: options.comments,
+ start (tag, attrs, unary) {
+ // check namespace.
+ // inherit parent ns if there is one
+ const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag)
+
+ // handle IE svg bug
+ /* istanbul ignore if */
+ if (isIE && ns === 'svg') {
+ attrs = guardIESVGBug(attrs)
+ }
+
+ let element: ASTElement = createASTElement(tag, attrs, currentParent)
+ if (ns) {
+ element.ns = ns
+ }
+
+ if (isForbiddenTag(element) && !isServerRendering()) {
+ element.forbidden = true
+ process.env.NODE_ENV !== 'production' && warn(
+ 'Templates should only be responsible for mapping the state to the ' +
+ 'UI. Avoid placing tags with side-effects in your templates, such as ' +
+ `<${tag}>` + ', as they will not be parsed.'
+ )
+ }
+
+ // apply pre-transforms
+ for (let i = 0; i < preTransforms.length; i++) {
+ element = preTransforms[i](element, options) || element
+ }
+
+ if (!inVPre) {
+ processPre(element)
+ if (element.pre) {
+ inVPre = true
+ }
+ }
+ if (platformIsPreTag(element.tag)) {
+ inPre = true
+ }
+ if (inVPre) {
+ processRawAttrs(element)
+ } else if (!element.processed) {
+ // structural directives
+ processFor(element)
+ processIf(element)
+ processOnce(element)
+ // element-scope stuff
+ processElement(element, options)
+ }
+
+ function checkRootConstraints (el) {
+ if (process.env.NODE_ENV !== 'production') {
+ if (el.tag === 'slot' || el.tag === 'template') {
+ warnOnce(
+ `Cannot use <${el.tag}> as component root element because it may ` +
+ 'contain multiple nodes.'
+ )
+ }
+ if (el.attrsMap.hasOwnProperty('v-for')) {
+ warnOnce(
+ 'Cannot use v-for on stateful component root element because ' +
+ 'it renders multiple elements.'
+ )
+ }
+ }
+ }
+
+ // tree management
+ if (!root) {
+ root = element
+ checkRootConstraints(root)
+ } else if (!stack.length) {
+ // allow root elements with v-if, v-else-if and v-else
+ if (root.if && (element.elseif || element.else)) {
+ checkRootConstraints(element)
+ addIfCondition(root, {
+ exp: element.elseif,
+ block: element
+ })
+ } else if (process.env.NODE_ENV !== 'production') {
+ warnOnce(
+ `Component template should contain exactly one root element. ` +
+ `If you are using v-if on multiple elements, ` +
+ `use v-else-if to chain them instead.`
+ )
+ }
+ }
+ if (currentParent && !element.forbidden) {
+ if (element.elseif || element.else) {
+ processIfConditions(element, currentParent)
+ } else if (element.slotScope) { // scoped slot
+ currentParent.plain = false
+ const name = element.slotTarget || '"default"'
+ ;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element
+ } else {
+ currentParent.children.push(element)
+ element.parent = currentParent
+ }
+ }
+ if (!unary) {
+ currentParent = element
+ stack.push(element)
+ } else {
+ closeElement(element)
+ }
+ },
+
+ end () {
+ // remove trailing whitespace
+ const element = stack[stack.length - 1]
+ const lastNode = element.children[element.children.length - 1]
+ if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) {
+ element.children.pop()
+ }
+ // pop stack
+ stack.length -= 1
+ currentParent = stack[stack.length - 1]
+ closeElement(element)
+ },
+
+ chars (text: string) {
+ if (!currentParent) {
+ if (process.env.NODE_ENV !== 'production') {
+ if (text === template) {
+ warnOnce(
+ 'Component template requires a root element, rather than just text.'
+ )
+ } else if ((text = text.trim())) {
+ warnOnce(
+ `text "${text}" outside root element will be ignored.`
+ )
+ }
+ }
+ return
+ }
+ // IE textarea placeholder bug
+ /* istanbul ignore if */
+ if (isIE &&
+ currentParent.tag === 'textarea' &&
+ currentParent.attrsMap.placeholder === text
+ ) {
+ return
+ }
+ const children = currentParent.children
+ text = inPre || text.trim()
+ ? isTextTag(currentParent) ? text : decodeHTMLCached(text)
+ // only preserve whitespace if its not right after a starting tag
+ : preserveWhitespace && children.length ? ' ' : ''
+ if (text) {
+ let res
+ if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) {
+ children.push({
+ type: 2,
+ expression: res.expression,
+ tokens: res.tokens,
+ text
+ })
+ } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
+ children.push({
+ type: 3,
+ text
+ })
+ }
+ }
+ },
+ comment (text: string) {
+ currentParent.children.push({
+ type: 3,
+ text,
+ isComment: true
+ })
+ }
+ })
+ return root
+}
+
+function processPre (el) {
+ if (getAndRemoveAttr(el, 'v-pre') != null) {
+ el.pre = true
+ }
+}
+
+function processRawAttrs (el) {
+ const l = el.attrsList.length
+ if (l) {
+ const attrs = el.attrs = new Array(l)
+ for (let i = 0; i < l; i++) {
+ attrs[i] = {
+ name: el.attrsList[i].name,
+ value: JSON.stringify(el.attrsList[i].value)
+ }
+ }
+ } else if (!el.pre) {
+ // non root node in pre blocks with no attributes
+ el.plain = true
+ }
+}
+
+export function processElement (element: ASTElement, options: CompilerOptions) {
+ processKey(element)
+
+ // determine whether this is a plain element after
+ // removing structural attributes
+ element.plain = !element.key && !element.attrsList.length
+
+ processRef(element)
+ processSlot(element)
+ processComponent(element)
+ for (let i = 0; i < transforms.length; i++) {
+ element = transforms[i](element, options) || element
+ }
+ processAttrs(element)
+}
+
+function processKey (el) {
+ const exp = getBindingAttr(el, 'key')
+ if (exp) {
+ if (process.env.NODE_ENV !== 'production' && el.tag === 'template') {
+ warn(`<template> cannot be keyed. Place the key on real elements instead.`)
+ }
+ el.key = exp
+ }
+}
+
+function processRef (el) {
+ const ref = getBindingAttr(el, 'ref')
+ if (ref) {
+ el.ref = ref
+ el.refInFor = checkInFor(el)
+ }
+}
+
+export function processFor (el: ASTElement) {
+ let exp
+ if ((exp = getAndRemoveAttr(el, 'v-for'))) {
+ const res = parseFor(exp)
+ if (res) {
+ extend(el, res)
+ } else if (process.env.NODE_ENV !== 'production') {
+ warn(
+ `Invalid v-for expression: ${exp}`
+ )
+ }
+ }
+}
+
+type ForParseResult = {
+ for: string;
+ alias: string;
+ iterator1?: string;
+ iterator2?: string;
+};
+
+export function parseFor (exp: string): ?ForParseResult {
+ const inMatch = exp.match(forAliasRE)
+ if (!inMatch) return
+ const res = {}
+ res.for = inMatch[2].trim()
+ const alias = inMatch[1].trim().replace(stripParensRE, '')
+ const iteratorMatch = alias.match(forIteratorRE)
+ if (iteratorMatch) {
+ res.alias = alias.replace(forIteratorRE, '')
+ res.iterator1 = iteratorMatch[1].trim()
+ if (iteratorMatch[2]) {
+ res.iterator2 = iteratorMatch[2].trim()
+ }
+ } else {
+ res.alias = alias
+ }
+ return res
+}
+
+function processIf (el) {
+ const exp = getAndRemoveAttr(el, 'v-if')
+ if (exp) {
+ el.if = exp
+ addIfCondition(el, {
+ exp: exp,
+ block: el
+ })
+ } else {
+ if (getAndRemoveAttr(el, 'v-else') != null) {
+ el.else = true
+ }
+ const elseif = getAndRemoveAttr(el, 'v-else-if')
+ if (elseif) {
+ el.elseif = elseif
+ }
+ }
+}
+
+function processIfConditions (el, parent) {
+ const prev = findPrevElement(parent.children)
+ if (prev && prev.if) {
+ addIfCondition(prev, {
+ exp: el.elseif,
+ block: el
+ })
+ } else if (process.env.NODE_ENV !== 'production') {
+ warn(
+ `v-${el.elseif ? ('else-if="' + el.elseif + '"') : 'else'} ` +
+ `used on element <${el.tag}> without corresponding v-if.`
+ )
+ }
+}
+
+function findPrevElement (children: Array<any>): ASTElement | void {
+ let i = children.length
+ while (i--) {
+ if (children[i].type === 1) {
+ return children[i]
+ } else {
+ if (process.env.NODE_ENV !== 'production' && children[i].text !== ' ') {
+ warn(
+ `text "${children[i].text.trim()}" between v-if and v-else(-if) ` +
+ `will be ignored.`
+ )
+ }
+ children.pop()
+ }
+ }
+}
+
+export function addIfCondition (el: ASTElement, condition: ASTIfCondition) {
+ if (!el.ifConditions) {
+ el.ifConditions = []
+ }
+ el.ifConditions.push(condition)
+}
+
+function processOnce (el) {
+ const once = getAndRemoveAttr(el, 'v-once')
+ if (once != null) {
+ el.once = true
+ }
+}
+
+function processSlot (el) {
+ if (el.tag === 'slot') {
+ el.slotName = getBindingAttr(el, 'name')
+ if (process.env.NODE_ENV !== 'production' && el.key) {
+ warn(
+ `\`key\` does not work on <slot> because slots are abstract outlets ` +
+ `and can possibly expand into multiple elements. ` +
+ `Use the key on a wrapping element instead.`
+ )
+ }
+ } else {
+ let slotScope
+ if (el.tag === 'template') {
+ slotScope = getAndRemoveAttr(el, 'scope')
+ /* istanbul ignore if */
+ if (process.env.NODE_ENV !== 'production' && slotScope) {
+ warn(
+ `the "scope" attribute for scoped slots have been deprecated and ` +
+ `replaced by "slot-scope" since 2.5. The new "slot-scope" attribute ` +
+ `can also be used on plain elements in addition to <template> to ` +
+ `denote scoped slots.`,
+ true
+ )
+ }
+ el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope')
+ } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {
+ /* istanbul ignore if */
+ if (process.env.NODE_ENV !== 'production' && el.attrsMap['v-for']) {
+ warn(
+ `Ambiguous combined usage of slot-scope and v-for on <${el.tag}> ` +
+ `(v-for takes higher priority). Use a wrapper <template> for the ` +
+ `scoped slot to make it clearer.`,
+ true
+ )
+ }
+ el.slotScope = slotScope
+ }
+ const slotTarget = getBindingAttr(el, 'slot')
+ if (slotTarget) {
+ el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
+ // preserve slot as an attribute for native shadow DOM compat
+ // only for non-scoped slots.
+ if (el.tag !== 'template' && !el.slotScope) {
+ addAttr(el, 'slot', slotTarget)
+ }
+ }
+ }
+}
+
+function processComponent (el) {
+ let binding
+ if ((binding = getBindingAttr(el, 'is'))) {
+ el.component = binding
+ }
+ if (getAndRemoveAttr(el, 'inline-template') != null) {
+ el.inlineTemplate = true
+ }
+}
+
+function processAttrs (el) {
+ const list = el.attrsList
+ let i, l, name, rawName, value, modifiers, isProp
+ for (i = 0, l = list.length; i < l; i++) {
+ name = rawName = list[i].name
+ value = list[i].value
+ if (dirRE.test(name)) {
+ // mark element as dynamic
+ el.hasBindings = true
+ // modifiers
+ modifiers = parseModifiers(name)
+ if (modifiers) {
+ name = name.replace(modifierRE, '')
+ }
+ if (bindRE.test(name)) { // v-bind
+ name = name.replace(bindRE, '')
+ value = parseFilters(value)
+ isProp = false
+ if (modifiers) {
+ if (modifiers.prop) {
+ isProp = true
+ name = camelize(name)
+ if (name === 'innerHtml') name = 'innerHTML'
+ }
+ if (modifiers.camel) {
+ name = camelize(name)
+ }
+ if (modifiers.sync) {
+ addHandler(
+ el,
+ `update:${camelize(name)}`,
+ genAssignmentCode(value, `$event`)
+ )
+ }
+ }
+ if (isProp || (
+ !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name)
+ )) {
+ addProp(el, name, value)
+ } else {
+ addAttr(el, name, value)
+ }
+ } else if (onRE.test(name)) { // v-on
+ name = name.replace(onRE, '')
+ addHandler(el, name, value, modifiers, false, warn)
+ } else { // normal directives
+ name = name.replace(dirRE, '')
+ // parse arg
+ const argMatch = name.match(argRE)
+ const arg = argMatch && argMatch[1]
+ if (arg) {
+ name = name.slice(0, -(arg.length + 1))
+ }
+ addDirective(el, name, rawName, value, arg, modifiers)
+ if (process.env.NODE_ENV !== 'production' && name === 'model') {
+ checkForAliasModel(el, value)
+ }
+ }
+ } else {
+ // literal attribute
+ if (process.env.NODE_ENV !== 'production') {
+ const res = parseText(value, delimiters)
+ if (res) {
+ warn(
+ `${name}="${value}": ` +
+ 'Interpolation inside attributes has been removed. ' +
+ 'Use v-bind or the colon shorthand instead. For example, ' +
+ 'instead of <div id="{{ val }}">, use <div :id="val">.'
+ )
+ }
+ }
+ addAttr(el, name, JSON.stringify(value))
+ // #6887 firefox doesn't update muted state if set via attribute
+ // even immediately after element creation
+ if (!el.component &&
+ name === 'muted' &&
+ platformMustUseProp(el.tag, el.attrsMap.type, name)) {
+ addProp(el, name, 'true')
+ }
+ }
+ }
+}
+
+function checkInFor (el: ASTElement): boolean {
+ let parent = el
+ while (parent) {
+ if (parent.for !== undefined) {
+ return true
+ }
+ parent = parent.parent
+ }
+ return false
+}
+
+function parseModifiers (name: string): Object | void {
+ const match = name.match(modifierRE)
+ if (match) {
+ const ret = {}
+ match.forEach(m => { ret[m.slice(1)] = true })
+ return ret
+ }
+}
+
+function makeAttrsMap (attrs: Array<Object>): Object {
+ const map = {}
+ for (let i = 0, l = attrs.length; i < l; i++) {
+ if (
+ process.env.NODE_ENV !== 'production' &&
+ map[attrs[i].name] && !isIE && !isEdge
+ ) {
+ warn('duplicate attribute: ' + attrs[i].name)
+ }
+ map[attrs[i].name] = attrs[i].value
+ }
+ return map
+}
+
+// for script (e.g. type="x/template") or style, do not decode content
+function isTextTag (el): boolean {
+ return el.tag === 'script' || el.tag === 'style'
+}
+
+function isForbiddenTag (el): boolean {
+ return (
+ el.tag === 'style' ||
+ (el.tag === 'script' && (
+ !el.attrsMap.type ||
+ el.attrsMap.type === 'text/javascript'
+ ))
+ )
+}
+
+const ieNSBug = /^xmlns:NS\d+/
+const ieNSPrefix = /^NS\d+:/
+
+/* istanbul ignore next */
+function guardIESVGBug (attrs) {
+ const res = []
+ for (let i = 0; i < attrs.length; i++) {
+ const attr = attrs[i]
+ if (!ieNSBug.test(attr.name)) {
+ attr.name = attr.name.replace(ieNSPrefix, '')
+ res.push(attr)
+ }
+ }
+ return res
+}
+
+function checkForAliasModel (el, value) {
+ let _el = el
+ while (_el) {
+ if (_el.for && _el.alias === value) {
+ warn(
+ `<${el.tag} v-model="${value}">: ` +
+ `You are binding v-model directly to a v-for iteration alias. ` +
+ `This will not be able to modify the v-for source array because ` +
+ `writing to the alias is like modifying a function local variable. ` +
+ `Consider using an array of objects and use v-model on an object property instead.`
+ )
+ }
+ _el = _el.parent
+ }
+}
diff --git a/node_modules/vue/src/compiler/parser/text-parser.js b/node_modules/vue/src/compiler/parser/text-parser.js
new file mode 100644
index 00000000..284fca33
--- /dev/null
+++ b/node_modules/vue/src/compiler/parser/text-parser.js
@@ -0,0 +1,53 @@
+/* @flow */
+
+import { cached } from 'shared/util'
+import { parseFilters } from './filter-parser'
+
+const defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g
+const regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g
+
+const buildRegex = cached(delimiters => {
+ const open = delimiters[0].replace(regexEscapeRE, '\\$&')
+ const close = delimiters[1].replace(regexEscapeRE, '\\$&')
+ return new RegExp(open + '((?:.|\\n)+?)' + close, 'g')
+})
+
+type TextParseResult = {
+ expression: string,
+ tokens: Array<string | { '@binding': string }>
+}
+
+export function parseText (
+ text: string,
+ delimiters?: [string, string]
+): TextParseResult | void {
+ const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE
+ if (!tagRE.test(text)) {
+ return
+ }
+ const tokens = []
+ const rawTokens = []
+ let lastIndex = tagRE.lastIndex = 0
+ let match, index, tokenValue
+ while ((match = tagRE.exec(text))) {
+ index = match.index
+ // push text token
+ if (index > lastIndex) {
+ rawTokens.push(tokenValue = text.slice(lastIndex, index))
+ tokens.push(JSON.stringify(tokenValue))
+ }
+ // tag token
+ const exp = parseFilters(match[1].trim())
+ tokens.push(`_s(${exp})`)
+ rawTokens.push({ '@binding': exp })
+ lastIndex = index + match[0].length
+ }
+ if (lastIndex < text.length) {
+ rawTokens.push(tokenValue = text.slice(lastIndex))
+ tokens.push(JSON.stringify(tokenValue))
+ }
+ return {
+ expression: tokens.join('+'),
+ tokens: rawTokens
+ }
+}