prefer-import-from-vue.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /**
  2. * @author Yosuke Ota
  3. * See LICENSE file in root directory for full license.
  4. */
  5. 'use strict'
  6. // ------------------------------------------------------------------------------
  7. // Requirements
  8. // ------------------------------------------------------------------------------
  9. const vue3ExportNames = new Set(require('../utils/vue3-export-names.json'))
  10. // ------------------------------------------------------------------------------
  11. // Helpers
  12. // ------------------------------------------------------------------------------
  13. const TARGET_AT_VUE_MODULES = new Set([
  14. '@vue/runtime-dom',
  15. '@vue/runtime-core',
  16. '@vue/reactivity',
  17. '@vue/shared'
  18. ])
  19. // Modules with the names of a subset of vue.
  20. const SUBSET_AT_VUE_MODULES = new Set(['@vue/runtime-dom', '@vue/runtime-core'])
  21. /**
  22. * @param {ImportDeclaration} node
  23. */
  24. function* extractImportNames(node) {
  25. for (const specifier of node.specifiers) {
  26. if (specifier.type === 'ImportDefaultSpecifier') {
  27. yield 'default'
  28. } else if (specifier.type === 'ImportNamespaceSpecifier') {
  29. yield null // all
  30. } else if (specifier.type === 'ImportSpecifier') {
  31. yield specifier.imported.name
  32. }
  33. }
  34. }
  35. // ------------------------------------------------------------------------------
  36. // Rule Definition
  37. // ------------------------------------------------------------------------------
  38. module.exports = {
  39. meta: {
  40. type: 'suggestion',
  41. docs: {
  42. description: "enforce import from 'vue' instead of import from '@vue/*'",
  43. // TODO We will change it in the next major version.
  44. // categories: ['vue3-essential'],
  45. categories: undefined,
  46. url: 'https://eslint.vuejs.org/rules/prefer-import-from-vue.html'
  47. },
  48. fixable: 'code',
  49. schema: [],
  50. messages: {
  51. importedAtVue: "Import from 'vue' instead of '{{source}}'."
  52. }
  53. },
  54. /**
  55. * @param {RuleContext} context
  56. * @returns {RuleListener}
  57. */
  58. create(context) {
  59. /**
  60. *
  61. * @param {Literal & { value: string }} source
  62. * @param { () => boolean } fixable
  63. */
  64. function verifySource(source, fixable) {
  65. if (!TARGET_AT_VUE_MODULES.has(source.value)) {
  66. return
  67. }
  68. context.report({
  69. node: source,
  70. messageId: 'importedAtVue',
  71. data: { source: source.value },
  72. fix: fixable()
  73. ? (fixer) =>
  74. fixer.replaceTextRange(
  75. [source.range[0] + 1, source.range[1] - 1],
  76. 'vue'
  77. )
  78. : null
  79. })
  80. }
  81. return {
  82. ImportDeclaration(node) {
  83. verifySource(node.source, () => {
  84. if (SUBSET_AT_VUE_MODULES.has(node.source.value)) {
  85. // If the module is a subset of 'vue', we can safely change it to 'vue'.
  86. return true
  87. }
  88. for (const name of extractImportNames(node)) {
  89. if (name == null) {
  90. return false // import all
  91. }
  92. if (!vue3ExportNames.has(name)) {
  93. // If there is a name that is not exported from 'vue', it will not be auto-fixed.
  94. return false
  95. }
  96. }
  97. return true
  98. })
  99. },
  100. ExportNamedDeclaration(node) {
  101. if (node.source) {
  102. verifySource(node.source, () => {
  103. for (const specifier of node.specifiers) {
  104. if (!vue3ExportNames.has(specifier.local.name)) {
  105. // If there is a name that is not exported from 'vue', it will not be auto-fixed.
  106. return false
  107. }
  108. }
  109. return true
  110. })
  111. }
  112. },
  113. ExportAllDeclaration(node) {
  114. verifySource(
  115. node.source,
  116. // If we change it to `from 'vue'`, it will export more, so it will not be auto-fixed.
  117. () => false
  118. )
  119. }
  120. }
  121. }
  122. }