defineEmits.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import { Identifier, LVal, Node, RestElement } from '@babel/types'
  2. import { isCallOf } from './utils'
  3. import { ScriptCompileContext } from './context'
  4. import { resolveTypeElements, resolveUnionType } from './resolveType'
  5. export const DEFINE_EMITS = 'defineEmits'
  6. export function processDefineEmits(
  7. ctx: ScriptCompileContext,
  8. node: Node,
  9. declId?: LVal
  10. ): boolean {
  11. if (!isCallOf(node, DEFINE_EMITS)) {
  12. return false
  13. }
  14. if (ctx.hasDefineEmitCall) {
  15. ctx.error(`duplicate ${DEFINE_EMITS}() call`, node)
  16. }
  17. ctx.hasDefineEmitCall = true
  18. ctx.emitsRuntimeDecl = node.arguments[0]
  19. if (node.typeParameters) {
  20. if (ctx.emitsRuntimeDecl) {
  21. ctx.error(
  22. `${DEFINE_EMITS}() cannot accept both type and non-type arguments ` +
  23. `at the same time. Use one or the other.`,
  24. node
  25. )
  26. }
  27. ctx.emitsTypeDecl = node.typeParameters.params[0]
  28. }
  29. if (declId) {
  30. ctx.emitIdentifier =
  31. declId.type === 'Identifier' ? declId.name : ctx.getString(declId)
  32. }
  33. return true
  34. }
  35. export function genRuntimeEmits(ctx: ScriptCompileContext): string | undefined {
  36. let emitsDecl = ''
  37. if (ctx.emitsRuntimeDecl) {
  38. emitsDecl = ctx.getString(ctx.emitsRuntimeDecl).trim()
  39. } else if (ctx.emitsTypeDecl) {
  40. const typeDeclaredEmits = extractRuntimeEmits(ctx)
  41. emitsDecl = typeDeclaredEmits.size
  42. ? `[${Array.from(typeDeclaredEmits)
  43. .map(k => JSON.stringify(k))
  44. .join(', ')}]`
  45. : ``
  46. }
  47. if (ctx.hasDefineModelCall) {
  48. let modelEmitsDecl = `[${Object.keys(ctx.modelDecls)
  49. .map(n => JSON.stringify(`update:${n}`))
  50. .join(', ')}]`
  51. emitsDecl = emitsDecl
  52. ? `${ctx.helper('mergeModels')}(${emitsDecl}, ${modelEmitsDecl})`
  53. : modelEmitsDecl
  54. }
  55. return emitsDecl
  56. }
  57. function extractRuntimeEmits(ctx: ScriptCompileContext): Set<string> {
  58. const emits = new Set<string>()
  59. const node = ctx.emitsTypeDecl!
  60. if (node.type === 'TSFunctionType') {
  61. extractEventNames(ctx, node.parameters[0], emits)
  62. return emits
  63. }
  64. const { props, calls } = resolveTypeElements(ctx, node)
  65. let hasProperty = false
  66. for (const key in props) {
  67. emits.add(key)
  68. hasProperty = true
  69. }
  70. if (calls) {
  71. if (hasProperty) {
  72. ctx.error(
  73. `defineEmits() type cannot mixed call signature and property syntax.`,
  74. node
  75. )
  76. }
  77. for (const call of calls) {
  78. extractEventNames(ctx, call.parameters[0], emits)
  79. }
  80. }
  81. return emits
  82. }
  83. function extractEventNames(
  84. ctx: ScriptCompileContext,
  85. eventName: Identifier | RestElement,
  86. emits: Set<string>
  87. ) {
  88. if (
  89. eventName.type === 'Identifier' &&
  90. eventName.typeAnnotation &&
  91. eventName.typeAnnotation.type === 'TSTypeAnnotation'
  92. ) {
  93. const types = resolveUnionType(ctx, eventName.typeAnnotation.typeAnnotation)
  94. for (const type of types) {
  95. if (type.type === 'TSLiteralType') {
  96. if (
  97. type.literal.type !== 'UnaryExpression' &&
  98. type.literal.type !== 'TemplateLiteral'
  99. ) {
  100. emits.add(String(type.literal.value))
  101. }
  102. }
  103. }
  104. }
  105. }