2019-12-11 01:53:26 +08:00
|
|
|
|
import { ParserOptions } from '../src/options'
|
2019-12-23 08:44:21 +08:00
|
|
|
|
import { baseParse, TextModes } from '../src/parse'
|
2019-09-18 07:08:47 +08:00
|
|
|
|
import { ErrorCodes } from '../src/errors'
|
2019-09-17 02:43:29 +08:00
|
|
|
|
import {
|
|
|
|
|
CommentNode,
|
|
|
|
|
ElementNode,
|
|
|
|
|
ElementTypes,
|
|
|
|
|
Namespaces,
|
|
|
|
|
NodeTypes,
|
|
|
|
|
Position,
|
2019-09-17 23:57:25 +08:00
|
|
|
|
TextNode,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
InterpolationNode,
|
|
|
|
|
ConstantTypes
|
2019-09-17 02:43:29 +08:00
|
|
|
|
} from '../src/ast'
|
|
|
|
|
|
2019-09-20 00:20:59 +08:00
|
|
|
|
describe('compiler: parse', () => {
|
2019-09-17 02:43:29 +08:00
|
|
|
|
describe('Text', () => {
|
|
|
|
|
test('simple text', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('some text')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const text = ast.children[0] as TextNode
|
|
|
|
|
|
|
|
|
|
expect(text).toStrictEqual({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'some text',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: 'some text'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('simple text with invalid end tag', () => {
|
2020-04-17 20:58:38 +08:00
|
|
|
|
const onError = jest.fn()
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('some text</div>', {
|
2020-04-17 20:58:38 +08:00
|
|
|
|
onError
|
2019-09-17 02:43:29 +08:00
|
|
|
|
})
|
|
|
|
|
const text = ast.children[0] as TextNode
|
|
|
|
|
|
2020-04-17 20:58:38 +08:00
|
|
|
|
expect(onError).toBeCalled()
|
2019-09-17 02:43:29 +08:00
|
|
|
|
expect(text).toStrictEqual({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'some text',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: 'some text'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('text with interpolation', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('some {{ foo + bar }} text')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const text1 = ast.children[0] as TextNode
|
|
|
|
|
const text2 = ast.children[2] as TextNode
|
|
|
|
|
|
|
|
|
|
expect(text1).toStrictEqual({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'some ',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
source: 'some '
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
expect(text2).toStrictEqual({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: ' text',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 20, line: 1, column: 21 },
|
|
|
|
|
end: { offset: 25, line: 1, column: 26 },
|
|
|
|
|
source: ' text'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('text with interpolation which has `<`', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('some {{ a<b && c>d }} text')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const text1 = ast.children[0] as TextNode
|
|
|
|
|
const text2 = ast.children[2] as TextNode
|
|
|
|
|
|
|
|
|
|
expect(text1).toStrictEqual({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'some ',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
source: 'some '
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
expect(text2).toStrictEqual({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: ' text',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 21, line: 1, column: 22 },
|
|
|
|
|
end: { offset: 26, line: 1, column: 27 },
|
|
|
|
|
source: ' text'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('text with mix of tags and interpolations', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('some <span>{{ foo < bar + foo }} text</span>')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const text1 = ast.children[0] as TextNode
|
|
|
|
|
const text2 = (ast.children[1] as ElementNode).children![1] as TextNode
|
|
|
|
|
|
|
|
|
|
expect(text1).toStrictEqual({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'some ',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
source: 'some '
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
expect(text2).toStrictEqual({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: ' text',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 32, line: 1, column: 33 },
|
|
|
|
|
end: { offset: 37, line: 1, column: 38 },
|
|
|
|
|
source: ' text'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2020-09-23 18:25:52 +08:00
|
|
|
|
test('lonely "<" doesn\'t separate nodes', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('a < b', {
|
2019-09-18 07:08:47 +08:00
|
|
|
|
onError: err => {
|
|
|
|
|
if (err.code !== ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME) {
|
|
|
|
|
throw err
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
const text = ast.children[0] as TextNode
|
|
|
|
|
|
|
|
|
|
expect(text).toStrictEqual({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'a < b',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
source: 'a < b'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2020-09-23 18:25:52 +08:00
|
|
|
|
test('lonely "{{" doesn\'t separate nodes', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('a {{ b', {
|
2019-09-18 07:08:47 +08:00
|
|
|
|
onError: error => {
|
|
|
|
|
if (error.code !== ErrorCodes.X_MISSING_INTERPOLATION_END) {
|
|
|
|
|
throw error
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
const text = ast.children[0] as TextNode
|
|
|
|
|
|
|
|
|
|
expect(text).toStrictEqual({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'a {{ b',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 6, line: 1, column: 7 },
|
|
|
|
|
source: 'a {{ b'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('Interpolation', () => {
|
|
|
|
|
test('simple interpolation', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('{{message}}')
|
2019-09-27 23:42:02 +08:00
|
|
|
|
const interpolation = ast.children[0] as InterpolationNode
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(interpolation).toStrictEqual({
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.INTERPOLATION,
|
|
|
|
|
content: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
content: `message`,
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 2, line: 1, column: 3 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: `message`
|
|
|
|
|
}
|
|
|
|
|
},
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 11, line: 1, column: 12 },
|
|
|
|
|
source: '{{message}}'
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('it can have tag-like notation', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('{{ a<b }}')
|
2019-09-27 23:42:02 +08:00
|
|
|
|
const interpolation = ast.children[0] as InterpolationNode
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(interpolation).toStrictEqual({
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.INTERPOLATION,
|
|
|
|
|
content: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
content: `a<b`,
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 3, line: 1, column: 4 },
|
|
|
|
|
end: { offset: 6, line: 1, column: 7 },
|
|
|
|
|
source: 'a<b'
|
|
|
|
|
}
|
|
|
|
|
},
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: '{{ a<b }}'
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('it can have tag-like notation (2)', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('{{ a<b }}{{ c>d }}')
|
2019-09-27 23:42:02 +08:00
|
|
|
|
const interpolation1 = ast.children[0] as InterpolationNode
|
|
|
|
|
const interpolation2 = ast.children[1] as InterpolationNode
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(interpolation1).toStrictEqual({
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.INTERPOLATION,
|
|
|
|
|
content: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
content: `a<b`,
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 3, line: 1, column: 4 },
|
|
|
|
|
end: { offset: 6, line: 1, column: 7 },
|
|
|
|
|
source: 'a<b'
|
|
|
|
|
}
|
|
|
|
|
},
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: '{{ a<b }}'
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
})
|
2019-09-27 23:42:02 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
expect(interpolation2).toStrictEqual({
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.INTERPOLATION,
|
|
|
|
|
content: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
content: 'c>d',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 12, line: 1, column: 13 },
|
|
|
|
|
end: { offset: 15, line: 1, column: 16 },
|
|
|
|
|
source: 'c>d'
|
|
|
|
|
}
|
|
|
|
|
},
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
start: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
end: { offset: 18, line: 1, column: 19 },
|
|
|
|
|
source: '{{ c>d }}'
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('it can have tag-like notation (3)', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div>{{ "</div>" }}</div>')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
2019-09-27 23:42:02 +08:00
|
|
|
|
const interpolation = element.children[0] as InterpolationNode
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(interpolation).toStrictEqual({
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.INTERPOLATION,
|
|
|
|
|
content: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
// The `constType` is the default value and will be determined in `transformExpression`.
|
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
content: '"</div>"',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 8, line: 1, column: 9 },
|
|
|
|
|
end: { offset: 16, line: 1, column: 17 },
|
|
|
|
|
source: '"</div>"'
|
|
|
|
|
}
|
|
|
|
|
},
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 19, line: 1, column: 20 },
|
|
|
|
|
source: '{{ "</div>" }}'
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
2019-10-18 03:04:52 +08:00
|
|
|
|
|
|
|
|
|
test('custom delimiters', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<p>{msg}</p>', {
|
2019-10-18 03:04:52 +08:00
|
|
|
|
delimiters: ['{', '}']
|
|
|
|
|
})
|
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
const interpolation = element.children[0] as InterpolationNode
|
|
|
|
|
|
|
|
|
|
expect(interpolation).toStrictEqual({
|
|
|
|
|
type: NodeTypes.INTERPOLATION,
|
|
|
|
|
content: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
content: `msg`,
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-10-18 03:04:52 +08:00
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 4, line: 1, column: 5 },
|
|
|
|
|
end: { offset: 7, line: 1, column: 8 },
|
|
|
|
|
source: 'msg'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 3, line: 1, column: 4 },
|
|
|
|
|
end: { offset: 8, line: 1, column: 9 },
|
|
|
|
|
source: '{msg}'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
2019-09-17 02:43:29 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('Comment', () => {
|
|
|
|
|
test('empty comment', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<!---->')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const comment = ast.children[0] as CommentNode
|
|
|
|
|
|
|
|
|
|
expect(comment).toStrictEqual({
|
|
|
|
|
type: NodeTypes.COMMENT,
|
|
|
|
|
content: '',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 7, line: 1, column: 8 },
|
|
|
|
|
source: '<!---->'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('simple comment', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<!--abc-->')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const comment = ast.children[0] as CommentNode
|
|
|
|
|
|
|
|
|
|
expect(comment).toStrictEqual({
|
|
|
|
|
type: NodeTypes.COMMENT,
|
|
|
|
|
content: 'abc',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
source: '<!--abc-->'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('two comments', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<!--abc--><!--def-->')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const comment1 = ast.children[0] as CommentNode
|
|
|
|
|
const comment2 = ast.children[1] as CommentNode
|
|
|
|
|
|
|
|
|
|
expect(comment1).toStrictEqual({
|
|
|
|
|
type: NodeTypes.COMMENT,
|
|
|
|
|
content: 'abc',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
source: '<!--abc-->'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
expect(comment2).toStrictEqual({
|
|
|
|
|
type: NodeTypes.COMMENT,
|
|
|
|
|
content: 'def',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
end: { offset: 20, line: 1, column: 21 },
|
|
|
|
|
source: '<!--def-->'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
2020-08-17 23:20:28 +08:00
|
|
|
|
|
|
|
|
|
test('comments option', () => {
|
|
|
|
|
__DEV__ = false
|
2020-10-14 04:03:33 +08:00
|
|
|
|
const astDefaultComment = baseParse('<!--abc-->')
|
|
|
|
|
const astNoComment = baseParse('<!--abc-->', { comments: false })
|
2020-08-17 23:20:28 +08:00
|
|
|
|
const astWithComments = baseParse('<!--abc-->', { comments: true })
|
|
|
|
|
__DEV__ = true
|
|
|
|
|
|
2020-10-14 04:03:33 +08:00
|
|
|
|
expect(astDefaultComment.children).toHaveLength(0)
|
2020-08-17 23:20:28 +08:00
|
|
|
|
expect(astNoComment.children).toHaveLength(0)
|
|
|
|
|
expect(astWithComments.children).toHaveLength(1)
|
|
|
|
|
})
|
2020-10-06 05:53:17 +08:00
|
|
|
|
|
|
|
|
|
// #2217
|
|
|
|
|
test('comments in the <pre> tag should be removed in production mode', () => {
|
|
|
|
|
__DEV__ = false
|
|
|
|
|
const rawText = `<p/><!-- foo --><p/>`
|
|
|
|
|
const ast = baseParse(`<pre>${rawText}</pre>`)
|
|
|
|
|
__DEV__ = true
|
|
|
|
|
|
|
|
|
|
expect((ast.children[0] as ElementNode).children).toMatchObject([
|
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'p'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'p'
|
|
|
|
|
}
|
|
|
|
|
])
|
|
|
|
|
})
|
2019-09-17 02:43:29 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('Element', () => {
|
|
|
|
|
test('simple div', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div>hello</div>')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
|
|
|
|
|
expect(element).toStrictEqual({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
ns: Namespaces.HTML,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
2019-09-22 05:48:17 +08:00
|
|
|
|
codegenNode: undefined,
|
|
|
|
|
props: [],
|
2019-09-17 02:43:29 +08:00
|
|
|
|
isSelfClosing: false,
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'hello',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
source: 'hello'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 16, line: 1, column: 17 },
|
|
|
|
|
source: '<div>hello</div>'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('empty', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div></div>')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
|
|
|
|
|
expect(element).toStrictEqual({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
ns: Namespaces.HTML,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
2019-09-22 05:48:17 +08:00
|
|
|
|
codegenNode: undefined,
|
|
|
|
|
props: [],
|
2019-09-17 02:43:29 +08:00
|
|
|
|
isSelfClosing: false,
|
|
|
|
|
children: [],
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 11, line: 1, column: 12 },
|
|
|
|
|
source: '<div></div>'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('self closing', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div/>after')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
|
|
|
|
|
expect(element).toStrictEqual({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
ns: Namespaces.HTML,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
2019-09-22 05:48:17 +08:00
|
|
|
|
codegenNode: undefined,
|
|
|
|
|
props: [],
|
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
isSelfClosing: true,
|
|
|
|
|
children: [],
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 6, line: 1, column: 7 },
|
|
|
|
|
source: '<div/>'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('void element', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<img>after', {
|
2019-09-17 23:07:46 +08:00
|
|
|
|
isVoidTag: tag => tag === 'img'
|
|
|
|
|
})
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
|
|
|
|
|
expect(element).toStrictEqual({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
ns: Namespaces.HTML,
|
|
|
|
|
tag: 'img',
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
2019-09-22 05:48:17 +08:00
|
|
|
|
codegenNode: undefined,
|
|
|
|
|
props: [],
|
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
isSelfClosing: false,
|
|
|
|
|
children: [],
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
source: '<img>'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2020-02-01 06:20:52 +08:00
|
|
|
|
test('template element with directives', () => {
|
|
|
|
|
const ast = baseParse('<template v-if="ok"></template>')
|
|
|
|
|
const element = ast.children[0]
|
|
|
|
|
expect(element).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tagType: ElementTypes.TEMPLATE
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('template element without directives', () => {
|
|
|
|
|
const ast = baseParse('<template></template>')
|
|
|
|
|
const element = ast.children[0]
|
|
|
|
|
expect(element).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tagType: ElementTypes.ELEMENT
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2019-10-11 02:54:06 +08:00
|
|
|
|
test('native element with `isNativeTag`', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div></div><comp></comp><Comp></Comp>', {
|
2019-10-11 02:54:06 +08:00
|
|
|
|
isNativeTag: tag => tag === 'div'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[0]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[1]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'comp',
|
|
|
|
|
tagType: ElementTypes.COMPONENT
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[2]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'Comp',
|
|
|
|
|
tagType: ElementTypes.COMPONENT
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('native element without `isNativeTag`', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div></div><comp></comp><Comp></Comp>')
|
2019-10-11 02:54:06 +08:00
|
|
|
|
|
|
|
|
|
expect(ast.children[0]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[1]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'comp',
|
|
|
|
|
tagType: ElementTypes.ELEMENT
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[2]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'Comp',
|
|
|
|
|
tagType: ElementTypes.COMPONENT
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2020-09-23 18:25:52 +08:00
|
|
|
|
test('v-is with `isNativeTag`', () => {
|
2020-03-28 04:38:31 +08:00
|
|
|
|
const ast = baseParse(
|
|
|
|
|
`<div></div><div v-is="'foo'"></div><Comp></Comp>`,
|
|
|
|
|
{
|
|
|
|
|
isNativeTag: tag => tag === 'div'
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(ast.children[0]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[1]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.COMPONENT
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[2]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'Comp',
|
|
|
|
|
tagType: ElementTypes.COMPONENT
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2020-09-23 18:25:52 +08:00
|
|
|
|
test('v-is without `isNativeTag`', () => {
|
2020-03-28 04:38:31 +08:00
|
|
|
|
const ast = baseParse(`<div></div><div v-is="'foo'"></div><Comp></Comp>`)
|
|
|
|
|
|
|
|
|
|
expect(ast.children[0]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[1]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.COMPONENT
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[2]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'Comp',
|
|
|
|
|
tagType: ElementTypes.COMPONENT
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2019-10-16 05:30:47 +08:00
|
|
|
|
test('custom element', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div></div><comp></comp>', {
|
2019-10-16 05:30:47 +08:00
|
|
|
|
isNativeTag: tag => tag === 'div',
|
|
|
|
|
isCustomElement: tag => tag === 'comp'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[0]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[1]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'comp',
|
|
|
|
|
tagType: ElementTypes.ELEMENT
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2020-10-14 04:03:33 +08:00
|
|
|
|
test('built-in component', () => {
|
|
|
|
|
const ast = baseParse('<div></div><comp></comp>', {
|
|
|
|
|
isBuiltInComponent: tag => (tag === 'comp' ? Symbol() : void 0)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[0]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[1]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'comp',
|
|
|
|
|
tagType: ElementTypes.COMPONENT
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('slot element', () => {
|
|
|
|
|
const ast = baseParse('<slot></slot><Comp></Comp>')
|
|
|
|
|
|
|
|
|
|
expect(ast.children[0]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'slot',
|
|
|
|
|
tagType: ElementTypes.SLOT
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children[1]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tag: 'Comp',
|
|
|
|
|
tagType: ElementTypes.COMPONENT
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
test('attribute with no value', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div id></div>')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
|
|
|
|
|
expect(element).toStrictEqual({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
ns: Namespaces.HTML,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
2019-09-22 05:48:17 +08:00
|
|
|
|
codegenNode: undefined,
|
|
|
|
|
props: [
|
2019-09-17 02:43:29 +08:00
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ATTRIBUTE,
|
|
|
|
|
name: 'id',
|
|
|
|
|
value: undefined,
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 7, line: 1, column: 8 },
|
|
|
|
|
source: 'id'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
2019-09-22 05:48:17 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
isSelfClosing: false,
|
|
|
|
|
children: [],
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 14, line: 1, column: 15 },
|
|
|
|
|
source: '<div id></div>'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('attribute with empty value, double quote', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div id=""></div>')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
|
|
|
|
|
expect(element).toStrictEqual({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
ns: Namespaces.HTML,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
2019-09-22 05:48:17 +08:00
|
|
|
|
codegenNode: undefined,
|
|
|
|
|
props: [
|
2019-09-17 02:43:29 +08:00
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ATTRIBUTE,
|
|
|
|
|
name: 'id',
|
|
|
|
|
value: {
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: '',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 8, line: 1, column: 9 },
|
|
|
|
|
end: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
source: '""'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
source: 'id=""'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
2019-09-22 05:48:17 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
isSelfClosing: false,
|
|
|
|
|
children: [],
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 17, line: 1, column: 18 },
|
|
|
|
|
source: '<div id=""></div>'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('attribute with empty value, single quote', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse("<div id=''></div>")
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
|
|
|
|
|
expect(element).toStrictEqual({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
ns: Namespaces.HTML,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
2019-09-22 05:48:17 +08:00
|
|
|
|
codegenNode: undefined,
|
|
|
|
|
props: [
|
2019-09-17 02:43:29 +08:00
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ATTRIBUTE,
|
|
|
|
|
name: 'id',
|
|
|
|
|
value: {
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: '',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 8, line: 1, column: 9 },
|
|
|
|
|
end: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
source: "''"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
source: "id=''"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
2019-09-22 05:48:17 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
isSelfClosing: false,
|
|
|
|
|
children: [],
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 17, line: 1, column: 18 },
|
|
|
|
|
source: "<div id=''></div>"
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('attribute with value, double quote', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div id=">\'"></div>')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
|
|
|
|
|
expect(element).toStrictEqual({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
ns: Namespaces.HTML,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
2019-09-22 05:48:17 +08:00
|
|
|
|
codegenNode: undefined,
|
|
|
|
|
props: [
|
2019-09-17 02:43:29 +08:00
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ATTRIBUTE,
|
|
|
|
|
name: 'id',
|
|
|
|
|
value: {
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: ">'",
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 8, line: 1, column: 9 },
|
|
|
|
|
end: { offset: 12, line: 1, column: 13 },
|
|
|
|
|
source: '">\'"'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 12, line: 1, column: 13 },
|
|
|
|
|
source: 'id=">\'"'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
2019-09-22 05:48:17 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
isSelfClosing: false,
|
|
|
|
|
children: [],
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 19, line: 1, column: 20 },
|
|
|
|
|
source: '<div id=">\'"></div>'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('attribute with value, single quote', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse("<div id='>\"'></div>")
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
|
|
|
|
|
expect(element).toStrictEqual({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
ns: Namespaces.HTML,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
2019-09-22 05:48:17 +08:00
|
|
|
|
codegenNode: undefined,
|
|
|
|
|
props: [
|
2019-09-17 02:43:29 +08:00
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ATTRIBUTE,
|
|
|
|
|
name: 'id',
|
|
|
|
|
value: {
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: '>"',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 8, line: 1, column: 9 },
|
|
|
|
|
end: { offset: 12, line: 1, column: 13 },
|
|
|
|
|
source: "'>\"'"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 12, line: 1, column: 13 },
|
|
|
|
|
source: "id='>\"'"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
2019-09-22 05:48:17 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
isSelfClosing: false,
|
|
|
|
|
children: [],
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 19, line: 1, column: 20 },
|
|
|
|
|
source: "<div id='>\"'></div>"
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('attribute with value, unquoted', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div id=a/></div>')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
|
|
|
|
|
expect(element).toStrictEqual({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
ns: Namespaces.HTML,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
2019-09-22 05:48:17 +08:00
|
|
|
|
codegenNode: undefined,
|
|
|
|
|
props: [
|
2019-09-17 02:43:29 +08:00
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ATTRIBUTE,
|
|
|
|
|
name: 'id',
|
|
|
|
|
value: {
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'a/',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 8, line: 1, column: 9 },
|
|
|
|
|
end: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
source: 'a/'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
source: 'id=a/'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
2019-09-22 05:48:17 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
isSelfClosing: false,
|
|
|
|
|
children: [],
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 17, line: 1, column: 18 },
|
|
|
|
|
source: '<div id=a/></div>'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('multiple attributes', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div id=a class="c" inert style=\'\'></div>')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
|
|
|
|
|
expect(element).toStrictEqual({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
ns: Namespaces.HTML,
|
|
|
|
|
tag: 'div',
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
2019-09-22 05:48:17 +08:00
|
|
|
|
codegenNode: undefined,
|
|
|
|
|
props: [
|
2019-09-17 02:43:29 +08:00
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ATTRIBUTE,
|
|
|
|
|
name: 'id',
|
|
|
|
|
value: {
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'a',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 8, line: 1, column: 9 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: 'a'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: 'id=a'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ATTRIBUTE,
|
|
|
|
|
name: 'class',
|
|
|
|
|
value: {
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'c',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 16, line: 1, column: 17 },
|
|
|
|
|
end: { offset: 19, line: 1, column: 20 },
|
|
|
|
|
source: '"c"'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
end: { offset: 19, line: 1, column: 20 },
|
|
|
|
|
source: 'class="c"'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ATTRIBUTE,
|
|
|
|
|
name: 'inert',
|
|
|
|
|
value: undefined,
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 20, line: 1, column: 21 },
|
|
|
|
|
end: { offset: 25, line: 1, column: 26 },
|
|
|
|
|
source: 'inert'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ATTRIBUTE,
|
|
|
|
|
name: 'style',
|
|
|
|
|
value: {
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: '',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 32, line: 1, column: 33 },
|
|
|
|
|
end: { offset: 34, line: 1, column: 35 },
|
|
|
|
|
source: "''"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 26, line: 1, column: 27 },
|
|
|
|
|
end: { offset: 34, line: 1, column: 35 },
|
|
|
|
|
source: "style=''"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
2019-09-22 05:48:17 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
isSelfClosing: false,
|
|
|
|
|
children: [],
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 0, line: 1, column: 1 },
|
|
|
|
|
end: { offset: 41, line: 1, column: 42 },
|
|
|
|
|
source: '<div id=a class="c" inert style=\'\'></div>'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('directive with no value', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div v-if/>')
|
2019-09-22 05:48:17 +08:00
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'if',
|
|
|
|
|
arg: undefined,
|
|
|
|
|
modifiers: [],
|
|
|
|
|
exp: undefined,
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: 'v-if'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('directive with value', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div v-if="a"/>')
|
2019-09-22 05:48:17 +08:00
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'if',
|
|
|
|
|
arg: undefined,
|
|
|
|
|
modifiers: [],
|
|
|
|
|
exp: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
content: 'a',
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
2019-09-26 07:17:45 +08:00
|
|
|
|
start: { offset: 11, line: 1, column: 12 },
|
|
|
|
|
end: { offset: 12, line: 1, column: 13 },
|
2019-09-27 02:55:53 +08:00
|
|
|
|
source: 'a'
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 13, line: 1, column: 14 },
|
|
|
|
|
source: 'v-if="a"'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('directive with argument', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div v-on:click/>')
|
2019-09-22 05:48:17 +08:00
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'on',
|
|
|
|
|
arg: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
content: 'click',
|
|
|
|
|
isStatic: true,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.CAN_STRINGIFY,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
|
|
|
|
source: 'click',
|
|
|
|
|
start: {
|
|
|
|
|
column: 11,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 10
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
column: 16,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 15
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
modifiers: [],
|
|
|
|
|
exp: undefined,
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 15, line: 1, column: 16 },
|
|
|
|
|
source: 'v-on:click'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2020-06-13 03:59:13 +08:00
|
|
|
|
test('directive with dynamic argument', () => {
|
|
|
|
|
const ast = baseParse('<div v-on:[event]/>')
|
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'on',
|
|
|
|
|
arg: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
content: 'event',
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2020-06-13 03:59:13 +08:00
|
|
|
|
|
|
|
|
|
loc: {
|
|
|
|
|
source: '[event]',
|
|
|
|
|
start: {
|
|
|
|
|
column: 11,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 10
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
column: 18,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 17
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
modifiers: [],
|
|
|
|
|
exp: undefined,
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 17, line: 1, column: 18 },
|
|
|
|
|
source: 'v-on:[event]'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
test('directive with a modifier', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div v-on.enter/>')
|
2019-09-22 05:48:17 +08:00
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'on',
|
|
|
|
|
arg: undefined,
|
|
|
|
|
modifiers: ['enter'],
|
|
|
|
|
exp: undefined,
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 15, line: 1, column: 16 },
|
|
|
|
|
source: 'v-on.enter'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('directive with two modifiers', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div v-on.enter.exact/>')
|
2019-09-22 05:48:17 +08:00
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'on',
|
|
|
|
|
arg: undefined,
|
|
|
|
|
modifiers: ['enter', 'exact'],
|
|
|
|
|
exp: undefined,
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 21, line: 1, column: 22 },
|
|
|
|
|
source: 'v-on.enter.exact'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('directive with argument and modifiers', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div v-on:click.enter.exact/>')
|
2019-09-22 05:48:17 +08:00
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'on',
|
|
|
|
|
arg: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
content: 'click',
|
|
|
|
|
isStatic: true,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.CAN_STRINGIFY,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
|
|
|
|
source: 'click',
|
|
|
|
|
start: {
|
|
|
|
|
column: 11,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 10
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
column: 16,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 15
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
modifiers: ['enter', 'exact'],
|
|
|
|
|
exp: undefined,
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 27, line: 1, column: 28 },
|
|
|
|
|
source: 'v-on:click.enter.exact'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2020-06-13 03:59:13 +08:00
|
|
|
|
test('directive with dynamic argument and modifiers', () => {
|
|
|
|
|
const ast = baseParse('<div v-on:[a.b].camel/>')
|
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'on',
|
|
|
|
|
arg: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
content: 'a.b',
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2020-06-13 03:59:13 +08:00
|
|
|
|
|
|
|
|
|
loc: {
|
|
|
|
|
source: '[a.b]',
|
|
|
|
|
start: {
|
|
|
|
|
column: 11,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 10
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
column: 16,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 15
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
modifiers: ['camel'],
|
|
|
|
|
exp: undefined,
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 21, line: 1, column: 22 },
|
|
|
|
|
source: 'v-on:[a.b].camel'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
test('v-bind shorthand', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div :a=b />')
|
2019-09-22 05:48:17 +08:00
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'bind',
|
|
|
|
|
arg: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
content: 'a',
|
|
|
|
|
isStatic: true,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.CAN_STRINGIFY,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
|
|
|
|
source: 'a',
|
|
|
|
|
start: {
|
|
|
|
|
column: 7,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 6
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
column: 8,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 7
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
modifiers: [],
|
|
|
|
|
exp: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
content: 'b',
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 8, line: 1, column: 9 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: 'b'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: ':a=b'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('v-bind shorthand with modifier', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div :a.sync=b />')
|
2019-09-22 05:48:17 +08:00
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'bind',
|
|
|
|
|
arg: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
content: 'a',
|
|
|
|
|
isStatic: true,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.CAN_STRINGIFY,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
|
|
|
|
source: 'a',
|
|
|
|
|
start: {
|
|
|
|
|
column: 7,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 6
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
column: 8,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 7
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
modifiers: ['sync'],
|
|
|
|
|
exp: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
content: 'b',
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 13, line: 1, column: 14 },
|
|
|
|
|
end: { offset: 14, line: 1, column: 15 },
|
|
|
|
|
source: 'b'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 14, line: 1, column: 15 },
|
|
|
|
|
source: ':a.sync=b'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('v-on shorthand', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div @a=b />')
|
2019-09-22 05:48:17 +08:00
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'on',
|
|
|
|
|
arg: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
content: 'a',
|
|
|
|
|
isStatic: true,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.CAN_STRINGIFY,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
|
|
|
|
source: 'a',
|
|
|
|
|
start: {
|
|
|
|
|
column: 7,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 6
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
column: 8,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 7
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
modifiers: [],
|
|
|
|
|
exp: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
content: 'b',
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 8, line: 1, column: 9 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: 'b'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 9, line: 1, column: 10 },
|
|
|
|
|
source: '@a=b'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('v-on shorthand with modifier', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div @a.enter=b />')
|
2019-09-22 05:48:17 +08:00
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'on',
|
|
|
|
|
arg: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
content: 'a',
|
|
|
|
|
isStatic: true,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.CAN_STRINGIFY,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
|
|
|
|
source: 'a',
|
|
|
|
|
start: {
|
|
|
|
|
column: 7,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 6
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
column: 8,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 7
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
modifiers: ['enter'],
|
|
|
|
|
exp: {
|
2019-09-27 23:42:02 +08:00
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
content: 'b',
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-09-27 23:42:02 +08:00
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 14, line: 1, column: 15 },
|
|
|
|
|
end: { offset: 15, line: 1, column: 16 },
|
|
|
|
|
source: 'b'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 15, line: 1, column: 16 },
|
|
|
|
|
source: '@a.enter=b'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2019-09-29 02:05:10 +08:00
|
|
|
|
test('v-slot shorthand', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<Comp #a="{ b }" />')
|
2019-09-29 02:05:10 +08:00
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
|
|
|
|
|
|
|
|
|
expect(directive).toStrictEqual({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'slot',
|
|
|
|
|
arg: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
content: 'a',
|
|
|
|
|
isStatic: true,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.CAN_STRINGIFY,
|
2019-09-29 02:05:10 +08:00
|
|
|
|
loc: {
|
|
|
|
|
source: 'a',
|
|
|
|
|
start: {
|
|
|
|
|
column: 8,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 7
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
column: 9,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 8
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
modifiers: [],
|
|
|
|
|
exp: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
content: '{ b }',
|
|
|
|
|
isStatic: false,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
// The `constType` is the default value and will be determined in transformExpression
|
|
|
|
|
constType: ConstantTypes.NOT_CONSTANT,
|
2019-09-29 02:05:10 +08:00
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
end: { offset: 15, line: 1, column: 16 },
|
|
|
|
|
source: '{ b }'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 6, line: 1, column: 7 },
|
|
|
|
|
end: { offset: 16, line: 1, column: 17 },
|
|
|
|
|
source: '#a="{ b }"'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2020-06-13 04:09:27 +08:00
|
|
|
|
// #1241 special case for 2.x compat
|
|
|
|
|
test('v-slot arg containing dots', () => {
|
|
|
|
|
const ast = baseParse('<Comp v-slot:foo.bar="{ a }" />')
|
|
|
|
|
const directive = (ast.children[0] as ElementNode).props[0]
|
|
|
|
|
|
|
|
|
|
expect(directive).toMatchObject({
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: 'slot',
|
|
|
|
|
arg: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
content: 'foo.bar',
|
|
|
|
|
isStatic: true,
|
2020-11-21 08:26:07 +08:00
|
|
|
|
constType: ConstantTypes.CAN_STRINGIFY,
|
2020-06-13 04:09:27 +08:00
|
|
|
|
loc: {
|
|
|
|
|
source: 'foo.bar',
|
|
|
|
|
start: {
|
|
|
|
|
column: 14,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 13
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
column: 21,
|
|
|
|
|
line: 1,
|
|
|
|
|
offset: 20
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2019-10-10 04:00:08 +08:00
|
|
|
|
test('v-pre', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(
|
2019-10-10 04:00:08 +08:00
|
|
|
|
`<div v-pre :id="foo"><Comp/>{{ bar }}</div>\n` +
|
|
|
|
|
`<div :id="foo"><Comp/>{{ bar }}</div>`
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const divWithPre = ast.children[0] as ElementNode
|
|
|
|
|
expect(divWithPre.props).toMatchObject([
|
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.ATTRIBUTE,
|
|
|
|
|
name: `:id`,
|
|
|
|
|
value: {
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: `foo`
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
source: `:id="foo"`,
|
|
|
|
|
start: {
|
|
|
|
|
line: 1,
|
|
|
|
|
column: 12
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
line: 1,
|
|
|
|
|
column: 21
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
])
|
|
|
|
|
expect(divWithPre.children[0]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tagType: ElementTypes.ELEMENT,
|
|
|
|
|
tag: `Comp`
|
|
|
|
|
})
|
|
|
|
|
expect(divWithPre.children[1]).toMatchObject({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: `{{ bar }}`
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// should not affect siblings after it
|
|
|
|
|
const divWithoutPre = ast.children[1] as ElementNode
|
|
|
|
|
expect(divWithoutPre.props).toMatchObject([
|
|
|
|
|
{
|
|
|
|
|
type: NodeTypes.DIRECTIVE,
|
|
|
|
|
name: `bind`,
|
|
|
|
|
arg: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
isStatic: true,
|
|
|
|
|
content: `id`
|
|
|
|
|
},
|
|
|
|
|
exp: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
isStatic: false,
|
|
|
|
|
content: `foo`
|
|
|
|
|
},
|
|
|
|
|
loc: {
|
|
|
|
|
source: `:id="foo"`,
|
|
|
|
|
start: {
|
|
|
|
|
line: 2,
|
|
|
|
|
column: 6
|
|
|
|
|
},
|
|
|
|
|
end: {
|
|
|
|
|
line: 2,
|
|
|
|
|
column: 15
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
])
|
|
|
|
|
expect(divWithoutPre.children[0]).toMatchObject({
|
|
|
|
|
type: NodeTypes.ELEMENT,
|
|
|
|
|
tagType: ElementTypes.COMPONENT,
|
|
|
|
|
tag: `Comp`
|
|
|
|
|
})
|
|
|
|
|
expect(divWithoutPre.children[1]).toMatchObject({
|
|
|
|
|
type: NodeTypes.INTERPOLATION,
|
|
|
|
|
content: {
|
|
|
|
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
|
|
|
content: `bar`,
|
|
|
|
|
isStatic: false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
test('end tags are case-insensitive.', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div>hello</DIV>after')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const element = ast.children[0] as ElementNode
|
|
|
|
|
const text = element.children[0] as TextNode
|
|
|
|
|
|
|
|
|
|
expect(text).toStrictEqual({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: 'hello',
|
|
|
|
|
loc: {
|
|
|
|
|
start: { offset: 5, line: 1, column: 6 },
|
|
|
|
|
end: { offset: 10, line: 1, column: 11 },
|
|
|
|
|
source: 'hello'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('self closing single tag', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse('<div :class="{ some: condition }" />')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
expect(ast.children).toHaveLength(1)
|
|
|
|
|
expect(ast.children[0]).toMatchObject({ tag: 'div' })
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('self closing multiple tag', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(
|
2019-09-17 02:43:29 +08:00
|
|
|
|
`<div :class="{ some: condition }" />\n` +
|
2019-09-17 23:07:46 +08:00
|
|
|
|
`<p v-bind:style="{ color: 'red' }"/>`
|
2019-09-17 02:43:29 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(ast).toMatchSnapshot()
|
|
|
|
|
|
|
|
|
|
expect(ast.children).toHaveLength(2)
|
|
|
|
|
expect(ast.children[0]).toMatchObject({ tag: 'div' })
|
|
|
|
|
expect(ast.children[1]).toMatchObject({ tag: 'p' })
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('valid html', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(
|
2019-09-17 02:43:29 +08:00
|
|
|
|
`<div :class="{ some: condition }">\n` +
|
|
|
|
|
` <p v-bind:style="{ color: 'red' }"/>\n` +
|
|
|
|
|
` <!-- a comment with <html> inside it -->\n` +
|
2019-09-17 23:07:46 +08:00
|
|
|
|
`</div>`
|
2019-09-17 02:43:29 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(ast).toMatchSnapshot()
|
|
|
|
|
|
|
|
|
|
expect(ast.children).toHaveLength(1)
|
|
|
|
|
const el = ast.children[0] as any
|
|
|
|
|
expect(el).toMatchObject({
|
|
|
|
|
tag: 'div'
|
|
|
|
|
})
|
|
|
|
|
expect(el.children).toHaveLength(2)
|
|
|
|
|
expect(el.children[0]).toMatchObject({
|
|
|
|
|
tag: 'p'
|
|
|
|
|
})
|
|
|
|
|
expect(el.children[1]).toMatchObject({
|
|
|
|
|
type: NodeTypes.COMMENT
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('invalid html', () => {
|
|
|
|
|
expect(() => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
baseParse(`<div>\n<span>\n</div>\n</span>`)
|
2019-12-20 23:10:08 +08:00
|
|
|
|
}).toThrow('Element is missing end tag.')
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
|
|
|
|
const spy = jest.fn()
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(`<div>\n<span>\n</div>\n</span>`, {
|
2019-09-17 02:43:29 +08:00
|
|
|
|
onError: spy
|
|
|
|
|
})
|
|
|
|
|
|
2019-09-18 07:08:47 +08:00
|
|
|
|
expect(spy.mock.calls).toMatchObject([
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
code: ErrorCodes.X_MISSING_END_TAG,
|
|
|
|
|
loc: {
|
2019-09-23 10:19:42 +08:00
|
|
|
|
start: {
|
2019-12-20 23:10:08 +08:00
|
|
|
|
offset: 6,
|
|
|
|
|
line: 2,
|
2019-09-23 10:19:42 +08:00
|
|
|
|
column: 1
|
|
|
|
|
}
|
2019-09-18 07:08:47 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
code: ErrorCodes.X_INVALID_END_TAG,
|
|
|
|
|
loc: {
|
2019-09-23 10:19:42 +08:00
|
|
|
|
start: {
|
|
|
|
|
offset: 20,
|
|
|
|
|
line: 4,
|
|
|
|
|
column: 1
|
|
|
|
|
}
|
2019-09-18 07:08:47 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
])
|
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
expect(ast).toMatchSnapshot()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('parse with correct location info', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const [foo, bar, but, baz] = baseParse(
|
2019-09-26 07:17:45 +08:00
|
|
|
|
`
|
|
|
|
|
foo
|
|
|
|
|
is {{ bar }} but {{ baz }}`.trim()
|
2019-09-17 02:43:29 +08:00
|
|
|
|
).children
|
|
|
|
|
|
|
|
|
|
let offset = 0
|
|
|
|
|
expect(foo.loc.start).toEqual({ line: 1, column: 1, offset })
|
|
|
|
|
offset += foo.loc.source.length
|
2019-09-26 07:17:45 +08:00
|
|
|
|
expect(foo.loc.end).toEqual({ line: 2, column: 5, offset })
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
2019-09-27 23:42:02 +08:00
|
|
|
|
expect(bar.loc.start).toEqual({ line: 2, column: 5, offset })
|
|
|
|
|
const barInner = (bar as InterpolationNode).content
|
2019-09-27 02:55:53 +08:00
|
|
|
|
offset += 3
|
2019-09-27 23:42:02 +08:00
|
|
|
|
expect(barInner.loc.start).toEqual({ line: 2, column: 8, offset })
|
|
|
|
|
offset += barInner.loc.source.length
|
|
|
|
|
expect(barInner.loc.end).toEqual({ line: 2, column: 11, offset })
|
2019-09-27 02:55:53 +08:00
|
|
|
|
offset += 3
|
2019-09-27 23:42:02 +08:00
|
|
|
|
expect(bar.loc.end).toEqual({ line: 2, column: 14, offset })
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
2019-09-26 07:17:45 +08:00
|
|
|
|
expect(but.loc.start).toEqual({ line: 2, column: 14, offset })
|
2019-09-17 02:43:29 +08:00
|
|
|
|
offset += but.loc.source.length
|
2019-09-26 07:17:45 +08:00
|
|
|
|
expect(but.loc.end).toEqual({ line: 2, column: 19, offset })
|
2019-09-17 02:43:29 +08:00
|
|
|
|
|
2019-09-27 23:42:02 +08:00
|
|
|
|
expect(baz.loc.start).toEqual({ line: 2, column: 19, offset })
|
|
|
|
|
const bazInner = (baz as InterpolationNode).content
|
|
|
|
|
offset += 3
|
|
|
|
|
expect(bazInner.loc.start).toEqual({ line: 2, column: 22, offset })
|
|
|
|
|
offset += bazInner.loc.source.length
|
|
|
|
|
expect(bazInner.loc.end).toEqual({ line: 2, column: 25, offset })
|
2019-09-27 02:55:53 +08:00
|
|
|
|
offset += 3
|
2019-09-27 23:42:02 +08:00
|
|
|
|
expect(baz.loc.end).toEqual({ line: 2, column: 28, offset })
|
2019-09-17 02:43:29 +08:00
|
|
|
|
})
|
|
|
|
|
|
2020-04-09 06:51:25 +08:00
|
|
|
|
describe('decodeEntities option', () => {
|
2020-10-14 04:03:33 +08:00
|
|
|
|
test('use default map', () => {
|
|
|
|
|
const ast: any = baseParse('><&'"&foo;')
|
|
|
|
|
|
|
|
|
|
expect(ast.children.length).toBe(1)
|
|
|
|
|
expect(ast.children[0].type).toBe(NodeTypes.TEXT)
|
|
|
|
|
expect(ast.children[0].content).toBe('><&\'"&foo;')
|
|
|
|
|
})
|
|
|
|
|
|
2019-09-17 02:43:29 +08:00
|
|
|
|
test('use the given map', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast: any = baseParse('&∪︀', {
|
2020-04-09 06:51:25 +08:00
|
|
|
|
decodeEntities: text => text.replace('∪︀', '\u222A\uFE00'),
|
2019-09-17 02:43:29 +08:00
|
|
|
|
onError: () => {} // Ignore errors
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(ast.children.length).toBe(1)
|
|
|
|
|
expect(ast.children[0].type).toBe(NodeTypes.TEXT)
|
|
|
|
|
expect(ast.children[0].content).toBe('&\u222A\uFE00')
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2019-10-25 04:42:09 +08:00
|
|
|
|
describe('whitespace management', () => {
|
|
|
|
|
it('should remove whitespaces at start/end inside an element', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(`<div> <span/> </div>`)
|
2019-10-25 04:42:09 +08:00
|
|
|
|
expect((ast.children[0] as ElementNode).children.length).toBe(1)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should remove whitespaces w/ newline between elements', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(`<div/> \n <div/> \n <div/>`)
|
2019-10-25 04:42:09 +08:00
|
|
|
|
expect(ast.children.length).toBe(3)
|
|
|
|
|
expect(ast.children.every(c => c.type === NodeTypes.ELEMENT)).toBe(true)
|
|
|
|
|
})
|
|
|
|
|
|
2019-10-25 05:55:00 +08:00
|
|
|
|
it('should remove whitespaces adjacent to comments', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(`<div/> \n <!--foo--> <div/>`)
|
2019-10-25 05:55:00 +08:00
|
|
|
|
expect(ast.children.length).toBe(3)
|
|
|
|
|
expect(ast.children[0].type).toBe(NodeTypes.ELEMENT)
|
|
|
|
|
expect(ast.children[1].type).toBe(NodeTypes.COMMENT)
|
|
|
|
|
expect(ast.children[2].type).toBe(NodeTypes.ELEMENT)
|
|
|
|
|
})
|
|
|
|
|
|
2019-10-25 04:42:09 +08:00
|
|
|
|
it('should remove whitespaces w/ newline between comments and elements', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(`<div/> \n <!--foo--> \n <div/>`)
|
2019-10-25 04:42:09 +08:00
|
|
|
|
expect(ast.children.length).toBe(3)
|
|
|
|
|
expect(ast.children[0].type).toBe(NodeTypes.ELEMENT)
|
|
|
|
|
expect(ast.children[1].type).toBe(NodeTypes.COMMENT)
|
|
|
|
|
expect(ast.children[2].type).toBe(NodeTypes.ELEMENT)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should NOT remove whitespaces w/ newline between interpolations', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(`{{ foo }} \n {{ bar }}`)
|
2019-10-25 04:42:09 +08:00
|
|
|
|
expect(ast.children.length).toBe(3)
|
|
|
|
|
expect(ast.children[0].type).toBe(NodeTypes.INTERPOLATION)
|
|
|
|
|
expect(ast.children[1]).toMatchObject({
|
|
|
|
|
type: NodeTypes.TEXT,
|
|
|
|
|
content: ' '
|
|
|
|
|
})
|
|
|
|
|
expect(ast.children[2].type).toBe(NodeTypes.INTERPOLATION)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should NOT remove whitespaces w/o newline between elements', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(`<div/> <div/> <div/>`)
|
2019-10-25 04:42:09 +08:00
|
|
|
|
expect(ast.children.length).toBe(5)
|
|
|
|
|
expect(ast.children.map(c => c.type)).toMatchObject([
|
|
|
|
|
NodeTypes.ELEMENT,
|
|
|
|
|
NodeTypes.TEXT,
|
|
|
|
|
NodeTypes.ELEMENT,
|
|
|
|
|
NodeTypes.TEXT,
|
|
|
|
|
NodeTypes.ELEMENT
|
|
|
|
|
])
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should condense consecutive whitespaces in text', () => {
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(` foo \n bar baz `)
|
2019-10-25 04:42:09 +08:00
|
|
|
|
expect((ast.children[0] as TextNode).content).toBe(` foo bar baz `)
|
|
|
|
|
})
|
2020-10-14 04:03:33 +08:00
|
|
|
|
|
|
|
|
|
it('should remove leading newline character immediately following the pre element start tag', () => {
|
|
|
|
|
const ast = baseParse(`<pre>\n foo bar </pre>`, {
|
|
|
|
|
isPreTag: tag => tag === 'pre'
|
|
|
|
|
})
|
|
|
|
|
expect(ast.children).toHaveLength(1)
|
|
|
|
|
const preElement = ast.children[0] as ElementNode
|
|
|
|
|
expect(preElement.children).toHaveLength(1)
|
|
|
|
|
expect((preElement.children[0] as TextNode).content).toBe(` foo bar `)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should NOT remove leading newline character immediately following child-tag of pre element', () => {
|
|
|
|
|
const ast = baseParse(`<pre><span></span>\n foo bar </pre>`, {
|
|
|
|
|
isPreTag: tag => tag === 'pre'
|
|
|
|
|
})
|
|
|
|
|
const preElement = ast.children[0] as ElementNode
|
|
|
|
|
expect(preElement.children).toHaveLength(2)
|
|
|
|
|
expect((preElement.children[1] as TextNode).content).toBe(
|
|
|
|
|
`\n foo bar `
|
|
|
|
|
)
|
|
|
|
|
})
|
2019-10-25 04:42:09 +08:00
|
|
|
|
})
|
|
|
|
|
|
2019-09-17 23:07:46 +08:00
|
|
|
|
describe('Errors', () => {
|
2019-09-17 02:43:29 +08:00
|
|
|
|
const patterns: {
|
|
|
|
|
[key: string]: Array<{
|
|
|
|
|
code: string
|
2019-09-18 07:08:47 +08:00
|
|
|
|
errors: Array<{ type: ErrorCodes; loc: Position }>
|
2019-09-17 02:43:29 +08:00
|
|
|
|
options?: Partial<ParserOptions>
|
|
|
|
|
}>
|
|
|
|
|
} = {
|
|
|
|
|
ABRUPT_CLOSING_OF_EMPTY_COMMENT: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!--></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.ABRUPT_CLOSING_OF_EMPTY_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!---></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.ABRUPT_CLOSING_OF_EMPTY_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!----></template>',
|
|
|
|
|
errors: []
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
CDATA_IN_HTML_CONTENT: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><![CDATA[cdata]]></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.CDATA_IN_HTML_CONTENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><svg><![CDATA[cdata]]></svg></template>',
|
|
|
|
|
errors: []
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
DUPLICATE_ATTRIBUTE: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id="" id=""></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.DUPLICATE_ATTRIBUTE,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 21, line: 1, column: 22 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
END_TAG_WITH_ATTRIBUTES: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div></div id=""></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.END_TAG_WITH_ATTRIBUTES,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 21, line: 1, column: 22 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
END_TAG_WITH_TRAILING_SOLIDUS: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div></div/></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.END_TAG_WITH_TRAILING_SOLIDUS,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 20, line: 1, column: 21 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
EOF_BEFORE_TAG_NAME: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_BEFORE_TAG_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 11, line: 1, column: 12 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template></',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_BEFORE_TAG_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 12, line: 1, column: 13 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
EOF_IN_CDATA: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><svg><![CDATA[cdata',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_CDATA,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 29, line: 1, column: 30 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><svg><![CDATA[',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_CDATA,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 24, line: 1, column: 25 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
EOF_IN_COMMENT: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!--comment',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 21, line: 1, column: 22 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!--',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 14, line: 1, column: 15 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
// Bogus comments don't throw eof-in-comment error.
|
|
|
|
|
// https://html.spec.whatwg.org/multipage/parsing.html#bogus-comment-state
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.INCORRECTLY_OPENED_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!-',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.INCORRECTLY_OPENED_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!abc',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.INCORRECTLY_OPENED_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT: [
|
|
|
|
|
{
|
|
|
|
|
code: "<script><!--console.log('hello')",
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 32, line: 1, column: 33 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: "<script>console.log('hello')",
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
EOF_IN_TAG: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 14, line: 1, column: 15 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div ',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 15, line: 1, column: 16 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 17, line: 1, column: 18 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id ',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 18, line: 1, column: 19 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id =',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.MISSING_ATTRIBUTE_VALUE,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 19, line: 1, column: 20 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 19, line: 1, column: 20 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: "<template><div id='abc",
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 22, line: 1, column: 23 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id="abc',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 22, line: 1, column: 23 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: "<template><div id='abc'",
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 23, line: 1, column: 24 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id="abc"',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 23, line: 1, column: 24 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id=abc',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 21, line: 1, column: 22 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: "<template><div id='abc'/",
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_SOLIDUS_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 23, line: 1, column: 24 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 24, line: 1, column: 25 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id="abc"/',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_SOLIDUS_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 23, line: 1, column: 24 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 24, line: 1, column: 25 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id=abc /',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_SOLIDUS_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 22, line: 1, column: 23 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 23, line: 1, column: 24 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
2020-10-14 04:03:33 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<div></div',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
|
|
|
|
type: ErrorCodes.EOF_IN_TAG,
|
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
}
|
|
|
|
|
]
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
INCORRECTLY_CLOSED_COMMENT: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!--comment--!></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.INCORRECTLY_CLOSED_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
INCORRECTLY_OPENED_COMMENT: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.INCORRECTLY_OPENED_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!-></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.INCORRECTLY_OPENED_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!ELEMENT br EMPTY></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.INCORRECTLY_OPENED_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
// Just ignore doctype.
|
|
|
|
|
{
|
|
|
|
|
code: '<!DOCTYPE html>',
|
|
|
|
|
errors: []
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
INVALID_FIRST_CHARACTER_OF_TAG_NAME: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template>a < b</template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 13, line: 1, column: 14 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><<3C>></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 11, line: 1, column: 12 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template>a </ b</template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 14, line: 1, column: 15 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template></<2F>></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 12, line: 1, column: 13 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
// Don't throw invalid-first-character-of-tag-name in interpolation
|
|
|
|
|
{
|
|
|
|
|
code: '<template>{{a < b}}</template>',
|
|
|
|
|
errors: []
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
MISSING_ATTRIBUTE_VALUE: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id=></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.MISSING_ATTRIBUTE_VALUE,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 18, line: 1, column: 19 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id= ></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.MISSING_ATTRIBUTE_VALUE,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 19, line: 1, column: 20 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id= /></div></template>',
|
|
|
|
|
errors: []
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
MISSING_END_TAG_NAME: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template></></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.MISSING_END_TAG_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 12, line: 1, column: 13 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
MISSING_WHITESPACE_BETWEEN_ATTRIBUTES: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id="foo"class="bar"></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.MISSING_WHITESPACE_BETWEEN_ATTRIBUTES,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 23, line: 1, column: 24 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
// CR doesn't appear in tokenization phase, but all CR are removed in preprocessing.
|
|
|
|
|
// https://html.spec.whatwg.org/multipage/parsing.html#preprocessing-the-input-stream
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div id="foo"\r\nclass="bar"></div></template>',
|
|
|
|
|
errors: []
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
NESTED_COMMENT: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!--a<!--b--></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.NESTED_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 15, line: 1, column: 16 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!--a<!--b<!--c--></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.NESTED_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 15, line: 1, column: 16 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.NESTED_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 20, line: 1, column: 21 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
2020-10-14 04:03:33 +08:00
|
|
|
|
{
|
|
|
|
|
code: '<template><!--a<!--b<!----></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
|
|
|
|
type: ErrorCodes.NESTED_COMMENT,
|
|
|
|
|
loc: { offset: 15, line: 1, column: 16 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
2019-09-17 02:43:29 +08:00
|
|
|
|
{
|
|
|
|
|
code: '<template><!--a<!--></template>',
|
|
|
|
|
errors: []
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><!--a<!--',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.EOF_IN_COMMENT,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 19, line: 1, column: 20 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME: [
|
|
|
|
|
{
|
|
|
|
|
code: "<template><div a\"bc=''></div></template>",
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 16, line: 1, column: 17 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: "<template><div a'bc=''></div></template>",
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 16, line: 1, column: 17 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: "<template><div a<bc=''></div></template>",
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 16, line: 1, column: 17 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div foo=bar"></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 22, line: 1, column: 23 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: "<template><div foo=bar'></div></template>",
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 22, line: 1, column: 23 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div foo=bar<div></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 22, line: 1, column: 23 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div foo=bar=baz></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 22, line: 1, column: 23 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div foo=bar`></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 22, line: 1, column: 23 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div =foo=bar></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 15, line: 1, column: 16 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div =></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 15, line: 1, column: 16 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><?xml?></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 11, line: 1, column: 12 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
UNEXPECTED_SOLIDUS_IN_TAG: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div a/b></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.UNEXPECTED_SOLIDUS_IN_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 16, line: 1, column: 17 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
X_INVALID_END_TAG: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_INVALID_END_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template></div></div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_INVALID_END_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_INVALID_END_TAG,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 16, line: 1, column: 17 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: "<template>{{'</div>'}}</template>",
|
|
|
|
|
errors: []
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<textarea></div></textarea>',
|
|
|
|
|
errors: []
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<svg><![CDATA[</div>]]></svg>',
|
|
|
|
|
errors: []
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<svg><!--</div>--></svg>',
|
|
|
|
|
errors: []
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
X_MISSING_END_TAG: [
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div></template>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '<template><div>',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 10, line: 1, column: 11 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_END_TAG,
|
2019-12-20 23:10:08 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
2019-09-17 02:43:29 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
X_MISSING_INTERPOLATION_END: [
|
|
|
|
|
{
|
|
|
|
|
code: '{{ foo',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_INTERPOLATION_END,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '{{',
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2019-09-18 07:08:47 +08:00
|
|
|
|
type: ErrorCodes.X_MISSING_INTERPOLATION_END,
|
2019-09-17 02:43:29 +08:00
|
|
|
|
loc: { offset: 0, line: 1, column: 1 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
code: '{{}}',
|
|
|
|
|
errors: []
|
|
|
|
|
}
|
2019-09-29 02:15:10 +08:00
|
|
|
|
],
|
|
|
|
|
X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END: [
|
|
|
|
|
{
|
|
|
|
|
code: `<div v-foo:[sef fsef] />`,
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
|
|
|
|
type: ErrorCodes.X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END,
|
|
|
|
|
loc: { offset: 15, line: 1, column: 16 }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
2019-09-17 02:43:29 +08:00
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const key of Object.keys(patterns) as (keyof (typeof patterns))[]) {
|
|
|
|
|
describe(key, () => {
|
|
|
|
|
for (const { code, errors, options } of patterns[key]) {
|
|
|
|
|
test(
|
|
|
|
|
code.replace(
|
|
|
|
|
/[\r\n]/g,
|
|
|
|
|
c => `\\x0${c.codePointAt(0)!.toString(16)};`
|
|
|
|
|
),
|
|
|
|
|
() => {
|
|
|
|
|
const spy = jest.fn()
|
2019-12-23 08:44:21 +08:00
|
|
|
|
const ast = baseParse(code, {
|
2019-09-17 23:07:46 +08:00
|
|
|
|
getNamespace: (tag, parent) => {
|
|
|
|
|
const ns = parent ? parent.ns : Namespaces.HTML
|
|
|
|
|
if (ns === Namespaces.HTML) {
|
|
|
|
|
if (tag === 'svg') {
|
|
|
|
|
return (Namespaces.HTML + 1) as any
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ns
|
|
|
|
|
},
|
2020-05-07 23:01:36 +08:00
|
|
|
|
getTextMode: ({ tag }) => {
|
2019-09-17 23:07:46 +08:00
|
|
|
|
if (tag === 'textarea') {
|
|
|
|
|
return TextModes.RCDATA
|
|
|
|
|
}
|
|
|
|
|
if (tag === 'script') {
|
|
|
|
|
return TextModes.RAWTEXT
|
|
|
|
|
}
|
|
|
|
|
return TextModes.DATA
|
|
|
|
|
},
|
2019-09-17 02:43:29 +08:00
|
|
|
|
...options,
|
|
|
|
|
onError: spy
|
|
|
|
|
})
|
|
|
|
|
|
2019-09-18 07:08:47 +08:00
|
|
|
|
expect(
|
|
|
|
|
spy.mock.calls.map(([err]) => ({
|
|
|
|
|
type: err.code,
|
2019-09-23 10:19:42 +08:00
|
|
|
|
loc: err.loc.start
|
2019-09-18 07:08:47 +08:00
|
|
|
|
}))
|
|
|
|
|
).toMatchObject(errors)
|
2019-09-17 02:43:29 +08:00
|
|
|
|
expect(ast).toMatchSnapshot()
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|