@taiga-ui/eslint-plugin-experience-next 0.475.0 → 0.477.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 CHANGED
@@ -45,10 +45,12 @@ export default [
45
45
  | flat-exports | Spread nested arrays when exporting Angular entity collections | | 🔧 | |
46
46
  | host-attributes-sort | Sort Angular host metadata attributes using configurable attribute groups | ✅ | 🔧 | |
47
47
  | injection-token-description | Require `InjectionToken` descriptions to include the token name | ✅ | 🔧 | |
48
+ | no-commonjs-import-patterns | Disallow legacy CommonJS interop import patterns | ✅ | | |
48
49
  | no-deep-imports | Disables deep imports of Taiga UI packages | ✅ | 🔧 | |
49
50
  | no-deep-imports-to-indexed-packages | Disallow deep imports from packages that expose an index.ts next to ng-package.json or package.json | ✅ | 🔧 | |
50
51
  | no-fully-untracked-effect | Disallow reactive callbacks where all signal reads are hidden inside `untracked()` | ✅ | | |
51
52
  | no-href-with-router-link | Do not use href and routerLink attributes together on the same element | ✅ | 🔧 | |
53
+ | no-import-assertions | Replace legacy `assert { ... }` import assertions with `with { ... }` | ✅ | 🔧 | |
52
54
  | no-implicit-public | Require explicit `public` modifier for class members and parameter properties | ✅ | 🔧 | |
53
55
  | no-infinite-loop | Disallow `while (true)` and `for` loops without an explicit condition | ✅ | | |
54
56
  | no-legacy-peer-deps | Disallow `legacy-peer-deps=true` in `.npmrc` | ✅ | | |
@@ -64,6 +66,7 @@ export default [
64
66
  | prefer-combined-if-control-flow | Combine consecutive `if` statements that use the same `return`, `break`, `continue`, or `throw` | ✅ | 🔧 | |
65
67
  | prefer-deep-imports | Allow deep imports of Taiga UI packages | | 🔧 | |
66
68
  | prefer-multi-arg-push | Combine consecutive `.push()` calls on the same array into a single multi-argument call | ✅ | 🔧 | |
69
+ | prefer-namespace-keyword | Replace `module Foo {}` with `namespace Foo {}` for TypeScript namespace declarations | ✅ | 🔧 | |
67
70
  | prefer-untracked-incidental-signal-reads | Wrap likely-incidental signal reads with `untracked()` in reactive callbacks | ✅ | 🔧 | |
68
71
  | prefer-untracked-signal-getter | Prefer `untracked(signalGetter)` over `untracked(() => signalGetter())` for a single signal getter | ✅ | 🔧 | |
69
72
  | short-tui-imports | Shorten TuiXxxComponent / TuiYyyDirective in Angular metadata | ✅ | 🔧 | |
@@ -307,6 +310,29 @@ export const TUI_MY_TOKEN = new InjectionToken<string>(ngDevMode ? '[TUI_MY_TOKE
307
310
 
308
311
  ---
309
312
 
313
+ ## no-commonjs-import-patterns
314
+
315
+ <sup>`✅ Recommended`</sup>
316
+
317
+ Disallows legacy CommonJS interop import patterns that are brittle under modern ESM-oriented toolchains. It reports
318
+ `import foo = require('foo')` and namespace imports that are used as callable values, constructors, or tag functions.
319
+
320
+ ```ts
321
+ // ❌ error
322
+ import toolkit = require('@taiga-ui/cdk');
323
+
324
+ import * as createClient from 'legacy-client';
325
+ createClient();
326
+
327
+ // ✅ ok
328
+ import toolkit from '@taiga-ui/cdk';
329
+
330
+ import createClient from 'legacy-client';
331
+ createClient();
332
+ ```
333
+
334
+ ---
335
+
310
336
  ## no-deep-imports
311
337
 
312
338
  <sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
@@ -419,6 +445,22 @@ attribute.
419
445
 
420
446
  ---
421
447
 
448
+ ## no-import-assertions
449
+
450
+ <sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
451
+
452
+ Disallows legacy `assert { ... }` import assertions and rewrites them to `with { ... }` import attributes.
453
+
454
+ ```ts
455
+ // ❌ error
456
+ import data from './file.json' assert {type: 'json'};
457
+
458
+ // ✅ after autofix
459
+ import data from './file.json' with {type: 'json'};
460
+ ```
461
+
462
+ ---
463
+
422
464
  ## no-implicit-public
423
465
 
424
466
  <sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
@@ -1043,6 +1085,27 @@ output.push('# Getting Started', '');
1043
1085
 
1044
1086
  ---
1045
1087
 
1088
+ ## prefer-namespace-keyword
1089
+
1090
+ <sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
1091
+
1092
+ Prefers `namespace Foo {}` over the older `module Foo {}` syntax for TypeScript namespace declarations. External module
1093
+ augmentations such as `declare module 'pkg' {}` are ignored.
1094
+
1095
+ ```ts
1096
+ // ❌ error
1097
+ module Foo.Bar {
1098
+ export type Value = string;
1099
+ }
1100
+
1101
+ // ✅ after autofix
1102
+ namespace Foo.Bar {
1103
+ export type Value = string;
1104
+ }
1105
+ ```
1106
+
1107
+ ---
1108
+
1046
1109
  ## prefer-untracked-incidental-signal-reads
1047
1110
 
1048
1111
  <sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
package/index.d.ts CHANGED
@@ -30,6 +30,9 @@ declare const plugin: {
30
30
  'injection-token-description': import("@typescript-eslint/utils/ts-eslint").RuleModule<"invalid-injection-token-description", readonly unknown[], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
31
31
  name: string;
32
32
  };
33
+ 'no-commonjs-import-patterns': import("@typescript-eslint/utils/ts-eslint").RuleModule<"avoidCallableNamespaceImport" | "avoidImportEquals", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
34
+ name: string;
35
+ };
33
36
  'no-deep-imports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"no-deep-imports", {
34
37
  currentProject: string;
35
38
  deepImport: string;
@@ -49,7 +52,10 @@ declare const plugin: {
49
52
  'no-implicit-public': import("@typescript-eslint/utils/ts-eslint").RuleModule<"implicitPublic", readonly unknown[], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
50
53
  name: string;
51
54
  };
52
- 'no-infinite-loop': import("@typescript-eslint/utils/ts-eslint").RuleModule<"forLoop" | "whileLoop", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
55
+ 'no-import-assertions': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useWithImportAttributes", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
56
+ name: string;
57
+ };
58
+ 'no-infinite-loop': import("@typescript-eslint/utils/ts-eslint").RuleModule<"doWhileLoop" | "forLoop" | "whileLoop", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
53
59
  name: string;
54
60
  };
55
61
  'no-legacy-peer-deps': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noLegacyPeerDeps", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
@@ -96,6 +102,9 @@ declare const plugin: {
96
102
  'prefer-multi-arg-push': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferMultiArgPush", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
97
103
  name: string;
98
104
  };
105
+ 'prefer-namespace-keyword': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useNamespaceKeyword", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
106
+ name: string;
107
+ };
99
108
  'prefer-untracked-incidental-signal-reads': import("@typescript-eslint/utils/ts-eslint").RuleModule<"incidentalRead", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
100
109
  name: string;
101
110
  };
package/index.esm.js CHANGED
@@ -898,6 +898,7 @@ var recommended = defineConfig([
898
898
  ],
899
899
  '@taiga-ui/experience-next/host-attributes-sort': 'error',
900
900
  '@taiga-ui/experience-next/injection-token-description': 'error',
901
+ '@taiga-ui/experience-next/no-commonjs-import-patterns': 'error',
901
902
  '@taiga-ui/experience-next/no-deep-imports': [
902
903
  'error',
903
904
  {
@@ -908,6 +909,7 @@ var recommended = defineConfig([
908
909
  '@taiga-ui/experience-next/no-deep-imports-to-indexed-packages': 'error',
909
910
  '@taiga-ui/experience-next/no-fully-untracked-effect': 'error',
910
911
  '@taiga-ui/experience-next/no-implicit-public': 'error',
912
+ '@taiga-ui/experience-next/no-import-assertions': 'error',
911
913
  '@taiga-ui/experience-next/no-infinite-loop': 'error',
912
914
  '@taiga-ui/experience-next/no-redundant-type-annotation': 'error',
913
915
  '@taiga-ui/experience-next/no-side-effects-in-computed': 'error',
@@ -917,6 +919,7 @@ var recommended = defineConfig([
917
919
  '@taiga-ui/experience-next/object-single-line': ['error', { printWidth: 90 }],
918
920
  '@taiga-ui/experience-next/prefer-combined-if-control-flow': 'error',
919
921
  '@taiga-ui/experience-next/prefer-multi-arg-push': 'error',
922
+ '@taiga-ui/experience-next/prefer-namespace-keyword': 'error',
920
923
  '@taiga-ui/experience-next/prefer-untracked-incidental-signal-reads': 'error',
921
924
  '@taiga-ui/experience-next/prefer-untracked-signal-getter': 'error',
922
925
  '@taiga-ui/experience-next/short-tui-imports': 'error',
@@ -1351,8 +1354,8 @@ function getTypeAwareRuleContext(context) {
1351
1354
  };
1352
1355
  }
1353
1356
 
1354
- const createRule$j = ESLintUtils.RuleCreator((name) => name);
1355
- var classPropertyNaming = createRule$j({
1357
+ const createRule$m = ESLintUtils.RuleCreator((name) => name);
1358
+ var classPropertyNaming = createRule$m({
1356
1359
  create(context, [configs]) {
1357
1360
  const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
1358
1361
  const flatConfig = configs.flat();
@@ -1520,9 +1523,9 @@ function isExternalPureTuple(typeChecker, type) {
1520
1523
  return typeArgs.every((item) => isClassType(item));
1521
1524
  }
1522
1525
 
1523
- const createRule$i = ESLintUtils.RuleCreator((name) => name);
1526
+ const createRule$l = ESLintUtils.RuleCreator((name) => name);
1524
1527
  const MESSAGE_ID$7 = 'spreadArrays';
1525
- var flatExports = createRule$i({
1528
+ var flatExports = createRule$l({
1526
1529
  create(context) {
1527
1530
  const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
1528
1531
  const arrays = new Map();
@@ -1802,8 +1805,8 @@ const PRESETS = {
1802
1805
  $VUE: ['$CLASS', '$ID', '$VUE_ATTRIBUTE'],
1803
1806
  $VUE_ATTRIBUTE: /^v-/,
1804
1807
  };
1805
- const createRule$h = ESLintUtils.RuleCreator((name) => name);
1806
- const rule$k = createRule$h({
1808
+ const createRule$k = ESLintUtils.RuleCreator((name) => name);
1809
+ const rule$n = createRule$k({
1807
1810
  create(context, [options]) {
1808
1811
  const sourceCode = context.sourceCode;
1809
1812
  const settings = {
@@ -2064,7 +2067,7 @@ const config$2 = {
2064
2067
  const MESSAGE_ID$5 = 'invalid-injection-token-description';
2065
2068
  const ERROR_MESSAGE$3 = "InjectionToken's description should contain token's name";
2066
2069
  const NG_DEV_MODE = 'ngDevMode';
2067
- const createRule$g = ESLintUtils.RuleCreator((name) => name);
2070
+ const createRule$j = ESLintUtils.RuleCreator((name) => name);
2068
2071
  function getVariableName(node) {
2069
2072
  if (node.parent.type !== AST_NODE_TYPES$1.VariableDeclarator) {
2070
2073
  return undefined;
@@ -2132,7 +2135,7 @@ function getNgDevModeDeclarationFix(program, fixer) {
2132
2135
  }
2133
2136
  return fixer.insertTextBeforeRange([0, 0], 'declare const ngDevMode: boolean;\n');
2134
2137
  }
2135
- const rule$j = createRule$g({
2138
+ const rule$m = createRule$j({
2136
2139
  create(context) {
2137
2140
  const { sourceCode } = context;
2138
2141
  const program = sourceCode.ast;
@@ -2181,6 +2184,88 @@ const rule$j = createRule$g({
2181
2184
  name: 'injection-token-description',
2182
2185
  });
2183
2186
 
2187
+ const createRule$i = ESLintUtils.RuleCreator((name) => name);
2188
+ function getResolvedVariable(sourceCode, node) {
2189
+ const scope = sourceCode.getScope(node);
2190
+ const reference = scope.references.find((item) => item.identifier === node);
2191
+ return reference?.resolved ?? null;
2192
+ }
2193
+ const rule$l = createRule$i({
2194
+ create(context) {
2195
+ const { sourceCode } = context;
2196
+ const namespaceImports = new Map();
2197
+ const markNamespaceImportAsUsedLikeValue = (identifier) => {
2198
+ const usage = namespaceImports.get(identifier.name);
2199
+ if (!usage ||
2200
+ usage.usedLikeValue ||
2201
+ getResolvedVariable(sourceCode, identifier) !== usage.variable) {
2202
+ return;
2203
+ }
2204
+ usage.usedLikeValue = true;
2205
+ };
2206
+ return {
2207
+ 'CallExpression > Identifier.callee'(node) {
2208
+ markNamespaceImportAsUsedLikeValue(node);
2209
+ },
2210
+ ImportDeclaration(node) {
2211
+ const namespaceImport = node.specifiers.find((specifier) => specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier);
2212
+ if (!namespaceImport) {
2213
+ return;
2214
+ }
2215
+ const [variable] = sourceCode.getDeclaredVariables(namespaceImport);
2216
+ if (!variable) {
2217
+ return;
2218
+ }
2219
+ namespaceImports.set(namespaceImport.local.name, {
2220
+ node: namespaceImport,
2221
+ usedLikeValue: false,
2222
+ variable,
2223
+ });
2224
+ },
2225
+ 'NewExpression > Identifier.callee'(node) {
2226
+ markNamespaceImportAsUsedLikeValue(node);
2227
+ },
2228
+ 'Program:exit'() {
2229
+ for (const usage of namespaceImports.values()) {
2230
+ if (!usage.usedLikeValue) {
2231
+ continue;
2232
+ }
2233
+ context.report({
2234
+ data: { name: usage.node.local.name },
2235
+ messageId: 'avoidCallableNamespaceImport',
2236
+ node: usage.node,
2237
+ });
2238
+ }
2239
+ },
2240
+ 'TaggedTemplateExpression > Identifier.tag'(node) {
2241
+ markNamespaceImportAsUsedLikeValue(node);
2242
+ },
2243
+ TSImportEqualsDeclaration(node) {
2244
+ if (node.moduleReference.type !== AST_NODE_TYPES.TSExternalModuleReference) {
2245
+ return;
2246
+ }
2247
+ context.report({
2248
+ data: { name: node.id.name },
2249
+ messageId: 'avoidImportEquals',
2250
+ node,
2251
+ });
2252
+ },
2253
+ };
2254
+ },
2255
+ meta: {
2256
+ docs: {
2257
+ description: 'Disallow legacy CommonJS interop import patterns such as `import = require(...)` and namespace imports used like callable values.',
2258
+ },
2259
+ messages: {
2260
+ avoidCallableNamespaceImport: 'Namespace import "{{name}}" is used like a value instead of a namespace. This is a brittle interop pattern and often should become a default import.',
2261
+ avoidImportEquals: '`import {{name}} = require(...)` is a legacy CommonJS import pattern.',
2262
+ },
2263
+ schema: [],
2264
+ type: 'problem',
2265
+ },
2266
+ name: 'no-commonjs-import-patterns',
2267
+ });
2268
+
2184
2269
  const MESSAGE_ID$4 = 'no-deep-imports';
2185
2270
  const ERROR_MESSAGE$2 = 'Deep imports of Taiga UI packages are prohibited';
2186
2271
  const CODE_EXTENSIONS = new Set([
@@ -2200,8 +2285,8 @@ const DEFAULT_OPTIONS = {
2200
2285
  importDeclaration: '^@taiga-ui*',
2201
2286
  projectName: String.raw `(?<=^@taiga-ui/)([-\w]+)`,
2202
2287
  };
2203
- const createRule$f = ESLintUtils.RuleCreator((name) => name);
2204
- const rule$i = createRule$f({
2288
+ const createRule$h = ESLintUtils.RuleCreator((name) => name);
2289
+ const rule$k = createRule$h({
2205
2290
  create(context) {
2206
2291
  const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
2207
2292
  const hasNonCodeExtension = (source) => {
@@ -2288,13 +2373,13 @@ const rule$i = createRule$f({
2288
2373
  name: 'no-deep-imports',
2289
2374
  });
2290
2375
 
2291
- const createRule$e = ESLintUtils.RuleCreator((name) => name);
2376
+ const createRule$g = ESLintUtils.RuleCreator((name) => name);
2292
2377
  const resolveCacheByOptions = new WeakMap();
2293
2378
  const nearestFileUpCache = new Map();
2294
2379
  const markerCache = new Map();
2295
2380
  const indexFileCache = new Map();
2296
2381
  const indexExportsCache = new Map();
2297
- var noDeepImportsToIndexedPackages = createRule$e({
2382
+ var noDeepImportsToIndexedPackages = createRule$g({
2298
2383
  create(context) {
2299
2384
  const parserServices = ESLintUtils.getParserServices(context);
2300
2385
  const program = parserServices.program;
@@ -3014,7 +3099,7 @@ const ANGULAR_SIGNALS_ASYNC_GUIDE_URL = 'https://angular.dev/guide/signals#react
3014
3099
  const UNTRACKED_RULES_README_URL = 'https://github.com/taiga-family/taiga-ui/blob/main/projects/eslint-plugin-experience-next/README.md';
3015
3100
  const createUntrackedRule = ESLintUtils.RuleCreator((name) => `${UNTRACKED_RULES_README_URL}#${name}`);
3016
3101
 
3017
- const rule$h = createUntrackedRule({
3102
+ const rule$j = createUntrackedRule({
3018
3103
  create(context) {
3019
3104
  const { checker, esTreeNodeToTSNodeMap, program } = getTypeAwareRuleContext(context);
3020
3105
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -3153,8 +3238,8 @@ function getScopeRoot(node) {
3153
3238
  return (findAncestor(node, (ancestor) => ancestor.type === AST_NODE_TYPES.Program || isFunctionLike(ancestor)) ?? node);
3154
3239
  }
3155
3240
 
3156
- const createRule$d = ESLintUtils.RuleCreator((name) => name);
3157
- const rule$g = createRule$d({
3241
+ const createRule$f = ESLintUtils.RuleCreator((name) => name);
3242
+ const rule$i = createRule$f({
3158
3243
  create(context) {
3159
3244
  const checkImplicitPublic = (node) => {
3160
3245
  const classRef = getEnclosingClass(node);
@@ -3216,6 +3301,38 @@ const rule$g = createRule$d({
3216
3301
  name: 'explicit-public-member',
3217
3302
  });
3218
3303
 
3304
+ const createRule$e = ESLintUtils.RuleCreator((name) => name);
3305
+ const rule$h = createRule$e({
3306
+ create(context) {
3307
+ const { sourceCode } = context;
3308
+ return {
3309
+ ImportDeclaration(node) {
3310
+ const importAttributesToken = sourceCode.getTokenAfter(node.source);
3311
+ if (importAttributesToken?.value !== 'assert') {
3312
+ return;
3313
+ }
3314
+ context.report({
3315
+ fix: (fixer) => fixer.replaceText(importAttributesToken, 'with'),
3316
+ messageId: 'useWithImportAttributes',
3317
+ node: importAttributesToken,
3318
+ });
3319
+ },
3320
+ };
3321
+ },
3322
+ meta: {
3323
+ docs: {
3324
+ description: 'Disallow legacy `assert { ... }` import assertions. Use `with { ... }` import attributes instead.',
3325
+ },
3326
+ fixable: 'code',
3327
+ messages: {
3328
+ useWithImportAttributes: 'Use `with { ... }` import attributes instead of `assert { ... }`.',
3329
+ },
3330
+ schema: [],
3331
+ type: 'problem',
3332
+ },
3333
+ name: 'no-import-assertions',
3334
+ });
3335
+
3219
3336
  function getParenthesizedInner(node) {
3220
3337
  const maybeNode = node;
3221
3338
  if (maybeNode.type === 'ParenthesizedExpression') {
@@ -3233,16 +3350,30 @@ function unwrapParenthesized(node) {
3233
3350
  return current;
3234
3351
  }
3235
3352
 
3236
- const createRule$c = ESLintUtils.RuleCreator((name) => name);
3237
- function isBooleanTrue(node) {
3353
+ const createRule$d = ESLintUtils.RuleCreator((name) => name);
3354
+ function isInfiniteLoopLiteral(node) {
3238
3355
  const unwrapped = unwrapParenthesized(node);
3239
- return unwrapped.type === AST_NODE_TYPES.Literal && unwrapped.value === true;
3356
+ if (unwrapped.type !== AST_NODE_TYPES.Literal) {
3357
+ return false;
3358
+ }
3359
+ return unwrapped.value === true || unwrapped.value === 1;
3240
3360
  }
3241
- const rule$f = createRule$c({
3361
+ function isInfiniteLoopTest(test) {
3362
+ return test === null || isInfiniteLoopLiteral(test);
3363
+ }
3364
+ const rule$g = createRule$d({
3242
3365
  create(context) {
3243
3366
  return {
3367
+ DoWhileStatement(node) {
3368
+ if (isInfiniteLoopTest(node.test)) {
3369
+ context.report({
3370
+ messageId: 'doWhileLoop',
3371
+ node: node.test,
3372
+ });
3373
+ }
3374
+ },
3244
3375
  ForStatement(node) {
3245
- if (!node.test) {
3376
+ if (isInfiniteLoopTest(node.test)) {
3246
3377
  context.report({
3247
3378
  messageId: 'forLoop',
3248
3379
  node,
@@ -3250,7 +3381,7 @@ const rule$f = createRule$c({
3250
3381
  }
3251
3382
  },
3252
3383
  WhileStatement(node) {
3253
- if (isBooleanTrue(node.test)) {
3384
+ if (isInfiniteLoopTest(node.test)) {
3254
3385
  context.report({
3255
3386
  messageId: 'whileLoop',
3256
3387
  node: node.test,
@@ -3261,9 +3392,10 @@ const rule$f = createRule$c({
3261
3392
  },
3262
3393
  meta: {
3263
3394
  docs: {
3264
- description: 'Disallow `while (true)` and `for` loops without an explicit condition. Prefer loops with meaningful exit conditions.',
3395
+ description: 'Disallow `for (;;)` / conditionals `for`, `while (true)`, and `do ... while (true)`. Prefer loops with meaningful exit conditions.',
3265
3396
  },
3266
3397
  messages: {
3398
+ doWhileLoop: 'Use an explicit exit condition instead of `do ... while (true)`.',
3267
3399
  forLoop: 'Use an explicit exit condition instead of a `for` loop without a condition.',
3268
3400
  whileLoop: 'Use an explicit exit condition instead of `while (true)`.',
3269
3401
  },
@@ -3273,9 +3405,9 @@ const rule$f = createRule$c({
3273
3405
  name: 'no-infinite-loop',
3274
3406
  });
3275
3407
 
3276
- const createRule$b = ESLintUtils.RuleCreator((name) => name);
3408
+ const createRule$c = ESLintUtils.RuleCreator((name) => name);
3277
3409
  const LEGACY_PEER_DEPS_PATTERN = /^legacy-peer-deps\s*=\s*true$/i;
3278
- const rule$e = createRule$b({
3410
+ const rule$f = createRule$c({
3279
3411
  create(context) {
3280
3412
  return {
3281
3413
  Program(node) {
@@ -3313,8 +3445,8 @@ const rule$e = createRule$b({
3313
3445
  name: 'no-legacy-peer-deps',
3314
3446
  });
3315
3447
 
3316
- const createRule$a = ESLintUtils.RuleCreator((name) => name);
3317
- const rule$d = createRule$a({
3448
+ const createRule$b = ESLintUtils.RuleCreator((name) => name);
3449
+ const rule$e = createRule$b({
3318
3450
  create(context) {
3319
3451
  const { checker, esTreeNodeToTSNodeMap, sourceCode } = getTypeAwareRuleContext(context);
3320
3452
  return {
@@ -3471,7 +3603,7 @@ const config = {
3471
3603
  },
3472
3604
  };
3473
3605
 
3474
- const createRule$9 = ESLintUtils.RuleCreator((name) => name);
3606
+ const createRule$a = ESLintUtils.RuleCreator((name) => name);
3475
3607
  function collectArrayExpressions(node) {
3476
3608
  const result = [];
3477
3609
  if (node.type === AST_NODE_TYPES.ArrayExpression) {
@@ -3497,7 +3629,7 @@ function collectArrayExpressions(node) {
3497
3629
  }
3498
3630
  return result;
3499
3631
  }
3500
- const rule$c = createRule$9({
3632
+ const rule$d = createRule$a({
3501
3633
  create(context) {
3502
3634
  const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
3503
3635
  const ignoreTupleContextualTyping = context.options[0]?.ignoreTupleContextualTyping ?? true;
@@ -3666,7 +3798,7 @@ function getSymbolAtNode(node, checker, esTreeNodeToTSNodeMap) {
3666
3798
  return checker.getSymbolAtLocation(tsNode) ?? null;
3667
3799
  }
3668
3800
 
3669
- const createRule$8 = ESLintUtils.RuleCreator((name) => name);
3801
+ const createRule$9 = ESLintUtils.RuleCreator((name) => name);
3670
3802
  function isFunctionLikeScope(node) {
3671
3803
  return !!node && isFunctionLike(node);
3672
3804
  }
@@ -3923,7 +4055,7 @@ function inspectComputedBody(root, context, localScopes, visitedFunctions, repor
3923
4055
  return;
3924
4056
  });
3925
4057
  }
3926
- const rule$b = createRule$8({
4058
+ const rule$c = createRule$9({
3927
4059
  create(context) {
3928
4060
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode, tsNodeToESTreeNodeMap, } = getTypeAwareRuleContext(context);
3929
4061
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -3966,7 +4098,7 @@ const rule$b = createRule$8({
3966
4098
  name: 'no-side-effects-in-computed',
3967
4099
  });
3968
4100
 
3969
- const rule$a = createUntrackedRule({
4101
+ const rule$b = createUntrackedRule({
3970
4102
  create(context) {
3971
4103
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
3972
4104
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -4012,7 +4144,7 @@ const rule$a = createUntrackedRule({
4012
4144
  name: 'no-signal-reads-after-await-in-reactive-context',
4013
4145
  });
4014
4146
 
4015
- const createRule$7 = ESLintUtils.RuleCreator((name) => name);
4147
+ const createRule$8 = ESLintUtils.RuleCreator((name) => name);
4016
4148
  function collectParts(node) {
4017
4149
  if (node.type === AST_NODE_TYPES.BinaryExpression && node.operator === '+') {
4018
4150
  return [...collectParts(node.left), ...collectParts(node.right)];
@@ -4062,7 +4194,7 @@ function templateContent(template, renderExpr) {
4062
4194
  : ''}`)
4063
4195
  .join('');
4064
4196
  }
4065
- const rule$9 = createRule$7({
4197
+ const rule$a = createRule$8({
4066
4198
  create(context) {
4067
4199
  const { sourceCode } = context;
4068
4200
  let parserServices = null;
@@ -4394,7 +4526,7 @@ function buildReactiveCallReplacement(outerUntrackedCall, reactiveCall, sourceCo
4394
4526
  }
4395
4527
  return dedent(text, reactiveCall.loc.start.column - outerUntrackedCall.parent.loc.start.column);
4396
4528
  }
4397
- const rule$8 = createUntrackedRule({
4529
+ const rule$9 = createUntrackedRule({
4398
4530
  create(context) {
4399
4531
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
4400
4532
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -4522,7 +4654,7 @@ function hasOpaqueSynchronousCalls(root, checker, esTreeNodeToTSNodeMap, program
4522
4654
  });
4523
4655
  return found;
4524
4656
  }
4525
- const rule$7 = createUntrackedRule({
4657
+ const rule$8 = createUntrackedRule({
4526
4658
  create(context) {
4527
4659
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
4528
4660
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -4604,8 +4736,8 @@ const rule$7 = createUntrackedRule({
4604
4736
  name: 'no-useless-untracked',
4605
4737
  });
4606
4738
 
4607
- const createRule$6 = ESLintUtils.RuleCreator((name) => name);
4608
- const rule$6 = createRule$6({
4739
+ const createRule$7 = ESLintUtils.RuleCreator((name) => name);
4740
+ const rule$7 = createRule$7({
4609
4741
  create(context, [{ printWidth }]) {
4610
4742
  const sourceCode = context.sourceCode;
4611
4743
  const getLineEndIndex = (lineStartIndex) => {
@@ -4857,7 +4989,7 @@ const rule$6 = createRule$6({
4857
4989
  name: 'object-single-line',
4858
4990
  });
4859
4991
 
4860
- const createRule$5 = ESLintUtils.RuleCreator((name) => name);
4992
+ const createRule$6 = ESLintUtils.RuleCreator((name) => name);
4861
4993
  const EMPTY_ARGUMENT = '__EMPTY_ARGUMENT__';
4862
4994
  function isSupportedControlFlowStatement(node) {
4863
4995
  return (node.type === AST_NODE_TYPES.BreakStatement ||
@@ -4926,7 +5058,7 @@ function renderTest(node, sourceCode) {
4926
5058
  const text = sourceCode.getText(node);
4927
5059
  return needsParenthesesInOrChain(node) ? `(${text})` : text;
4928
5060
  }
4929
- const rule$5 = createRule$5({
5061
+ const rule$6 = createRule$6({
4930
5062
  create(context) {
4931
5063
  const { sourceCode } = context;
4932
5064
  function checkBody(statements) {
@@ -5006,8 +5138,8 @@ const rule$5 = createRule$5({
5006
5138
 
5007
5139
  const MESSAGE_ID$1 = 'prefer-deep-imports';
5008
5140
  const ERROR_MESSAGE = 'Import via root entry point is prohibited when nested entry points exist';
5009
- const createRule$4 = ESLintUtils.RuleCreator(() => ERROR_MESSAGE);
5010
- var preferDeepImports = createRule$4({
5141
+ const createRule$5 = ESLintUtils.RuleCreator(() => ERROR_MESSAGE);
5142
+ var preferDeepImports = createRule$5({
5011
5143
  create(context, [options]) {
5012
5144
  const allowedPackages = normalizeImportFilter(options.importFilter);
5013
5145
  const isStrictMode = options.strict ?? false;
@@ -5348,7 +5480,7 @@ function buildRewrittenImports(node, baseImportPath, symbolToEntryPoint) {
5348
5480
  return importStatements.join('\n');
5349
5481
  }
5350
5482
 
5351
- const createRule$3 = ESLintUtils.RuleCreator((name) => name);
5483
+ const createRule$4 = ESLintUtils.RuleCreator((name) => name);
5352
5484
  function getPushCall(node) {
5353
5485
  if (node.expression.type !== AST_NODE_TYPES.CallExpression) {
5354
5486
  return null;
@@ -5363,7 +5495,7 @@ function getPushCall(node) {
5363
5495
  }
5364
5496
  return call;
5365
5497
  }
5366
- const rule$4 = createRule$3({
5498
+ const rule$5 = createRule$4({
5367
5499
  create(context) {
5368
5500
  const { sourceCode } = context;
5369
5501
  function checkBody(statements) {
@@ -5441,6 +5573,53 @@ const rule$4 = createRule$3({
5441
5573
  name: 'prefer-multi-arg-push',
5442
5574
  });
5443
5575
 
5576
+ const createRule$3 = ESLintUtils.RuleCreator((name) => name);
5577
+ function getModuleKeywordToken(sourceCode, node) {
5578
+ const firstToken = sourceCode.getFirstToken(node);
5579
+ if (!firstToken) {
5580
+ return null;
5581
+ }
5582
+ if (firstToken.value === 'declare') {
5583
+ return sourceCode.getTokenAfter(firstToken) ?? null;
5584
+ }
5585
+ return firstToken;
5586
+ }
5587
+ const rule$4 = createRule$3({
5588
+ create(context) {
5589
+ const { sourceCode } = context;
5590
+ return {
5591
+ TSModuleDeclaration(node) {
5592
+ if (node.kind !== 'module' ||
5593
+ node.global ||
5594
+ node.id.type === AST_NODE_TYPES.Literal) {
5595
+ return;
5596
+ }
5597
+ const moduleKeywordToken = getModuleKeywordToken(sourceCode, node);
5598
+ if (moduleKeywordToken?.value !== 'module') {
5599
+ return;
5600
+ }
5601
+ context.report({
5602
+ fix: (fixer) => fixer.replaceText(moduleKeywordToken, 'namespace'),
5603
+ messageId: 'useNamespaceKeyword',
5604
+ node: moduleKeywordToken,
5605
+ });
5606
+ },
5607
+ };
5608
+ },
5609
+ meta: {
5610
+ docs: {
5611
+ description: 'Prefer `namespace Foo {}` over the older `module Foo {}` syntax for TypeScript namespace declarations.',
5612
+ },
5613
+ fixable: 'code',
5614
+ messages: {
5615
+ useNamespaceKeyword: 'Use `namespace` instead of `module` for TypeScript namespace declarations.',
5616
+ },
5617
+ schema: [],
5618
+ type: 'problem',
5619
+ },
5620
+ name: 'prefer-namespace-keyword',
5621
+ });
5622
+
5444
5623
  /**
5445
5624
  * prefer-untracked-incidental-signal-reads
5446
5625
  *
@@ -6223,28 +6402,31 @@ const plugin = {
6223
6402
  'class-property-naming': classPropertyNaming,
6224
6403
  'decorator-key-sort': config$3,
6225
6404
  'flat-exports': flatExports,
6226
- 'host-attributes-sort': rule$k,
6405
+ 'host-attributes-sort': rule$n,
6227
6406
  'html-logical-properties': config$2,
6228
- 'injection-token-description': rule$j,
6229
- 'no-deep-imports': rule$i,
6407
+ 'injection-token-description': rule$m,
6408
+ 'no-commonjs-import-patterns': rule$l,
6409
+ 'no-deep-imports': rule$k,
6230
6410
  'no-deep-imports-to-indexed-packages': noDeepImportsToIndexedPackages,
6231
- 'no-fully-untracked-effect': rule$h,
6411
+ 'no-fully-untracked-effect': rule$j,
6232
6412
  'no-href-with-router-link': config$1,
6233
- 'no-implicit-public': rule$g,
6234
- 'no-infinite-loop': rule$f,
6235
- 'no-legacy-peer-deps': rule$e,
6236
- 'no-playwright-empty-fill': rule$d,
6413
+ 'no-implicit-public': rule$i,
6414
+ 'no-import-assertions': rule$h,
6415
+ 'no-infinite-loop': rule$g,
6416
+ 'no-legacy-peer-deps': rule$f,
6417
+ 'no-playwright-empty-fill': rule$e,
6237
6418
  'no-project-as-in-ng-template': config,
6238
- 'no-redundant-type-annotation': rule$c,
6239
- 'no-side-effects-in-computed': rule$b,
6240
- 'no-signal-reads-after-await-in-reactive-context': rule$a,
6241
- 'no-string-literal-concat': rule$9,
6242
- 'no-untracked-outside-reactive-context': rule$8,
6243
- 'no-useless-untracked': rule$7,
6244
- 'object-single-line': rule$6,
6245
- 'prefer-combined-if-control-flow': rule$5,
6419
+ 'no-redundant-type-annotation': rule$d,
6420
+ 'no-side-effects-in-computed': rule$c,
6421
+ 'no-signal-reads-after-await-in-reactive-context': rule$b,
6422
+ 'no-string-literal-concat': rule$a,
6423
+ 'no-untracked-outside-reactive-context': rule$9,
6424
+ 'no-useless-untracked': rule$8,
6425
+ 'object-single-line': rule$7,
6426
+ 'prefer-combined-if-control-flow': rule$6,
6246
6427
  'prefer-deep-imports': preferDeepImports,
6247
- 'prefer-multi-arg-push': rule$4,
6428
+ 'prefer-multi-arg-push': rule$5,
6429
+ 'prefer-namespace-keyword': rule$4,
6248
6430
  'prefer-untracked-incidental-signal-reads': rule$3,
6249
6431
  'prefer-untracked-signal-getter': rule$2,
6250
6432
  'short-tui-imports': rule$1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ui/eslint-plugin-experience-next",
3
- "version": "0.475.0",
3
+ "version": "0.477.0",
4
4
  "description": "An ESLint plugin to enforce a consistent code styles across taiga-ui projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,6 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ type MessageId = 'avoidCallableNamespaceImport' | 'avoidImportEquals';
3
+ export declare const rule: ESLintUtils.RuleModule<MessageId, [], unknown, ESLintUtils.RuleListener> & {
4
+ name: string;
5
+ };
6
+ export default rule;
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const rule: ESLintUtils.RuleModule<"useWithImportAttributes", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default rule;
@@ -1,5 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- type MessageId = 'forLoop' | 'whileLoop';
2
+ type MessageId = 'doWhileLoop' | 'forLoop' | 'whileLoop';
3
3
  export declare const rule: ESLintUtils.RuleModule<MessageId, [], unknown, ESLintUtils.RuleListener> & {
4
4
  name: string;
5
5
  };
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const rule: ESLintUtils.RuleModule<"useNamespaceKeyword", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default rule;