125 lines
3.2 KiB
TypeScript
125 lines
3.2 KiB
TypeScript
import { Identifier, LVal, Node, RestElement } from '@babel/types'
|
|
import { isCallOf } from './utils'
|
|
import { ScriptCompileContext } from './context'
|
|
import { resolveTypeElements } from './resolveType'
|
|
|
|
export const DEFINE_EMITS = 'defineEmits'
|
|
|
|
export function processDefineEmits(
|
|
ctx: ScriptCompileContext,
|
|
node: Node,
|
|
declId?: LVal
|
|
): boolean {
|
|
if (!isCallOf(node, DEFINE_EMITS)) {
|
|
return false
|
|
}
|
|
if (ctx.hasDefineEmitCall) {
|
|
ctx.error(`duplicate ${DEFINE_EMITS}() call`, node)
|
|
}
|
|
ctx.hasDefineEmitCall = true
|
|
ctx.emitsRuntimeDecl = node.arguments[0]
|
|
if (node.typeParameters) {
|
|
if (ctx.emitsRuntimeDecl) {
|
|
ctx.error(
|
|
`${DEFINE_EMITS}() cannot accept both type and non-type arguments ` +
|
|
`at the same time. Use one or the other.`,
|
|
node
|
|
)
|
|
}
|
|
ctx.emitsTypeDecl = node.typeParameters.params[0]
|
|
}
|
|
|
|
if (declId) {
|
|
ctx.emitIdentifier =
|
|
declId.type === 'Identifier' ? declId.name : ctx.getString(declId)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
export function genRuntimeEmits(ctx: ScriptCompileContext): string | undefined {
|
|
let emitsDecl = ''
|
|
if (ctx.emitsRuntimeDecl) {
|
|
emitsDecl = ctx.getString(ctx.emitsRuntimeDecl).trim()
|
|
} else if (ctx.emitsTypeDecl) {
|
|
const typeDeclaredEmits = extractRuntimeEmits(ctx)
|
|
emitsDecl = typeDeclaredEmits.size
|
|
? `[${Array.from(typeDeclaredEmits)
|
|
.map(k => JSON.stringify(k))
|
|
.join(', ')}]`
|
|
: ``
|
|
}
|
|
if (ctx.hasDefineModelCall) {
|
|
let modelEmitsDecl = `[${Object.keys(ctx.modelDecls)
|
|
.map(n => JSON.stringify(`update:${n}`))
|
|
.join(', ')}]`
|
|
emitsDecl = emitsDecl
|
|
? `${ctx.helper('mergeModels')}(${emitsDecl}, ${modelEmitsDecl})`
|
|
: modelEmitsDecl
|
|
}
|
|
return emitsDecl
|
|
}
|
|
|
|
function extractRuntimeEmits(ctx: ScriptCompileContext): Set<string> {
|
|
const emits = new Set<string>()
|
|
const node = ctx.emitsTypeDecl!
|
|
|
|
if (node.type === 'TSFunctionType') {
|
|
extractEventNames(node.parameters[0], emits)
|
|
return emits
|
|
}
|
|
|
|
const { props, calls } = resolveTypeElements(ctx, node)
|
|
|
|
let hasProperty = false
|
|
for (const key in props) {
|
|
emits.add(key)
|
|
hasProperty = true
|
|
}
|
|
|
|
if (calls) {
|
|
if (hasProperty) {
|
|
ctx.error(
|
|
`defineEmits() type cannot mixed call signature and property syntax.`,
|
|
node
|
|
)
|
|
}
|
|
for (const call of calls) {
|
|
extractEventNames(call.parameters[0], emits)
|
|
}
|
|
}
|
|
|
|
return emits
|
|
}
|
|
|
|
function extractEventNames(
|
|
eventName: Identifier | RestElement,
|
|
emits: Set<string>
|
|
) {
|
|
if (
|
|
eventName.type === 'Identifier' &&
|
|
eventName.typeAnnotation &&
|
|
eventName.typeAnnotation.type === 'TSTypeAnnotation'
|
|
) {
|
|
const typeNode = eventName.typeAnnotation.typeAnnotation
|
|
if (typeNode.type === 'TSLiteralType') {
|
|
if (
|
|
typeNode.literal.type !== 'UnaryExpression' &&
|
|
typeNode.literal.type !== 'TemplateLiteral'
|
|
) {
|
|
emits.add(String(typeNode.literal.value))
|
|
}
|
|
} else if (typeNode.type === 'TSUnionType') {
|
|
for (const t of typeNode.types) {
|
|
if (
|
|
t.type === 'TSLiteralType' &&
|
|
t.literal.type !== 'UnaryExpression' &&
|
|
t.literal.type !== 'TemplateLiteral'
|
|
) {
|
|
emits.add(String(t.literal.value))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|