test: tests for v-on transform
This commit is contained in:
parent
597ada36ed
commit
84909648e7
@ -76,9 +76,9 @@ return function render() {
|
|||||||
exports[`compiler: codegen ifNode 1`] = `
|
exports[`compiler: codegen ifNode 1`] = `
|
||||||
"return function render() {
|
"return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
return (foo)
|
return foo
|
||||||
? \\"foo\\"
|
? \\"foo\\"
|
||||||
: (bar)
|
: (a + b)
|
||||||
? toString(bye)
|
? toString(bye)
|
||||||
: createVNode(Comment, 0, \\"foo\\")
|
: createVNode(Comment, 0, \\"foo\\")
|
||||||
}
|
}
|
||||||
@ -88,9 +88,9 @@ exports[`compiler: codegen ifNode 1`] = `
|
|||||||
exports[`compiler: codegen ifNode with no v-else 1`] = `
|
exports[`compiler: codegen ifNode with no v-else 1`] = `
|
||||||
"return function render() {
|
"return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
return (foo)
|
return foo
|
||||||
? \\"foo\\"
|
? \\"foo\\"
|
||||||
: (bar)
|
: (a + b)
|
||||||
? toString(bye)
|
? toString(bye)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ describe('compiler: codegen', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: NodeTypes.IF_BRANCH,
|
type: NodeTypes.IF_BRANCH,
|
||||||
condition: createExpression('bar', false, mockLoc),
|
condition: createExpression('a + b', false, mockLoc),
|
||||||
loc: mockLoc,
|
loc: mockLoc,
|
||||||
isRoot: true,
|
isRoot: true,
|
||||||
children: [createExpression(`bye`, false, mockLoc, true)]
|
children: [createExpression(`bye`, false, mockLoc, true)]
|
||||||
@ -215,9 +215,9 @@ describe('compiler: codegen', () => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
expect(code).toMatch(`
|
expect(code).toMatch(`
|
||||||
return (foo)
|
return foo
|
||||||
? "foo"
|
? "foo"
|
||||||
: (bar)
|
: (a + b)
|
||||||
? ${TO_STRING}(bye)
|
? ${TO_STRING}(bye)
|
||||||
: ${CREATE_VNODE}(${COMMENT}, 0, "foo")`)
|
: ${CREATE_VNODE}(${COMMENT}, 0, "foo")`)
|
||||||
expect(code).toMatchSnapshot()
|
expect(code).toMatchSnapshot()
|
||||||
@ -248,7 +248,7 @@ describe('compiler: codegen', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: NodeTypes.IF_BRANCH,
|
type: NodeTypes.IF_BRANCH,
|
||||||
condition: createExpression('bar', false, mockLoc),
|
condition: createExpression('a + b', false, mockLoc),
|
||||||
loc: mockLoc,
|
loc: mockLoc,
|
||||||
isRoot: true,
|
isRoot: true,
|
||||||
children: [createExpression(`bye`, false, mockLoc, true)]
|
children: [createExpression(`bye`, false, mockLoc, true)]
|
||||||
@ -259,9 +259,9 @@ describe('compiler: codegen', () => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
expect(code).toMatch(`
|
expect(code).toMatch(`
|
||||||
return (foo)
|
return foo
|
||||||
? "foo"
|
? "foo"
|
||||||
: (bar)
|
: (a + b)
|
||||||
? ${TO_STRING}(bye)
|
? ${TO_STRING}(bye)
|
||||||
: null`)
|
: null`)
|
||||||
expect(code).toMatchSnapshot()
|
expect(code).toMatchSnapshot()
|
||||||
|
@ -1 +1,125 @@
|
|||||||
test.todo('v-on')
|
import {
|
||||||
|
parse,
|
||||||
|
transform,
|
||||||
|
ElementNode,
|
||||||
|
ObjectExpression,
|
||||||
|
CompilerOptions,
|
||||||
|
ErrorCodes
|
||||||
|
} from '../../src'
|
||||||
|
import { transformOn } from '../../src/transforms/vOn'
|
||||||
|
import { transformElement } from '../../src/transforms/transformElement'
|
||||||
|
import { transformExpression } from '../../src/transforms/transformExpression'
|
||||||
|
|
||||||
|
function parseWithVOn(
|
||||||
|
template: string,
|
||||||
|
options: CompilerOptions = {}
|
||||||
|
): ElementNode {
|
||||||
|
const ast = parse(template)
|
||||||
|
transform(ast, {
|
||||||
|
nodeTransforms: [transformExpression, transformElement],
|
||||||
|
directiveTransforms: {
|
||||||
|
on: transformOn
|
||||||
|
},
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
return ast.children[0] as ElementNode
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('compiler: transform v-bind', () => {
|
||||||
|
test('basic', () => {
|
||||||
|
const node = parseWithVOn(`<div v-on:click="onClick"/>`)
|
||||||
|
const props = node.codegenNode!.arguments[1] as ObjectExpression
|
||||||
|
expect(props.properties[0]).toMatchObject({
|
||||||
|
key: {
|
||||||
|
content: `onClick`,
|
||||||
|
isStatic: true,
|
||||||
|
loc: {
|
||||||
|
start: {
|
||||||
|
line: 1,
|
||||||
|
column: 11
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
line: 1,
|
||||||
|
column: 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
content: `onClick`,
|
||||||
|
isStatic: false,
|
||||||
|
loc: {
|
||||||
|
start: {
|
||||||
|
line: 1,
|
||||||
|
column: 17
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
line: 1,
|
||||||
|
column: 26
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loc: {
|
||||||
|
start: {
|
||||||
|
line: 1,
|
||||||
|
column: 6
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
line: 1,
|
||||||
|
column: 26
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dynamic arg', () => {
|
||||||
|
const node = parseWithVOn(`<div v-on:[event]="handler"/>`)
|
||||||
|
const props = node.codegenNode!.arguments[1] as ObjectExpression
|
||||||
|
expect(props.properties[0]).toMatchObject({
|
||||||
|
key: {
|
||||||
|
content: `"on" + event`,
|
||||||
|
isStatic: false
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
content: `handler`,
|
||||||
|
isStatic: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dynamic arg with prefixing', () => {
|
||||||
|
const node = parseWithVOn(`<div v-on:[event]="handler"/>`, {
|
||||||
|
prefixIdentifiers: true
|
||||||
|
})
|
||||||
|
const props = node.codegenNode!.arguments[1] as ObjectExpression
|
||||||
|
expect(props.properties[0]).toMatchObject({
|
||||||
|
key: {
|
||||||
|
isStatic: false,
|
||||||
|
children: [`"on" + `, `_ctx.`, { content: `event` }]
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
content: `handler`,
|
||||||
|
isStatic: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should error if no expression', () => {
|
||||||
|
const onError = jest.fn()
|
||||||
|
parseWithVOn(`<div v-on />`, { onError })
|
||||||
|
expect(onError.mock.calls[0][0]).toMatchObject({
|
||||||
|
code: ErrorCodes.X_V_ON_NO_EXPRESSION,
|
||||||
|
loc: {
|
||||||
|
start: {
|
||||||
|
line: 1,
|
||||||
|
column: 6
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
line: 1,
|
||||||
|
column: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test.todo('.once modifier')
|
||||||
|
})
|
||||||
|
@ -15,7 +15,11 @@ import {
|
|||||||
IfBranchNode
|
IfBranchNode
|
||||||
} from './ast'
|
} from './ast'
|
||||||
import { SourceMapGenerator, RawSourceMap } from 'source-map'
|
import { SourceMapGenerator, RawSourceMap } from 'source-map'
|
||||||
import { advancePositionWithMutation, assert } from './utils'
|
import {
|
||||||
|
advancePositionWithMutation,
|
||||||
|
assert,
|
||||||
|
isSimpleIdentifier
|
||||||
|
} from './utils'
|
||||||
import { isString, isArray } from '@vue/shared'
|
import { isString, isArray } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
RENDER_LIST,
|
RENDER_LIST,
|
||||||
@ -325,7 +329,7 @@ function genExpressionAsPropertyKey(
|
|||||||
push(`]`)
|
push(`]`)
|
||||||
} else if (isStatic) {
|
} else if (isStatic) {
|
||||||
// only quote keys if necessary
|
// only quote keys if necessary
|
||||||
const text = /^\d|[^\w]/.test(content) ? JSON.stringify(content) : content
|
const text = isSimpleIdentifier(content) ? content : JSON.stringify(content)
|
||||||
push(text, node)
|
push(text, node)
|
||||||
} else {
|
} else {
|
||||||
push(`[${content}]`, node)
|
push(`[${content}]`, node)
|
||||||
@ -366,9 +370,10 @@ function genIfBranch(
|
|||||||
if (condition) {
|
if (condition) {
|
||||||
// v-if or v-else-if
|
// v-if or v-else-if
|
||||||
const { push, indent, deindent, newline } = context
|
const { push, indent, deindent, newline } = context
|
||||||
push(`(`)
|
const needsQuote = !isSimpleIdentifier(condition.content)
|
||||||
|
needsQuote && push(`(`)
|
||||||
genExpression(condition, context)
|
genExpression(condition, context)
|
||||||
push(`)`)
|
needsQuote && push(`)`)
|
||||||
indent()
|
indent()
|
||||||
context.indentLevel++
|
context.indentLevel++
|
||||||
push(`? `)
|
push(`? `)
|
||||||
|
@ -17,22 +17,21 @@ export function compile(
|
|||||||
template: string | RootNode,
|
template: string | RootNode,
|
||||||
options: CompilerOptions = {}
|
options: CompilerOptions = {}
|
||||||
): CodegenResult {
|
): CodegenResult {
|
||||||
const ast = isString(template) ? parse(template, options) : template
|
if (__BROWSER__ && options.prefixIdentifiers) {
|
||||||
const prefixIdentifiers = !__BROWSER__ && options.prefixIdentifiers === true
|
|
||||||
|
|
||||||
if (__BROWSER__ && options.prefixIdentifiers === false) {
|
|
||||||
;(options.onError || defaultOnError)(
|
;(options.onError || defaultOnError)(
|
||||||
createCompilerError(ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED)
|
createCompilerError(ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ast = isString(template) ? parse(template, options) : template
|
||||||
|
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
...options,
|
...options,
|
||||||
prefixIdentifiers,
|
prefixIdentifiers: !__BROWSER__ && options.prefixIdentifiers === true,
|
||||||
nodeTransforms: [
|
nodeTransforms: [
|
||||||
transformIf,
|
transformIf,
|
||||||
transformFor,
|
transformFor,
|
||||||
...(prefixIdentifiers ? [transformExpression] : []),
|
transformExpression,
|
||||||
transformElement,
|
transformElement,
|
||||||
...(options.nodeTransforms || []) // user transforms
|
...(options.nodeTransforms || []) // user transforms
|
||||||
],
|
],
|
||||||
|
@ -56,6 +56,9 @@ export function processExpression(
|
|||||||
node: ExpressionNode,
|
node: ExpressionNode,
|
||||||
context: TransformContext
|
context: TransformContext
|
||||||
) {
|
) {
|
||||||
|
if (!context.prefixIdentifiers) {
|
||||||
|
return
|
||||||
|
}
|
||||||
// lazy require dependencies so that they don't end up in rollup's dep graph
|
// lazy require dependencies so that they don't end up in rollup's dep graph
|
||||||
// and thus can be tree-shaken in browser builds.
|
// and thus can be tree-shaken in browser builds.
|
||||||
const parseScript =
|
const parseScript =
|
||||||
@ -155,6 +158,9 @@ export function processExpression(
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (children.length) {
|
if (children.length) {
|
||||||
|
// mark it empty so that it's more noticeable in case another transform or
|
||||||
|
// codegen forget to handle `.children` first.
|
||||||
|
node.content = __DEV__ ? `[[REMOVED]]` : ``
|
||||||
node.children = children
|
node.children = children
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,33 @@
|
|||||||
import { DirectiveTransform } from '../transform'
|
import { DirectiveTransform } from '../transform'
|
||||||
import { createObjectProperty, createExpression } from '../ast'
|
import { createObjectProperty, createExpression, ExpressionNode } from '../ast'
|
||||||
import { capitalize } from '@vue/shared'
|
import { capitalize } from '@vue/shared'
|
||||||
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
|
import { isSimpleIdentifier } from '../utils'
|
||||||
|
|
||||||
// v-on without arg is handled directly in ./element.ts due to it affecting
|
// 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
|
// codegen for the entire props object. This transform here is only for v-on
|
||||||
// *with* args.
|
// *with* args.
|
||||||
export const transformOn: DirectiveTransform = ({ arg, exp, loc }) => {
|
export const transformOn: DirectiveTransform = ({ arg, exp, loc }, context) => {
|
||||||
const eventName = arg!.isStatic
|
if (!exp) {
|
||||||
? createExpression(`on${capitalize(arg!.content)}`, true, arg!.loc)
|
context.onError(createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc))
|
||||||
: createExpression(`'on' + (${arg!.content})`, false, arg!.loc)
|
}
|
||||||
|
const { content, children, isStatic, loc: argLoc } = arg!
|
||||||
|
let eventName: ExpressionNode
|
||||||
|
if (isStatic) {
|
||||||
|
// static arg
|
||||||
|
eventName = createExpression(`on${capitalize(content)}`, true, argLoc)
|
||||||
|
} else if (!children) {
|
||||||
|
// dynamic arg with no rewrite
|
||||||
|
eventName = createExpression(
|
||||||
|
`"on" + ${isSimpleIdentifier(content) ? content : `(${content})`}`,
|
||||||
|
false,
|
||||||
|
argLoc
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// dynamic arg with ctx prefixing
|
||||||
|
eventName = arg!
|
||||||
|
children.unshift(`"on" + `)
|
||||||
|
}
|
||||||
// TODO .once modifier handling since it is platform agnostic
|
// TODO .once modifier handling since it is platform agnostic
|
||||||
// other modifiers are handled in compiler-dom
|
// other modifiers are handled in compiler-dom
|
||||||
return {
|
return {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { SourceLocation, Position } from './ast'
|
import { SourceLocation, Position } from './ast'
|
||||||
|
|
||||||
|
export const isSimpleIdentifier = (name: string): boolean =>
|
||||||
|
!/^\d|[^\w]/.test(name)
|
||||||
|
|
||||||
export function getInnerRange(
|
export function getInnerRange(
|
||||||
loc: SourceLocation,
|
loc: SourceLocation,
|
||||||
offset: number,
|
offset: number,
|
||||||
|
Loading…
Reference in New Issue
Block a user