Просмотр исходного кода

test(compiler): tests for ast transform

Evan You 6 лет назад
Родитель
Сommit
5f49018601

+ 1 - 1
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')

+ 128 - 0
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(`<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
+      }
+    ])
+  })
+})

+ 6 - 3
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 {