117616: Fixed alias-imports edge case where duplicate imports with different aliases were both kept

This commit is contained in:
Alexandre Vryghem
2024-08-28 19:00:49 +02:00
parent abb03799e0
commit f10447b8d3
2 changed files with 33 additions and 7 deletions

View File

@@ -1,4 +1,5 @@
import { import {
AST_NODE_TYPES,
ESLintUtils, ESLintUtils,
TSESLint, TSESLint,
TSESTree, TSESTree,
@@ -12,9 +13,16 @@ import {
export enum Message { export enum Message {
NO_ALIAS = 'noAlias', NO_ALIAS = 'noAlias',
WRONG_ALIAS = 'wrongAlias', WRONG_ALIAS = 'wrongAlias',
MULTIPLE_ALIASES = 'multipleAliases',
} }
export const info: DSpaceESLintRuleInfo = { interface AliasImportOption {
package: string;
imported: string;
local: string;
}
export const info: DSpaceESLintRuleInfo<[[AliasImportOption]]> = {
name: 'alias-imports', name: 'alias-imports',
meta: { meta: {
docs: { docs: {
@@ -23,6 +31,7 @@ export const info: DSpaceESLintRuleInfo = {
messages: { messages: {
[Message.NO_ALIAS]: 'This import must be aliased', [Message.NO_ALIAS]: 'This import must be aliased',
[Message.WRONG_ALIAS]: 'This import uses the wrong alias (should be {{ local }})', [Message.WRONG_ALIAS]: 'This import uses the wrong alias (should be {{ local }})',
[Message.MULTIPLE_ALIASES]: 'This import was used twice with a different alias (should be {{ local }})',
}, },
fixable: 'code', fixable: 'code',
type: 'problem', type: 'problem',
@@ -54,7 +63,7 @@ export const info: DSpaceESLintRuleInfo = {
export const rule = ESLintUtils.RuleCreator.withoutDocs({ export const rule = ESLintUtils.RuleCreator.withoutDocs({
...info, ...info,
create(context: TSESLint.RuleContext<Message, unknown[]>, options: any) { create(context: TSESLint.RuleContext<Message, unknown[]>, options: any) {
return options[0].reduce((selectors: any, option: any) => { return options[0].reduce((selectors: any, option: AliasImportOption) => {
selectors[`ImportDeclaration[source.value = "${option.package}"] > ImportSpecifier[imported.name = "${option.imported}"][local.name != "${option.local}"]`] = (node: TSESTree.ImportSpecifier) => handleUnaliasedImport(context, option, node); selectors[`ImportDeclaration[source.value = "${option.package}"] > ImportSpecifier[imported.name = "${option.imported}"][local.name != "${option.local}"]`] = (node: TSESTree.ImportSpecifier) => handleUnaliasedImport(context, option, node);
return selectors; return selectors;
}, {}); }, {});
@@ -107,11 +116,28 @@ import { of as observableOf } from 'rxjs';
* Replaces the incorrectly aliased imports with the ones defined in the defaultOptions * Replaces the incorrectly aliased imports with the ones defined in the defaultOptions
* *
* @param context The current {@link TSESLint.RuleContext} * @param context The current {@link TSESLint.RuleContext}
* @param option The current `defaultOptions` that needs to be handled * @param option The current {@link AliasImportOption} that needs to be handled
* @param node The incorrect import node that should be fixed * @param node The incorrect import node that should be fixed
*/ */
function handleUnaliasedImport(context: TSESLint.RuleContext<Message, unknown[]>, option: any, node: TSESTree.ImportSpecifier): void { function handleUnaliasedImport(context: TSESLint.RuleContext<Message, unknown[]>, option: AliasImportOption, node: TSESTree.ImportSpecifier): void {
if (node.local.name === node.imported.name) { const hasAliasedImport: boolean = (node.parent as TSESTree.ImportDeclaration).specifiers.find((specifier: TSESTree.ImportClause) => specifier.local.name === option.local && specifier.type === AST_NODE_TYPES.ImportSpecifier && (specifier as TSESTree.ImportSpecifier).imported.name === option.imported) !== undefined;
if (hasAliasedImport) {
context.report({
messageId: Message.MULTIPLE_ALIASES,
node: node,
fix(fixer: TSESLint.RuleFixer) {
const commaAfter = context.sourceCode.getTokenAfter(node, {
filter: (token: TSESTree.Token) => token.value === ',',
});
if (commaAfter) {
return fixer.removeRange([node.range[0], commaAfter.range[1]]);
} else {
return fixer.remove(node);
}
},
});
} else if (node.local.name === node.imported.name) {
context.report({ context.report({
messageId: Message.NO_ALIAS, messageId: Message.NO_ALIAS,
node: node, node: node,

View File

@@ -17,10 +17,10 @@ export type Meta = RuleMetaData<string, unknown[]>;
export type Valid = ValidTestCase<unknown[]>; export type Valid = ValidTestCase<unknown[]>;
export type Invalid = InvalidTestCase<string, unknown[]>; export type Invalid = InvalidTestCase<string, unknown[]>;
export interface DSpaceESLintRuleInfo { export interface DSpaceESLintRuleInfo<T = unknown[]> {
name: string; name: string;
meta: Meta, meta: Meta,
defaultOptions: unknown[], defaultOptions: T,
} }
export interface NamedTests { export interface NamedTests {