Skip to content

feat: allow slot prop vars as components #13573

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,25 @@ describe('compiler: element transform', () => {
expect(node.tag).toBe(`Example`)
})

test('resolve component from scoped slot bindings', () => {
const { root, node } = parseWithElementTransform(
`<Example v-slot="{Foo}"><Foo /></Example>`,
{
inline: true,
bindingMetadata: {
Example: BindingTypes.SETUP_CONST,
},
identifiers: {
Foo: 1,
},
},
)

expect(root.helpers).not.toContain(RESOLVE_COMPONENT)
expect(root.components).not.toContain('Foo')
expect(node.tag).toBe(`Example`)
})

test('resolve namespaced component from setup bindings', () => {
const { root, node } = parseWithElementTransform(`<Foo.Example/>`, {
bindingMetadata: {
Expand Down Expand Up @@ -175,6 +194,25 @@ describe('compiler: element transform', () => {
expect(node.tag).toBe('_unref($props["Foo"]).Example')
})

test('resolve namespaced component from scoped slot bindings', () => {
const { root, node } = parseWithElementTransform(
`<Example v-slot="SlotProps"><SlotProps.Foo /></Example>`,
{
inline: true,
bindingMetadata: {
Example: BindingTypes.SETUP_CONST,
},
identifiers: {
SlotProps: 1,
},
},
)

expect(root.helpers).not.toContain(RESOLVE_COMPONENT)
expect(root.components).not.toContain('SlotProps')
expect(node.tag).toBe(`Example`)
})

test('do not resolve component from non-script-setup bindings', () => {
const bindingMetadata = {
Example: BindingTypes.SETUP_MAYBE_REF,
Expand Down
5 changes: 4 additions & 1 deletion packages/compiler-core/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ enum NewlineType {
}

export interface CodegenContext
extends Omit<Required<CodegenOptions>, 'bindingMetadata' | 'inline'> {
extends Omit<
Required<CodegenOptions>,
'bindingMetadata' | 'inline' | 'identifiers'
> {
source: string
code: string
line: number
Expand Down
2 changes: 2 additions & 0 deletions packages/compiler-core/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ interface SharedTransformCodegenOptions {
* binding access when `prefixIdentifiers` is enabled.
*/
bindingMetadata?: BindingMetadata

identifiers?: { [name: string]: number | undefined }
/**
* Compile the function for inlining inside setup().
* This allows the function to directly access setup() local bindings.
Expand Down
3 changes: 2 additions & 1 deletion packages/compiler-core/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export function createTransformContext(
inSSR = false,
ssrCssVars = ``,
bindingMetadata = EMPTY_OBJ,
identifiers = Object.create(null),
inline = false,
isTS = false,
onError = defaultOnError,
Expand Down Expand Up @@ -187,7 +188,7 @@ export function createTransformContext(
cached: [],
constantCache: new WeakMap(),
temps: 0,
identifiers: Object.create(null),
identifiers,
scopes: {
vFor: 0,
vSlot: 0,
Expand Down
28 changes: 25 additions & 3 deletions packages/compiler-core/src/transforms/transformElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,24 @@ export function resolveComponentType(
return builtIn
}

// 3. user component (from setup bindings)
// 3. component from slot props
// this is skipped in browser build since browser builds do not perform
// identifier tracking.
if (!__BROWSER__) {
if (context.identifiers[tag]) {
return tag
}

const dotIndex = tag.indexOf('.')
if (dotIndex > 0) {
const ns = tag.slice(0, dotIndex)
if (context.identifiers[ns]) {
return ns + tag.slice(dotIndex)
}
}
}

// 4. user component (from setup bindings)
// this is skipped in browser build since browser builds do not perform
// binding analysis.
if (!__BROWSER__) {
Expand All @@ -299,7 +316,7 @@ export function resolveComponentType(
}
}

// 4. Self referencing component (inferred from filename)
// 5. Self referencing component (inferred from filename)
if (
!__BROWSER__ &&
context.selfName &&
Expand All @@ -313,7 +330,7 @@ export function resolveComponentType(
return toValidAssetId(tag, `component`)
}

// 5. user component (resolve)
// 6. user component (resolve)
context.helper(RESOLVE_COMPONENT)
context.components.add(tag)
return toValidAssetId(tag, `component`)
Expand Down Expand Up @@ -367,6 +384,11 @@ function resolveSetupReference(name: string, context: TransformContext) {
context.inline ? '__props' : '$props'
}[${JSON.stringify(fromProps)}])`
}

// const fromSlotScope = checkType(BindingTypes.SLOT_SCOPE)
// if (fromSlotScope) {
// return fromSlotScope
// }
}

export type PropsExpression = ObjectExpression | CallExpression | ExpressionNode
Expand Down