prefer-define-options.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /**
  2. * @author Yosuke Ota <https://github.com/ota-meshi>
  3. * See LICENSE file in root directory for full license.
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. module.exports = {
  8. meta: {
  9. type: 'suggestion',
  10. docs: {
  11. description: 'enforce use of `defineOptions` instead of default export.',
  12. categories: undefined,
  13. url: 'https://eslint.vuejs.org/rules/prefer-define-options.html'
  14. },
  15. fixable: 'code',
  16. schema: [],
  17. messages: {
  18. preferDefineOptions: 'Use `defineOptions` instead of default export.'
  19. }
  20. },
  21. /**
  22. * @param {RuleContext} context
  23. * @returns {RuleListener}
  24. */
  25. create(context) {
  26. const scriptSetup = utils.getScriptSetupElement(context)
  27. if (!scriptSetup) {
  28. return {}
  29. }
  30. /** @type {CallExpression | null} */
  31. let defineOptionsNode = null
  32. /** @type {ExportDefaultDeclaration | null} */
  33. let exportDefaultDeclaration = null
  34. return utils.compositingVisitors(
  35. utils.defineScriptSetupVisitor(context, {
  36. onDefineOptionsEnter(node) {
  37. defineOptionsNode = node
  38. }
  39. }),
  40. {
  41. ExportDefaultDeclaration(node) {
  42. exportDefaultDeclaration = node
  43. },
  44. 'Program:exit'() {
  45. if (!exportDefaultDeclaration) {
  46. return
  47. }
  48. context.report({
  49. node: exportDefaultDeclaration,
  50. messageId: 'preferDefineOptions',
  51. fix: defineOptionsNode
  52. ? null
  53. : buildFix(exportDefaultDeclaration, scriptSetup)
  54. })
  55. }
  56. }
  57. )
  58. /**
  59. * @param {ExportDefaultDeclaration} node
  60. * @param {VElement} scriptSetup
  61. * @returns {(fixer: RuleFixer) => Fix[]}
  62. */
  63. function buildFix(node, scriptSetup) {
  64. return (fixer) => {
  65. const sourceCode = context.getSourceCode()
  66. // Calc remove range
  67. /** @type {Range} */
  68. let removeRange = [...node.range]
  69. const script = scriptSetup.parent.children
  70. .filter(utils.isVElement)
  71. .find(
  72. (node) =>
  73. node.name === 'script' && !utils.hasAttribute(node, 'setup')
  74. )
  75. if (
  76. script &&
  77. script.endTag &&
  78. sourceCode
  79. .getTokensBetween(script.startTag, script.endTag, {
  80. includeComments: true
  81. })
  82. .every(
  83. (token) =>
  84. removeRange[0] <= token.range[0] &&
  85. token.range[1] <= removeRange[1]
  86. )
  87. ) {
  88. removeRange = [...script.range]
  89. }
  90. const removeStartLoc = sourceCode.getLocFromIndex(removeRange[0])
  91. if (
  92. sourceCode.lines[removeStartLoc.line - 1]
  93. .slice(0, removeStartLoc.column)
  94. .trim() === ''
  95. ) {
  96. removeRange[0] =
  97. removeStartLoc.line === 1
  98. ? 0
  99. : sourceCode.getIndexFromLoc({
  100. line: removeStartLoc.line - 1,
  101. column: sourceCode.lines[removeStartLoc.line - 2].length
  102. })
  103. }
  104. return [
  105. fixer.removeRange(removeRange),
  106. fixer.insertTextAfter(
  107. scriptSetup.startTag,
  108. `\ndefineOptions(${sourceCode.getText(node.declaration)})\n`
  109. )
  110. ]
  111. }
  112. }
  113. }
  114. }