feat: detect and warn invalid dynamic argument expressions

This commit is contained in:
Evan You 2019-01-28 18:08:37 -05:00
parent 624c79930a
commit c9e3a5d1d9
3 changed files with 41 additions and 7 deletions

View File

@ -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}`)
@ -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

View File

@ -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
@ -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()) {

View File

@ -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)