export {
ErrorCodes,
createCompilerError,
+ defaultOnError,
+ defaultOnWarn,
type CoreCompilerError,
type CompilerError
} from './errors'
// TODO remove it
import { format } from 'prettier'
import { compile as _compile } from '../src'
+import { ErrorCodes } from '../src/errors'
async function compile(
template: string | RootNode,
})
expect(code).matchSnapshot()
})
+
+ test('should error if no expression', async () => {
+ const onError = vi.fn()
+ await compile(`<div v-bind:arg />`, { onError })
+
+ expect(onError.mock.calls[0][0]).toMatchObject({
+ code: ErrorCodes.VAPOR_BIND_NO_EXPRESSION,
+ loc: {
+ start: {
+ line: 1,
+ column: 6,
+ },
+ end: {
+ line: 1,
+ column: 16,
+ },
+ },
+ })
+ })
})
describe('v-on', () => {
})
expect(code).matchSnapshot()
})
+
+ test('should error if no expression AND no modifier', async () => {
+ const onError = vi.fn()
+ await compile(`<div v-on:click />`, { onError })
+ expect(onError.mock.calls[0][0]).toMatchObject({
+ code: ErrorCodes.VAPOR_ON_NO_EXPRESSION,
+ loc: {
+ start: {
+ line: 1,
+ column: 6,
+ },
+ end: {
+ line: 1,
+ column: 16,
+ },
+ },
+ })
+ })
})
describe('v-html', () => {
--- /dev/null
+export {
+ createCompilerError,
+ defaultOnError,
+ defaultOnWarn,
+ type CoreCompilerError,
+ type CompilerError,
+} from '@vue/compiler-dom'
+
+export const enum ErrorCodes {
+ // transform errors
+ VAPOR_BIND_NO_EXPRESSION,
+ VAPOR_ON_NO_EXPRESSION,
+}
+
+export const errorMessages: Record<ErrorCodes, string> = {
+ // transform errors
+ [ErrorCodes.VAPOR_BIND_NO_EXPRESSION]: `v-bind is missing expression.`,
+ [ErrorCodes.VAPOR_ON_NO_EXPRESSION]: `v-on is missing expression.`,
+}
-import type {
+import {
NodeTypes,
- RootNode,
- Node,
- TemplateChildNode,
- ElementNode,
- AttributeNode,
- InterpolationNode,
- TransformOptions,
- DirectiveNode,
- ExpressionNode,
+ type RootNode,
+ type Node,
+ type TemplateChildNode,
+ type ElementNode,
+ type AttributeNode,
+ type InterpolationNode,
+ type TransformOptions,
+ type DirectiveNode,
+ type ExpressionNode,
} from '@vue/compiler-dom'
import {
type OperationNode,
DynamicInfo,
} from './ir'
import { isVoidTag } from '@vue/shared'
+import {
+ ErrorCodes,
+ createCompilerError,
+ defaultOnError,
+ defaultOnWarn,
+} from './errors'
export interface TransformContext<T extends Node = Node> {
node: T
root: RootNode,
options: TransformOptions = {},
): RootIRNode {
+ options.onError ||= defaultOnError
+ options.onWarn ||= defaultOnWarn
+
const ir: RootIRNode = {
type: IRNodeTypes.ROOT,
loc: root.loc,
helpers: new Set([]),
vaporHelpers: new Set([]),
}
+
const ctx = createRootContext(ir, root, options)
// TODO: transform presets, see packages/compiler-core/src/transforms
return
}
- const expr = processExpression(ctx, node.exp)
+ const { exp, loc, modifiers } = node
+
+ const expr = processExpression(ctx, exp)
switch (name) {
case 'bind': {
+ if (
+ !exp ||
+ (exp.type === NodeTypes.SIMPLE_EXPRESSION! && !exp.content.trim())
+ ) {
+ ctx.options.onError!(
+ createCompilerError(ErrorCodes.VAPOR_BIND_NO_EXPRESSION, loc),
+ )
+ return
+ }
+
if (expr === null) {
// TODO: Vue 3.4 supported shorthand syntax
// https://github.com/vuejs/core/pull/9451
break
}
case 'on': {
+ if (!exp && !modifiers.length) {
+ ctx.options.onError!(
+ createCompilerError(ErrorCodes.VAPOR_ON_NO_EXPRESSION, loc),
+ )
+ return
+ }
+
if (!node.arg) {
// TODO support v-on="{}"
return