mirror of https://github.com/vuejs/core.git
feat(compiler-vapor): support custom directives argument & modifiers (#34)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
parent
924b9ad732
commit
0cca23f574
|
@ -16,6 +16,90 @@ export function render(_ctx) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`compile > directives > custom directive > basic 1`] = `
|
||||
"import { template as _template, children as _children, withDirectives as _withDirectives } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_withDirectives(n1, [[_ctx.vExample]])
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compile > directives > custom directive > binding value 1`] = `
|
||||
"import { template as _template, children as _children, withDirectives as _withDirectives } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_withDirectives(n1, [[_ctx.vExample, _ctx.msg]])
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compile > directives > custom directive > dynamic parameters 1`] = `
|
||||
"import { template as _template, children as _children, withDirectives as _withDirectives } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_withDirectives(n1, [[_ctx.vExample, _ctx.msg, _ctx.foo]])
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compile > directives > custom directive > modifiers 1`] = `
|
||||
"import { template as _template, children as _children, withDirectives as _withDirectives } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_withDirectives(n1, [[_ctx.vExample, _ctx.msg, void 0, { bar: true }]])
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compile > directives > custom directive > modifiers w/o binding 1`] = `
|
||||
"import { template as _template, children as _children, withDirectives as _withDirectives } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_withDirectives(n1, [[_ctx.vExample, void 0, void 0, { "foo-bar": true }]])
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compile > directives > custom directive > static parameters 1`] = `
|
||||
"import { template as _template, children as _children, withDirectives as _withDirectives } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_withDirectives(n1, [[_ctx.vExample, _ctx.msg, "foo"]])
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compile > directives > custom directive > static parameters and modifiers 1`] = `
|
||||
"import { template as _template, children as _children, withDirectives as _withDirectives } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_withDirectives(n1, [[_ctx.vExample, _ctx.msg, "foo", { bar: true }]])
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compile > directives > v-bind > .camel modifier 1`] = `
|
||||
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
|
||||
|
||||
|
|
|
@ -332,6 +332,76 @@ describe('compile', () => {
|
|||
expect(code).not.contains('v-cloak')
|
||||
})
|
||||
})
|
||||
|
||||
describe('custom directive', () => {
|
||||
test('basic', async () => {
|
||||
const code = await compile(`<div v-example></div>`, {
|
||||
bindingMetadata: {
|
||||
vExample: BindingTypes.SETUP_CONST,
|
||||
},
|
||||
})
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('binding value', async () => {
|
||||
const code = await compile(`<div v-example="msg"></div>`, {
|
||||
bindingMetadata: {
|
||||
msg: BindingTypes.SETUP_REF,
|
||||
vExample: BindingTypes.SETUP_CONST,
|
||||
},
|
||||
})
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('static parameters', async () => {
|
||||
const code = await compile(`<div v-example:foo="msg"></div>`, {
|
||||
bindingMetadata: {
|
||||
msg: BindingTypes.SETUP_REF,
|
||||
vExample: BindingTypes.SETUP_CONST,
|
||||
},
|
||||
})
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('modifiers', async () => {
|
||||
const code = await compile(`<div v-example.bar="msg"></div>`, {
|
||||
bindingMetadata: {
|
||||
msg: BindingTypes.SETUP_REF,
|
||||
vExample: BindingTypes.SETUP_CONST,
|
||||
},
|
||||
})
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('modifiers w/o binding', async () => {
|
||||
const code = await compile(`<div v-example.foo-bar></div>`, {
|
||||
bindingMetadata: {
|
||||
vExample: BindingTypes.SETUP_CONST,
|
||||
},
|
||||
})
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('static parameters and modifiers', async () => {
|
||||
const code = await compile(`<div v-example:foo.bar="msg"></div>`, {
|
||||
bindingMetadata: {
|
||||
msg: BindingTypes.SETUP_REF,
|
||||
vExample: BindingTypes.SETUP_CONST,
|
||||
},
|
||||
})
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('dynamic parameters', async () => {
|
||||
const code = await compile(`<div v-example:[foo]="msg"></div>`, {
|
||||
bindingMetadata: {
|
||||
foo: BindingTypes.SETUP_REF,
|
||||
vExample: BindingTypes.SETUP_CONST,
|
||||
},
|
||||
})
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('expression parsing', () => {
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
createSimpleExpression,
|
||||
walkIdentifiers,
|
||||
advancePositionWithClone,
|
||||
isSimpleIdentifier,
|
||||
} from '@vue/compiler-dom'
|
||||
import {
|
||||
type IRDynamicChildren,
|
||||
|
@ -469,21 +470,38 @@ function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
|
|||
|
||||
function genWithDirective(oper: WithDirectiveIRNode, context: CodegenContext) {
|
||||
const { push, pushWithNewline, vaporHelper, bindingMetadata } = context
|
||||
const { dir } = oper
|
||||
|
||||
// TODO merge directive for the same node
|
||||
pushWithNewline(`${vaporHelper('withDirectives')}(n${oper.element}, [[`)
|
||||
|
||||
// TODO resolve directive
|
||||
const directiveReference = camelize(`v-${oper.name}`)
|
||||
const directiveReference = camelize(`v-${dir.name}`)
|
||||
if (bindingMetadata[directiveReference]) {
|
||||
const directiveExpression = createSimpleExpression(directiveReference)
|
||||
directiveExpression.ast = null
|
||||
genExpression(directiveExpression, context)
|
||||
}
|
||||
|
||||
if (oper.binding) {
|
||||
if (dir.exp) {
|
||||
push(', ')
|
||||
genExpression(oper.binding, context)
|
||||
genExpression(dir.exp, context)
|
||||
} else if (dir.arg || dir.modifiers.length) {
|
||||
push(', void 0')
|
||||
}
|
||||
|
||||
if (dir.arg) {
|
||||
push(', ')
|
||||
genExpression(dir.arg, context)
|
||||
} else if (dir.modifiers.length) {
|
||||
push(', void 0')
|
||||
}
|
||||
|
||||
if (dir.modifiers.length) {
|
||||
push(', ')
|
||||
push('{ ')
|
||||
push(genDirectiveModifiers(dir.modifiers))
|
||||
push(' }')
|
||||
}
|
||||
push(']])')
|
||||
return
|
||||
|
@ -576,3 +594,12 @@ function genIdentifier(
|
|||
}
|
||||
push(id, NewlineType.None, loc, name)
|
||||
}
|
||||
|
||||
function genDirectiveModifiers(modifiers: string[]) {
|
||||
return modifiers
|
||||
.map(
|
||||
(value) =>
|
||||
`${isSimpleIdentifier(value) ? value : JSON.stringify(value)}: true`,
|
||||
)
|
||||
.join(', ')
|
||||
}
|
||||
|
|
|
@ -117,8 +117,7 @@ export interface AppendNodeIRNode extends BaseIRNode {
|
|||
export interface WithDirectiveIRNode extends BaseIRNode {
|
||||
type: IRNodeTypes.WITH_DIRECTIVE
|
||||
element: number
|
||||
name: string
|
||||
binding: IRExpression | undefined
|
||||
dir: VaporDirectiveNode
|
||||
}
|
||||
|
||||
export type IRNode =
|
||||
|
|
|
@ -59,7 +59,7 @@ function transformProp(
|
|||
node: ElementNode,
|
||||
context: TransformContext<ElementNode>,
|
||||
): void {
|
||||
const { name } = prop
|
||||
const { name, loc } = prop
|
||||
if (prop.type === NodeTypes.ATTRIBUTE) {
|
||||
context.template += ` ${name}`
|
||||
if (prop.value) context.template += `="${prop.value.content}"`
|
||||
|
@ -70,13 +70,11 @@ function transformProp(
|
|||
if (directiveTransform) {
|
||||
directiveTransform(prop, node, context)
|
||||
} else if (!isBuiltInDirective(name)) {
|
||||
// custom directive
|
||||
context.registerOperation({
|
||||
type: IRNodeTypes.WITH_DIRECTIVE,
|
||||
element: context.reference(),
|
||||
name,
|
||||
binding: prop.exp,
|
||||
loc: prop.loc,
|
||||
dir: prop,
|
||||
loc: loc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -279,7 +279,8 @@ export type {
|
|||
DirectiveHook,
|
||||
ObjectDirective,
|
||||
FunctionDirective,
|
||||
DirectiveArguments
|
||||
DirectiveArguments,
|
||||
DirectiveModifiers
|
||||
} from './directives'
|
||||
export type { SuspenseBoundary } from './components/Suspense'
|
||||
export type {
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import { isFunction } from '@vue/shared'
|
||||
import { currentInstance, type ComponentInternalInstance } from './component'
|
||||
|
||||
import type { DirectiveModifiers } from '@vue/runtime-dom'
|
||||
export interface DirectiveBinding<V = any> {
|
||||
instance: ComponentInternalInstance | null
|
||||
value: V
|
||||
oldValue: V | null
|
||||
arg?: string
|
||||
// TODO: should we support modifiers for custom directives?
|
||||
// modifiers: DirectiveModifiers
|
||||
modifiers?: DirectiveModifiers
|
||||
dir: ObjectDirective<any, V>
|
||||
}
|
||||
|
||||
|
@ -41,6 +40,12 @@ export type DirectiveArguments = Array<
|
|||
| [Directive | undefined]
|
||||
| [Directive | undefined, value: any]
|
||||
| [Directive | undefined, value: any, argument: string]
|
||||
| [
|
||||
Directive | undefined,
|
||||
value: any,
|
||||
argument: string,
|
||||
modifiers: DirectiveModifiers,
|
||||
]
|
||||
>
|
||||
|
||||
export function withDirectives<T extends Node>(
|
||||
|
@ -56,7 +61,7 @@ export function withDirectives<T extends Node>(
|
|||
const bindings = currentInstance.dirs.get(node)!
|
||||
|
||||
for (const directive of directives) {
|
||||
let [dir, value, arg] = directive
|
||||
let [dir, value, arg, modifiers] = directive
|
||||
if (!dir) continue
|
||||
if (isFunction(dir)) {
|
||||
// TODO function directive
|
||||
|
@ -71,6 +76,7 @@ export function withDirectives<T extends Node>(
|
|||
value,
|
||||
oldValue: void 0,
|
||||
arg,
|
||||
modifiers,
|
||||
}
|
||||
if (dir.created) dir.created(node, binding)
|
||||
bindings.push(binding)
|
||||
|
|
|
@ -21,5 +21,5 @@ const vDirective: ObjectDirective<HTMLDivElement, undefined> = {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div v-directive v-text="text" />
|
||||
<div v-directive:foo.bar="text" v-text="text" />
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue