diff --git a/packages/compiler-core/__tests__/parse.spec.ts b/packages/compiler-core/__tests__/parse.spec.ts index 348dbd9e4..d8945f9de 100644 --- a/packages/compiler-core/__tests__/parse.spec.ts +++ b/packages/compiler-core/__tests__/parse.spec.ts @@ -12,7 +12,7 @@ import { AttributeNode } from '../src/ast' -describe('base parser', () => { +describe('compiler: parse', () => { describe('Text', () => { test('simple text', () => { const ast = parse('some text') diff --git a/packages/compiler-core/__tests__/transform.spec.ts b/packages/compiler-core/__tests__/transform.spec.ts new file mode 100644 index 000000000..2875e0956 --- /dev/null +++ b/packages/compiler-core/__tests__/transform.spec.ts @@ -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(`
+ 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(`
`) + 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(``) + 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 + } + ]) + }) +}) diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index 8ef56927f..c7615e391 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -46,9 +46,12 @@ function createTransformContext( }, ...options, parent: root, - ancestors: [root], + ancestors: [], childIndex: 0, replaceNode(node) { + if (__DEV__ && context.nodeRemoved) { + throw new Error(`node being replaced is already removed`) + } context.parent.children[context.childIndex] = node }, removeNode() { @@ -85,9 +88,9 @@ function traverseNode( // apply transform plugins const transforms = context.transforms for (let i = 0; i < transforms.length; i++) { - const transform = transforms[i] + const plugin = transforms[i] context.nodeRemoved = false - transform(node, context) + plugin(node, context) if (context.nodeRemoved) { return } else {