@taiga-ui/eslint-plugin-experience-next 0.455.0 → 0.457.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/index.d.ts +3 -0
- package/index.esm.js +83 -19
- package/package.json +1 -1
- package/rules/no-redundant-type-annotation.d.ts +5 -0
- package/rules/short-tui-imports.d.ts +1 -1
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ export default [
|
|
|
49
49
|
| no-href-with-router-link | Do not use href and routerLink attributes together on the same element | | 🔧 | |
|
|
50
50
|
| no-implicit-public | Require explicit `public` modifier for class members and parameter properties | ✅ | 🔧 | |
|
|
51
51
|
| no-playwright-empty-fill | Enforce `clear()` over `fill('')` in Playwright tests | ✅ | 🔧 | |
|
|
52
|
+
| no-redundant-type-annotation | Disallow redundant type annotations when the type is already inferred from the initializer | ✅ | 🔧 | |
|
|
52
53
|
| no-string-literal-concat | Disallow string literal concatenation; merge adjacent literals into one | ✅ | 🔧 | |
|
|
53
54
|
| object-single-line | Enforce single-line formatting for single-property objects when it fits `printWidth` | ✅ | 🔧 | |
|
|
54
55
|
| prefer-deep-imports | Allow deep imports of Taiga UI packages | | 🔧 | |
|
|
@@ -501,6 +502,42 @@ Spread elements are placed after named identifiers.
|
|
|
501
502
|
|
|
502
503
|
---
|
|
503
504
|
|
|
505
|
+
## no-redundant-type-annotation
|
|
506
|
+
|
|
507
|
+
Disallow explicit type annotations on class properties and variable declarations when TypeScript can already infer the
|
|
508
|
+
same type from the initializer. Requires type information (`parserOptions.project`).
|
|
509
|
+
|
|
510
|
+
Works well in combination with `unused-imports/no-unused-imports` or `@typescript-eslint/no-unused-vars`, which will
|
|
511
|
+
then clean up any import that is no longer referenced after the annotation is removed.
|
|
512
|
+
|
|
513
|
+
```ts
|
|
514
|
+
// ❌ error — type is already inferred from inject()
|
|
515
|
+
private readonly options: TuiInputNumberOptions = inject(TUI_INPUT_NUMBER_OPTIONS);
|
|
516
|
+
|
|
517
|
+
// ✅ after autofix
|
|
518
|
+
private readonly options = inject(TUI_INPUT_NUMBER_OPTIONS);
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
```ts
|
|
522
|
+
// ❌ error — variable declaration
|
|
523
|
+
const service: MyService = inject(MyService);
|
|
524
|
+
|
|
525
|
+
// ✅ after autofix
|
|
526
|
+
const service = inject(MyService);
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
The rule does **not** report when the annotation intentionally widens or changes the type:
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
// ✅ ok — annotation widens Dog to Animal
|
|
533
|
+
x: Animal = new Dog();
|
|
534
|
+
|
|
535
|
+
// ✅ ok — annotation adds null to the union
|
|
536
|
+
x: MyService | null = inject(MyService);
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
504
541
|
## strict-tui-doc-example
|
|
505
542
|
|
|
506
543
|
Validates that properties of a `TuiDocExample`-typed object have keys matching known file-type names (`TypeScript`,
|
package/index.d.ts
CHANGED
|
@@ -40,6 +40,9 @@ declare const plugin: {
|
|
|
40
40
|
'no-playwright-empty-fill': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useClear", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
41
41
|
name: string;
|
|
42
42
|
};
|
|
43
|
+
'no-redundant-type-annotation': import("@typescript-eslint/utils/ts-eslint").RuleModule<"redundantTypeAnnotation", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
44
|
+
name: string;
|
|
45
|
+
};
|
|
43
46
|
'no-string-literal-concat': import("@typescript-eslint/utils/ts-eslint").RuleModule<"flattenTemplate" | "mergeLiterals" | "useTemplate", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
44
47
|
name: string;
|
|
45
48
|
};
|
package/index.esm.js
CHANGED
|
@@ -29,7 +29,7 @@ import tseslint from 'typescript-eslint';
|
|
|
29
29
|
import { createRequire } from 'node:module';
|
|
30
30
|
import { globSync } from 'glob';
|
|
31
31
|
import { ESLintUtils, AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
32
|
-
import ts from 'typescript';
|
|
32
|
+
import ts, { isCallExpression } from 'typescript';
|
|
33
33
|
import { AST_NODE_TYPES as AST_NODE_TYPES$1 } from '@typescript-eslint/types';
|
|
34
34
|
import path from 'node:path';
|
|
35
35
|
|
|
@@ -916,6 +916,7 @@ var recommended = defineConfig([
|
|
|
916
916
|
],
|
|
917
917
|
'@taiga-ui/experience-next/no-deep-imports-to-indexed-packages': 'error',
|
|
918
918
|
'@taiga-ui/experience-next/no-implicit-public': 'error',
|
|
919
|
+
'@taiga-ui/experience-next/no-redundant-type-annotation': 'error',
|
|
919
920
|
'@taiga-ui/experience-next/object-single-line': ['error', { printWidth: 90 }],
|
|
920
921
|
'@taiga-ui/experience-next/prefer-multi-arg-push': 'error',
|
|
921
922
|
'@taiga-ui/experience-next/short-tui-imports': 'error',
|
|
@@ -1329,8 +1330,8 @@ function intersect(a, b) {
|
|
|
1329
1330
|
return a.some((type) => origin.has(type));
|
|
1330
1331
|
}
|
|
1331
1332
|
|
|
1332
|
-
const createRule$
|
|
1333
|
-
var classPropertyNaming = createRule$
|
|
1333
|
+
const createRule$e = ESLintUtils.RuleCreator((name) => name);
|
|
1334
|
+
var classPropertyNaming = createRule$e({
|
|
1334
1335
|
create(context, [configs]) {
|
|
1335
1336
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
1336
1337
|
const typeChecker = parserServices.program.getTypeChecker();
|
|
@@ -1499,9 +1500,9 @@ function isExternalPureTuple(typeChecker, type) {
|
|
|
1499
1500
|
return typeArgs.every((item) => isClassType(item));
|
|
1500
1501
|
}
|
|
1501
1502
|
|
|
1502
|
-
const createRule$
|
|
1503
|
+
const createRule$d = ESLintUtils.RuleCreator((name) => name);
|
|
1503
1504
|
const MESSAGE_ID$5 = 'spreadArrays';
|
|
1504
|
-
var flatExports = createRule$
|
|
1505
|
+
var flatExports = createRule$d({
|
|
1505
1506
|
create(context) {
|
|
1506
1507
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
1507
1508
|
const typeChecker = parserServices.program.getTypeChecker();
|
|
@@ -1627,8 +1628,8 @@ var flatExports = createRule$c({
|
|
|
1627
1628
|
|
|
1628
1629
|
const MESSAGE_ID$4 = 'invalid-injection-token-description';
|
|
1629
1630
|
const ERROR_MESSAGE$3 = "InjectionToken's description should contain token's name";
|
|
1630
|
-
const createRule$
|
|
1631
|
-
const rule$
|
|
1631
|
+
const createRule$c = ESLintUtils.RuleCreator((name) => name);
|
|
1632
|
+
const rule$9 = createRule$c({
|
|
1632
1633
|
create(context) {
|
|
1633
1634
|
return {
|
|
1634
1635
|
'NewExpression[callee.name="InjectionToken"]'(node) {
|
|
@@ -1695,8 +1696,8 @@ const DEFAULT_OPTIONS = {
|
|
|
1695
1696
|
importDeclaration: '^@taiga-ui*',
|
|
1696
1697
|
projectName: String.raw `(?<=^@taiga-ui/)([-\w]+)`,
|
|
1697
1698
|
};
|
|
1698
|
-
const createRule$
|
|
1699
|
-
const rule$
|
|
1699
|
+
const createRule$b = ESLintUtils.RuleCreator((name) => name);
|
|
1700
|
+
const rule$8 = createRule$b({
|
|
1700
1701
|
create(context) {
|
|
1701
1702
|
const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
|
|
1702
1703
|
const hasNonCodeExtension = (source) => {
|
|
@@ -1783,13 +1784,13 @@ const rule$7 = createRule$a({
|
|
|
1783
1784
|
name: 'no-deep-imports',
|
|
1784
1785
|
});
|
|
1785
1786
|
|
|
1786
|
-
const createRule$
|
|
1787
|
+
const createRule$a = ESLintUtils.RuleCreator((name) => name);
|
|
1787
1788
|
const resolveCacheByOptions = new WeakMap();
|
|
1788
1789
|
const nearestFileUpCache = new Map();
|
|
1789
1790
|
const markerCache = new Map();
|
|
1790
1791
|
const indexFileCache = new Map();
|
|
1791
1792
|
const indexExportsCache = new Map();
|
|
1792
|
-
var noDeepImportsToIndexedPackages = createRule$
|
|
1793
|
+
var noDeepImportsToIndexedPackages = createRule$a({
|
|
1793
1794
|
create(context) {
|
|
1794
1795
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
1795
1796
|
const program = parserServices.program;
|
|
@@ -2031,8 +2032,8 @@ const config = {
|
|
|
2031
2032
|
},
|
|
2032
2033
|
};
|
|
2033
2034
|
|
|
2034
|
-
const createRule$
|
|
2035
|
-
const rule$
|
|
2035
|
+
const createRule$9 = ESLintUtils.RuleCreator((name) => name);
|
|
2036
|
+
const rule$7 = createRule$9({
|
|
2036
2037
|
create(context) {
|
|
2037
2038
|
const checkImplicitPublic = (node) => {
|
|
2038
2039
|
const classRef = getClass(node);
|
|
@@ -2103,8 +2104,8 @@ function getClass(node) {
|
|
|
2103
2104
|
return getClass(node.parent);
|
|
2104
2105
|
}
|
|
2105
2106
|
|
|
2106
|
-
const createRule$
|
|
2107
|
-
const rule$
|
|
2107
|
+
const createRule$8 = ESLintUtils.RuleCreator((name) => name);
|
|
2108
|
+
const rule$6 = createRule$8({
|
|
2108
2109
|
create(context) {
|
|
2109
2110
|
const services = ESLintUtils.getParserServices(context);
|
|
2110
2111
|
const checker = services.program.getTypeChecker();
|
|
@@ -2191,6 +2192,68 @@ function isPlaywrightLocatorType(type) {
|
|
|
2191
2192
|
});
|
|
2192
2193
|
}
|
|
2193
2194
|
|
|
2195
|
+
const createRule$7 = ESLintUtils.RuleCreator((name) => name);
|
|
2196
|
+
const rule$5 = createRule$7({
|
|
2197
|
+
create(context) {
|
|
2198
|
+
const parserServices = ESLintUtils.getParserServices(context);
|
|
2199
|
+
const typeChecker = parserServices.program.getTypeChecker();
|
|
2200
|
+
function check(node, typeAnnotation, value) {
|
|
2201
|
+
if (!typeAnnotation || !value) {
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
2205
|
+
const tsValueNode = parserServices.esTreeNodeToTSNodeMap.get(value);
|
|
2206
|
+
const declaredType = typeChecker.getTypeAtLocation(tsNode);
|
|
2207
|
+
const inferredType = typeChecker.getTypeAtLocation(tsValueNode);
|
|
2208
|
+
if (typeChecker.typeToString(declaredType) !==
|
|
2209
|
+
typeChecker.typeToString(inferredType)) {
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
// If the initializer is a call to a generic function with no explicit
|
|
2213
|
+
// type arguments, the type parameters may be inferred from the
|
|
2214
|
+
// contextual return type provided by this annotation. Removing the
|
|
2215
|
+
// annotation could change the inferred type (e.g., T → unknown).
|
|
2216
|
+
if (isCallExpression(tsValueNode) && !tsValueNode.typeArguments?.length) {
|
|
2217
|
+
const sig = typeChecker.getResolvedSignature(tsValueNode);
|
|
2218
|
+
const decl = sig?.declaration;
|
|
2219
|
+
if (decl && 'typeParameters' in decl && decl.typeParameters?.length) {
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
context.report({
|
|
2224
|
+
fix(fixer) {
|
|
2225
|
+
return fixer.remove(typeAnnotation);
|
|
2226
|
+
},
|
|
2227
|
+
messageId: 'redundantTypeAnnotation',
|
|
2228
|
+
node: typeAnnotation,
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2231
|
+
return {
|
|
2232
|
+
PropertyDefinition(node) {
|
|
2233
|
+
check(node, node.typeAnnotation, node.value);
|
|
2234
|
+
},
|
|
2235
|
+
VariableDeclarator(node) {
|
|
2236
|
+
if (node.id.type !== AST_NODE_TYPES.Identifier) {
|
|
2237
|
+
return;
|
|
2238
|
+
}
|
|
2239
|
+
check(node, node.id.typeAnnotation, node.init);
|
|
2240
|
+
},
|
|
2241
|
+
};
|
|
2242
|
+
},
|
|
2243
|
+
meta: {
|
|
2244
|
+
docs: {
|
|
2245
|
+
description: 'Disallow redundant type annotations when the type is already inferred from the initializer',
|
|
2246
|
+
},
|
|
2247
|
+
fixable: 'code',
|
|
2248
|
+
messages: {
|
|
2249
|
+
redundantTypeAnnotation: 'Type annotation is redundant — the type is already inferred from the initializer',
|
|
2250
|
+
},
|
|
2251
|
+
schema: [],
|
|
2252
|
+
type: 'suggestion',
|
|
2253
|
+
},
|
|
2254
|
+
name: 'no-redundant-type-annotation',
|
|
2255
|
+
});
|
|
2256
|
+
|
|
2194
2257
|
const createRule$6 = ESLintUtils.RuleCreator((name) => name);
|
|
2195
2258
|
function isStringLiteral(node) {
|
|
2196
2259
|
return (node.type === AST_NODE_TYPES.Literal &&
|
|
@@ -3552,12 +3615,13 @@ const plugin = {
|
|
|
3552
3615
|
'class-property-naming': classPropertyNaming,
|
|
3553
3616
|
'decorator-key-sort': config$1,
|
|
3554
3617
|
'flat-exports': flatExports,
|
|
3555
|
-
'injection-token-description': rule$
|
|
3556
|
-
'no-deep-imports': rule$
|
|
3618
|
+
'injection-token-description': rule$9,
|
|
3619
|
+
'no-deep-imports': rule$8,
|
|
3557
3620
|
'no-deep-imports-to-indexed-packages': noDeepImportsToIndexedPackages,
|
|
3558
3621
|
'no-href-with-router-link': config,
|
|
3559
|
-
'no-implicit-public': rule$
|
|
3560
|
-
'no-playwright-empty-fill': rule$
|
|
3622
|
+
'no-implicit-public': rule$7,
|
|
3623
|
+
'no-playwright-empty-fill': rule$6,
|
|
3624
|
+
'no-redundant-type-annotation': rule$5,
|
|
3561
3625
|
'no-string-literal-concat': rule$4,
|
|
3562
3626
|
'object-single-line': rule$3,
|
|
3563
3627
|
'prefer-deep-imports': preferDeepImports,
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ export type Options = [
|
|
|
10
10
|
}
|
|
11
11
|
];
|
|
12
12
|
export type MessageIds = 'replaceTuiImport';
|
|
13
|
-
export declare const MESSAGE_ID
|
|
13
|
+
export declare const MESSAGE_ID = "replaceTuiImport";
|
|
14
14
|
export declare const rule: ESLintUtils.RuleModule<"replaceTuiImport", Options, unknown, ESLintUtils.RuleListener> & {
|
|
15
15
|
name: string;
|
|
16
16
|
};
|