defineEmits.ts 3.0 KB

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