diff --git a/packages/compiler-core/__tests__/parse.spec.ts b/packages/compiler-core/__tests__/parse.spec.ts
index 4e5a9616511..1b37526daba 100644
--- a/packages/compiler-core/__tests__/parse.spec.ts
+++ b/packages/compiler-core/__tests__/parse.spec.ts
@@ -1446,6 +1446,202 @@ describe('compiler: parse', () => {
})
})
+ test('directive with dynamic modifiers', () => {
+ const ast = baseParse('
')
+ const directive = (ast.children[0] as ElementNode).props[0]
+
+ expect(directive).toStrictEqual({
+ type: NodeTypes.DIRECTIVE,
+ name: 'on',
+ rawName: 'v-on.enter.[a]',
+ arg: undefined,
+ modifiers: [
+ {
+ constType: 3,
+ content: 'enter',
+ isStatic: true,
+ loc: {
+ end: {
+ column: 16,
+ line: 1,
+ offset: 15,
+ },
+ source: 'enter',
+ start: {
+ column: 11,
+ line: 1,
+ offset: 10,
+ },
+ },
+ type: 4,
+ },
+ {
+ constType: 0,
+ content: 'a',
+ isStatic: false,
+ loc: {
+ end: {
+ column: 20,
+ line: 1,
+ offset: 19,
+ },
+ source: '[a]',
+ start: {
+ column: 17,
+ line: 1,
+ offset: 16,
+ },
+ },
+ type: 4,
+ },
+ ],
+ exp: undefined,
+ loc: {
+ start: { offset: 5, line: 1, column: 6 },
+ end: { column: 20, line: 1, offset: 19 },
+ source: 'v-on.enter.[a]',
+ },
+ })
+ })
+
+ test('directive with empty modifier name', () => {
+ let errorCode = -1
+ const ast = baseParse('', {
+ onError: err => {
+ errorCode = err.code as number
+ },
+ })
+ const directive = (ast.children[0] as ElementNode).props[0]
+
+ expect(errorCode).toBe(ErrorCodes.X_MISSING_DIRECTIVE_MODIFIER_NAME)
+
+ expect(directive).toStrictEqual({
+ type: NodeTypes.DIRECTIVE,
+ name: 'on',
+ rawName: 'v-on.',
+ arg: undefined,
+ modifiers: [],
+ exp: undefined,
+ loc: {
+ start: { offset: 5, line: 1, column: 6 },
+ end: { column: 11, line: 1, offset: 10 },
+ source: 'v-on.',
+ },
+ })
+ })
+
+ test('directive with empty modifier name and value', () => {
+ let errorCode = -1
+ const ast = baseParse('', {
+ onError: err => {
+ errorCode = err.code as number
+ },
+ })
+ const directive = (ast.children[0] as ElementNode).props[0]
+
+ expect(errorCode).toBe(ErrorCodes.X_MISSING_DIRECTIVE_MODIFIER_NAME)
+
+ expect(directive).toStrictEqual({
+ type: NodeTypes.DIRECTIVE,
+ name: 'on',
+ rawName: 'v-on.',
+ arg: undefined,
+ modifiers: [],
+ exp: {
+ constType: 0,
+ content: 'a',
+ isStatic: false,
+ loc: {
+ end: {
+ column: 14,
+ line: 1,
+ offset: 13,
+ },
+ source: 'a',
+ start: {
+ column: 13,
+ line: 1,
+ offset: 12,
+ },
+ },
+ type: 4,
+ },
+ loc: {
+ start: { offset: 5, line: 1, column: 6 },
+ end: { column: 15, line: 1, offset: 14 },
+ source: 'v-on.="a"',
+ },
+ })
+ })
+
+ test('directive with missing dynamic modifier value', () => {
+ let errorCode = -1
+ const ast = baseParse('', {
+ onError: err => {
+ errorCode = err.code as number
+ },
+ })
+ const directive = (ast.children[0] as ElementNode).props[0]
+
+ expect(errorCode).toBe(
+ ErrorCodes.X_MISSING_DYNAMIC_DIRECTIVE_MODIFIER_VALUE,
+ )
+
+ expect(directive).toStrictEqual({
+ type: NodeTypes.DIRECTIVE,
+ name: 'on',
+ rawName: 'v-on.[]',
+ arg: undefined,
+ modifiers: [],
+ exp: undefined,
+ loc: {
+ start: { offset: 5, line: 1, column: 6 },
+ end: { column: 13, line: 1, offset: 12 },
+ source: 'v-on.[]',
+ },
+ })
+ })
+
+ test('directive with invalid dynamic modifier value', () => {
+ const possibleWrongValues = [
+ '[]',
+ 'null',
+ '123',
+ '"foo"',
+ '`foo`',
+ '!false',
+ ]
+
+ possibleWrongValues.forEach(val => {
+ let errorCode = -1
+ const ast = baseParse(``, {
+ onError: err => {
+ errorCode = err.code as number
+ },
+ prefixIdentifiers: true,
+ })
+ const directive = (ast.children[0] as ElementNode).props[0]
+
+ expect(errorCode).toBe(
+ ErrorCodes.X_INVALID_VALUE_IN_DYNAMIC_DIRECTIVE_MODIFIER,
+ )
+
+ expect(directive).toStrictEqual({
+ type: NodeTypes.DIRECTIVE,
+ name: 'on',
+ rawName: `v-on.[${val}]`,
+ arg: undefined,
+ modifiers: [],
+ exp: undefined,
+ loc: {
+ end: { column: 13 + val.length, line: 1, offset: 12 + val.length },
+ source: `v-on.[${val}]`,
+ start: { column: 6, line: 1, offset: 5 },
+ },
+ })
+ })
+ })
+
test('directive with argument and modifiers', () => {
const ast = baseParse('')
const directive = (ast.children[0] as ElementNode).props[0]
diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
index bf3510a052d..662954b3a90 100644
--- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
@@ -664,12 +664,13 @@ describe('compiler: element transform', () => {
test('runtime directives', () => {
const { root, node } = parseWithElementTransform(
- ``,
+ ``,
)
expect(root.helpers).toContain(RESOLVE_DIRECTIVE)
expect(root.directives).toContain(`foo`)
expect(root.directives).toContain(`bar`)
expect(root.directives).toContain(`baz`)
+ expect(root.directives).toContain(`baa`)
expect(node).toMatchObject({
directives: {
@@ -708,33 +709,54 @@ describe('compiler: element transform', () => {
},
// modifiers
{
- type: NodeTypes.JS_OBJECT_EXPRESSION,
- properties: [
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: '{ mod: true, mad: true }',
+ },
+ ],
+ },
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION,
+ elements: [
+ `_directive_baa`,
+ // exp
+ {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: `z`,
+ isStatic: false,
+ },
+ //arg
+ 'void 0',
+ // modifiers
+ {
+ type: NodeTypes.JS_CALL_EXPRESSION,
+ callee: 'Object.assign',
+ arguments: [
+ createObjectMatcher({}),
{
- type: NodeTypes.JS_PROPERTY,
- key: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `mod`,
- isStatic: true,
- },
- value: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `true`,
- isStatic: false,
- },
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: `{dyn:true}`,
+ },
+ {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: `mid`,
},
{
- type: NodeTypes.JS_PROPERTY,
- key: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `mad`,
- isStatic: true,
- },
- value: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `true`,
- isStatic: false,
- },
+ type: NodeTypes.JS_OBJECT_EXPRESSION,
+ properties: [
+ {
+ type: NodeTypes.JS_PROPERTY,
+ key: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: `boo`,
+ isStatic: true,
+ },
+ value: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: `true`,
+ isStatic: false,
+ },
+ },
+ ],
},
],
},
diff --git a/packages/compiler-core/__tests__/transforms/vModel.spec.ts b/packages/compiler-core/__tests__/transforms/vModel.spec.ts
index 82dd4909fd6..4f4202a45b7 100644
--- a/packages/compiler-core/__tests__/transforms/vModel.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/vModel.spec.ts
@@ -507,6 +507,42 @@ describe('compiler: transform v-model', () => {
)
})
+ test('should generate modelModifiers for component v-model with dynamic modifiers', () => {
+ const root = parseWithVModel('', {
+ prefixIdentifiers: true,
+ })
+ const vnodeCall = (root.children[0] as ComponentNode)
+ .codegenNode as VNodeCall
+ // props
+ expect(vnodeCall.props).toMatchObject({
+ properties: [
+ { key: { content: `modelValue` } },
+ { key: { content: `onUpdate:modelValue` } },
+ {
+ key: { content: 'modelModifiers' },
+ value: {
+ arguments: [
+ {
+ properties: [
+ {
+ key: { content: 'trim' },
+ value: { content: 'true', isStatic: false },
+ },
+ ],
+ },
+ { content: '_ctx.bar', isStatic: false },
+ ],
+ },
+ },
+ ],
+ })
+ // should now include modelModifiers in dynamicPropNames because it's
+ // gonna change
+ expect(vnodeCall.dynamicProps).toBe(
+ `["modelValue", "onUpdate:modelValue", "modelModifiers"]`,
+ )
+ })
+
describe('errors', () => {
test('missing expression', () => {
const onError = vi.fn()
diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts
index 2d6df9d9010..d17b2c38224 100644
--- a/packages/compiler-core/src/ast.ts
+++ b/packages/compiler-core/src/ast.ts
@@ -203,7 +203,7 @@ export interface DirectiveNode extends Node {
rawName?: string
exp: ExpressionNode | undefined
arg: ExpressionNode | undefined
- modifiers: SimpleExpressionNode[]
+ modifiers: ExpressionNode[]
/**
* optional property to cache the expression parse result for v-for
*/
diff --git a/packages/compiler-core/src/errors.ts b/packages/compiler-core/src/errors.ts
index 58e113ab19e..dab4ee89d26 100644
--- a/packages/compiler-core/src/errors.ts
+++ b/packages/compiler-core/src/errors.ts
@@ -69,6 +69,10 @@ export enum ErrorCodes {
X_MISSING_INTERPOLATION_END,
X_MISSING_DIRECTIVE_NAME,
X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END,
+ X_MISSING_DYNAMIC_DIRECTIVE_MODIFIER_END,
+ X_MISSING_DIRECTIVE_MODIFIER_NAME,
+ X_MISSING_DYNAMIC_DIRECTIVE_MODIFIER_VALUE,
+ X_INVALID_VALUE_IN_DYNAMIC_DIRECTIVE_MODIFIER,
// transform errors
X_V_IF_NO_EXPRESSION,
@@ -150,6 +154,16 @@ export const errorMessages: Record = {
[ErrorCodes.X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END]:
'End bracket for dynamic directive argument was not found. ' +
'Note that dynamic directive argument cannot contain spaces.',
+ [ErrorCodes.X_MISSING_DYNAMIC_DIRECTIVE_MODIFIER_END]:
+ 'End bracket for dynamic directive modifier was not found. ' +
+ 'Note that dynamic directive modifier cannot contain spaces.',
+ [ErrorCodes.X_MISSING_DIRECTIVE_MODIFIER_NAME]:
+ 'Directive modifier name cannot be empty. ',
+ [ErrorCodes.X_MISSING_DYNAMIC_DIRECTIVE_MODIFIER_VALUE]:
+ 'Dynamic directive modifier value cannot be empty. ',
+ [ErrorCodes.X_INVALID_VALUE_IN_DYNAMIC_DIRECTIVE_MODIFIER]:
+ 'Invalid value in dynamic directive modifier. ' +
+ 'Note that dynamic directive modifier can only be objects or arrays.',
[ErrorCodes.X_MISSING_DIRECTIVE_NAME]: 'Legal directive name was expected.',
// transform errors
diff --git a/packages/compiler-core/src/parser.ts b/packages/compiler-core/src/parser.ts
index 95c5e129f25..c65428bb495 100644
--- a/packages/compiler-core/src/parser.ts
+++ b/packages/compiler-core/src/parser.ts
@@ -274,7 +274,44 @@ const tokenizer = new Tokenizer(stack, {
setLocEnd(arg.loc, end)
}
} else {
- const exp = createSimpleExpression(mod, true, getLoc(start, end))
+ const isStatic = mod[0] !== `[`
+ const exp = createExp(
+ isStatic ? mod : mod.slice(1, -1),
+ isStatic,
+ getLoc(start, end),
+ isStatic ? ConstantTypes.CAN_STRINGIFY : ConstantTypes.NOT_CONSTANT,
+ )
+
+ if (!exp.ast && !exp.content.trim()) {
+ emitError(
+ isStatic ? ErrorCodes.X_MISSING_DIRECTIVE_MODIFIER_NAME :
+ ErrorCodes.X_MISSING_DYNAMIC_DIRECTIVE_MODIFIER_VALUE,
+ exp.loc.start.offset,
+ )
+ return
+ }
+
+ const invalidBabelNodeTypes = [
+ 'ArrayExpression',
+ 'UnaryExpression',
+ 'StringLiteral',
+ 'NumericLiteral',
+ 'TemplateLiteral',
+ ]
+
+ const invalidSimpleValues = ['true', 'false', 'null', 'undefined']
+
+ if (
+ (exp.ast && invalidBabelNodeTypes.includes(exp.ast.type)) ||
+ (!exp.ast && invalidSimpleValues.includes(exp.content))
+ ) {
+ emitError(
+ ErrorCodes.X_INVALID_VALUE_IN_DYNAMIC_DIRECTIVE_MODIFIER,
+ exp.loc.start.offset + 1,
+ )
+ return
+ }
+
;(currentProp as DirectiveNode).modifiers.push(exp)
}
},
@@ -382,7 +419,7 @@ const tokenizer = new Tokenizer(stack, {
__COMPAT__ &&
currentProp.name === 'bind' &&
(syncIndex = currentProp.modifiers.findIndex(
- mod => mod.content === 'sync',
+ mod => (mod as SimpleExpressionNode).content === 'sync',
)) > -1 &&
checkCompatEnabled(
CompilerDeprecationTypes.COMPILER_V_BIND_SYNC,
diff --git a/packages/compiler-core/src/tokenizer.ts b/packages/compiler-core/src/tokenizer.ts
index 329e8b48181..dfa8a43335f 100644
--- a/packages/compiler-core/src/tokenizer.ts
+++ b/packages/compiler-core/src/tokenizer.ts
@@ -107,6 +107,7 @@ export enum State {
InDirArg,
InDirDynamicArg,
InDirModifier,
+ InDirDynamicModifier,
AfterAttrName,
BeforeAttrValue,
InAttrValueDq, // "
@@ -749,11 +750,27 @@ export default class Tokenizer {
if (c === CharCodes.Eq || isEndOfTagSection(c)) {
this.cbs.ondirmodifier(this.sectionStart, this.index)
this.handleAttrNameEnd(c)
+ } else if (c === CharCodes.LeftSquare) {
+ this.state = State.InDirDynamicModifier
} else if (c === CharCodes.Dot) {
this.cbs.ondirmodifier(this.sectionStart, this.index)
this.sectionStart = this.index + 1
}
}
+ private stateInDirDynamicModifier(c: number): void {
+ if (c === CharCodes.RightSquare) {
+ this.state = State.InDirModifier
+ } else if (c === CharCodes.Eq || isEndOfTagSection(c)) {
+ this.cbs.ondirmodifier(this.sectionStart, this.index + 1)
+ this.handleAttrNameEnd(c)
+ if (__DEV__ || !__BROWSER__) {
+ this.cbs.onerr(
+ ErrorCodes.X_MISSING_DYNAMIC_DIRECTIVE_MODIFIER_END,
+ this.index,
+ )
+ }
+ }
+ }
private handleAttrNameEnd(c: number): void {
this.sectionStart = this.index
this.state = State.AfterAttrName
@@ -985,6 +1002,10 @@ export default class Tokenizer {
this.stateInDirModifier(c)
break
}
+ case State.InDirDynamicModifier: {
+ this.stateInDirDynamicModifier(c)
+ break
+ }
case State.InCommentLike: {
this.stateInCommentLike(c)
break
diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts
index aeb96cc2b4a..48bc55a59dd 100644
--- a/packages/compiler-core/src/transform.ts
+++ b/packages/compiler-core/src/transform.ts
@@ -338,7 +338,7 @@ export function transform(root: RootNode, options: TransformOptions): void {
createRootCodegen(root, context)
}
// finalize meta information
- root.helpers = new Set([...context.helpers.keys()])
+ root.helpers = new Set(context.helpers.keys())
root.components = [...context.components]
root.directives = [...context.directives]
root.imports = context.imports
diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts
index 76ca1d44353..f97b3ec1b80 100644
--- a/packages/compiler-core/src/transforms/transformElement.ts
+++ b/packages/compiler-core/src/transforms/transformElement.ts
@@ -13,6 +13,7 @@ import {
NodeTypes,
type ObjectExpression,
type Property,
+ type SimpleExpressionNode,
type TemplateTextChildNode,
type VNodeCall,
createArrayExpression,
@@ -51,6 +52,7 @@ import {
import {
findProp,
isCoreComponent,
+ isSimpleIdentifier,
isStaticArgOf,
isStaticExp,
toValidAssetId,
@@ -665,7 +667,10 @@ export function buildProps(
}
// force hydration for v-bind with .prop modifier
- if (isVBind && modifiers.some(mod => mod.content === 'prop')) {
+ if (
+ isVBind &&
+ modifiers.some(mod => (mod as SimpleExpressionNode).content === 'prop')
+ ) {
patchFlag |= PatchFlags.NEED_HYDRATION
}
@@ -894,7 +899,6 @@ export function buildDirectiveArgs(
dirArgs.push(toValidAssetId(dir.name, `directive`))
}
}
- const { loc } = dir
if (dir.exp) dirArgs.push(dir.exp)
if (dir.arg) {
if (!dir.exp) {
@@ -909,17 +913,73 @@ export function buildDirectiveArgs(
}
dirArgs.push(`void 0`)
}
- const trueExpression = createSimpleExpression(`true`, false, loc)
- dirArgs.push(
- createObjectExpression(
- dir.modifiers.map(modifier =>
- createObjectProperty(modifier, trueExpression),
+ dirArgs.push(transformModifiers(dir))
+ }
+ return createArrayExpression(dirArgs, dir.loc)
+}
+
+export function transformModifiers(dir: DirectiveNode): Property['value'] {
+ const trueExpression = createSimpleExpression(
+ `true`,
+ false,
+ dir.loc,
+ ConstantTypes.CAN_CACHE,
+ )
+
+ const staticMods: ExpressionNode[] = []
+ const callArgs: (ObjectExpression | ExpressionNode)[] = []
+
+ for (let i = 0; i < dir.modifiers.length; i++) {
+ const modifier = dir.modifiers[i] as SimpleExpressionNode
+ const isStatic = modifier.isStatic
+
+ if (isStatic) {
+ staticMods.push(modifier)
+ }
+
+ // Collect all static expressions into a single object
+ // This must also happen when we hit the last element in the array
+ // And it also must ensure that an object always comes first in callArgs
+ if (
+ (!isStatic && (staticMods.length || i === 0)) ||
+ (isStatic && i === dir.modifiers.length - 1)
+ ) {
+ callArgs.push(
+ createObjectExpression(
+ staticMods.map(modifier =>
+ createObjectProperty(modifier, trueExpression),
+ ),
+ dir.loc,
),
- loc,
- ),
+ )
+ }
+
+ if (!isStatic) {
+ callArgs.push(modifier)
+ // We only reset the array on hitting a dynamic modifier so we can check its length
+ // after the loop has finished
+ staticMods.length = 0
+ }
+ }
+
+ // Only static mods were passed. Use simple expression to avoid adding modelModidifiers to dynamic prop keys
+ if (staticMods.length === dir.modifiers.length) {
+ const modifiers = staticMods
+ .map(m => (m as SimpleExpressionNode).content)
+ .map(m => (isSimpleIdentifier(m) ? m : JSON.stringify(m)) + `: true`)
+ .join(`, `)
+
+ return createSimpleExpression(
+ `{ ${modifiers} }`,
+ false,
+ dir.loc,
+ ConstantTypes.CAN_CACHE,
)
}
- return createArrayExpression(dirArgs, dir.loc)
+
+ return callArgs.length !== 1
+ ? createCallExpression('Object.assign', callArgs)
+ : callArgs[0]
}
function stringifyDynamicPropNames(props: string[]): string {
diff --git a/packages/compiler-core/src/transforms/transformExpression.ts b/packages/compiler-core/src/transforms/transformExpression.ts
index 9ae8897e674..392c889d10e 100644
--- a/packages/compiler-core/src/transforms/transformExpression.ts
+++ b/packages/compiler-core/src/transforms/transformExpression.ts
@@ -61,6 +61,7 @@ export const transformExpression: NodeTransform = (node, context) => {
if (dir.type === NodeTypes.DIRECTIVE && dir.name !== 'for') {
const exp = dir.exp
const arg = dir.arg
+ const mods = dir.modifiers
// do not process exp if this is v-on:arg - we need special handling
// for wrapping inline statements.
if (
@@ -85,6 +86,12 @@ export const transformExpression: NodeTransform = (node, context) => {
if (arg && arg.type === NodeTypes.SIMPLE_EXPRESSION && !arg.isStatic) {
dir.arg = processExpression(arg, context)
}
+ for (let j = 0; j < mods.length; j++) {
+ const mod = mods[j]
+ if (mod.type === NodeTypes.SIMPLE_EXPRESSION && !mod.isStatic) {
+ mods[j] = processExpression(mod, context)
+ }
+ }
}
}
}
diff --git a/packages/compiler-core/src/transforms/vBind.ts b/packages/compiler-core/src/transforms/vBind.ts
index 1e5e371418b..b49a731fa6f 100644
--- a/packages/compiler-core/src/transforms/vBind.ts
+++ b/packages/compiler-core/src/transforms/vBind.ts
@@ -69,7 +69,9 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
}
// .sync is replaced by v-model:arg
- if (modifiers.some(mod => mod.content === 'camel')) {
+ if (
+ modifiers.some(mod => (mod as SimpleExpressionNode).content === 'camel')
+ ) {
if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
if (arg.isStatic) {
arg.content = camelize(arg.content)
@@ -83,10 +85,14 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
}
if (!context.inSSR) {
- if (modifiers.some(mod => mod.content === 'prop')) {
+ if (
+ modifiers.some(mod => (mod as SimpleExpressionNode).content === 'prop')
+ ) {
injectPrefix(arg, '.')
}
- if (modifiers.some(mod => mod.content === 'attr')) {
+ if (
+ modifiers.some(mod => (mod as SimpleExpressionNode).content === 'attr')
+ ) {
injectPrefix(arg, '^')
}
}
diff --git a/packages/compiler-core/src/transforms/vModel.ts b/packages/compiler-core/src/transforms/vModel.ts
index 598c1ea4387..3d6bfb9635f 100644
--- a/packages/compiler-core/src/transforms/vModel.ts
+++ b/packages/compiler-core/src/transforms/vModel.ts
@@ -1,6 +1,5 @@
import type { DirectiveTransform } from '../transform'
import {
- ConstantTypes,
ElementTypes,
type ExpressionNode,
NodeTypes,
@@ -19,6 +18,7 @@ import {
import { IS_REF } from '../runtimeHelpers'
import { BindingTypes } from '../options'
import { camelize } from '@vue/shared'
+import { transformModifiers } from './transformElement'
export const transformModel: DirectiveTransform = (dir, node, context) => {
const { exp, arg } = dir
@@ -130,26 +130,13 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
// modelModifiers: { foo: true, "bar-baz": true }
if (dir.modifiers.length && node.tagType === ElementTypes.COMPONENT) {
- const modifiers = dir.modifiers
- .map(m => m.content)
- .map(m => (isSimpleIdentifier(m) ? m : JSON.stringify(m)) + `: true`)
- .join(`, `)
const modifiersKey = arg
? isStaticExp(arg)
? `${arg.content}Modifiers`
: createCompoundExpression([arg, ' + "Modifiers"'])
: `modelModifiers`
- props.push(
- createObjectProperty(
- modifiersKey,
- createSimpleExpression(
- `{ ${modifiers} }`,
- false,
- dir.loc,
- ConstantTypes.CAN_CACHE,
- ),
- ),
- )
+
+ props.push(createObjectProperty(modifiersKey, transformModifiers(dir)))
}
return createTransformProps(props)
diff --git a/packages/compiler-dom/src/errors.ts b/packages/compiler-dom/src/errors.ts
index b47624840ab..cfda19dcf28 100644
--- a/packages/compiler-dom/src/errors.ts
+++ b/packages/compiler-dom/src/errors.ts
@@ -21,7 +21,7 @@ export function createDOMCompilerError(
}
export enum DOMErrorCodes {
- X_V_HTML_NO_EXPRESSION = 53 /* ErrorCodes.__EXTEND_POINT__ */,
+ X_V_HTML_NO_EXPRESSION = 58 /* ErrorCodes.__EXTEND_POINT__ */,
X_V_HTML_WITH_CHILDREN,
X_V_TEXT_NO_EXPRESSION,
X_V_TEXT_WITH_CHILDREN,
diff --git a/packages/compiler-dom/src/transforms/vOn.ts b/packages/compiler-dom/src/transforms/vOn.ts
index 1bb5763188b..b9c3e0602dc 100644
--- a/packages/compiler-dom/src/transforms/vOn.ts
+++ b/packages/compiler-dom/src/transforms/vOn.ts
@@ -112,7 +112,12 @@ export const transformOn: DirectiveTransform = (dir, node, context) => {
let { key, value: handlerExp } = baseResult.props[0]
const { keyModifiers, nonKeyModifiers, eventOptionModifiers } =
- resolveModifiers(key, modifiers, context, dir.loc)
+ resolveModifiers(
+ key,
+ modifiers as SimpleExpressionNode[],
+ context,
+ dir.loc,
+ )
// normalize click.right and click.middle since they don't actually fire
if (nonKeyModifiers.includes('right')) {
diff --git a/packages/compiler-ssr/src/errors.ts b/packages/compiler-ssr/src/errors.ts
index e4fd505d282..4816431dd20 100644
--- a/packages/compiler-ssr/src/errors.ts
+++ b/packages/compiler-ssr/src/errors.ts
@@ -17,7 +17,7 @@ export function createSSRCompilerError(
}
export enum SSRErrorCodes {
- X_SSR_UNSAFE_ATTR_NAME = 65 /* DOMErrorCodes.__EXTEND_POINT__ */,
+ X_SSR_UNSAFE_ATTR_NAME = 70 /* DOMErrorCodes.__EXTEND_POINT__ */,
X_SSR_NO_TELEPORT_TARGET,
X_SSR_INVALID_AST_NODE,
}