diff --git a/.eslintrc.json b/.eslintrc.json index 3f3dbf27c9..6e43df97be 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -270,7 +270,7 @@ { "package": "rxjs", "imported": "of", - "local": "observableOf" + "local": "of" } ] } diff --git a/docs/lint/html/rules/no-disabled-attribute-on-button.md b/docs/lint/html/rules/no-disabled-attribute-on-button.md index 8f7539cbc7..60ed50732e 100644 --- a/docs/lint/html/rules/no-disabled-attribute-on-button.md +++ b/docs/lint/html/rules/no-disabled-attribute-on-button.md @@ -21,24 +21,28 @@ _______ ```html ``` + ##### disabled attribute is still valid on non-button elements ```html ``` + ##### [disabled] attribute is still valid on non-button elements ```html ``` + ##### angular dynamic attributes that use disabled are still valid ```html ``` + @@ -49,6 +53,9 @@ _______ ```html + + + ``` Will produce the following error(s): ``` @@ -65,6 +72,9 @@ Result of `yarn lint --fix`: ```html + + + ``` Will produce the following error(s): ``` diff --git a/docs/lint/html/rules/themed-component-usages.md b/docs/lint/html/rules/themed-component-usages.md index 885a81014b..aff479d2c0 100644 --- a/docs/lint/html/rules/themed-component-usages.md +++ b/docs/lint/html/rules/themed-component-usages.md @@ -25,6 +25,7 @@ _______ ``` + ##### use no-prefix selectors in TypeScript templates @@ -35,6 +36,7 @@ _______ class Test { } ``` + ##### use no-prefix selectors in TypeScript test templates @@ -47,6 +49,7 @@ Filename: `lint/test/fixture/src/test.spec.ts` class Test { } ``` + ##### base selectors are also allowed in TypeScript test templates @@ -59,6 +62,7 @@ Filename: `lint/test/fixture/src/test.spec.ts` class Test { } ``` + @@ -71,6 +75,9 @@ class Test { + + + ``` Will produce the following error(s): ``` @@ -93,6 +100,9 @@ Result of `yarn lint --fix`: + + + ``` Will produce the following error(s): ``` diff --git a/docs/lint/ts/rules/alias-imports.md b/docs/lint/ts/rules/alias-imports.md index 319047e0aa..9a2d6cda2b 100644 --- a/docs/lint/ts/rules/alias-imports.md +++ b/docs/lint/ts/rules/alias-imports.md @@ -32,6 +32,42 @@ A list of all the imports that you want to alias for clarity. Every alias should ```typescript import { of as observableOf } from 'rxjs'; ``` + +With options: + +```json +{ + "aliases": [ + { + "package": "rxjs", + "imported": "of", + "local": "observableOf" + } + ] +} +``` + + +##### enforce unaliased import + +```typescript +import { combineLatest } from 'rxjs'; +``` + +With options: + +```json +{ + "aliases": [ + { + "package": "rxjs", + "imported": "combineLatest", + "local": "combineLatest" + } + ] +} +``` + @@ -42,6 +78,9 @@ import { of as observableOf } from 'rxjs'; ```typescript import { of } from 'rxjs'; + + + ``` Will produce the following error(s): ``` @@ -58,6 +97,9 @@ import { of as observableOf } from 'rxjs'; ```typescript import { of as ofSomething } from 'rxjs'; + + + ``` Will produce the following error(s): ``` @@ -70,4 +112,37 @@ import { of as observableOf } from 'rxjs'; ``` +##### disallow aliasing import + +```typescript +import { combineLatest as observableCombineLatest } from 'rxjs'; + + +With options: + +```json +{ + "aliases": [ + { + "package": "rxjs", + "imported": "combineLatest", + "local": "combineLatest" + } + ] +} +``` + + +``` +Will produce the following error(s): +``` +This import should not use an alias +``` + +Result of `yarn lint --fix`: +```typescript +import { combineLatest } from 'rxjs'; +``` + + diff --git a/docs/lint/ts/rules/sort-standalone-imports.md b/docs/lint/ts/rules/sort-standalone-imports.md index d1d2c28266..d6c7896c3e 100644 --- a/docs/lint/ts/rules/sort-standalone-imports.md +++ b/docs/lint/ts/rules/sort-standalone-imports.md @@ -44,6 +44,7 @@ Whether the last import should have a trailing comma (only applicable for multil }) export class AppComponent {} ``` + ##### should not inlines singular imports when maxItems is 0 @@ -59,6 +60,7 @@ export class AppComponent {} }) export class AppComponent {} ``` + ##### should inline singular imports when maxItems is 1 @@ -72,6 +74,15 @@ export class AppComponent {} }) export class AppComponent {} ``` + +With options: + +```json +{ + "maxItems": 1 +} +``` + @@ -92,6 +103,9 @@ export class AppComponent {} ], }) export class AppComponent {} + + + ``` Will produce the following error(s): ``` @@ -125,6 +139,9 @@ export class AppComponent {} imports: [RootComponent], }) export class AppComponent {} + + + ``` Will produce the following error(s): ``` @@ -159,6 +176,17 @@ export class AppComponent {} ], }) export class AppComponent {} + + +With options: + +```json +{ + "maxItems": 1 +} +``` + + ``` Will produce the following error(s): ``` @@ -189,6 +217,9 @@ export class AppComponent {} imports: [AsyncPipe, RootComponent], }) export class AppComponent {} + + + ``` Will produce the following error(s): ``` diff --git a/docs/lint/ts/rules/themed-component-classes.md b/docs/lint/ts/rules/themed-component-classes.md index ab9bb84632..0f64dc8907 100644 --- a/docs/lint/ts/rules/themed-component-classes.md +++ b/docs/lint/ts/rules/themed-component-classes.md @@ -28,6 +28,7 @@ _______ class Something { } ``` + ##### Base component @@ -39,6 +40,7 @@ class Something { class TestThemeableComponent { } ``` + ##### Wrapper component @@ -55,6 +57,7 @@ Filename: `lint/test/fixture/src/app/test/themed-test-themeable.component.ts` class ThemedTestThemeableComponent extends ThemedComponent { } ``` + ##### Override component @@ -68,6 +71,7 @@ Filename: `lint/test/fixture/src/themes/test/app/test/test-themeable.component.t class Override extends BaseComponent { } ``` + @@ -82,6 +86,9 @@ class Override extends BaseComponent { }) class TestThemeableComponent { } + + + ``` Will produce the following error(s): ``` @@ -109,6 +116,9 @@ Filename: `lint/test/fixture/src/app/test/themed-test-themeable.component.ts` }) class ThemedTestThemeableComponent extends ThemedComponent { } + + + ``` Will produce the following error(s): ``` @@ -139,6 +149,9 @@ Filename: `lint/test/fixture/src/app/test/themed-test-themeable.component.ts` }) class ThemedTestThemeableComponent extends ThemedComponent { } + + + ``` Will produce the following error(s): ``` @@ -173,6 +186,9 @@ import { SomethingElse } from './somewhere-else'; }) class ThemedTestThemeableComponent extends ThemedComponent { } + + + ``` Will produce the following error(s): ``` @@ -209,6 +225,9 @@ import { Something, SomethingElse } from './somewhere-else'; }) class ThemedTestThemeableComponent extends ThemedComponent { } + + + ``` Will produce the following error(s): ``` @@ -239,6 +258,9 @@ Filename: `lint/test/fixture/src/themes/test/app/test/test-themeable.component.t }) class Override extends BaseComponent { } + + + ``` Will produce the following error(s): ``` diff --git a/docs/lint/ts/rules/themed-component-selectors.md b/docs/lint/ts/rules/themed-component-selectors.md index c24263a934..bf150223ba 100644 --- a/docs/lint/ts/rules/themed-component-selectors.md +++ b/docs/lint/ts/rules/themed-component-selectors.md @@ -33,6 +33,7 @@ _______ class Something { } ``` + ##### Themeable component selector should replace the original version, unthemed version should be changed to ds-base- @@ -55,6 +56,7 @@ class ThemedSomething extends ThemedComponent { class OverrideSomething extends Something { } ``` + ##### Other themed component wrappers should not interfere @@ -71,6 +73,7 @@ class Something { class ThemedSomethingElse extends ThemedComponent { } ``` + @@ -87,6 +90,9 @@ Filename: `lint/test/fixture/src/app/test/test-themeable.component.ts` }) class TestThemeableComponent { } + + + ``` Will produce the following error(s): ``` @@ -113,6 +119,9 @@ Filename: `lint/test/fixture/src/app/test/themed-test-themeable.component.ts` }) class ThemedTestThemeableComponent extends ThemedComponent { } + + + ``` Will produce the following error(s): ``` @@ -139,6 +148,9 @@ Filename: `lint/test/fixture/src/themes/test/app/test/test-themeable.component.t }) class TestThememeableComponent extends BaseComponent { } + + + ``` Will produce the following error(s): ``` diff --git a/docs/lint/ts/rules/themed-component-usages.md b/docs/lint/ts/rules/themed-component-usages.md index 9b7efb42c7..2aab911af9 100644 --- a/docs/lint/ts/rules/themed-component-usages.md +++ b/docs/lint/ts/rules/themed-component-usages.md @@ -32,6 +32,7 @@ const config = { b: ChipsComponent, } ``` + ##### allow base class in class declaration @@ -39,6 +40,7 @@ const config = { export class TestThemeableComponent { } ``` + ##### allow inheriting from base class @@ -48,6 +50,7 @@ import { TestThemeableComponent } from './app/test/test-themeable.component'; export class ThemedAdminSidebarComponent extends ThemedComponent { } ``` + ##### allow base class in ViewChild @@ -58,6 +61,7 @@ export class Something { @ViewChild(TestThemeableComponent) test: TestThemeableComponent; } ``` + ##### allow wrapper selectors in test queries @@ -67,6 +71,7 @@ Filename: `lint/test/fixture/src/app/test/test.component.spec.ts` By.css('ds-themeable'); By.css('#test > ds-themeable > #nest'); ``` + ##### allow wrapper selectors in cypress queries @@ -76,6 +81,7 @@ Filename: `lint/test/fixture/src/app/test/test.component.cy.ts` By.css('ds-themeable'); By.css('#test > ds-themeable > #nest'); ``` + @@ -92,6 +98,9 @@ const config = { a: TestThemeableComponent, b: TestComponent, } + + + ``` Will produce the following error(s): ``` @@ -122,6 +131,9 @@ const config = { b: TestComponent, c: Something, } + + + ``` Will produce the following error(s): ``` @@ -152,6 +164,9 @@ const DECLARATIONS = [ Something, ThemedTestThemeableComponent, ]; + + + ``` Will produce the following error(s): ``` @@ -175,6 +190,9 @@ Filename: `lint/test/fixture/src/app/test/test.component.spec.ts` ```typescript By.css('ds-themed-themeable'); By.css('#test > ds-themed-themeable > #nest'); + + + ``` Will produce the following error(s): ``` @@ -196,6 +214,9 @@ Filename: `lint/test/fixture/src/app/test/test.component.spec.ts` ```typescript By.css('ds-base-themeable'); By.css('#test > ds-base-themeable > #nest'); + + + ``` Will produce the following error(s): ``` @@ -217,6 +238,9 @@ Filename: `lint/test/fixture/src/app/test/test.component.cy.ts` ```typescript cy.get('ds-themed-themeable'); cy.get('#test > ds-themed-themeable > #nest'); + + + ``` Will produce the following error(s): ``` @@ -238,6 +262,9 @@ Filename: `lint/test/fixture/src/app/test/test.component.cy.ts` ```typescript cy.get('ds-base-themeable'); cy.get('#test > ds-base-themeable > #nest'); + + + ``` Will produce the following error(s): ``` @@ -268,6 +295,9 @@ import { TestThemeableComponent } from '../../../../app/test/test-themeable.comp }) export class UsageComponent { } + + + ``` Will produce the following error(s): ``` @@ -308,6 +338,9 @@ import { ThemedTestThemeableComponent } from '../../../../app/test/themed-test-t }) export class UsageComponent { } + + + ``` Will produce the following error(s): ``` diff --git a/docs/lint/ts/rules/themed-decorators.md b/docs/lint/ts/rules/themed-decorators.md index 9b7755c5aa..6a9a0bf693 100644 --- a/docs/lint/ts/rules/themed-decorators.md +++ b/docs/lint/ts/rules/themed-decorators.md @@ -29,6 +29,7 @@ Filename: `lint/test/fixture/src/themes/test/app/dynamic-component/dynamic-compo export class Something extends SomethingElse { } ``` + ##### plain file declares no theme in @listableObjectComponent @@ -39,6 +40,7 @@ Filename: `lint/test/fixture/src/app/dynamic-component/dynamic-component.ts` export class Something extends SomethingElse { } ``` + ##### plain file declares explicit undefined theme in @listableObjectComponent @@ -49,6 +51,7 @@ Filename: `lint/test/fixture/src/app/dynamic-component/dynamic-component.ts` export class Something extends SomethingElse { } ``` + ##### test file declares theme outside of theme directory @@ -59,6 +62,7 @@ Filename: `lint/test/fixture/src/app/dynamic-component/dynamic-component.spec.ts export class Something extends SomethingElse { } ``` + ##### only track configured decorators @@ -69,6 +73,7 @@ Filename: `lint/test/fixture/src/app/dynamic-component/dynamic-component.ts` export class Something extends SomethingElse { } ``` + @@ -83,6 +88,9 @@ Filename: `lint/test/fixture/src/themes/test/app/dynamic-component/dynamic-compo @listableObjectComponent(something, somethingElse, undefined, 'test-2') export class Something extends SomethingElse { } + + + ``` Will produce the following error(s): ``` @@ -105,6 +113,9 @@ Filename: `lint/test/fixture/src/app/dynamic-component/dynamic-component.ts` @listableObjectComponent(something, somethingElse, undefined, 'test-2') export class Something extends SomethingElse { } + + + ``` Will produce the following error(s): ``` @@ -127,6 +138,9 @@ Filename: `lint/test/fixture/src/themes/test-2/app/dynamic-component/dynamic-com @listableObjectComponent(something, somethingElse, undefined) export class Something extends SomethingElse { } + + + ``` Will produce the following error(s): ``` @@ -149,6 +163,9 @@ Filename: `lint/test/fixture/src/themes/test-2/app/dynamic-component/dynamic-com @listableObjectComponent(something, somethingElse, undefined, undefined) export class Something extends SomethingElse { } + + + ``` Will produce the following error(s): ``` diff --git a/docs/lint/ts/rules/themed-wrapper-no-input-defaults.md b/docs/lint/ts/rules/themed-wrapper-no-input-defaults.md index 2653aab3a5..4e8e833325 100644 --- a/docs/lint/ts/rules/themed-wrapper-no-input-defaults.md +++ b/docs/lint/ts/rules/themed-wrapper-no-input-defaults.md @@ -23,6 +23,7 @@ export class TTest extends ThemedComponent { test; } ``` + ##### Regular class defines an input with a default value @@ -33,6 +34,7 @@ export class Test { test = 'test'; } ``` + @@ -56,6 +58,9 @@ test2: number = 123; @Input() test3: number[] = [1,2,3]; } + + + ``` Will produce the following error(s): ``` @@ -74,6 +79,9 @@ export class TTest extends ThemedComponent { @Input() test = undefined; } + + + ``` Will produce the following error(s): ``` diff --git a/docs/lint/ts/rules/unique-decorators.md b/docs/lint/ts/rules/unique-decorators.md index 09089e67b2..70ba1235ef 100644 --- a/docs/lint/ts/rules/unique-decorators.md +++ b/docs/lint/ts/rules/unique-decorators.md @@ -43,6 +43,7 @@ export class C { export class C { } ``` + ##### unchecked decorator, some repetitions @@ -55,6 +56,7 @@ export class A { export class B { } ``` + @@ -71,6 +73,9 @@ export class A { @listableObjectComponent(a) export class B { } + + + ``` Will produce the following error(s): ``` diff --git a/lint/src/rules/ts/alias-imports.ts b/lint/src/rules/ts/alias-imports.ts index 9fca824f6b..5ff5726374 100644 --- a/lint/src/rules/ts/alias-imports.ts +++ b/lint/src/rules/ts/alias-imports.ts @@ -13,9 +13,10 @@ import { } from '../../util/structure'; export enum Message { - NO_ALIAS = 'noAlias', + MISSING_ALIAS = 'missingAlias', WRONG_ALIAS = 'wrongAlias', MULTIPLE_ALIASES = 'multipleAliases', + UNNECESSARY_ALIAS = 'unnecessaryAlias', } interface AliasImportOptions { @@ -39,9 +40,10 @@ export const info: DSpaceESLintRuleInfo<[AliasImportOptions], [AliasImportDocOpt description: 'Unclear imports should be aliased for clarity', }, messages: { - [Message.NO_ALIAS]: 'This import must be aliased', + [Message.MISSING_ALIAS]: 'This import must be aliased', [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 }})', + [Message.UNNECESSARY_ALIAS]: 'This import should not use an alias', }, fixable: 'code', type: 'problem', @@ -103,6 +105,34 @@ export const tests: NamedTests = { code: ` import { of as observableOf } from 'rxjs'; `, + options: [ + { + aliases: [ + { + package: 'rxjs', + imported: 'of', + local: 'observableOf', + }, + ], + }, + ], + }, + { + name: 'enforce unaliased import', + code: ` +import { combineLatest } from 'rxjs'; + `, + options: [ + { + aliases: [ + { + package: 'rxjs', + imported: 'combineLatest', + local: 'combineLatest', + }, + ], + }, + ], }, ], invalid: [ @@ -113,7 +143,7 @@ import { of } from 'rxjs'; `, errors: [ { - messageId: 'noAlias', + messageId: 'missingAlias', }, ], output: ` @@ -134,6 +164,31 @@ import { of as ofSomething } from 'rxjs'; import { of as observableOf } from 'rxjs'; `, }, + { + name: 'disallow aliasing import', + code: ` +import { combineLatest as observableCombineLatest } from 'rxjs'; + `, + errors: [ + { + messageId: 'unnecessaryAlias', + }, + ], + output: ` +import { combineLatest } from 'rxjs'; + `, + options: [ + { + aliases: [ + { + package: 'rxjs', + imported: 'combineLatest', + local: 'combineLatest', + }, + ], + }, + ], + }, ], }; @@ -145,56 +200,94 @@ import { of as observableOf } from 'rxjs'; * @param node The incorrect import node that should be fixed */ function handleUnaliasedImport(context: TSESLint.RuleContext, option: AliasImportOption, node: TSESTree.ImportSpecifier): void { - 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; + const hasCorrectAliasedImport: 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 (option.imported === option.local) { + if (hasCorrectAliasedImport) { + context.report({ + messageId: Message.MULTIPLE_ALIASES, + data: { local: option.local }, + node: node, + fix(fixer: TSESLint.RuleFixer) { + const fixes: TSESLint.RuleFix[] = []; - if (hasAliasedImport) { - context.report({ - messageId: Message.MULTIPLE_ALIASES, - data: { local: option.local }, - node: node, - fix(fixer: TSESLint.RuleFixer) { - const fixes: TSESLint.RuleFix[] = []; + const commaAfter = context.sourceCode.getTokenAfter(node, { + filter: (token: TSESTree.Token) => token.value === ',', + }); + if (commaAfter) { + fixes.push(fixer.removeRange([node.range[0], commaAfter.range[1]])); + } else { + fixes.push(fixer.remove(node)); + } + fixes.push(...retrieveUsageReplacementFixes(context, fixer, node, option.local)); - const commaAfter = context.sourceCode.getTokenAfter(node, { - filter: (token: TSESTree.Token) => token.value === ',', - }); - if (commaAfter) { - fixes.push(fixer.removeRange([node.range[0], commaAfter.range[1]])); - } else { - fixes.push(fixer.remove(node)); - } - fixes.push(...retrieveUsageReplacementFixes(context, fixer, node, option.local)); + return fixes; + }, + }); + } else { + context.report({ + messageId: Message.UNNECESSARY_ALIAS, + data: { local: option.local }, + node: node, + fix(fixer: TSESLint.RuleFixer) { + const fixes: TSESLint.RuleFix[] = []; - return fixes; - }, - }); - } else if (node.local.name === node.imported.name) { - context.report({ - messageId: Message.NO_ALIAS, - node: node, - fix(fixer: TSESLint.RuleFixer) { - const fixes: TSESLint.RuleFix[] = []; + fixes.push(fixer.replaceText(node, option.imported)); + fixes.push(...retrieveUsageReplacementFixes(context, fixer, node, option.local)); - fixes.push(fixer.replaceText(node.local, `${option.imported} as ${option.local}`)); - fixes.push(...retrieveUsageReplacementFixes(context, fixer, node, option.local)); - - return fixes; - }, - }); + return fixes; + }, + }); + } } else { - context.report({ - messageId: Message.WRONG_ALIAS, - data: { local: option.local }, - node: node, - fix(fixer: TSESLint.RuleFixer) { - const fixes: TSESLint.RuleFix[] = []; + if (hasCorrectAliasedImport) { + context.report({ + messageId: Message.MULTIPLE_ALIASES, + data: { local: option.local }, + node: node, + fix(fixer: TSESLint.RuleFixer) { + const fixes: TSESLint.RuleFix[] = []; - fixes.push(fixer.replaceText(node.local, option.local)); - fixes.push(...retrieveUsageReplacementFixes(context, fixer, node, option.local)); + const commaAfter = context.sourceCode.getTokenAfter(node, { + filter: (token: TSESTree.Token) => token.value === ',', + }); + if (commaAfter) { + fixes.push(fixer.removeRange([node.range[0], commaAfter.range[1]])); + } else { + fixes.push(fixer.remove(node)); + } + fixes.push(...retrieveUsageReplacementFixes(context, fixer, node, option.local)); - return fixes; - }, - }); + return fixes; + }, + }); + } else if (node.local.name === node.imported.name) { + context.report({ + messageId: Message.MISSING_ALIAS, + node: node, + fix(fixer: TSESLint.RuleFixer) { + const fixes: TSESLint.RuleFix[] = []; + + fixes.push(fixer.replaceText(node.local, `${option.imported} as ${option.local}`)); + fixes.push(...retrieveUsageReplacementFixes(context, fixer, node, option.local)); + + return fixes; + }, + }); + } else { + context.report({ + messageId: Message.WRONG_ALIAS, + data: { local: option.local }, + node: node, + fix(fixer: TSESLint.RuleFixer) { + const fixes: TSESLint.RuleFix[] = []; + + fixes.push(fixer.replaceText(node.local, option.local)); + fixes.push(...retrieveUsageReplacementFixes(context, fixer, node, option.local)); + + return fixes; + }, + }); + } } } diff --git a/lint/src/util/templates/rule.ejs b/lint/src/util/templates/rule.ejs index 06232d021d..2fc0548fba 100644 --- a/lint/src/util/templates/rule.ejs +++ b/lint/src/util/templates/rule.ejs @@ -24,6 +24,13 @@ Filename: `<%- test.filename %>` ```<%- plugin.language.toLowerCase() %> <%- test.code.trim() %> ``` + <% if (test?.options?.length > 0) { %> +With options: + +```json +<%- JSON.stringify(test.options[0], null, 2) %> +``` + <% }%> <% }) %> <% } %> @@ -36,6 +43,15 @@ Filename: `<%- test.filename %>` <% } %> ```<%- plugin.language.toLowerCase() %> <%- test.code.trim() %> + + <% if (test?.options?.length > 0) { %> +With options: + +```json +<%- JSON.stringify(test.options[0], null, 2) %> +``` + <% }%> + ``` Will produce the following error(s): ```