test(compiler): tests for ast transform
This commit is contained in:
parent
10c1a2b332
commit
5f49018601
@ -12,7 +12,7 @@ import {
|
|||||||
AttributeNode
|
AttributeNode
|
||||||
} from '../src/ast'
|
} from '../src/ast'
|
||||||
|
|
||||||
describe('base parser', () => {
|
describe('compiler: parse', () => {
|
||||||
describe('Text', () => {
|
describe('Text', () => {
|
||||||
test('simple text', () => {
|
test('simple text', () => {
|
||||||
const ast = parse('some text')
|
const ast = parse('some text')
|
||||||
|
128
packages/compiler-core/__tests__/transform.spec.ts
Normal file
128
packages/compiler-core/__tests__/transform.spec.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import { parse } from '../src/parse'
|
||||||
|
import { transform, Transform } from '../src/transform'
|
||||||
|
import { ElementNode, NodeTypes } from '../src/ast'
|
||||||
|
import { ErrorCodes, createCompilerError } from '../src/errors'
|
||||||
|
|
||||||
|
describe('compiler: transform', () => {
|
||||||
|
test('context state', () => {
|
||||||
|
const ast = parse(`<div>hello {{ world }}</div>`)
|
||||||
|
|
||||||
|
// manually store call arguments because context is mutable and shared
|
||||||
|
// across calls
|
||||||
|
const calls: any[] = []
|
||||||
|
const plugin: Transform = (node, context) => {
|
||||||
|
calls.push([node, Object.assign({}, context)])
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(ast, {
|
||||||
|
transforms: [plugin]
|
||||||
|
})
|
||||||
|
|
||||||
|
const div = ast.children[0] as ElementNode
|
||||||
|
expect(calls.length).toBe(3)
|
||||||
|
expect(calls[0]).toMatchObject([
|
||||||
|
div,
|
||||||
|
{
|
||||||
|
parent: ast,
|
||||||
|
ancestors: [ast],
|
||||||
|
childIndex: 0
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(calls[1]).toMatchObject([
|
||||||
|
div.children[0],
|
||||||
|
{
|
||||||
|
parent: div,
|
||||||
|
ancestors: [ast, div],
|
||||||
|
childIndex: 0
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(calls[2]).toMatchObject([
|
||||||
|
div.children[1],
|
||||||
|
{
|
||||||
|
parent: div,
|
||||||
|
ancestors: [ast, div],
|
||||||
|
childIndex: 1
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('context.replaceNode', () => {
|
||||||
|
const ast = parse(`<div/><span/>`)
|
||||||
|
const plugin: Transform = (node, context) => {
|
||||||
|
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
||||||
|
// change the node to <p>
|
||||||
|
context.replaceNode(
|
||||||
|
Object.assign({}, node, {
|
||||||
|
tag: 'p',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.TEXT,
|
||||||
|
content: 'hello',
|
||||||
|
isEmpty: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const spy = jest.fn(plugin)
|
||||||
|
transform(ast, {
|
||||||
|
transforms: [spy]
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(ast.children.length).toBe(2)
|
||||||
|
const newElement = ast.children[0] as ElementNode
|
||||||
|
expect(newElement.tag).toBe('p')
|
||||||
|
expect(spy).toHaveBeenCalledTimes(3)
|
||||||
|
// should traverse the children of replaced node
|
||||||
|
expect(spy.mock.calls[1][0]).toBe(newElement.children[0])
|
||||||
|
// should traverse the node after the replaced node
|
||||||
|
expect(spy.mock.calls[2][0]).toBe(ast.children[1])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('context.removeNode', () => {
|
||||||
|
const ast = parse(`<span/><div/><span/>`)
|
||||||
|
const c1 = ast.children[0]
|
||||||
|
const c2 = ast.children[2]
|
||||||
|
|
||||||
|
const plugin: Transform = (node, context) => {
|
||||||
|
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
||||||
|
context.removeNode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const spy = jest.fn(plugin)
|
||||||
|
transform(ast, {
|
||||||
|
transforms: [spy]
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(ast.children.length).toBe(2)
|
||||||
|
expect(ast.children[0]).toBe(c1)
|
||||||
|
expect(ast.children[1]).toBe(c2)
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledTimes(3)
|
||||||
|
// should traverse nodes around removed
|
||||||
|
expect(spy.mock.calls[0][0]).toBe(c1)
|
||||||
|
expect(spy.mock.calls[2][0]).toBe(c2)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('onError option', () => {
|
||||||
|
const ast = parse(`<div/>`)
|
||||||
|
const loc = ast.children[0].loc.start
|
||||||
|
const plugin: Transform = (node, context) => {
|
||||||
|
context.onError(
|
||||||
|
createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc.start)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const spy = jest.fn()
|
||||||
|
transform(ast, {
|
||||||
|
transforms: [plugin],
|
||||||
|
onError: spy
|
||||||
|
})
|
||||||
|
expect(spy.mock.calls[0]).toMatchObject([
|
||||||
|
{
|
||||||
|
code: ErrorCodes.X_INVALID_END_TAG,
|
||||||
|
loc
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
@ -46,9 +46,12 @@ function createTransformContext(
|
|||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
parent: root,
|
parent: root,
|
||||||
ancestors: [root],
|
ancestors: [],
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
replaceNode(node) {
|
replaceNode(node) {
|
||||||
|
if (__DEV__ && context.nodeRemoved) {
|
||||||
|
throw new Error(`node being replaced is already removed`)
|
||||||
|
}
|
||||||
context.parent.children[context.childIndex] = node
|
context.parent.children[context.childIndex] = node
|
||||||
},
|
},
|
||||||
removeNode() {
|
removeNode() {
|
||||||
@ -85,9 +88,9 @@ function traverseNode(
|
|||||||
// apply transform plugins
|
// apply transform plugins
|
||||||
const transforms = context.transforms
|
const transforms = context.transforms
|
||||||
for (let i = 0; i < transforms.length; i++) {
|
for (let i = 0; i < transforms.length; i++) {
|
||||||
const transform = transforms[i]
|
const plugin = transforms[i]
|
||||||
context.nodeRemoved = false
|
context.nodeRemoved = false
|
||||||
transform(node, context)
|
plugin(node, context)
|
||||||
if (context.nodeRemoved) {
|
if (context.nodeRemoved) {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user