Skip to content

Commit

Permalink
feat($compile): display line numbers for errors in the parser
Browse files Browse the repository at this point in the history
  • Loading branch information
Kingwl committed Oct 24, 2017
1 parent 048e940 commit 6cf8df9
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 3 deletions.
26 changes: 23 additions & 3 deletions src/compiler/parser/html-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
*/

import { makeMap, no } from 'shared/util'
import { makeMap, no, splitLine } from 'shared/util'
import { isNonPhrasingTag } from 'web/compiler/util'

// Regular Expressions for parsing tags and attributes
Expand Down Expand Up @@ -53,11 +53,30 @@ function decodeAttr (value, shouldDecodeNewlines) {
return value.replace(re, match => decodingMap[match])
}

function resolveLineNumbers (lines, start) {
let l = 0
let cur = 0
for (let i = 0, len = lines.length; i < len; ++i) {
const { line, linefeed = '' } = lines[i]
if (cur + line.length < start) {
cur += (line.length + linefeed.length)
l++
} else {
break
}
}
return {
line: l,
column: start - cur
}
}

export function parseHTML (html, options) {
const stack = []
const expectHTML = options.expectHTML
const isUnaryTag = options.isUnaryTag || no
const canBeLeftOpenTag = options.canBeLeftOpenTag || no
const lines = splitLine(html)
let index = 0
let last, lastTag
while (html) {
Expand Down Expand Up @@ -243,7 +262,7 @@ export function parseHTML (html, options) {
}

if (!unary) {
stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs })
stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs, start: match.start })
lastTag = tagName
}

Expand Down Expand Up @@ -280,8 +299,9 @@ export function parseHTML (html, options) {
(i > pos || !tagName) &&
options.warn
) {
const { line, column } = resolveLineNumbers(lines, stack[i].start)
options.warn(
`tag <${stack[i].tag}> has no matching end tag.`
`tag <${stack[i].tag}> has no matching end tag at line ${line} column ${column}.`
)
}
if (options.end) {
Expand Down
24 changes: 24 additions & 0 deletions src/shared/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,27 @@ export function once (fn: Function): Function {
}
}
}

/**
* split string with different linefeed.
*/
const linefeedRE = /\r\n|\n|\u2028|\u2029/
export function splitLine (str: string): Array<Object> {
const lines = []
while (str) {
const line = linefeedRE.exec(str)
if (line) {
const linefeed = line[0]
const idx = line.index
lines.push({
line: str.substring(0, idx),
linefeed
})
str = str.substring(idx + linefeed.length)
} else {
lines.push({ length: str })
break
}
}
return lines
}
25 changes: 25 additions & 0 deletions test/unit/modules/compiler/parser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,4 +593,29 @@ describe('parser', () => {
expect(ast.children[1].isComment).toBe(true) // parse comment with ASTText
expect(ast.children[1].text).toBe('comment here')
})

it('should warn with line and column', () => {
parse(`
<div>
<span>
123
</div>
`, baseOptions)
expect(`tag <span> has no matching end tag at line 2 column 8.`).toHaveBeenWarned()
})

it('should warn with correct match', () => {
parse(`
<div>
<div>
123
</div>
`, baseOptions)
expect(`tag <div> has no matching end tag at line 1 column 6.`).toHaveBeenWarned()
})

it('should work with different linefeed', () => {
parse('<div>\n <div>\r\n <span>\u2028 123\u2029 </span>\n </div>\n <li>\n</div>', baseOptions)
expect(`tag <li> has no matching end tag at line 6 column 2.`).toHaveBeenWarned()
})
})

0 comments on commit 6cf8df9

Please sign in to comment.