From abb03799e091520f8bef0d80ec35cd7b5a014840 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Wed, 28 Aug 2024 16:43:40 +0200 Subject: [PATCH] 117616: Ported alias-imports rule --- .eslintrc.json | 1 + docs/lint/ts/index.md | 1 + docs/lint/ts/rules/alias-imports.md | 58 ++++++++++++ lint/src/rules/ts/alias-imports.ts | 132 ++++++++++++++++++++++++++++ lint/src/rules/ts/index.ts | 2 + 5 files changed, 194 insertions(+) create mode 100644 docs/lint/ts/rules/alias-imports.md create mode 100644 lint/src/rules/ts/alias-imports.ts diff --git a/.eslintrc.json b/.eslintrc.json index 1e6e67ca76..656722cfe9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -263,6 +263,7 @@ "rxjs/no-nested-subscribe": "off", // todo: go over _all_ cases // Custom DSpace Angular rules + "dspace-angular-ts/alias-imports": "error", "dspace-angular-ts/themed-component-classes": "error", "dspace-angular-ts/themed-component-selectors": "error", "dspace-angular-ts/themed-component-usages": "error" diff --git a/docs/lint/ts/index.md b/docs/lint/ts/index.md index ed060c946e..3411202be9 100644 --- a/docs/lint/ts/index.md +++ b/docs/lint/ts/index.md @@ -1,6 +1,7 @@ [DSpace ESLint plugins](../../../lint/README.md) > TypeScript rules _______ +- [`dspace-angular-ts/alias-imports`](./rules/alias-imports.md): Unclear imports should be aliased for clarity - [`dspace-angular-ts/themed-component-classes`](./rules/themed-component-classes.md): Formatting rules for themeable component classes - [`dspace-angular-ts/themed-component-selectors`](./rules/themed-component-selectors.md): Themeable component selectors should follow the DSpace convention - [`dspace-angular-ts/themed-component-usages`](./rules/themed-component-usages.md): Themeable components should be used via their `ThemedComponent` wrapper class diff --git a/docs/lint/ts/rules/alias-imports.md b/docs/lint/ts/rules/alias-imports.md new file mode 100644 index 0000000000..281c821419 --- /dev/null +++ b/docs/lint/ts/rules/alias-imports.md @@ -0,0 +1,58 @@ +[DSpace ESLint plugins](../../../../lint/README.md) > [TypeScript rules](../index.md) > `dspace-angular-ts/alias-imports` +_______ + +Unclear imports should be aliased for clarity + +_______ + +[Source code](../../../../lint/src/rules/ts/alias-imports.ts) + +### Examples + + +#### Valid code + +##### correctly aliased imports + +```typescript +import { of as observableOf } from 'rxjs'; +``` + + + + +#### Invalid code & automatic fixes + +##### imports without alias + +```typescript +import { of } from 'rxjs'; +``` +Will produce the following error(s): +``` +This import must be aliased +``` + +Result of `yarn lint --fix`: +```typescript +import { of as observableOf } from 'rxjs'; +``` + + +##### imports under the wrong alias + +```typescript +import { of as ofSomething } from 'rxjs'; +``` +Will produce the following error(s): +``` +This import uses the wrong alias (should be {{ local }}) +``` + +Result of `yarn lint --fix`: +```typescript +import { of as observableOf } from 'rxjs'; +``` + + + diff --git a/lint/src/rules/ts/alias-imports.ts b/lint/src/rules/ts/alias-imports.ts new file mode 100644 index 0000000000..2b16bb26bd --- /dev/null +++ b/lint/src/rules/ts/alias-imports.ts @@ -0,0 +1,132 @@ +import { + ESLintUtils, + TSESLint, + TSESTree, +} from '@typescript-eslint/utils'; + +import { + DSpaceESLintRuleInfo, + NamedTests, +} from '../../util/structure'; + +export enum Message { + NO_ALIAS = 'noAlias', + WRONG_ALIAS = 'wrongAlias', +} + +export const info: DSpaceESLintRuleInfo = { + name: 'alias-imports', + meta: { + docs: { + description: 'Unclear imports should be aliased for clarity', + }, + messages: { + [Message.NO_ALIAS]: 'This import must be aliased', + [Message.WRONG_ALIAS]: 'This import uses the wrong alias (should be {{ local }})', + }, + fixable: 'code', + type: 'problem', + schema: [ + { + type: 'array', + items: { + type: 'object', + properties: { + package: { type: 'string' }, + imported: { type: 'string' }, + local: { type: 'string' }, + }, + }, + }, + ], + }, + defaultOptions: [ + [ + { + package: 'rxjs', + imported: 'of', + local: 'observableOf', + }, + ], + ], +}; + +export const rule = ESLintUtils.RuleCreator.withoutDocs({ + ...info, + create(context: TSESLint.RuleContext, options: any) { + return options[0].reduce((selectors: any, option: any) => { + selectors[`ImportDeclaration[source.value = "${option.package}"] > ImportSpecifier[imported.name = "${option.imported}"][local.name != "${option.local}"]`] = (node: TSESTree.ImportSpecifier) => handleUnaliasedImport(context, option, node); + return selectors; + }, {}); + }, +}); + +export const tests: NamedTests = { + plugin: info.name, + valid: [ + { + name: 'correctly aliased imports', + code: ` +import { of as observableOf } from 'rxjs'; + `, + }, + ], + invalid: [ + { + name: 'imports without alias', + code: ` +import { of } from 'rxjs'; + `, + errors: [ + { + messageId: 'noAlias', + }, + ], + output: ` +import { of as observableOf } from 'rxjs'; + `, + }, + { + name: 'imports under the wrong alias', + code: ` +import { of as ofSomething } from 'rxjs'; + `, + errors: [ + { + messageId: 'wrongAlias', + }, + ], + output: ` +import { of as observableOf } from 'rxjs'; + `, + }, + ], +}; + +/** + * Replaces the incorrectly aliased imports with the ones defined in the defaultOptions + * + * @param context The current {@link TSESLint.RuleContext} + * @param option The current `defaultOptions` that needs to be handled + * @param node The incorrect import node that should be fixed + */ +function handleUnaliasedImport(context: TSESLint.RuleContext, option: any, node: TSESTree.ImportSpecifier): void { + if (node.local.name === node.imported.name) { + context.report({ + messageId: Message.NO_ALIAS, + node: node, + fix(fixer: TSESLint.RuleFixer) { + return fixer.replaceText(node.local, `${option.imported} as ${option.local}`); + }, + }); + } else { + context.report({ + messageId: Message.WRONG_ALIAS, + data: { local: option.local }, + node: node, + fix(fixer: TSESLint.RuleFixer) { + return fixer.replaceText(node.local, option.local); + }, + }); + } +} diff --git a/lint/src/rules/ts/index.ts b/lint/src/rules/ts/index.ts index a7fdfe41ef..0b42ee24c4 100644 --- a/lint/src/rules/ts/index.ts +++ b/lint/src/rules/ts/index.ts @@ -10,11 +10,13 @@ import { RuleExports, } from '../../util/structure'; /* eslint-disable import/no-namespace */ +import * as aliasImports from './alias-imports'; import * as themedComponentClasses from './themed-component-classes'; import * as themedComponentSelectors from './themed-component-selectors'; import * as themedComponentUsages from './themed-component-usages'; const index = [ + aliasImports, themedComponentClasses, themedComponentSelectors, themedComponentUsages,