define-emits-declaration.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /**
  2. * @author Amorites
  3. * See LICENSE file in root directory for full license.
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. /**
  8. * @typedef {import('@typescript-eslint/types').TSESTree.TypeNode} TypeNode
  9. *
  10. */
  11. module.exports = {
  12. meta: {
  13. type: 'suggestion',
  14. docs: {
  15. description: 'enforce declaration style of `defineEmits`',
  16. categories: undefined,
  17. url: 'https://eslint.vuejs.org/rules/define-emits-declaration.html'
  18. },
  19. fixable: null,
  20. schema: [
  21. {
  22. enum: ['type-based', 'type-literal', 'runtime']
  23. }
  24. ],
  25. messages: {
  26. hasArg: 'Use type based declaration instead of runtime declaration.',
  27. hasTypeArg: 'Use runtime declaration instead of type based declaration.',
  28. hasTypeCallArg:
  29. 'Use new type literal declaration instead of the old call signature declaration.'
  30. }
  31. },
  32. /** @param {RuleContext} context */
  33. create(context) {
  34. const scriptSetup = utils.getScriptSetupElement(context)
  35. if (!scriptSetup || !utils.hasAttribute(scriptSetup, 'lang', 'ts')) {
  36. return {}
  37. }
  38. const defineType = context.options[0] || 'type-based'
  39. return utils.defineScriptSetupVisitor(context, {
  40. onDefineEmitsEnter(node) {
  41. switch (defineType) {
  42. case 'type-based': {
  43. if (node.arguments.length > 0) {
  44. context.report({
  45. node,
  46. messageId: 'hasArg'
  47. })
  48. }
  49. break
  50. }
  51. case 'type-literal': {
  52. verifyTypeLiteral(node)
  53. break
  54. }
  55. case 'runtime': {
  56. const typeArguments =
  57. 'typeArguments' in node ? node.typeArguments : node.typeParameters
  58. if (typeArguments && typeArguments.params.length > 0) {
  59. context.report({
  60. node,
  61. messageId: 'hasTypeArg'
  62. })
  63. }
  64. break
  65. }
  66. }
  67. }
  68. })
  69. /** @param {CallExpression} node */
  70. function verifyTypeLiteral(node) {
  71. if (node.arguments.length > 0) {
  72. context.report({
  73. node,
  74. messageId: 'hasArg'
  75. })
  76. return
  77. }
  78. const typeArguments = node.typeArguments || node.typeParameters
  79. const param = /** @type {TypeNode|undefined} */ (typeArguments?.params[0])
  80. if (!param) return
  81. if (param.type === 'TSTypeLiteral') {
  82. for (const memberNode of param.members) {
  83. if (memberNode.type !== 'TSPropertySignature') {
  84. context.report({
  85. node: memberNode,
  86. messageId: 'hasTypeCallArg'
  87. })
  88. }
  89. }
  90. } else if (param.type === 'TSFunctionType') {
  91. context.report({
  92. node: param,
  93. messageId: 'hasTypeCallArg'
  94. })
  95. }
  96. }
  97. }
  98. }