This commit is contained in:
edison 2025-06-18 17:07:48 +03:00 committed by GitHub
commit 6be2804473
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 153 additions and 18 deletions

View File

@ -15,6 +15,30 @@ return { myEmit }
}"
`;
exports[`defineEmits > w/ enum member 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
enum Foo { Foo = 'foo', Bar = 'bar', Qux = 'qux' }
export type FooRef = Bar.Foo
interface Emits {
(e: FooRef, value: string): void;
(e: Foo.Bar, value: string): void;
}
export default /*@__PURE__*/_defineComponent({
emits: ["foo", "bar"],
setup(__props, { expose: __expose, emit: __emit }) {
__expose();
enum Bar { Foo = Foo.Foo }
const emit = __emit
return { Foo, Bar, emit }
}
})"
`;
exports[`defineEmits > w/ runtime options 1`] = `
"import { defineComponent as _defineComponent } from 'vue'

View File

@ -103,6 +103,24 @@ const emit = defineEmits(['a', 'b'])
expect(content).toMatch(`emits: ["foo", "bar"]`)
})
test('w/ enum member', () => {
const { content } = compile(`
<script setup lang="ts">
enum Foo { Foo = 'foo', Bar = 'bar', Qux = 'qux' }
enum Bar { Foo = Foo.Foo }
export type FooRef = Bar.Foo
interface Emits {
(e: FooRef, value: string): void;
(e: Foo.Bar, value: string): void;
}
const emit = defineEmits<Emits>()
</script>
`)
assertCode(content)
expect(content).toMatch(`emits: ["foo", "bar"]`)
})
test('w/ type from normal script', () => {
const { content } = compile(`
<script lang="ts">

View File

@ -10,7 +10,9 @@ import { isCallOf } from './utils'
import type { ScriptCompileContext } from './context'
import {
type TypeResolveContext,
resolveEnumMemberValue,
resolveTypeElements,
resolveTypeReference,
resolveUnionType,
} from './resolveType'
@ -112,7 +114,8 @@ function extractEventNames(
eventName.typeAnnotation &&
eventName.typeAnnotation.type === 'TSTypeAnnotation'
) {
const types = resolveUnionType(ctx, eventName.typeAnnotation.typeAnnotation)
const typeNode = eventName.typeAnnotation.typeAnnotation
const types = resolveUnionType(ctx, typeNode)
for (const type of types) {
if (type.type === 'TSLiteralType') {
@ -122,6 +125,38 @@ function extractEventNames(
) {
emits.add(String(type.literal.value))
}
} else if (type.type === 'TSEnumDeclaration') {
if (
typeNode.type === 'TSTypeReference' &&
typeNode.typeName.type === 'TSQualifiedName'
) {
const memberValue = resolveEnumMemberValue(
ctx,
type,
typeNode.typeName.right.name,
)
if (memberValue) emits.add(memberValue)
}
} else if (type.type === 'TSTypeReference') {
if (
type.typeName.type === 'TSQualifiedName' &&
type.typeName.left.type === 'Identifier'
) {
const resolved = resolveTypeReference(
ctx,
type,
undefined,
type.typeName.left.name,
)
if (resolved && resolved.type === 'TSEnumDeclaration') {
const memberValue = resolveEnumMemberValue(
ctx,
resolved,
type.typeName.right.name,
)
if (memberValue) emits.add(memberValue)
}
}
}
}
}

View File

@ -718,8 +718,9 @@ type ReferenceTypes =
| TSExpressionWithTypeArguments
| TSImportType
| TSTypeQuery
| Identifier
function resolveTypeReference(
export function resolveTypeReference(
ctx: TypeResolveContext,
node: ReferenceTypes & {
_resolvedReference?: ScopeTypeNode
@ -778,21 +779,35 @@ function innerResolveTypeReference(
}
}
} else {
let ns = innerResolveTypeReference(ctx, scope, name[0], node, onlyExported)
if (ns) {
if (ns.type !== 'TSModuleDeclaration') {
// namespace merged with other types, attached as _ns
ns = ns._ns
}
if (ns) {
const childScope = moduleDeclToScope(ctx, ns, ns._ownerScope || scope)
return innerResolveTypeReference(
ctx,
childScope,
name.length > 2 ? name.slice(1) : name[name.length - 1],
node,
!ns.declare,
)
let resolved = innerResolveTypeReference(
ctx,
scope,
name[0],
node,
onlyExported,
)
if (resolved) {
if (resolved.type === 'TSEnumDeclaration') {
return resolved
} else {
if (resolved.type !== 'TSModuleDeclaration') {
// namespace merged with other types, attached as _ns
resolved = resolved._ns
}
if (resolved) {
const childScope = moduleDeclToScope(
ctx,
resolved,
resolved._ownerScope || scope,
)
return innerResolveTypeReference(
ctx,
childScope,
name.length > 2 ? name.slice(1) : name[name.length - 1],
node,
!resolved.declare,
)
}
}
}
}
@ -806,7 +821,9 @@ function getReferenceName(node: ReferenceTypes): string | string[] {
? node.expression
: node.type === 'TSImportType'
? node.qualifier
: node.exprName
: node.type === 'TSTypeQuery'
? node.exprName
: node
if (ref?.type === 'Identifier') {
return ref.name
} else if (ref?.type === 'TSQualifiedName') {
@ -1984,3 +2001,44 @@ export function resolveUnionType(
return types
}
export function resolveEnumMemberValue(
ctx: TypeResolveContext,
node: Node & MaybeWithScope & { _resolvedElements?: ResolvedElements },
typeName: string,
scope?: TypeScope,
): string | undefined {
if (node.type === 'TSTypeReference') {
const resolved = resolveTypeReference(ctx, node, scope, typeName)
if (resolved) node = resolved
}
if (node.type === 'Identifier') {
const resolved = resolveTypeReference(ctx, node, scope, node.name)
if (resolved) node = resolved
}
if (node.type === 'TSEnumDeclaration') {
for (const m of node.members) {
if (m.id.type === 'Identifier' && m.id.name === typeName) {
if (m.initializer) {
if (m.initializer.type === 'StringLiteral') {
return m.initializer.value
} else if (m.initializer.type === 'MemberExpression') {
if (
m.initializer.object.type === 'Identifier' &&
m.initializer.property.type === 'Identifier'
) {
return resolveEnumMemberValue(
ctx,
m.initializer.object,
m.initializer.property.name,
scope,
)
}
}
}
}
}
}
}