feat(compiler): basic v-bind & v-on transforms
This commit is contained in:
parent
3ab016e44f
commit
914087edea
@ -195,7 +195,9 @@ describe('compiler: parse', () => {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
code: ErrorCodes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
|
code: ErrorCodes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
|
||||||
loc: { offset: 4, line: 1, column: 5 }
|
loc: {
|
||||||
|
start: { offset: 4, line: 1, column: 5 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
@ -249,7 +251,9 @@ describe('compiler: parse', () => {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
code: ErrorCodes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
|
code: ErrorCodes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
|
||||||
loc: { offset: 45, line: 1, column: 46 }
|
loc: {
|
||||||
|
start: { offset: 45, line: 1, column: 46 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
@ -274,7 +278,9 @@ describe('compiler: parse', () => {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
code: ErrorCodes.CONTROL_CHARACTER_REFERENCE,
|
code: ErrorCodes.CONTROL_CHARACTER_REFERENCE,
|
||||||
loc: { offset: 0, line: 1, column: 1 }
|
loc: {
|
||||||
|
start: { offset: 0, line: 1, column: 1 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
@ -1254,9 +1260,11 @@ describe('compiler: parse', () => {
|
|||||||
{
|
{
|
||||||
code: ErrorCodes.X_MISSING_END_TAG,
|
code: ErrorCodes.X_MISSING_END_TAG,
|
||||||
loc: {
|
loc: {
|
||||||
offset: 13,
|
start: {
|
||||||
line: 3,
|
offset: 13,
|
||||||
column: 1
|
line: 3,
|
||||||
|
column: 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -1264,9 +1272,11 @@ describe('compiler: parse', () => {
|
|||||||
{
|
{
|
||||||
code: ErrorCodes.X_INVALID_END_TAG,
|
code: ErrorCodes.X_INVALID_END_TAG,
|
||||||
loc: {
|
loc: {
|
||||||
offset: 20,
|
start: {
|
||||||
line: 4,
|
offset: 20,
|
||||||
column: 1
|
line: 4,
|
||||||
|
column: 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -2386,7 +2396,7 @@ describe('compiler: parse', () => {
|
|||||||
expect(
|
expect(
|
||||||
spy.mock.calls.map(([err]) => ({
|
spy.mock.calls.map(([err]) => ({
|
||||||
type: err.code,
|
type: err.code,
|
||||||
loc: err.loc
|
loc: err.loc.start
|
||||||
}))
|
}))
|
||||||
).toMatchObject(errors)
|
).toMatchObject(errors)
|
||||||
expect(ast).toMatchSnapshot()
|
expect(ast).toMatchSnapshot()
|
||||||
|
@ -162,10 +162,10 @@ 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
|
||||||
const plugin: NodeTransform = (node, context) => {
|
const plugin: NodeTransform = (node, context) => {
|
||||||
context.onError(
|
context.onError(
|
||||||
createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc.start)
|
createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const spy = jest.fn()
|
const spy = jest.fn()
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
describe('compiler: element transform', () => {
|
||||||
|
test.todo('should work')
|
||||||
|
})
|
@ -4,10 +4,293 @@ 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'
|
||||||
|
|
||||||
describe('v-for', () => {
|
describe('compiler: transform v-for', () => {
|
||||||
describe('transform', () => {
|
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, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
|
expect(forNode.valueAlias!.content).toBe('index')
|
||||||
|
expect(forNode.source.content).toBe('5')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('value', () => {
|
||||||
|
const node = parse('<span v-for="(item) in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
|
expect(forNode.valueAlias!.content).toBe('item')
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('object de-structured value', () => {
|
||||||
|
const node = parse('<span v-for="({ id, value }) in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
|
expect(forNode.valueAlias!.content).toBe('{ id, value }')
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('array de-structured value', () => {
|
||||||
|
const node = parse('<span v-for="([ id, value ]) in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
|
expect(forNode.valueAlias!.content).toBe('[ id, value ]')
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('value and key', () => {
|
||||||
|
const node = parse('<span v-for="(item, key) in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.keyAlias).not.toBeUndefined()
|
||||||
|
expect(forNode.keyAlias!.content).toBe('key')
|
||||||
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
|
expect(forNode.valueAlias!.content).toBe('item')
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('value, key and index', () => {
|
||||||
|
const node = parse('<span v-for="(value, key, index) in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).not.toBeUndefined()
|
||||||
|
expect(forNode.keyAlias!.content).toBe('key')
|
||||||
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias!.content).toBe('index')
|
||||||
|
expect(forNode.valueAlias!.content).toBe('value')
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('skipped key', () => {
|
||||||
|
const node = parse('<span v-for="(value,,index) in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias!.content).toBe('index')
|
||||||
|
expect(forNode.valueAlias!.content).toBe('value')
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('skipped value and key', () => {
|
||||||
|
const node = parse('<span v-for="(,,index) in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias!.content).toBe('index')
|
||||||
|
expect(forNode.valueAlias).toBeUndefined()
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('unbracketed value', () => {
|
||||||
|
const node = parse('<span v-for="item in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
|
expect(forNode.valueAlias!.content).toBe('item')
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('unbracketed value and key', () => {
|
||||||
|
const node = parse('<span v-for="item, key in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).not.toBeUndefined()
|
||||||
|
expect(forNode.keyAlias!.content).toBe('key')
|
||||||
|
expect(forNode.objectIndexAlias).toBeUndefined()
|
||||||
|
expect(forNode.valueAlias!.content).toBe('item')
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('unbracketed value, key and index', () => {
|
||||||
|
const node = parse('<span v-for="value, key, index in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).not.toBeUndefined()
|
||||||
|
expect(forNode.keyAlias!.content).toBe('key')
|
||||||
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias!.content).toBe('index')
|
||||||
|
expect(forNode.valueAlias!.content).toBe('value')
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('unbracketed skipped key', () => {
|
||||||
|
const node = parse('<span v-for="value, , index in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias!.content).toBe('index')
|
||||||
|
expect(forNode.valueAlias!.content).toBe('value')
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('unbracketed skipped value and key', () => {
|
||||||
|
const node = parse('<span v-for=", , index in items" />')
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
expect(forNode.keyAlias).toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
||||||
|
expect(forNode.objectIndexAlias!.content).toBe('index')
|
||||||
|
expect(forNode.valueAlias).toBeUndefined()
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('missing expression', () => {
|
||||||
|
const node = parse('<span v-for />')
|
||||||
|
const onError = jest.fn()
|
||||||
|
transform(node, { nodeTransforms: [transformFor], onError })
|
||||||
|
|
||||||
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onError).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: ErrorCodes.X_FOR_NO_EXPRESSION
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('empty expression', () => {
|
||||||
|
const node = parse('<span v-for="" />')
|
||||||
|
const onError = jest.fn()
|
||||||
|
transform(node, { nodeTransforms: [transformFor], onError })
|
||||||
|
|
||||||
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onError).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('invalid expression', () => {
|
||||||
|
const node = parse('<span v-for="items" />')
|
||||||
|
const onError = jest.fn()
|
||||||
|
transform(node, { nodeTransforms: [transformFor], onError })
|
||||||
|
|
||||||
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onError).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('missing source', () => {
|
||||||
|
const node = parse('<span v-for="item in" />')
|
||||||
|
const onError = jest.fn()
|
||||||
|
transform(node, { nodeTransforms: [transformFor], onError })
|
||||||
|
|
||||||
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onError).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('missing value', () => {
|
||||||
|
const node = parse('<span v-for="in items" />')
|
||||||
|
const onError = jest.fn()
|
||||||
|
transform(node, { nodeTransforms: [transformFor], onError })
|
||||||
|
|
||||||
|
expect(onError).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onError).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('source location', () => {
|
||||||
|
test('value & source', () => {
|
||||||
|
const source = '<span v-for="item in items" />'
|
||||||
|
const node = parse(source)
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
@ -16,30 +299,29 @@ describe('v-for', () => {
|
|||||||
const forNode = node.children[0] as ForNode
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
|
||||||
expect(forNode.valueAlias!.content).toBe('index')
|
|
||||||
expect(forNode.source.content).toBe('5')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('value', () => {
|
|
||||||
const node = parse('<span v-for="(item) in items" />')
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
expect(forNode.valueAlias!.content).toBe('item')
|
||||||
|
expect(forNode.valueAlias!.loc.start.offset).toBe(
|
||||||
|
source.indexOf('item') - 1
|
||||||
|
)
|
||||||
|
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item'))
|
||||||
|
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.valueAlias!.loc.end.column).toBe(
|
||||||
|
source.indexOf('item') + 4
|
||||||
|
)
|
||||||
|
|
||||||
expect(forNode.source.content).toBe('items')
|
expect(forNode.source.content).toBe('items')
|
||||||
|
expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1)
|
||||||
|
expect(forNode.source.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
|
||||||
|
expect(forNode.source.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('object de-structured value', () => {
|
test('bracketed value', () => {
|
||||||
const node = parse('<span v-for="({ id, value }) in items" />')
|
const source = '<span v-for="( item ) in items" />'
|
||||||
|
const node = parse(source)
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
@ -48,46 +330,29 @@ describe('v-for', () => {
|
|||||||
const forNode = node.children[0] as ForNode
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
|
||||||
expect(forNode.valueAlias!.content).toBe('{ id, value }')
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('array de-structured value', () => {
|
|
||||||
const node = parse('<span v-for="([ id, value ]) in items" />')
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
|
||||||
expect(forNode.valueAlias!.content).toBe('[ id, value ]')
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('value and key', () => {
|
|
||||||
const node = parse('<span v-for="(item, key) in items" />')
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.keyAlias).not.toBeUndefined()
|
|
||||||
expect(forNode.keyAlias!.content).toBe('key')
|
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
expect(forNode.valueAlias!.content).toBe('item')
|
||||||
|
expect(forNode.valueAlias!.loc.start.offset).toBe(
|
||||||
|
source.indexOf('item') - 1
|
||||||
|
)
|
||||||
|
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item'))
|
||||||
|
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.valueAlias!.loc.end.column).toBe(
|
||||||
|
source.indexOf('item') + 4
|
||||||
|
)
|
||||||
|
|
||||||
expect(forNode.source.content).toBe('items')
|
expect(forNode.source.content).toBe('items')
|
||||||
|
expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1)
|
||||||
|
expect(forNode.source.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
|
||||||
|
expect(forNode.source.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('value, key and index', () => {
|
test('de-structured value', () => {
|
||||||
const node = parse('<span v-for="(value, key, index) in items" />')
|
const source = '<span v-for="( { id, key })in items" />'
|
||||||
|
const node = parse(source)
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
@ -96,16 +361,82 @@ describe('v-for', () => {
|
|||||||
const forNode = node.children[0] as ForNode
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
expect(forNode.keyAlias).not.toBeUndefined()
|
|
||||||
|
expect(forNode.valueAlias!.content).toBe('{ id, key }')
|
||||||
|
expect(forNode.valueAlias!.loc.start.offset).toBe(
|
||||||
|
source.indexOf('{ id, key }') - 1
|
||||||
|
)
|
||||||
|
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.valueAlias!.loc.start.column).toBe(
|
||||||
|
source.indexOf('{ id, key }')
|
||||||
|
)
|
||||||
|
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.valueAlias!.loc.end.column).toBe(
|
||||||
|
source.indexOf('{ id, key }') + '{ id, key }'.length
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(forNode.source.content).toBe('items')
|
||||||
|
expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1)
|
||||||
|
expect(forNode.source.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
|
||||||
|
expect(forNode.source.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('bracketed value, key, index', () => {
|
||||||
|
const source = '<span v-for="( item, key, index ) in items" />'
|
||||||
|
const node = parse(source)
|
||||||
|
|
||||||
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
|
expect(node.children.length).toBe(1)
|
||||||
|
|
||||||
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
|
|
||||||
|
expect(forNode.valueAlias!.content).toBe('item')
|
||||||
|
expect(forNode.valueAlias!.loc.start.offset).toBe(
|
||||||
|
source.indexOf('item') - 1
|
||||||
|
)
|
||||||
|
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item'))
|
||||||
|
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.valueAlias!.loc.end.column).toBe(
|
||||||
|
source.indexOf('item') + 4
|
||||||
|
)
|
||||||
|
|
||||||
expect(forNode.keyAlias!.content).toBe('key')
|
expect(forNode.keyAlias!.content).toBe('key')
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
expect(forNode.keyAlias!.loc.start.offset).toBe(source.indexOf('key') - 1)
|
||||||
|
expect(forNode.keyAlias!.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.keyAlias!.loc.start.column).toBe(source.indexOf('key'))
|
||||||
|
expect(forNode.keyAlias!.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.keyAlias!.loc.end.column).toBe(source.indexOf('key') + 3)
|
||||||
|
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
expect(forNode.objectIndexAlias!.content).toBe('index')
|
||||||
expect(forNode.valueAlias!.content).toBe('value')
|
expect(forNode.objectIndexAlias!.loc.start.offset).toBe(
|
||||||
|
source.indexOf('index') - 1
|
||||||
|
)
|
||||||
|
expect(forNode.objectIndexAlias!.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.objectIndexAlias!.loc.start.column).toBe(
|
||||||
|
source.indexOf('index')
|
||||||
|
)
|
||||||
|
expect(forNode.objectIndexAlias!.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.objectIndexAlias!.loc.end.column).toBe(
|
||||||
|
source.indexOf('index') + 5
|
||||||
|
)
|
||||||
|
|
||||||
expect(forNode.source.content).toBe('items')
|
expect(forNode.source.content).toBe('items')
|
||||||
|
expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1)
|
||||||
|
expect(forNode.source.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
|
||||||
|
expect(forNode.source.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('skipped key', () => {
|
test('skipped key', () => {
|
||||||
const node = parse('<span v-for="(value,,index) in items" />')
|
const source = '<span v-for="( item,, index ) in items" />'
|
||||||
|
const node = parse(source)
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
transform(node, { nodeTransforms: [transformFor] })
|
||||||
|
|
||||||
@ -114,390 +445,37 @@ describe('v-for', () => {
|
|||||||
const forNode = node.children[0] as ForNode
|
const forNode = node.children[0] as ForNode
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
expect(forNode.type).toBe(NodeTypes.FOR)
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
|
||||||
expect(forNode.valueAlias!.content).toBe('value')
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('skipped value and key', () => {
|
|
||||||
const node = parse('<span v-for="(,,index) in items" />')
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
|
||||||
expect(forNode.valueAlias).toBeUndefined()
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('unbracketed value', () => {
|
|
||||||
const node = parse('<span v-for="item in items" />')
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
expect(forNode.valueAlias!.content).toBe('item')
|
||||||
expect(forNode.source.content).toBe('items')
|
expect(forNode.valueAlias!.loc.start.offset).toBe(
|
||||||
})
|
source.indexOf('item') - 1
|
||||||
|
)
|
||||||
|
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item'))
|
||||||
|
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.valueAlias!.loc.end.column).toBe(
|
||||||
|
source.indexOf('item') + 4
|
||||||
|
)
|
||||||
|
|
||||||
test('unbracketed value and key', () => {
|
|
||||||
const node = parse('<span v-for="item, key in items" />')
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
expect(forNode.keyAlias).not.toBeUndefined()
|
|
||||||
expect(forNode.keyAlias!.content).toBe('key')
|
|
||||||
expect(forNode.objectIndexAlias).toBeUndefined()
|
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('unbracketed value, key and index', () => {
|
|
||||||
const node = parse('<span v-for="value, key, index in items" />')
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
expect(forNode.keyAlias).not.toBeUndefined()
|
|
||||||
expect(forNode.keyAlias!.content).toBe('key')
|
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
expect(forNode.objectIndexAlias!.content).toBe('index')
|
||||||
expect(forNode.valueAlias!.content).toBe('value')
|
expect(forNode.objectIndexAlias!.loc.start.offset).toBe(
|
||||||
|
source.indexOf('index') - 1
|
||||||
|
)
|
||||||
|
expect(forNode.objectIndexAlias!.loc.start.line).toBe(1)
|
||||||
|
expect(forNode.objectIndexAlias!.loc.start.column).toBe(
|
||||||
|
source.indexOf('index')
|
||||||
|
)
|
||||||
|
expect(forNode.objectIndexAlias!.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.objectIndexAlias!.loc.end.column).toBe(
|
||||||
|
source.indexOf('index') + 5
|
||||||
|
)
|
||||||
|
|
||||||
expect(forNode.source.content).toBe('items')
|
expect(forNode.source.content).toBe('items')
|
||||||
})
|
expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1)
|
||||||
|
expect(forNode.source.loc.start.line).toBe(1)
|
||||||
test('unbracketed skipped key', () => {
|
expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
|
||||||
const node = parse('<span v-for="value, , index in items" />')
|
expect(forNode.source.loc.end.line).toBe(1)
|
||||||
|
expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
|
||||||
expect(forNode.valueAlias!.content).toBe('value')
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('unbracketed skipped value and key', () => {
|
|
||||||
const node = parse('<span v-for=", , index in items" />')
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
expect(forNode.keyAlias).toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias).not.toBeUndefined()
|
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
|
||||||
expect(forNode.valueAlias).toBeUndefined()
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('missing expression', () => {
|
|
||||||
const node = parse('<span v-for />')
|
|
||||||
const onError = jest.fn()
|
|
||||||
transform(node, { nodeTransforms: [transformFor], onError })
|
|
||||||
|
|
||||||
expect(onError).toHaveBeenCalledTimes(1)
|
|
||||||
expect(onError).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
code: ErrorCodes.X_FOR_NO_EXPRESSION
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('empty expression', () => {
|
|
||||||
const node = parse('<span v-for="" />')
|
|
||||||
const onError = jest.fn()
|
|
||||||
transform(node, { nodeTransforms: [transformFor], onError })
|
|
||||||
|
|
||||||
expect(onError).toHaveBeenCalledTimes(1)
|
|
||||||
expect(onError).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('invalid expression', () => {
|
|
||||||
const node = parse('<span v-for="items" />')
|
|
||||||
const onError = jest.fn()
|
|
||||||
transform(node, { nodeTransforms: [transformFor], onError })
|
|
||||||
|
|
||||||
expect(onError).toHaveBeenCalledTimes(1)
|
|
||||||
expect(onError).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('missing source', () => {
|
|
||||||
const node = parse('<span v-for="item in" />')
|
|
||||||
const onError = jest.fn()
|
|
||||||
transform(node, { nodeTransforms: [transformFor], onError })
|
|
||||||
|
|
||||||
expect(onError).toHaveBeenCalledTimes(1)
|
|
||||||
expect(onError).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('missing value', () => {
|
|
||||||
const node = parse('<span v-for="in items" />')
|
|
||||||
const onError = jest.fn()
|
|
||||||
transform(node, { nodeTransforms: [transformFor], onError })
|
|
||||||
|
|
||||||
expect(onError).toHaveBeenCalledTimes(1)
|
|
||||||
expect(onError).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('source location', () => {
|
|
||||||
test('value & source', () => {
|
|
||||||
const source = '<span v-for="item in items" />'
|
|
||||||
const node = parse(source)
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
|
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
|
||||||
expect(forNode.valueAlias!.loc.start.offset).toBe(
|
|
||||||
source.indexOf('item') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.valueAlias!.loc.start.column).toBe(
|
|
||||||
source.indexOf('item')
|
|
||||||
)
|
|
||||||
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.valueAlias!.loc.end.column).toBe(
|
|
||||||
source.indexOf('item') + 4
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
expect(forNode.source.loc.start.offset).toBe(
|
|
||||||
source.indexOf('items') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.source.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
|
|
||||||
expect(forNode.source.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('bracketed value', () => {
|
|
||||||
const source = '<span v-for="( item ) in items" />'
|
|
||||||
const node = parse(source)
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
|
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
|
||||||
expect(forNode.valueAlias!.loc.start.offset).toBe(
|
|
||||||
source.indexOf('item') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.valueAlias!.loc.start.column).toBe(
|
|
||||||
source.indexOf('item')
|
|
||||||
)
|
|
||||||
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.valueAlias!.loc.end.column).toBe(
|
|
||||||
source.indexOf('item') + 4
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
expect(forNode.source.loc.start.offset).toBe(
|
|
||||||
source.indexOf('items') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.source.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
|
|
||||||
expect(forNode.source.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('de-structured value', () => {
|
|
||||||
const source = '<span v-for="( { id, key })in items" />'
|
|
||||||
const node = parse(source)
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
|
|
||||||
expect(forNode.valueAlias!.content).toBe('{ id, key }')
|
|
||||||
expect(forNode.valueAlias!.loc.start.offset).toBe(
|
|
||||||
source.indexOf('{ id, key }') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.valueAlias!.loc.start.column).toBe(
|
|
||||||
source.indexOf('{ id, key }')
|
|
||||||
)
|
|
||||||
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.valueAlias!.loc.end.column).toBe(
|
|
||||||
source.indexOf('{ id, key }') + '{ id, key }'.length
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
expect(forNode.source.loc.start.offset).toBe(
|
|
||||||
source.indexOf('items') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.source.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
|
|
||||||
expect(forNode.source.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('bracketed value, key, index', () => {
|
|
||||||
const source = '<span v-for="( item, key, index ) in items" />'
|
|
||||||
const node = parse(source)
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
|
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
|
||||||
expect(forNode.valueAlias!.loc.start.offset).toBe(
|
|
||||||
source.indexOf('item') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.valueAlias!.loc.start.column).toBe(
|
|
||||||
source.indexOf('item')
|
|
||||||
)
|
|
||||||
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.valueAlias!.loc.end.column).toBe(
|
|
||||||
source.indexOf('item') + 4
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(forNode.keyAlias!.content).toBe('key')
|
|
||||||
expect(forNode.keyAlias!.loc.start.offset).toBe(
|
|
||||||
source.indexOf('key') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.keyAlias!.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.keyAlias!.loc.start.column).toBe(source.indexOf('key'))
|
|
||||||
expect(forNode.keyAlias!.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.keyAlias!.loc.end.column).toBe(source.indexOf('key') + 3)
|
|
||||||
|
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
|
||||||
expect(forNode.objectIndexAlias!.loc.start.offset).toBe(
|
|
||||||
source.indexOf('index') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.objectIndexAlias!.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.objectIndexAlias!.loc.start.column).toBe(
|
|
||||||
source.indexOf('index')
|
|
||||||
)
|
|
||||||
expect(forNode.objectIndexAlias!.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.objectIndexAlias!.loc.end.column).toBe(
|
|
||||||
source.indexOf('index') + 5
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
expect(forNode.source.loc.start.offset).toBe(
|
|
||||||
source.indexOf('items') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.source.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
|
|
||||||
expect(forNode.source.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('skipped key', () => {
|
|
||||||
const source = '<span v-for="( item,, index ) in items" />'
|
|
||||||
const node = parse(source)
|
|
||||||
|
|
||||||
transform(node, { nodeTransforms: [transformFor] })
|
|
||||||
|
|
||||||
expect(node.children.length).toBe(1)
|
|
||||||
|
|
||||||
const forNode = node.children[0] as ForNode
|
|
||||||
|
|
||||||
expect(forNode.type).toBe(NodeTypes.FOR)
|
|
||||||
|
|
||||||
expect(forNode.valueAlias!.content).toBe('item')
|
|
||||||
expect(forNode.valueAlias!.loc.start.offset).toBe(
|
|
||||||
source.indexOf('item') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.valueAlias!.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.valueAlias!.loc.start.column).toBe(
|
|
||||||
source.indexOf('item')
|
|
||||||
)
|
|
||||||
expect(forNode.valueAlias!.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.valueAlias!.loc.end.column).toBe(
|
|
||||||
source.indexOf('item') + 4
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(forNode.objectIndexAlias!.content).toBe('index')
|
|
||||||
expect(forNode.objectIndexAlias!.loc.start.offset).toBe(
|
|
||||||
source.indexOf('index') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.objectIndexAlias!.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.objectIndexAlias!.loc.start.column).toBe(
|
|
||||||
source.indexOf('index')
|
|
||||||
)
|
|
||||||
expect(forNode.objectIndexAlias!.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.objectIndexAlias!.loc.end.column).toBe(
|
|
||||||
source.indexOf('index') + 5
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(forNode.source.content).toBe('items')
|
|
||||||
expect(forNode.source.loc.start.offset).toBe(
|
|
||||||
source.indexOf('items') - 1
|
|
||||||
)
|
|
||||||
expect(forNode.source.loc.start.line).toBe(1)
|
|
||||||
expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
|
|
||||||
expect(forNode.source.loc.end.line).toBe(1)
|
|
||||||
expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -10,248 +10,242 @@ import {
|
|||||||
} from '../../src/ast'
|
} from '../../src/ast'
|
||||||
import { ErrorCodes } from '../../src/errors'
|
import { ErrorCodes } from '../../src/errors'
|
||||||
|
|
||||||
describe('compiler: v-if', () => {
|
describe('compiler: transform v-if', () => {
|
||||||
describe('transform', () => {
|
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, {
|
nodeTransforms: [transformIf]
|
||||||
nodeTransforms: [transformIf]
|
|
||||||
})
|
|
||||||
const node = ast.children[0] as IfNode
|
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
|
||||||
expect(node.branches.length).toBe(1)
|
|
||||||
expect(node.branches[0].condition!.content).toBe(`ok`)
|
|
||||||
expect(node.branches[0].children.length).toBe(1)
|
|
||||||
expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
|
|
||||||
expect((node.branches[0].children[0] as ElementNode).tag).toBe(`div`)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('template v-if', () => {
|
|
||||||
const ast = parse(`<template v-if="ok"><div/>hello<p/></template>`)
|
|
||||||
transform(ast, {
|
|
||||||
nodeTransforms: [transformIf]
|
|
||||||
})
|
|
||||||
const node = ast.children[0] as IfNode
|
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
|
||||||
expect(node.branches.length).toBe(1)
|
|
||||||
expect(node.branches[0].condition!.content).toBe(`ok`)
|
|
||||||
expect(node.branches[0].children.length).toBe(3)
|
|
||||||
expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
|
|
||||||
expect((node.branches[0].children[0] as ElementNode).tag).toBe(`div`)
|
|
||||||
expect(node.branches[0].children[1].type).toBe(NodeTypes.TEXT)
|
|
||||||
expect((node.branches[0].children[1] as TextNode).content).toBe(`hello`)
|
|
||||||
expect(node.branches[0].children[2].type).toBe(NodeTypes.ELEMENT)
|
|
||||||
expect((node.branches[0].children[2] as ElementNode).tag).toBe(`p`)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('v-if + v-else', () => {
|
|
||||||
const ast = parse(`<div v-if="ok"/><p v-else/>`)
|
|
||||||
transform(ast, {
|
|
||||||
nodeTransforms: [transformIf]
|
|
||||||
})
|
|
||||||
// should fold branches
|
|
||||||
expect(ast.children.length).toBe(1)
|
|
||||||
|
|
||||||
const node = ast.children[0] as IfNode
|
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
|
||||||
expect(node.branches.length).toBe(2)
|
|
||||||
|
|
||||||
const b1 = node.branches[0]
|
|
||||||
expect(b1.condition!.content).toBe(`ok`)
|
|
||||||
expect(b1.children.length).toBe(1)
|
|
||||||
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
|
||||||
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
|
||||||
|
|
||||||
const b2 = node.branches[1]
|
|
||||||
expect(b2.condition).toBeUndefined()
|
|
||||||
expect(b2.children.length).toBe(1)
|
|
||||||
expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
|
|
||||||
expect((b2.children[0] as ElementNode).tag).toBe(`p`)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('v-if + v-else-if', () => {
|
|
||||||
const ast = parse(`<div v-if="ok"/><p v-else-if="orNot"/>`)
|
|
||||||
transform(ast, {
|
|
||||||
nodeTransforms: [transformIf]
|
|
||||||
})
|
|
||||||
// should fold branches
|
|
||||||
expect(ast.children.length).toBe(1)
|
|
||||||
|
|
||||||
const node = ast.children[0] as IfNode
|
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
|
||||||
expect(node.branches.length).toBe(2)
|
|
||||||
|
|
||||||
const b1 = node.branches[0]
|
|
||||||
expect(b1.condition!.content).toBe(`ok`)
|
|
||||||
expect(b1.children.length).toBe(1)
|
|
||||||
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
|
||||||
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
|
||||||
|
|
||||||
const b2 = node.branches[1]
|
|
||||||
expect(b2.condition!.content).toBe(`orNot`)
|
|
||||||
expect(b2.children.length).toBe(1)
|
|
||||||
expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
|
|
||||||
expect((b2.children[0] as ElementNode).tag).toBe(`p`)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('v-if + v-else-if + v-else', () => {
|
|
||||||
const ast = parse(
|
|
||||||
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
|
|
||||||
)
|
|
||||||
transform(ast, {
|
|
||||||
nodeTransforms: [transformIf]
|
|
||||||
})
|
|
||||||
// should fold branches
|
|
||||||
expect(ast.children.length).toBe(1)
|
|
||||||
|
|
||||||
const node = ast.children[0] as IfNode
|
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
|
||||||
expect(node.branches.length).toBe(3)
|
|
||||||
|
|
||||||
const b1 = node.branches[0]
|
|
||||||
expect(b1.condition!.content).toBe(`ok`)
|
|
||||||
expect(b1.children.length).toBe(1)
|
|
||||||
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
|
||||||
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
|
||||||
|
|
||||||
const b2 = node.branches[1]
|
|
||||||
expect(b2.condition!.content).toBe(`orNot`)
|
|
||||||
expect(b2.children.length).toBe(1)
|
|
||||||
expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
|
|
||||||
expect((b2.children[0] as ElementNode).tag).toBe(`p`)
|
|
||||||
|
|
||||||
const b3 = node.branches[2]
|
|
||||||
expect(b3.condition).toBeUndefined()
|
|
||||||
expect(b3.children.length).toBe(1)
|
|
||||||
expect(b3.children[0].type).toBe(NodeTypes.TEXT)
|
|
||||||
expect((b3.children[0] as TextNode).content).toBe(`fine`)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('comment between branches', () => {
|
|
||||||
const ast = parse(`
|
|
||||||
<div v-if="ok"/>
|
|
||||||
<!--foo-->
|
|
||||||
<p v-else-if="orNot"/>
|
|
||||||
<!--bar-->
|
|
||||||
<template v-else>fine</template>
|
|
||||||
`)
|
|
||||||
transform(ast, {
|
|
||||||
nodeTransforms: [transformIf]
|
|
||||||
})
|
|
||||||
// should fold branches
|
|
||||||
expect(ast.children.length).toBe(1)
|
|
||||||
|
|
||||||
const node = ast.children[0] as IfNode
|
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
|
||||||
expect(node.branches.length).toBe(3)
|
|
||||||
|
|
||||||
const b1 = node.branches[0]
|
|
||||||
expect(b1.condition!.content).toBe(`ok`)
|
|
||||||
expect(b1.children.length).toBe(1)
|
|
||||||
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
|
||||||
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
|
||||||
|
|
||||||
const b2 = node.branches[1]
|
|
||||||
expect(b2.condition!.content).toBe(`orNot`)
|
|
||||||
expect(b2.children.length).toBe(2)
|
|
||||||
expect(b2.children[0].type).toBe(NodeTypes.COMMENT)
|
|
||||||
expect((b2.children[0] as CommentNode).content).toBe(`foo`)
|
|
||||||
expect(b2.children[1].type).toBe(NodeTypes.ELEMENT)
|
|
||||||
expect((b2.children[1] as ElementNode).tag).toBe(`p`)
|
|
||||||
|
|
||||||
const b3 = node.branches[2]
|
|
||||||
expect(b3.condition).toBeUndefined()
|
|
||||||
expect(b3.children.length).toBe(2)
|
|
||||||
expect(b3.children[0].type).toBe(NodeTypes.COMMENT)
|
|
||||||
expect((b3.children[0] as CommentNode).content).toBe(`bar`)
|
|
||||||
expect(b3.children[1].type).toBe(NodeTypes.TEXT)
|
|
||||||
expect((b3.children[1] as TextNode).content).toBe(`fine`)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('error on v-else missing adjacent v-if', () => {
|
|
||||||
const ast = parse(`<div v-else/>`)
|
|
||||||
const spy = jest.fn()
|
|
||||||
transform(ast, {
|
|
||||||
nodeTransforms: [transformIf],
|
|
||||||
onError: spy
|
|
||||||
})
|
|
||||||
expect(spy.mock.calls[0]).toMatchObject([
|
|
||||||
{
|
|
||||||
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
|
||||||
loc: ast.children[0].loc.start
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
const ast2 = parse(`<div/><div v-else/>`)
|
|
||||||
const spy2 = jest.fn()
|
|
||||||
transform(ast2, {
|
|
||||||
nodeTransforms: [transformIf],
|
|
||||||
onError: spy2
|
|
||||||
})
|
|
||||||
expect(spy2.mock.calls[0]).toMatchObject([
|
|
||||||
{
|
|
||||||
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
|
||||||
loc: ast2.children[1].loc.start
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
const ast3 = parse(`<div/>foo<div v-else/>`)
|
|
||||||
const spy3 = jest.fn()
|
|
||||||
transform(ast3, {
|
|
||||||
nodeTransforms: [transformIf],
|
|
||||||
onError: spy3
|
|
||||||
})
|
|
||||||
expect(spy3.mock.calls[0]).toMatchObject([
|
|
||||||
{
|
|
||||||
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
|
||||||
loc: ast3.children[2].loc.start
|
|
||||||
}
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
test('error on v-else-if missing adjacent v-if', () => {
|
|
||||||
const ast = parse(`<div v-else-if="foo"/>`)
|
|
||||||
const spy = jest.fn()
|
|
||||||
transform(ast, {
|
|
||||||
nodeTransforms: [transformIf],
|
|
||||||
onError: spy
|
|
||||||
})
|
|
||||||
expect(spy.mock.calls[0]).toMatchObject([
|
|
||||||
{
|
|
||||||
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
|
||||||
loc: ast.children[0].loc.start
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
const ast2 = parse(`<div/><div v-else-if="foo"/>`)
|
|
||||||
const spy2 = jest.fn()
|
|
||||||
transform(ast2, {
|
|
||||||
nodeTransforms: [transformIf],
|
|
||||||
onError: spy2
|
|
||||||
})
|
|
||||||
expect(spy2.mock.calls[0]).toMatchObject([
|
|
||||||
{
|
|
||||||
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
|
||||||
loc: ast2.children[1].loc.start
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
const ast3 = parse(`<div/>foo<div v-else-if="foo"/>`)
|
|
||||||
const spy3 = jest.fn()
|
|
||||||
transform(ast3, {
|
|
||||||
nodeTransforms: [transformIf],
|
|
||||||
onError: spy3
|
|
||||||
})
|
|
||||||
expect(spy3.mock.calls[0]).toMatchObject([
|
|
||||||
{
|
|
||||||
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
|
||||||
loc: ast3.children[2].loc.start
|
|
||||||
}
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
|
const node = ast.children[0] as IfNode
|
||||||
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
|
expect(node.branches.length).toBe(1)
|
||||||
|
expect(node.branches[0].condition!.content).toBe(`ok`)
|
||||||
|
expect(node.branches[0].children.length).toBe(1)
|
||||||
|
expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect((node.branches[0].children[0] as ElementNode).tag).toBe(`div`)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('codegen', () => {
|
test('template v-if', () => {
|
||||||
// TODO
|
const ast = parse(`<template v-if="ok"><div/>hello<p/></template>`)
|
||||||
|
transform(ast, {
|
||||||
|
nodeTransforms: [transformIf]
|
||||||
|
})
|
||||||
|
const node = ast.children[0] as IfNode
|
||||||
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
|
expect(node.branches.length).toBe(1)
|
||||||
|
expect(node.branches[0].condition!.content).toBe(`ok`)
|
||||||
|
expect(node.branches[0].children.length).toBe(3)
|
||||||
|
expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect((node.branches[0].children[0] as ElementNode).tag).toBe(`div`)
|
||||||
|
expect(node.branches[0].children[1].type).toBe(NodeTypes.TEXT)
|
||||||
|
expect((node.branches[0].children[1] as TextNode).content).toBe(`hello`)
|
||||||
|
expect(node.branches[0].children[2].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect((node.branches[0].children[2] as ElementNode).tag).toBe(`p`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-if + v-else', () => {
|
||||||
|
const ast = parse(`<div v-if="ok"/><p v-else/>`)
|
||||||
|
transform(ast, {
|
||||||
|
nodeTransforms: [transformIf]
|
||||||
|
})
|
||||||
|
// should fold branches
|
||||||
|
expect(ast.children.length).toBe(1)
|
||||||
|
|
||||||
|
const node = ast.children[0] as IfNode
|
||||||
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
|
expect(node.branches.length).toBe(2)
|
||||||
|
|
||||||
|
const b1 = node.branches[0]
|
||||||
|
expect(b1.condition!.content).toBe(`ok`)
|
||||||
|
expect(b1.children.length).toBe(1)
|
||||||
|
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
||||||
|
|
||||||
|
const b2 = node.branches[1]
|
||||||
|
expect(b2.condition).toBeUndefined()
|
||||||
|
expect(b2.children.length).toBe(1)
|
||||||
|
expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect((b2.children[0] as ElementNode).tag).toBe(`p`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-if + v-else-if', () => {
|
||||||
|
const ast = parse(`<div v-if="ok"/><p v-else-if="orNot"/>`)
|
||||||
|
transform(ast, {
|
||||||
|
nodeTransforms: [transformIf]
|
||||||
|
})
|
||||||
|
// should fold branches
|
||||||
|
expect(ast.children.length).toBe(1)
|
||||||
|
|
||||||
|
const node = ast.children[0] as IfNode
|
||||||
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
|
expect(node.branches.length).toBe(2)
|
||||||
|
|
||||||
|
const b1 = node.branches[0]
|
||||||
|
expect(b1.condition!.content).toBe(`ok`)
|
||||||
|
expect(b1.children.length).toBe(1)
|
||||||
|
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
||||||
|
|
||||||
|
const b2 = node.branches[1]
|
||||||
|
expect(b2.condition!.content).toBe(`orNot`)
|
||||||
|
expect(b2.children.length).toBe(1)
|
||||||
|
expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect((b2.children[0] as ElementNode).tag).toBe(`p`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-if + v-else-if + v-else', () => {
|
||||||
|
const ast = parse(
|
||||||
|
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
|
||||||
|
)
|
||||||
|
transform(ast, {
|
||||||
|
nodeTransforms: [transformIf]
|
||||||
|
})
|
||||||
|
// should fold branches
|
||||||
|
expect(ast.children.length).toBe(1)
|
||||||
|
|
||||||
|
const node = ast.children[0] as IfNode
|
||||||
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
|
expect(node.branches.length).toBe(3)
|
||||||
|
|
||||||
|
const b1 = node.branches[0]
|
||||||
|
expect(b1.condition!.content).toBe(`ok`)
|
||||||
|
expect(b1.children.length).toBe(1)
|
||||||
|
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
||||||
|
|
||||||
|
const b2 = node.branches[1]
|
||||||
|
expect(b2.condition!.content).toBe(`orNot`)
|
||||||
|
expect(b2.children.length).toBe(1)
|
||||||
|
expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect((b2.children[0] as ElementNode).tag).toBe(`p`)
|
||||||
|
|
||||||
|
const b3 = node.branches[2]
|
||||||
|
expect(b3.condition).toBeUndefined()
|
||||||
|
expect(b3.children.length).toBe(1)
|
||||||
|
expect(b3.children[0].type).toBe(NodeTypes.TEXT)
|
||||||
|
expect((b3.children[0] as TextNode).content).toBe(`fine`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('comment between branches', () => {
|
||||||
|
const ast = parse(`
|
||||||
|
<div v-if="ok"/>
|
||||||
|
<!--foo-->
|
||||||
|
<p v-else-if="orNot"/>
|
||||||
|
<!--bar-->
|
||||||
|
<template v-else>fine</template>
|
||||||
|
`)
|
||||||
|
transform(ast, {
|
||||||
|
nodeTransforms: [transformIf]
|
||||||
|
})
|
||||||
|
// should fold branches
|
||||||
|
expect(ast.children.length).toBe(1)
|
||||||
|
|
||||||
|
const node = ast.children[0] as IfNode
|
||||||
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
|
expect(node.branches.length).toBe(3)
|
||||||
|
|
||||||
|
const b1 = node.branches[0]
|
||||||
|
expect(b1.condition!.content).toBe(`ok`)
|
||||||
|
expect(b1.children.length).toBe(1)
|
||||||
|
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
||||||
|
|
||||||
|
const b2 = node.branches[1]
|
||||||
|
expect(b2.condition!.content).toBe(`orNot`)
|
||||||
|
expect(b2.children.length).toBe(2)
|
||||||
|
expect(b2.children[0].type).toBe(NodeTypes.COMMENT)
|
||||||
|
expect((b2.children[0] as CommentNode).content).toBe(`foo`)
|
||||||
|
expect(b2.children[1].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
expect((b2.children[1] as ElementNode).tag).toBe(`p`)
|
||||||
|
|
||||||
|
const b3 = node.branches[2]
|
||||||
|
expect(b3.condition).toBeUndefined()
|
||||||
|
expect(b3.children.length).toBe(2)
|
||||||
|
expect(b3.children[0].type).toBe(NodeTypes.COMMENT)
|
||||||
|
expect((b3.children[0] as CommentNode).content).toBe(`bar`)
|
||||||
|
expect(b3.children[1].type).toBe(NodeTypes.TEXT)
|
||||||
|
expect((b3.children[1] as TextNode).content).toBe(`fine`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('error on v-else missing adjacent v-if', () => {
|
||||||
|
const ast = parse(`<div v-else/>`)
|
||||||
|
const spy = jest.fn()
|
||||||
|
transform(ast, {
|
||||||
|
nodeTransforms: [transformIf],
|
||||||
|
onError: spy
|
||||||
|
})
|
||||||
|
expect(spy.mock.calls[0]).toMatchObject([
|
||||||
|
{
|
||||||
|
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
||||||
|
loc: ast.children[0].loc
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const ast2 = parse(`<div/><div v-else/>`)
|
||||||
|
const spy2 = jest.fn()
|
||||||
|
transform(ast2, {
|
||||||
|
nodeTransforms: [transformIf],
|
||||||
|
onError: spy2
|
||||||
|
})
|
||||||
|
expect(spy2.mock.calls[0]).toMatchObject([
|
||||||
|
{
|
||||||
|
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
||||||
|
loc: ast2.children[1].loc
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const ast3 = parse(`<div/>foo<div v-else/>`)
|
||||||
|
const spy3 = jest.fn()
|
||||||
|
transform(ast3, {
|
||||||
|
nodeTransforms: [transformIf],
|
||||||
|
onError: spy3
|
||||||
|
})
|
||||||
|
expect(spy3.mock.calls[0]).toMatchObject([
|
||||||
|
{
|
||||||
|
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
||||||
|
loc: ast3.children[2].loc
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('error on v-else-if missing adjacent v-if', () => {
|
||||||
|
const ast = parse(`<div v-else-if="foo"/>`)
|
||||||
|
const spy = jest.fn()
|
||||||
|
transform(ast, {
|
||||||
|
nodeTransforms: [transformIf],
|
||||||
|
onError: spy
|
||||||
|
})
|
||||||
|
expect(spy.mock.calls[0]).toMatchObject([
|
||||||
|
{
|
||||||
|
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
||||||
|
loc: ast.children[0].loc
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const ast2 = parse(`<div/><div v-else-if="foo"/>`)
|
||||||
|
const spy2 = jest.fn()
|
||||||
|
transform(ast2, {
|
||||||
|
nodeTransforms: [transformIf],
|
||||||
|
onError: spy2
|
||||||
|
})
|
||||||
|
expect(spy2.mock.calls[0]).toMatchObject([
|
||||||
|
{
|
||||||
|
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
||||||
|
loc: ast2.children[1].loc
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const ast3 = parse(`<div/>foo<div v-else-if="foo"/>`)
|
||||||
|
const spy3 = jest.fn()
|
||||||
|
transform(ast3, {
|
||||||
|
nodeTransforms: [transformIf],
|
||||||
|
onError: spy3
|
||||||
|
})
|
||||||
|
expect(spy3.mock.calls[0]).toMatchObject([
|
||||||
|
{
|
||||||
|
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
||||||
|
loc: ast3.children[2].loc
|
||||||
|
}
|
||||||
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -366,7 +366,7 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
|
|||||||
const { push, indent, deindent, newline } = context
|
const { push, indent, deindent, newline } = context
|
||||||
const { properties } = node
|
const { properties } = node
|
||||||
const multilines = properties.length > 1
|
const multilines = properties.length > 1
|
||||||
push(`{`, node)
|
push(multilines ? `{` : `{ `, node)
|
||||||
multilines && indent()
|
multilines && indent()
|
||||||
for (let i = 0; i < properties.length; i++) {
|
for (let i = 0; i < properties.length; i++) {
|
||||||
const { key, value } = properties[i]
|
const { key, value } = properties[i]
|
||||||
@ -385,7 +385,7 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
multilines && deindent()
|
multilines && deindent()
|
||||||
push(`}`)
|
push(multilines ? `}` : ` }`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function genArrayExpression(node: ArrayExpression, context: CodegenContext) {
|
function genArrayExpression(node: ArrayExpression, context: CodegenContext) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Position } from './ast'
|
import { SourceLocation } from './ast'
|
||||||
|
|
||||||
export interface CompilerError extends SyntaxError {
|
export interface CompilerError extends SyntaxError {
|
||||||
code: ErrorCodes
|
code: ErrorCodes
|
||||||
loc: Position
|
loc: SourceLocation
|
||||||
}
|
}
|
||||||
|
|
||||||
export function defaultOnError(error: CompilerError) {
|
export function defaultOnError(error: CompilerError) {
|
||||||
@ -11,12 +11,12 @@ export function defaultOnError(error: CompilerError) {
|
|||||||
|
|
||||||
export function createCompilerError(
|
export function createCompilerError(
|
||||||
code: ErrorCodes,
|
code: ErrorCodes,
|
||||||
loc: Position
|
loc: SourceLocation
|
||||||
): CompilerError {
|
): CompilerError {
|
||||||
const error = new SyntaxError(
|
const error = new SyntaxError(
|
||||||
`${__DEV__ || !__BROWSER__ ? errorMessages[code] : code} (${loc.line}:${
|
`${__DEV__ || !__BROWSER__ ? errorMessages[code] : code} (${
|
||||||
loc.column
|
loc.start.line
|
||||||
})`
|
}:${loc.start.column})`
|
||||||
) as CompilerError
|
) as CompilerError
|
||||||
error.code = code
|
error.code = code
|
||||||
error.loc = loc
|
error.loc = loc
|
||||||
|
@ -6,6 +6,8 @@ import { isString } from '@vue/shared'
|
|||||||
import { transformIf } from './transforms/vIf'
|
import { transformIf } from './transforms/vIf'
|
||||||
import { transformFor } from './transforms/vFor'
|
import { transformFor } from './transforms/vFor'
|
||||||
import { prepareElementForCodegen } from './transforms/element'
|
import { prepareElementForCodegen } from './transforms/element'
|
||||||
|
import { transformOn } from './transforms/vOn'
|
||||||
|
import { transformBind } from './transforms/vBind'
|
||||||
|
|
||||||
export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions
|
export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions
|
||||||
|
|
||||||
@ -24,7 +26,8 @@ export function compile(
|
|||||||
...(options.nodeTransforms || []) // user transforms
|
...(options.nodeTransforms || []) // user transforms
|
||||||
],
|
],
|
||||||
directiveTransforms: {
|
directiveTransforms: {
|
||||||
// TODO include built-in directive transforms
|
on: transformOn,
|
||||||
|
bind: transformBind,
|
||||||
...(options.directiveTransforms || {}) // user transforms
|
...(options.directiveTransforms || {}) // user transforms
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -842,7 +842,13 @@ function emitError(
|
|||||||
loc.offset += offset
|
loc.offset += offset
|
||||||
loc.column += offset
|
loc.column += offset
|
||||||
}
|
}
|
||||||
context.options.onError(createCompilerError(code, loc))
|
context.options.onError(
|
||||||
|
createCompilerError(code, {
|
||||||
|
start: loc,
|
||||||
|
end: loc,
|
||||||
|
source: ''
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEnd(
|
function isEnd(
|
||||||
|
@ -123,10 +123,7 @@ function buildProps(
|
|||||||
mergeArgs.push(prop.exp)
|
mergeArgs.push(prop.exp)
|
||||||
} else {
|
} else {
|
||||||
context.onError(
|
context.onError(
|
||||||
createCompilerError(
|
createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, prop.loc)
|
||||||
ErrorCodes.X_V_BIND_NO_EXPRESSION,
|
|
||||||
prop.loc.start
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
@ -1 +1,24 @@
|
|||||||
// TODO
|
import { DirectiveTransform } from '../transform'
|
||||||
|
import { createObjectProperty, createExpression } from '../ast'
|
||||||
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
|
|
||||||
|
// v-bind without arg is handled directly in ./element.ts due to it affecting
|
||||||
|
// codegen for the entire props object. This transform here is only for v-bind
|
||||||
|
// *with* args.
|
||||||
|
export const transformBind: DirectiveTransform = (dir, context) => {
|
||||||
|
if (!dir.exp) {
|
||||||
|
context.onError(
|
||||||
|
createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, dir.loc)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// TODO handle .prop modifier
|
||||||
|
// TODO handle .sync modifier
|
||||||
|
return {
|
||||||
|
props: createObjectProperty(
|
||||||
|
dir.arg!,
|
||||||
|
dir.exp || createExpression('', true, dir.loc),
|
||||||
|
dir.loc
|
||||||
|
),
|
||||||
|
needRuntime: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -32,15 +32,12 @@ export const transformFor = createStructuralDirectiveTransform(
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
context.onError(
|
context.onError(
|
||||||
createCompilerError(
|
createCompilerError(ErrorCodes.X_FOR_MALFORMED_EXPRESSION, dir.loc)
|
||||||
ErrorCodes.X_FOR_MALFORMED_EXPRESSION,
|
|
||||||
dir.loc.start
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.onError(
|
context.onError(
|
||||||
createCompilerError(ErrorCodes.X_FOR_NO_EXPRESSION, dir.loc.start)
|
createCompilerError(ErrorCodes.X_FOR_NO_EXPRESSION, dir.loc)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ export const transformIf = createStructuralDirectiveTransform(
|
|||||||
dir.name === 'else'
|
dir.name === 'else'
|
||||||
? ErrorCodes.X_ELSE_NO_ADJACENT_IF
|
? ErrorCodes.X_ELSE_NO_ADJACENT_IF
|
||||||
: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
||||||
node.loc.start
|
node.loc
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1 +1,24 @@
|
|||||||
// TODO
|
import { DirectiveTransform } from '../transform'
|
||||||
|
import { createObjectProperty, createExpression } from '../ast'
|
||||||
|
import { capitalize } from '@vue/shared'
|
||||||
|
|
||||||
|
// v-on without arg is handled directly in ./element.ts due to it affecting
|
||||||
|
// codegen for the entire props object. This transform here is only for v-on
|
||||||
|
// *with* args.
|
||||||
|
export const transformOn: DirectiveTransform = (dir, context) => {
|
||||||
|
const arg = dir.arg!
|
||||||
|
const eventName = arg.isStatic
|
||||||
|
? createExpression(`on${capitalize(arg.content)}`, true, arg.loc)
|
||||||
|
: // TODO inject capitalize helper
|
||||||
|
createExpression(`'on' + capitalize(${arg.content})`, false, arg.loc)
|
||||||
|
// TODO .once modifier handling since it is platform agnostic
|
||||||
|
// other modifiers are handled in compiler-dom
|
||||||
|
return {
|
||||||
|
props: createObjectProperty(
|
||||||
|
eventName,
|
||||||
|
dir.exp || createExpression(`() => {}`, false, dir.loc),
|
||||||
|
dir.loc
|
||||||
|
),
|
||||||
|
needRuntime: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user