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,
|
||||
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,
|
||||
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,
|
||||
loc: { offset: 0, line: 1, column: 1 }
|
||||
loc: {
|
||||
start: { offset: 0, line: 1, column: 1 }
|
||||
}
|
||||
}
|
||||
]
|
||||
])
|
||||
@ -1254,21 +1260,25 @@ describe('compiler: parse', () => {
|
||||
{
|
||||
code: ErrorCodes.X_MISSING_END_TAG,
|
||||
loc: {
|
||||
start: {
|
||||
offset: 13,
|
||||
line: 3,
|
||||
column: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
code: ErrorCodes.X_INVALID_END_TAG,
|
||||
loc: {
|
||||
start: {
|
||||
offset: 20,
|
||||
line: 4,
|
||||
column: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
@ -2386,7 +2396,7 @@ describe('compiler: parse', () => {
|
||||
expect(
|
||||
spy.mock.calls.map(([err]) => ({
|
||||
type: err.code,
|
||||
loc: err.loc
|
||||
loc: err.loc.start
|
||||
}))
|
||||
).toMatchObject(errors)
|
||||
expect(ast).toMatchSnapshot()
|
||||
|
@ -162,10 +162,10 @@ describe('compiler: transform', () => {
|
||||
|
||||
test('onError option', () => {
|
||||
const ast = parse(`<div/>`)
|
||||
const loc = ast.children[0].loc.start
|
||||
const loc = ast.children[0].loc
|
||||
const plugin: NodeTransform = (node, context) => {
|
||||
context.onError(
|
||||
createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc.start)
|
||||
createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc)
|
||||
)
|
||||
}
|
||||
const spy = jest.fn()
|
||||
|
@ -0,0 +1,3 @@
|
||||
describe('compiler: element transform', () => {
|
||||
test.todo('should work')
|
||||
})
|
@ -4,8 +4,7 @@ import { transformFor } from '../../src/transforms/vFor'
|
||||
import { ForNode, NodeTypes } from '../../src/ast'
|
||||
import { ErrorCodes } from '../../src/errors'
|
||||
|
||||
describe('v-for', () => {
|
||||
describe('transform', () => {
|
||||
describe('compiler: transform v-for', () => {
|
||||
test('number expression', () => {
|
||||
const node = parse('<span v-for="index in 5" />')
|
||||
|
||||
@ -306,18 +305,14 @@ describe('v-for', () => {
|
||||
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.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.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)
|
||||
@ -341,18 +336,14 @@ describe('v-for', () => {
|
||||
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.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.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)
|
||||
@ -385,9 +376,7 @@ describe('v-for', () => {
|
||||
)
|
||||
|
||||
expect(forNode.source.content).toBe('items')
|
||||
expect(forNode.source.loc.start.offset).toBe(
|
||||
source.indexOf('items') - 1
|
||||
)
|
||||
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)
|
||||
@ -411,18 +400,14 @@ describe('v-for', () => {
|
||||
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.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.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)
|
||||
@ -442,9 +427,7 @@ describe('v-for', () => {
|
||||
)
|
||||
|
||||
expect(forNode.source.content).toBe('items')
|
||||
expect(forNode.source.loc.start.offset).toBe(
|
||||
source.indexOf('items') - 1
|
||||
)
|
||||
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)
|
||||
@ -468,9 +451,7 @@ describe('v-for', () => {
|
||||
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.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
|
||||
@ -490,14 +471,11 @@ describe('v-for', () => {
|
||||
)
|
||||
|
||||
expect(forNode.source.content).toBe('items')
|
||||
expect(forNode.source.loc.start.offset).toBe(
|
||||
source.indexOf('items') - 1
|
||||
)
|
||||
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,8 +10,7 @@ import {
|
||||
} from '../../src/ast'
|
||||
import { ErrorCodes } from '../../src/errors'
|
||||
|
||||
describe('compiler: v-if', () => {
|
||||
describe('transform', () => {
|
||||
describe('compiler: transform v-if', () => {
|
||||
test('basic v-if', () => {
|
||||
const ast = parse(`<div v-if="ok"/>`)
|
||||
transform(ast, {
|
||||
@ -178,7 +177,7 @@ describe('compiler: v-if', () => {
|
||||
expect(spy.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
||||
loc: ast.children[0].loc.start
|
||||
loc: ast.children[0].loc
|
||||
}
|
||||
])
|
||||
|
||||
@ -191,7 +190,7 @@ describe('compiler: v-if', () => {
|
||||
expect(spy2.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
||||
loc: ast2.children[1].loc.start
|
||||
loc: ast2.children[1].loc
|
||||
}
|
||||
])
|
||||
|
||||
@ -204,7 +203,7 @@ describe('compiler: v-if', () => {
|
||||
expect(spy3.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
||||
loc: ast3.children[2].loc.start
|
||||
loc: ast3.children[2].loc
|
||||
}
|
||||
])
|
||||
})
|
||||
@ -219,7 +218,7 @@ describe('compiler: v-if', () => {
|
||||
expect(spy.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
||||
loc: ast.children[0].loc.start
|
||||
loc: ast.children[0].loc
|
||||
}
|
||||
])
|
||||
|
||||
@ -232,7 +231,7 @@ describe('compiler: v-if', () => {
|
||||
expect(spy2.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
||||
loc: ast2.children[1].loc.start
|
||||
loc: ast2.children[1].loc
|
||||
}
|
||||
])
|
||||
|
||||
@ -245,13 +244,8 @@ describe('compiler: v-if', () => {
|
||||
expect(spy3.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
||||
loc: ast3.children[2].loc.start
|
||||
loc: ast3.children[2].loc
|
||||
}
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('codegen', () => {
|
||||
// TODO
|
||||
})
|
||||
})
|
||||
|
@ -366,7 +366,7 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
|
||||
const { push, indent, deindent, newline } = context
|
||||
const { properties } = node
|
||||
const multilines = properties.length > 1
|
||||
push(`{`, node)
|
||||
push(multilines ? `{` : `{ `, node)
|
||||
multilines && indent()
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
const { key, value } = properties[i]
|
||||
@ -385,7 +385,7 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
|
||||
}
|
||||
}
|
||||
multilines && deindent()
|
||||
push(`}`)
|
||||
push(multilines ? `}` : ` }`)
|
||||
}
|
||||
|
||||
function genArrayExpression(node: ArrayExpression, context: CodegenContext) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Position } from './ast'
|
||||
import { SourceLocation } from './ast'
|
||||
|
||||
export interface CompilerError extends SyntaxError {
|
||||
code: ErrorCodes
|
||||
loc: Position
|
||||
loc: SourceLocation
|
||||
}
|
||||
|
||||
export function defaultOnError(error: CompilerError) {
|
||||
@ -11,12 +11,12 @@ export function defaultOnError(error: CompilerError) {
|
||||
|
||||
export function createCompilerError(
|
||||
code: ErrorCodes,
|
||||
loc: Position
|
||||
loc: SourceLocation
|
||||
): CompilerError {
|
||||
const error = new SyntaxError(
|
||||
`${__DEV__ || !__BROWSER__ ? errorMessages[code] : code} (${loc.line}:${
|
||||
loc.column
|
||||
})`
|
||||
`${__DEV__ || !__BROWSER__ ? errorMessages[code] : code} (${
|
||||
loc.start.line
|
||||
}:${loc.start.column})`
|
||||
) as CompilerError
|
||||
error.code = code
|
||||
error.loc = loc
|
||||
|
@ -6,6 +6,8 @@ import { isString } from '@vue/shared'
|
||||
import { transformIf } from './transforms/vIf'
|
||||
import { transformFor } from './transforms/vFor'
|
||||
import { prepareElementForCodegen } from './transforms/element'
|
||||
import { transformOn } from './transforms/vOn'
|
||||
import { transformBind } from './transforms/vBind'
|
||||
|
||||
export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions
|
||||
|
||||
@ -24,7 +26,8 @@ export function compile(
|
||||
...(options.nodeTransforms || []) // user transforms
|
||||
],
|
||||
directiveTransforms: {
|
||||
// TODO include built-in directive transforms
|
||||
on: transformOn,
|
||||
bind: transformBind,
|
||||
...(options.directiveTransforms || {}) // user transforms
|
||||
}
|
||||
})
|
||||
|
@ -842,7 +842,13 @@ function emitError(
|
||||
loc.offset += offset
|
||||
loc.column += offset
|
||||
}
|
||||
context.options.onError(createCompilerError(code, loc))
|
||||
context.options.onError(
|
||||
createCompilerError(code, {
|
||||
start: loc,
|
||||
end: loc,
|
||||
source: ''
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function isEnd(
|
||||
|
@ -123,10 +123,7 @@ function buildProps(
|
||||
mergeArgs.push(prop.exp)
|
||||
} else {
|
||||
context.onError(
|
||||
createCompilerError(
|
||||
ErrorCodes.X_V_BIND_NO_EXPRESSION,
|
||||
prop.loc.start
|
||||
)
|
||||
createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, prop.loc)
|
||||
)
|
||||
}
|
||||
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 {
|
||||
context.onError(
|
||||
createCompilerError(
|
||||
ErrorCodes.X_FOR_MALFORMED_EXPRESSION,
|
||||
dir.loc.start
|
||||
)
|
||||
createCompilerError(ErrorCodes.X_FOR_MALFORMED_EXPRESSION, dir.loc)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
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'
|
||||
? ErrorCodes.X_ELSE_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…
Reference in New Issue
Block a user