@taiga-ui/eslint-plugin-experience-next 0.450.0 → 0.452.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 +88 -0
- package/index.d.ts +8 -5
- package/index.esm.js +221 -59
- package/package.json +1 -1
- package/rules/injection-token-description.d.ts +1 -1
- package/rules/no-deep-imports-to-indexed-packages.d.ts +1 -1
- package/rules/no-deep-imports.d.ts +2 -2
- package/rules/no-implicit-public.d.ts +1 -1
- package/rules/no-string-literal-concat.d.ts +6 -0
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-string-literal-concat | Disallow string literal concatenation; merge adjacent literals into one | ✅ | 🔧 | |
|
|
52
53
|
| object-single-line | Enforce single-line formatting for single-property objects when it fits `printWidth` | ✅ | 🔧 | |
|
|
53
54
|
| prefer-deep-imports | Allow deep imports of Taiga UI packages | | 🔧 | |
|
|
54
55
|
| prefer-multi-arg-push | Combine consecutive `.push()` calls on the same array into a single multi-argument call | ✅ | 🔧 | |
|
|
@@ -283,6 +284,93 @@ await page.getByLabel('Name').clear();
|
|
|
283
284
|
|
|
284
285
|
---
|
|
285
286
|
|
|
287
|
+
## no-string-literal-concat
|
|
288
|
+
|
|
289
|
+
Disallows concatenating string literals with `+`. Adjacent string literals are always mergeable into one — splitting
|
|
290
|
+
them with `+` adds noise without benefit, and multi-line splits are especially easy to miss.
|
|
291
|
+
|
|
292
|
+
Replaces the built-in `no-useless-concat` rule, which only catches same-line concatenation.
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
// ❌ error
|
|
296
|
+
const msg = 'Hello, ' + 'world!';
|
|
297
|
+
|
|
298
|
+
// ✅ after autofix
|
|
299
|
+
const msg = 'Hello, world!';
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
// ❌ error — also caught across lines
|
|
304
|
+
it(
|
|
305
|
+
'returns the last day of month when' +
|
|
306
|
+
' the result month has fewer days',
|
|
307
|
+
() => { ... },
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
// ✅ after autofix
|
|
311
|
+
it('returns the last day of month when the result month has fewer days', () => {
|
|
312
|
+
...
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
// ❌ error — string variables concatenated with +
|
|
318
|
+
const a = 'hello';
|
|
319
|
+
const b = 'world';
|
|
320
|
+
const c = a + b;
|
|
321
|
+
|
|
322
|
+
// ✅ after autofix
|
|
323
|
+
const c = `${a}${b}`;
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
When the concatenation is a **direct expression inside a template literal**, the parts are inlined into the outer
|
|
327
|
+
template instead of producing a nested template literal:
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
// ❌ error
|
|
331
|
+
const url = `${base}${path + query}`;
|
|
332
|
+
|
|
333
|
+
// ✅ after autofix — inlined, no nesting
|
|
334
|
+
const url = `${base}${path}${query}`;
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
// ❌ error — literal concat inside template
|
|
339
|
+
const mask = `${'HH' + ':MM'}`;
|
|
340
|
+
|
|
341
|
+
// ✅ after autofix
|
|
342
|
+
const mask = `HH:MM`;
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
When the concatenation appears **inside a method call or other expression** within a template literal, the rule skips it
|
|
346
|
+
to avoid creating unreadable nested template literals like `` `${`${a}${b}`.method()}` ``.
|
|
347
|
+
|
|
348
|
+
The rule also **flattens already-nested template literals** produced by earlier autofixes or written by hand:
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
// ❌ error
|
|
352
|
+
const s = `${`${dateMode}${dateTimeSeparator}`}HH:MM`;
|
|
353
|
+
|
|
354
|
+
// ✅ after autofix
|
|
355
|
+
const s = `${dateMode}${dateTimeSeparator}HH:MM`;
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Concatenation that uses **inline comments between parts** is intentionally left untouched, as the comments serve as
|
|
359
|
+
documentation:
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
// ✅ not flagged — comments are preserved
|
|
363
|
+
const urlRegex =
|
|
364
|
+
String.raw`^([a-zA-Z]+:\/\/)?` + // protocol
|
|
365
|
+
String.raw`([\w-]+\.)+[\w]{2,}` + // domain
|
|
366
|
+
String.raw`(\/\S*)?$`; // path
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
> For mixed concatenation (`'prefix' + variable`) use the standard `prefer-template` rule, which is already enabled in
|
|
370
|
+
> `recommended`. Template literals (`` `foo` + `bar` ``) and tagged templates are not flagged by this rule.
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
286
374
|
## object-single-line
|
|
287
375
|
|
|
288
376
|
Single-property object literals that fit within `printWidth` characters on one line are collapsed to a single line.
|
package/index.d.ts
CHANGED
|
@@ -18,28 +18,31 @@ declare const plugin: {
|
|
|
18
18
|
'flat-exports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"spreadArrays", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
19
19
|
name: string;
|
|
20
20
|
};
|
|
21
|
-
'injection-token-description': import("@typescript-eslint/utils/ts-eslint").RuleModule<"invalid-injection-token-description", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
21
|
+
'injection-token-description': import("@typescript-eslint/utils/ts-eslint").RuleModule<"invalid-injection-token-description", readonly unknown[], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
22
22
|
name: string;
|
|
23
23
|
};
|
|
24
|
-
'no-deep-imports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"no-deep-imports",
|
|
24
|
+
'no-deep-imports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"no-deep-imports", {
|
|
25
25
|
currentProject: string;
|
|
26
26
|
deepImport: string;
|
|
27
27
|
ignoreImports: string[];
|
|
28
28
|
importDeclaration: string;
|
|
29
29
|
projectName: string;
|
|
30
|
-
}], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
30
|
+
}[], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
31
31
|
name: string;
|
|
32
32
|
};
|
|
33
|
-
'no-deep-imports-to-indexed-packages': import("@typescript-eslint/utils/ts-eslint").RuleModule<"deepImport", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
33
|
+
'no-deep-imports-to-indexed-packages': import("@typescript-eslint/utils/ts-eslint").RuleModule<"deepImport", readonly unknown[], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
34
34
|
name: string;
|
|
35
35
|
};
|
|
36
36
|
'no-href-with-router-link': import("eslint").Rule.RuleModule;
|
|
37
|
-
'no-implicit-public': import("@typescript-eslint/utils/ts-eslint").RuleModule<"implicitPublic", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
37
|
+
'no-implicit-public': import("@typescript-eslint/utils/ts-eslint").RuleModule<"implicitPublic", readonly unknown[], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
38
38
|
name: string;
|
|
39
39
|
};
|
|
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-string-literal-concat': import("@typescript-eslint/utils/ts-eslint").RuleModule<"flattenTemplate" | "mergeLiterals" | "useTemplate", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
44
|
+
name: string;
|
|
45
|
+
};
|
|
43
46
|
'object-single-line': import("@typescript-eslint/utils/ts-eslint").RuleModule<"oneLine", [{
|
|
44
47
|
printWidth: number;
|
|
45
48
|
}], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
package/index.esm.js
CHANGED
|
@@ -87,23 +87,25 @@ var htmlEslint = defineConfig([
|
|
|
87
87
|
String.raw `\[style\.border-top(\.[a-z]+)?\]`,
|
|
88
88
|
String.raw `\[style\.border-bottom(\.[a-z]+)?\]`,
|
|
89
89
|
],
|
|
90
|
-
message:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
90
|
+
message: `
|
|
91
|
+
Use logical CSS properties instead of directional properties. Replace:
|
|
92
|
+
• left → inset-inline-start
|
|
93
|
+
• right → inset-inline-end
|
|
94
|
+
• top → inset-block-start
|
|
95
|
+
• bottom → inset-block-end
|
|
96
|
+
• margin-left → margin-inline-start
|
|
97
|
+
• margin-right → margin-inline-end
|
|
98
|
+
• margin-top → margin-block-start
|
|
99
|
+
• margin-bottom → margin-block-end
|
|
100
|
+
• padding-left → padding-inline-start
|
|
101
|
+
• padding-right → padding-inline-end
|
|
102
|
+
• padding-top → padding-block-start
|
|
103
|
+
• padding-bottom → padding-block-end
|
|
104
|
+
• border-left → border-inline-start
|
|
105
|
+
• border-right → border-inline-end
|
|
106
|
+
• border-top → border-block-start
|
|
107
|
+
• border-bottom → border-block-end
|
|
108
|
+
`,
|
|
107
109
|
tagPatterns: ['.*'],
|
|
108
110
|
},
|
|
109
111
|
],
|
|
@@ -346,6 +348,7 @@ var recommended = defineConfig([
|
|
|
346
348
|
],
|
|
347
349
|
'@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
|
|
348
350
|
'@stylistic/type-annotation-spacing': 'error',
|
|
351
|
+
'@taiga-ui/experience-next/no-string-literal-concat': 'error',
|
|
349
352
|
'@typescript-eslint/adjacent-overload-signatures': 'off',
|
|
350
353
|
'@typescript-eslint/array-type': [
|
|
351
354
|
'error',
|
|
@@ -706,7 +709,6 @@ var recommended = defineConfig([
|
|
|
706
709
|
],
|
|
707
710
|
'no-return-assign': ['error', 'always'],
|
|
708
711
|
'no-unneeded-ternary': 'error',
|
|
709
|
-
'no-useless-concat': 'error',
|
|
710
712
|
'no-useless-escape': 'error',
|
|
711
713
|
'no-useless-rename': [
|
|
712
714
|
'error',
|
|
@@ -777,6 +779,7 @@ var recommended = defineConfig([
|
|
|
777
779
|
'sonarjs/no-identical-functions': 'error',
|
|
778
780
|
'sonarjs/no-inverted-boolean-check': 'error',
|
|
779
781
|
'spaced-comment': ['error', 'always', { markers: ['/'] }],
|
|
782
|
+
'template-curly-spacing': ['error', 'never'],
|
|
780
783
|
'unicorn/consistent-empty-array-spread': 'error',
|
|
781
784
|
'unicorn/escape-case': 'error',
|
|
782
785
|
'unicorn/filename-case': ['error', { case: 'kebabCase' }],
|
|
@@ -907,6 +910,7 @@ var recommended = defineConfig([
|
|
|
907
910
|
'error',
|
|
908
911
|
{ decorators: ['Component', 'Directive', 'NgModule', 'Pipe'] },
|
|
909
912
|
],
|
|
913
|
+
'no-multi-str': 'error',
|
|
910
914
|
'rxjs/no-compat': 'error',
|
|
911
915
|
'rxjs/no-connectable': 'error',
|
|
912
916
|
'rxjs/no-cyclic-action': 'error',
|
|
@@ -1313,8 +1317,8 @@ function intersect(a, b) {
|
|
|
1313
1317
|
return a.some((type) => origin.has(type));
|
|
1314
1318
|
}
|
|
1315
1319
|
|
|
1316
|
-
const createRule$
|
|
1317
|
-
var classPropertyNaming = createRule$
|
|
1320
|
+
const createRule$d = ESLintUtils.RuleCreator((name) => name);
|
|
1321
|
+
var classPropertyNaming = createRule$d({
|
|
1318
1322
|
create(context, [configs]) {
|
|
1319
1323
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
1320
1324
|
const typeChecker = parserServices.program.getTypeChecker();
|
|
@@ -1347,8 +1351,8 @@ var classPropertyNaming = createRule$c({
|
|
|
1347
1351
|
},
|
|
1348
1352
|
};
|
|
1349
1353
|
},
|
|
1350
|
-
defaultOptions: [[]],
|
|
1351
1354
|
meta: {
|
|
1355
|
+
defaultOptions: [[]],
|
|
1352
1356
|
docs: {
|
|
1353
1357
|
description: 'Enforce custom naming for class properties based on their type.',
|
|
1354
1358
|
},
|
|
@@ -1483,9 +1487,9 @@ function isExternalPureTuple(typeChecker, type) {
|
|
|
1483
1487
|
return typeArgs.every((item) => isClassType(item));
|
|
1484
1488
|
}
|
|
1485
1489
|
|
|
1486
|
-
const createRule$
|
|
1490
|
+
const createRule$c = ESLintUtils.RuleCreator((name) => name);
|
|
1487
1491
|
const MESSAGE_ID$5 = 'spreadArrays';
|
|
1488
|
-
var flatExports = createRule$
|
|
1492
|
+
var flatExports = createRule$c({
|
|
1489
1493
|
create(context) {
|
|
1490
1494
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
1491
1495
|
const typeChecker = parserServices.program.getTypeChecker();
|
|
@@ -1595,7 +1599,6 @@ var flatExports = createRule$b({
|
|
|
1595
1599
|
},
|
|
1596
1600
|
};
|
|
1597
1601
|
},
|
|
1598
|
-
defaultOptions: [],
|
|
1599
1602
|
meta: {
|
|
1600
1603
|
docs: {
|
|
1601
1604
|
description: 'Ensure exported const tuples contain spread arrays in pure entity chains.',
|
|
@@ -1612,8 +1615,8 @@ var flatExports = createRule$b({
|
|
|
1612
1615
|
|
|
1613
1616
|
const MESSAGE_ID$4 = 'invalid-injection-token-description';
|
|
1614
1617
|
const ERROR_MESSAGE$3 = "InjectionToken's description should contain token's name";
|
|
1615
|
-
const createRule$
|
|
1616
|
-
const rule$
|
|
1618
|
+
const createRule$b = ESLintUtils.RuleCreator((name) => name);
|
|
1619
|
+
const rule$8 = createRule$b({
|
|
1617
1620
|
create(context) {
|
|
1618
1621
|
return {
|
|
1619
1622
|
'NewExpression[callee.name="InjectionToken"]'(node) {
|
|
@@ -1651,7 +1654,6 @@ const rule$7 = createRule$a({
|
|
|
1651
1654
|
},
|
|
1652
1655
|
};
|
|
1653
1656
|
},
|
|
1654
|
-
defaultOptions: [],
|
|
1655
1657
|
meta: {
|
|
1656
1658
|
docs: { description: ERROR_MESSAGE$3 },
|
|
1657
1659
|
fixable: 'code',
|
|
@@ -1681,8 +1683,8 @@ const DEFAULT_OPTIONS = {
|
|
|
1681
1683
|
importDeclaration: '^@taiga-ui*',
|
|
1682
1684
|
projectName: String.raw `(?<=^@taiga-ui/)([-\w]+)`,
|
|
1683
1685
|
};
|
|
1684
|
-
const createRule$
|
|
1685
|
-
const rule$
|
|
1686
|
+
const createRule$a = ESLintUtils.RuleCreator((name) => name);
|
|
1687
|
+
const rule$7 = createRule$a({
|
|
1686
1688
|
create(context) {
|
|
1687
1689
|
const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
|
|
1688
1690
|
const hasNonCodeExtension = (source) => {
|
|
@@ -1730,8 +1732,8 @@ const rule$6 = createRule$9({
|
|
|
1730
1732
|
},
|
|
1731
1733
|
};
|
|
1732
1734
|
},
|
|
1733
|
-
defaultOptions: [DEFAULT_OPTIONS],
|
|
1734
1735
|
meta: {
|
|
1736
|
+
defaultOptions: [DEFAULT_OPTIONS],
|
|
1735
1737
|
docs: { description: ERROR_MESSAGE$2 },
|
|
1736
1738
|
fixable: 'code',
|
|
1737
1739
|
messages: { [MESSAGE_ID$3]: ERROR_MESSAGE$2 },
|
|
@@ -1769,13 +1771,13 @@ const rule$6 = createRule$9({
|
|
|
1769
1771
|
name: 'no-deep-imports',
|
|
1770
1772
|
});
|
|
1771
1773
|
|
|
1772
|
-
const createRule$
|
|
1774
|
+
const createRule$9 = ESLintUtils.RuleCreator((name) => name);
|
|
1773
1775
|
const resolveCacheByOptions = new WeakMap();
|
|
1774
1776
|
const nearestFileUpCache = new Map();
|
|
1775
1777
|
const markerCache = new Map();
|
|
1776
1778
|
const indexFileCache = new Map();
|
|
1777
1779
|
const indexExportsCache = new Map();
|
|
1778
|
-
var noDeepImportsToIndexedPackages = createRule$
|
|
1780
|
+
var noDeepImportsToIndexedPackages = createRule$9({
|
|
1779
1781
|
create(context) {
|
|
1780
1782
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
1781
1783
|
const program = parserServices.program;
|
|
@@ -1923,7 +1925,6 @@ var noDeepImportsToIndexedPackages = createRule$8({
|
|
|
1923
1925
|
},
|
|
1924
1926
|
};
|
|
1925
1927
|
},
|
|
1926
|
-
defaultOptions: [],
|
|
1927
1928
|
meta: {
|
|
1928
1929
|
docs: {
|
|
1929
1930
|
description: 'Disallow deep imports only when package root index.ts (or index.d.ts) re-exports that subpath, and the package is marked by ng-package.json or package.json',
|
|
@@ -2018,8 +2019,8 @@ const config = {
|
|
|
2018
2019
|
},
|
|
2019
2020
|
};
|
|
2020
2021
|
|
|
2021
|
-
const createRule$
|
|
2022
|
-
const rule$
|
|
2022
|
+
const createRule$8 = ESLintUtils.RuleCreator((name) => name);
|
|
2023
|
+
const rule$6 = createRule$8({
|
|
2023
2024
|
create(context) {
|
|
2024
2025
|
const checkImplicitPublic = (node) => {
|
|
2025
2026
|
const classRef = getClass(node);
|
|
@@ -2069,7 +2070,6 @@ const rule$5 = createRule$7({
|
|
|
2069
2070
|
},
|
|
2070
2071
|
};
|
|
2071
2072
|
},
|
|
2072
|
-
defaultOptions: [],
|
|
2073
2073
|
meta: {
|
|
2074
2074
|
docs: {
|
|
2075
2075
|
description: 'Require explicit `public` modifier for class members and parameter properties',
|
|
@@ -2091,8 +2091,8 @@ function getClass(node) {
|
|
|
2091
2091
|
return getClass(node.parent);
|
|
2092
2092
|
}
|
|
2093
2093
|
|
|
2094
|
-
const createRule$
|
|
2095
|
-
const rule$
|
|
2094
|
+
const createRule$7 = ESLintUtils.RuleCreator((name) => name);
|
|
2095
|
+
const rule$5 = createRule$7({
|
|
2096
2096
|
create(context) {
|
|
2097
2097
|
const services = ESLintUtils.getParserServices(context);
|
|
2098
2098
|
const checker = services.program.getTypeChecker();
|
|
@@ -2129,7 +2129,6 @@ const rule$4 = createRule$6({
|
|
|
2129
2129
|
},
|
|
2130
2130
|
};
|
|
2131
2131
|
},
|
|
2132
|
-
defaultOptions: [],
|
|
2133
2132
|
meta: {
|
|
2134
2133
|
docs: {
|
|
2135
2134
|
description: "Enforce Playwright clear() instead of fill('') for emptying fields",
|
|
@@ -2180,6 +2179,170 @@ function isPlaywrightLocatorType(type) {
|
|
|
2180
2179
|
});
|
|
2181
2180
|
}
|
|
2182
2181
|
|
|
2182
|
+
const createRule$6 = ESLintUtils.RuleCreator((name) => name);
|
|
2183
|
+
function isStringLiteral(node) {
|
|
2184
|
+
return (node.type === AST_NODE_TYPES.Literal &&
|
|
2185
|
+
typeof node.value === 'string');
|
|
2186
|
+
}
|
|
2187
|
+
function collectParts(node) {
|
|
2188
|
+
if (node.type === AST_NODE_TYPES.BinaryExpression && node.operator === '+') {
|
|
2189
|
+
return [...collectParts(node.left), ...collectParts(node.right)];
|
|
2190
|
+
}
|
|
2191
|
+
return [node];
|
|
2192
|
+
}
|
|
2193
|
+
function isRootConcat(node) {
|
|
2194
|
+
const { parent } = node;
|
|
2195
|
+
return (parent.type !== AST_NODE_TYPES.BinaryExpression ||
|
|
2196
|
+
parent.operator !== '+');
|
|
2197
|
+
}
|
|
2198
|
+
function isStringType(type, checker) {
|
|
2199
|
+
if (type.isUnion()) {
|
|
2200
|
+
return type.types.every((t) => isStringType(t, checker));
|
|
2201
|
+
}
|
|
2202
|
+
return type.isStringLiteral() || checker.typeToString(type) === 'string';
|
|
2203
|
+
}
|
|
2204
|
+
function buildMergedString(parts) {
|
|
2205
|
+
const combined = parts.map((p) => p.value).join('');
|
|
2206
|
+
const quote = combined.includes("'") && !combined.includes('"') ? '"' : "'";
|
|
2207
|
+
const escaped = combined
|
|
2208
|
+
.replaceAll('\\', '\\\\')
|
|
2209
|
+
.replaceAll('\r', String.raw `\r`)
|
|
2210
|
+
.replaceAll('\n', String.raw `\n`)
|
|
2211
|
+
.replaceAll('\t', String.raw `\t`)
|
|
2212
|
+
.replaceAll(new RegExp(quote, 'g'), `\\${quote}`);
|
|
2213
|
+
return `${quote}${escaped}${quote}`;
|
|
2214
|
+
}
|
|
2215
|
+
function escapeForTemplateLiteral(value) {
|
|
2216
|
+
return value.replaceAll('\\', '\\\\').replaceAll('`', '\\`').replaceAll('${', '\\${');
|
|
2217
|
+
}
|
|
2218
|
+
function partsToTemplateContent(parts, getText) {
|
|
2219
|
+
return parts
|
|
2220
|
+
.map((part) => isStringLiteral(part)
|
|
2221
|
+
? escapeForTemplateLiteral(part.value)
|
|
2222
|
+
: `\${${getText(part)}}`)
|
|
2223
|
+
.join('');
|
|
2224
|
+
}
|
|
2225
|
+
/**
|
|
2226
|
+
* Returns the raw content between the backticks of a TemplateLiteral,
|
|
2227
|
+
* delegating each expression slot to `renderExpr`.
|
|
2228
|
+
*/
|
|
2229
|
+
function templateContent(template, renderExpr) {
|
|
2230
|
+
return template.quasis
|
|
2231
|
+
.map((quasi, i) => `${quasi.value.raw}${i < template.expressions.length
|
|
2232
|
+
? renderExpr(template.expressions[i], i)
|
|
2233
|
+
: ''}`)
|
|
2234
|
+
.join('');
|
|
2235
|
+
}
|
|
2236
|
+
function hasTemplateLiteralAncestor(node) {
|
|
2237
|
+
let current = node.parent;
|
|
2238
|
+
while (current != null) {
|
|
2239
|
+
if (current.type === AST_NODE_TYPES.TemplateLiteral) {
|
|
2240
|
+
return true;
|
|
2241
|
+
}
|
|
2242
|
+
current = current.parent;
|
|
2243
|
+
}
|
|
2244
|
+
return false;
|
|
2245
|
+
}
|
|
2246
|
+
const rule$4 = createRule$6({
|
|
2247
|
+
create(context) {
|
|
2248
|
+
const { sourceCode } = context;
|
|
2249
|
+
let parserServices = null;
|
|
2250
|
+
let checker = null;
|
|
2251
|
+
try {
|
|
2252
|
+
parserServices = ESLintUtils.getParserServices(context);
|
|
2253
|
+
checker = parserServices.program.getTypeChecker();
|
|
2254
|
+
}
|
|
2255
|
+
catch {
|
|
2256
|
+
// Type checking not available — only literal concatenation will be checked
|
|
2257
|
+
}
|
|
2258
|
+
function isStringNode(node) {
|
|
2259
|
+
if (isStringLiteral(node)) {
|
|
2260
|
+
return true;
|
|
2261
|
+
}
|
|
2262
|
+
if (!parserServices || !checker) {
|
|
2263
|
+
return false;
|
|
2264
|
+
}
|
|
2265
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
2266
|
+
return isStringType(checker.getTypeAtLocation(tsNode), checker);
|
|
2267
|
+
}
|
|
2268
|
+
const getText = (n) => sourceCode.getText(n);
|
|
2269
|
+
const wrapExpr = (expr) => `\${${getText(expr)}}`;
|
|
2270
|
+
return {
|
|
2271
|
+
BinaryExpression(node) {
|
|
2272
|
+
if (node.operator !== '+' || !isRootConcat(node)) {
|
|
2273
|
+
return;
|
|
2274
|
+
}
|
|
2275
|
+
// Comments between parts serve as inline documentation — preserve them
|
|
2276
|
+
if (sourceCode.getCommentsInside(node).length > 0) {
|
|
2277
|
+
return;
|
|
2278
|
+
}
|
|
2279
|
+
const parts = collectParts(node);
|
|
2280
|
+
if (!parts.every((p) => p.type !== AST_NODE_TYPES.TemplateLiteral && isStringNode(p))) {
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
const allLiterals = parts.every(isStringLiteral);
|
|
2284
|
+
// Direct child of a template expression → inline parts to avoid
|
|
2285
|
+
// nested template literals like `${`${a}${b}`}`
|
|
2286
|
+
if (node.parent.type === AST_NODE_TYPES.TemplateLiteral) {
|
|
2287
|
+
const template = node.parent;
|
|
2288
|
+
// Tagged templates: changing quasis/expressions count alters behaviour
|
|
2289
|
+
if (template.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) {
|
|
2290
|
+
return;
|
|
2291
|
+
}
|
|
2292
|
+
context.report({
|
|
2293
|
+
fix: (fixer) => fixer.replaceText(template, `\`${templateContent(template, (expr) => (expr === node ? partsToTemplateContent(parts, getText) : wrapExpr(expr)))}\``),
|
|
2294
|
+
messageId: allLiterals ? 'mergeLiterals' : 'useTemplate',
|
|
2295
|
+
node,
|
|
2296
|
+
});
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
// Nested inside a template but not direct child — would produce
|
|
2300
|
+
// `${`${a}${b}`.method()}`, so skip
|
|
2301
|
+
if (hasTemplateLiteralAncestor(node)) {
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
context.report({
|
|
2305
|
+
fix: (fixer) => fixer.replaceText(node, allLiterals
|
|
2306
|
+
? buildMergedString(parts)
|
|
2307
|
+
: `\`${partsToTemplateContent(parts, getText)}\``),
|
|
2308
|
+
messageId: allLiterals ? 'mergeLiterals' : 'useTemplate',
|
|
2309
|
+
node,
|
|
2310
|
+
});
|
|
2311
|
+
},
|
|
2312
|
+
TemplateLiteral(node) {
|
|
2313
|
+
// Tagged templates: changing quasis/expressions count alters behaviour
|
|
2314
|
+
if (node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) {
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
node.expressions.forEach((expr, i) => {
|
|
2318
|
+
if (expr.type === AST_NODE_TYPES.TemplateLiteral &&
|
|
2319
|
+
expr.parent.type !== AST_NODE_TYPES.TaggedTemplateExpression) {
|
|
2320
|
+
context.report({
|
|
2321
|
+
fix: (fixer) => fixer.replaceText(node, `\`${templateContent(node, (e, j) => (j === i ? templateContent(expr, wrapExpr) : wrapExpr(e)))}\``),
|
|
2322
|
+
messageId: 'flattenTemplate',
|
|
2323
|
+
node: expr,
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
});
|
|
2327
|
+
},
|
|
2328
|
+
};
|
|
2329
|
+
},
|
|
2330
|
+
meta: {
|
|
2331
|
+
docs: {
|
|
2332
|
+
description: 'Disallow string concatenation. Merge adjacent string literals into one; use template literals for string variables.',
|
|
2333
|
+
},
|
|
2334
|
+
fixable: 'code',
|
|
2335
|
+
messages: {
|
|
2336
|
+
flattenTemplate: 'Flatten nested template literal into its parent.',
|
|
2337
|
+
mergeLiterals: 'Merge string literals instead of concatenating them.',
|
|
2338
|
+
useTemplate: 'Use a template literal instead of string concatenation.',
|
|
2339
|
+
},
|
|
2340
|
+
schema: [],
|
|
2341
|
+
type: 'suggestion',
|
|
2342
|
+
},
|
|
2343
|
+
name: 'no-string-literal-concat',
|
|
2344
|
+
});
|
|
2345
|
+
|
|
2183
2346
|
const createRule$5 = ESLintUtils.RuleCreator((name) => name);
|
|
2184
2347
|
const rule$3 = createRule$5({
|
|
2185
2348
|
create(context, [{ printWidth }]) {
|
|
@@ -2440,8 +2603,8 @@ const rule$3 = createRule$5({
|
|
|
2440
2603
|
},
|
|
2441
2604
|
};
|
|
2442
2605
|
},
|
|
2443
|
-
defaultOptions: [{ printWidth: 90 }],
|
|
2444
2606
|
meta: {
|
|
2607
|
+
defaultOptions: [{ printWidth: 90 }],
|
|
2445
2608
|
docs: {
|
|
2446
2609
|
description: 'Enforce single-line formatting for single-property objects when possible (Prettier-friendly)',
|
|
2447
2610
|
},
|
|
@@ -2526,13 +2689,13 @@ var preferDeepImports = createRule$4({
|
|
|
2526
2689
|
},
|
|
2527
2690
|
};
|
|
2528
2691
|
},
|
|
2529
|
-
defaultOptions: [
|
|
2530
|
-
{
|
|
2531
|
-
importFilter: [],
|
|
2532
|
-
strict: false,
|
|
2533
|
-
},
|
|
2534
|
-
],
|
|
2535
2692
|
meta: {
|
|
2693
|
+
defaultOptions: [
|
|
2694
|
+
{
|
|
2695
|
+
importFilter: [],
|
|
2696
|
+
strict: false,
|
|
2697
|
+
},
|
|
2698
|
+
],
|
|
2536
2699
|
docs: { description: ERROR_MESSAGE },
|
|
2537
2700
|
fixable: 'code',
|
|
2538
2701
|
messages: { [MESSAGE_ID$1]: ERROR_MESSAGE },
|
|
@@ -2889,7 +3052,6 @@ const rule$2 = createRule$3({
|
|
|
2889
3052
|
},
|
|
2890
3053
|
};
|
|
2891
3054
|
},
|
|
2892
|
-
defaultOptions: [],
|
|
2893
3055
|
meta: {
|
|
2894
3056
|
docs: {
|
|
2895
3057
|
description: 'Enforce combining consecutive .push() calls on the same array into a single call.',
|
|
@@ -3041,13 +3203,13 @@ const rule$1 = createRule$2({
|
|
|
3041
3203
|
},
|
|
3042
3204
|
};
|
|
3043
3205
|
},
|
|
3044
|
-
defaultOptions: [
|
|
3045
|
-
{
|
|
3046
|
-
decorators: DEFAULT_DECORATORS,
|
|
3047
|
-
exceptions: DEFAULT_EXCEPTIONS,
|
|
3048
|
-
},
|
|
3049
|
-
],
|
|
3050
3206
|
meta: {
|
|
3207
|
+
defaultOptions: [
|
|
3208
|
+
{
|
|
3209
|
+
decorators: DEFAULT_DECORATORS,
|
|
3210
|
+
exceptions: DEFAULT_EXCEPTIONS,
|
|
3211
|
+
},
|
|
3212
|
+
],
|
|
3051
3213
|
docs: {
|
|
3052
3214
|
description: 'Shorten TuiXxxComponent / TuiYyyDirective in Angular metadata (supports configurable decorators and exceptions).',
|
|
3053
3215
|
},
|
|
@@ -3244,8 +3406,8 @@ var standaloneImportsSort = createRule$1({
|
|
|
3244
3406
|
},
|
|
3245
3407
|
};
|
|
3246
3408
|
},
|
|
3247
|
-
defaultOptions: [{ decorators: ['Component', 'Directive', 'NgModule', 'Pipe'] }],
|
|
3248
3409
|
meta: {
|
|
3410
|
+
defaultOptions: [{ decorators: ['Component', 'Directive', 'NgModule', 'Pipe'] }],
|
|
3249
3411
|
docs: { description: 'Sort Angular standalone imports inside decorators.' },
|
|
3250
3412
|
fixable: 'code',
|
|
3251
3413
|
messages: { incorrectOrder: 'Order in imports should be [{{expected}}]' },
|
|
@@ -3351,7 +3513,6 @@ const rule = createRule({
|
|
|
3351
3513
|
},
|
|
3352
3514
|
};
|
|
3353
3515
|
},
|
|
3354
|
-
defaultOptions: [],
|
|
3355
3516
|
meta: {
|
|
3356
3517
|
docs: {
|
|
3357
3518
|
description: `Ensures that keys and values are valid in a ${DOC_EXAMPLE_INTERFACE_NAME} interface.`,
|
|
@@ -3379,12 +3540,13 @@ const plugin = {
|
|
|
3379
3540
|
'class-property-naming': classPropertyNaming,
|
|
3380
3541
|
'decorator-key-sort': config$1,
|
|
3381
3542
|
'flat-exports': flatExports,
|
|
3382
|
-
'injection-token-description': rule$
|
|
3383
|
-
'no-deep-imports': rule$
|
|
3543
|
+
'injection-token-description': rule$8,
|
|
3544
|
+
'no-deep-imports': rule$7,
|
|
3384
3545
|
'no-deep-imports-to-indexed-packages': noDeepImportsToIndexedPackages,
|
|
3385
3546
|
'no-href-with-router-link': config,
|
|
3386
|
-
'no-implicit-public': rule$
|
|
3387
|
-
'no-playwright-empty-fill': rule$
|
|
3547
|
+
'no-implicit-public': rule$6,
|
|
3548
|
+
'no-playwright-empty-fill': rule$5,
|
|
3549
|
+
'no-string-literal-concat': rule$4,
|
|
3388
3550
|
'object-single-line': rule$3,
|
|
3389
3551
|
'prefer-deep-imports': preferDeepImports,
|
|
3390
3552
|
'prefer-multi-arg-push': rule$2,
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
export declare const rule: ESLintUtils.RuleModule<"invalid-injection-token-description", [], unknown, ESLintUtils.RuleListener> & {
|
|
2
|
+
export declare const rule: ESLintUtils.RuleModule<"invalid-injection-token-description", readonly unknown[], unknown, ESLintUtils.RuleListener> & {
|
|
3
3
|
name: string;
|
|
4
4
|
};
|
|
5
5
|
export default rule;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
declare const _default: ESLintUtils.RuleModule<"deepImport", [], unknown, ESLintUtils.RuleListener> & {
|
|
2
|
+
declare const _default: ESLintUtils.RuleModule<"deepImport", readonly unknown[], unknown, ESLintUtils.RuleListener> & {
|
|
3
3
|
name: string;
|
|
4
4
|
};
|
|
5
5
|
export default _default;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
export declare const rule: ESLintUtils.RuleModule<"no-deep-imports",
|
|
2
|
+
export declare const rule: ESLintUtils.RuleModule<"no-deep-imports", {
|
|
3
3
|
currentProject: string;
|
|
4
4
|
deepImport: string;
|
|
5
5
|
ignoreImports: string[];
|
|
6
6
|
importDeclaration: string;
|
|
7
7
|
projectName: string;
|
|
8
|
-
}], unknown, ESLintUtils.RuleListener> & {
|
|
8
|
+
}[], unknown, ESLintUtils.RuleListener> & {
|
|
9
9
|
name: string;
|
|
10
10
|
};
|
|
11
11
|
export default rule;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
export declare const rule: ESLintUtils.RuleModule<"implicitPublic", [], unknown, ESLintUtils.RuleListener> & {
|
|
2
|
+
export declare const rule: ESLintUtils.RuleModule<"implicitPublic", readonly unknown[], unknown, ESLintUtils.RuleListener> & {
|
|
3
3
|
name: string;
|
|
4
4
|
};
|
|
5
5
|
export default rule;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
type MessageId = 'flattenTemplate' | 'mergeLiterals' | 'useTemplate';
|
|
3
|
+
export declare const rule: ESLintUtils.RuleModule<MessageId, [], unknown, ESLintUtils.RuleListener> & {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
export default rule;
|