117616: Ported alias-imports rule

This commit is contained in:
Alexandre Vryghem
2024-08-28 16:43:40 +02:00
parent d4aedbbb51
commit abb03799e0
5 changed files with 194 additions and 0 deletions

View File

@@ -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"

View File

@@ -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

View File

@@ -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';
```

View File

@@ -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<Message, unknown[]>, 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<Message, unknown[]>, 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);
},
});
}
}

View File

@@ -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,