refactor: expose parse in compiler-dom, improve sfc parse error handling
This commit is contained in:
parent
7d436ab59a
commit
90ddb7c260
@ -1,5 +1,5 @@
|
||||
import { ParserOptions } from '../src/options'
|
||||
import { parse, TextModes } from '../src/parse'
|
||||
import { baseParse, TextModes } from '../src/parse'
|
||||
import { ErrorCodes } from '../src/errors'
|
||||
import {
|
||||
CommentNode,
|
||||
@ -16,7 +16,7 @@ import {
|
||||
describe('compiler: parse', () => {
|
||||
describe('Text', () => {
|
||||
test('simple text', () => {
|
||||
const ast = parse('some text')
|
||||
const ast = baseParse('some text')
|
||||
const text = ast.children[0] as TextNode
|
||||
|
||||
expect(text).toStrictEqual({
|
||||
@ -31,7 +31,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('simple text with invalid end tag', () => {
|
||||
const ast = parse('some text</div>', {
|
||||
const ast = baseParse('some text</div>', {
|
||||
onError: () => {}
|
||||
})
|
||||
const text = ast.children[0] as TextNode
|
||||
@ -48,7 +48,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('text with interpolation', () => {
|
||||
const ast = parse('some {{ foo + bar }} text')
|
||||
const ast = baseParse('some {{ foo + bar }} text')
|
||||
const text1 = ast.children[0] as TextNode
|
||||
const text2 = ast.children[2] as TextNode
|
||||
|
||||
@ -73,7 +73,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('text with interpolation which has `<`', () => {
|
||||
const ast = parse('some {{ a<b && c>d }} text')
|
||||
const ast = baseParse('some {{ a<b && c>d }} text')
|
||||
const text1 = ast.children[0] as TextNode
|
||||
const text2 = ast.children[2] as TextNode
|
||||
|
||||
@ -98,7 +98,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('text with mix of tags and interpolations', () => {
|
||||
const ast = parse('some <span>{{ foo < bar + foo }} text</span>')
|
||||
const ast = baseParse('some <span>{{ foo < bar + foo }} text</span>')
|
||||
const text1 = ast.children[0] as TextNode
|
||||
const text2 = (ast.children[1] as ElementNode).children![1] as TextNode
|
||||
|
||||
@ -123,7 +123,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('lonly "<" don\'t separate nodes', () => {
|
||||
const ast = parse('a < b', {
|
||||
const ast = baseParse('a < b', {
|
||||
onError: err => {
|
||||
if (err.code !== ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME) {
|
||||
throw err
|
||||
@ -144,7 +144,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('lonly "{{" don\'t separate nodes', () => {
|
||||
const ast = parse('a {{ b', {
|
||||
const ast = baseParse('a {{ b', {
|
||||
onError: error => {
|
||||
if (error.code !== ErrorCodes.X_MISSING_INTERPOLATION_END) {
|
||||
throw error
|
||||
@ -166,7 +166,7 @@ describe('compiler: parse', () => {
|
||||
|
||||
test('HTML entities compatibility in text (https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state).', () => {
|
||||
const spy = jest.fn()
|
||||
const ast = parse('&ersand;', {
|
||||
const ast = baseParse('&ersand;', {
|
||||
namedCharacterReferences: { amp: '&' },
|
||||
onError: spy
|
||||
})
|
||||
@ -195,7 +195,7 @@ describe('compiler: parse', () => {
|
||||
|
||||
test('HTML entities compatibility in attribute (https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state).', () => {
|
||||
const spy = jest.fn()
|
||||
const ast = parse(
|
||||
const ast = baseParse(
|
||||
'<div a="&ersand;" b="&ersand;" c="&!"></div>',
|
||||
{
|
||||
namedCharacterReferences: { amp: '&', 'amp;': '&' },
|
||||
@ -248,7 +248,7 @@ describe('compiler: parse', () => {
|
||||
|
||||
test('Some control character reference should be replaced.', () => {
|
||||
const spy = jest.fn()
|
||||
const ast = parse('†', { onError: spy })
|
||||
const ast = baseParse('†', { onError: spy })
|
||||
const text = ast.children[0] as TextNode
|
||||
|
||||
expect(text).toStrictEqual({
|
||||
@ -275,7 +275,7 @@ describe('compiler: parse', () => {
|
||||
|
||||
describe('Interpolation', () => {
|
||||
test('simple interpolation', () => {
|
||||
const ast = parse('{{message}}')
|
||||
const ast = baseParse('{{message}}')
|
||||
const interpolation = ast.children[0] as InterpolationNode
|
||||
|
||||
expect(interpolation).toStrictEqual({
|
||||
@ -300,7 +300,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('it can have tag-like notation', () => {
|
||||
const ast = parse('{{ a<b }}')
|
||||
const ast = baseParse('{{ a<b }}')
|
||||
const interpolation = ast.children[0] as InterpolationNode
|
||||
|
||||
expect(interpolation).toStrictEqual({
|
||||
@ -325,7 +325,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('it can have tag-like notation (2)', () => {
|
||||
const ast = parse('{{ a<b }}{{ c>d }}')
|
||||
const ast = baseParse('{{ a<b }}{{ c>d }}')
|
||||
const interpolation1 = ast.children[0] as InterpolationNode
|
||||
const interpolation2 = ast.children[1] as InterpolationNode
|
||||
|
||||
@ -371,7 +371,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('it can have tag-like notation (3)', () => {
|
||||
const ast = parse('<div>{{ "</div>" }}</div>')
|
||||
const ast = baseParse('<div>{{ "</div>" }}</div>')
|
||||
const element = ast.children[0] as ElementNode
|
||||
const interpolation = element.children[0] as InterpolationNode
|
||||
|
||||
@ -398,7 +398,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('custom delimiters', () => {
|
||||
const ast = parse('<p>{msg}</p>', {
|
||||
const ast = baseParse('<p>{msg}</p>', {
|
||||
delimiters: ['{', '}']
|
||||
})
|
||||
const element = ast.children[0] as ElementNode
|
||||
@ -428,7 +428,7 @@ describe('compiler: parse', () => {
|
||||
|
||||
describe('Comment', () => {
|
||||
test('empty comment', () => {
|
||||
const ast = parse('<!---->')
|
||||
const ast = baseParse('<!---->')
|
||||
const comment = ast.children[0] as CommentNode
|
||||
|
||||
expect(comment).toStrictEqual({
|
||||
@ -443,7 +443,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('simple comment', () => {
|
||||
const ast = parse('<!--abc-->')
|
||||
const ast = baseParse('<!--abc-->')
|
||||
const comment = ast.children[0] as CommentNode
|
||||
|
||||
expect(comment).toStrictEqual({
|
||||
@ -458,7 +458,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('two comments', () => {
|
||||
const ast = parse('<!--abc--><!--def-->')
|
||||
const ast = baseParse('<!--abc--><!--def-->')
|
||||
const comment1 = ast.children[0] as CommentNode
|
||||
const comment2 = ast.children[1] as CommentNode
|
||||
|
||||
@ -485,7 +485,7 @@ describe('compiler: parse', () => {
|
||||
|
||||
describe('Element', () => {
|
||||
test('simple div', () => {
|
||||
const ast = parse('<div>hello</div>')
|
||||
const ast = baseParse('<div>hello</div>')
|
||||
const element = ast.children[0] as ElementNode
|
||||
|
||||
expect(element).toStrictEqual({
|
||||
@ -516,7 +516,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('empty', () => {
|
||||
const ast = parse('<div></div>')
|
||||
const ast = baseParse('<div></div>')
|
||||
const element = ast.children[0] as ElementNode
|
||||
|
||||
expect(element).toStrictEqual({
|
||||
@ -538,7 +538,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('self closing', () => {
|
||||
const ast = parse('<div/>after')
|
||||
const ast = baseParse('<div/>after')
|
||||
const element = ast.children[0] as ElementNode
|
||||
|
||||
expect(element).toStrictEqual({
|
||||
@ -560,7 +560,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('void element', () => {
|
||||
const ast = parse('<img>after', {
|
||||
const ast = baseParse('<img>after', {
|
||||
isVoidTag: tag => tag === 'img'
|
||||
})
|
||||
const element = ast.children[0] as ElementNode
|
||||
@ -584,7 +584,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('native element with `isNativeTag`', () => {
|
||||
const ast = parse('<div></div><comp></comp><Comp></Comp>', {
|
||||
const ast = baseParse('<div></div><comp></comp><Comp></Comp>', {
|
||||
isNativeTag: tag => tag === 'div'
|
||||
})
|
||||
|
||||
@ -608,7 +608,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('native element without `isNativeTag`', () => {
|
||||
const ast = parse('<div></div><comp></comp><Comp></Comp>')
|
||||
const ast = baseParse('<div></div><comp></comp><Comp></Comp>')
|
||||
|
||||
expect(ast.children[0]).toMatchObject({
|
||||
type: NodeTypes.ELEMENT,
|
||||
@ -630,7 +630,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('custom element', () => {
|
||||
const ast = parse('<div></div><comp></comp>', {
|
||||
const ast = baseParse('<div></div><comp></comp>', {
|
||||
isNativeTag: tag => tag === 'div',
|
||||
isCustomElement: tag => tag === 'comp'
|
||||
})
|
||||
@ -649,7 +649,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('attribute with no value', () => {
|
||||
const ast = parse('<div id></div>')
|
||||
const ast = baseParse('<div id></div>')
|
||||
const element = ast.children[0] as ElementNode
|
||||
|
||||
expect(element).toStrictEqual({
|
||||
@ -682,7 +682,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('attribute with empty value, double quote', () => {
|
||||
const ast = parse('<div id=""></div>')
|
||||
const ast = baseParse('<div id=""></div>')
|
||||
const element = ast.children[0] as ElementNode
|
||||
|
||||
expect(element).toStrictEqual({
|
||||
@ -723,7 +723,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('attribute with empty value, single quote', () => {
|
||||
const ast = parse("<div id=''></div>")
|
||||
const ast = baseParse("<div id=''></div>")
|
||||
const element = ast.children[0] as ElementNode
|
||||
|
||||
expect(element).toStrictEqual({
|
||||
@ -764,7 +764,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('attribute with value, double quote', () => {
|
||||
const ast = parse('<div id=">\'"></div>')
|
||||
const ast = baseParse('<div id=">\'"></div>')
|
||||
const element = ast.children[0] as ElementNode
|
||||
|
||||
expect(element).toStrictEqual({
|
||||
@ -805,7 +805,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('attribute with value, single quote', () => {
|
||||
const ast = parse("<div id='>\"'></div>")
|
||||
const ast = baseParse("<div id='>\"'></div>")
|
||||
const element = ast.children[0] as ElementNode
|
||||
|
||||
expect(element).toStrictEqual({
|
||||
@ -846,7 +846,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('attribute with value, unquoted', () => {
|
||||
const ast = parse('<div id=a/></div>')
|
||||
const ast = baseParse('<div id=a/></div>')
|
||||
const element = ast.children[0] as ElementNode
|
||||
|
||||
expect(element).toStrictEqual({
|
||||
@ -887,7 +887,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('multiple attributes', () => {
|
||||
const ast = parse('<div id=a class="c" inert style=\'\'></div>')
|
||||
const ast = baseParse('<div id=a class="c" inert style=\'\'></div>')
|
||||
const element = ast.children[0] as ElementNode
|
||||
|
||||
expect(element).toStrictEqual({
|
||||
@ -974,7 +974,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('directive with no value', () => {
|
||||
const ast = parse('<div v-if/>')
|
||||
const ast = baseParse('<div v-if/>')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
@ -992,7 +992,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('directive with value', () => {
|
||||
const ast = parse('<div v-if="a"/>')
|
||||
const ast = baseParse('<div v-if="a"/>')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
@ -1020,7 +1020,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('directive with argument', () => {
|
||||
const ast = parse('<div v-on:click/>')
|
||||
const ast = baseParse('<div v-on:click/>')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
@ -1057,7 +1057,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('directive with a modifier', () => {
|
||||
const ast = parse('<div v-on.enter/>')
|
||||
const ast = baseParse('<div v-on.enter/>')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
@ -1075,7 +1075,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('directive with two modifiers', () => {
|
||||
const ast = parse('<div v-on.enter.exact/>')
|
||||
const ast = baseParse('<div v-on.enter.exact/>')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
@ -1093,7 +1093,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('directive with argument and modifiers', () => {
|
||||
const ast = parse('<div v-on:click.enter.exact/>')
|
||||
const ast = baseParse('<div v-on:click.enter.exact/>')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
@ -1130,7 +1130,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('v-bind shorthand', () => {
|
||||
const ast = parse('<div :a=b />')
|
||||
const ast = baseParse('<div :a=b />')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
@ -1178,7 +1178,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('v-bind shorthand with modifier', () => {
|
||||
const ast = parse('<div :a.sync=b />')
|
||||
const ast = baseParse('<div :a.sync=b />')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
@ -1226,7 +1226,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('v-on shorthand', () => {
|
||||
const ast = parse('<div @a=b />')
|
||||
const ast = baseParse('<div @a=b />')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
@ -1274,7 +1274,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('v-on shorthand with modifier', () => {
|
||||
const ast = parse('<div @a.enter=b />')
|
||||
const ast = baseParse('<div @a.enter=b />')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
@ -1322,7 +1322,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('v-slot shorthand', () => {
|
||||
const ast = parse('<Comp #a="{ b }" />')
|
||||
const ast = baseParse('<Comp #a="{ b }" />')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
@ -1369,7 +1369,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('v-pre', () => {
|
||||
const ast = parse(
|
||||
const ast = baseParse(
|
||||
`<div v-pre :id="foo"><Comp/>{{ bar }}</div>\n` +
|
||||
`<div :id="foo"><Comp/>{{ bar }}</div>`
|
||||
)
|
||||
@ -1451,7 +1451,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('end tags are case-insensitive.', () => {
|
||||
const ast = parse('<div>hello</DIV>after')
|
||||
const ast = baseParse('<div>hello</DIV>after')
|
||||
const element = ast.children[0] as ElementNode
|
||||
const text = element.children[0] as TextNode
|
||||
|
||||
@ -1468,14 +1468,14 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('self closing single tag', () => {
|
||||
const ast = parse('<div :class="{ some: condition }" />')
|
||||
const ast = baseParse('<div :class="{ some: condition }" />')
|
||||
|
||||
expect(ast.children).toHaveLength(1)
|
||||
expect(ast.children[0]).toMatchObject({ tag: 'div' })
|
||||
})
|
||||
|
||||
test('self closing multiple tag', () => {
|
||||
const ast = parse(
|
||||
const ast = baseParse(
|
||||
`<div :class="{ some: condition }" />\n` +
|
||||
`<p v-bind:style="{ color: 'red' }"/>`
|
||||
)
|
||||
@ -1488,7 +1488,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('valid html', () => {
|
||||
const ast = parse(
|
||||
const ast = baseParse(
|
||||
`<div :class="{ some: condition }">\n` +
|
||||
` <p v-bind:style="{ color: 'red' }"/>\n` +
|
||||
` <!-- a comment with <html> inside it -->\n` +
|
||||
@ -1513,11 +1513,11 @@ describe('compiler: parse', () => {
|
||||
|
||||
test('invalid html', () => {
|
||||
expect(() => {
|
||||
parse(`<div>\n<span>\n</div>\n</span>`)
|
||||
baseParse(`<div>\n<span>\n</div>\n</span>`)
|
||||
}).toThrow('Element is missing end tag.')
|
||||
|
||||
const spy = jest.fn()
|
||||
const ast = parse(`<div>\n<span>\n</div>\n</span>`, {
|
||||
const ast = baseParse(`<div>\n<span>\n</div>\n</span>`, {
|
||||
onError: spy
|
||||
})
|
||||
|
||||
@ -1552,7 +1552,7 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
|
||||
test('parse with correct location info', () => {
|
||||
const [foo, bar, but, baz] = parse(
|
||||
const [foo, bar, but, baz] = baseParse(
|
||||
`
|
||||
foo
|
||||
is {{ bar }} but {{ baz }}`.trim()
|
||||
@ -1588,7 +1588,7 @@ foo
|
||||
|
||||
describe('namedCharacterReferences option', () => {
|
||||
test('use the given map', () => {
|
||||
const ast: any = parse('&∪︀', {
|
||||
const ast: any = baseParse('&∪︀', {
|
||||
namedCharacterReferences: {
|
||||
'cups;': '\u222A\uFE00' // UNION with serifs
|
||||
},
|
||||
@ -1603,18 +1603,18 @@ foo
|
||||
|
||||
describe('whitespace management', () => {
|
||||
it('should remove whitespaces at start/end inside an element', () => {
|
||||
const ast = parse(`<div> <span/> </div>`)
|
||||
const ast = baseParse(`<div> <span/> </div>`)
|
||||
expect((ast.children[0] as ElementNode).children.length).toBe(1)
|
||||
})
|
||||
|
||||
it('should remove whitespaces w/ newline between elements', () => {
|
||||
const ast = parse(`<div/> \n <div/> \n <div/>`)
|
||||
const ast = baseParse(`<div/> \n <div/> \n <div/>`)
|
||||
expect(ast.children.length).toBe(3)
|
||||
expect(ast.children.every(c => c.type === NodeTypes.ELEMENT)).toBe(true)
|
||||
})
|
||||
|
||||
it('should remove whitespaces adjacent to comments', () => {
|
||||
const ast = parse(`<div/> \n <!--foo--> <div/>`)
|
||||
const ast = baseParse(`<div/> \n <!--foo--> <div/>`)
|
||||
expect(ast.children.length).toBe(3)
|
||||
expect(ast.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect(ast.children[1].type).toBe(NodeTypes.COMMENT)
|
||||
@ -1622,7 +1622,7 @@ foo
|
||||
})
|
||||
|
||||
it('should remove whitespaces w/ newline between comments and elements', () => {
|
||||
const ast = parse(`<div/> \n <!--foo--> \n <div/>`)
|
||||
const ast = baseParse(`<div/> \n <!--foo--> \n <div/>`)
|
||||
expect(ast.children.length).toBe(3)
|
||||
expect(ast.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect(ast.children[1].type).toBe(NodeTypes.COMMENT)
|
||||
@ -1630,7 +1630,7 @@ foo
|
||||
})
|
||||
|
||||
it('should NOT remove whitespaces w/ newline between interpolations', () => {
|
||||
const ast = parse(`{{ foo }} \n {{ bar }}`)
|
||||
const ast = baseParse(`{{ foo }} \n {{ bar }}`)
|
||||
expect(ast.children.length).toBe(3)
|
||||
expect(ast.children[0].type).toBe(NodeTypes.INTERPOLATION)
|
||||
expect(ast.children[1]).toMatchObject({
|
||||
@ -1641,7 +1641,7 @@ foo
|
||||
})
|
||||
|
||||
it('should NOT remove whitespaces w/o newline between elements', () => {
|
||||
const ast = parse(`<div/> <div/> <div/>`)
|
||||
const ast = baseParse(`<div/> <div/> <div/>`)
|
||||
expect(ast.children.length).toBe(5)
|
||||
expect(ast.children.map(c => c.type)).toMatchObject([
|
||||
NodeTypes.ELEMENT,
|
||||
@ -1653,7 +1653,7 @@ foo
|
||||
})
|
||||
|
||||
it('should condense consecutive whitespaces in text', () => {
|
||||
const ast = parse(` foo \n bar baz `)
|
||||
const ast = baseParse(` foo \n bar baz `)
|
||||
expect((ast.children[0] as TextNode).content).toBe(` foo bar baz `)
|
||||
})
|
||||
})
|
||||
@ -2716,7 +2716,7 @@ foo
|
||||
),
|
||||
() => {
|
||||
const spy = jest.fn()
|
||||
const ast = parse(code, {
|
||||
const ast = baseParse(code, {
|
||||
getNamespace: (tag, parent) => {
|
||||
const ns = parent ? parent.ns : Namespaces.HTML
|
||||
if (ns === Namespaces.HTML) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { parse } from '../src/parse'
|
||||
import { baseParse } from '../src/parse'
|
||||
import { transform, NodeTransform } from '../src/transform'
|
||||
import {
|
||||
ElementNode,
|
||||
@ -26,7 +26,7 @@ import { PatchFlags } from '@vue/shared'
|
||||
|
||||
describe('compiler: transform', () => {
|
||||
test('context state', () => {
|
||||
const ast = parse(`<div>hello {{ world }}</div>`)
|
||||
const ast = baseParse(`<div>hello {{ world }}</div>`)
|
||||
|
||||
// manually store call arguments because context is mutable and shared
|
||||
// across calls
|
||||
@ -72,7 +72,7 @@ describe('compiler: transform', () => {
|
||||
})
|
||||
|
||||
test('context.replaceNode', () => {
|
||||
const ast = parse(`<div/><span/>`)
|
||||
const ast = baseParse(`<div/><span/>`)
|
||||
const plugin: NodeTransform = (node, context) => {
|
||||
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
||||
// change the node to <p>
|
||||
@ -106,7 +106,7 @@ describe('compiler: transform', () => {
|
||||
})
|
||||
|
||||
test('context.removeNode', () => {
|
||||
const ast = parse(`<span/><div>hello</div><span/>`)
|
||||
const ast = baseParse(`<span/><div>hello</div><span/>`)
|
||||
const c1 = ast.children[0]
|
||||
const c2 = ast.children[2]
|
||||
|
||||
@ -132,7 +132,7 @@ describe('compiler: transform', () => {
|
||||
})
|
||||
|
||||
test('context.removeNode (prev sibling)', () => {
|
||||
const ast = parse(`<span/><div/><span/>`)
|
||||
const ast = baseParse(`<span/><div/><span/>`)
|
||||
const c1 = ast.children[0]
|
||||
const c2 = ast.children[2]
|
||||
|
||||
@ -159,7 +159,7 @@ describe('compiler: transform', () => {
|
||||
})
|
||||
|
||||
test('context.removeNode (next sibling)', () => {
|
||||
const ast = parse(`<span/><div/><span/>`)
|
||||
const ast = baseParse(`<span/><div/><span/>`)
|
||||
const c1 = ast.children[0]
|
||||
const d1 = ast.children[1]
|
||||
|
||||
@ -186,7 +186,7 @@ describe('compiler: transform', () => {
|
||||
})
|
||||
|
||||
test('context.hoist', () => {
|
||||
const ast = parse(`<div :id="foo"/><div :id="bar"/>`)
|
||||
const ast = baseParse(`<div :id="foo"/><div :id="bar"/>`)
|
||||
const hoisted: ExpressionNode[] = []
|
||||
const mock: NodeTransform = (node, context) => {
|
||||
if (node.type === NodeTypes.ELEMENT) {
|
||||
@ -204,7 +204,7 @@ describe('compiler: transform', () => {
|
||||
})
|
||||
|
||||
test('onError option', () => {
|
||||
const ast = parse(`<div/>`)
|
||||
const ast = baseParse(`<div/>`)
|
||||
const loc = ast.children[0].loc
|
||||
const plugin: NodeTransform = (node, context) => {
|
||||
context.onError(
|
||||
@ -225,20 +225,20 @@ describe('compiler: transform', () => {
|
||||
})
|
||||
|
||||
test('should inject toString helper for interpolations', () => {
|
||||
const ast = parse(`{{ foo }}`)
|
||||
const ast = baseParse(`{{ foo }}`)
|
||||
transform(ast, {})
|
||||
expect(ast.helpers).toContain(TO_STRING)
|
||||
})
|
||||
|
||||
test('should inject createVNode and Comment for comments', () => {
|
||||
const ast = parse(`<!--foo-->`)
|
||||
const ast = baseParse(`<!--foo-->`)
|
||||
transform(ast, {})
|
||||
expect(ast.helpers).toContain(CREATE_COMMENT)
|
||||
})
|
||||
|
||||
describe('root codegenNode', () => {
|
||||
function transformWithCodegen(template: string) {
|
||||
const ast = parse(template)
|
||||
const ast = baseParse(template)
|
||||
transform(ast, {
|
||||
nodeTransforms: [
|
||||
transformIf,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
NodeTypes,
|
||||
generate,
|
||||
|
@ -1,4 +1,9 @@
|
||||
import { CompilerOptions, parse, transform, ErrorCodes } from '../../src'
|
||||
import {
|
||||
CompilerOptions,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
ErrorCodes
|
||||
} from '../../src'
|
||||
import {
|
||||
RESOLVE_COMPONENT,
|
||||
CREATE_VNODE,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
ElementNode,
|
||||
DirectiveNode,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {
|
||||
CompilerOptions,
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
ElementNode,
|
||||
NodeTypes,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {
|
||||
CompilerOptions,
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
NodeTypes,
|
||||
generate,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
ElementNode,
|
||||
ObjectExpression,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { parse } from '../../src/parse'
|
||||
import { baseParse as parse } from '../../src/parse'
|
||||
import { transform } from '../../src/transform'
|
||||
import { transformIf } from '../../src/transforms/vIf'
|
||||
import { transformFor } from '../../src/transforms/vFor'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { parse } from '../../src/parse'
|
||||
import { baseParse as parse } from '../../src/parse'
|
||||
import { transform } from '../../src/transform'
|
||||
import { transformIf } from '../../src/transforms/vIf'
|
||||
import { transformElement } from '../../src/transforms/transformElement'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
generate,
|
||||
ElementNode,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
ElementNode,
|
||||
ObjectExpression,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
NodeTypes,
|
||||
generate,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {
|
||||
CompilerOptions,
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
generate,
|
||||
ElementNode,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { CompilerOptions } from './options'
|
||||
import { parse } from './parse'
|
||||
import { baseParse } from './parse'
|
||||
import { transform } from './transform'
|
||||
import { generate, CodegenResult } from './codegen'
|
||||
import { RootNode } from './ast'
|
||||
@ -43,7 +43,7 @@ export function baseCompile(
|
||||
onError(createCompilerError(ErrorCodes.X_SCOPE_ID_NOT_SUPPORTED))
|
||||
}
|
||||
|
||||
const ast = isString(template) ? parse(template, options) : template
|
||||
const ast = isString(template) ? baseParse(template, options) : template
|
||||
transform(ast, {
|
||||
...options,
|
||||
prefixIdentifiers,
|
||||
|
@ -7,7 +7,7 @@ export {
|
||||
TransformOptions,
|
||||
CodegenOptions
|
||||
} from './options'
|
||||
export { parse, TextModes } from './parse'
|
||||
export { baseParse, TextModes } from './parse'
|
||||
export {
|
||||
transform,
|
||||
createStructuralDirectiveTransform,
|
||||
|
@ -66,7 +66,10 @@ interface ParserContext {
|
||||
inPre: boolean
|
||||
}
|
||||
|
||||
export function parse(content: string, options: ParserOptions = {}): RootNode {
|
||||
export function baseParse(
|
||||
content: string,
|
||||
options: ParserOptions = {}
|
||||
): RootNode {
|
||||
const context = createParserContext(content, options)
|
||||
const start = getCursor(context)
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
NodeTypes,
|
||||
ElementNode,
|
||||
TextNode,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
CompilerOptions,
|
||||
ElementNode,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
ElementNode,
|
||||
CallExpression
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
PlainElementNode,
|
||||
CompilerOptions
|
||||
|
@ -1,4 +1,9 @@
|
||||
import { parse, transform, CompilerOptions, generate } from '@vue/compiler-core'
|
||||
import {
|
||||
baseParse as parse,
|
||||
transform,
|
||||
CompilerOptions,
|
||||
generate
|
||||
} from '@vue/compiler-core'
|
||||
import { transformModel } from '../../src/transforms/vModel'
|
||||
import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
|
||||
import { DOMErrorCodes } from '../../src/errors'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
CompilerOptions,
|
||||
ElementNode,
|
||||
|
@ -1,4 +1,9 @@
|
||||
import { parse, transform, generate, CompilerOptions } from '@vue/compiler-core'
|
||||
import {
|
||||
baseParse as parse,
|
||||
transform,
|
||||
generate,
|
||||
CompilerOptions
|
||||
} from '@vue/compiler-core'
|
||||
import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
|
||||
import { transformShow } from '../../src/transforms/vShow'
|
||||
import { DOMErrorCodes } from '../../src/errors'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
parse,
|
||||
baseParse as parse,
|
||||
transform,
|
||||
PlainElementNode,
|
||||
CompilerOptions
|
||||
|
@ -1,8 +1,10 @@
|
||||
import {
|
||||
baseCompile,
|
||||
baseParse,
|
||||
CompilerOptions,
|
||||
CodegenResult,
|
||||
isBuiltInType
|
||||
isBuiltInType,
|
||||
ParserOptions
|
||||
} from '@vue/compiler-core'
|
||||
import { parserOptionsMinimal } from './parserOptionsMinimal'
|
||||
import { parserOptionsStandard } from './parserOptionsStandard'
|
||||
@ -15,13 +17,15 @@ import { transformOn } from './transforms/vOn'
|
||||
import { transformShow } from './transforms/vShow'
|
||||
import { TRANSITION, TRANSITION_GROUP } from './runtimeHelpers'
|
||||
|
||||
const parserOptions = __BROWSER__ ? parserOptionsMinimal : parserOptionsStandard
|
||||
|
||||
export function compile(
|
||||
template: string,
|
||||
options: CompilerOptions = {}
|
||||
): CodegenResult {
|
||||
return baseCompile(template, {
|
||||
...parserOptions,
|
||||
...options,
|
||||
...(__BROWSER__ ? parserOptionsMinimal : parserOptionsStandard),
|
||||
nodeTransforms: [transformStyle, ...(options.nodeTransforms || [])],
|
||||
directiveTransforms: {
|
||||
cloak: transformCloak,
|
||||
@ -42,4 +46,11 @@ export function compile(
|
||||
})
|
||||
}
|
||||
|
||||
export function parse(template: string, options: ParserOptions = {}) {
|
||||
return baseParse(template, {
|
||||
...parserOptions,
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
export * from '@vue/compiler-core'
|
||||
|
@ -23,7 +23,7 @@ body
|
||||
</template>
|
||||
`,
|
||||
{ filename: 'example.vue', sourceMap: true }
|
||||
).template as SFCTemplateBlock
|
||||
).descriptor.template as SFCTemplateBlock
|
||||
|
||||
const result = compileTemplate({
|
||||
filename: 'example.vue',
|
||||
@ -35,10 +35,10 @@ body
|
||||
})
|
||||
|
||||
test('warn missing preprocessor', () => {
|
||||
const template = parse(`<template lang="unknownLang">\n</template>\n`, {
|
||||
const template = parse(`<template lang="unknownLang">hi</template>\n`, {
|
||||
filename: 'example.vue',
|
||||
sourceMap: true
|
||||
}).template as SFCTemplateBlock
|
||||
}).descriptor.template as SFCTemplateBlock
|
||||
|
||||
const result = compileTemplate({
|
||||
filename: 'example.vue',
|
||||
@ -70,10 +70,10 @@ test('source map', () => {
|
||||
`
|
||||
<template>
|
||||
<div><p>{{ render }}</p></div>
|
||||
</template>
|
||||
</template>
|
||||
`,
|
||||
{ filename: 'example.vue', sourceMap: true }
|
||||
).template as SFCTemplateBlock
|
||||
).descriptor.template as SFCTemplateBlock
|
||||
|
||||
const result = compileTemplate({
|
||||
filename: 'example.vue',
|
||||
@ -86,7 +86,7 @@ test('source map', () => {
|
||||
test('template errors', () => {
|
||||
const result = compileTemplate({
|
||||
filename: 'example.vue',
|
||||
source: `<div :foo
|
||||
source: `<div :foo
|
||||
:bar="a[" v-model="baz"/>`
|
||||
})
|
||||
expect(result.errors).toMatchSnapshot()
|
||||
@ -100,7 +100,7 @@ test('preprocessor errors', () => {
|
||||
</template>
|
||||
`,
|
||||
{ filename: 'example.vue', sourceMap: true }
|
||||
).template as SFCTemplateBlock
|
||||
).descriptor.template as SFCTemplateBlock
|
||||
|
||||
const result = compileTemplate({
|
||||
filename: 'example.vue',
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { parse } from '../src'
|
||||
import { mockWarn } from '@vue/runtime-test'
|
||||
import { baseParse, baseCompile } from '@vue/compiler-core'
|
||||
|
||||
describe('compiler:sfc', () => {
|
||||
mockWarn()
|
||||
@ -7,13 +8,14 @@ describe('compiler:sfc', () => {
|
||||
describe('source map', () => {
|
||||
test('style block', () => {
|
||||
const style = parse(`<style>\n.color {\n color: red;\n }\n</style>\n`)
|
||||
.styles[0]
|
||||
.descriptor.styles[0]
|
||||
// TODO need to actually test this with SourceMapConsumer
|
||||
expect(style.map).not.toBeUndefined()
|
||||
})
|
||||
|
||||
test('script block', () => {
|
||||
const script = parse(`<script>\nconsole.log(1)\n }\n</script>\n`).script
|
||||
const script = parse(`<script>\nconsole.log(1)\n }\n</script>\n`)
|
||||
.descriptor.script
|
||||
// TODO need to actually test this with SourceMapConsumer
|
||||
expect(script!.map).not.toBeUndefined()
|
||||
})
|
||||
@ -30,12 +32,12 @@ export default {}
|
||||
<style>
|
||||
h1 { color: red }
|
||||
</style>`
|
||||
const padFalse = parse(content.trim(), { pad: false })
|
||||
const padFalse = parse(content.trim(), { pad: false }).descriptor
|
||||
expect(padFalse.template!.content).toBe('\n<div></div>\n')
|
||||
expect(padFalse.script!.content).toBe('\nexport default {}\n')
|
||||
expect(padFalse.styles[0].content).toBe('\nh1 { color: red }\n')
|
||||
|
||||
const padTrue = parse(content.trim(), { pad: true })
|
||||
const padTrue = parse(content.trim(), { pad: true }).descriptor
|
||||
expect(padTrue.script!.content).toBe(
|
||||
Array(3 + 1).join('//\n') + '\nexport default {}\n'
|
||||
)
|
||||
@ -43,7 +45,7 @@ h1 { color: red }
|
||||
Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
|
||||
)
|
||||
|
||||
const padLine = parse(content.trim(), { pad: 'line' })
|
||||
const padLine = parse(content.trim(), { pad: 'line' }).descriptor
|
||||
expect(padLine.script!.content).toBe(
|
||||
Array(3 + 1).join('//\n') + '\nexport default {}\n'
|
||||
)
|
||||
@ -51,7 +53,7 @@ h1 { color: red }
|
||||
Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
|
||||
)
|
||||
|
||||
const padSpace = parse(content.trim(), { pad: 'space' })
|
||||
const padSpace = parse(content.trim(), { pad: 'space' }).descriptor
|
||||
expect(padSpace.script!.content).toBe(
|
||||
`<template>\n<div></div>\n</template>\n<script>`.replace(/./g, ' ') +
|
||||
'\nexport default {}\n'
|
||||
@ -65,13 +67,42 @@ h1 { color: red }
|
||||
})
|
||||
|
||||
test('should ignore nodes with no content', () => {
|
||||
expect(parse(`<template/>`).template).toBe(null)
|
||||
expect(parse(`<script/>`).script).toBe(null)
|
||||
expect(parse(`<style/>`).styles.length).toBe(0)
|
||||
expect(parse(`<custom/>`).customBlocks.length).toBe(0)
|
||||
expect(parse(`<template/>`).descriptor.template).toBe(null)
|
||||
expect(parse(`<script/>`).descriptor.script).toBe(null)
|
||||
expect(parse(`<style/>`).descriptor.styles.length).toBe(0)
|
||||
expect(parse(`<custom/>`).descriptor.customBlocks.length).toBe(0)
|
||||
})
|
||||
|
||||
describe('error', () => {
|
||||
test('nested templates', () => {
|
||||
const content = `
|
||||
<template v-if="ok">ok</template>
|
||||
<div><div></div></div>
|
||||
`
|
||||
const sfc = parse(`<template>${content}</template>`).descriptor
|
||||
expect(sfc.template!.content).toBe(content)
|
||||
})
|
||||
|
||||
test('error tolerance', () => {
|
||||
const { errors } = parse(`<template>`)
|
||||
expect(errors.length).toBe(1)
|
||||
})
|
||||
|
||||
test('should parse as DOM by default', () => {
|
||||
const { errors } = parse(`<template><input></template>`)
|
||||
expect(errors.length).toBe(0)
|
||||
})
|
||||
|
||||
test('custom compiler', () => {
|
||||
const { errors } = parse(`<template><input></template>`, {
|
||||
compiler: {
|
||||
parse: baseParse,
|
||||
compile: baseCompile
|
||||
}
|
||||
})
|
||||
expect(errors.length).toBe(1)
|
||||
})
|
||||
|
||||
describe('warnings', () => {
|
||||
test('should only allow single template element', () => {
|
||||
parse(`<template><div/></template><template><div/></template>`)
|
||||
expect(
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { generate, parse, transform } from '@vue/compiler-core'
|
||||
import { generate, baseParse, transform } from '@vue/compiler-core'
|
||||
import { transformAssetUrl } from '../src/templateTransformAssetUrl'
|
||||
import { transformElement } from '../../compiler-core/src/transforms/transformElement'
|
||||
import { transformBind } from '../../compiler-core/src/transforms/vBind'
|
||||
|
||||
function compileWithAssetUrls(template: string) {
|
||||
const ast = parse(template)
|
||||
const ast = baseParse(template)
|
||||
transform(ast, {
|
||||
nodeTransforms: [transformAssetUrl, transformElement],
|
||||
directiveTransforms: {
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { generate, parse, transform } from '@vue/compiler-core'
|
||||
import { generate, baseParse, transform } from '@vue/compiler-core'
|
||||
import { transformSrcset } from '../src/templateTransformSrcset'
|
||||
import { transformElement } from '../../compiler-core/src/transforms/transformElement'
|
||||
import { transformBind } from '../../compiler-core/src/transforms/vBind'
|
||||
|
||||
function compileWithSrcset(template: string) {
|
||||
const ast = parse(template)
|
||||
const ast = baseParse(template)
|
||||
transform(ast, {
|
||||
nodeTransforms: [transformSrcset, transformElement],
|
||||
directiveTransforms: {
|
||||
|
@ -2,7 +2,9 @@ import {
|
||||
CompilerOptions,
|
||||
CodegenResult,
|
||||
CompilerError,
|
||||
NodeTransform
|
||||
NodeTransform,
|
||||
ParserOptions,
|
||||
RootNode
|
||||
} from '@vue/compiler-core'
|
||||
import { SourceMapConsumer, SourceMapGenerator, RawSourceMap } from 'source-map'
|
||||
import {
|
||||
@ -16,6 +18,7 @@ import consolidate from 'consolidate'
|
||||
|
||||
export interface TemplateCompiler {
|
||||
compile(template: string, options: CompilerOptions): CodegenResult
|
||||
parse(template: string, options: ParserOptions): RootNode
|
||||
}
|
||||
|
||||
export interface SFCTemplateCompileResults {
|
||||
|
@ -18,4 +18,8 @@ export {
|
||||
SFCTemplateCompileResults
|
||||
} from './compileTemplate'
|
||||
export { SFCStyleCompileOptions, SFCStyleCompileResults } from './compileStyle'
|
||||
export { CompilerOptions, generateCodeFrame } from '@vue/compiler-core'
|
||||
export {
|
||||
CompilerOptions,
|
||||
CompilerError,
|
||||
generateCodeFrame
|
||||
} from '@vue/compiler-core'
|
||||
|
@ -1,20 +1,20 @@
|
||||
import {
|
||||
parse as baseParse,
|
||||
TextModes,
|
||||
NodeTypes,
|
||||
TextNode,
|
||||
ElementNode,
|
||||
SourceLocation
|
||||
SourceLocation,
|
||||
CompilerError
|
||||
} from '@vue/compiler-core'
|
||||
import { RawSourceMap, SourceMapGenerator } from 'source-map'
|
||||
import LRUCache from 'lru-cache'
|
||||
import { generateCodeFrame } from '@vue/shared'
|
||||
import { TemplateCompiler } from './compileTemplate'
|
||||
|
||||
export interface SFCParseOptions {
|
||||
filename?: string
|
||||
sourceMap?: boolean
|
||||
sourceRoot?: string
|
||||
pad?: boolean | 'line' | 'space'
|
||||
compiler?: TemplateCompiler
|
||||
}
|
||||
|
||||
export interface SFCBlock {
|
||||
@ -50,24 +50,32 @@ export interface SFCDescriptor {
|
||||
customBlocks: SFCBlock[]
|
||||
}
|
||||
|
||||
export interface SFCParseResult {
|
||||
descriptor: SFCDescriptor
|
||||
errors: CompilerError[]
|
||||
}
|
||||
|
||||
const SFC_CACHE_MAX_SIZE = 500
|
||||
const sourceToSFC = new LRUCache<string, SFCDescriptor>(SFC_CACHE_MAX_SIZE)
|
||||
const sourceToSFC = new LRUCache<string, SFCParseResult>(SFC_CACHE_MAX_SIZE)
|
||||
|
||||
export function parse(
|
||||
source: string,
|
||||
{
|
||||
sourceMap = true,
|
||||
filename = 'component.vue',
|
||||
sourceRoot = '',
|
||||
pad = false
|
||||
pad = false,
|
||||
compiler = require('@vue/compiler-dom')
|
||||
}: SFCParseOptions = {}
|
||||
): SFCDescriptor {
|
||||
const sourceKey = source + sourceMap + filename + sourceRoot + pad
|
||||
): SFCParseResult {
|
||||
const sourceKey =
|
||||
source + sourceMap + filename + sourceRoot + pad + compiler.parse
|
||||
const cache = sourceToSFC.get(sourceKey)
|
||||
if (cache) {
|
||||
return cache
|
||||
}
|
||||
|
||||
const sfc: SFCDescriptor = {
|
||||
const descriptor: SFCDescriptor = {
|
||||
filename,
|
||||
template: null,
|
||||
script: null,
|
||||
@ -75,9 +83,15 @@ export function parse(
|
||||
customBlocks: []
|
||||
}
|
||||
|
||||
const ast = baseParse(source, {
|
||||
const errors: CompilerError[] = []
|
||||
const ast = compiler.parse(source, {
|
||||
// there are no components at SFC parsing level
|
||||
isNativeTag: () => true,
|
||||
getTextMode: () => TextModes.RAWTEXT
|
||||
// preserve all whitespaces
|
||||
isPreTag: () => true,
|
||||
onError: e => {
|
||||
errors.push(e)
|
||||
}
|
||||
})
|
||||
|
||||
ast.children.forEach(node => {
|
||||
@ -89,24 +103,28 @@ export function parse(
|
||||
}
|
||||
switch (node.tag) {
|
||||
case 'template':
|
||||
if (!sfc.template) {
|
||||
sfc.template = createBlock(node, source, pad) as SFCTemplateBlock
|
||||
if (!descriptor.template) {
|
||||
descriptor.template = createBlock(
|
||||
node,
|
||||
source,
|
||||
pad
|
||||
) as SFCTemplateBlock
|
||||
} else {
|
||||
warnDuplicateBlock(source, filename, node)
|
||||
}
|
||||
break
|
||||
case 'script':
|
||||
if (!sfc.script) {
|
||||
sfc.script = createBlock(node, source, pad) as SFCScriptBlock
|
||||
if (!descriptor.script) {
|
||||
descriptor.script = createBlock(node, source, pad) as SFCScriptBlock
|
||||
} else {
|
||||
warnDuplicateBlock(source, filename, node)
|
||||
}
|
||||
break
|
||||
case 'style':
|
||||
sfc.styles.push(createBlock(node, source, pad) as SFCStyleBlock)
|
||||
descriptor.styles.push(createBlock(node, source, pad) as SFCStyleBlock)
|
||||
break
|
||||
default:
|
||||
sfc.customBlocks.push(createBlock(node, source, pad))
|
||||
descriptor.customBlocks.push(createBlock(node, source, pad))
|
||||
break
|
||||
}
|
||||
})
|
||||
@ -123,13 +141,17 @@ export function parse(
|
||||
)
|
||||
}
|
||||
}
|
||||
genMap(sfc.template)
|
||||
genMap(sfc.script)
|
||||
sfc.styles.forEach(genMap)
|
||||
genMap(descriptor.template)
|
||||
genMap(descriptor.script)
|
||||
descriptor.styles.forEach(genMap)
|
||||
}
|
||||
sourceToSFC.set(sourceKey, sfc)
|
||||
|
||||
return sfc
|
||||
const result = {
|
||||
descriptor,
|
||||
errors
|
||||
}
|
||||
sourceToSFC.set(sourceKey, result)
|
||||
return result
|
||||
}
|
||||
|
||||
function warnDuplicateBlock(
|
||||
@ -156,12 +178,19 @@ function createBlock(
|
||||
pad: SFCParseOptions['pad']
|
||||
): SFCBlock {
|
||||
const type = node.tag
|
||||
const text = node.children[0] as TextNode
|
||||
const start = node.children[0].loc.start
|
||||
const end = node.children[node.children.length - 1].loc.end
|
||||
const content = source.slice(start.offset, end.offset)
|
||||
const loc = {
|
||||
source: content,
|
||||
start,
|
||||
end
|
||||
}
|
||||
const attrs: Record<string, string | true> = {}
|
||||
const block: SFCBlock = {
|
||||
type,
|
||||
content: text.content,
|
||||
loc: text.loc,
|
||||
content,
|
||||
loc,
|
||||
attrs
|
||||
}
|
||||
if (node.tag !== 'template' && pad) {
|
||||
|
Loading…
Reference in New Issue
Block a user