eslint-plugin-import-x
Version:
Import with sanity.
172 lines • 7.68 kB
JavaScript
import { createRule, getValue } from '../utils/index.js';
function isComma(token) {
return token.type === 'Punctuator' && token.value === ',';
}
function removeSpecifiers(fixes, fixer, sourceCode, specifiers) {
for (const specifier of specifiers) {
const token = sourceCode.getTokenAfter(specifier);
if (token && isComma(token)) {
fixes.push(fixer.remove(token));
}
fixes.push(fixer.remove(specifier));
}
}
function getImportText(node, sourceCode, specifiers, kind) {
const sourceString = sourceCode.getText(node.source);
if (specifiers.length === 0) {
return '';
}
const names = specifiers.map(s => {
const importedName = getValue(s.imported);
if (importedName === s.local.name) {
return importedName;
}
return `${importedName} as ${s.local.name}`;
});
return `import ${kind} {${names.join(', ')}} from ${sourceString};`;
}
export default createRule({
name: 'consistent-type-specifier-style',
meta: {
type: 'suggestion',
docs: {
category: 'Style guide',
description: 'Enforce or ban the use of inline type-only markers for named imports.',
},
fixable: 'code',
schema: [
{
type: 'string',
enum: ['prefer-top-level', 'prefer-inline'],
default: 'prefer-top-level',
},
],
messages: {
inline: 'Prefer using inline {{kind}} specifiers instead of a top-level {{kind}}-only import.',
topLevel: 'Prefer using a top-level {{kind}}-only import instead of inline {{kind}} specifiers.',
},
},
defaultOptions: [],
create(context) {
const { sourceCode } = context;
if (context.options[0] === 'prefer-inline') {
return {
ImportDeclaration(node) {
if (node.importKind === 'value' || node.importKind == null) {
return;
}
if (node.specifiers.length === 0 ||
(node.specifiers.length === 1 &&
(node.specifiers[0].type === 'ImportDefaultSpecifier' ||
node.specifiers[0].type === 'ImportNamespaceSpecifier'))) {
return;
}
context.report({
node,
messageId: 'inline',
data: {
kind: node.importKind,
},
fix(fixer) {
const kindToken = sourceCode.getFirstToken(node, { skip: 1 });
return [
kindToken ? fixer.remove(kindToken) : [],
node.specifiers.map(specifier => fixer.insertTextBefore(specifier, `${node.importKind} `)),
].flat();
},
});
},
};
}
return {
ImportDeclaration(node) {
if (node.importKind === 'type' ||
node.importKind === 'typeof' ||
node.specifiers.length === 0 ||
(node.specifiers.length === 1 &&
(node.specifiers[0].type === 'ImportDefaultSpecifier' ||
node.specifiers[0].type === 'ImportNamespaceSpecifier'))) {
return;
}
const typeSpecifiers = [];
const typeofSpecifiers = [];
const valueSpecifiers = [];
let defaultSpecifier = null;
for (const specifier of node.specifiers) {
if (specifier.type === 'ImportDefaultSpecifier') {
defaultSpecifier = specifier;
continue;
}
if (!('importKind' in specifier)) {
continue;
}
if (specifier.importKind === 'type') {
typeSpecifiers.push(specifier);
}
else if (specifier.importKind === 'typeof') {
typeofSpecifiers.push(specifier);
}
else if (specifier.importKind === 'value' ||
specifier.importKind == null) {
valueSpecifiers.push(specifier);
}
}
const typeImport = getImportText(node, sourceCode, typeSpecifiers, 'type');
const typeofImport = getImportText(node, sourceCode, typeofSpecifiers, 'typeof');
const newImports = `${typeImport}\n${typeofImport}`.trim();
if (typeSpecifiers.length + typeofSpecifiers.length ===
node.specifiers.length) {
const kind = [
typeSpecifiers.length > 0 ? 'type' : [],
typeofSpecifiers.length > 0 ? 'typeof' : [],
].flat();
context.report({
node,
messageId: 'topLevel',
data: {
kind: kind.join('/'),
},
fix(fixer) {
return fixer.replaceText(node, newImports);
},
});
}
else {
for (const specifier of [...typeSpecifiers, ...typeofSpecifiers]) {
context.report({
node: specifier,
messageId: 'topLevel',
data: {
kind: specifier.importKind,
},
fix(fixer) {
const fixes = [];
if (valueSpecifiers.length > 0) {
removeSpecifiers(fixes, fixer, sourceCode, typeSpecifiers);
removeSpecifiers(fixes, fixer, sourceCode, typeofSpecifiers);
const maybeComma = sourceCode.getTokenAfter(valueSpecifiers[valueSpecifiers.length - 1]);
if (isComma(maybeComma)) {
fixes.push(fixer.remove(maybeComma));
}
}
else if (defaultSpecifier) {
const comma = sourceCode.getTokenAfter(defaultSpecifier, isComma);
const closingBrace = sourceCode.getTokenAfter(node.specifiers[node.specifiers.length - 1], token => token.type === 'Punctuator' && token.value === '}');
fixes.push(fixer.removeRange([
comma.range[0],
closingBrace.range[1],
]));
}
return [
...fixes,
fixer.insertTextAfter(node, `\n${newImports}`),
];
},
});
}
}
},
};
},
});
//# sourceMappingURL=consistent-type-specifier-style.js.map