@taiga-ui/eslint-plugin-experience-next 0.383.0 → 0.385.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.
@@ -1,58 +1,2 @@
1
- import html from '@html-eslint/eslint-plugin';
2
- import htmlParser from '@html-eslint/parser';
3
- declare const _default: ({
4
- ignores: string[];
5
- files?: undefined;
6
- rules?: undefined;
7
- } | {
8
- files: string[];
9
- plugins: {
10
- html: typeof html;
11
- };
12
- language: string;
13
- languageOptions: {
14
- parser: typeof htmlParser;
15
- };
16
- rules: {
17
- '@taiga-ui/experience-next/no-href-with-router-link': string;
18
- 'html/indent': string;
19
- 'html/no-extra-spacing-attrs': string;
20
- 'html/no-multiple-h1': string;
21
- 'html/no-restricted-attr-values': (string | {
22
- attrPatterns: string[];
23
- attrValuePatterns: string[];
24
- message: string;
25
- })[];
26
- 'html/no-restricted-attrs': (string | {
27
- attrPatterns: string[];
28
- message: string;
29
- tagPatterns: string[];
30
- })[];
31
- 'html/require-closing-tags': string;
32
- 'html/require-img-alt': (string | {
33
- substitute: string[];
34
- })[];
35
- 'html/use-baseline': string;
36
- "html/require-lang": "error";
37
- "html/require-doctype": "error";
38
- "html/require-title": "error";
39
- "html/attrs-newline": "error";
40
- "html/element-newline": ["error", {
41
- inline: string[];
42
- }];
43
- "html/no-duplicate-id": "error";
44
- "html/require-li-container": "error";
45
- "html/quotes": "error";
46
- "html/no-obsolete-tags": "error";
47
- "html/no-duplicate-attrs": "error";
48
- "html/no-duplicate-in-head": "error";
49
- };
50
- ignores?: undefined;
51
- } | {
52
- files: string[];
53
- rules: {
54
- 'html/no-restricted-attr-values': string;
55
- };
56
- ignores?: undefined;
57
- })[];
1
+ declare const _default: import("@eslint/config-helpers").Config[];
58
2
  export default _default;
package/index.esm.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import fs, { readFileSync } from 'node:fs';
2
2
  import html from '@html-eslint/eslint-plugin';
3
3
  import htmlParser from '@html-eslint/parser';
4
+ import { defineConfig } from 'eslint/config';
4
5
  import { createRequire } from 'node:module';
5
6
  import eslint from '@eslint/js';
6
7
  import markdown from '@eslint/markdown';
7
8
  import rxjs from '@smarttools/eslint-plugin-rxjs';
8
9
  import stylistic from '@stylistic/eslint-plugin';
9
10
  import angular from 'angular-eslint';
10
- import { defineConfig } from 'eslint/config';
11
11
  import compat from 'eslint-plugin-compat';
12
12
  import progress from 'eslint-plugin-file-progress';
13
13
  import jest from 'eslint-plugin-jest';
@@ -26,7 +26,7 @@ import ts from 'typescript';
26
26
  import { AST_NODE_TYPES as AST_NODE_TYPES$1 } from '@typescript-eslint/types';
27
27
  import path from 'node:path';
28
28
 
29
- var htmlEslint = [
29
+ var htmlEslint = defineConfig([
30
30
  {
31
31
  ignores: [
32
32
  '**/tests-report/**',
@@ -38,15 +38,14 @@ var htmlEslint = [
38
38
  ],
39
39
  },
40
40
  {
41
- ...html.configs.recommended,
42
41
  files: ['**/*.html'],
43
42
  plugins: { html },
43
+ extends: ['html/recommended'],
44
44
  language: 'html/html',
45
45
  languageOptions: {
46
46
  parser: htmlParser,
47
47
  },
48
48
  rules: {
49
- ...html.configs.recommended.rules,
50
49
  '@taiga-ui/experience-next/no-href-with-router-link': 'error',
51
50
  'html/indent': 'off', // prettier conflicts
52
51
  'html/no-extra-spacing-attrs': 'off', // prettier conflicts
@@ -116,7 +115,7 @@ var htmlEslint = [
116
115
  'html/no-restricted-attr-values': 'off',
117
116
  },
118
117
  },
119
- ];
118
+ ]);
120
119
 
121
120
  const TUI_RECOMMENDED_NAMING_CONVENTION = [
122
121
  {
@@ -272,6 +271,11 @@ var recommended = defineConfig([
272
271
  tseslint.configs.all,
273
272
  ],
274
273
  languageOptions: {
274
+ globals: {
275
+ ...globals.builtin,
276
+ ...globals.browser,
277
+ ...globals.node,
278
+ },
275
279
  parserOptions: {
276
280
  ecmaVersion: 'latest',
277
281
  errorOnTypeScriptSyntacticAndSemanticIssues: false,
@@ -280,18 +284,8 @@ var recommended = defineConfig([
280
284
  warnOnUnsupportedTypeScriptVersion: false,
281
285
  ...parserOptions,
282
286
  },
283
- globals: {
284
- ...globals.builtin,
285
- ...globals.browser,
286
- ...globals.node,
287
- },
288
287
  },
289
288
  rules: {
290
- '@typescript-eslint/no-confusing-void-expression': 'off',
291
- '@typescript-eslint/no-import-type-side-effects': 'off', // verbatimModuleSyntax should be false
292
- '@typescript-eslint/no-invalid-void-type': 'off',
293
- 'no-void': ['error', { allowAsStatement: true }],
294
- 'sonarjs/no-identical-functions': 'error',
295
289
  '@stylistic/function-call-spacing': 'error',
296
290
  '@stylistic/lines-between-class-members': [
297
291
  'error',
@@ -376,6 +370,7 @@ var recommended = defineConfig([
376
370
  '@typescript-eslint/explicit-member-accessibility': [
377
371
  'error',
378
372
  {
373
+ accessibility: 'explicit',
379
374
  overrides: {
380
375
  accessors: 'explicit',
381
376
  constructors: 'no-public',
@@ -383,7 +378,6 @@ var recommended = defineConfig([
383
378
  parameterProperties: 'explicit',
384
379
  properties: 'explicit',
385
380
  },
386
- accessibility: 'explicit',
387
381
  },
388
382
  ],
389
383
  '@typescript-eslint/explicit-module-boundary-types': 'off',
@@ -400,6 +394,7 @@ var recommended = defineConfig([
400
394
  ],
401
395
  '@typescript-eslint/no-base-to-string': 'off',
402
396
  '@typescript-eslint/no-confusing-non-null-assertion': 'error',
397
+ '@typescript-eslint/no-confusing-void-expression': 'off',
403
398
  '@typescript-eslint/no-deprecated': 'off',
404
399
  '@typescript-eslint/no-duplicate-enum-values': 'error',
405
400
  '@typescript-eslint/no-duplicate-type-constituents': 'error',
@@ -431,7 +426,9 @@ var recommended = defineConfig([
431
426
  '@typescript-eslint/no-floating-promises': 'off',
432
427
  '@typescript-eslint/no-for-in-array': 'error',
433
428
  '@typescript-eslint/no-implied-eval': 'error',
429
+ '@typescript-eslint/no-import-type-side-effects': 'off', // verbatimModuleSyntax should be false
434
430
  '@typescript-eslint/no-inferrable-types': 'error',
431
+ '@typescript-eslint/no-invalid-void-type': 'off',
435
432
  '@typescript-eslint/no-magic-numbers': 'off',
436
433
  '@typescript-eslint/no-misused-promises': 'off',
437
434
  '@typescript-eslint/no-misused-spread': 'off',
@@ -547,8 +544,8 @@ var recommended = defineConfig([
547
544
  '@typescript-eslint/switch-exhaustiveness-check': [
548
545
  'error',
549
546
  {
550
- considerDefaultExhaustiveForUnions: true,
551
547
  allowDefaultCaseForExhaustiveSwitch: true,
548
+ considerDefaultExhaustiveForUnions: true,
552
549
  requireDefaultForNonUnion: false,
553
550
  },
554
551
  ],
@@ -566,8 +563,8 @@ var recommended = defineConfig([
566
563
  'decorator-position/decorator-position': [
567
564
  'error',
568
565
  {
569
- printWidth: 120,
570
566
  methods: 'above',
567
+ printWidth: 120,
571
568
  properties: 'above',
572
569
  },
573
570
  ],
@@ -722,10 +719,10 @@ var recommended = defineConfig([
722
719
  },
723
720
  ],
724
721
  'no-var': 'error',
722
+ 'no-void': ['error', { allowAsStatement: true }],
725
723
  'perfectionist/sort-array-includes': [
726
724
  'error',
727
725
  {
728
- groupKind: 'literals-first',
729
726
  ignoreCase: true,
730
727
  order: 'asc',
731
728
  type: 'alphabetical',
@@ -742,7 +739,6 @@ var recommended = defineConfig([
742
739
  'perfectionist/sort-sets': [
743
740
  'error',
744
741
  {
745
- groupKind: 'literals-first',
746
742
  ignoreCase: true,
747
743
  order: 'asc',
748
744
  type: 'alphabetical',
@@ -778,6 +774,7 @@ var recommended = defineConfig([
778
774
  'promise/param-names': 'error',
779
775
  'simple-import-sort/exports': 'error',
780
776
  'simple-import-sort/imports': 'error',
777
+ 'sonarjs/no-identical-functions': 'error',
781
778
  'sonarjs/no-inverted-boolean-check': 'error',
782
779
  'spaced-comment': [
783
780
  'error',
@@ -947,6 +944,7 @@ var recommended = defineConfig([
947
944
  {
948
945
  ignoreWithDirectives: [
949
946
  'tuiButtonClose',
947
+ 'tuiButtonCopy',
950
948
  'tuiAccordion',
951
949
  'tuiButtonX',
952
950
  'tuiOption',
@@ -992,7 +990,6 @@ var recommended = defineConfig([
992
990
  files: ['**/*.pw.spec.ts'],
993
991
  extends: [playwright.configs['flat/recommended']],
994
992
  rules: {
995
- 'playwright/no-networkidle': 'off',
996
993
  'compat/compat': 'off',
997
994
  'jest/prefer-importing-jest-globals': 'off',
998
995
  'playwright/expect-expect': [
@@ -1002,6 +999,7 @@ var recommended = defineConfig([
1002
999
  },
1003
1000
  ],
1004
1001
  'playwright/no-force-option': 'error',
1002
+ 'playwright/no-networkidle': 'off',
1005
1003
  'playwright/no-skipped-test': 'off',
1006
1004
  'playwright/no-wait-for-selector': 'off',
1007
1005
  'playwright/no-wait-for-timeout': 'off',
@@ -1034,7 +1032,6 @@ var recommended = defineConfig([
1034
1032
  * If enabled we have
1035
1033
  * Expected to be running in 'ProxyZone', but it was not found
1036
1034
  */
1037
- 'jest/valid-title': 'error',
1038
1035
  'jest/prefer-ending-with-an-expect': 'off',
1039
1036
  'jest/prefer-importing-jest-globals': 'off',
1040
1037
  'jest/prefer-lowercase-title': [
@@ -1072,6 +1069,7 @@ var recommended = defineConfig([
1072
1069
  },
1073
1070
  ],
1074
1071
  'jest/unbound-method': 'off',
1072
+ 'jest/valid-title': 'error',
1075
1073
  },
1076
1074
  },
1077
1075
  {
@@ -1667,6 +1665,7 @@ const rule$3 = createRule$6({
1667
1665
  create(context) {
1668
1666
  const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
1669
1667
  const isDeepImport = (source) => !!source && new RegExp(deepImport, 'g').test(source);
1668
+ const isSideEffectImport = (node) => node.specifiers.length === 0;
1670
1669
  const isInsideTheSameEntryPoint = (source) => {
1671
1670
  const filePath = path
1672
1671
  .relative(context.cwd, context.filename)
@@ -1680,9 +1679,11 @@ const rule$3 = createRule$6({
1680
1679
  const shouldIgnore = (source) => !!source && ignoreImports.some((p) => new RegExp(p, 'g').test(source));
1681
1680
  return {
1682
1681
  [`ImportDeclaration[source.value=/${importDeclaration}/]`](node) {
1683
- const importSource = node?.source.value;
1684
- if (!node ||
1685
- !importSource ||
1682
+ if (!node || isSideEffectImport(node)) {
1683
+ return;
1684
+ }
1685
+ const importSource = node.source.value;
1686
+ if (!importSource ||
1686
1687
  !isDeepImport(importSource) ||
1687
1688
  isInsideTheSameEntryPoint(importSource) ||
1688
1689
  shouldIgnore(importSource)) {
@@ -1749,54 +1750,154 @@ var noDeepImportsToIndexedPackages = createRule$5({
1749
1750
  const parserServices = ESLintUtils.getParserServices(context);
1750
1751
  const program = parserServices.program;
1751
1752
  const compilerHost = ts.createCompilerHost(program.getCompilerOptions(), true);
1753
+ function resolveTypescriptModule(moduleSpecifier) {
1754
+ const resolved = ts.resolveModuleName(moduleSpecifier, context.filename, program.getCompilerOptions(), compilerHost);
1755
+ return resolved.resolvedModule?.resolvedFileName ?? null;
1756
+ }
1752
1757
  return {
1753
1758
  ImportDeclaration(node) {
1754
- const importPath = node.source.value;
1755
- if (typeof importPath !== 'string' || importPath.startsWith('.')) {
1759
+ const importSpecifier = node.source.value;
1760
+ if (typeof importSpecifier !== 'string') {
1756
1761
  return;
1757
1762
  }
1758
- const containingFile = context.filename;
1759
- const { resolvedModule } = ts.resolveModuleName(importPath, containingFile, program.getCompilerOptions(), compilerHost);
1760
- const resolvedPath = resolvedModule?.resolvedFileName;
1761
- if (!resolvedPath || resolvedPath.endsWith('index.ts')) {
1763
+ if (!isExternalModuleSpecifier(importSpecifier)) {
1762
1764
  return;
1763
1765
  }
1764
- const dir = path.dirname(resolvedPath);
1765
- const baseDir = path.resolve(dir, '..');
1766
- const indexPath = path.join(baseDir, 'index.ts');
1767
- const ngPackagePath = path.join(baseDir, 'ng-package.json');
1768
- const packageJsonPath = path.join(baseDir, 'package.json');
1769
- const hasIndex = fs.existsSync(indexPath);
1770
- const hasPackage = fs.existsSync(ngPackagePath) || fs.existsSync(packageJsonPath);
1771
- if (hasIndex && hasPackage) {
1772
- const relative = path.relative(baseDir, resolvedPath);
1773
- const shouldFix = !!(relative && !relative.startsWith('..'));
1774
- if (shouldFix) {
1775
- const parts = importPath.split('/');
1776
- const suggestedImport = parts.slice(0, -1).join('/');
1777
- context.report({
1778
- data: { importPath, suggestedImport },
1779
- messageId: 'deepImport',
1780
- node: node.source,
1781
- });
1782
- }
1766
+ const packageRootSpecifier = getPackageRootSpecifier(importSpecifier);
1767
+ const importSubpath = getSubpath(importSpecifier, packageRootSpecifier);
1768
+ if (!importSubpath) {
1769
+ return;
1770
+ }
1771
+ const resolvedRootModuleFilePath = resolveTypescriptModule(packageRootSpecifier);
1772
+ if (!resolvedRootModuleFilePath) {
1773
+ return;
1783
1774
  }
1775
+ const packageMarkerFilePath = pickPackageMarkerFile(resolvedRootModuleFilePath);
1776
+ if (!packageMarkerFilePath) {
1777
+ return;
1778
+ }
1779
+ const packageDirectory = path.dirname(packageMarkerFilePath);
1780
+ const indexFilePath = pickIndexFileInDirectory(packageDirectory);
1781
+ if (!indexFilePath) {
1782
+ return;
1783
+ }
1784
+ const hasMatchingReExport = indexExportsSubpath(indexFilePath, importSubpath);
1785
+ if (!hasMatchingReExport) {
1786
+ return;
1787
+ }
1788
+ context.report({
1789
+ data: {
1790
+ importPath: importSpecifier,
1791
+ suggestedImport: packageRootSpecifier,
1792
+ },
1793
+ messageId: 'deepImport',
1794
+ node: node.source,
1795
+ });
1784
1796
  },
1785
1797
  };
1786
1798
  },
1787
1799
  defaultOptions: [],
1788
1800
  meta: {
1789
1801
  docs: {
1790
- description: 'Disallow deep imports from packages that expose an index.ts next to ng-package.json or package.json',
1802
+ description: 'Disallow deep imports only when package root index.ts (or index.d.ts) re-exports that subpath, and the package is marked by ng-package.json or package.json',
1791
1803
  },
1792
1804
  messages: {
1793
- deepImport: 'Import "{{importPath}}" should go through the package index.ts (use "{{suggestedImport}}").',
1805
+ deepImport: 'Import "{{importPath}}" should go through the package root export (use "{{suggestedImport}}").',
1794
1806
  },
1795
1807
  schema: [],
1796
1808
  type: 'problem',
1797
1809
  },
1798
1810
  name: 'no-deep-imports-to-indexed-packages',
1799
1811
  });
1812
+ function isExternalModuleSpecifier(moduleSpecifier) {
1813
+ if (!moduleSpecifier || moduleSpecifier.startsWith('.')) {
1814
+ return false;
1815
+ }
1816
+ return !path.isAbsolute(moduleSpecifier);
1817
+ }
1818
+ function pickPackageMarkerFile(resolvedRootFilePath) {
1819
+ const resolvedRootDirectory = path.dirname(resolvedRootFilePath);
1820
+ const nearestNgPackageJson = findNearestFileUpwards(resolvedRootDirectory, 'ng-package.json');
1821
+ const nearestPackageJson = findNearestFileUpwards(resolvedRootDirectory, 'package.json');
1822
+ return nearestNgPackageJson ?? nearestPackageJson ?? null;
1823
+ }
1824
+ function pickIndexFileInDirectory(packageDirectory) {
1825
+ const indexTypescriptPath = path.join(packageDirectory, 'index.ts');
1826
+ const indexTypesDeclarationPath = path.join(packageDirectory, 'index.d.ts');
1827
+ if (fs.existsSync(indexTypescriptPath)) {
1828
+ return indexTypescriptPath;
1829
+ }
1830
+ if (fs.existsSync(indexTypesDeclarationPath)) {
1831
+ return indexTypesDeclarationPath;
1832
+ }
1833
+ return null;
1834
+ }
1835
+ function isScopedPackage(importSpecifier) {
1836
+ return importSpecifier.startsWith('@');
1837
+ }
1838
+ function getPackageRootSpecifier(importSpecifier) {
1839
+ const pathParts = importSpecifier.split('/');
1840
+ if (isScopedPackage(importSpecifier)) {
1841
+ if (pathParts.length >= 2) {
1842
+ return `${pathParts[0]}/${pathParts[1]}`;
1843
+ }
1844
+ return importSpecifier;
1845
+ }
1846
+ return pathParts[0] ?? importSpecifier;
1847
+ }
1848
+ function getSubpath(importSpecifier, packageRootSpecifier) {
1849
+ if (importSpecifier === packageRootSpecifier) {
1850
+ return null;
1851
+ }
1852
+ if (!importSpecifier.startsWith(`${packageRootSpecifier}/`)) {
1853
+ return null;
1854
+ }
1855
+ return importSpecifier.slice(packageRootSpecifier.length + 1);
1856
+ }
1857
+ function findNearestFileUpwards(startDirectory, fileName) {
1858
+ let currentDirectory = startDirectory;
1859
+ while (currentDirectory.length > 0) {
1860
+ const candidatePath = path.join(currentDirectory, fileName);
1861
+ if (fs.existsSync(candidatePath)) {
1862
+ return candidatePath;
1863
+ }
1864
+ const parentDirectory = path.dirname(currentDirectory);
1865
+ if (parentDirectory === currentDirectory) {
1866
+ break;
1867
+ }
1868
+ currentDirectory = parentDirectory;
1869
+ }
1870
+ return null;
1871
+ }
1872
+ function normalizeModuleSpecifier(moduleSpecifier) {
1873
+ return moduleSpecifier.replaceAll('\\', '/');
1874
+ }
1875
+ function stripKnownExtensions(filePathOrSpecifier) {
1876
+ return filePathOrSpecifier.replace(/\.(d\.ts|ts|tsx|js|jsx|mjs|cjs)$/, '');
1877
+ }
1878
+ function indexExportsSubpath(indexFilePath, subpath) {
1879
+ const fileText = fs.readFileSync(indexFilePath, 'utf8');
1880
+ const sourceFile = ts.createSourceFile(indexFilePath, fileText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
1881
+ const expectedSpecifier = normalizeModuleSpecifier(stripKnownExtensions(`./${subpath}`));
1882
+ const expectedIndexSpecifier = normalizeModuleSpecifier(stripKnownExtensions(`./${subpath}/index`));
1883
+ let isReExportFound = false;
1884
+ sourceFile.forEachChild((astNode) => {
1885
+ if (isReExportFound) {
1886
+ return;
1887
+ }
1888
+ if (ts.isExportDeclaration(astNode)) {
1889
+ const moduleSpecifierNode = astNode.moduleSpecifier;
1890
+ if (moduleSpecifierNode && ts.isStringLiteral(moduleSpecifierNode)) {
1891
+ const exportedSpecifier = normalizeModuleSpecifier(stripKnownExtensions(moduleSpecifierNode.text));
1892
+ if (exportedSpecifier === expectedSpecifier ||
1893
+ exportedSpecifier === expectedIndexSpecifier) {
1894
+ isReExportFound = true;
1895
+ }
1896
+ }
1897
+ }
1898
+ });
1899
+ return isReExportFound;
1900
+ }
1800
1901
 
1801
1902
  const MESSAGE_ID$2 = 'no-href-with-router-link';
1802
1903
  const ERROR_MESSAGE$1 = 'Do not use href and routerLink attributes together on the same element';
@@ -2294,6 +2395,7 @@ const DEFAULT_DECORATORS = ['Component', 'Directive', 'NgModule', 'Pipe'];
2294
2395
  const DEFAULT_EXCEPTIONS = [
2295
2396
  { from: 'TuiTextfieldOptionsDirective', to: 'TuiTextfield' },
2296
2397
  { from: 'TuiPreviewDialogDirective', to: 'TuiPreview' },
2398
+ { from: 'TuiAccountComponent', to: 'TuiAccountComponent' },
2297
2399
  ];
2298
2400
  const createRule$2 = ESLintUtils.RuleCreator((name) => name);
2299
2401
  const rule$1 = createRule$2({
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@taiga-ui/eslint-plugin-experience-next",
3
- "version": "0.383.0",
3
+ "version": "0.385.0",
4
4
  "description": "An ESLint plugin to enforce a consistent code styles across taiga-ui projects",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "devDependencies": {
8
- "@typescript-eslint/rule-tester": "8.50.0",
8
+ "@typescript-eslint/rule-tester": "8.53.0",
9
9
  "glob": "13.0.0"
10
10
  },
11
11
  "peerDependencies": {
12
12
  "@eslint/compat": "^2.0.0",
13
13
  "@eslint/markdown": "^7.5.1",
14
- "@html-eslint/eslint-plugin": "^0.48.0",
14
+ "@html-eslint/eslint-plugin": "^0.52.1",
15
15
  "@html-eslint/parser": "^0.52.0",
16
16
  "@smarttools/eslint-plugin-rxjs": "^1.0.22",
17
17
  "@stylistic/eslint-plugin": "^5.6.1",
18
18
  "@types/glob": "*",
19
- "@typescript-eslint/eslint-plugin": "^8.50.0",
20
- "angular-eslint": "^20.7.0",
19
+ "@typescript-eslint/eslint-plugin": "^8.53.0",
20
+ "angular-eslint": "^21.1.0",
21
21
  "eslint": "^9.39.2",
22
22
  "eslint-config-prettier": "^10.1.7",
23
23
  "eslint-plugin-compat": "^6.0.2",
@@ -27,8 +27,8 @@
27
27
  "eslint-plugin-import": "^2.32.0",
28
28
  "eslint-plugin-jest": "^29.5.0",
29
29
  "eslint-plugin-package-json": "^0.85.0",
30
- "eslint-plugin-perfectionist": "^4.15.1",
31
- "eslint-plugin-playwright": "^2.4.0",
30
+ "eslint-plugin-perfectionist": "^5.3.1",
31
+ "eslint-plugin-playwright": "^2.5.0",
32
32
  "eslint-plugin-prettier": "^5.5.4",
33
33
  "eslint-plugin-promise": "^7.2.1",
34
34
  "eslint-plugin-simple-import-sort": "^12.1.1",
@@ -36,7 +36,7 @@
36
36
  "eslint-plugin-unicorn": "^62.0.0",
37
37
  "eslint-plugin-unused-imports": "^4.3.0",
38
38
  "glob": "*",
39
- "globals": "^16.5.0",
39
+ "globals": "^17.0.0",
40
40
  "typescript-eslint": "^8.50.0"
41
41
  },
42
42
  "publishConfig": {