feat(vapor): merge inherited attrs with current attrs

This commit is contained in:
三咲智子 Kevin Deng 2024-11-14 20:01:10 +08:00
parent c91586528a
commit 247617612a
No known key found for this signature in database
33 changed files with 506 additions and 179 deletions

View File

@ -29,7 +29,7 @@
"dev-prepare-cjs": "node scripts/prepare-cjs.js || npm run build-all-cjs", "dev-prepare-cjs": "node scripts/prepare-cjs.js || npm run build-all-cjs",
"dev-compiler": "run-p \"dev template-explorer\" serve open", "dev-compiler": "run-p \"dev template-explorer\" serve open",
"dev-sfc": "run-s dev-prepare-cjs dev-sfc-run", "dev-sfc": "run-s dev-prepare-cjs dev-sfc-run",
"dev-sfc-serve": "vite packages-private/sfc-playground --host", "dev-sfc-serve": "vite packages-private/sfc-playground",
"dev-sfc-run": "run-p \"dev compiler-sfc -f esm-browser\" \"dev vue -if esm-bundler-runtime\" \"dev vue -ipf esm-browser-runtime\" \"dev server-renderer -if esm-bundler\" dev-sfc-serve", "dev-sfc-run": "run-p \"dev compiler-sfc -f esm-browser\" \"dev vue -if esm-bundler-runtime\" \"dev vue -ipf esm-browser-runtime\" \"dev server-renderer -if esm-bundler\" dev-sfc-serve",
"dev-vapor": "pnpm -C playground run dev", "dev-vapor": "pnpm -C playground run dev",
"serve": "serve", "serve": "serve",

View File

@ -140,11 +140,12 @@ export function render(_ctx) {
`; `;
exports[`compile > directives > v-pre > basic 1`] = ` exports[`compile > directives > v-pre > basic 1`] = `
"import { template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>") const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>")
export function render(_ctx, $props) { export function render(_ctx, $props) {
const n0 = t0() const n0 = t0()
_setInheritAttrs(false)
return n0 return n0
}" }"
`; `;
@ -176,15 +177,16 @@ export function render(_ctx) {
`; `;
exports[`compile > dynamic root nodes and interpolation 1`] = ` exports[`compile > dynamic root nodes and interpolation 1`] = `
"import { delegate as _delegate, renderEffect as _renderEffect, setText as _setText, setDynamicProp as _setDynamicProp, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor'; "import { delegate as _delegate, setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setText as _setText, setDynamicProp as _setDynamicProp, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor';
const t0 = _template("<button></button>") const t0 = _template("<button></button>")
_delegateEvents("click") _delegateEvents("click")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_delegate(n0, "click", () => _ctx.handleClick) _delegate(n0, "click", () => _ctx.handleClick)
_setInheritAttrs(["id"])
_renderEffect(() => _setText(n0, _ctx.count, "foo", _ctx.count, "foo", _ctx.count)) _renderEffect(() => _setText(n0, _ctx.count, "foo", _ctx.count, "foo", _ctx.count))
_renderEffect(() => _setDynamicProp(n0, "id", _ctx.count)) _renderEffect(() => _setDynamicProp(n0, "id", _ctx.count, true))
return n0 return n0
}" }"
`; `;
@ -199,7 +201,8 @@ exports[`compile > expression parsing > interpolation 1`] = `
exports[`compile > expression parsing > v-bind 1`] = ` exports[`compile > expression parsing > v-bind 1`] = `
"((_ctx) => { "((_ctx) => {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, { [key.value+1]: _unref(foo)[key.value+1]() })) _setInheritAttrs(true)
_renderEffect(() => _setDynamicProps(n0, [{ [key.value+1]: _unref(foo)[key.value+1]() }], true))
return n0 return n0
})()" })()"
`; `;

View File

@ -286,22 +286,24 @@ export function render(_ctx) {
`; `;
exports[`compiler: element transform > props + children 1`] = ` exports[`compiler: element transform > props + children 1`] = `
"import { template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
const t0 = _template("<div id=\\"foo\\"><span></span></div>") const t0 = _template("<div id=\\"foo\\"><span></span></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_setInheritAttrs(false)
return n0 return n0
}" }"
`; `;
exports[`compiler: element transform > props merging: class 1`] = ` exports[`compiler: element transform > props merging: class 1`] = `
"import { renderEffect as _renderEffect, setClass as _setClass, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setClass as _setClass, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setClass(n0, ["foo", { bar: _ctx.isBar }])) _setInheritAttrs(["class"])
_renderEffect(() => _setClass(n0, ["foo", { bar: _ctx.isBar }], true))
return n0 return n0
}" }"
`; `;
@ -324,66 +326,72 @@ export function render(_ctx) {
`; `;
exports[`compiler: element transform > props merging: style 1`] = ` exports[`compiler: element transform > props merging: style 1`] = `
"import { renderEffect as _renderEffect, setStyle as _setStyle, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setStyle as _setStyle, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setStyle(n0, ["color: green", { color: 'red' }])) _setInheritAttrs(["style"])
_renderEffect(() => _setStyle(n0, ["color: green", { color: 'red' }], true))
return n0 return n0
}" }"
`; `;
exports[`compiler: element transform > static props 1`] = ` exports[`compiler: element transform > static props 1`] = `
"import { template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
const t0 = _template("<div id=\\"foo\\" class=\\"bar\\"></div>") const t0 = _template("<div id=\\"foo\\" class=\\"bar\\"></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_setInheritAttrs(false)
return n0 return n0
}" }"
`; `;
exports[`compiler: element transform > v-bind="obj" 1`] = ` exports[`compiler: element transform > v-bind="obj" 1`] = `
"import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, _ctx.obj)) _setInheritAttrs(true)
_renderEffect(() => _setDynamicProps(n0, [_ctx.obj], true))
return n0 return n0
}" }"
`; `;
exports[`compiler: element transform > v-bind="obj" after static prop 1`] = ` exports[`compiler: element transform > v-bind="obj" after static prop 1`] = `
"import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, { id: "foo" }, _ctx.obj)) _setInheritAttrs(true)
_renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj], true))
return n0 return n0
}" }"
`; `;
exports[`compiler: element transform > v-bind="obj" before static prop 1`] = ` exports[`compiler: element transform > v-bind="obj" before static prop 1`] = `
"import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, _ctx.obj, { id: "foo" })) _setInheritAttrs(true)
_renderEffect(() => _setDynamicProps(n0, [_ctx.obj, { id: "foo" }], true))
return n0 return n0
}" }"
`; `;
exports[`compiler: element transform > v-bind="obj" between static props 1`] = ` exports[`compiler: element transform > v-bind="obj" between static props 1`] = `
"import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, { id: "foo" }, _ctx.obj, { class: "bar" })) _setInheritAttrs(true)
_renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj, { class: "bar" }], true))
return n0 return n0
}" }"
`; `;

View File

@ -1,177 +1,193 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler v-bind > .attr modifier 1`] = ` exports[`compiler v-bind > .attr modifier 1`] = `
"import { renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setAttr(n0, "foo-bar", _ctx.id)) _setInheritAttrs(["foo-bar"])
_renderEffect(() => _setAttr(n0, "foo-bar", _ctx.id, true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > .attr modifier w/ no expression 1`] = ` exports[`compiler v-bind > .attr modifier w/ no expression 1`] = `
"import { renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setAttr(n0, "foo-bar", _ctx.fooBar)) _setInheritAttrs(["foo-bar"])
_renderEffect(() => _setAttr(n0, "foo-bar", _ctx.fooBar, true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > .camel modifier 1`] = ` exports[`compiler v-bind > .camel modifier 1`] = `
"import { renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProp(n0, "fooBar", _ctx.id)) _setInheritAttrs(["fooBar"])
_renderEffect(() => _setDynamicProp(n0, "fooBar", _ctx.id, true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > .camel modifier w/ dynamic arg 1`] = ` exports[`compiler v-bind > .camel modifier w/ dynamic arg 1`] = `
"import { camelize as _camelize } from 'vue'; "import { camelize as _camelize } from 'vue';
import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor'; import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, { [_camelize(_ctx.foo)]: _ctx.id })) _setInheritAttrs(true)
_renderEffect(() => _setDynamicProps(n0, [{ [_camelize(_ctx.foo)]: _ctx.id }], true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > .camel modifier w/ no expression 1`] = ` exports[`compiler v-bind > .camel modifier w/ no expression 1`] = `
"import { renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProp(n0, "fooBar", _ctx.fooBar)) _setInheritAttrs(["fooBar"])
_renderEffect(() => _setDynamicProp(n0, "fooBar", _ctx.fooBar, true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > .prop modifier (shortband) w/ no expression 1`] = ` exports[`compiler v-bind > .prop modifier (shortband) w/ no expression 1`] = `
"import { renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.fooBar)) _setInheritAttrs(["fooBar"])
_renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.fooBar, true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > .prop modifier (shorthand) 1`] = ` exports[`compiler v-bind > .prop modifier (shorthand) 1`] = `
"import { renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.id)) _setInheritAttrs(["fooBar"])
_renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.id, true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > .prop modifier 1`] = ` exports[`compiler v-bind > .prop modifier 1`] = `
"import { renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.id)) _setInheritAttrs(["fooBar"])
_renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.id, true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > .prop modifier w/ dynamic arg 1`] = ` exports[`compiler v-bind > .prop modifier w/ dynamic arg 1`] = `
"import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, { ["." + _ctx.fooBar]: _ctx.id })) _setInheritAttrs(true)
_renderEffect(() => _setDynamicProps(n0, [{ ["." + _ctx.fooBar]: _ctx.id }], true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > .prop modifier w/ no expression 1`] = ` exports[`compiler v-bind > .prop modifier w/ no expression 1`] = `
"import { renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.fooBar)) _setInheritAttrs(["fooBar"])
_renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.fooBar, true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > basic 1`] = ` exports[`compiler v-bind > basic 1`] = `
"import { renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProp(n0, "id", _ctx.id)) _setInheritAttrs(["id"])
_renderEffect(() => _setDynamicProp(n0, "id", _ctx.id, true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > dynamic arg 1`] = ` exports[`compiler v-bind > dynamic arg 1`] = `
"import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, { [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title })) _setInheritAttrs(true)
_renderEffect(() => _setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title }], true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > dynamic arg w/ static attribute 1`] = ` exports[`compiler v-bind > dynamic arg w/ static attribute 1`] = `
"import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, { [_ctx.id]: _ctx.id, foo: "bar", checked: "" })) _setInheritAttrs(true)
_renderEffect(() => _setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, foo: "bar", checked: "" }], true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > no expression (shorthand) 1`] = ` exports[`compiler v-bind > no expression (shorthand) 1`] = `
"import { renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProp(n0, "camel-case", _ctx.camelCase)) _setInheritAttrs(["camel-case"])
_renderEffect(() => _setDynamicProp(n0, "camel-case", _ctx.camelCase, true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > no expression 1`] = ` exports[`compiler v-bind > no expression 1`] = `
"import { renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_renderEffect(() => _setDynamicProp(n0, "id", _ctx.id)) _setInheritAttrs(["id"])
_renderEffect(() => _setDynamicProp(n0, "id", _ctx.id, true))
return n0 return n0
}" }"
`; `;
exports[`compiler v-bind > should error if empty expression 1`] = ` exports[`compiler v-bind > should error if empty expression 1`] = `
"import { template as _template } from 'vue/vapor'; "import { setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
const t0 = _template("<div arg></div>") const t0 = _template("<div arg></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_setInheritAttrs(false)
return n0 return n0
}" }"
`; `;

View File

@ -126,13 +126,14 @@ export function render(_ctx) {
`; `;
exports[`compiler: vModel transform > should support input (checkbox) 1`] = ` exports[`compiler: vModel transform > should support input (checkbox) 1`] = `
"import { vModelCheckbox as _vModelCheckbox, withDirectives as _withDirectives, delegate as _delegate, template as _template } from 'vue/vapor'; "import { vModelCheckbox as _vModelCheckbox, withDirectives as _withDirectives, delegate as _delegate, setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
const t0 = _template("<input type=\\"checkbox\\">") const t0 = _template("<input type=\\"checkbox\\">")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_withDirectives(n0, [[_vModelCheckbox, () => _ctx.model]]) _withDirectives(n0, [[_vModelCheckbox, () => _ctx.model]])
_delegate(n0, "update:modelValue", () => $event => (_ctx.model = $event)) _delegate(n0, "update:modelValue", () => $event => (_ctx.model = $event))
_setInheritAttrs(false)
return n0 return n0
}" }"
`; `;
@ -150,25 +151,27 @@ export function render(_ctx) {
`; `;
exports[`compiler: vModel transform > should support input (radio) 1`] = ` exports[`compiler: vModel transform > should support input (radio) 1`] = `
"import { vModelRadio as _vModelRadio, withDirectives as _withDirectives, delegate as _delegate, template as _template } from 'vue/vapor'; "import { vModelRadio as _vModelRadio, withDirectives as _withDirectives, delegate as _delegate, setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
const t0 = _template("<input type=\\"radio\\">") const t0 = _template("<input type=\\"radio\\">")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_withDirectives(n0, [[_vModelRadio, () => _ctx.model]]) _withDirectives(n0, [[_vModelRadio, () => _ctx.model]])
_delegate(n0, "update:modelValue", () => $event => (_ctx.model = $event)) _delegate(n0, "update:modelValue", () => $event => (_ctx.model = $event))
_setInheritAttrs(false)
return n0 return n0
}" }"
`; `;
exports[`compiler: vModel transform > should support input (text) 1`] = ` exports[`compiler: vModel transform > should support input (text) 1`] = `
"import { vModelText as _vModelText, withDirectives as _withDirectives, delegate as _delegate, template as _template } from 'vue/vapor'; "import { vModelText as _vModelText, withDirectives as _withDirectives, delegate as _delegate, setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
const t0 = _template("<input type=\\"text\\">") const t0 = _template("<input type=\\"text\\">")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_withDirectives(n0, [[_vModelText, () => _ctx.model]]) _withDirectives(n0, [[_vModelText, () => _ctx.model]])
_delegate(n0, "update:modelValue", () => $event => (_ctx.model = $event)) _delegate(n0, "update:modelValue", () => $event => (_ctx.model = $event))
_setInheritAttrs(false)
return n0 return n0
}" }"
`; `;
@ -243,14 +246,15 @@ export function render(_ctx) {
`; `;
exports[`compiler: vModel transform > should support w/ dynamic v-bind 1`] = ` exports[`compiler: vModel transform > should support w/ dynamic v-bind 1`] = `
"import { vModelDynamic as _vModelDynamic, withDirectives as _withDirectives, delegate as _delegate, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor'; "import { vModelDynamic as _vModelDynamic, withDirectives as _withDirectives, delegate as _delegate, setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
const t0 = _template("<input>") const t0 = _template("<input>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_withDirectives(n0, [[_vModelDynamic, () => _ctx.model]]) _withDirectives(n0, [[_vModelDynamic, () => _ctx.model]])
_delegate(n0, "update:modelValue", () => $event => (_ctx.model = $event)) _delegate(n0, "update:modelValue", () => $event => (_ctx.model = $event))
_renderEffect(() => _setDynamicProps(n0, _ctx.obj)) _setInheritAttrs(true)
_renderEffect(() => _setDynamicProps(n0, [_ctx.obj], true))
return n0 return n0
}" }"
`; `;

View File

@ -1,12 +1,13 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler: v-once > as root node 1`] = ` exports[`compiler: v-once > as root node 1`] = `
"import { setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor'; "import { setDynamicProp as _setDynamicProp, setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = t0() const n0 = t0()
_setDynamicProp(n0, "id", _ctx.foo) _setDynamicProp(n0, "id", _ctx.foo, true)
_setInheritAttrs(["id"])
return n0 return n0
}" }"
`; `;

View File

@ -588,7 +588,7 @@ describe('compiler: element transform', () => {
], ],
}, },
]) ])
expect(code).contains('_setDynamicProps(n0, _ctx.obj)') expect(code).contains('_setDynamicProps(n0, [_ctx.obj], true)')
}) })
test('v-bind="obj" after static prop', () => { test('v-bind="obj" after static prop', () => {
@ -624,7 +624,9 @@ describe('compiler: element transform', () => {
], ],
}, },
]) ])
expect(code).contains('_setDynamicProps(n0, { id: "foo" }, _ctx.obj)') expect(code).contains(
'_setDynamicProps(n0, [{ id: "foo" }, _ctx.obj], true)',
)
}) })
test('v-bind="obj" before static prop', () => { test('v-bind="obj" before static prop', () => {
@ -650,7 +652,9 @@ describe('compiler: element transform', () => {
], ],
}, },
]) ])
expect(code).contains('_setDynamicProps(n0, _ctx.obj, { id: "foo" })') expect(code).contains(
'_setDynamicProps(n0, [_ctx.obj, { id: "foo" }], true)',
)
}) })
test('v-bind="obj" between static props', () => { test('v-bind="obj" between static props', () => {
@ -678,7 +682,7 @@ describe('compiler: element transform', () => {
}, },
]) ])
expect(code).contains( expect(code).contains(
'_setDynamicProps(n0, { id: "foo" }, _ctx.obj, { class: "bar" })', '_setDynamicProps(n0, [{ id: "foo" }, _ctx.obj, { class: "bar" }], true)',
) )
}) })
@ -723,6 +727,9 @@ describe('compiler: element transform', () => {
delegate: true, delegate: true,
effect: false, effect: false,
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
}) })

View File

@ -30,8 +30,8 @@ describe('compiler: template ref transform', () => {
flags: DynamicFlag.REFERENCED, flags: DynamicFlag.REFERENCED,
}) })
expect(ir.template).toEqual(['<div></div>']) expect(ir.template).toEqual(['<div></div>'])
expect(ir.block.operation).lengthOf(1) expect(ir.block.operation).lengthOf(2)
expect(ir.block.operation[0]).toMatchObject({ expect(ir.block.operation[1]).toMatchObject({
type: IRNodeTypes.SET_TEMPLATE_REF, type: IRNodeTypes.SET_TEMPLATE_REF,
element: 0, element: 0,
value: { value: {
@ -56,6 +56,9 @@ describe('compiler: template ref transform', () => {
}) })
expect(ir.template).toEqual(['<div></div>']) expect(ir.template).toEqual(['<div></div>'])
expect(ir.block.operation).toMatchObject([ expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
{ {
type: IRNodeTypes.DECLARE_OLD_REF, type: IRNodeTypes.DECLARE_OLD_REF,
id: 0, id: 0,

View File

@ -74,7 +74,7 @@ describe('compiler v-bind', () => {
}) })
expect(code).matchSnapshot() expect(code).matchSnapshot()
expect(code).contains('_setDynamicProp(n0, "id", _ctx.id)') expect(code).contains('_setDynamicProp(n0, "id", _ctx.id, true)')
}) })
test('no expression', () => { test('no expression', () => {
@ -104,7 +104,7 @@ describe('compiler v-bind', () => {
], ],
}, },
}) })
expect(code).contains('_setDynamicProp(n0, "id", _ctx.id)') expect(code).contains('_setDynamicProp(n0, "id", _ctx.id, true)')
}) })
test('no expression (shorthand)', () => { test('no expression (shorthand)', () => {
@ -126,7 +126,9 @@ describe('compiler v-bind', () => {
], ],
}, },
}) })
expect(code).contains('_setDynamicProp(n0, "camel-case", _ctx.camelCase)') expect(code).contains(
'_setDynamicProp(n0, "camel-case", _ctx.camelCase, true)',
)
}) })
test('dynamic arg', () => { test('dynamic arg', () => {
@ -171,7 +173,7 @@ describe('compiler v-bind', () => {
], ],
}) })
expect(code).contains( expect(code).contains(
'_setDynamicProps(n0, { [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title })', '_setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title }], true)',
) )
}) })
@ -224,7 +226,7 @@ describe('compiler v-bind', () => {
], ],
}) })
expect(code).contains( expect(code).contains(
'_setDynamicProps(n0, { [_ctx.id]: _ctx.id, foo: "bar", checked: "" })', '_setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, foo: "bar", checked: "" }], true)',
) )
}) })
@ -286,7 +288,7 @@ describe('compiler v-bind', () => {
}) })
expect(code).matchSnapshot() expect(code).matchSnapshot()
expect(code).contains('_setDynamicProp(n0, "fooBar", _ctx.id)') expect(code).contains('_setDynamicProp(n0, "fooBar", _ctx.id, true)')
}) })
test('.camel modifier w/ no expression', () => { test('.camel modifier w/ no expression', () => {
@ -310,7 +312,7 @@ describe('compiler v-bind', () => {
}, },
}) })
expect(code).contains('renderEffect') expect(code).contains('renderEffect')
expect(code).contains('_setDynamicProp(n0, "fooBar", _ctx.fooBar)') expect(code).contains('_setDynamicProp(n0, "fooBar", _ctx.fooBar, true)')
}) })
test('.camel modifier w/ dynamic arg', () => { test('.camel modifier w/ dynamic arg', () => {
@ -341,7 +343,7 @@ describe('compiler v-bind', () => {
expect(code).matchSnapshot() expect(code).matchSnapshot()
expect(code).contains('renderEffect') expect(code).contains('renderEffect')
expect(code).contains( expect(code).contains(
`_setDynamicProps(n0, { [_camelize(_ctx.foo)]: _ctx.id })`, `_setDynamicProps(n0, [{ [_camelize(_ctx.foo)]: _ctx.id }], true)`,
) )
}) })
@ -368,7 +370,7 @@ describe('compiler v-bind', () => {
}, },
}) })
expect(code).contains('renderEffect') expect(code).contains('renderEffect')
expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.id)') expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.id, true)')
}) })
test('.prop modifier w/ no expression', () => { test('.prop modifier w/ no expression', () => {
@ -392,7 +394,7 @@ describe('compiler v-bind', () => {
}, },
}) })
expect(code).contains('renderEffect') expect(code).contains('renderEffect')
expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.fooBar)') expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.fooBar, true)')
}) })
test('.prop modifier w/ dynamic arg', () => { test('.prop modifier w/ dynamic arg', () => {
@ -422,7 +424,7 @@ describe('compiler v-bind', () => {
}) })
expect(code).contains('renderEffect') expect(code).contains('renderEffect')
expect(code).contains( expect(code).contains(
`_setDynamicProps(n0, { ["." + _ctx.fooBar]: _ctx.id })`, `_setDynamicProps(n0, [{ ["." + _ctx.fooBar]: _ctx.id }], true)`,
) )
}) })
@ -449,7 +451,7 @@ describe('compiler v-bind', () => {
}, },
}) })
expect(code).contains('renderEffect') expect(code).contains('renderEffect')
expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.id)') expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.id, true)')
}) })
test('.prop modifier (shortband) w/ no expression', () => { test('.prop modifier (shortband) w/ no expression', () => {
@ -473,7 +475,7 @@ describe('compiler v-bind', () => {
}, },
}) })
expect(code).contains('renderEffect') expect(code).contains('renderEffect')
expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.fooBar)') expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.fooBar, true)')
}) })
test('.attr modifier', () => { test('.attr modifier', () => {
@ -497,7 +499,7 @@ describe('compiler v-bind', () => {
}, },
}) })
expect(code).contains('renderEffect') expect(code).contains('renderEffect')
expect(code).contains('_setAttr(n0, "foo-bar", _ctx.id)') expect(code).contains('_setAttr(n0, "foo-bar", _ctx.id, true)')
}) })
test('.attr modifier w/ no expression', () => { test('.attr modifier w/ no expression', () => {
@ -522,6 +524,6 @@ describe('compiler v-bind', () => {
}) })
expect(code).contains('renderEffect') expect(code).contains('renderEffect')
expect(code).contains('_setAttr(n0, "foo-bar", _ctx.fooBar)') expect(code).contains('_setAttr(n0, "foo-bar", _ctx.fooBar, true)')
}) })
}) })

View File

@ -28,7 +28,11 @@ describe('v-html', () => {
expect(vaporHelpers).contains('setHtml') expect(vaporHelpers).contains('setHtml')
expect(helpers.size).toBe(0) expect(helpers.size).toBe(0)
expect(ir.block.operation).toEqual([]) expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
])
expect(ir.block.effect).toMatchObject([ expect(ir.block.effect).toMatchObject([
{ {
expressions: [ expressions: [
@ -70,7 +74,11 @@ describe('v-html', () => {
// children should have been removed // children should have been removed
expect(ir.template).toEqual(['<div></div>']) expect(ir.template).toEqual(['<div></div>'])
expect(ir.block.operation).toEqual([]) expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
])
expect(ir.block.effect).toMatchObject([ expect(ir.block.effect).toMatchObject([
{ {
expressions: [ expressions: [

View File

@ -47,6 +47,9 @@ describe('v-on', () => {
keyOverride: undefined, keyOverride: undefined,
delegate: true, delegate: true,
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
}) })
@ -91,7 +94,11 @@ describe('v-on', () => {
expect(vaporHelpers).contains('on') expect(vaporHelpers).contains('on')
expect(vaporHelpers).contains('renderEffect') expect(vaporHelpers).contains('renderEffect')
expect(helpers.size).toBe(0) expect(helpers.size).toBe(0)
expect(ir.block.operation).toEqual([]) expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
])
expect(ir.block.effect[0].operations[0]).toMatchObject({ expect(ir.block.effect[0].operations[0]).toMatchObject({
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
@ -130,7 +137,11 @@ describe('v-on', () => {
expect(vaporHelpers).contains('on') expect(vaporHelpers).contains('on')
expect(vaporHelpers).contains('renderEffect') expect(vaporHelpers).contains('renderEffect')
expect(helpers.size).toBe(0) expect(helpers.size).toBe(0)
expect(ir.block.operation).toEqual([]) expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
])
expect(ir.block.effect[0].operations[0]).toMatchObject({ expect(ir.block.effect[0].operations[0]).toMatchObject({
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
@ -169,6 +180,9 @@ describe('v-on', () => {
}, },
delegate: true, delegate: true,
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).contains(`_delegate(n0, "click", () => $event => (_ctx.i++))`) expect(code).contains(`_delegate(n0, "click", () => $event => (_ctx.i++))`)
}) })
@ -206,6 +220,9 @@ describe('v-on', () => {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
value: { content: 'foo();bar()' }, value: { content: 'foo();bar()' },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
// should wrap with `{` for multiple statements // should wrap with `{` for multiple statements
// in this case the return value is discarded and the behavior is // in this case the return value is discarded and the behavior is
@ -224,6 +241,9 @@ describe('v-on', () => {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
value: { content: '\nfoo();\nbar()\n' }, value: { content: '\nfoo();\nbar()\n' },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
// should wrap with `{` for multiple statements // should wrap with `{` for multiple statements
// in this case the return value is discarded and the behavior is // in this case the return value is discarded and the behavior is
@ -244,6 +264,9 @@ describe('v-on', () => {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
value: { content: 'foo($event)' }, value: { content: 'foo($event)' },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
// should NOT prefix $event // should NOT prefix $event
expect(code).contains( expect(code).contains(
@ -262,6 +285,9 @@ describe('v-on', () => {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
value: { content: 'foo($event);bar()' }, value: { content: 'foo($event);bar()' },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
// should NOT prefix $event // should NOT prefix $event
expect(code).contains( expect(code).contains(
@ -278,6 +304,9 @@ describe('v-on', () => {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
value: { content: '$event => foo($event)' }, value: { content: '$event => foo($event)' },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).contains( expect(code).contains(
`_delegate(n0, "click", () => $event => _ctx.foo($event))`, `_delegate(n0, "click", () => $event => _ctx.foo($event))`,
@ -296,6 +325,9 @@ describe('v-on', () => {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
value: { content: '(e: any): any => foo(e)' }, value: { content: '(e: any): any => foo(e)' },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).contains( expect(code).contains(
`_delegate(n0, "click", () => (e: any): any => _ctx.foo(e))`, `_delegate(n0, "click", () => (e: any): any => _ctx.foo(e))`,
@ -323,6 +355,9 @@ describe('v-on', () => {
`, `,
}, },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
}) })
@ -349,6 +384,9 @@ describe('v-on', () => {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
value: { content: `a['b' + c]` }, value: { content: `a['b' + c]` },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).matchSnapshot() expect(code).matchSnapshot()
@ -361,6 +399,9 @@ describe('v-on', () => {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
value: { content: `a['b' + c]` }, value: { content: `a['b' + c]` },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).matchSnapshot() expect(code).matchSnapshot()
@ -378,6 +419,9 @@ describe('v-on', () => {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
value: { content: `e => foo(e)` }, value: { content: `e => foo(e)` },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).contains(`_delegate(n0, "click", () => e => _ctx.foo(e))`) expect(code).contains(`_delegate(n0, "click", () => e => _ctx.foo(e))`)
}) })
@ -432,6 +476,9 @@ describe('v-on', () => {
keyOverride: undefined, keyOverride: undefined,
delegate: false, delegate: false,
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).contains( expect(code).contains(
`_on(n0, "click", () => _ctx.test, { `_on(n0, "click", () => _ctx.test, {
@ -487,6 +534,9 @@ describe('v-on', () => {
options: [], options: [],
}, },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).matchSnapshot() expect(code).matchSnapshot()
@ -529,6 +579,9 @@ describe('v-on', () => {
options: ['capture'], options: ['capture'],
}, },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).matchSnapshot() expect(code).matchSnapshot()
@ -543,6 +596,9 @@ describe('v-on', () => {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
modifiers: { nonKeys: ['exact'] }, modifiers: { nonKeys: ['exact'] },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).matchSnapshot() expect(code).matchSnapshot()
@ -562,6 +618,9 @@ describe('v-on', () => {
options: [], options: [],
}, },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).matchSnapshot() expect(code).matchSnapshot()
@ -604,6 +663,9 @@ describe('v-on', () => {
modifiers: { nonKeys: ['right'] }, modifiers: { nonKeys: ['right'] },
keyOverride: undefined, keyOverride: undefined,
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).matchSnapshot() expect(code).matchSnapshot()
@ -645,6 +707,9 @@ describe('v-on', () => {
modifiers: { nonKeys: ['middle'] }, modifiers: { nonKeys: ['middle'] },
keyOverride: undefined, keyOverride: undefined,
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).matchSnapshot() expect(code).matchSnapshot()
@ -694,6 +759,9 @@ describe('v-on', () => {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
delegate: true, delegate: true,
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
}) })
}) })

View File

@ -68,6 +68,7 @@ describe('compiler: v-once', () => {
elements: [0], elements: [0],
parent: 2, parent: 2,
}, },
{ type: IRNodeTypes.SET_INHERIT_ATTRS },
]) ])
}) })
@ -96,6 +97,9 @@ describe('compiler: v-once', () => {
], ],
}, },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
expect(code).not.contains('effect') expect(code).not.contains('effect')
}) })
@ -128,6 +132,9 @@ describe('compiler: v-once', () => {
], ],
}, },
}, },
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
]) ])
}) })
@ -147,6 +154,7 @@ describe('compiler: v-once', () => {
elements: [0], elements: [0],
parent: 1, parent: 1,
}, },
{ type: IRNodeTypes.SET_INHERIT_ATTRS },
]) ])
}) })
@ -160,7 +168,7 @@ describe('compiler: v-once', () => {
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
expect(helpers).lengthOf(0) expect(helpers).lengthOf(0)
expect(ir.block.effect).lengthOf(0) expect(ir.block.effect).lengthOf(0)
expect(ir.block.operation).lengthOf(0) expect(ir.block.operation).lengthOf(1)
}) })
test.todo('with hoistStatic: true') test.todo('with hoistStatic: true')

View File

@ -28,7 +28,11 @@ describe('v-text', () => {
expect(vaporHelpers).contains('setText') expect(vaporHelpers).contains('setText')
expect(helpers.size).toBe(0) expect(helpers.size).toBe(0)
expect(ir.block.operation).toEqual([]) expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SET_INHERIT_ATTRS,
},
])
expect(ir.block.effect).toMatchObject([ expect(ir.block.effect).toMatchObject([
{ {

View File

@ -28,7 +28,7 @@ import {
genMulti, genMulti,
} from './utils' } from './utils'
import { genExpression } from './expression' import { genExpression } from './expression'
import { genPropKey } from './prop' import { genPropKey, genPropValue } from './prop'
import { import {
createSimpleExpression, createSimpleExpression,
toValidAssetId, toValidAssetId,
@ -121,14 +121,15 @@ function genStaticProps(
} }
function genProp(prop: IRProp, context: CodegenContext, isStatic?: boolean) { function genProp(prop: IRProp, context: CodegenContext, isStatic?: boolean) {
const values = genPropValue(prop.values, context)
return [ return [
...genPropKey(prop, context), ...genPropKey(prop, context),
': ', ': ',
...(prop.handler ...(prop.handler
? genEventHandler(context, prop.values[0]) ? genEventHandler(context, prop.values[0])
: isStatic : isStatic
? ['() => (', ...genExpression(prop.values[0], context), ')'] ? ['() => (', ...values, ')']
: genExpression(prop.values[0], context)), : values),
...(prop.model ...(prop.model
? [...genModelEvent(prop, context), ...genModelModifiers(prop, context)] ? [...genModelEvent(prop, context), ...genModelModifiers(prop, context)]
: []), : []),

View File

@ -132,7 +132,7 @@ function genIdentifier(
prefix = `${raw}: ` prefix = `${raw}: `
} }
const type = bindingMetadata[raw] const type = bindingMetadata && bindingMetadata[raw]
if (inline) { if (inline) {
switch (type) { switch (type) {
case BindingTypes.SETUP_LET: case BindingTypes.SETUP_LET:

View File

@ -6,7 +6,7 @@ import { genFor } from './for'
import { genSetHtml } from './html' import { genSetHtml } from './html'
import { genIf } from './if' import { genIf } from './if'
import { genSetModelValue } from './modelValue' import { genSetModelValue } from './modelValue'
import { genDynamicProps, genSetProp } from './prop' import { genDynamicProps, genSetInheritAttrs, genSetProp } from './prop'
import { genDeclareOldRef, genSetTemplateRef } from './templateRef' import { genDeclareOldRef, genSetTemplateRef } from './templateRef'
import { genCreateTextNode, genSetText } from './text' import { genCreateTextNode, genSetText } from './text'
import { import {
@ -67,6 +67,8 @@ export function genOperation(
return genDeclareOldRef(oper) return genDeclareOldRef(oper)
case IRNodeTypes.SLOT_OUTLET_NODE: case IRNodeTypes.SLOT_OUTLET_NODE:
return genSlotOutlet(oper, context) return genSlotOutlet(oper, context)
case IRNodeTypes.SET_INHERIT_ATTRS:
return genSetInheritAttrs(oper, context)
} }
return [] return []

View File

@ -8,6 +8,7 @@ import {
IRDynamicPropsKind, IRDynamicPropsKind,
type IRProp, type IRProp,
type SetDynamicPropsIRNode, type SetDynamicPropsIRNode,
type SetInheritAttrsIRNode,
type SetPropIRNode, type SetPropIRNode,
type VaporHelper, type VaporHelper,
} from '../ir' } from '../ir'
@ -55,6 +56,7 @@ export function genSetProp(
`n${oper.element}`, `n${oper.element}`,
omitKey ? false : genExpression(key, context), omitKey ? false : genExpression(key, context),
genPropValue(values, context), genPropValue(values, context),
oper.root && 'true',
), ),
] ]
} }
@ -70,14 +72,18 @@ export function genDynamicProps(
...genCall( ...genCall(
vaporHelper('setDynamicProps'), vaporHelper('setDynamicProps'),
`n${oper.element}`, `n${oper.element}`,
...oper.props.map( genMulti(
props => DELIMITERS_ARRAY,
Array.isArray(props) ...oper.props.map(
? genLiteralObjectProps(props, context) // static and dynamic arg props props =>
: props.kind === IRDynamicPropsKind.ATTRIBUTE Array.isArray(props)
? genLiteralObjectProps([props], context) // dynamic arg props ? genLiteralObjectProps(props, context) // static and dynamic arg props
: genExpression(props.value, context), // v-bind="" : props.kind === IRDynamicPropsKind.ATTRIBUTE
? genLiteralObjectProps([props], context) // dynamic arg props
: genExpression(props.value, context), // v-bind=""
),
), ),
oper.root && 'true',
), ),
] ]
} }
@ -125,7 +131,10 @@ export function genPropKey(
return ['[', modifier && `${JSON.stringify(modifier)} + `, ...key, ']'] return ['[', modifier && `${JSON.stringify(modifier)} + `, ...key, ']']
} }
function genPropValue(values: SimpleExpressionNode[], context: CodegenContext) { export function genPropValue(
values: SimpleExpressionNode[],
context: CodegenContext,
): CodeFragment[] {
if (values.length === 1) { if (values.length === 1) {
return genExpression(values[0], context) return genExpression(values[0], context)
} }
@ -134,3 +143,28 @@ function genPropValue(values: SimpleExpressionNode[], context: CodegenContext) {
...values.map(expr => genExpression(expr, context)), ...values.map(expr => genExpression(expr, context)),
) )
} }
export function genSetInheritAttrs(
{ staticProps, dynamicProps }: SetInheritAttrsIRNode,
context: CodegenContext,
): CodeFragment[] {
const { vaporHelper } = context
// - `undefined` : no props
// - `false` : all props are static
// - `string[]` : list of props are dynamic
// - `true` : all props as dynamic
const value =
dynamicProps === true
? 'true'
: dynamicProps.length
? genMulti(
DELIMITERS_ARRAY,
...dynamicProps.map(p => JSON.stringify(p)),
)
: staticProps
? 'false'
: null
if (value == null) return []
return [NEWLINE, ...genCall(vaporHelper('setInheritAttrs'), value)]
}

View File

@ -46,7 +46,7 @@ export function genMulti(
...frags: CodeFragments[] ...frags: CodeFragments[]
): CodeFragment[] { ): CodeFragment[] {
if (placeholder) { if (placeholder) {
while (!frags[frags.length - 1]) { while (frags.length > 0 && !frags[frags.length - 1]) {
frags.pop() frags.pop()
} }
frags = frags.map(frag => frag || placeholder) frags = frags.map(frag => frag || placeholder)

View File

@ -24,6 +24,7 @@ export enum IRNodeTypes {
SET_HTML, SET_HTML,
SET_TEMPLATE_REF, SET_TEMPLATE_REF,
SET_MODEL_VALUE, SET_MODEL_VALUE,
SET_INHERIT_ATTRS,
INSERT_NODE, INSERT_NODE,
PREPEND_NODE, PREPEND_NODE,
@ -93,12 +94,14 @@ export interface SetPropIRNode extends BaseIRNode {
type: IRNodeTypes.SET_PROP type: IRNodeTypes.SET_PROP
element: number element: number
prop: IRProp prop: IRProp
root: boolean
} }
export interface SetDynamicPropsIRNode extends BaseIRNode { export interface SetDynamicPropsIRNode extends BaseIRNode {
type: IRNodeTypes.SET_DYNAMIC_PROPS type: IRNodeTypes.SET_DYNAMIC_PROPS
element: number element: number
props: IRProps[] props: IRProps[]
root: boolean
} }
export interface SetDynamicEventsIRNode extends BaseIRNode { export interface SetDynamicEventsIRNode extends BaseIRNode {
@ -156,6 +159,12 @@ export interface SetModelValueIRNode extends BaseIRNode {
isComponent: boolean isComponent: boolean
} }
export interface SetInheritAttrsIRNode extends BaseIRNode {
type: IRNodeTypes.SET_INHERIT_ATTRS
staticProps: boolean
dynamicProps: true | string[]
}
export interface CreateTextNodeIRNode extends BaseIRNode { export interface CreateTextNodeIRNode extends BaseIRNode {
type: IRNodeTypes.CREATE_TEXT_NODE type: IRNodeTypes.CREATE_TEXT_NODE
id: number id: number
@ -220,6 +229,7 @@ export type OperationNode =
| SetHtmlIRNode | SetHtmlIRNode
| SetTemplateRefIRNode | SetTemplateRefIRNode
| SetModelValueIRNode | SetModelValueIRNode
| SetInheritAttrsIRNode
| CreateTextNodeIRNode | CreateTextNodeIRNode
| InsertNodeIRNode | InsertNodeIRNode
| PrependNodeIRNode | PrependNodeIRNode

View File

@ -64,9 +64,15 @@ export const transformElement: NodeTransform = (node, context) => {
isDynamicComponent, isDynamicComponent,
) )
const singleRoot =
context.root === context.parent &&
context.parent.node.children.filter(
child => child.type !== NodeTypes.COMMENT,
).length === 1
;(isComponent ? transformComponentElement : transformNativeElement)( ;(isComponent ? transformComponentElement : transformNativeElement)(
node as any, node as any,
propsResult, propsResult,
singleRoot,
context as TransformContext<ElementNode>, context as TransformContext<ElementNode>,
isDynamicComponent, isDynamicComponent,
) )
@ -76,6 +82,7 @@ export const transformElement: NodeTransform = (node, context) => {
function transformComponentElement( function transformComponentElement(
node: ComponentNode, node: ComponentNode,
propsResult: PropsResult, propsResult: PropsResult,
singleRoot: boolean,
context: TransformContext, context: TransformContext,
isDynamicComponent: boolean, isDynamicComponent: boolean,
) { ) {
@ -108,16 +115,13 @@ function transformComponentElement(
} }
context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT
const root =
context.root === context.parent && context.parent.node.children.length === 1
context.registerOperation({ context.registerOperation({
type: IRNodeTypes.CREATE_COMPONENT_NODE, type: IRNodeTypes.CREATE_COMPONENT_NODE,
id: context.reference(), id: context.reference(),
tag, tag,
props: propsResult[0] ? propsResult[1] : [propsResult[1]], props: propsResult[0] ? propsResult[1] : [propsResult[1]],
asset, asset,
root, root: singleRoot,
slots: [...context.slots], slots: [...context.slots],
once: context.inVOnce, once: context.inVOnce,
dynamic: dynamicComponent, dynamic: dynamicComponent,
@ -162,6 +166,7 @@ function resolveSetupReference(name: string, context: TransformContext) {
function transformNativeElement( function transformNativeElement(
node: PlainElementNode, node: PlainElementNode,
propsResult: PropsResult, propsResult: PropsResult,
singleRoot: boolean,
context: TransformContext<ElementNode>, context: TransformContext<ElementNode>,
) { ) {
const { tag } = node const { tag } = node
@ -172,29 +177,43 @@ function transformNativeElement(
template += `<${tag}` template += `<${tag}`
if (scopeId) template += ` ${scopeId}` if (scopeId) template += ` ${scopeId}`
let staticProps = false
const dynamicProps: string[] = []
if (propsResult[0] /* dynamic props */) { if (propsResult[0] /* dynamic props */) {
const [, dynamicArgs, expressions] = propsResult const [, dynamicArgs, expressions] = propsResult
context.registerEffect(expressions, { context.registerEffect(expressions, {
type: IRNodeTypes.SET_DYNAMIC_PROPS, type: IRNodeTypes.SET_DYNAMIC_PROPS,
element: context.reference(), element: context.reference(),
props: dynamicArgs, props: dynamicArgs,
root: singleRoot,
}) })
} else { } else {
for (const prop of propsResult[1]) { for (const prop of propsResult[1]) {
const { key, values } = prop const { key, values } = prop
if (key.isStatic && values.length === 1 && values[0].isStatic) { if (key.isStatic && values.length === 1 && values[0].isStatic) {
staticProps = true
template += ` ${key.content}` template += ` ${key.content}`
if (values[0].content) template += `="${values[0].content}"` if (values[0].content) template += `="${values[0].content}"`
} else { } else {
dynamicProps.push(key.content)
context.registerEffect(values, { context.registerEffect(values, {
type: IRNodeTypes.SET_PROP, type: IRNodeTypes.SET_PROP,
element: context.reference(), element: context.reference(),
prop, prop,
root: singleRoot,
}) })
} }
} }
} }
if (singleRoot) {
context.registerOperation({
type: IRNodeTypes.SET_INHERIT_ATTRS,
staticProps: staticProps,
dynamicProps: propsResult[0] ? true : dynamicProps,
})
}
template += `>` + context.childrenTemplate.join('') template += `>` + context.childrenTemplate.join('')
// TODO remove unnecessary close tag, e.g. if it's the last element of the template // TODO remove unnecessary close tag, e.g. if it's the last element of the template
if (!isVoidTag(tag)) { if (!isVoidTag(tag)) {

View File

@ -11,6 +11,7 @@ import {
ref, ref,
renderEffect, renderEffect,
setDynamicProps, setDynamicProps,
setInheritAttrs,
template, template,
watchEffect, watchEffect,
} from '../src' } from '../src'
@ -77,9 +78,7 @@ describe('api: setup context', () => {
inheritAttrs: false, inheritAttrs: false,
setup(props, { attrs }) { setup(props, { attrs }) {
const el = document.createElement('div') const el = document.createElement('div')
renderEffect(() => { renderEffect(() => setDynamicProps(el, [attrs]))
setDynamicProps(el, attrs)
})
return el return el
}, },
}) })
@ -103,23 +102,24 @@ describe('api: setup context', () => {
const toggle = ref(true) const toggle = ref(true)
const Wrapper = defineComponent({ const Wrapper = defineComponent({
setup(_, { slots }) { setup(_) {
return slots.default!() const n0 = createSlot('default')
setInheritAttrs(false, true)
return n0
}, },
}) })
const Child = defineComponent({ const Child = defineComponent({
inheritAttrs: false, inheritAttrs: false,
setup(_: any, { attrs }: any) { setup(_: any, { attrs }: any) {
return createComponent(Wrapper, null, { const n0 = createComponent(Wrapper, null, {
default: () => { default: () => {
const n0 = template('<div>')() as HTMLDivElement const n0 = template('<div>')() as HTMLDivElement
renderEffect(() => { renderEffect(() => setDynamicProps(n0, [attrs], true))
setDynamicProps(n0, attrs)
})
return n0 return n0
}, },
}) })
return n0
}, },
}) })

View File

@ -3,6 +3,7 @@ import {
getCurrentInstance, getCurrentInstance,
nextTick, nextTick,
ref, ref,
setInheritAttrs,
setText, setText,
template, template,
watchEffect, watchEffect,
@ -18,7 +19,8 @@ describe('attribute fallthrough', () => {
props: ['foo'], props: ['foo'],
render() { render() {
const instance = getCurrentInstance()! const instance = getCurrentInstance()!
const n0 = t0() const n0 = t0() as Element
setInheritAttrs()
watchEffect(() => setText(n0, instance.props.foo)) watchEffect(() => setText(n0, instance.props.foo))
return n0 return n0
}, },
@ -62,7 +64,8 @@ describe('attribute fallthrough', () => {
inheritAttrs: false, inheritAttrs: false,
render() { render() {
const instance = getCurrentInstance()! const instance = getCurrentInstance()!
const n0 = t0() const n0 = t0() as Element
setInheritAttrs()
watchEffect(() => setText(n0, instance.props.foo)) watchEffect(() => setText(n0, instance.props.foo))
return n0 return n0
}, },
@ -105,7 +108,8 @@ describe('attribute fallthrough', () => {
props: ['custom-attr'], props: ['custom-attr'],
render() { render() {
const instance = getCurrentInstance()! const instance = getCurrentInstance()!
const n0 = t0() const n0 = t0() as Element
setInheritAttrs()
watchEffect(() => setText(n0, instance.attrs.foo)) watchEffect(() => setText(n0, instance.attrs.foo))
return n0 return n0
}, },

View File

@ -407,25 +407,25 @@ describe('patchProp', () => {
describe('setDynamicProps', () => { describe('setDynamicProps', () => {
test('basic set dynamic props', () => { test('basic set dynamic props', () => {
const el = document.createElement('div') const el = document.createElement('div')
setDynamicProps(el, { foo: 'val' }, { bar: 'val' }) setDynamicProps(el, [{ foo: 'val' }, { bar: 'val' }])
expect(el.getAttribute('foo')).toBe('val') expect(el.getAttribute('foo')).toBe('val')
expect(el.getAttribute('bar')).toBe('val') expect(el.getAttribute('bar')).toBe('val')
}) })
test('should merge props', () => { test('should merge props', () => {
const el = document.createElement('div') const el = document.createElement('div')
setDynamicProps(el, { foo: 'val' }, { foo: 'newVal' }) setDynamicProps(el, [{ foo: 'val' }, { foo: 'newVal' }])
expect(el.getAttribute('foo')).toBe('newVal') expect(el.getAttribute('foo')).toBe('newVal')
}) })
test('should reset old props', () => { test('should reset old props', () => {
const el = document.createElement('div') const el = document.createElement('div')
setDynamicProps(el, { foo: 'val' }) setDynamicProps(el, [{ foo: 'val' }])
expect(el.attributes.length).toBe(1) expect(el.attributes.length).toBe(1)
expect(el.getAttribute('foo')).toBe('val') expect(el.getAttribute('foo')).toBe('val')
setDynamicProps(el, { bar: 'val' }) setDynamicProps(el, [{ bar: 'val' }])
expect(el.attributes.length).toBe(1) expect(el.attributes.length).toBe(1)
expect(el.getAttribute('bar')).toBe('val') expect(el.getAttribute('bar')).toBe('val')
expect(el.getAttribute('foo')).toBeNull() expect(el.getAttribute('foo')).toBeNull()
@ -434,18 +434,18 @@ describe('patchProp', () => {
test('should reset old modifier props', () => { test('should reset old modifier props', () => {
const el = document.createElement('div') const el = document.createElement('div')
setDynamicProps(el, { ['.foo']: 'val' }) setDynamicProps(el, [{ ['.foo']: 'val' }])
expect((el as any).foo).toBe('val') expect((el as any).foo).toBe('val')
setDynamicProps(el, { ['.bar']: 'val' }) setDynamicProps(el, [{ ['.bar']: 'val' }])
expect((el as any).bar).toBe('val') expect((el as any).bar).toBe('val')
expect((el as any).foo).toBe('') expect((el as any).foo).toBe('')
setDynamicProps(el, { ['^foo']: 'val' }) setDynamicProps(el, [{ ['^foo']: 'val' }])
expect(el.attributes.length).toBe(1) expect(el.attributes.length).toBe(1)
expect(el.getAttribute('foo')).toBe('val') expect(el.getAttribute('foo')).toBe('val')
setDynamicProps(el, { ['^bar']: 'val' }) setDynamicProps(el, [{ ['^bar']: 'val' }])
expect(el.attributes.length).toBe(1) expect(el.attributes.length).toBe(1)
expect(el.getAttribute('bar')).toBe('val') expect(el.getAttribute('bar')).toBe('val')
expect(el.getAttribute('foo')).toBeNull() expect(el.getAttribute('foo')).toBeNull()

View File

@ -12,11 +12,12 @@ import {
walkRawProps, walkRawProps,
} from './componentProps' } from './componentProps'
import { type RawSlots, isDynamicSlotFn } from './componentSlots' import { type RawSlots, isDynamicSlotFn } from './componentSlots'
import { withAttrs } from './componentAttrs' import { setInheritAttrs, withAttrs } from './componentAttrs'
import { isString } from '@vue/shared' import { isString } from '@vue/shared'
import { renderEffect } from './renderEffect' import { renderEffect } from './renderEffect'
import { normalizeBlock } from './dom/element' import { normalizeBlock } from './dom/element'
import { setDynamicProp } from './dom/prop' import { setClass, setDynamicProp } from './dom/prop'
import { setStyle } from './dom/style'
export function createComponent( export function createComponent(
comp: Component | string, comp: Component | string,
@ -25,11 +26,12 @@ export function createComponent(
singleRoot: boolean = false, singleRoot: boolean = false,
once: boolean = false, once: boolean = false,
): ComponentInternalInstance | HTMLElement { ): ComponentInternalInstance | HTMLElement {
const current = currentInstance!
if (isString(comp)) { if (isString(comp)) {
return fallbackComponent(comp, rawProps, slots) return fallbackComponent(comp, rawProps, slots, current, singleRoot)
} }
const current = currentInstance!
const instance = createComponentInstance( const instance = createComponentInstance(
comp, comp,
singleRoot ? withAttrs(rawProps) : rawProps, singleRoot ? withAttrs(rawProps) : rawProps,
@ -48,16 +50,31 @@ function fallbackComponent(
comp: string, comp: string,
rawProps: RawProps | null, rawProps: RawProps | null,
slots: RawSlots | null, slots: RawSlots | null,
instance: ComponentInternalInstance,
singleRoot: boolean = false,
): HTMLElement { ): HTMLElement {
// eslint-disable-next-line no-restricted-globals // eslint-disable-next-line no-restricted-globals
const el = document.createElement(comp) const el = document.createElement(comp)
if (rawProps) { if (rawProps || Object.keys(instance.attrs).length) {
rawProps = normalizeRawProps(rawProps) rawProps = [() => instance.attrs, ...normalizeRawProps(rawProps)]
renderEffect(() => { renderEffect(() => {
walkRawProps(rawProps as NormalizedRawProps, (key, value, getter) => { let classes: unknown[] | undefined
setDynamicProp(el, key, getter ? value() : value) let styles: unknown[] | undefined
})
walkRawProps(
rawProps as NormalizedRawProps,
(key, valueOrGetter, getter) => {
const value = getter ? valueOrGetter() : valueOrGetter
if (key === 'class') (classes ||= []).push(value)
else if (key === 'style') (styles ||= []).push(value)
else setDynamicProp(el, key, value)
},
)
if (classes) setClass(el, classes)
if (styles) setStyle(el, styles)
}) })
} }
@ -72,5 +89,9 @@ function fallbackComponent(
} }
} }
if (singleRoot) {
setInheritAttrs(true)
}
return el return el
} }

View File

@ -16,10 +16,10 @@ import {
shallowReadonly, shallowReadonly,
} from '@vue/reactivity' } from '@vue/reactivity'
import { isArray, isFunction, isObject } from '@vue/shared' import { isArray, isFunction, isObject } from '@vue/shared'
import { fallThroughAttrs } from './componentAttrs'
import { VaporErrorCodes, callWithErrorHandling } from './errorHandling' import { VaporErrorCodes, callWithErrorHandling } from './errorHandling'
import { endMeasure, startMeasure } from './profiling' import { endMeasure, startMeasure } from './profiling'
import { devtoolsComponentAdded } from './devtools' import { devtoolsComponentAdded } from './devtools'
import { fallThroughAttrs } from './componentAttrs'
export const fragmentKey: unique symbol = Symbol(__DEV__ ? `fragmentKey` : ``) export const fragmentKey: unique symbol = Symbol(__DEV__ ? `fragmentKey` : ``)
@ -86,9 +86,6 @@ export function setupComponent(instance: ComponentInternalInstance): void {
resetTracking() resetTracking()
} }
if (block instanceof DocumentFragment) {
block = Array.from(block.childNodes)
}
if (!block) { if (!block) {
// TODO: warn no template // TODO: warn no template
block = [] block = []

View File

@ -174,6 +174,13 @@ export interface ComponentInternalInstance {
emit: EmitFn emit: EmitFn
emitted: Record<string, boolean> | null emitted: Record<string, boolean> | null
attrs: Data attrs: Data
/**
* - `undefined` : no props
* - `false` : all props are static
* - `string[]` : list of props are dynamic
* - `true` : all props as dynamic
*/
dynamicAttrs?: string[] | boolean
slots: StaticSlots slots: StaticSlots
refs: Data refs: Data
// exposed properties via expose() // exposed properties via expose()

View File

@ -1,11 +1,14 @@
import { camelize, isArray } from '@vue/shared' import { camelize, isArray, normalizeClass, normalizeStyle } from '@vue/shared'
import { type ComponentInternalInstance, currentInstance } from './component' import { type ComponentInternalInstance, currentInstance } from './component'
import { isEmitListener } from './componentEmits' import { isEmitListener } from './componentEmits'
import { setDynamicProps } from './dom/prop'
import { type RawProps, walkRawProps } from './componentProps' import { type RawProps, walkRawProps } from './componentProps'
import { renderEffect } from './renderEffect' import { renderEffect } from './renderEffect'
import { mergeProp, setDynamicProp } from './dom/prop'
export function patchAttrs(instance: ComponentInternalInstance): void { export function patchAttrs(
instance: ComponentInternalInstance,
hasDynamicProps?: boolean,
): void {
const { const {
attrs, attrs,
rawProps, rawProps,
@ -14,6 +17,8 @@ export function patchAttrs(instance: ComponentInternalInstance): void {
if (!rawProps.length) return if (!rawProps.length) return
const keys = new Set<string>() const keys = new Set<string>()
const classes: any[] = []
const styles: any[] = []
walkRawProps(rawProps, registerAttr) walkRawProps(rawProps, registerAttr)
for (const key in attrs) { for (const key in attrs) {
@ -22,14 +27,42 @@ export function patchAttrs(instance: ComponentInternalInstance): void {
} }
} }
setClassOrStyle(classes, 'class', normalizeClass)
setClassOrStyle(styles, 'style', normalizeStyle)
function setClassOrStyle(
values: any[],
field: 'class' | 'style',
normalize: (value: any) => any,
) {
if (values.length) {
if (hasDynamicProps) {
Object.defineProperty(attrs, field, {
get() {
return normalize(values.map(value => value()))
},
enumerable: true,
configurable: true,
})
} else {
attrs[field] = normalizeClass(values)
}
}
}
function registerAttr(key: string, value: any, getter?: boolean) { function registerAttr(key: string, value: any, getter?: boolean) {
if ( if (
(!options || !(camelize(key) in options)) && (!options || !(camelize(key) in options)) &&
!isEmitListener(instance.emitsOptions, key) && !isEmitListener(instance.emitsOptions, key) &&
!keys.has(key) (key === 'class' || key === 'style' || !keys.has(key))
) { ) {
keys.add(key) keys.add(key)
if (getter) {
if (key === 'class' || key === 'style') {
;(key === 'class' ? classes : styles).push(
hasDynamicProps ? (getter ? value : () => value) : value,
)
} else if (getter) {
Object.defineProperty(attrs, key, { Object.defineProperty(attrs, key, {
get: value, get: value,
enumerable: true, enumerable: true,
@ -57,16 +90,47 @@ export function fallThroughAttrs(instance: ComponentInternalInstance): void {
const { const {
block, block,
type: { inheritAttrs }, type: { inheritAttrs },
dynamicAttrs,
} = instance } = instance
if (inheritAttrs === false) return if (
inheritAttrs === false ||
!(block instanceof Element) ||
// all props as dynamic
dynamicAttrs === true
)
return
if (block instanceof Element) { const hasStaticAttrs = dynamicAttrs || dynamicAttrs === false
let initial: Record<string, string> | undefined
if (hasStaticAttrs) {
// attrs in static template // attrs in static template
const initial: Record<string, string> = {} initial = {}
for (let i = 0; i < block.attributes.length; i++) { for (let i = 0; i < block.attributes.length; i++) {
const attr = block.attributes[i] const attr = block.attributes[i]
if (dynamicAttrs && dynamicAttrs.includes(attr.name)) continue
initial[attr.name] = attr.value initial[attr.name] = attr.value
} }
renderEffect(() => setDynamicProps(block, instance.attrs, initial))
} }
renderEffect(() => {
for (const key in instance.attrs) {
if (dynamicAttrs && dynamicAttrs.includes(key)) continue
let value: unknown
if (hasStaticAttrs) {
value = mergeProp(key, instance.attrs[key], initial![key])
} else {
value = instance.attrs[key]
}
setDynamicProp(block, key, value)
}
})
}
export function setInheritAttrs(dynamicAttrs?: string[] | boolean): void {
const instance = currentInstance!
if (instance.type.inheritAttrs === false) return
instance.dynamicAttrs = dynamicAttrs
} }

View File

@ -116,7 +116,7 @@ export function initProps(
} }
if (hasDynamicProps) { if (hasDynamicProps) {
firstEffect(instance, () => patchAttrs(instance)) firstEffect(instance, () => patchAttrs(instance, true))
} else { } else {
patchAttrs(instance) patchAttrs(instance)
} }

View File

@ -18,9 +18,20 @@ import {
} from '../componentMetadata' } from '../componentMetadata'
import { on } from './event' import { on } from './event'
import type { Data } from '@vue/runtime-shared' import type { Data } from '@vue/runtime-shared'
import { currentInstance } from '../component'
export function mergeInheritAttr(key: string, value: any): unknown {
const instance = currentInstance!
return mergeProp(key, instance.attrs[key], value)
}
export function setClass(el: Element, value: any, root?: boolean): void {
const prev = recordPropMetadata(
el,
'class',
(value = normalizeClass(root ? mergeInheritAttr('class', value) : value)),
)
export function setClass(el: Element, value: any): void {
const prev = recordPropMetadata(el, 'class', (value = normalizeClass(value)))
if (value !== prev && (value || prev)) { if (value !== prev && (value || prev)) {
el.className = value el.className = value
} }
@ -132,8 +143,15 @@ export function setDynamicProp(el: Element, key: string, value: any): void {
} }
} }
export function setDynamicProps(el: Element, ...args: any): void { export function setDynamicProps(
el: Element,
args: any[],
root?: boolean,
): void {
const oldProps = getMetadata(el)[MetadataKind.prop] const oldProps = getMetadata(el)[MetadataKind.prop]
if (root) {
args.unshift(currentInstance!.attrs)
}
const props = args.length > 1 ? mergeProps(...args) : args[0] const props = args.length > 1 ? mergeProps(...args) : args[0]
for (const key in oldProps) { for (const key in oldProps) {
@ -153,32 +171,36 @@ export function setDynamicProps(el: Element, ...args: any): void {
} }
} }
// TODO copied from runtime-core export function mergeProp(
key: string,
existing: unknown,
incoming: unknown,
): unknown {
if (key === 'class') {
if (existing !== incoming) {
return normalizeClass([existing, incoming])
}
} else if (key === 'style') {
return normalizeStyle([existing, incoming])
} else if (isOn(key)) {
if (
incoming &&
existing !== incoming &&
!(isArray(existing) && existing.includes(incoming))
) {
return existing ? [].concat(existing as any, incoming as any) : incoming
}
}
return incoming
}
export function mergeProps(...args: Data[]): Data { export function mergeProps(...args: Data[]): Data {
const ret: Data = {} const ret: Data = {}
for (let i = 0; i < args.length; i++) { for (let i = 0; i < args.length; i++) {
const toMerge = args[i] const toMerge = args[i]
for (const key in toMerge) { for (const key in toMerge) {
if (key === 'class') { if (key !== '') {
if (ret.class !== toMerge.class) { ret[key] = mergeProp(key, ret[key], toMerge[key])
ret.class = normalizeClass([ret.class, toMerge.class])
}
} else if (key === 'style') {
ret.style = normalizeStyle([ret.style, toMerge.style])
} else if (isOn(key)) {
const existing = ret[key]
const incoming = toMerge[key]
if (
incoming &&
existing !== incoming &&
!(isArray(existing) && existing.includes(incoming))
) {
ret[key] = existing
? [].concat(existing as any, incoming as any)
: incoming
}
} else if (key !== '') {
ret[key] = toMerge[key]
} }
} }
} }

View File

@ -8,9 +8,14 @@ import {
} from '@vue/shared' } from '@vue/shared'
import { warn } from '../warning' import { warn } from '../warning'
import { recordPropMetadata } from '../componentMetadata' import { recordPropMetadata } from '../componentMetadata'
import { mergeInheritAttr } from './prop'
export function setStyle(el: HTMLElement, value: any): void { export function setStyle(el: HTMLElement, value: any, root?: boolean): void {
const prev = recordPropMetadata(el, 'style', (value = normalizeStyle(value))) const prev = recordPropMetadata(
el,
'style',
(value = normalizeStyle(root ? mergeInheritAttr('style', value) : value)),
)
patchStyle(el, prev, value) patchStyle(el, prev, value)
} }

View File

@ -130,6 +130,7 @@ export { createIf } from './apiCreateIf'
export { createFor, createForSlots } from './apiCreateFor' export { createFor, createForSlots } from './apiCreateFor'
export { createComponent } from './apiCreateComponent' export { createComponent } from './apiCreateComponent'
export { createSelector } from './apiCreateSelector' export { createSelector } from './apiCreateSelector'
export { setInheritAttrs } from './componentAttrs'
export { export {
resolveComponent, resolveComponent,

View File

@ -12,7 +12,7 @@
"vue": "workspace:*" "vue": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "https://pkg.pr.new/@vitejs/plugin-vue@e3c5ce5", "@vitejs/plugin-vue": "https://pkg.pr.new/@vitejs/plugin-vue@481bcd4",
"vite": "catalog:", "vite": "catalog:",
"vite-hyper-config": "^0.4.0", "vite-hyper-config": "^0.4.0",
"vite-plugin-inspect": "^0.8.7" "vite-plugin-inspect": "^0.8.7"

View File

@ -521,8 +521,8 @@ importers:
version: link:../packages/vue version: link:../packages/vue
devDependencies: devDependencies:
'@vitejs/plugin-vue': '@vitejs/plugin-vue':
specifier: https://pkg.pr.new/@vitejs/plugin-vue@e3c5ce5 specifier: https://pkg.pr.new/@vitejs/plugin-vue@481bcd4
version: https://pkg.pr.new/@vitejs/plugin-vue@e3c5ce5(vite@5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0))(vue@packages+vue) version: https://pkg.pr.new/@vitejs/plugin-vue@481bcd4(vite@5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0))(vue@packages+vue)
vite: vite:
specifier: 'catalog:' specifier: 'catalog:'
version: 5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0) version: 5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0)
@ -1416,6 +1416,14 @@ packages:
vite: ^5.0.0 vite: ^5.0.0
vue: ^3.2.25 vue: ^3.2.25
'@vitejs/plugin-vue@https://pkg.pr.new/@vitejs/plugin-vue@481bcd4':
resolution: {tarball: https://pkg.pr.new/@vitejs/plugin-vue@481bcd4}
version: 5.1.5
engines: {node: ^18.0.0 || >=20.0.0}
peerDependencies:
vite: ^5.0.0
vue: ^3.2.25
'@vitejs/plugin-vue@https://pkg.pr.new/@vitejs/plugin-vue@e3c5ce5': '@vitejs/plugin-vue@https://pkg.pr.new/@vitejs/plugin-vue@e3c5ce5':
resolution: {tarball: https://pkg.pr.new/@vitejs/plugin-vue@e3c5ce5} resolution: {tarball: https://pkg.pr.new/@vitejs/plugin-vue@e3c5ce5}
version: 5.1.4 version: 5.1.4
@ -4495,16 +4503,16 @@ snapshots:
vite: 5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0) vite: 5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0)
vue: link:packages/vue vue: link:packages/vue
'@vitejs/plugin-vue@https://pkg.pr.new/@vitejs/plugin-vue@481bcd4(vite@5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0))(vue@packages+vue)':
dependencies:
vite: 5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0)
vue: link:packages/vue
'@vitejs/plugin-vue@https://pkg.pr.new/@vitejs/plugin-vue@e3c5ce5(vite@5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0))(vue@3.5.12(typescript@5.6.2))': '@vitejs/plugin-vue@https://pkg.pr.new/@vitejs/plugin-vue@e3c5ce5(vite@5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0))(vue@3.5.12(typescript@5.6.2))':
dependencies: dependencies:
vite: 5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0) vite: 5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0)
vue: 3.5.12(typescript@5.6.2) vue: 3.5.12(typescript@5.6.2)
'@vitejs/plugin-vue@https://pkg.pr.new/@vitejs/plugin-vue@e3c5ce5(vite@5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0))(vue@packages+vue)':
dependencies:
vite: 5.4.8(@types/node@22.8.7)(sass@1.80.6)(terser@5.33.0)
vue: link:packages/vue
'@vitest/coverage-v8@2.1.1(vitest@2.1.1)': '@vitest/coverage-v8@2.1.1(vitest@2.1.1)':
dependencies: dependencies:
'@ampproject/remapping': 2.3.0 '@ampproject/remapping': 2.3.0