`;
exports[`compile > directives > v-pre > self-closing v-pre 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, renderEffect as _renderEffect, setText as _setText, setAttr as _setAttr } from 'vue/vapor';
+"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, renderEffect as _renderEffect, setText as _setText, setDynamicProp as _setDynamicProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div><div><Comp></Comp></div>")
_setText(n1, undefined, _ctx.bar)
})
_renderEffect(() => {
- _setAttr(n2, "id", undefined, _ctx.foo)
+ _setDynamicProp(n2, "id", undefined, _ctx.foo)
})
return n0
}"
`;
exports[`compile > directives > v-pre > should not affect siblings after it 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, renderEffect as _renderEffect, setText as _setText, setAttr as _setAttr } from 'vue/vapor';
+"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, renderEffect as _renderEffect, setText as _setText, setDynamicProp as _setDynamicProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div><div><Comp></Comp></div>")
_setText(n1, undefined, _ctx.bar)
})
_renderEffect(() => {
- _setAttr(n2, "id", undefined, _ctx.foo)
+ _setDynamicProp(n2, "id", undefined, _ctx.foo)
})
return n0
}"
`;
exports[`compile > dynamic root nodes and interpolation 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, on as _on, renderEffect as _renderEffect, setText as _setText, setAttr as _setAttr } from 'vue/vapor';
+"import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, on as _on, renderEffect as _renderEffect, setText as _setText, setDynamicProp as _setDynamicProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<button>foo<!>foo</button>")
_setText(n1, undefined, _ctx.count)
_setText(n2, undefined, _ctx.count)
_setText(n3, undefined, _ctx.count)
- _setAttr(n4, "id", undefined, _ctx.count)
+ _setDynamicProp(n4, "id", undefined, _ctx.count)
})
return n0
}"
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setAttr(n1, key.value+1, undefined, _unref(foo)[key.value+1]())
+ _setDynamicProp(n1, key.value+1, undefined, _unref(foo)[key.value+1]())
})
return n0
})()"
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+exports[`compiler v-bind > .attr modifier 1`] = `
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+
+export function render(_ctx) {
+ const t0 = _template("<div></div>")
+ const n0 = t0()
+ const { 0: [n1],} = _children(n0)
+ _renderEffect(() => {
+ _setDynamicProp(n1, "^foo-bar", undefined, _ctx.id)
+ })
+ return n0
+}"
+`;
+
+exports[`compiler v-bind > .attr modifier w/ no expression 1`] = `
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+
+export function render(_ctx) {
+ const t0 = _template("<div></div>")
+ const n0 = t0()
+ const { 0: [n1],} = _children(n0)
+ _renderEffect(() => {
+ _setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar)
+ })
+ return n0
+}"
+`;
+
exports[`compiler v-bind > .camel modifier 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setAttr(n1, "fooBar", undefined, _ctx.id)
+ _setDynamicProp(n1, "fooBar", undefined, _ctx.id)
})
return n0
}"
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setAttr(n1, _camelize(_ctx.foo), undefined, _ctx.id)
+ _setDynamicProp(n1, _camelize(_ctx.foo), undefined, _ctx.id)
})
return n0
}"
`;
exports[`compiler v-bind > .camel modifier w/ no expression 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+
+export function render(_ctx) {
+ const t0 = _template("<div></div>")
+ const n0 = t0()
+ const { 0: [n1],} = _children(n0)
+ _renderEffect(() => {
+ _setDynamicProp(n1, "fooBar", undefined, _ctx.fooBar)
+ })
+ return n0
+}"
+`;
+
+exports[`compiler v-bind > .prop modifier (shortband) w/ no expression 1`] = `
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+
+export function render(_ctx) {
+ const t0 = _template("<div></div>")
+ const n0 = t0()
+ const { 0: [n1],} = _children(n0)
+ _renderEffect(() => {
+ _setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)
+ })
+ return n0
+}"
+`;
+
+exports[`compiler v-bind > .prop modifier (shorthand) 1`] = `
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+
+export function render(_ctx) {
+ const t0 = _template("<div></div>")
+ const n0 = t0()
+ const { 0: [n1],} = _children(n0)
+ _renderEffect(() => {
+ _setDynamicProp(n1, ".fooBar", undefined, _ctx.id)
+ })
+ return n0
+}"
+`;
+
+exports[`compiler v-bind > .prop modifier 1`] = `
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+
+export function render(_ctx) {
+ const t0 = _template("<div></div>")
+ const n0 = t0()
+ const { 0: [n1],} = _children(n0)
+ _renderEffect(() => {
+ _setDynamicProp(n1, ".fooBar", undefined, _ctx.id)
+ })
+ return n0
+}"
+`;
+
+exports[`compiler v-bind > .prop modifier w/ dynamic arg 1`] = `
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+
+export function render(_ctx) {
+ const t0 = _template("<div></div>")
+ const n0 = t0()
+ const { 0: [n1],} = _children(n0)
+ _renderEffect(() => {
+ _setDynamicProp(n1, \`.\${_ctx.fooBar}\`, undefined, _ctx.id)
+ })
+ return n0
+}"
+`;
+
+exports[`compiler v-bind > .prop modifier w/ no expression 1`] = `
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setAttr(n1, "fooBar", undefined, _ctx.fooBar)
+ _setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)
})
return n0
}"
`;
exports[`compiler v-bind > basic 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setAttr(n1, "id", undefined, _ctx.id)
+ _setDynamicProp(n1, "id", undefined, _ctx.id)
})
return n0
}"
`;
exports[`compiler v-bind > dynamic arg 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setAttr(n1, _ctx.id, undefined, _ctx.id)
+ _setDynamicProp(n1, _ctx.id, undefined, _ctx.id)
})
return n0
}"
`;
exports[`compiler v-bind > no expression (shorthand) 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setAttr(n1, "camel-case", undefined, _ctx.camelCase)
+ _setDynamicProp(n1, "camel-case", undefined, _ctx.camelCase)
})
return n0
}"
`;
exports[`compiler v-bind > no expression 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setAttr(n1, "id", undefined, _ctx.id)
+ _setDynamicProp(n1, "id", undefined, _ctx.id)
})
return n0
}"
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler: v-once > as root node 1`] = `
-"import { template as _template, children as _children, setAttr as _setAttr } from 'vue/vapor';
+"import { template as _template, children as _children, setDynamicProp as _setDynamicProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
- _setAttr(n1, "id", undefined, _ctx.foo)
+ _setDynamicProp(n1, "id", undefined, _ctx.foo)
return n0
}"
`;
exports[`compiler: v-once > basic 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setAttr as _setAttr, prepend as _prepend } from 'vue/vapor';
+"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setDynamicProp as _setDynamicProp, prepend as _prepend } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div> <span></span></div>")
const { 0: [n3, { 1: [n2],}],} = _children(n0)
const n1 = _createTextNode(_ctx.msg)
_setText(n1, undefined, _ctx.msg)
- _setAttr(n2, "class", undefined, _ctx.clz)
+ _setDynamicProp(n2, "class", undefined, _ctx.clz)
_prepend(n3, n1)
return n0
}"
`;
exports[`compiler: v-once > on nested plain element 1`] = `
-"import { template as _template, children as _children, setAttr as _setAttr } from 'vue/vapor';
+"import { template as _template, children as _children, setDynamicProp as _setDynamicProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div><div></div></div>")
const n0 = t0()
const { 0: [, { 0: [n1],}],} = _children(n0)
- _setAttr(n1, "id", undefined, _ctx.foo)
+ _setDynamicProp(n1, "id", undefined, _ctx.foo)
return n0
}"
`;
})
expect(code).matchSnapshot()
- expect(code).contains('_setAttr(n1, "id", undefined, _ctx.id)')
+ expect(code).contains('_setDynamicProp(n1, "id", undefined, _ctx.id)')
})
test('no expression', () => {
})
expect(code).matchSnapshot()
- expect(code).contains('_setAttr(n1, "id", undefined, _ctx.id)')
+ expect(code).contains('_setDynamicProp(n1, "id", undefined, _ctx.id)')
})
test('no expression (shorthand)', () => {
expect(code).matchSnapshot()
expect(code).contains(
- '_setAttr(n1, "camel-case", undefined, _ctx.camelCase)',
+ '_setDynamicProp(n1, "camel-case", undefined, _ctx.camelCase)',
)
})
})
expect(code).matchSnapshot()
- expect(code).contains('_setAttr(n1, _ctx.id, undefined, _ctx.id)')
+ expect(code).contains('_setDynamicProp(n1, _ctx.id, undefined, _ctx.id)')
})
test('should error if empty expression', () => {
})
expect(code).matchSnapshot()
- expect(code).contains('_setAttr(n1, "fooBar", undefined, _ctx.id)')
+ expect(code).contains('_setDynamicProp(n1, "fooBar", undefined, _ctx.id)')
})
test('.camel modifier w/ no expression', () => {
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains('_setAttr(n1, "fooBar", undefined, _ctx.fooBar)')
+ expect(code).contains(
+ '_setDynamicProp(n1, "fooBar", undefined, _ctx.fooBar)',
+ )
})
test('.camel modifier w/ dynamic arg', () => {
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
expect(code).contains(
- `_setAttr(n1, _camelize(_ctx.foo), undefined, _ctx.id)`,
+ `_setDynamicProp(n1, _camelize(_ctx.foo), undefined, _ctx.id)`,
)
})
test.todo('.camel modifier w/ dynamic arg + prefixIdentifiers')
- test.todo('.prop modifier')
- test.todo('.prop modifier w/ no expression')
- test.todo('.prop modifier w/ dynamic arg')
+ test('.prop modifier', () => {
+ const { ir, code } = compileWithVBind(`<div v-bind:fooBar.prop="id"/>`)
+
+ expect(ir.effect[0].operations[0]).toMatchObject({
+ key: {
+ content: `.fooBar`,
+ isStatic: true,
+ },
+ value: {
+ content: `id`,
+ isStatic: false,
+ },
+ })
+
+ expect(code).matchSnapshot()
+ expect(code).contains('renderEffect')
+ expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)')
+ })
+
+ test('.prop modifier w/ no expression', () => {
+ const { ir, code } = compileWithVBind(`<div v-bind:fooBar.prop />`)
+
+ expect(ir.effect[0].operations[0]).toMatchObject({
+ key: {
+ content: `.fooBar`,
+ isStatic: true,
+ },
+ value: {
+ content: `fooBar`,
+ isStatic: false,
+ },
+ })
+
+ expect(code).matchSnapshot()
+ expect(code).contains('renderEffect')
+ expect(code).contains(
+ '_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)',
+ )
+ })
+
+ test('.prop modifier w/ dynamic arg', () => {
+ const { ir, code } = compileWithVBind(`<div v-bind:[fooBar].prop="id"/>`)
+
+ expect(ir.effect[0].operations[0]).toMatchObject({
+ key: {
+ content: `fooBar`,
+ isStatic: false,
+ },
+ value: {
+ content: `id`,
+ isStatic: false,
+ },
+ })
+
+ expect(code).matchSnapshot()
+ expect(code).contains('renderEffect')
+ expect(code).contains(
+ '_setDynamicProp(n1, `.${_ctx.fooBar}`, undefined, _ctx.id)',
+ )
+ })
+
test.todo('.prop modifier w/ dynamic arg + prefixIdentifiers')
- test.todo('.prop modifier (shorthand)')
- test.todo('.prop modifier (shortband) w/ no expression')
- test.todo('.attr modifier')
- test.todo('.attr modifier w/ no expression')
+
+ test('.prop modifier (shorthand)', () => {
+ const { ir, code } = compileWithVBind(`<div .fooBar="id"/>`)
+
+ expect(ir.effect[0].operations[0]).toMatchObject({
+ key: {
+ content: `.fooBar`,
+ isStatic: true,
+ },
+ value: {
+ content: `id`,
+ isStatic: false,
+ },
+ })
+
+ expect(code).matchSnapshot()
+ expect(code).contains('renderEffect')
+ expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)')
+ })
+
+ test('.prop modifier (shortband) w/ no expression', () => {
+ const { ir, code } = compileWithVBind(`<div .fooBar />`)
+
+ expect(ir.effect[0].operations[0]).toMatchObject({
+ key: {
+ content: `.fooBar`,
+ isStatic: true,
+ },
+ value: {
+ content: `fooBar`,
+ isStatic: false,
+ },
+ })
+
+ expect(code).matchSnapshot()
+ expect(code).contains('renderEffect')
+ expect(code).contains(
+ '_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)',
+ )
+ })
+
+ test('.attr modifier', () => {
+ const { ir, code } = compileWithVBind(`<div v-bind:foo-bar.attr="id"/>`)
+
+ expect(ir.effect[0].operations[0]).toMatchObject({
+ key: {
+ content: `^foo-bar`,
+ isStatic: true,
+ },
+ value: {
+ content: `id`,
+ isStatic: false,
+ },
+ })
+
+ expect(code).matchSnapshot()
+ expect(code).contains('renderEffect')
+ expect(code).contains('_setDynamicProp(n1, "^foo-bar", undefined, _ctx.id)')
+ })
+
+ test('.attr modifier w/ no expression', () => {
+ const { ir, code } = compileWithVBind(`<div v-bind:foo-bar.attr />`)
+
+ expect(ir.effect[0].operations[0]).toMatchObject({
+ key: {
+ content: `^foo-bar`,
+ isStatic: true,
+ },
+ value: {
+ content: `fooBar`,
+ isStatic: false,
+ },
+ })
+
+ expect(code).matchSnapshot()
+ expect(code).contains('renderEffect')
+ expect(code).contains(
+ '_setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar)',
+ )
+ })
})
}
function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
- const { pushFnCall, newline, vaporHelper, helper } = context
+ const { pushFnCall, pushMulti, newline, vaporHelper, helper } = context
newline()
pushFnCall(
- vaporHelper('setAttr'),
+ vaporHelper('setDynamicProp'),
`n${oper.element}`,
// 2. key name
() => {
if (oper.runtimeCamelize) {
pushFnCall(helper('camelize'), () => genExpression(oper.key, context))
+ } else if (oper.runtimePrefix) {
+ pushMulti([`\`${oper.runtimePrefix}\${`, `}\``], () =>
+ genExpression(oper.key, context),
+ )
} else {
genExpression(oper.key, context)
}
key: IRExpression
value: IRExpression
runtimeCamelize: boolean
+ runtimePrefix?: string
}
export interface SetTextIRNode extends BaseIRNode {
import {
ErrorCodes,
+ type SimpleExpressionNode,
createCompilerError,
createSimpleExpression,
} from '@vue/compiler-core'
}
}
+ let prefix: string | undefined
+ if (modifiers.includes('prop')) {
+ prefix = injectPrefix(arg, '.')
+ }
+ if (modifiers.includes('attr')) {
+ prefix = injectPrefix(arg, '^')
+ }
+
if (!exp.content.trim()) {
context.options.onError(
createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, loc),
key: arg,
value: exp,
runtimeCamelize: camel,
+ runtimePrefix: prefix,
},
],
)
}
+
+const injectPrefix = (arg: SimpleExpressionNode, prefix: string) => {
+ if (!arg.isStatic) {
+ return prefix
+ }
+ arg.content = prefix + arg.content
+}
}
}
-export function setDynamicProp(el: Element, key: string, val: any) {
+export function setDOMProp(el: any, key: string, oldVal: any, newVal: any) {
+ // TODO special checks
+ if (newVal !== oldVal) {
+ el[key] = newVal
+ }
+}
+
+export function setDynamicProp(
+ el: Element,
+ key: string,
+ oldVal: any,
+ newVal: any,
+) {
if (key === 'class') {
- setClass(el, void 0, val)
+ setClass(el, oldVal, newVal)
} else if (key === 'style') {
- setStyle(el as HTMLElement, void 0, val)
- } else if (key in el) {
- ;(el as any)[key] = val
+ setStyle(el as HTMLElement, oldVal, newVal)
+ } else if (
+ key[0] === '.'
+ ? ((key = key.slice(1)), true)
+ : key[0] === '^'
+ ? ((key = key.slice(1)), false)
+ : key in el
+ ) {
+ setDOMProp(el, key, oldVal, newVal)
} else {
// TODO special checks
- setAttr(el, key, void 0, val)
+ setAttr(el, key, oldVal, newVal)
}
}