Skip to content

Commit

Permalink
feat: detect and warn invalid dynamic argument expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Jan 28, 2019
1 parent 624c799 commit c9e3a5d
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 7 deletions.
3 changes: 2 additions & 1 deletion src/compiler/parser/html-parser.js
Expand Up @@ -15,6 +15,7 @@ import { unicodeLetters } from 'core/util/lang'

// Regular Expressions for parsing tags and attributes
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
const dynamicArgAttribute = /^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z${unicodeLetters}]*`
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
const startTagOpen = new RegExp(`^<${qnameCapture}`)
Expand Down Expand Up @@ -192,7 +193,7 @@ export function parseHTML (html, options) {
}
advance(start[0].length)
let end, attr
while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
while (!(end = html.match(startTagClose)) && (attr = html.match(dynamicArgAttribute) || html.match(attribute))) {
attr.start = index
advance(attr[0].length)
attr.end = index
Expand Down
28 changes: 22 additions & 6 deletions src/compiler/parser/index.js
Expand Up @@ -38,6 +38,8 @@ const slotRE = /^v-slot(:|$)|^#/
const lineBreakRE = /[\r\n]/
const whitespaceRE = /\s+/g

const invalidAttributeRE = /[\s"'<>\/=]/

const decodeHTMLCached = cached(he.decode)

// configurable state
Expand Down Expand Up @@ -194,12 +196,26 @@ export function parse (
element.ns = ns
}

if (process.env.NODE_ENV !== 'production' && options.outputSourceRange) {
element.start = start
element.rawAttrsMap = element.attrsList.reduce((cumulated, attr) => {
cumulated[attr.name] = attr
return cumulated
}, {})
if (process.env.NODE_ENV !== 'production') {
if (options.outputSourceRange) {
element.start = start
element.rawAttrsMap = element.attrsList.reduce((cumulated, attr) => {
cumulated[attr.name] = attr
return cumulated
}, {})
}
attrs.forEach(attr => {
if (invalidAttributeRE.test(attr.name)) {
warn(
`Invalid dynamic argument expression: attribute names cannot contain ` +
`spaces, quotes, <, >, / or =.`,
{
start: attr.start + attr.name.indexOf(`[`),
end: attr.start + attr.name.length
}
)
}
})
}

if (isForbiddenTag(element) && !isServerRendering()) {
Expand Down
17 changes: 17 additions & 0 deletions test/unit/modules/compiler/parser.spec.js
Expand Up @@ -550,6 +550,23 @@ describe('parser', () => {
expect(ast.props).toEqual([{ name: 'id', value: 'foo', dynamic: true }])
})

// This only works for string templates.
// In-DOM templates will be malformed before Vue can parse it.
describe('parse and warn invalid dynamic arguments', () => {
[
`<div v-bind:['foo' + bar]="baz"/>`,
`<div :['foo' + bar]="baz"/>`,
`<div @['foo' + bar]="baz"/>`,
`<foo #['foo' + bar]="baz"/>`,
`<div :['foo' + bar].some.mod="baz"/>`
].forEach(template => {
it(template, () => {
const ast = parse(template, baseOptions)
expect(`Invalid dynamic argument expression`).toHaveBeenWarned()
})
})
})

// #6887
it('special case static attribute that must be props', () => {
const ast = parse('<video muted></video>', baseOptions)
Expand Down

0 comments on commit c9e3a5d

Please sign in to comment.