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, }