@taiga-ui/eslint-plugin-experience-next 0.504.0 → 0.506.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
@@ -109,6 +109,7 @@ from third-party plugins. The exact severities and file globs live in
109
109
  | [object-single-line](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/object-single-line.md) | Enforce single-line formatting for single-property objects when it fits `printWidth` | ✅ | 🔧 | |
110
110
  | [prefer-combined-if-control-flow](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-combined-if-control-flow.md) | Combine consecutive `if` statements that use the same `return`, `break`, `continue`, or `throw` | ✅ | 🔧 | |
111
111
  | [prefer-deep-imports](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-deep-imports.md) | Allow deep imports of Taiga UI packages | | 🔧 | |
112
+ | [prefer-loose-null-check](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-loose-null-check.md) | Prefer loose null checks over paired strict comparisons against `null` and `undefined` | ✅ | 🔧 | |
112
113
  | [prefer-multi-arg-push](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-multi-arg-push.md) | Combine consecutive `.push()` calls on the same array into a single multi-argument call | ✅ | 🔧 | |
113
114
  | [prefer-namespace-keyword](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-namespace-keyword.md) | Replace `module Foo {}` with `namespace Foo {}` for TypeScript namespace declarations | ✅ | 🔧 | |
114
115
  | [prefer-untracked-incidental-signal-reads](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-untracked-incidental-signal-reads.md) | Wrap likely-incidental signal reads with `untracked()` in reactive callbacks | ✅ | 🔧 | |
package/index.d.ts CHANGED
@@ -148,6 +148,9 @@ declare const plugin: {
148
148
  }], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
149
149
  name: string;
150
150
  };
151
+ 'prefer-loose-null-check': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferLooseNullCheck", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
152
+ name: string;
153
+ };
151
154
  'prefer-multi-arg-push': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferMultiArgPush", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
152
155
  name: string;
153
156
  };
package/index.esm.js CHANGED
@@ -1213,6 +1213,7 @@ var recommended = defineConfig([
1213
1213
  '@taiga-ui/experience-next/no-useless-untracked': 'error',
1214
1214
  '@taiga-ui/experience-next/object-single-line': ['error', { printWidth: 90 }],
1215
1215
  '@taiga-ui/experience-next/prefer-combined-if-control-flow': 'error',
1216
+ '@taiga-ui/experience-next/prefer-loose-null-check': 'error',
1216
1217
  '@taiga-ui/experience-next/prefer-multi-arg-push': 'error',
1217
1218
  '@taiga-ui/experience-next/prefer-namespace-keyword': 'error',
1218
1219
  '@taiga-ui/experience-next/prefer-untracked-incidental-signal-reads': 'error',
@@ -1509,6 +1510,7 @@ var taigaSpecific = defineConfig([
1509
1510
  {
1510
1511
  files: pattern('**/*.html'),
1511
1512
  ignores: [
1513
+ '**/proprietary/**',
1512
1514
  // Angular ESLint virtual files for inline templates in spec/cy files
1513
1515
  '**/*.spec.ts/**/*.html',
1514
1516
  '**/*.cy.ts/**/*.html',
@@ -46222,7 +46224,7 @@ function buildMultilineStartTag(node, sourceText) {
46222
46224
  closing,
46223
46225
  ].join('\n');
46224
46226
  }
46225
- const rule$P = createRule({
46227
+ const rule$Q = createRule({
46226
46228
  name: 'attrs-newline',
46227
46229
  rule: {
46228
46230
  create(context) {
@@ -46369,7 +46371,7 @@ const config$5 = {
46369
46371
  function getCorrectOrderRelative(correct, current) {
46370
46372
  return correct.filter((item) => current.includes(item));
46371
46373
  }
46372
- const rule$O = createRule({
46374
+ const rule$P = createRule({
46373
46375
  name: 'decorator-key-sort',
46374
46376
  rule: config$5,
46375
46377
  });
@@ -46433,7 +46435,7 @@ function getNodeLabel(node) {
46433
46435
  }
46434
46436
  return 'text';
46435
46437
  }
46436
- const rule$N = createRule({
46438
+ const rule$O = createRule({
46437
46439
  name: 'element-newline',
46438
46440
  rule: {
46439
46441
  create(context) {
@@ -46664,7 +46666,7 @@ const PRESETS = {
46664
46666
  $VUE: ['$CLASS', '$ID', '$VUE_ATTRIBUTE'],
46665
46667
  $VUE_ATTRIBUTE: /^v-/,
46666
46668
  };
46667
- const rule$M = createRule({
46669
+ const rule$N = createRule({
46668
46670
  create(context, [options]) {
46669
46671
  const sourceCode = context.sourceCode;
46670
46672
  const settings = {
@@ -46778,7 +46780,7 @@ function getHostAttributeProperties(hostObject) {
46778
46780
  return null;
46779
46781
  }
46780
46782
  const name = getStaticPropertyName(property.key);
46781
- if (name === null) {
46783
+ if (name == null) {
46782
46784
  return null;
46783
46785
  }
46784
46786
  properties.push({ name, node: property });
@@ -46997,7 +46999,7 @@ const config$4 = {
46997
46999
  type: 'suggestion',
46998
47000
  },
46999
47001
  };
47000
- const rule$L = createRule({
47002
+ const rule$M = createRule({
47001
47003
  name: 'html-logical-properties',
47002
47004
  rule: config$4,
47003
47005
  });
@@ -248249,7 +248251,7 @@ function isImportUsedOnlyAsAngularDiFirstArg(node, sourceCode) {
248249
248251
  }
248250
248252
  return hasSafeRuntimeUsage;
248251
248253
  }
248252
- const rule$K = createRule({
248254
+ const rule$L = createRule({
248253
248255
  create(context) {
248254
248256
  const { checker, esTreeNodeToTSNodeMap, sourceCode, tsProgram } = getTypeAwareRuleContext(context);
248255
248257
  const checkCycles = context.options[0]?.checkCycles ?? true;
@@ -248441,21 +248443,21 @@ const rule$K = createRule({
248441
248443
  getExcessParentPathReplacement(moduleSpecifierPath, resolved.resolvedFileName));
248442
248444
  }
248443
248445
  function checkUselessPathSegments(source) {
248444
- if (!shouldCheckUselessPathSegments || !source.value.startsWith('.')) {
248446
+ if (!shouldCheckUselessPathSegments ||
248447
+ !source.value.startsWith('.') ||
248448
+ splitModuleSpecifierQuery(source.value).query) {
248445
248449
  return;
248446
248450
  }
248447
- const parts = splitModuleSpecifierQuery(source.value);
248448
- const proposedPath = getUselessPathSegmentsReplacement(parts.path);
248451
+ const proposedPath = getUselessPathSegmentsReplacement(source.value);
248449
248452
  if (!proposedPath) {
248450
248453
  return;
248451
248454
  }
248452
- const proposedModuleSpecifier = `${proposedPath}${parts.query}`;
248453
248455
  context.report({
248454
248456
  data: {
248455
248457
  moduleSpecifier: source.value,
248456
- proposedPath: proposedModuleSpecifier,
248458
+ proposedPath,
248457
248459
  },
248458
- fix: (fixer) => fixer.replaceText(source, quoteModuleSpecifier(source, proposedModuleSpecifier)),
248460
+ fix: (fixer) => fixer.replaceText(source, quoteModuleSpecifier(source, proposedPath)),
248459
248461
  messageId: 'uselessPathSegments',
248460
248462
  node: source,
248461
248463
  });
@@ -248955,7 +248957,7 @@ function prependTokenName(text, name) {
248955
248957
  return `${text.slice(0, 1)}[${name}]: ${text.slice(1)}`;
248956
248958
  }
248957
248959
  function isNgDevModeVisible(sourceCode, node) {
248958
- for (let scope = sourceCode.getScope(node); scope !== null; scope = scope.upper) {
248960
+ for (let scope = sourceCode.getScope(node); scope != null; scope = scope.upper) {
248959
248961
  if (scope.variables.some((variable) => variable.name === NG_DEV_MODE)) {
248960
248962
  return true;
248961
248963
  }
@@ -248975,7 +248977,7 @@ function getNgDevModeDeclarationFix(program, fixer) {
248975
248977
  }
248976
248978
  return fixer.insertTextBeforeRange([0, 0], 'declare const ngDevMode: boolean;\n');
248977
248979
  }
248978
- const rule$J = createRule({
248980
+ const rule$K = createRule({
248979
248981
  create(context) {
248980
248982
  const { sourceCode } = context;
248981
248983
  const program = sourceCode.ast;
@@ -249024,7 +249026,7 @@ const rule$J = createRule({
249024
249026
  name: 'injection-token-description',
249025
249027
  });
249026
249028
 
249027
- const rule$I = createRule({
249029
+ const rule$J = createRule({
249028
249030
  create(context) {
249029
249031
  const { sourceCode } = context;
249030
249032
  const namespaceImports = new Map();
@@ -249119,7 +249121,7 @@ const DEFAULT_OPTIONS = {
249119
249121
  importDeclaration: '^@taiga-ui*',
249120
249122
  projectName: String.raw `(?<=^@taiga-ui/)([-\w]+)`,
249121
249123
  };
249122
- const rule$H = createRule({
249124
+ const rule$I = createRule({
249123
249125
  create(context) {
249124
249126
  const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
249125
249127
  const hasNonCodeExtension = (source) => {
@@ -249211,7 +249213,7 @@ const nearestFileUpCache = new Map();
249211
249213
  const markerCache = new Map();
249212
249214
  const indexFileCache = new Map();
249213
249215
  const indexExportsCache = new Map();
249214
- const rule$G = createRule({
249216
+ const rule$H = createRule({
249215
249217
  create(context) {
249216
249218
  const parserServices = dist$3.ESLintUtils.getParserServices(context);
249217
249219
  const program = parserServices.program;
@@ -249226,7 +249228,7 @@ const rule$G = createRule({
249226
249228
  }
249227
249229
  const key = `${containingDir}\0${moduleSpecifier}`;
249228
249230
  const cachedFile = cache.get(key);
249229
- if (cachedFile !== undefined) {
249231
+ if (cachedFile != null) {
249230
249232
  return cachedFile;
249231
249233
  }
249232
249234
  const resolved = ts.resolveModuleName(moduleSpecifier, context.filename, compilerOptions, compilerHost);
@@ -249237,7 +249239,7 @@ const rule$G = createRule({
249237
249239
  function findNearestFileUpwardsCached(startDirectory, fileName) {
249238
249240
  const key = `${startDirectory}\0${fileName}`;
249239
249241
  const cachedPath = nearestFileUpCache.get(key);
249240
- if (cachedPath !== undefined) {
249242
+ if (cachedPath != null) {
249241
249243
  return cachedPath;
249242
249244
  }
249243
249245
  let currentDirectory = startDirectory;
@@ -249258,7 +249260,7 @@ const rule$G = createRule({
249258
249260
  }
249259
249261
  function pickPackageMarkerFileCached(resolvedRootFilePath) {
249260
249262
  const cachedMarker = markerCache.get(resolvedRootFilePath);
249261
- if (cachedMarker !== undefined) {
249263
+ if (cachedMarker != null) {
249262
249264
  return cachedMarker;
249263
249265
  }
249264
249266
  const resolvedRootDirectory = path.dirname(resolvedRootFilePath);
@@ -249270,7 +249272,7 @@ const rule$G = createRule({
249270
249272
  }
249271
249273
  function pickIndexFileInDirectoryCached(packageDirectory) {
249272
249274
  const cachedIndexFile = indexFileCache.get(packageDirectory);
249273
- if (cachedIndexFile !== undefined) {
249275
+ if (cachedIndexFile != null) {
249274
249276
  return cachedIndexFile;
249275
249277
  }
249276
249278
  const indexTypescriptPath = path.join(packageDirectory, 'index.ts');
@@ -249408,13 +249410,13 @@ const noDuplicateAttributesRule = angular.templatePlugin.rules?.['no-duplicate-a
249408
249410
  if (!noDuplicateAttributesRule) {
249409
249411
  throw new Error('angular-eslint template rule "no-duplicate-attributes" is not available');
249410
249412
  }
249411
- const rule$F = createRule({
249413
+ const rule$G = createRule({
249412
249414
  name: 'no-duplicate-attrs',
249413
249415
  rule: noDuplicateAttributesRule,
249414
249416
  });
249415
249417
 
249416
249418
  const MESSAGE_ID$c = 'duplicateId';
249417
- const rule$E = createRule({
249419
+ const rule$F = createRule({
249418
249420
  name: 'no-duplicate-id',
249419
249421
  rule: {
249420
249422
  create(context) {
@@ -249476,7 +249478,7 @@ function getTrackingKey(node) {
249476
249478
  }
249477
249479
  return null;
249478
249480
  }
249479
- const rule$D = createRule({
249481
+ const rule$E = createRule({
249480
249482
  name: 'no-duplicate-in-head',
249481
249483
  rule: {
249482
249484
  create(context) {
@@ -249542,7 +249544,7 @@ function getOrderedChildren(node) {
249542
249544
  ...node.params,
249543
249545
  node.body,
249544
249546
  ];
249545
- return children.filter((child) => child !== undefined && child !== null);
249547
+ return children.filter((child) => child != null);
249546
249548
  }
249547
249549
  if (node.type === dist$3.AST_NODE_TYPES.BlockStatement ||
249548
249550
  node.type === dist$3.AST_NODE_TYPES.Program) {
@@ -249569,7 +249571,7 @@ function getOrderedChildren(node) {
249569
249571
  node.update,
249570
249572
  node.body,
249571
249573
  ];
249572
- return children.filter((child) => child !== null);
249574
+ return children.filter((child) => child != null);
249573
249575
  }
249574
249576
  if (node.type === dist$3.AST_NODE_TYPES.ForInStatement ||
249575
249577
  node.type === dist$3.AST_NODE_TYPES.ForOfStatement) {
@@ -250061,7 +250063,7 @@ const ANGULAR_SIGNALS_UNTRACKED_GUIDE_URL = 'https://angular.dev/guide/signals#r
250061
250063
  const ANGULAR_SIGNALS_ASYNC_GUIDE_URL = 'https://angular.dev/guide/signals#reactive-context-and-async-operations';
250062
250064
  const createUntrackedRule = createRule;
250063
250065
 
250064
- const rule$C = createUntrackedRule({
250066
+ const rule$D = createUntrackedRule({
250065
250067
  create(context) {
250066
250068
  const { checker, esTreeNodeToTSNodeMap, program } = getTypeAwareRuleContext(context);
250067
250069
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -250134,7 +250136,7 @@ const config$3 = {
250134
250136
  type: 'problem',
250135
250137
  },
250136
250138
  };
250137
- const rule$B = createRule({
250139
+ const rule$C = createRule({
250138
250140
  name: 'no-href-with-router-link',
250139
250141
  rule: config$3,
250140
250142
  });
@@ -250195,7 +250197,7 @@ function getScopeRoot(node) {
250195
250197
  return (findAncestor(node, (ancestor) => ancestor.type === dist$3.AST_NODE_TYPES.Program || isFunctionLike(ancestor)) ?? node);
250196
250198
  }
250197
250199
 
250198
- const rule$A = createRule({
250200
+ const rule$B = createRule({
250199
250201
  create(context) {
250200
250202
  const checkImplicitPublic = (node) => {
250201
250203
  const classRef = getEnclosingClass(node);
@@ -250257,7 +250259,7 @@ const rule$A = createRule({
250257
250259
  name: 'no-implicit-public',
250258
250260
  });
250259
250261
 
250260
- const rule$z = createRule({
250262
+ const rule$A = createRule({
250261
250263
  create(context) {
250262
250264
  const { sourceCode } = context;
250263
250265
  return {
@@ -250313,9 +250315,9 @@ function isInfiniteLoopLiteral(node) {
250313
250315
  return unwrapped.value === true || unwrapped.value === 1;
250314
250316
  }
250315
250317
  function isInfiniteLoopTest(test) {
250316
- return test === null || isInfiniteLoopLiteral(test);
250318
+ return test == null || isInfiniteLoopLiteral(test);
250317
250319
  }
250318
- const rule$y = createRule({
250320
+ const rule$z = createRule({
250319
250321
  create(context) {
250320
250322
  return {
250321
250323
  DoWhileStatement(node) {
@@ -250360,7 +250362,7 @@ const rule$y = createRule({
250360
250362
  });
250361
250363
 
250362
250364
  const LEGACY_PEER_DEPS_PATTERN = /^legacy-peer-deps\s*=\s*true$/i;
250363
- const rule$x = createRule({
250365
+ const rule$y = createRule({
250364
250366
  create(context) {
250365
250367
  return {
250366
250368
  Program(node) {
@@ -250820,7 +250822,7 @@ const OBSOLETE_HTML_ATTRS = {
250820
250822
  };
250821
250823
 
250822
250824
  const MESSAGE_ID$9 = 'obsolete';
250823
- const rule$w = createRule({
250825
+ const rule$x = createRule({
250824
250826
  name: 'no-obsolete-attrs',
250825
250827
  rule: {
250826
250828
  create(context) {
@@ -250898,7 +250900,7 @@ const OBSOLETE_HTML_TAGS = new Set([
250898
250900
  ]);
250899
250901
 
250900
250902
  const MESSAGE_ID$8 = 'unexpected';
250901
- const rule$v = createRule({
250903
+ const rule$w = createRule({
250902
250904
  name: 'no-obsolete-tags',
250903
250905
  rule: {
250904
250906
  create(context) {
@@ -250925,7 +250927,7 @@ const rule$v = createRule({
250925
250927
  },
250926
250928
  });
250927
250929
 
250928
- const rule$u = createRule({
250930
+ const rule$v = createRule({
250929
250931
  create(context) {
250930
250932
  const { checker, esTreeNodeToTSNodeMap, sourceCode } = getTypeAwareRuleContext(context);
250931
250933
  return {
@@ -251069,7 +251071,7 @@ const config$2 = {
251069
251071
  type: 'problem',
251070
251072
  },
251071
251073
  };
251072
- const rule$t = createRule({
251074
+ const rule$u = createRule({
251073
251075
  name: 'no-project-as-in-ng-template',
251074
251076
  rule: config$2,
251075
251077
  });
@@ -251106,7 +251108,7 @@ function collectArrayExpressions(node) {
251106
251108
  }
251107
251109
  return result;
251108
251110
  }
251109
- const rule$s = createRule({
251111
+ const rule$t = createRule({
251110
251112
  create(context) {
251111
251113
  const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
251112
251114
  const ignoreTupleContextualTyping = context.options[0]?.ignoreTupleContextualTyping ?? true;
@@ -251281,7 +251283,7 @@ function getStatementIndent(statement, sourceText) {
251281
251283
  const before = sourceText.slice(lineStart, start);
251282
251284
  return /^\s*$/.test(before) ? before : '';
251283
251285
  }
251284
- const rule$r = createRule({
251286
+ const rule$s = createRule({
251285
251287
  create(context) {
251286
251288
  const { checker, esTreeNodeToTSNodeMap, sourceCode } = getTypeAwareRuleContext(context);
251287
251289
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -251712,7 +251714,7 @@ function inspectComputedBody(root, context, localScopes, visitedFunctions, repor
251712
251714
  return;
251713
251715
  });
251714
251716
  }
251715
- const rule$q = createRule({
251717
+ const rule$r = createRule({
251716
251718
  create(context) {
251717
251719
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode, tsNodeToESTreeNodeMap, } = getTypeAwareRuleContext(context);
251718
251720
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -251755,7 +251757,7 @@ const rule$q = createRule({
251755
251757
  name: 'no-side-effects-in-computed',
251756
251758
  });
251757
251759
 
251758
- const rule$p = createUntrackedRule({
251760
+ const rule$q = createUntrackedRule({
251759
251761
  create(context) {
251760
251762
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
251761
251763
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -251849,7 +251851,7 @@ function templateContent(template, renderExpr) {
251849
251851
  : ''}`)
251850
251852
  .join('');
251851
251853
  }
251852
- const rule$o = createRule({
251854
+ const rule$p = createRule({
251853
251855
  create(context) {
251854
251856
  const { sourceCode } = context;
251855
251857
  let parserServices = null;
@@ -252184,7 +252186,7 @@ function buildReactiveCallReplacement(outerUntrackedCall, reactiveCall, sourceCo
252184
252186
  }
252185
252187
  return dedent(text, reactiveCall.loc.start.column - outerUntrackedCall.parent.loc.start.column);
252186
252188
  }
252187
- const rule$n = createUntrackedRule({
252189
+ const rule$o = createUntrackedRule({
252188
252190
  create(context) {
252189
252191
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
252190
252192
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -252220,7 +252222,7 @@ const rule$n = createUntrackedRule({
252220
252222
  fixer.replaceText(node, buildReactiveCallReplacement(node, reactiveCall, sourceCode)),
252221
252223
  ];
252222
252224
  const untrackedLocalName = findUntrackedAlias(program);
252223
- const stillUsed = untrackedLocalName !== null &&
252225
+ const stillUsed = untrackedLocalName != null &&
252224
252226
  isUntrackedUsedElsewhere(untrackedLocalName, node);
252225
252227
  if (!stillUsed) {
252226
252228
  fixes.push(...buildImportRemovalFixes(program, fixer, sourceCode));
@@ -252313,7 +252315,7 @@ function hasOpaqueSynchronousCalls(root, checker, esTreeNodeToTSNodeMap, program
252313
252315
  });
252314
252316
  return found;
252315
252317
  }
252316
- const rule$m = createUntrackedRule({
252318
+ const rule$n = createUntrackedRule({
252317
252319
  create(context) {
252318
252320
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
252319
252321
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -252344,11 +252346,11 @@ const rule$m = createUntrackedRule({
252344
252346
  ? (fixer) => {
252345
252347
  const parentStmt = parent;
252346
252348
  const replacement = buildReplacement(untrackedCall, parentStmt, sourceCode);
252347
- if (replacement === null) {
252349
+ if (replacement == null) {
252348
252350
  return null;
252349
252351
  }
252350
252352
  const untrackedLocalName = findUntrackedAlias(program);
252351
- const stillUsed = untrackedLocalName !== null &&
252353
+ const stillUsed = untrackedLocalName != null &&
252352
252354
  isUntrackedUsedElsewhere(untrackedLocalName, untrackedCall);
252353
252355
  const fixes = [fixer.replaceText(parentStmt, replacement)];
252354
252356
  if (!stillUsed) {
@@ -252395,7 +252397,7 @@ const rule$m = createUntrackedRule({
252395
252397
  name: 'no-useless-untracked',
252396
252398
  });
252397
252399
 
252398
- const rule$l = createRule({
252400
+ const rule$m = createRule({
252399
252401
  create(context, [{ printWidth }]) {
252400
252402
  const sourceCode = context.sourceCode;
252401
252403
  const getLineEndIndex = (lineStartIndex) => {
@@ -252716,7 +252718,7 @@ function renderTest(node, sourceCode) {
252716
252718
  const text = sourceCode.getText(node);
252717
252719
  return needsParenthesesInOrChain(node) ? `(${text})` : text;
252718
252720
  }
252719
- const rule$k = createRule({
252721
+ const rule$l = createRule({
252720
252722
  create(context) {
252721
252723
  const { sourceCode } = context;
252722
252724
  function checkBody(statements) {
@@ -252805,6 +252807,110 @@ const rule$k = createRule({
252805
252807
  name: 'prefer-combined-if-control-flow',
252806
252808
  });
252807
252809
 
252810
+ function parseStrictNullCheck(node, getText) {
252811
+ if (node.type !== dist$3.AST_NODE_TYPES.BinaryExpression) {
252812
+ return null;
252813
+ }
252814
+ const { left, operator, right } = node;
252815
+ if (operator !== '!==' && operator !== '===') {
252816
+ return null;
252817
+ }
252818
+ if (right.type === dist$3.AST_NODE_TYPES.Literal && right.value === null) {
252819
+ return { comparedWith: 'null', operand: getText(left), strictOp: operator };
252820
+ }
252821
+ if (right.type === dist$3.AST_NODE_TYPES.Identifier && right.name === 'undefined') {
252822
+ return { comparedWith: 'undefined', operand: getText(left), strictOp: operator };
252823
+ }
252824
+ if (left.type === dist$3.AST_NODE_TYPES.Literal && left.value === null) {
252825
+ return { comparedWith: 'null', operand: getText(right), strictOp: operator };
252826
+ }
252827
+ if (left.type === dist$3.AST_NODE_TYPES.Identifier && left.name === 'undefined') {
252828
+ return { comparedWith: 'undefined', operand: getText(right), strictOp: operator };
252829
+ }
252830
+ return null;
252831
+ }
252832
+ function isParsedNullCheck(value) {
252833
+ return value !== null;
252834
+ }
252835
+ function getLooseNullCheck(left, right) {
252836
+ if (!isParsedNullCheck(left) ||
252837
+ !isParsedNullCheck(right) ||
252838
+ left.strictOp !== right.strictOp ||
252839
+ left.operand !== right.operand ||
252840
+ left.comparedWith === right.comparedWith) {
252841
+ return null;
252842
+ }
252843
+ return `${left.operand} ${left.strictOp === '!==' ? '!=' : '=='} null`;
252844
+ }
252845
+ function collectAndLeaves(node) {
252846
+ if (node.type === dist$3.AST_NODE_TYPES.LogicalExpression && node.operator === '&&') {
252847
+ return [...collectAndLeaves(node.left), ...collectAndLeaves(node.right)];
252848
+ }
252849
+ return [node];
252850
+ }
252851
+ function isAndChainRoot(node) {
252852
+ return (node.parent.type !== dist$3.AST_NODE_TYPES.LogicalExpression ||
252853
+ node.parent.operator !== '&&');
252854
+ }
252855
+ function findLooseNullCheckMatch(parsedChecks) {
252856
+ for (const [firstCheckIndex, firstCheck] of parsedChecks.entries()) {
252857
+ if (!isParsedNullCheck(firstCheck)) {
252858
+ continue;
252859
+ }
252860
+ for (let secondCheckIndex = firstCheckIndex + 1; secondCheckIndex < parsedChecks.length; secondCheckIndex++) {
252861
+ const replacement = getLooseNullCheck(firstCheck, parsedChecks[secondCheckIndex] ?? null);
252862
+ if (replacement !== null) {
252863
+ return { firstCheckIndex, replacement, secondCheckIndex };
252864
+ }
252865
+ }
252866
+ }
252867
+ return null;
252868
+ }
252869
+ const rule$k = createRule({
252870
+ create(context) {
252871
+ const getText = (n) => context.sourceCode.getText(n);
252872
+ return {
252873
+ /**
252874
+ * Handles only paired null/undefined checks in the same `&&` chain.
252875
+ */
252876
+ LogicalExpression(node) {
252877
+ if (node.operator !== '&&' || !isAndChainRoot(node)) {
252878
+ return;
252879
+ }
252880
+ const leaves = collectAndLeaves(node);
252881
+ const parsedChecks = leaves.map((leaf) => parseStrictNullCheck(leaf, getText));
252882
+ const match = findLooseNullCheckMatch(parsedChecks);
252883
+ if (match === null) {
252884
+ return;
252885
+ }
252886
+ const replacement = leaves
252887
+ .filter((_, index) => index !== match.secondCheckIndex)
252888
+ .map((leaf, index) => index === match.firstCheckIndex
252889
+ ? match.replacement
252890
+ : getText(leaf))
252891
+ .join(' && ');
252892
+ context.report({
252893
+ fix: (fixer) => fixer.replaceText(node, replacement),
252894
+ messageId: 'preferLooseNullCheck',
252895
+ node,
252896
+ });
252897
+ },
252898
+ };
252899
+ },
252900
+ meta: {
252901
+ docs: {
252902
+ description: 'Prefer loose null checks over paired strict comparisons against `null` and `undefined`.',
252903
+ },
252904
+ fixable: 'code',
252905
+ messages: {
252906
+ preferLooseNullCheck: 'Prefer loose null check over paired strict comparisons against `null` and `undefined`.',
252907
+ },
252908
+ schema: [],
252909
+ type: 'suggestion',
252910
+ },
252911
+ name: 'prefer-loose-null-check',
252912
+ });
252913
+
252808
252914
  function getPushCall(node) {
252809
252915
  if (node.expression.type !== dist$3.AST_NODE_TYPES.CallExpression) {
252810
252916
  return null;
@@ -253190,7 +253296,7 @@ const rule$h = createUntrackedRule({
253190
253296
  const estreeNodeMap = tsNodeToESTreeNodeMap;
253191
253297
  function buildFix(read) {
253192
253298
  const untrackedAlias = findUntrackedAlias(program);
253193
- const alreadyHasUntracked = untrackedAlias !== null;
253299
+ const alreadyHasUntracked = untrackedAlias != null;
253194
253300
  const wrapped = buildUntrackedWrap(read, sourceCode, untrackedAlias ?? 'untracked');
253195
253301
  if (alreadyHasUntracked) {
253196
253302
  return (fixer) => [fixer.replaceText(read, wrapped)];
@@ -253520,13 +253626,13 @@ const VALID_CONTAINERS = new Set(['menu', 'ol', 'ul']);
253520
253626
  */
253521
253627
  function isElement(node) {
253522
253628
  return (typeof node === 'object' &&
253523
- node !== null &&
253629
+ node != null &&
253524
253630
  typeof node['name'] === 'string' &&
253525
253631
  Array.isArray(node['children']));
253526
253632
  }
253527
253633
  function getClosestParentElement(node) {
253528
253634
  let current = node['parent'];
253529
- while (current !== null && current !== undefined) {
253635
+ while (current != null) {
253530
253636
  if (isElement(current)) {
253531
253637
  return current;
253532
253638
  }
@@ -254198,7 +254304,7 @@ const rule$6 = createRule({
254198
254304
  if (!arr) {
254199
254305
  continue;
254200
254306
  }
254201
- const elements = arr.elements.filter((el) => el !== null);
254307
+ const elements = arr.elements.filter((el) => el != null);
254202
254308
  if (elements.length <= 1) {
254203
254309
  continue;
254204
254310
  }
@@ -254444,7 +254550,7 @@ const rule$3 = createRule({
254444
254550
  const purityCache = new WeakMap();
254445
254551
  const isPureArray = (arr) => {
254446
254552
  const cachedPurity = purityCache.get(arr);
254447
- if (cachedPurity !== undefined) {
254553
+ if (cachedPurity != null) {
254448
254554
  return cachedPurity;
254449
254555
  }
254450
254556
  if (arr.isDirty) {
@@ -254613,7 +254719,7 @@ const config = {
254613
254719
  }
254614
254720
  for (const attr of node.inputs) {
254615
254721
  const attrValue = getLiteralStringValue(attr);
254616
- if (attrValue !== null) {
254722
+ if (attrValue != null) {
254617
254723
  checkAttribute(context, attr, attrValue, checkers);
254618
254724
  }
254619
254725
  }
@@ -255119,42 +255225,43 @@ const plugin = {
255119
255225
  },
255120
255226
  rules: {
255121
255227
  'array-as-const': rule$5,
255122
- 'attrs-newline': rule$P,
255228
+ 'attrs-newline': rule$Q,
255123
255229
  'class-property-naming': rule$4,
255124
- 'decorator-key-sort': rule$O,
255125
- 'element-newline': rule$N,
255230
+ 'decorator-key-sort': rule$P,
255231
+ 'element-newline': rule$O,
255126
255232
  'flat-exports': rule$3,
255127
- 'host-attributes-sort': rule$M,
255128
- 'html-logical-properties': rule$L,
255129
- 'import-integrity': rule$K,
255130
- 'injection-token-description': rule$J,
255131
- 'no-commonjs-import-patterns': rule$I,
255132
- 'no-deep-imports': rule$H,
255133
- 'no-deep-imports-to-indexed-packages': rule$G,
255134
- 'no-duplicate-attrs': rule$F,
255135
- 'no-duplicate-id': rule$E,
255136
- 'no-duplicate-in-head': rule$D,
255137
- 'no-fully-untracked-effect': rule$C,
255138
- 'no-href-with-router-link': rule$B,
255139
- 'no-implicit-public': rule$A,
255140
- 'no-import-assertions': rule$z,
255141
- 'no-infinite-loop': rule$y,
255142
- 'no-legacy-peer-deps': rule$x,
255143
- 'no-obsolete-attrs': rule$w,
255144
- 'no-obsolete-tags': rule$v,
255145
- 'no-playwright-empty-fill': rule$u,
255146
- 'no-project-as-in-ng-template': rule$t,
255147
- 'no-redundant-type-annotation': rule$s,
255148
- 'no-repeated-signal-in-conditional': rule$r,
255233
+ 'host-attributes-sort': rule$N,
255234
+ 'html-logical-properties': rule$M,
255235
+ 'import-integrity': rule$L,
255236
+ 'injection-token-description': rule$K,
255237
+ 'no-commonjs-import-patterns': rule$J,
255238
+ 'no-deep-imports': rule$I,
255239
+ 'no-deep-imports-to-indexed-packages': rule$H,
255240
+ 'no-duplicate-attrs': rule$G,
255241
+ 'no-duplicate-id': rule$F,
255242
+ 'no-duplicate-in-head': rule$E,
255243
+ 'no-fully-untracked-effect': rule$D,
255244
+ 'no-href-with-router-link': rule$C,
255245
+ 'no-implicit-public': rule$B,
255246
+ 'no-import-assertions': rule$A,
255247
+ 'no-infinite-loop': rule$z,
255248
+ 'no-legacy-peer-deps': rule$y,
255249
+ 'no-obsolete-attrs': rule$x,
255250
+ 'no-obsolete-tags': rule$w,
255251
+ 'no-playwright-empty-fill': rule$v,
255252
+ 'no-project-as-in-ng-template': rule$u,
255253
+ 'no-redundant-type-annotation': rule$t,
255254
+ 'no-repeated-signal-in-conditional': rule$s,
255149
255255
  'no-restricted-attr-values': rule$2,
255150
- 'no-side-effects-in-computed': rule$q,
255151
- 'no-signal-reads-after-await-in-reactive-context': rule$p,
255152
- 'no-string-literal-concat': rule$o,
255153
- 'no-untracked-outside-reactive-context': rule$n,
255154
- 'no-useless-untracked': rule$m,
255155
- 'object-single-line': rule$l,
255156
- 'prefer-combined-if-control-flow': rule$k,
255256
+ 'no-side-effects-in-computed': rule$r,
255257
+ 'no-signal-reads-after-await-in-reactive-context': rule$q,
255258
+ 'no-string-literal-concat': rule$p,
255259
+ 'no-untracked-outside-reactive-context': rule$o,
255260
+ 'no-useless-untracked': rule$n,
255261
+ 'object-single-line': rule$m,
255262
+ 'prefer-combined-if-control-flow': rule$l,
255157
255263
  'prefer-deep-imports': rule$1,
255264
+ 'prefer-loose-null-check': rule$k,
255158
255265
  'prefer-multi-arg-push': rule$j,
255159
255266
  'prefer-namespace-keyword': rule$i,
255160
255267
  'prefer-untracked-incidental-signal-reads': rule$h,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ui/eslint-plugin-experience-next",
3
- "version": "0.504.0",
3
+ "version": "0.506.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,4 @@
1
+ export declare const rule: import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferLooseNullCheck", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
2
+ name: string;
3
+ };
4
+ export default rule;