-
-
Notifications
You must be signed in to change notification settings - Fork 666
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
vue/prefer-import-from-vue
rule (#1804)
- Loading branch information
Showing
9 changed files
with
776 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
--- | ||
pageClass: rule-details | ||
sidebarDepth: 0 | ||
title: vue/prefer-import-from-vue | ||
description: enforce import from 'vue' instead of import from '@vue/*' | ||
--- | ||
# vue/prefer-import-from-vue | ||
|
||
> enforce import from 'vue' instead of import from '@vue/*' | ||
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge> | ||
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule. | ||
|
||
## :book: Rule Details | ||
|
||
This rule aims to use imports from `'vue'` instead of imports from `'@vue/*'`. | ||
|
||
Imports from the following modules are almost always wrong. You should import from `vue` instead. | ||
|
||
- `@vue/runtime-dom` | ||
- `@vue/runtime-core` | ||
- `@vue/reactivity` | ||
- `@vue/shared` | ||
|
||
<eslint-code-block fix :rules="{'vue/prefer-import-from-vue': ['error']}" filename="example.js" language="javascript"> | ||
|
||
```js | ||
/* ✓ GOOD */ | ||
import { createApp, ref, Component } from 'vue' | ||
``` | ||
|
||
</eslint-code-block> | ||
|
||
<eslint-code-block fix :rules="{'vue/prefer-import-from-vue': ['error']}" filename="example.js" language="javascript"> | ||
|
||
```js | ||
/* ✗ BAD */ | ||
import { createApp } from '@vue/runtime-dom' | ||
import { Component } from '@vue/runtime-core' | ||
import { ref } from '@vue/reactivity' | ||
``` | ||
|
||
</eslint-code-block> | ||
|
||
## :wrench: Options | ||
|
||
Nothing. | ||
|
||
## :mag: Implementation | ||
|
||
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-import-from-vue.js) | ||
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-import-from-vue.js) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/** | ||
* @author Yosuke Ota | ||
* See LICENSE file in root directory for full license. | ||
*/ | ||
'use strict' | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Requirements | ||
// ------------------------------------------------------------------------------ | ||
|
||
const vue3ExportNames = new Set(require('../utils/vue3-export-names.json')) | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Helpers | ||
// ------------------------------------------------------------------------------ | ||
|
||
const TARGET_AT_VUE_MODULES = new Set([ | ||
'@vue/runtime-dom', | ||
'@vue/runtime-core', | ||
'@vue/reactivity', | ||
'@vue/shared' | ||
]) | ||
// Modules with the names of a subset of vue. | ||
const SUBSET_AT_VUE_MODULES = new Set(['@vue/runtime-dom', '@vue/runtime-core']) | ||
|
||
/** | ||
* @param {ImportDeclaration} node | ||
*/ | ||
function* extractImportNames(node) { | ||
for (const specifier of node.specifiers) { | ||
if (specifier.type === 'ImportDefaultSpecifier') { | ||
yield 'default' | ||
} else if (specifier.type === 'ImportNamespaceSpecifier') { | ||
yield null // all | ||
} else if (specifier.type === 'ImportSpecifier') { | ||
yield specifier.imported.name | ||
} | ||
} | ||
} | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Rule Definition | ||
// ------------------------------------------------------------------------------ | ||
|
||
module.exports = { | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
description: "enforce import from 'vue' instead of import from '@vue/*'", | ||
// TODO We will change it in the next major version. | ||
// categories: ['vue3-essential'], | ||
categories: undefined, | ||
url: 'https://eslint.vuejs.org/rules/prefer-import-from-vue.html' | ||
}, | ||
fixable: 'code', | ||
schema: [], | ||
messages: { | ||
importedAtVue: "Import from 'vue' instead of '{{source}}'." | ||
} | ||
}, | ||
/** | ||
* @param {RuleContext} context | ||
* @returns {RuleListener} | ||
*/ | ||
create(context) { | ||
/** | ||
* | ||
* @param {Literal & { value: string }} source | ||
* @param { () => boolean } fixable | ||
*/ | ||
function verifySource(source, fixable) { | ||
if (!TARGET_AT_VUE_MODULES.has(source.value)) { | ||
return | ||
} | ||
|
||
context.report({ | ||
node: source, | ||
messageId: 'importedAtVue', | ||
data: { source: source.value }, | ||
fix: fixable() | ||
? (fixer) => | ||
fixer.replaceTextRange( | ||
[source.range[0] + 1, source.range[1] - 1], | ||
'vue' | ||
) | ||
: null | ||
}) | ||
} | ||
|
||
return { | ||
ImportDeclaration(node) { | ||
verifySource(node.source, () => { | ||
if (SUBSET_AT_VUE_MODULES.has(node.source.value)) { | ||
// If the module is a subset of 'vue', we can safely change it to 'vue'. | ||
return true | ||
} | ||
for (const name of extractImportNames(node)) { | ||
if (name == null) { | ||
return false // import all | ||
} | ||
if (!vue3ExportNames.has(name)) { | ||
// If there is a name that is not exported from 'vue', it will not be auto-fixed. | ||
return false | ||
} | ||
} | ||
return true | ||
}) | ||
}, | ||
ExportNamedDeclaration(node) { | ||
if (node.source) { | ||
verifySource(node.source, () => { | ||
for (const specifier of node.specifiers) { | ||
if (!vue3ExportNames.has(specifier.local.name)) { | ||
// If there is a name that is not exported from 'vue', it will not be auto-fixed. | ||
return false | ||
} | ||
} | ||
return true | ||
}) | ||
} | ||
}, | ||
ExportAllDeclaration(node) { | ||
verifySource( | ||
node.source, | ||
// If we change it to `from 'vue'`, it will export more, so it will not be auto-fixed. | ||
() => false | ||
) | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.