@taiga-ui/eslint-plugin-experience-next 0.507.0 → 0.509.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
@@ -88,6 +88,7 @@ from third-party plugins. The exact severities and file globs live in
88
88
  | [no-duplicate-attrs](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-duplicate-attrs.md) | Disallow duplicate attributes on the same HTML element | ✅ | | |
89
89
  | [no-duplicate-id](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-duplicate-id.md) | Disallow duplicate static `id` values in HTML templates | ✅ | | |
90
90
  | [no-duplicate-in-head](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-duplicate-in-head.md) | Disallow duplicate `title`, `base`, and key metadata tags inside `<head>` | ✅ | | |
91
+ | [no-empty-style-metadata](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-empty-style-metadata.md) | Remove empty Angular component style metadata | ✅ | 🔧 | |
91
92
  | [no-fully-untracked-effect](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-fully-untracked-effect.md) | Disallow reactive callbacks where all signal reads are hidden inside `untracked()` | ✅ | | |
92
93
  | [no-href-with-router-link](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-href-with-router-link.md) | Do not use href and routerLink attributes together on the same element | ✅ | 🔧 | |
93
94
  | [no-import-assertions](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-import-assertions.md) | Replace legacy `assert { ... }` import assertions with `with { ... }` | ✅ | 🔧 | |
@@ -126,39 +127,3 @@ from third-party plugins. The exact severities and file globs live in
126
127
  | [single-line-variable-spacing](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/single-line-variable-spacing.md) | Group consecutive single-line variables and separate multiline ones with a blank line | ✅ | 🔧 | |
127
128
  | [standalone-imports-sort](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/standalone-imports-sort.md) | Auto sort names inside Angular decorators | ✅ | 🔧 | |
128
129
  | [strict-tui-doc-example](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/strict-tui-doc-example.md) | If you use the addon-doc, there will be a hint that you are importing something incorrectly | | 🔧 | |
129
-
130
- ## prefer-conditional-return
131
-
132
- Prefer a single conditional return when an `if` statement returns one expression and the immediately following statement
133
- returns the fallback expression. The rule skips branches with comments, `else`, empty returns, intervening statements,
134
- or a return expression that is already a conditional expression.
135
-
136
- ```ts
137
- // ❌ error
138
- if (index < count) {
139
- return {value: index++, done: false};
140
- }
141
-
142
- return {value: undefined, done: true};
143
-
144
- // ✅ after autofix
145
- return index < count ? {value: index++, done: false} : {value: undefined, done: true};
146
- ```
147
-
148
- ```ts
149
- // not changed: if branch return is already conditional
150
- if (condition) {
151
- return first ? second : third;
152
- }
153
-
154
- return fallback;
155
- ```
156
-
157
- ```ts
158
- // not changed: fallback return is already conditional
159
- if (condition) {
160
- return value;
161
- }
162
-
163
- return first ? second : third;
164
- ```
package/index.d.ts CHANGED
@@ -18,7 +18,7 @@ declare const plugin: {
18
18
  'class-property-naming': import("@typescript-eslint/utils/ts-eslint").RuleModule<"invalidName", [import("./rules/taiga-specific/class-property-naming").RuleConfig[]], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
19
19
  name: string;
20
20
  };
21
- 'decorator-key-sort': import("eslint").Rule.RuleModule & {
21
+ 'decorator-key-sort': import("@typescript-eslint/utils/ts-eslint").RuleModule<"incorrectOrder", [Record<string, readonly string[]>], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
22
22
  name: string;
23
23
  };
24
24
  'element-newline': import("eslint").Rule.RuleModule & {
@@ -78,6 +78,9 @@ declare const plugin: {
78
78
  'no-duplicate-in-head': import("eslint").Rule.RuleModule & {
79
79
  name: string;
80
80
  };
81
+ 'no-empty-style-metadata': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noEmptyStyleMetadata", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
82
+ name: string;
83
+ };
81
84
  'no-fully-untracked-effect': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noTrackedReads", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
82
85
  name: string;
83
86
  };
package/index.esm.js CHANGED
@@ -532,6 +532,7 @@ var recommended = defineConfig([
532
532
  progress.configs['recommended-ci'],
533
533
  {
534
534
  ignores: [
535
+ '**/tests-results/**',
535
536
  '**/tests-report/**',
536
537
  '**/snapshots/**',
537
538
  '**/test-results/**',
@@ -1201,6 +1202,7 @@ var recommended = defineConfig([
1201
1202
  },
1202
1203
  ],
1203
1204
  '@taiga-ui/experience-next/no-deep-imports-to-indexed-packages': 'error',
1205
+ '@taiga-ui/experience-next/no-empty-style-metadata': 'error',
1204
1206
  '@taiga-ui/experience-next/no-fully-untracked-effect': 'error',
1205
1207
  '@taiga-ui/experience-next/no-implicit-public': 'error',
1206
1208
  '@taiga-ui/experience-next/no-import-assertions': 'error',
@@ -46222,7 +46224,7 @@ function buildMultilineStartTag(node, sourceText) {
46222
46224
  closing,
46223
46225
  ].join('\n');
46224
46226
  }
46225
- const rule$R = createRule({
46227
+ const rule$S = createRule({
46226
46228
  name: 'attrs-newline',
46227
46229
  rule: {
46228
46230
  create(context) {
@@ -46308,54 +46310,151 @@ const rule$R = createRule({
46308
46310
  },
46309
46311
  });
46310
46312
 
46313
+ function isObject(node) {
46314
+ return node?.type === dist$2.AST_NODE_TYPES.ObjectExpression;
46315
+ }
46316
+
46317
+ /**
46318
+ * Extracts the metadata object from a class decorator such as
46319
+ * `@Component()`, `@Directive()`, `@NgModule()`, or `@Pipe()`.
46320
+ *
46321
+ * Returns the first argument of the decorator call *if and only if*
46322
+ * it is an `ObjectExpression`.
46323
+ *
46324
+ * @example
46325
+ * // Given:
46326
+ * @Component({
46327
+ * selector: 'x',
46328
+ * imports: [A, B],
46329
+ * })
46330
+ * class MyCmp {}
46331
+ *
46332
+ * // In the AST for @Component(...)
46333
+ * getDecoratorMetadata(decorator, allowed) returns
46334
+ * ObjectExpression({ selector: ..., imports: ... })
46335
+ *
46336
+ * @param decorator - The decorator node attached to a class declaration.
46337
+ * @param allowedNames - A set of decorator names to consider
46338
+ * (e.g., Component, Directive, NgModule, Pipe).
46339
+ *
46340
+ * @returns The metadata `ObjectExpression` if present and valid,
46341
+ * otherwise `null`.
46342
+ */
46343
+ function getDecoratorMetadata(decorator, allowedNames) {
46344
+ return getDecoratorMetadataWithName(decorator, allowedNames)?.metadata ?? null;
46345
+ }
46346
+ function getDecoratorMetadataWithName(decorator, allowedNames) {
46347
+ const expr = decorator.expression;
46348
+ if (expr.type !== dist$2.AST_NODE_TYPES.CallExpression) {
46349
+ return null;
46350
+ }
46351
+ const callee = expr.callee;
46352
+ if (callee.type !== dist$2.AST_NODE_TYPES.Identifier || !allowedNames.has(callee.name)) {
46353
+ return null;
46354
+ }
46355
+ const arg = expr.arguments[0];
46356
+ return isObject(arg) ? { expression: expr, metadata: arg, name: callee.name } : null;
46357
+ }
46358
+
46359
+ function isStringLiteral(node) {
46360
+ return node?.type === dist$3.AST_NODE_TYPES.Literal && typeof node.value === 'string';
46361
+ }
46362
+ function isStaticTemplateLiteral(node) {
46363
+ return (node?.type === dist$3.AST_NODE_TYPES.TemplateLiteral &&
46364
+ node.expressions.length === 0 &&
46365
+ node.quasis.length === 1);
46366
+ }
46367
+ function getStaticStringValue(node) {
46368
+ if (isStringLiteral(node)) {
46369
+ return node.value;
46370
+ }
46371
+ return isStaticTemplateLiteral(node)
46372
+ ? (node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw ?? '')
46373
+ : null;
46374
+ }
46375
+ function isEmptyStaticString(node) {
46376
+ return getStaticStringValue(node) === '';
46377
+ }
46378
+
46379
+ function getStaticPropertyName(key) {
46380
+ if (key.type === dist$3.AST_NODE_TYPES.Identifier) {
46381
+ return key.name;
46382
+ }
46383
+ return key.type === dist$3.AST_NODE_TYPES.Literal &&
46384
+ (typeof key.value === 'string' || typeof key.value === 'number')
46385
+ ? String(key.value)
46386
+ : getStaticStringValue(key);
46387
+ }
46388
+ function getObjectPropertyName(node) {
46389
+ return node.computed ? null : getStaticPropertyName(node.key);
46390
+ }
46391
+ function getMemberExpressionPropertyName(node) {
46392
+ if (!node.computed && node.property.type === dist$3.AST_NODE_TYPES.Identifier) {
46393
+ return node.property.name;
46394
+ }
46395
+ return node.computed ? getStaticStringValue(node.property) : null;
46396
+ }
46397
+ function getClassMemberName(member) {
46398
+ return member.key.type === dist$3.AST_NODE_TYPES.PrivateIdentifier
46399
+ ? null
46400
+ : getStaticPropertyName(member.key);
46401
+ }
46402
+
46311
46403
  function sameOrder(a, b) {
46312
46404
  return a.length === b.length && a.every((value, index) => value === b[index]);
46313
46405
  }
46314
46406
 
46315
- const config$5 = {
46316
- create(context) {
46317
- const order = context.options[0] || {};
46407
+ const rule$R = createRule({
46408
+ create(context, [order]) {
46409
+ const decorators = new Set(Object.keys(order));
46318
46410
  return {
46319
46411
  ClassDeclaration(node) {
46320
- const decorators = Array.from(node.decorators ?? []);
46321
- for (const decorator of decorators) {
46322
- const { expression } = decorator;
46323
- const decoratorName = expression.callee?.name ?? '';
46324
- if (decoratorName in (order || {})) {
46325
- const orderList = order[decoratorName];
46326
- const decoratorArguments = Array.from(expression.arguments ?? []);
46327
- for (const argument of decoratorArguments) {
46328
- const properties = Array.from(argument.properties ?? []);
46329
- const current = properties
46330
- .map((prop) => prop.key?.name)
46331
- .filter(Boolean);
46332
- const correct = getCorrectOrderRelative(orderList, current);
46333
- if (!sameOrder(correct, current.filter((item) => correct.includes(item)))) {
46334
- context.report({
46335
- fix: (fixer) => {
46336
- const fileContent = context.sourceCode.text;
46337
- const forgottenProps = current.filter((key) => !orderList.includes(key));
46338
- const sortedDecoratorProperties = [
46339
- ...correct,
46340
- ...forgottenProps,
46341
- ].map((key) => properties.find((prop) => prop.key.name === key));
46342
- const newDecoratorArgument = `{${sortedDecoratorProperties
46343
- .map(({ range }) => fileContent.slice(...range))
46344
- .toString()}}`;
46345
- return fixer.replaceTextRange(argument.range, newDecoratorArgument);
46346
- },
46347
- message: `Incorrect order keys in @${decoratorName} decorator, please sort by [${correct.join(' -> ')}]`,
46348
- node: expression,
46349
- });
46350
- }
46351
- }
46412
+ for (const decorator of node?.decorators ?? []) {
46413
+ const metadata = getDecoratorMetadataWithName(decorator, decorators);
46414
+ if (!metadata) {
46415
+ continue;
46416
+ }
46417
+ const orderList = order[metadata.name] ?? [];
46418
+ const properties = getDecoratorProperties(metadata.metadata);
46419
+ if (!properties) {
46420
+ continue;
46421
+ }
46422
+ const current = properties.map(({ name }) => name);
46423
+ const correct = getCorrectOrderRelative(orderList, current);
46424
+ const sortableCurrent = current.filter((item) => correct.includes(item));
46425
+ if (sameOrder(correct, sortableCurrent)) {
46426
+ continue;
46352
46427
  }
46428
+ context.report({
46429
+ data: {
46430
+ decorator: metadata.name,
46431
+ order: correct.join(' -> '),
46432
+ },
46433
+ fix: (fixer) => {
46434
+ const forgottenProps = properties.filter(({ name }) => !orderList.includes(name));
46435
+ const sortedDecoratorProperties = [
46436
+ ...correct.flatMap((key) => properties.filter(({ name }) => name === key)),
46437
+ ...forgottenProps,
46438
+ ];
46439
+ const newDecoratorArgument = `{${sortedDecoratorProperties
46440
+ .map(({ node }) => context.sourceCode.text.slice(...node.range))
46441
+ .join(',')}}`;
46442
+ return fixer.replaceTextRange(metadata.metadata.range, newDecoratorArgument);
46443
+ },
46444
+ messageId: 'incorrectOrder',
46445
+ node: metadata.expression,
46446
+ });
46353
46447
  }
46354
46448
  },
46355
46449
  };
46356
46450
  },
46357
46451
  meta: {
46452
+ defaultOptions: [{}],
46453
+ docs: { description: 'Sorts keys of Angular decorator metadata.' },
46358
46454
  fixable: 'code',
46455
+ messages: {
46456
+ incorrectOrder: 'Incorrect order keys in @{{decorator}} decorator, please sort by [{{order}}]',
46457
+ },
46359
46458
  schema: [
46360
46459
  {
46361
46460
  additionalProperties: true,
@@ -46365,14 +46464,25 @@ const config$5 = {
46365
46464
  ],
46366
46465
  type: 'problem',
46367
46466
  },
46368
- };
46467
+ name: 'decorator-key-sort',
46468
+ });
46369
46469
  function getCorrectOrderRelative(correct, current) {
46370
46470
  return correct.filter((item) => current.includes(item));
46371
46471
  }
46372
- const rule$Q = createRule({
46373
- name: 'decorator-key-sort',
46374
- rule: config$5,
46375
- });
46472
+ function getDecoratorProperties(metadata) {
46473
+ const properties = [];
46474
+ for (const property of metadata.properties) {
46475
+ if (property.type !== dist$3.AST_NODE_TYPES.Property) {
46476
+ return null;
46477
+ }
46478
+ const name = getObjectPropertyName(property);
46479
+ if (!name) {
46480
+ return null;
46481
+ }
46482
+ properties.push({ name, node: property });
46483
+ }
46484
+ return properties;
46485
+ }
46376
46486
 
46377
46487
  const INLINE_HTML_ELEMENTS = new Set([
46378
46488
  'a',
@@ -46430,7 +46540,7 @@ function getNodeLabel(node) {
46430
46540
  }
46431
46541
  return node instanceof dist$4.TmplAstBoundText ? 'binding' : 'text';
46432
46542
  }
46433
- const rule$P = createRule({
46543
+ const rule$Q = createRule({
46434
46544
  name: 'element-newline',
46435
46545
  rule: {
46436
46546
  create(context) {
@@ -46504,93 +46614,6 @@ const rule$P = createRule({
46504
46614
  },
46505
46615
  });
46506
46616
 
46507
- function isObject(node) {
46508
- return node?.type === dist$2.AST_NODE_TYPES.ObjectExpression;
46509
- }
46510
-
46511
- /**
46512
- * Extracts the metadata object from a class decorator such as
46513
- * `@Component()`, `@Directive()`, `@NgModule()`, or `@Pipe()`.
46514
- *
46515
- * Returns the first argument of the decorator call *if and only if*
46516
- * it is an `ObjectExpression`.
46517
- *
46518
- * @example
46519
- * // Given:
46520
- * @Component({
46521
- * selector: 'x',
46522
- * imports: [A, B],
46523
- * })
46524
- * class MyCmp {}
46525
- *
46526
- * // In the AST for @Component(...)
46527
- * getDecoratorMetadata(decorator, allowed) →
46528
- * ObjectExpression({ selector: ..., imports: ... })
46529
- *
46530
- * @param decorator - The decorator node attached to a class declaration.
46531
- * @param allowedNames - A set of decorator names to consider
46532
- * (e.g., Component, Directive, NgModule, Pipe).
46533
- *
46534
- * @returns The metadata `ObjectExpression` if present and valid,
46535
- * otherwise `null`.
46536
- */
46537
- function getDecoratorMetadata(decorator, allowedNames) {
46538
- const expr = decorator.expression;
46539
- if (expr.type !== dist$2.AST_NODE_TYPES.CallExpression) {
46540
- return null;
46541
- }
46542
- const callee = expr.callee;
46543
- if (callee.type !== dist$2.AST_NODE_TYPES.Identifier || !allowedNames.has(callee.name)) {
46544
- return null;
46545
- }
46546
- const arg = expr.arguments[0];
46547
- return isObject(arg) ? arg : null;
46548
- }
46549
-
46550
- function isStringLiteral(node) {
46551
- return node?.type === dist$3.AST_NODE_TYPES.Literal && typeof node.value === 'string';
46552
- }
46553
- function isStaticTemplateLiteral(node) {
46554
- return (node?.type === dist$3.AST_NODE_TYPES.TemplateLiteral &&
46555
- node.expressions.length === 0 &&
46556
- node.quasis.length === 1);
46557
- }
46558
- function getStaticStringValue(node) {
46559
- if (isStringLiteral(node)) {
46560
- return node.value;
46561
- }
46562
- return isStaticTemplateLiteral(node)
46563
- ? (node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw ?? '')
46564
- : null;
46565
- }
46566
- function isEmptyStaticString(node) {
46567
- return getStaticStringValue(node) === '';
46568
- }
46569
-
46570
- function getStaticPropertyName(key) {
46571
- if (key.type === dist$3.AST_NODE_TYPES.Identifier) {
46572
- return key.name;
46573
- }
46574
- return key.type === dist$3.AST_NODE_TYPES.Literal &&
46575
- (typeof key.value === 'string' || typeof key.value === 'number')
46576
- ? String(key.value)
46577
- : getStaticStringValue(key);
46578
- }
46579
- function getObjectPropertyName(node) {
46580
- return node.computed ? null : getStaticPropertyName(node.key);
46581
- }
46582
- function getMemberExpressionPropertyName(node) {
46583
- if (!node.computed && node.property.type === dist$3.AST_NODE_TYPES.Identifier) {
46584
- return node.property.name;
46585
- }
46586
- return node.computed ? getStaticStringValue(node.property) : null;
46587
- }
46588
- function getClassMemberName(member) {
46589
- return member.key.type === dist$3.AST_NODE_TYPES.PrivateIdentifier
46590
- ? null
46591
- : getStaticPropertyName(member.key);
46592
- }
46593
-
46594
46617
  const DEFAULT_GROUP = '$DEFAULT';
46595
46618
  const DEFAULT_ATTRIBUTE_GROUPS = [
46596
46619
  '$ANGULAR_STRUCTURAL_DIRECTIVE',
@@ -46656,7 +46679,7 @@ const PRESETS = {
46656
46679
  $VUE: ['$CLASS', '$ID', '$VUE_ATTRIBUTE'],
46657
46680
  $VUE_ATTRIBUTE: /^v-/,
46658
46681
  };
46659
- const rule$O = createRule({
46682
+ const rule$P = createRule({
46660
46683
  create(context, [options]) {
46661
46684
  const sourceCode = context.sourceCode;
46662
46685
  const settings = {
@@ -46987,7 +47010,7 @@ const config$4 = {
46987
47010
  type: 'suggestion',
46988
47011
  },
46989
47012
  };
46990
- const rule$N = createRule({
47013
+ const rule$O = createRule({
46991
47014
  name: 'html-logical-properties',
46992
47015
  rule: config$4,
46993
47016
  });
@@ -247920,6 +247943,9 @@ function sourceFileHasDefaultExport(sourceFile) {
247920
247943
  hasRuntimeDefaultModifier(statement) ||
247921
247944
  (ts.isExportDeclaration(statement) && exportsDefaultSpecifier(statement)));
247922
247945
  }
247946
+ function isJsonModuleFileName(fileName) {
247947
+ return path.extname(fileName).toLowerCase() === '.json';
247948
+ }
247923
247949
  function hasDefaultExport(program, fileName) {
247924
247950
  const canonicalFileName = createCanonicalFileName();
247925
247951
  const normalizedFileName = canonicalFileName(fileName);
@@ -248226,7 +248252,7 @@ function isImportUsedOnlyAsAngularDiFirstArg(node, sourceCode) {
248226
248252
  }
248227
248253
  return hasSafeRuntimeUsage;
248228
248254
  }
248229
- const rule$M = createRule({
248255
+ const rule$N = createRule({
248230
248256
  create(context) {
248231
248257
  const { checker, esTreeNodeToTSNodeMap, sourceCode, tsProgram } = getTypeAwareRuleContext(context);
248232
248258
  const checkCycles = context.options[0]?.checkCycles ?? true;
@@ -248379,7 +248405,8 @@ const rule$M = createRule({
248379
248405
  : null;
248380
248406
  }
248381
248407
  function getIndexPathReplacement(moduleSpecifierPath, canonicalResolvedFileName) {
248382
- if (!isIndexModulePath(moduleSpecifierPath)) {
248408
+ if (codeFileExtensionRegExp.test(moduleSpecifierPath) ||
248409
+ !isIndexModulePath(moduleSpecifierPath)) {
248383
248410
  return null;
248384
248411
  }
248385
248412
  const parentDirectory = path.posix.dirname(moduleSpecifierPath);
@@ -248596,7 +248623,8 @@ const rule$M = createRule({
248596
248623
  const moduleSpecifier = node.source.value;
248597
248624
  const resolved = resolveModule(tsProgram, context.filename, moduleSpecifier);
248598
248625
  if (!resolved ||
248599
- (resolved.isExternalLibraryImport && ignoreExternalDefaultImports)) {
248626
+ (resolved.isExternalLibraryImport && ignoreExternalDefaultImports) ||
248627
+ isJsonModuleFileName(resolved.resolvedFileName)) {
248600
248628
  return;
248601
248629
  }
248602
248630
  const hasResolvedDefaultExport = hasDefaultExport(tsProgram, resolved.resolvedFileName);
@@ -248687,6 +248715,7 @@ const rule$M = createRule({
248687
248715
  const resolved = resolveModule(tsProgram, context.filename, moduleSpecifier);
248688
248716
  if (!resolved ||
248689
248717
  (resolved.isExternalLibraryImport && ignoreExternalDefaultImports) ||
248718
+ isJsonModuleFileName(resolved.resolvedFileName) ||
248690
248719
  !hasDefaultExport(tsProgram, resolved.resolvedFileName)) {
248691
248720
  return;
248692
248721
  }
@@ -248712,7 +248741,11 @@ const rule$M = createRule({
248712
248741
  return;
248713
248742
  }
248714
248743
  const namespaceImport = node.specifiers.find((specifier) => specifier.type === dist$3.AST_NODE_TYPES.ImportNamespaceSpecifier);
248715
- if (!namespaceImport) {
248744
+ if (!namespaceImport || typeof node.source.value !== 'string') {
248745
+ return;
248746
+ }
248747
+ const resolved = resolveModule(tsProgram, context.filename, node.source.value);
248748
+ if (resolved && isJsonModuleFileName(resolved.resolvedFileName)) {
248716
248749
  return;
248717
248750
  }
248718
248751
  const exportNames = getNamespaceImportExportNames(checker, esTreeNodeToTSNodeMap, node);
@@ -248944,7 +248977,7 @@ function getNgDevModeDeclarationFix(program, fixer) {
248944
248977
  ? fixer.insertTextBefore(firstStatement, 'declare const ngDevMode: boolean;\n\n')
248945
248978
  : fixer.insertTextBeforeRange([0, 0], 'declare const ngDevMode: boolean;\n');
248946
248979
  }
248947
- const rule$L = createRule({
248980
+ const rule$M = createRule({
248948
248981
  create(context) {
248949
248982
  const { sourceCode } = context;
248950
248983
  const program = sourceCode.ast;
@@ -248993,7 +249026,7 @@ const rule$L = createRule({
248993
249026
  name: 'injection-token-description',
248994
249027
  });
248995
249028
 
248996
- const rule$K = createRule({
249029
+ const rule$L = createRule({
248997
249030
  create(context) {
248998
249031
  const { sourceCode } = context;
248999
249032
  const namespaceImports = new Map();
@@ -249088,7 +249121,7 @@ const DEFAULT_OPTIONS = {
249088
249121
  importDeclaration: '^@taiga-ui*',
249089
249122
  projectName: String.raw `(?<=^@taiga-ui/)([-\w]+)`,
249090
249123
  };
249091
- const rule$J = createRule({
249124
+ const rule$K = createRule({
249092
249125
  create(context) {
249093
249126
  const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
249094
249127
  const hasNonCodeExtension = (source) => {
@@ -249180,7 +249213,7 @@ const nearestFileUpCache = new Map();
249180
249213
  const markerCache = new Map();
249181
249214
  const indexFileCache = new Map();
249182
249215
  const indexExportsCache = new Map();
249183
- const rule$I = createRule({
249216
+ const rule$J = createRule({
249184
249217
  create(context) {
249185
249218
  const parserServices = dist$3.ESLintUtils.getParserServices(context);
249186
249219
  const program = parserServices.program;
@@ -249374,13 +249407,13 @@ const noDuplicateAttributesRule = angular.templatePlugin.rules?.['no-duplicate-a
249374
249407
  if (!noDuplicateAttributesRule) {
249375
249408
  throw new Error('angular-eslint template rule "no-duplicate-attributes" is not available');
249376
249409
  }
249377
- const rule$H = createRule({
249410
+ const rule$I = createRule({
249378
249411
  name: 'no-duplicate-attrs',
249379
249412
  rule: noDuplicateAttributesRule,
249380
249413
  });
249381
249414
 
249382
249415
  const MESSAGE_ID$c = 'duplicateId';
249383
- const rule$G = createRule({
249416
+ const rule$H = createRule({
249384
249417
  name: 'no-duplicate-id',
249385
249418
  rule: {
249386
249419
  create(context) {
@@ -249441,7 +249474,7 @@ function getTrackingKey(node) {
249441
249474
  ? 'link[rel=canonical]'
249442
249475
  : null;
249443
249476
  }
249444
- const rule$F = createRule({
249477
+ const rule$G = createRule({
249445
249478
  name: 'no-duplicate-in-head',
249446
249479
  rule: {
249447
249480
  create(context) {
@@ -249496,6 +249529,133 @@ const rule$F = createRule({
249496
249529
  },
249497
249530
  });
249498
249531
 
249532
+ const COMPONENT_DECORATORS = new Set(['Component']);
249533
+ const rule$F = createRule({
249534
+ create(context) {
249535
+ const { sourceCode } = context;
249536
+ return {
249537
+ ClassDeclaration(node) {
249538
+ for (const decorator of node?.decorators ?? []) {
249539
+ const metadata = getDecoratorMetadata(decorator, COMPONENT_DECORATORS);
249540
+ if (!metadata) {
249541
+ continue;
249542
+ }
249543
+ const emptyProperties = metadata.properties.filter((property) => property.type === dist$3.AST_NODE_TYPES.Property &&
249544
+ isEmptyStyleMetadata(getObjectPropertyName(property), property, sourceCode));
249545
+ if (emptyProperties.length === 0) {
249546
+ continue;
249547
+ }
249548
+ const [firstEmptyProperty] = emptyProperties;
249549
+ if (!firstEmptyProperty) {
249550
+ continue;
249551
+ }
249552
+ context.report({
249553
+ fix: (fixer) => {
249554
+ const ranges = emptyProperties.map((property) => getPropertyRemovalRange(sourceCode, property));
249555
+ return ranges.some((range) => hasCommentsInRange(sourceCode, range))
249556
+ ? null
249557
+ : fixer.replaceTextRange(metadata.range, removeRanges(sourceCode.text.slice(...metadata.range), ranges.map(([start, end]) => [
249558
+ start - metadata.range[0],
249559
+ end - metadata.range[0],
249560
+ ])));
249561
+ },
249562
+ messageId: 'noEmptyStyleMetadata',
249563
+ node: firstEmptyProperty,
249564
+ });
249565
+ }
249566
+ },
249567
+ };
249568
+ },
249569
+ meta: {
249570
+ docs: {
249571
+ description: 'Disallow empty `styles`, `styleUrl`, and `styleUrls` metadata in Angular components.',
249572
+ },
249573
+ fixable: 'code',
249574
+ messages: {
249575
+ noEmptyStyleMetadata: 'Empty style metadata should be removed from @Component decorator.',
249576
+ },
249577
+ schema: [],
249578
+ type: 'problem',
249579
+ },
249580
+ name: 'no-empty-style-metadata',
249581
+ });
249582
+ function isEmptyStyleMetadata(name, property, sourceCode) {
249583
+ return (((name === 'styles' || name === 'styleUrl') &&
249584
+ isEmptyStringExpression(property.value)) ||
249585
+ (name === 'styleUrls' &&
249586
+ property.value.type === dist$3.AST_NODE_TYPES.ArrayExpression &&
249587
+ property.value.elements.length === 0 &&
249588
+ sourceCode.getText(property.value).replaceAll(/\s/g, '') === '[]'));
249589
+ }
249590
+ function isEmptyStringExpression(node) {
249591
+ if (node.type === dist$3.AST_NODE_TYPES.Literal) {
249592
+ return node.value === '';
249593
+ }
249594
+ if (node.type !== dist$3.AST_NODE_TYPES.TemplateLiteral || node.expressions.length > 0) {
249595
+ return false;
249596
+ }
249597
+ const [quasi] = node.quasis;
249598
+ return quasi?.value.raw === '';
249599
+ }
249600
+ function getPropertyRemovalRange(sourceCode, property) {
249601
+ const nextToken = sourceCode.getTokenAfter(property);
249602
+ if (nextToken?.value === ',') {
249603
+ return getRangeWithFollowingComma(sourceCode.text, property, nextToken.range);
249604
+ }
249605
+ const previousToken = sourceCode.getTokenBefore(property);
249606
+ return previousToken?.value === ','
249607
+ ? [previousToken.range[0], property.range[1]]
249608
+ : getSinglePropertyRange(sourceCode.text, property);
249609
+ }
249610
+ function getRangeWithFollowingComma(text, property, commaRange) {
249611
+ const lineStart = getLineStart(text, property.range[0]);
249612
+ const nextLineStart = getNextLineStart(text, commaRange[1]);
249613
+ return text.slice(lineStart, property.range[0]).trim() === '' &&
249614
+ text.slice(commaRange[1], nextLineStart).trim() === '' &&
249615
+ nextLineStart > commaRange[1]
249616
+ ? [lineStart, nextLineStart]
249617
+ : [property.range[0], commaRange[1]];
249618
+ }
249619
+ function getSinglePropertyRange(text, property) {
249620
+ const lineStart = getLineStart(text, property.range[0]);
249621
+ const nextLineStart = getNextLineStart(text, property.range[1]);
249622
+ return text.slice(lineStart, property.range[0]).trim() === '' &&
249623
+ text.slice(property.range[1], nextLineStart).trim() === '' &&
249624
+ nextLineStart > property.range[1]
249625
+ ? [lineStart, nextLineStart]
249626
+ : [property.range[0], property.range[1]];
249627
+ }
249628
+ function getLineStart(text, index) {
249629
+ return text.lastIndexOf('\n', index - 1) + 1;
249630
+ }
249631
+ function getNextLineStart(text, index) {
249632
+ const lineEnd = text.indexOf('\n', index);
249633
+ return lineEnd === -1 ? index : lineEnd + 1;
249634
+ }
249635
+ function hasCommentsInRange(sourceCode, [start, end]) {
249636
+ return sourceCode
249637
+ .getAllComments()
249638
+ .some((comment) => comment.range[0] >= start && comment.range[1] <= end);
249639
+ }
249640
+ function removeRanges(text, ranges) {
249641
+ return mergeRanges(ranges)
249642
+ .sort((left, right) => right[0] - left[0])
249643
+ .reduce((result, [start, end]) => `${result.slice(0, start)}${result.slice(end)}`, text);
249644
+ }
249645
+ function mergeRanges(ranges) {
249646
+ const sorted = [...ranges].sort((left, right) => left[0] - right[0]);
249647
+ const merged = [];
249648
+ for (const range of sorted) {
249649
+ const last = merged[merged.length - 1];
249650
+ if (!last || range[0] > last[1]) {
249651
+ merged.push([...range]);
249652
+ continue;
249653
+ }
249654
+ last[1] = Math.max(last[1], range[1]);
249655
+ }
249656
+ return merged;
249657
+ }
249658
+
249499
249659
  function isFunctionLike(node) {
249500
249660
  return (node.type === dist$3.AST_NODE_TYPES.ArrowFunctionExpression ||
249501
249661
  node.type === dist$3.AST_NODE_TYPES.FunctionDeclaration ||
@@ -251231,6 +251391,34 @@ function getStatementIndent$1(statement, sourceText) {
251231
251391
  const before = sourceText.slice(lineStart, start);
251232
251392
  return /^\s*$/.test(before) ? before : '';
251233
251393
  }
251394
+ function isAstNode(value) {
251395
+ if (!value || typeof value !== 'object' || !('type' in value)) {
251396
+ return false;
251397
+ }
251398
+ const { type } = value;
251399
+ return typeof type === 'string';
251400
+ }
251401
+ function getParent(node) {
251402
+ const maybeNode = node;
251403
+ if (!maybeNode || typeof maybeNode !== 'object' || !('parent' in maybeNode)) {
251404
+ return null;
251405
+ }
251406
+ const { parent } = maybeNode;
251407
+ return isAstNode(parent) ? parent : null;
251408
+ }
251409
+ function isOptionalMemberReceiver(call) {
251410
+ let current = call;
251411
+ let parent = getParent(current);
251412
+ while (parent?.type === dist$3.AST_NODE_TYPES.TSAsExpression ||
251413
+ parent?.type === dist$3.AST_NODE_TYPES.TSNonNullExpression ||
251414
+ parent?.type === dist$3.AST_NODE_TYPES.TSTypeAssertion) {
251415
+ current = parent;
251416
+ parent = getParent(current);
251417
+ }
251418
+ return (parent?.type === dist$3.AST_NODE_TYPES.MemberExpression &&
251419
+ parent.object === current &&
251420
+ parent.optional);
251421
+ }
251234
251422
  const rule$t = createRule({
251235
251423
  create(context) {
251236
251424
  const { checker, esTreeNodeToTSNodeMap, sourceCode } = getTypeAwareRuleContext(context);
@@ -251247,6 +251435,7 @@ const rule$t = createRule({
251247
251435
  return false;
251248
251436
  }
251249
251437
  if (child.type === dist$3.AST_NODE_TYPES.CallExpression &&
251438
+ !isOptionalMemberReceiver(child) &&
251250
251439
  isSignalReadCall(child, checker, signalNodeMap) &&
251251
251440
  isNullableCallType(child, checker, signalNodeMap)) {
251252
251441
  const text = sourceCode.getText(child);
@@ -252723,6 +252912,18 @@ const rule$m = createRule({
252723
252912
  });
252724
252913
 
252725
252914
  const MAX_INLINE_CONDITIONAL_RETURN_LENGTH = 90;
252915
+ const BOOLEAN_BINARY_OPERATORS = new Set([
252916
+ '!=',
252917
+ '!==',
252918
+ '<',
252919
+ '<=',
252920
+ '==',
252921
+ '===',
252922
+ '>',
252923
+ '>=',
252924
+ 'in',
252925
+ 'instanceof',
252926
+ ]);
252726
252927
  function getReturnStatement(statement) {
252727
252928
  if (statement.type === dist$3.AST_NODE_TYPES.ReturnStatement) {
252728
252929
  return statement;
@@ -252763,6 +252964,52 @@ function isConditionalExpression(node) {
252763
252964
  return (node !== null &&
252764
252965
  unwrapTypeAndParentheses(node).type === dist$3.AST_NODE_TYPES.ConditionalExpression);
252765
252966
  }
252967
+ function isBooleanLiteral(node) {
252968
+ return node.type === dist$3.AST_NODE_TYPES.Literal && typeof node.value === 'boolean';
252969
+ }
252970
+ function getBooleanLiteralValue(node) {
252971
+ return isBooleanLiteral(node) ? node.value : null;
252972
+ }
252973
+ function isKnownBooleanExpression(node) {
252974
+ const unwrapped = unwrapTypeAndParentheses(node);
252975
+ switch (unwrapped.type) {
252976
+ case dist$3.AST_NODE_TYPES.BinaryExpression:
252977
+ return BOOLEAN_BINARY_OPERATORS.has(unwrapped.operator);
252978
+ case dist$3.AST_NODE_TYPES.Literal:
252979
+ return typeof unwrapped.value === 'boolean';
252980
+ case dist$3.AST_NODE_TYPES.LogicalExpression:
252981
+ return (isKnownBooleanExpression(unwrapped.left) &&
252982
+ isKnownBooleanExpression(unwrapped.right));
252983
+ case dist$3.AST_NODE_TYPES.UnaryExpression:
252984
+ return unwrapped.operator === '!';
252985
+ default:
252986
+ return false;
252987
+ }
252988
+ }
252989
+ function isBooleanType(type) {
252990
+ const types = type.isUnion() ? type.types : [type];
252991
+ return types.every((part) => !!(part.flags & ts.TypeFlags.BooleanLike));
252992
+ }
252993
+ function getBooleanTestReturnStrategy(test, consequentExpression, alternateExpression, isTypeCheckedBooleanExpression) {
252994
+ const consequentValue = getBooleanLiteralValue(consequentExpression);
252995
+ const alternateValue = getBooleanLiteralValue(alternateExpression);
252996
+ if (consequentValue === false && alternateValue === true) {
252997
+ return unwrapTypeAndParentheses(test).type === dist$3.AST_NODE_TYPES.LogicalExpression
252998
+ ? 'skip'
252999
+ : 'negate';
253000
+ }
253001
+ if (consequentValue !== true || alternateValue !== false) {
253002
+ return 'skip';
253003
+ }
253004
+ return isKnownBooleanExpression(test) || isTypeCheckedBooleanExpression(test)
253005
+ ? 'direct'
253006
+ : 'coerce';
253007
+ }
253008
+ function shouldSkipBooleanReturn(test, consequentExpression, alternateExpression) {
253009
+ return (getBooleanLiteralValue(consequentExpression) === false &&
253010
+ getBooleanLiteralValue(alternateExpression) === true &&
253011
+ unwrapTypeAndParentheses(test).type === dist$3.AST_NODE_TYPES.LogicalExpression);
253012
+ }
252766
253013
  function needsParenthesesInConditionalTest(node) {
252767
253014
  if (getParenthesizedInner(node)) {
252768
253015
  return false;
@@ -252800,6 +253047,27 @@ function needsParenthesesInConditionalBranch(node) {
252800
253047
  return false;
252801
253048
  }
252802
253049
  }
253050
+ function needsParenthesesInBooleanCoercion(node) {
253051
+ if (getParenthesizedInner(node)) {
253052
+ return false;
253053
+ }
253054
+ switch (node.type) {
253055
+ case dist$3.AST_NODE_TYPES.ArrowFunctionExpression:
253056
+ case dist$3.AST_NODE_TYPES.AssignmentExpression:
253057
+ case dist$3.AST_NODE_TYPES.BinaryExpression:
253058
+ case dist$3.AST_NODE_TYPES.ConditionalExpression:
253059
+ case dist$3.AST_NODE_TYPES.LogicalExpression:
253060
+ case dist$3.AST_NODE_TYPES.ObjectExpression:
253061
+ case dist$3.AST_NODE_TYPES.SequenceExpression:
253062
+ case dist$3.AST_NODE_TYPES.TSAsExpression:
253063
+ case dist$3.AST_NODE_TYPES.TSSatisfiesExpression:
253064
+ case dist$3.AST_NODE_TYPES.TSTypeAssertion:
253065
+ case dist$3.AST_NODE_TYPES.YieldExpression:
253066
+ return true;
253067
+ default:
253068
+ return false;
253069
+ }
253070
+ }
252803
253071
  function renderExpression(node, sourceCode, needsParentheses) {
252804
253072
  const text = sourceCode.getText(node);
252805
253073
  return needsParentheses(node) ? `(${text})` : text;
@@ -252823,9 +253091,58 @@ function renderConditionalReturn(ifStatement, consequentExpression, alternateExp
252823
253091
  const branchIndent = `${indent} `;
252824
253092
  return `return ${test}\n${branchIndent}? ${consequent}\n${branchIndent}: ${alternate};`;
252825
253093
  }
253094
+ function renderBooleanTestReturn(ifStatement, sourceCode, strategy) {
253095
+ const test = sourceCode.getText(ifStatement.test);
253096
+ if (strategy === 'negate') {
253097
+ if (!test.includes('\n')) {
253098
+ const renderedTest = needsParenthesesInBooleanCoercion(ifStatement.test)
253099
+ ? `(${test})`
253100
+ : test;
253101
+ return `return !${renderedTest};`;
253102
+ }
253103
+ const indent = getStatementIndent(ifStatement, sourceCode);
253104
+ return `return !(\n${indent} ${test}\n${indent});`;
253105
+ }
253106
+ if (strategy === 'coerce') {
253107
+ if (!test.includes('\n')) {
253108
+ const renderedTest = needsParenthesesInBooleanCoercion(ifStatement.test)
253109
+ ? `(${test})`
253110
+ : test;
253111
+ return `return !!${renderedTest};`;
253112
+ }
253113
+ const indent = getStatementIndent(ifStatement, sourceCode);
253114
+ return `return !!(\n${indent} ${test}\n${indent});`;
253115
+ }
253116
+ if (!test.includes('\n')) {
253117
+ return `return ${test};`;
253118
+ }
253119
+ const indent = getStatementIndent(ifStatement, sourceCode);
253120
+ return `return (\n${indent} ${test}\n${indent});`;
253121
+ }
252826
253122
  const rule$l = createRule({
252827
253123
  create(context) {
252828
253124
  const { sourceCode } = context;
253125
+ let parserServices = null;
253126
+ let checker = null;
253127
+ try {
253128
+ parserServices = dist$3.ESLintUtils.getParserServices(context);
253129
+ checker = parserServices.program.getTypeChecker();
253130
+ }
253131
+ catch {
253132
+ // Type checking is optional; syntactic boolean detection still works.
253133
+ }
253134
+ function isTypeCheckedBooleanExpression(node) {
253135
+ if (!parserServices || !checker) {
253136
+ return false;
253137
+ }
253138
+ try {
253139
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
253140
+ return isBooleanType(checker.getTypeAtLocation(tsNode));
253141
+ }
253142
+ catch {
253143
+ return false;
253144
+ }
253145
+ }
252829
253146
  function checkBody(statements) {
252830
253147
  for (const [index, statement] of statements.entries()) {
252831
253148
  if (statement.type !== dist$3.AST_NODE_TYPES.IfStatement) {
@@ -252844,6 +253161,7 @@ const rule$l = createRule({
252844
253161
  !finalArgument ||
252845
253162
  isConditionalExpression(consequentArgument) ||
252846
253163
  isConditionalExpression(finalArgument) ||
253164
+ shouldSkipBooleanReturn(statement.test, consequentArgument, finalArgument) ||
252847
253165
  !hasOnlyWhitespaceBetween(sourceCode, statement, finalReturn) ||
252848
253166
  sourceCode.getCommentsInside(statement).length > 0 ||
252849
253167
  sourceCode.getCommentsInside(finalReturn).length > 0) {
@@ -252851,7 +253169,10 @@ const rule$l = createRule({
252851
253169
  }
252852
253170
  context.report({
252853
253171
  fix(fixer) {
252854
- const replacement = renderConditionalReturn(statement, consequentArgument, finalArgument, sourceCode);
253172
+ const booleanTestReturnStrategy = getBooleanTestReturnStrategy(statement.test, consequentArgument, finalArgument, isTypeCheckedBooleanExpression);
253173
+ const replacement = booleanTestReturnStrategy === 'skip'
253174
+ ? renderConditionalReturn(statement, consequentArgument, finalArgument, sourceCode)
253175
+ : renderBooleanTestReturn(statement, sourceCode, booleanTestReturnStrategy);
252855
253176
  return fixer.replaceTextRange([statement.range[0], finalReturn.range[1]], replacement);
252856
253177
  },
252857
253178
  messageId: 'preferConditionalReturn',
@@ -253819,10 +254140,17 @@ function isArray(node) {
253819
254140
  }
253820
254141
 
253821
254142
  function isImportsArrayProperty(property) {
253822
- const isProperty = property?.type === dist$3.AST_NODE_TYPES.Property;
253823
- const hasIdentifierKey = property?.key.type === dist$3.AST_NODE_TYPES.Identifier &&
254143
+ if (property?.type !== dist$3.AST_NODE_TYPES.Property) {
254144
+ return false;
254145
+ }
254146
+ const hasIdentifierKey = property.key.type === dist$3.AST_NODE_TYPES.Identifier &&
253824
254147
  property.key.name === 'imports';
253825
- return isProperty && hasIdentifierKey && isArray(property.value);
254148
+ return hasIdentifierKey && isArray(property.value);
254149
+ }
254150
+
254151
+ function getImportsArray(meta) {
254152
+ const property = meta.properties.find(isImportsArrayProperty);
254153
+ return property?.value ?? null;
253826
254154
  }
253827
254155
 
253828
254156
  function getImportedName$1(spec) {
@@ -253843,28 +254171,19 @@ const DEFAULT_EXCEPTIONS = [
253843
254171
  const rule$9 = createRule({
253844
254172
  create(context, [{ decorators = DEFAULT_DECORATORS, exceptions = DEFAULT_EXCEPTIONS }]) {
253845
254173
  const sourceCode = context.getSourceCode();
254174
+ const allowedDecorators = new Set(decorators);
253846
254175
  const importedFromTaiga = {};
253847
- const decoratorSelector = `Decorator[expression.callee.name=/^(${decorators.join('|')})$/]`;
253848
254176
  return {
253849
- [decoratorSelector](decorator) {
253850
- const expression = decorator.expression;
253851
- const isInvalidExpression = expression.type !== dist$3.AST_NODE_TYPES.CallExpression ||
253852
- expression.arguments.length === 0;
253853
- if (isInvalidExpression) {
254177
+ Decorator(decorator) {
254178
+ const metadata = getDecoratorMetadata(decorator, allowedDecorators);
254179
+ if (!metadata) {
253854
254180
  return;
253855
254181
  }
253856
- const [arg] = expression.arguments;
253857
- const isNotObject = arg?.type !== dist$3.AST_NODE_TYPES.ObjectExpression;
253858
- if (isNotObject) {
254182
+ const importsArray = getImportsArray(metadata);
254183
+ if (!importsArray) {
253859
254184
  return;
253860
254185
  }
253861
- const importsProperty = arg.properties
253862
- .filter((literal) => literal.type === dist$3.AST_NODE_TYPES.Property)
253863
- .find((literal) => isImportsArrayProperty(literal));
253864
- if (!importsProperty) {
253865
- return;
253866
- }
253867
- const imports = importsProperty.value.elements.filter((el) => {
254186
+ const imports = importsArray.elements.filter((el) => {
253868
254187
  const isIdentifier = el?.type === dist$3.AST_NODE_TYPES.Identifier;
253869
254188
  return Boolean(el && isIdentifier);
253870
254189
  });
@@ -254275,14 +254594,6 @@ const rule$7 = createRule({
254275
254594
  name: 'single-line-variable-spacing',
254276
254595
  });
254277
254596
 
254278
- function getImportsArray(meta) {
254279
- const property = meta.properties.find((literal) => literal.type === dist$2.AST_NODE_TYPES.Property &&
254280
- literal.key.type === dist$2.AST_NODE_TYPES.Identifier &&
254281
- literal.key.name === 'imports' &&
254282
- isArray(literal.value));
254283
- return property ? property.value : null;
254284
- }
254285
-
254286
254597
  function isSpread(node) {
254287
254598
  return node.type === dist$2.AST_NODE_TYPES.SpreadElement;
254288
254599
  }
@@ -254322,11 +254633,12 @@ function nameOf(node, source) {
254322
254633
  return source.getText(node);
254323
254634
  }
254324
254635
 
254636
+ const IMPORT_NAME_COLLATOR = new Intl.Collator('en', { numeric: true });
254325
254637
  /**
254326
- * Sorts Angular standalone import elements into a deterministic, alphabetical order.
254638
+ * Sorts Angular standalone import elements into a deterministic, natural order.
254327
254639
  *
254328
254640
  * The sorting rules:
254329
- * 1. Regular elements (Identifiers, MemberExpressions, etc.) are sorted alphabetically.
254641
+ * 1. Regular elements (Identifiers, MemberExpressions, etc.) are sorted naturally.
254330
254642
  * 2. Spread elements (e.g. `...A`) are sorted separately and placed after regular ones.
254331
254643
  * 3. Sorting is based on the string value returned by `nameOf()`.
254332
254644
  *
@@ -254345,10 +254657,13 @@ function nameOf(node, source) {
254345
254657
  function getSortedNames(elements, source) {
254346
254658
  const regular = elements.filter((e) => !isSpread(e));
254347
254659
  const spreads = elements.filter((e) => isSpread(e));
254348
- const sortedRegular = [...regular].sort((a, b) => nameOf(a, source).localeCompare(nameOf(b, source)));
254349
- const sortedSpreads = [...spreads].sort((a, b) => nameOf(a.argument, source).localeCompare(nameOf(b.argument, source)));
254660
+ const sortedRegular = [...regular].sort((a, b) => compareImportNames(nameOf(a, source), nameOf(b, source)));
254661
+ const sortedSpreads = [...spreads].sort((a, b) => compareImportNames(nameOf(a.argument, source), nameOf(b.argument, source)));
254350
254662
  return [...sortedRegular, ...sortedSpreads].map((n) => nameOf(n, source));
254351
254663
  }
254664
+ function compareImportNames(x, y) {
254665
+ return IMPORT_NAME_COLLATOR.compare(x, y);
254666
+ }
254352
254667
 
254353
254668
  const rule$6 = createRule({
254354
254669
  create(context, [options]) {
@@ -255277,21 +255592,22 @@ const plugin = {
255277
255592
  },
255278
255593
  rules: {
255279
255594
  'array-as-const': rule$5,
255280
- 'attrs-newline': rule$R,
255595
+ 'attrs-newline': rule$S,
255281
255596
  'class-property-naming': rule$4,
255282
- 'decorator-key-sort': rule$Q,
255283
- 'element-newline': rule$P,
255597
+ 'decorator-key-sort': rule$R,
255598
+ 'element-newline': rule$Q,
255284
255599
  'flat-exports': rule$3,
255285
- 'host-attributes-sort': rule$O,
255286
- 'html-logical-properties': rule$N,
255287
- 'import-integrity': rule$M,
255288
- 'injection-token-description': rule$L,
255289
- 'no-commonjs-import-patterns': rule$K,
255290
- 'no-deep-imports': rule$J,
255291
- 'no-deep-imports-to-indexed-packages': rule$I,
255292
- 'no-duplicate-attrs': rule$H,
255293
- 'no-duplicate-id': rule$G,
255294
- 'no-duplicate-in-head': rule$F,
255600
+ 'host-attributes-sort': rule$P,
255601
+ 'html-logical-properties': rule$O,
255602
+ 'import-integrity': rule$N,
255603
+ 'injection-token-description': rule$M,
255604
+ 'no-commonjs-import-patterns': rule$L,
255605
+ 'no-deep-imports': rule$K,
255606
+ 'no-deep-imports-to-indexed-packages': rule$J,
255607
+ 'no-duplicate-attrs': rule$I,
255608
+ 'no-duplicate-id': rule$H,
255609
+ 'no-duplicate-in-head': rule$G,
255610
+ 'no-empty-style-metadata': rule$F,
255295
255611
  'no-fully-untracked-effect': rule$E,
255296
255612
  'no-href-with-router-link': rule$D,
255297
255613
  'no-implicit-public': rule$C,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ui/eslint-plugin-experience-next",
3
- "version": "0.507.0",
3
+ "version": "0.509.0",
4
4
  "description": "An ESLint plugin to enforce a consistent code styles across taiga-ui projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,5 +1,5 @@
1
- import { type Rule } from 'eslint';
2
- export declare const rule: Rule.RuleModule & {
1
+ type Options = [Record<string, readonly string[]>];
2
+ export declare const rule: import("@typescript-eslint/utils/ts-eslint").RuleModule<"incorrectOrder", Options, unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
3
3
  name: string;
4
4
  };
5
5
  export default rule;
@@ -0,0 +1,5 @@
1
+ import { type TSESLint } from '@typescript-eslint/utils';
2
+ export declare const rule: TSESLint.RuleModule<"noEmptyStyleMetadata", [], unknown, TSESLint.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default rule;
@@ -1,5 +1,5 @@
1
- import { type TSESLint } from '@typescript-eslint/utils';
2
- export declare const rule: TSESLint.RuleModule<"preferConditionalReturn", [], unknown, TSESLint.RuleListener> & {
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const rule: ESLintUtils.RuleModule<"preferConditionalReturn", [], unknown, ESLintUtils.RuleListener> & {
3
3
  name: string;
4
4
  };
5
5
  export default rule;
@@ -1,4 +1,9 @@
1
1
  import { type TSESTree } from '@typescript-eslint/utils';
2
+ export interface DecoratorMetadataWithName {
3
+ readonly expression: TSESTree.CallExpression;
4
+ readonly metadata: TSESTree.ObjectExpression;
5
+ readonly name: string;
6
+ }
2
7
  /**
3
8
  * Extracts the metadata object from a class decorator such as
4
9
  * `@Component()`, `@Directive()`, `@NgModule()`, or `@Pipe()`.
@@ -15,7 +20,7 @@ import { type TSESTree } from '@typescript-eslint/utils';
15
20
  * class MyCmp {}
16
21
  *
17
22
  * // In the AST for @Component(...)
18
- * getDecoratorMetadata(decorator, allowed)
23
+ * getDecoratorMetadata(decorator, allowed) returns
19
24
  * ObjectExpression({ selector: ..., imports: ... })
20
25
  *
21
26
  * @param decorator - The decorator node attached to a class declaration.
@@ -25,4 +30,5 @@ import { type TSESTree } from '@typescript-eslint/utils';
25
30
  * @returns The metadata `ObjectExpression` if present and valid,
26
31
  * otherwise `null`.
27
32
  */
28
- export declare function getDecoratorMetadata(decorator: TSESTree.Decorator, allowedNames: Set<string>): TSESTree.ObjectExpression | null;
33
+ export declare function getDecoratorMetadata(decorator: TSESTree.Decorator, allowedNames: ReadonlySet<string>): TSESTree.ObjectExpression | null;
34
+ export declare function getDecoratorMetadataWithName(decorator: TSESTree.Decorator, allowedNames: ReadonlySet<string>): DecoratorMetadataWithName | null;
@@ -1,4 +1,4 @@
1
1
  import { type TSESTree } from '@typescript-eslint/utils';
2
- export declare function isImportsArrayProperty(property?: TSESTree.Property): property is TSESTree.Property & {
2
+ export declare function isImportsArrayProperty(property?: TSESTree.ObjectExpression['properties'][number]): property is TSESTree.Property & {
3
3
  value: TSESTree.ArrayExpression;
4
4
  };
@@ -1,9 +1,9 @@
1
1
  import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';
2
2
  /**
3
- * Sorts Angular standalone import elements into a deterministic, alphabetical order.
3
+ * Sorts Angular standalone import elements into a deterministic, natural order.
4
4
  *
5
5
  * The sorting rules:
6
- * 1. Regular elements (Identifiers, MemberExpressions, etc.) are sorted alphabetically.
6
+ * 1. Regular elements (Identifiers, MemberExpressions, etc.) are sorted naturally.
7
7
  * 2. Spread elements (e.g. `...A`) are sorted separately and placed after regular ones.
8
8
  * 3. Sorting is based on the string value returned by `nameOf()`.
9
9
  *