wip(vapor): align compiler with new props runtime behavior

This commit is contained in:
Evan You 2024-12-04 20:55:10 +08:00
parent 59b1aeda51
commit 23ba438be1
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
10 changed files with 198 additions and 198 deletions

View File

@ -100,9 +100,7 @@ exports[`compiler: element transform > component > should wrap as function if v-
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [
{ onBar: () => $event => (_ctx.handleBar($event)) }
], null, true)
const n0 = _createComponent(_component_Foo, { onBar: () => $event => (_ctx.handleBar($event)) }, null, true)
return n0
}"
`;
@ -112,12 +110,10 @@ exports[`compiler: element transform > component > static props 1`] = `
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [
{
id: () => ("foo"),
class: () => ("bar")
}
], null, true)
const n0 = _createComponent(_component_Foo, {
id: () => ("foo"),
class: () => ("bar")
}, null, true)
return n0
}"
`;
@ -127,9 +123,9 @@ exports[`compiler: element transform > component > v-bind="obj" 1`] = `
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [
const n0 = _createComponent(_component_Foo, { $: [
() => (_ctx.obj)
], null, true)
] }, null, true)
return n0
}"
`;
@ -139,10 +135,12 @@ exports[`compiler: element transform > component > v-bind="obj" after static pro
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [
{ id: () => ("foo") },
() => (_ctx.obj)
], null, true)
const n0 = _createComponent(_component_Foo, {
id: () => ("foo"),
$: [
() => (_ctx.obj)
]
}, null, true)
return n0
}"
`;
@ -152,10 +150,10 @@ exports[`compiler: element transform > component > v-bind="obj" before static pr
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [
const n0 = _createComponent(_component_Foo, { $: [
() => (_ctx.obj),
{ id: () => ("foo") }
], null, true)
] }, null, true)
return n0
}"
`;
@ -165,11 +163,13 @@ exports[`compiler: element transform > component > v-bind="obj" between static p
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [
{ id: () => ("foo") },
() => (_ctx.obj),
{ class: () => ("bar") }
], null, true)
const n0 = _createComponent(_component_Foo, {
id: () => ("foo"),
$: [
() => (_ctx.obj),
{ class: () => ("bar") }
]
}, null, true)
return n0
}"
`;
@ -179,9 +179,9 @@ exports[`compiler: element transform > component > v-on="obj" 1`] = `
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [
const n0 = _createComponent(_component_Foo, { $: [
() => (_toHandlers(_ctx.obj))
], null, true)
] }, null, true)
return n0
}"
`;
@ -192,10 +192,10 @@ import { resolveComponent as _resolveComponent, createComponent as _createCompon
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [
const n0 = _createComponent(_component_Foo, { $: [
() => ({ [_toHandlerKey(_ctx.foo-_ctx.bar)]: () => _ctx.bar }),
() => ({ [_toHandlerKey(_ctx.baz)]: () => _ctx.qux })
], null, true)
] }, null, true)
return n0
}"
`;
@ -205,10 +205,10 @@ exports[`compiler: element transform > component with dynamic prop arguments 1`]
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [
const n0 = _createComponent(_component_Foo, { $: [
() => ({ [_ctx.foo-_ctx.bar]: _ctx.bar }),
() => ({ [_ctx.baz]: _ctx.qux })
], null, true)
] }, null, true)
return n0
}"
`;
@ -245,9 +245,7 @@ exports[`compiler: element transform > dynamic component > normal component with
export function render(_ctx) {
const _component_custom_input = _resolveComponent("custom-input")
const n0 = _createComponent(_component_custom_input, [
{ is: () => ("foo") }
], null, true)
const n0 = _createComponent(_component_custom_input, { is: () => ("foo") }, null, true)
return n0
}"
`;

View File

@ -27,9 +27,7 @@ exports[`compiler: transform <slot> outlets > default slot outlet with props & f
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = _createSlot("default", [
{ foo: () => (_ctx.bar) }
], () => {
const n0 = _createSlot("default", { foo: () => (_ctx.bar) }, () => {
const n2 = t0()
return n2
})
@ -41,13 +39,11 @@ exports[`compiler: transform <slot> outlets > default slot outlet with props 1`]
"import { createSlot as _createSlot } from 'vue/vapor';
export function render(_ctx) {
const n0 = _createSlot("default", [
{
foo: () => ("bar"),
baz: () => (_ctx.qux),
fooBar: () => (_ctx.foo-_ctx.bar)
}
])
const n0 = _createSlot("default", {
foo: () => ("bar"),
baz: () => (_ctx.qux),
fooBar: () => (_ctx.foo-_ctx.bar)
})
return n0
}"
`;
@ -107,9 +103,7 @@ exports[`compiler: transform <slot> outlets > named slot outlet with props & fal
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = _createSlot("foo", [
{ foo: () => (_ctx.bar) }
], () => {
const n0 = _createSlot("foo", { foo: () => (_ctx.bar) }, () => {
const n2 = t0()
return n2
})
@ -130,12 +124,10 @@ exports[`compiler: transform <slot> outlets > statically named slot outlet with
"import { createSlot as _createSlot } from 'vue/vapor';
export function render(_ctx) {
const n0 = _createSlot("foo", [
{
foo: () => ("bar"),
baz: () => (_ctx.qux)
}
])
const n0 = _createSlot("foo", {
foo: () => ("bar"),
baz: () => (_ctx.qux)
})
return n0
}"
`;
@ -144,11 +136,13 @@ exports[`compiler: transform <slot> outlets > statically named slot outlet with
"import { createSlot as _createSlot } from 'vue/vapor';
export function render(_ctx) {
const n0 = _createSlot("foo", [
{ foo: () => ("bar") },
() => (_ctx.obj),
{ baz: () => (_ctx.qux) }
])
const n0 = _createSlot("foo", {
foo: () => ("bar"),
$: [
() => (_ctx.obj),
{ baz: () => (_ctx.qux) }
]
})
return n0
}"
`;
@ -157,11 +151,13 @@ exports[`compiler: transform <slot> outlets > statically named slot outlet with
"import { createSlot as _createSlot, toHandlers as _toHandlers } from 'vue/vapor';
export function render(_ctx) {
const n0 = _createSlot("default", [
{ onClick: () => _ctx.foo },
() => (_toHandlers(_ctx.bar)),
{ baz: () => (_ctx.qux) }
])
const n0 = _createSlot("default", {
onClick: () => _ctx.foo,
$: [
() => (_toHandlers(_ctx.bar)),
{ baz: () => (_ctx.qux) }
]
})
return n0
}"
`;

View File

@ -464,9 +464,7 @@ exports[`compiler v-bind > number value 1`] = `
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [
{ depth: () => (0) }
], null, true)
const n0 = _createComponent(_component_Comp, { depth: () => (0) }, null, true)
return n0
}"
`;

View File

@ -5,11 +5,9 @@ exports[`compiler: vModel transform > component > v-model for component should g
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [
{ modelValue: () => (_ctx.foo),
"onUpdate:modelValue": () => $event => (_ctx.foo = $event),
modelModifiers: () => ({ trim: true, "bar-baz": true }) }
], null, true)
const n0 = _createComponent(_component_Comp, { modelValue: () => (_ctx.foo),
"onUpdate:modelValue": () => $event => (_ctx.foo = $event),
modelModifiers: () => ({ trim: true, "bar-baz": true }) }, null, true)
return n0
}"
`;
@ -19,10 +17,8 @@ exports[`compiler: vModel transform > component > v-model for component should w
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [
{ modelValue: () => (_ctx.foo),
"onUpdate:modelValue": () => $event => (_ctx.foo = $event) }
], null, true)
const n0 = _createComponent(_component_Comp, { modelValue: () => (_ctx.foo),
"onUpdate:modelValue": () => $event => (_ctx.foo = $event) }, null, true)
return n0
}"
`;
@ -32,16 +28,14 @@ exports[`compiler: vModel transform > component > v-model with arguments for com
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [
{
foo: () => (_ctx.foo),
"onUpdate:foo": () => $event => (_ctx.foo = $event),
fooModifiers: () => ({ trim: true }),
bar: () => (_ctx.bar),
"onUpdate:bar": () => $event => (_ctx.bar = $event),
barModifiers: () => ({ number: true })
}
], null, true)
const n0 = _createComponent(_component_Comp, {
foo: () => (_ctx.foo),
"onUpdate:foo": () => $event => (_ctx.foo = $event),
fooModifiers: () => ({ trim: true }),
bar: () => (_ctx.bar),
"onUpdate:bar": () => $event => (_ctx.bar = $event),
barModifiers: () => ({ number: true })
}, null, true)
return n0
}"
`;
@ -51,10 +45,8 @@ exports[`compiler: vModel transform > component > v-model with arguments for com
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [
{ bar: () => (_ctx.foo),
"onUpdate:bar": () => $event => (_ctx.foo = $event) }
], null, true)
const n0 = _createComponent(_component_Comp, { bar: () => (_ctx.foo),
"onUpdate:bar": () => $event => (_ctx.foo = $event) }, null, true)
return n0
}"
`;
@ -64,14 +56,14 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [
const n0 = _createComponent(_component_Comp, { $: [
() => ({ [_ctx.foo]: _ctx.foo,
["onUpdate:" + _ctx.foo]: () => $event => (_ctx.foo = $event),
[_ctx.foo + "Modifiers"]: () => ({ trim: true }) }),
() => ({ [_ctx.bar]: _ctx.bar,
["onUpdate:" + _ctx.bar]: () => $event => (_ctx.bar = $event),
[_ctx.bar + "Modifiers"]: () => ({ number: true }) })
], null, true)
] }, null, true)
return n0
}"
`;
@ -81,10 +73,10 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [
const n0 = _createComponent(_component_Comp, { $: [
() => ({ [_ctx.arg]: _ctx.foo,
["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event) })
], null, true)
] }, null, true)
return n0
}"
`;

View File

@ -43,9 +43,7 @@ const t0 = _template("<div></div>")
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n1 = t0()
const n0 = _createComponent(_component_Comp, [
{ id: () => (_ctx.foo) }
], null, null, true)
const n0 = _createComponent(_component_Comp, { id: () => (_ctx.foo) }, null, null, true)
_insert(n0, n1)
return n1
}"

View File

@ -199,12 +199,10 @@ describe('compiler: element transform', () => {
)
expect(code).toMatchSnapshot()
expect(code).contains(`[
{
id: () => ("foo"),
class: () => ("bar")
}
]`)
expect(code).contains(`{
id: () => ("foo"),
class: () => ("bar")
}`)
expect(ir.block.operation).toMatchObject([
{
@ -273,10 +271,12 @@ describe('compiler: element transform', () => {
`<Foo id="foo" v-bind="obj" />`,
)
expect(code).toMatchSnapshot()
expect(code).contains(`[
{ id: () => ("foo") },
() => (_ctx.obj)
]`)
expect(code).contains(`{
id: () => ("foo"),
$: [
() => (_ctx.obj)
]
}`)
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
@ -321,11 +321,13 @@ describe('compiler: element transform', () => {
`<Foo id="foo" v-bind="obj" class="bar" />`,
)
expect(code).toMatchSnapshot()
expect(code).contains(`[
{ id: () => ("foo") },
() => (_ctx.obj),
{ class: () => ("bar") }
]`)
expect(code).contains(`{
id: () => ("foo"),
$: [
() => (_ctx.obj),
{ class: () => ("bar") }
]
}`)
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,

View File

@ -208,9 +208,9 @@ describe('compiler: vModel transform', () => {
test('v-model for component should work', () => {
const { code, ir } = compileWithVModel('<Comp v-model="foo" />')
expect(code).toMatchSnapshot()
expect(code).contains(`modelValue: () => (_ctx.foo),`)
expect(code).contains(
`modelValue: () => (_ctx.foo),
"onUpdate:modelValue": () => $event => (_ctx.foo = $event)`,
`"onUpdate:modelValue": () => $event => (_ctx.foo = $event)`,
)
expect(ir.block.operation).toMatchObject([
{
@ -233,9 +233,9 @@ describe('compiler: vModel transform', () => {
test('v-model with arguments for component should work', () => {
const { code, ir } = compileWithVModel('<Comp v-model:bar="foo" />')
expect(code).toMatchSnapshot()
expect(code).contains(`bar: () => (_ctx.foo),`)
expect(code).contains(
`bar: () => (_ctx.foo),
"onUpdate:bar": () => $event => (_ctx.foo = $event)`,
`"onUpdate:bar": () => $event => (_ctx.foo = $event)`,
)
expect(ir.block.operation).toMatchObject([
{

View File

@ -40,19 +40,19 @@ import { genModelHandler } from './modelValue'
import { genBlock } from './block'
export function genCreateComponent(
oper: CreateComponentIRNode,
operation: CreateComponentIRNode,
context: CodegenContext,
): CodeFragment[] {
const { vaporHelper } = context
const tag = genTag()
const { root, props, slots, once } = oper
const { root, props, slots, once } = operation
const rawProps = genRawProps(props, context)
const rawSlots = genRawSlots(slots, context)
return [
NEWLINE,
`const n${oper.id} = `,
`const n${operation.id} = `,
...genCall(
vaporHelper('createComponent'),
tag,
@ -61,20 +61,20 @@ export function genCreateComponent(
root ? 'true' : false,
once && 'true',
),
...genDirectivesForElement(oper.id, context),
...genDirectivesForElement(operation.id, context),
]
function genTag() {
if (oper.dynamic) {
if (operation.dynamic) {
return genCall(
vaporHelper('resolveDynamicComponent'),
genExpression(oper.dynamic, context),
genExpression(operation.dynamic, context),
)
} else if (oper.asset) {
return toValidAssetId(oper.tag, 'component')
} else if (operation.asset) {
return toValidAssetId(operation.tag, 'component')
} else {
return genExpression(
extend(createSimpleExpression(oper.tag, false), { ast: null }),
extend(createSimpleExpression(operation.tag, false), { ast: null }),
context,
)
}
@ -85,41 +85,65 @@ export function genRawProps(
props: IRProps[],
context: CodegenContext,
): CodeFragment[] | undefined {
const { vaporHelper } = context
const frag = props
.map(props => {
if (isArray(props)) {
if (!props.length) return
return genStaticProps(props, context)
} else {
let expr: CodeFragment[]
if (props.kind === IRDynamicPropsKind.ATTRIBUTE)
expr = genMulti(DELIMITERS_OBJECT, genProp(props, context))
else {
expr = genExpression(props.value, context)
if (props.handler) expr = genCall(vaporHelper('toHandlers'), expr)
}
return ['() => (', ...expr, ')']
}
})
.filter(
Boolean as any as (v: CodeFragment[] | undefined) => v is CodeFragment[],
const staticProps = props[0]
if (isArray(staticProps)) {
if (!staticProps.length && props.length === 1) {
return
}
return genStaticProps(
staticProps,
context,
genDynamicProps(props.slice(1), context),
)
if (frag.length) {
return genMulti(DELIMITERS_ARRAY_NEWLINE, ...frag)
} else if (props.length) {
// all dynamic
return genStaticProps([], context, genDynamicProps(props, context))
}
}
function genStaticProps(
props: IRPropsStatic,
context: CodegenContext,
dynamicProps?: CodeFragment[],
): CodeFragment[] {
const args = props.map(prop => genProp(prop, context, true))
if (dynamicProps) {
args.push([`$: `, ...dynamicProps])
}
return genMulti(
props.length > 1 ? DELIMITERS_OBJECT_NEWLINE : DELIMITERS_OBJECT,
...props.map(prop => genProp(prop, context, true)),
args.length > 1 ? DELIMITERS_OBJECT_NEWLINE : DELIMITERS_OBJECT,
...args,
)
}
function genDynamicProps(
props: IRProps[],
context: CodegenContext,
): CodeFragment[] | undefined {
const { vaporHelper } = context
const frags: CodeFragment[][] = []
for (const p of props) {
let expr: CodeFragment[]
if (isArray(p)) {
if (p.length) {
frags.push(genStaticProps(p, context))
}
continue
} else {
if (p.kind === IRDynamicPropsKind.ATTRIBUTE)
expr = genMulti(DELIMITERS_OBJECT, genProp(p, context))
else {
expr = genExpression(p.value, context)
if (p.handler) expr = genCall(vaporHelper('toHandlers'), expr)
}
}
frags.push(['() => (', ...expr, ')'])
}
if (frags.length) {
return genMulti(DELIMITERS_ARRAY_NEWLINE, ...frags)
}
}
function genProp(prop: IRProp, context: CodegenContext, isStatic?: boolean) {
const values = genPropValue(prop.values, context)
return [

View File

@ -76,10 +76,11 @@ export function createComponent(
// check if we are the single root of the parent
// if yes, inject parent attrs as dynamic props source
if (isSingleRoot && currentInstance && currentInstance.hasFallthrough) {
const attrs = currentInstance.attrs
if (rawProps) {
;(rawProps.$ || (rawProps.$ = [])).push(currentInstance.attrs)
;(rawProps.$ || (rawProps.$ = [])).push(() => attrs)
} else {
rawProps = { $: [currentInstance.attrs] }
rawProps = { $: [() => attrs] } as RawProps
}
}

View File

@ -8,14 +8,13 @@ import {
} from '@vue/runtime-dom'
import { normalizeEmitsOptions } from './componentEmits'
export interface RawProps {
[key: string]: PropSource
export type RawProps = Record<string, () => unknown> & {
$?: DynamicPropsSource[]
}
type PropSource<T = any> = T | (() => T)
type DynamicPropsSource = PropSource<Record<string, any>>
type DynamicPropsSource =
| (() => Record<string, unknown>)
| Record<string, () => unknown>
export function initStaticProps(
comp: VaporComponent,
@ -38,40 +37,24 @@ export function initStaticProps(
const needCast = needCastKeys && needCastKeys.includes(normalizedKey)
const source = rawProps[key]
if (propsOptions && normalizedKey in propsOptions) {
if (isFunction(source)) {
Object.defineProperty(props, normalizedKey, {
enumerable: true,
get: needCast
? () =>
resolvePropValue(
propsOptions,
normalizedKey,
source(),
instance,
resolveDefault,
)
: source,
})
} else {
props[normalizedKey] = needCast
? resolvePropValue(
propsOptions,
normalizedKey,
source,
instance,
resolveDefault,
)
: source
}
Object.defineProperty(props, normalizedKey, {
enumerable: true,
get: needCast
? () =>
resolvePropValue(
propsOptions,
normalizedKey,
source(),
instance,
resolveDefault,
)
: source,
})
} else if (!isEmitListener(emitsOptions, key)) {
if (isFunction(source)) {
Object.defineProperty(attrs, key, {
enumerable: true,
get: source,
})
} else {
attrs[normalizedKey] = source
}
Object.defineProperty(attrs, key, {
enumerable: true,
get: source,
})
hasAttrs = true
}
}
@ -98,7 +81,9 @@ function resolveDefault(
}
// TODO optimization: maybe convert functions into computeds
export function resolveSource(source: PropSource): Record<string, any> {
export function resolveSource(
source: Record<string, any> | (() => Record<string, any>),
): Record<string, any> {
return isFunction(source) ? source() : source
}
@ -138,15 +123,18 @@ export function getDynamicPropsHandlers(
: passThrough
if (key in target) {
return castProp(resolveSource(target[key as string]))
return castProp(target[key as string]())
}
if (target.$) {
let i = target.$.length
let source
const dynamicSources = target.$
if (dynamicSources) {
let i = dynamicSources.length
let source, isDynamic
while (i--) {
source = resolveSource(target.$[i])
source = dynamicSources[i]
isDynamic = isFunction(source)
source = isDynamic ? (source as Function)() : source
if (hasOwn(source, key)) {
return castProp(source[key])
return castProp(isDynamic ? source[key] : source[key]())
}
}
}
@ -174,12 +162,14 @@ export function getDynamicPropsHandlers(
: null
const hasAttr = (target: RawProps, key: string) => {
if (key === '$' || isProp(key) || isEmitListener(emitsOptions, key))
if (key === '$' || isProp(key) || isEmitListener(emitsOptions, key)) {
return false
if (target.$) {
let i = target.$.length
}
const dynamicSources = target.$
if (dynamicSources) {
let i = dynamicSources.length
while (i--) {
if (hasOwn(resolveSource(target.$[i]), key)) {
if (hasOwn(resolveSource(dynamicSources[i]), key)) {
return true
}
}
@ -201,10 +191,11 @@ export function getDynamicPropsHandlers(
},
ownKeys(target) {
const keys = Object.keys(target)
if (target.$) {
let i = target.$.length
const dynamicSources = target.$
if (dynamicSources) {
let i = dynamicSources.length
while (i--) {
keys.push(...Object.keys(resolveSource(target.$[i])))
keys.push(...Object.keys(resolveSource(dynamicSources[i])))
}
}
return keys.filter(key => hasAttr(target, key))