feat(compiler): element transform
This commit is contained in:
parent
93440bba97
commit
baa8954884
@ -1,6 +1,6 @@
|
|||||||
import { parse } from '../../src/parse'
|
import { parse } from '../../src/parse'
|
||||||
import { transform } from '../../src/transform'
|
import { transform } from '../../src/transform'
|
||||||
import { transformFor } from '../../src/directives/vFor'
|
import { transformFor } from '../../src/transforms/vFor'
|
||||||
import { ForNode, NodeTypes } from '../../src/ast'
|
import { ForNode, NodeTypes } from '../../src/ast'
|
||||||
import { ErrorCodes } from '../../src/errors'
|
import { ErrorCodes } from '../../src/errors'
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ describe('v-for', () => {
|
|||||||
test('number expression', () => {
|
test('number expression', () => {
|
||||||
const node = parse('<span v-for="index in 5" />')
|
const node = parse('<span v-for="index in 5" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ describe('v-for', () => {
|
|||||||
test('value', () => {
|
test('value', () => {
|
||||||
const node = parse('<span v-for="(item) in items" />')
|
const node = parse('<span v-for="(item) in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ describe('v-for', () => {
|
|||||||
test('object de-structured value', () => {
|
test('object de-structured value', () => {
|
||||||
const node = parse('<span v-for="({ id, value }) in items" />')
|
const node = parse('<span v-for="({ id, value }) in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ describe('v-for', () => {
|
|||||||
test('array de-structured value', () => {
|
test('array de-structured value', () => {
|
||||||
const node = parse('<span v-for="([ id, value ]) in items" />')
|
const node = parse('<span v-for="([ id, value ]) in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ describe('v-for', () => {
|
|||||||
test('value and key', () => {
|
test('value and key', () => {
|
||||||
const node = parse('<span v-for="(item, key) in items" />')
|
const node = parse('<span v-for="(item, key) in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ describe('v-for', () => {
|
|||||||
test('value, key and index', () => {
|
test('value, key and index', () => {
|
||||||
const node = parse('<span v-for="(value, key, index) in items" />')
|
const node = parse('<span v-for="(value, key, index) in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ describe('v-for', () => {
|
|||||||
test('skipped key', () => {
|
test('skipped key', () => {
|
||||||
const node = parse('<span v-for="(value,,index) in items" />')
|
const node = parse('<span v-for="(value,,index) in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ describe('v-for', () => {
|
|||||||
test('skipped value and key', () => {
|
test('skipped value and key', () => {
|
||||||
const node = parse('<span v-for="(,,index) in items" />')
|
const node = parse('<span v-for="(,,index) in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ describe('v-for', () => {
|
|||||||
test('unbracketed value', () => {
|
test('unbracketed value', () => {
|
||||||
const node = parse('<span v-for="item in items" />')
|
const node = parse('<span v-for="item in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -157,7 +157,7 @@ describe('v-for', () => {
|
|||||||
test('unbracketed value and key', () => {
|
test('unbracketed value and key', () => {
|
||||||
const node = parse('<span v-for="item, key in items" />')
|
const node = parse('<span v-for="item, key in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ describe('v-for', () => {
|
|||||||
test('unbracketed value, key and index', () => {
|
test('unbracketed value, key and index', () => {
|
||||||
const node = parse('<span v-for="value, key, index in items" />')
|
const node = parse('<span v-for="value, key, index in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ describe('v-for', () => {
|
|||||||
test('unbracketed skipped key', () => {
|
test('unbracketed skipped key', () => {
|
||||||
const node = parse('<span v-for="value, , index in items" />')
|
const node = parse('<span v-for="value, , index in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ describe('v-for', () => {
|
|||||||
test('unbracketed skipped value and key', () => {
|
test('unbracketed skipped value and key', () => {
|
||||||
const node = parse('<span v-for=", , index in items" />')
|
const node = parse('<span v-for=", , index in items" />')
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -226,7 +226,7 @@ describe('v-for', () => {
|
|||||||
test('missing expression', () => {
|
test('missing expression', () => {
|
||||||
const node = parse('<span v-for />')
|
const node = parse('<span v-for />')
|
||||||
const onError = jest.fn()
|
const onError = jest.fn()
|
||||||
transform(node, { transforms: [transformFor], onError })
|
transform(node, { nodeTransforms: [transformFor], onError })
|
||||||
|
|
||||||
expect(onError).toHaveBeenCalledTimes(1)
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
expect(onError).toHaveBeenCalledWith(
|
expect(onError).toHaveBeenCalledWith(
|
||||||
@ -239,7 +239,7 @@ describe('v-for', () => {
|
|||||||
test('empty expression', () => {
|
test('empty expression', () => {
|
||||||
const node = parse('<span v-for="" />')
|
const node = parse('<span v-for="" />')
|
||||||
const onError = jest.fn()
|
const onError = jest.fn()
|
||||||
transform(node, { transforms: [transformFor], onError })
|
transform(node, { nodeTransforms: [transformFor], onError })
|
||||||
|
|
||||||
expect(onError).toHaveBeenCalledTimes(1)
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
expect(onError).toHaveBeenCalledWith(
|
expect(onError).toHaveBeenCalledWith(
|
||||||
@ -252,7 +252,7 @@ describe('v-for', () => {
|
|||||||
test('invalid expression', () => {
|
test('invalid expression', () => {
|
||||||
const node = parse('<span v-for="items" />')
|
const node = parse('<span v-for="items" />')
|
||||||
const onError = jest.fn()
|
const onError = jest.fn()
|
||||||
transform(node, { transforms: [transformFor], onError })
|
transform(node, { nodeTransforms: [transformFor], onError })
|
||||||
|
|
||||||
expect(onError).toHaveBeenCalledTimes(1)
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
expect(onError).toHaveBeenCalledWith(
|
expect(onError).toHaveBeenCalledWith(
|
||||||
@ -265,7 +265,7 @@ describe('v-for', () => {
|
|||||||
test('missing source', () => {
|
test('missing source', () => {
|
||||||
const node = parse('<span v-for="item in" />')
|
const node = parse('<span v-for="item in" />')
|
||||||
const onError = jest.fn()
|
const onError = jest.fn()
|
||||||
transform(node, { transforms: [transformFor], onError })
|
transform(node, { nodeTransforms: [transformFor], onError })
|
||||||
|
|
||||||
expect(onError).toHaveBeenCalledTimes(1)
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
expect(onError).toHaveBeenCalledWith(
|
expect(onError).toHaveBeenCalledWith(
|
||||||
@ -278,7 +278,7 @@ describe('v-for', () => {
|
|||||||
test('missing value', () => {
|
test('missing value', () => {
|
||||||
const node = parse('<span v-for="in items" />')
|
const node = parse('<span v-for="in items" />')
|
||||||
const onError = jest.fn()
|
const onError = jest.fn()
|
||||||
transform(node, { transforms: [transformFor], onError })
|
transform(node, { nodeTransforms: [transformFor], onError })
|
||||||
|
|
||||||
expect(onError).toHaveBeenCalledTimes(1)
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
expect(onError).toHaveBeenCalledWith(
|
expect(onError).toHaveBeenCalledWith(
|
||||||
@ -293,7 +293,7 @@ describe('v-for', () => {
|
|||||||
const source = '<span v-for="item in items" />'
|
const source = '<span v-for="item in items" />'
|
||||||
const node = parse(source)
|
const node = parse(source)
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -328,7 +328,7 @@ describe('v-for', () => {
|
|||||||
const source = '<span v-for="( item ) in items" />'
|
const source = '<span v-for="( item ) in items" />'
|
||||||
const node = parse(source)
|
const node = parse(source)
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -363,7 +363,7 @@ describe('v-for', () => {
|
|||||||
const source = '<span v-for="( { id, key })in items" />'
|
const source = '<span v-for="( { id, key })in items" />'
|
||||||
const node = parse(source)
|
const node = parse(source)
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -398,7 +398,7 @@ describe('v-for', () => {
|
|||||||
const source = '<span v-for="( item, key, index ) in items" />'
|
const source = '<span v-for="( item, key, index ) in items" />'
|
||||||
const node = parse(source)
|
const node = parse(source)
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
@ -455,7 +455,7 @@ describe('v-for', () => {
|
|||||||
const source = '<span v-for="( item,, index ) in items" />'
|
const source = '<span v-for="( item,, index ) in items" />'
|
||||||
const node = parse(source)
|
const node = parse(source)
|
||||||
|
|
||||||
transform(node, { transforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { parse } from '../../src/parse'
|
import { parse } from '../../src/parse'
|
||||||
import { transform } from '../../src/transform'
|
import { transform } from '../../src/transform'
|
||||||
import { transformIf } from '../../src/directives/vIf'
|
import { transformIf } from '../../src/transforms/vIf'
|
||||||
import {
|
import {
|
||||||
IfNode,
|
IfNode,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
@ -15,7 +15,7 @@ describe('compiler: v-if', () => {
|
|||||||
test('basic v-if', () => {
|
test('basic v-if', () => {
|
||||||
const ast = parse(`<div v-if="ok"/>`)
|
const ast = parse(`<div v-if="ok"/>`)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [transformIf]
|
nodeTransforms: [transformIf]
|
||||||
})
|
})
|
||||||
const node = ast.children[0] as IfNode
|
const node = ast.children[0] as IfNode
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
@ -29,7 +29,7 @@ describe('compiler: v-if', () => {
|
|||||||
test('template v-if', () => {
|
test('template v-if', () => {
|
||||||
const ast = parse(`<template v-if="ok"><div/>hello<p/></template>`)
|
const ast = parse(`<template v-if="ok"><div/>hello<p/></template>`)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [transformIf]
|
nodeTransforms: [transformIf]
|
||||||
})
|
})
|
||||||
const node = ast.children[0] as IfNode
|
const node = ast.children[0] as IfNode
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
@ -47,7 +47,7 @@ describe('compiler: v-if', () => {
|
|||||||
test('v-if + v-else', () => {
|
test('v-if + v-else', () => {
|
||||||
const ast = parse(`<div v-if="ok"/><p v-else/>`)
|
const ast = parse(`<div v-if="ok"/><p v-else/>`)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [transformIf]
|
nodeTransforms: [transformIf]
|
||||||
})
|
})
|
||||||
// should fold branches
|
// should fold branches
|
||||||
expect(ast.children.length).toBe(1)
|
expect(ast.children.length).toBe(1)
|
||||||
@ -72,7 +72,7 @@ describe('compiler: v-if', () => {
|
|||||||
test('v-if + v-else-if', () => {
|
test('v-if + v-else-if', () => {
|
||||||
const ast = parse(`<div v-if="ok"/><p v-else-if="orNot"/>`)
|
const ast = parse(`<div v-if="ok"/><p v-else-if="orNot"/>`)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [transformIf]
|
nodeTransforms: [transformIf]
|
||||||
})
|
})
|
||||||
// should fold branches
|
// should fold branches
|
||||||
expect(ast.children.length).toBe(1)
|
expect(ast.children.length).toBe(1)
|
||||||
@ -99,7 +99,7 @@ describe('compiler: v-if', () => {
|
|||||||
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
|
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
|
||||||
)
|
)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [transformIf]
|
nodeTransforms: [transformIf]
|
||||||
})
|
})
|
||||||
// should fold branches
|
// should fold branches
|
||||||
expect(ast.children.length).toBe(1)
|
expect(ast.children.length).toBe(1)
|
||||||
@ -136,7 +136,7 @@ describe('compiler: v-if', () => {
|
|||||||
<template v-else>fine</template>
|
<template v-else>fine</template>
|
||||||
`)
|
`)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [transformIf]
|
nodeTransforms: [transformIf]
|
||||||
})
|
})
|
||||||
// should fold branches
|
// should fold branches
|
||||||
expect(ast.children.length).toBe(1)
|
expect(ast.children.length).toBe(1)
|
||||||
@ -172,7 +172,7 @@ describe('compiler: v-if', () => {
|
|||||||
const ast = parse(`<div v-else/>`)
|
const ast = parse(`<div v-else/>`)
|
||||||
const spy = jest.fn()
|
const spy = jest.fn()
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [transformIf],
|
nodeTransforms: [transformIf],
|
||||||
onError: spy
|
onError: spy
|
||||||
})
|
})
|
||||||
expect(spy.mock.calls[0]).toMatchObject([
|
expect(spy.mock.calls[0]).toMatchObject([
|
||||||
@ -185,7 +185,7 @@ describe('compiler: v-if', () => {
|
|||||||
const ast2 = parse(`<div/><div v-else/>`)
|
const ast2 = parse(`<div/><div v-else/>`)
|
||||||
const spy2 = jest.fn()
|
const spy2 = jest.fn()
|
||||||
transform(ast2, {
|
transform(ast2, {
|
||||||
transforms: [transformIf],
|
nodeTransforms: [transformIf],
|
||||||
onError: spy2
|
onError: spy2
|
||||||
})
|
})
|
||||||
expect(spy2.mock.calls[0]).toMatchObject([
|
expect(spy2.mock.calls[0]).toMatchObject([
|
||||||
@ -198,7 +198,7 @@ describe('compiler: v-if', () => {
|
|||||||
const ast3 = parse(`<div/>foo<div v-else/>`)
|
const ast3 = parse(`<div/>foo<div v-else/>`)
|
||||||
const spy3 = jest.fn()
|
const spy3 = jest.fn()
|
||||||
transform(ast3, {
|
transform(ast3, {
|
||||||
transforms: [transformIf],
|
nodeTransforms: [transformIf],
|
||||||
onError: spy3
|
onError: spy3
|
||||||
})
|
})
|
||||||
expect(spy3.mock.calls[0]).toMatchObject([
|
expect(spy3.mock.calls[0]).toMatchObject([
|
||||||
@ -213,7 +213,7 @@ describe('compiler: v-if', () => {
|
|||||||
const ast = parse(`<div v-else-if="foo"/>`)
|
const ast = parse(`<div v-else-if="foo"/>`)
|
||||||
const spy = jest.fn()
|
const spy = jest.fn()
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [transformIf],
|
nodeTransforms: [transformIf],
|
||||||
onError: spy
|
onError: spy
|
||||||
})
|
})
|
||||||
expect(spy.mock.calls[0]).toMatchObject([
|
expect(spy.mock.calls[0]).toMatchObject([
|
||||||
@ -226,7 +226,7 @@ describe('compiler: v-if', () => {
|
|||||||
const ast2 = parse(`<div/><div v-else-if="foo"/>`)
|
const ast2 = parse(`<div/><div v-else-if="foo"/>`)
|
||||||
const spy2 = jest.fn()
|
const spy2 = jest.fn()
|
||||||
transform(ast2, {
|
transform(ast2, {
|
||||||
transforms: [transformIf],
|
nodeTransforms: [transformIf],
|
||||||
onError: spy2
|
onError: spy2
|
||||||
})
|
})
|
||||||
expect(spy2.mock.calls[0]).toMatchObject([
|
expect(spy2.mock.calls[0]).toMatchObject([
|
||||||
@ -239,7 +239,7 @@ describe('compiler: v-if', () => {
|
|||||||
const ast3 = parse(`<div/>foo<div v-else-if="foo"/>`)
|
const ast3 = parse(`<div/>foo<div v-else-if="foo"/>`)
|
||||||
const spy3 = jest.fn()
|
const spy3 = jest.fn()
|
||||||
transform(ast3, {
|
transform(ast3, {
|
||||||
transforms: [transformIf],
|
nodeTransforms: [transformIf],
|
||||||
onError: spy3
|
onError: spy3
|
||||||
})
|
})
|
||||||
expect(spy3.mock.calls[0]).toMatchObject([
|
expect(spy3.mock.calls[0]).toMatchObject([
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { parse } from '../src/parse'
|
import { parse } from '../src/parse'
|
||||||
import { transform, Transform } from '../src/transform'
|
import { transform, NodeTransform } from '../src/transform'
|
||||||
import { ElementNode, NodeTypes } from '../src/ast'
|
import { ElementNode, NodeTypes } from '../src/ast'
|
||||||
import { ErrorCodes, createCompilerError } from '../src/errors'
|
import { ErrorCodes, createCompilerError } from '../src/errors'
|
||||||
|
|
||||||
@ -10,12 +10,12 @@ describe('compiler: transform', () => {
|
|||||||
// manually store call arguments because context is mutable and shared
|
// manually store call arguments because context is mutable and shared
|
||||||
// across calls
|
// across calls
|
||||||
const calls: any[] = []
|
const calls: any[] = []
|
||||||
const plugin: Transform = (node, context) => {
|
const plugin: NodeTransform = (node, context) => {
|
||||||
calls.push([node, Object.assign({}, context)])
|
calls.push([node, Object.assign({}, context)])
|
||||||
}
|
}
|
||||||
|
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [plugin]
|
nodeTransforms: [plugin]
|
||||||
})
|
})
|
||||||
|
|
||||||
const div = ast.children[0] as ElementNode
|
const div = ast.children[0] as ElementNode
|
||||||
@ -48,7 +48,7 @@ describe('compiler: transform', () => {
|
|||||||
|
|
||||||
test('context.replaceNode', () => {
|
test('context.replaceNode', () => {
|
||||||
const ast = parse(`<div/><span/>`)
|
const ast = parse(`<div/><span/>`)
|
||||||
const plugin: Transform = (node, context) => {
|
const plugin: NodeTransform = (node, context) => {
|
||||||
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
||||||
// change the node to <p>
|
// change the node to <p>
|
||||||
context.replaceNode(
|
context.replaceNode(
|
||||||
@ -67,7 +67,7 @@ describe('compiler: transform', () => {
|
|||||||
}
|
}
|
||||||
const spy = jest.fn(plugin)
|
const spy = jest.fn(plugin)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [spy]
|
nodeTransforms: [spy]
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(ast.children.length).toBe(2)
|
expect(ast.children.length).toBe(2)
|
||||||
@ -85,14 +85,14 @@ describe('compiler: transform', () => {
|
|||||||
const c1 = ast.children[0]
|
const c1 = ast.children[0]
|
||||||
const c2 = ast.children[2]
|
const c2 = ast.children[2]
|
||||||
|
|
||||||
const plugin: Transform = (node, context) => {
|
const plugin: NodeTransform = (node, context) => {
|
||||||
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
||||||
context.removeNode()
|
context.removeNode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const spy = jest.fn(plugin)
|
const spy = jest.fn(plugin)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [spy]
|
nodeTransforms: [spy]
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(ast.children.length).toBe(2)
|
expect(ast.children.length).toBe(2)
|
||||||
@ -111,7 +111,7 @@ describe('compiler: transform', () => {
|
|||||||
const c1 = ast.children[0]
|
const c1 = ast.children[0]
|
||||||
const c2 = ast.children[2]
|
const c2 = ast.children[2]
|
||||||
|
|
||||||
const plugin: Transform = (node, context) => {
|
const plugin: NodeTransform = (node, context) => {
|
||||||
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
||||||
context.removeNode()
|
context.removeNode()
|
||||||
// remove previous sibling
|
// remove previous sibling
|
||||||
@ -120,7 +120,7 @@ describe('compiler: transform', () => {
|
|||||||
}
|
}
|
||||||
const spy = jest.fn(plugin)
|
const spy = jest.fn(plugin)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [spy]
|
nodeTransforms: [spy]
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(ast.children.length).toBe(1)
|
expect(ast.children.length).toBe(1)
|
||||||
@ -138,7 +138,7 @@ describe('compiler: transform', () => {
|
|||||||
const c1 = ast.children[0]
|
const c1 = ast.children[0]
|
||||||
const d1 = ast.children[1]
|
const d1 = ast.children[1]
|
||||||
|
|
||||||
const plugin: Transform = (node, context) => {
|
const plugin: NodeTransform = (node, context) => {
|
||||||
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
||||||
context.removeNode()
|
context.removeNode()
|
||||||
// remove next sibling
|
// remove next sibling
|
||||||
@ -147,7 +147,7 @@ describe('compiler: transform', () => {
|
|||||||
}
|
}
|
||||||
const spy = jest.fn(plugin)
|
const spy = jest.fn(plugin)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [spy]
|
nodeTransforms: [spy]
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(ast.children.length).toBe(1)
|
expect(ast.children.length).toBe(1)
|
||||||
@ -163,14 +163,14 @@ describe('compiler: transform', () => {
|
|||||||
test('onError option', () => {
|
test('onError option', () => {
|
||||||
const ast = parse(`<div/>`)
|
const ast = parse(`<div/>`)
|
||||||
const loc = ast.children[0].loc.start
|
const loc = ast.children[0].loc.start
|
||||||
const plugin: Transform = (node, context) => {
|
const plugin: NodeTransform = (node, context) => {
|
||||||
context.emitError(
|
context.onError(
|
||||||
createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc.start)
|
createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc.start)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const spy = jest.fn()
|
const spy = jest.fn()
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
transforms: [plugin],
|
nodeTransforms: [plugin],
|
||||||
onError: spy
|
onError: spy
|
||||||
})
|
})
|
||||||
expect(spy.mock.calls[0]).toMatchObject([
|
expect(spy.mock.calls[0]).toMatchObject([
|
||||||
|
@ -73,8 +73,7 @@ export interface ElementNode extends Node {
|
|||||||
tag: string
|
tag: string
|
||||||
tagType: ElementTypes
|
tagType: ElementTypes
|
||||||
isSelfClosing: boolean
|
isSelfClosing: boolean
|
||||||
attrs: AttributeNode[]
|
props: Array<AttributeNode | DirectiveNode>
|
||||||
directives: DirectiveNode[]
|
|
||||||
children: ChildNode[]
|
children: ChildNode[]
|
||||||
codegenNode: CallExpression | undefined
|
codegenNode: CallExpression | undefined
|
||||||
}
|
}
|
||||||
@ -161,3 +160,64 @@ export interface ArrayExpression extends Node {
|
|||||||
type: NodeTypes.ARRAY_EXPRESSION
|
type: NodeTypes.ARRAY_EXPRESSION
|
||||||
elements: Array<CodegenNode>
|
elements: Array<CodegenNode>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createArrayExpression(
|
||||||
|
elements: ArrayExpression['elements'],
|
||||||
|
loc: SourceLocation
|
||||||
|
): ArrayExpression {
|
||||||
|
return {
|
||||||
|
type: NodeTypes.ARRAY_EXPRESSION,
|
||||||
|
loc,
|
||||||
|
elements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createObjectExpression(
|
||||||
|
properties: Property[],
|
||||||
|
loc: SourceLocation
|
||||||
|
): ObjectExpression {
|
||||||
|
return {
|
||||||
|
type: NodeTypes.OBJECT_EXPRESSION,
|
||||||
|
loc,
|
||||||
|
properties
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createObjectProperty(
|
||||||
|
key: ExpressionNode,
|
||||||
|
value: ExpressionNode,
|
||||||
|
loc: SourceLocation
|
||||||
|
): Property {
|
||||||
|
return {
|
||||||
|
type: NodeTypes.PROPERTY,
|
||||||
|
loc,
|
||||||
|
key,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createExpression(
|
||||||
|
content: string,
|
||||||
|
isStatic: boolean,
|
||||||
|
loc: SourceLocation
|
||||||
|
): ExpressionNode {
|
||||||
|
return {
|
||||||
|
type: NodeTypes.EXPRESSION,
|
||||||
|
loc,
|
||||||
|
content,
|
||||||
|
isStatic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCallExpression(
|
||||||
|
callee: string,
|
||||||
|
args: CallExpression['arguments'],
|
||||||
|
loc: SourceLocation
|
||||||
|
): CallExpression {
|
||||||
|
return {
|
||||||
|
type: NodeTypes.CALL_EXPRESSION,
|
||||||
|
loc,
|
||||||
|
callee,
|
||||||
|
arguments: args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -65,7 +65,8 @@ export const enum ErrorCodes {
|
|||||||
X_ELSE_IF_NO_ADJACENT_IF,
|
X_ELSE_IF_NO_ADJACENT_IF,
|
||||||
X_ELSE_NO_ADJACENT_IF,
|
X_ELSE_NO_ADJACENT_IF,
|
||||||
X_FOR_NO_EXPRESSION,
|
X_FOR_NO_EXPRESSION,
|
||||||
X_FOR_MALFORMED_EXPRESSION
|
X_FOR_MALFORMED_EXPRESSION,
|
||||||
|
X_V_BIND_NO_EXPRESSION
|
||||||
}
|
}
|
||||||
|
|
||||||
export const errorMessages: { [code: number]: string } = {
|
export const errorMessages: { [code: number]: string } = {
|
||||||
|
@ -14,10 +14,14 @@ export function compile(
|
|||||||
|
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
...options,
|
...options,
|
||||||
transforms: [
|
nodeTransforms: [
|
||||||
// TODO include built-in core transforms
|
// TODO include built-in core transforms
|
||||||
...(options.transforms || []) // user transforms
|
...(options.nodeTransforms || []) // user transforms
|
||||||
]
|
],
|
||||||
|
directiveTransforms: {
|
||||||
|
// TODO include built-in directive transforms
|
||||||
|
...(options.directiveTransforms || {}) // user transforms
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return generate(ast, options)
|
return generate(ast, options)
|
||||||
@ -27,11 +31,11 @@ export function compile(
|
|||||||
export { parse, ParserOptions, TextModes } from './parse'
|
export { parse, ParserOptions, TextModes } from './parse'
|
||||||
export {
|
export {
|
||||||
transform,
|
transform,
|
||||||
createDirectiveTransform,
|
createStructuralDirectiveTransform,
|
||||||
TransformOptions,
|
TransformOptions,
|
||||||
TransformContext,
|
TransformContext,
|
||||||
Transform,
|
NodeTransform as Transform,
|
||||||
DirectiveTransform
|
StructuralDirectiveTransform
|
||||||
} from './transform'
|
} from './transform'
|
||||||
export {
|
export {
|
||||||
generate,
|
generate,
|
||||||
@ -41,3 +45,6 @@ export {
|
|||||||
} from './codegen'
|
} from './codegen'
|
||||||
export { ErrorCodes, CompilerError, createCompilerError } from './errors'
|
export { ErrorCodes, CompilerError, createCompilerError } from './errors'
|
||||||
export * from './ast'
|
export * from './ast'
|
||||||
|
|
||||||
|
// debug
|
||||||
|
export { prepareElementForCodegen } from './transforms/element'
|
||||||
|
@ -376,8 +376,7 @@ function parseTag(
|
|||||||
const start = getCursor(context)
|
const start = getCursor(context)
|
||||||
const match = /^<\/?([a-z][^\t\r\n\f />]*)/i.exec(context.source)!
|
const match = /^<\/?([a-z][^\t\r\n\f />]*)/i.exec(context.source)!
|
||||||
const tag = match[1]
|
const tag = match[1]
|
||||||
const attrs = []
|
const props = []
|
||||||
const directives = []
|
|
||||||
const ns = context.options.getNamespace(tag, parent)
|
const ns = context.options.getNamespace(tag, parent)
|
||||||
|
|
||||||
advanceBy(context, match[0].length)
|
advanceBy(context, match[0].length)
|
||||||
@ -402,11 +401,7 @@ function parseTag(
|
|||||||
|
|
||||||
const attr = parseAttribute(context, attributeNames)
|
const attr = parseAttribute(context, attributeNames)
|
||||||
if (type === TagType.Start) {
|
if (type === TagType.Start) {
|
||||||
if (attr.type === NodeTypes.DIRECTIVE) {
|
props.push(attr)
|
||||||
directives.push(attr)
|
|
||||||
} else {
|
|
||||||
attrs.push(attr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/^[^\t\r\n\f />]/.test(context.source)) {
|
if (/^[^\t\r\n\f />]/.test(context.source)) {
|
||||||
@ -438,11 +433,11 @@ function parseTag(
|
|||||||
ns,
|
ns,
|
||||||
tag,
|
tag,
|
||||||
tagType,
|
tagType,
|
||||||
attrs,
|
props,
|
||||||
directives,
|
|
||||||
isSelfClosing,
|
isSelfClosing,
|
||||||
children: [],
|
children: [],
|
||||||
loc: getSelection(context, start)
|
loc: getSelection(context, start),
|
||||||
|
codegenNode: undefined // to be created during transform phase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,27 +4,45 @@ import {
|
|||||||
ParentNode,
|
ParentNode,
|
||||||
ChildNode,
|
ChildNode,
|
||||||
ElementNode,
|
ElementNode,
|
||||||
DirectiveNode
|
DirectiveNode,
|
||||||
|
Property
|
||||||
} from './ast'
|
} from './ast'
|
||||||
import { isString } from '@vue/shared'
|
import { isString } from '@vue/shared'
|
||||||
import { CompilerError, defaultOnError } from './errors'
|
import { CompilerError, defaultOnError } from './errors'
|
||||||
|
|
||||||
export type Transform = (node: ChildNode, context: TransformContext) => void
|
// There are two types of transforms:
|
||||||
|
//
|
||||||
|
// - NodeTransform:
|
||||||
|
// Transforms that operate directly on a ChildNode. NodeTransforms may mutate,
|
||||||
|
// replace or remove the node being processed.
|
||||||
|
export type NodeTransform = (node: ChildNode, context: TransformContext) => void
|
||||||
|
|
||||||
|
// - DirectiveTransform:
|
||||||
|
// Transforms that handles a single directive attribute on an element.
|
||||||
|
// It translates the raw directive into actual props for the VNode.
|
||||||
export type DirectiveTransform = (
|
export type DirectiveTransform = (
|
||||||
|
dir: DirectiveNode,
|
||||||
|
context: TransformContext
|
||||||
|
) => {
|
||||||
|
props: Property | Property[]
|
||||||
|
needRuntime: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// A structural directive transform is a techically a NodeTransform;
|
||||||
|
// Only v-if and v-for fall into this category.
|
||||||
|
export type StructuralDirectiveTransform = (
|
||||||
node: ElementNode,
|
node: ElementNode,
|
||||||
dir: DirectiveNode,
|
dir: DirectiveNode,
|
||||||
context: TransformContext
|
context: TransformContext
|
||||||
) => false | void
|
) => void
|
||||||
|
|
||||||
export interface TransformOptions {
|
export interface TransformOptions {
|
||||||
transforms?: Transform[]
|
nodeTransforms?: NodeTransform[]
|
||||||
|
directiveTransforms?: { [name: string]: DirectiveTransform }
|
||||||
onError?: (error: CompilerError) => void
|
onError?: (error: CompilerError) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransformContext {
|
export interface TransformContext extends Required<TransformOptions> {
|
||||||
transforms: Transform[]
|
|
||||||
emitError: (error: CompilerError) => void
|
|
||||||
parent: ParentNode
|
parent: ParentNode
|
||||||
ancestors: ParentNode[]
|
ancestors: ParentNode[]
|
||||||
childIndex: number
|
childIndex: number
|
||||||
@ -44,8 +62,9 @@ function createTransformContext(
|
|||||||
options: TransformOptions
|
options: TransformOptions
|
||||||
): TransformContext {
|
): TransformContext {
|
||||||
const context: TransformContext = {
|
const context: TransformContext = {
|
||||||
transforms: options.transforms || [],
|
nodeTransforms: options.nodeTransforms || [],
|
||||||
emitError: options.onError || defaultOnError,
|
directiveTransforms: options.directiveTransforms || {},
|
||||||
|
onError: options.onError || defaultOnError,
|
||||||
parent: root,
|
parent: root,
|
||||||
ancestors: [],
|
ancestors: [],
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
@ -109,9 +128,9 @@ function traverseNode(
|
|||||||
ancestors: ParentNode[]
|
ancestors: ParentNode[]
|
||||||
) {
|
) {
|
||||||
// apply transform plugins
|
// apply transform plugins
|
||||||
const { transforms } = context
|
const { nodeTransforms } = context
|
||||||
for (let i = 0; i < transforms.length; i++) {
|
for (let i = 0; i < nodeTransforms.length; i++) {
|
||||||
const plugin = transforms[i]
|
const plugin = nodeTransforms[i]
|
||||||
plugin(node, context)
|
plugin(node, context)
|
||||||
if (!context.currentNode) {
|
if (!context.currentNode) {
|
||||||
return
|
return
|
||||||
@ -135,33 +154,26 @@ function traverseNode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const identity = <T>(_: T): T => _
|
export function createStructuralDirectiveTransform(
|
||||||
|
|
||||||
export function createDirectiveTransform(
|
|
||||||
name: string | RegExp,
|
name: string | RegExp,
|
||||||
fn: DirectiveTransform
|
fn: StructuralDirectiveTransform
|
||||||
): Transform {
|
): NodeTransform {
|
||||||
const matches = isString(name)
|
const matches = isString(name)
|
||||||
? (n: string) => n === name
|
? (n: string) => n === name
|
||||||
: (n: string) => name.test(n)
|
: (n: string) => name.test(n)
|
||||||
|
|
||||||
return (node, context) => {
|
return (node, context) => {
|
||||||
if (node.type === NodeTypes.ELEMENT) {
|
if (node.type === NodeTypes.ELEMENT) {
|
||||||
const dirs = node.directives
|
const { props } = node
|
||||||
let didRemove = false
|
for (let i = 0; i < props.length; i++) {
|
||||||
for (let i = 0; i < dirs.length; i++) {
|
const prop = props[i]
|
||||||
if (matches(dirs[i].name)) {
|
if (prop.type === NodeTypes.DIRECTIVE && matches(prop.name)) {
|
||||||
const res = fn(node, dirs[i], context)
|
fn(node, prop, context)
|
||||||
// Directives are removed after transformation by default. A transform
|
// structural directives are removed after being processed
|
||||||
// returning false means the directive should not be removed.
|
// to avoid infinite recursion
|
||||||
if (res !== false) {
|
props.splice(i, 1)
|
||||||
;(dirs as any)[i] = undefined
|
i--
|
||||||
didRemove = true
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (didRemove) {
|
|
||||||
node.directives = dirs.filter(identity)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,43 @@
|
|||||||
import { Transform, TransformContext } from '../transform'
|
import { NodeTransform, TransformContext } from '../transform'
|
||||||
import {
|
import {
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
ElementTypes,
|
ElementTypes,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
ObjectExpression,
|
ObjectExpression,
|
||||||
ElementNode
|
ElementNode,
|
||||||
|
DirectiveNode,
|
||||||
|
ExpressionNode,
|
||||||
|
ArrayExpression,
|
||||||
|
createCallExpression,
|
||||||
|
createArrayExpression,
|
||||||
|
createObjectProperty,
|
||||||
|
createExpression,
|
||||||
|
createObjectExpression
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
|
import { isArray } from '@vue/shared'
|
||||||
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
|
|
||||||
// generate a JavaScript AST for this element's codegen
|
// generate a JavaScript AST for this element's codegen
|
||||||
export const prepareElementForCodegen: Transform = (node, context) => {
|
export const prepareElementForCodegen: NodeTransform = (node, context) => {
|
||||||
if (node.type === NodeTypes.ELEMENT) {
|
if (node.type === NodeTypes.ELEMENT) {
|
||||||
if (
|
if (
|
||||||
node.tagType === ElementTypes.ELEMENT ||
|
node.tagType === ElementTypes.ELEMENT ||
|
||||||
node.tagType === ElementTypes.COMPONENT
|
node.tagType === ElementTypes.COMPONENT
|
||||||
) {
|
) {
|
||||||
const isComponent = node.tagType === ElementTypes.ELEMENT
|
const isComponent = node.tagType === ElementTypes.ELEMENT
|
||||||
const hasProps = node.attrs.length > 0 || node.directives.length > 0
|
const hasProps = node.props.length > 0
|
||||||
const hasChildren = node.children.length > 0
|
const hasChildren = node.children.length > 0
|
||||||
|
let runtimeDirectives: DirectiveNode[] | undefined
|
||||||
|
|
||||||
const args: CallExpression['arguments'] = [
|
const args: CallExpression['arguments'] = [
|
||||||
|
// TODO inject resolveComponent dep to root
|
||||||
isComponent ? node.tag : `"${node.tag}"`
|
isComponent ? node.tag : `"${node.tag}"`
|
||||||
]
|
]
|
||||||
// props
|
// props
|
||||||
if (hasProps) {
|
if (hasProps) {
|
||||||
args.push(buildProps(node))
|
const { props, directives } = buildProps(node, context)
|
||||||
|
args.push(props)
|
||||||
|
runtimeDirectives = directives
|
||||||
}
|
}
|
||||||
// children
|
// children
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
@ -34,53 +48,155 @@ export const prepareElementForCodegen: Transform = (node, context) => {
|
|||||||
args.push(isComponent ? buildSlots(node, context) : node.children)
|
args.push(isComponent ? buildSlots(node, context) : node.children)
|
||||||
}
|
}
|
||||||
|
|
||||||
node.codegenNode = {
|
const { loc } = node
|
||||||
type: NodeTypes.CALL_EXPRESSION,
|
const vnode = createCallExpression(`h`, args, loc)
|
||||||
loc: node.loc,
|
|
||||||
callee: `h`,
|
if (runtimeDirectives) {
|
||||||
arguments: args
|
node.codegenNode = createCallExpression(
|
||||||
|
`applyDirectives`,
|
||||||
|
[
|
||||||
|
vnode,
|
||||||
|
createArrayExpression(
|
||||||
|
runtimeDirectives.map(dir => {
|
||||||
|
return createDirectiveArgs(dir, context)
|
||||||
|
}),
|
||||||
|
loc
|
||||||
|
)
|
||||||
|
],
|
||||||
|
loc
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
node.codegenNode = vnode
|
||||||
}
|
}
|
||||||
|
} else if (node.tagType === ElementTypes.SLOT) {
|
||||||
|
// <slot [name="xxx"]/>
|
||||||
|
// TODO
|
||||||
|
} else if (node.tagType === ElementTypes.TEMPLATE) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildProps({ loc, attrs }: ElementNode): ObjectExpression {
|
function buildProps(
|
||||||
|
{ loc, props }: ElementNode,
|
||||||
|
context: TransformContext
|
||||||
|
): {
|
||||||
|
props: ObjectExpression | CallExpression
|
||||||
|
directives: DirectiveNode[]
|
||||||
|
} {
|
||||||
|
let properties: ObjectExpression['properties'] = []
|
||||||
|
const mergeArgs: Array<ObjectExpression | ExpressionNode> = []
|
||||||
|
const runtimeDirectives: DirectiveNode[] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < props.length; i++) {
|
||||||
|
// static attribute
|
||||||
|
const prop = props[i]
|
||||||
|
if (prop.type === NodeTypes.ATTRIBUTE) {
|
||||||
|
const { loc, name, value } = prop
|
||||||
|
properties.push(
|
||||||
|
createObjectProperty(
|
||||||
|
createExpression(name, true, loc),
|
||||||
|
createExpression(
|
||||||
|
value ? value.content : '',
|
||||||
|
true,
|
||||||
|
value ? value.loc : loc
|
||||||
|
),
|
||||||
|
loc
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// directives
|
||||||
|
// special case for v-bind with no argument
|
||||||
|
if (prop.name === 'bind' && !prop.arg) {
|
||||||
|
if (prop.exp) {
|
||||||
|
if (properties.length) {
|
||||||
|
mergeArgs.push(createObjectExpression(properties, loc))
|
||||||
|
properties = []
|
||||||
|
}
|
||||||
|
mergeArgs.push(prop.exp)
|
||||||
|
} else {
|
||||||
|
context.onError(
|
||||||
|
createCompilerError(
|
||||||
|
ErrorCodes.X_V_BIND_NO_EXPRESSION,
|
||||||
|
prop.loc.start
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const directiveTransform = context.directiveTransforms[prop.name]
|
||||||
|
if (directiveTransform) {
|
||||||
|
const { props, needRuntime } = directiveTransform(prop, context)
|
||||||
|
if (isArray(props)) {
|
||||||
|
properties.push(...props)
|
||||||
|
} else {
|
||||||
|
properties.push(props)
|
||||||
|
}
|
||||||
|
if (needRuntime) {
|
||||||
|
runtimeDirectives.push(prop)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no built-in transform, this is a user custom directive.
|
||||||
|
runtimeDirectives.push(prop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret: ObjectExpression | CallExpression
|
||||||
|
|
||||||
|
// has v-bind="object", wrap with mergeProps
|
||||||
|
if (mergeArgs.length) {
|
||||||
|
if (properties.length) {
|
||||||
|
mergeArgs.push(createObjectExpression(properties, loc))
|
||||||
|
}
|
||||||
|
if (mergeArgs.length > 1) {
|
||||||
|
ret = createCallExpression(`mergeProps`, mergeArgs, loc)
|
||||||
|
} else {
|
||||||
|
// single v-bind with nothing else - no need for a mergeProps call
|
||||||
|
ret = createObjectExpression(properties, loc)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = createObjectExpression(properties, loc)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: NodeTypes.OBJECT_EXPRESSION,
|
props: ret,
|
||||||
loc,
|
directives: runtimeDirectives
|
||||||
// At this stage we will only process static attrs. Directive bindings will
|
|
||||||
// be handled by their respective transforms which adds/modifies the props.
|
|
||||||
properties: attrs.map(({ name, value, loc }) => {
|
|
||||||
return {
|
|
||||||
type: NodeTypes.PROPERTY,
|
|
||||||
loc,
|
|
||||||
key: {
|
|
||||||
type: NodeTypes.EXPRESSION,
|
|
||||||
loc,
|
|
||||||
content: name,
|
|
||||||
isStatic: true
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: NodeTypes.EXPRESSION,
|
|
||||||
loc: value ? value.loc : loc,
|
|
||||||
content: value ? value.content : '',
|
|
||||||
isStatic: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
function createDirectiveArgs(
|
||||||
|
dir: DirectiveNode,
|
||||||
|
context: TransformContext
|
||||||
|
): ArrayExpression {
|
||||||
|
// TODO inject resolveDirective dep to root
|
||||||
|
const dirArgs: ArrayExpression['elements'] = [dir.name]
|
||||||
|
const { loc } = dir
|
||||||
|
if (dir.exp) dirArgs.push(dir.exp)
|
||||||
|
if (dir.arg) dirArgs.push(dir.arg)
|
||||||
|
if (Object.keys(dir.modifiers).length) {
|
||||||
|
dirArgs.push(
|
||||||
|
createObjectExpression(
|
||||||
|
dir.modifiers.map(modifier =>
|
||||||
|
createObjectProperty(
|
||||||
|
createExpression(modifier, true, loc),
|
||||||
|
createExpression(`true`, false, loc),
|
||||||
|
loc
|
||||||
|
)
|
||||||
|
),
|
||||||
|
loc
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
return createArrayExpression(dirArgs, dir.loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSlots(
|
function buildSlots(
|
||||||
{ loc, children }: ElementNode,
|
{ loc, children }: ElementNode,
|
||||||
context: TransformContext
|
context: TransformContext
|
||||||
): ObjectExpression {
|
): ObjectExpression {
|
||||||
const slots: ObjectExpression = {
|
const slots = createObjectExpression([], loc)
|
||||||
type: NodeTypes.OBJECT_EXPRESSION,
|
|
||||||
loc,
|
|
||||||
properties: []
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
return slots
|
return slots
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createDirectiveTransform } from '../transform'
|
import { createStructuralDirectiveTransform } from '../transform'
|
||||||
import { NodeTypes, ExpressionNode } from '../ast'
|
import { NodeTypes, ExpressionNode, createExpression } from '../ast'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import { getInnerRange } from '../utils'
|
import { getInnerRange } from '../utils'
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ const forAliasRE = /([\s\S]*?)(?:(?<=\))|\s+)(?:in|of)\s+([\s\S]*)/
|
|||||||
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
|
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
|
||||||
const stripParensRE = /^\(|\)$/g
|
const stripParensRE = /^\(|\)$/g
|
||||||
|
|
||||||
export const transformFor = createDirectiveTransform(
|
export const transformFor = createStructuralDirectiveTransform(
|
||||||
'for',
|
'for',
|
||||||
(node, dir, context) => {
|
(node, dir, context) => {
|
||||||
if (dir.exp) {
|
if (dir.exp) {
|
||||||
@ -27,7 +27,7 @@ export const transformFor = createDirectiveTransform(
|
|||||||
children: [node]
|
children: [node]
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
context.emitError(
|
context.onError(
|
||||||
createCompilerError(
|
createCompilerError(
|
||||||
ErrorCodes.X_FOR_MALFORMED_EXPRESSION,
|
ErrorCodes.X_FOR_MALFORMED_EXPRESSION,
|
||||||
dir.loc.start
|
dir.loc.start
|
||||||
@ -35,7 +35,7 @@ export const transformFor = createDirectiveTransform(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.emitError(
|
context.onError(
|
||||||
createCompilerError(ErrorCodes.X_FOR_NO_EXPRESSION, dir.loc.start)
|
createCompilerError(ErrorCodes.X_FOR_NO_EXPRESSION, dir.loc.start)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -118,11 +118,10 @@ function maybeCreateExpression(
|
|||||||
node: ExpressionNode
|
node: ExpressionNode
|
||||||
): ExpressionNode | undefined {
|
): ExpressionNode | undefined {
|
||||||
if (alias) {
|
if (alias) {
|
||||||
return {
|
return createExpression(
|
||||||
type: NodeTypes.EXPRESSION,
|
alias.content,
|
||||||
loc: getInnerRange(node.loc, alias.offset, alias.content.length),
|
false,
|
||||||
content: alias.content,
|
getInnerRange(node.loc, alias.offset, alias.content.length)
|
||||||
isStatic: false
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { createDirectiveTransform } from '../transform'
|
import { createStructuralDirectiveTransform } from '../transform'
|
||||||
import {
|
import {
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
ElementTypes,
|
ElementTypes,
|
||||||
@ -8,7 +8,7 @@ import {
|
|||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
|
|
||||||
export const transformIf = createDirectiveTransform(
|
export const transformIf = createStructuralDirectiveTransform(
|
||||||
/^(if|else|else-if)$/,
|
/^(if|else|else-if)$/,
|
||||||
(node, dir, context) => {
|
(node, dir, context) => {
|
||||||
if (dir.name === 'if') {
|
if (dir.name === 'if') {
|
||||||
@ -38,7 +38,7 @@ export const transformIf = createDirectiveTransform(
|
|||||||
}
|
}
|
||||||
sibling.branches.push(branch)
|
sibling.branches.push(branch)
|
||||||
} else {
|
} else {
|
||||||
context.emitError(
|
context.onError(
|
||||||
createCompilerError(
|
createCompilerError(
|
||||||
dir.name === 'else'
|
dir.name === 'else'
|
||||||
? ErrorCodes.X_ELSE_NO_ADJACENT_IF
|
? ErrorCodes.X_ELSE_NO_ADJACENT_IF
|
||||||
|
@ -1 +0,0 @@
|
|||||||
// TODO
|
|
@ -6,6 +6,8 @@ import {
|
|||||||
import { parserOptionsMinimal } from './parserOptionsMinimal'
|
import { parserOptionsMinimal } from './parserOptionsMinimal'
|
||||||
import { parserOptionsStandard } from './parserOptionsStandard'
|
import { parserOptionsStandard } from './parserOptionsStandard'
|
||||||
|
|
||||||
|
export * from '@vue/compiler-core'
|
||||||
|
|
||||||
export function compile(
|
export function compile(
|
||||||
template: string,
|
template: string,
|
||||||
options: CompilerOptions = {}
|
options: CompilerOptions = {}
|
||||||
@ -13,11 +15,9 @@ export function compile(
|
|||||||
return baseCompile(template, {
|
return baseCompile(template, {
|
||||||
...options,
|
...options,
|
||||||
...(__BROWSER__ ? parserOptionsMinimal : parserOptionsStandard),
|
...(__BROWSER__ ? parserOptionsMinimal : parserOptionsStandard),
|
||||||
transforms: [
|
directiveTransforms: {
|
||||||
// TODO include DOM-specific transforms
|
// TODO include DOM-specific directiveTransforms
|
||||||
...(options.transforms || []) // extra user transforms
|
...(options.directiveTransforms || {})
|
||||||
]
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from '@vue/compiler-core'
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user