@taiga-ui/eslint-plugin-experience-next 0.411.0 → 0.413.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
@@ -48,6 +48,7 @@ export default [
48
48
  | no-deep-imports-to-indexed-packages | Disallow deep imports from packages that expose an index.ts next to ng-package.json or package.json | ✅ | 🔧 | |
49
49
  | no-href-with-router-link | Do not use href and routerLink attributes together on the same element | | 🔧 | |
50
50
  | no-implicit-public | Require explicit `public` modifier for class members and parameter properties | ✅ | 🔧 | |
51
+ | no-playwright-empty-fill | Enforce `clear()` over `fill('')` in Playwright tests | ✅ | 🔧 | |
51
52
  | standalone-imports-sort | Sort imports alphabetically | ✅ | 🔧 | |
52
53
  | prefer-deep-imports | Allow deep imports of Taiga UI packages | | 🔧 | |
53
54
  | short-tui-imports | Shorten TuiXxxComponent / TuiYyyDirective in Angular metadata | ✅ | 🔧 | |
package/index.d.ts CHANGED
@@ -37,6 +37,9 @@ declare const plugin: {
37
37
  'no-implicit-public': import("@typescript-eslint/utils/ts-eslint").RuleModule<"implicitPublic", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
38
38
  name: string;
39
39
  };
40
+ 'no-playwright-empty-fill': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useClear", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
41
+ name: string;
42
+ };
40
43
  'object-single-line': import("@typescript-eslint/utils/ts-eslint").RuleModule<"oneLine", [{
41
44
  printWidth: number;
42
45
  }], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
package/index.esm.js CHANGED
@@ -946,6 +946,7 @@ var recommended = defineConfig([
946
946
  files: ['**/*.pw.spec.ts'],
947
947
  extends: [playwright.configs['flat/recommended']],
948
948
  rules: {
949
+ '@taiga-ui/experience-next/no-playwright-empty-fill': 'error',
949
950
  'compat/compat': 'off',
950
951
  'jest/prefer-importing-jest-globals': 'off',
951
952
  'playwright/expect-expect': [
@@ -1242,8 +1243,8 @@ function intersect(a, b) {
1242
1243
  return a.some((type) => origin.has(type));
1243
1244
  }
1244
1245
 
1245
- const createRule$a = ESLintUtils.RuleCreator((name) => name);
1246
- var classPropertyNaming = createRule$a({
1246
+ const createRule$b = ESLintUtils.RuleCreator((name) => name);
1247
+ var classPropertyNaming = createRule$b({
1247
1248
  create(context, [configs]) {
1248
1249
  const parserServices = ESLintUtils.getParserServices(context);
1249
1250
  const typeChecker = parserServices.program.getTypeChecker();
@@ -1412,9 +1413,9 @@ function isExternalPureTuple(typeChecker, type) {
1412
1413
  return typeArgs.every((item) => isClassType(item));
1413
1414
  }
1414
1415
 
1415
- const createRule$9 = ESLintUtils.RuleCreator((name) => name);
1416
+ const createRule$a = ESLintUtils.RuleCreator((name) => name);
1416
1417
  const MESSAGE_ID$5 = 'spreadArrays';
1417
- var flatExports = createRule$9({
1418
+ var flatExports = createRule$a({
1418
1419
  create(context) {
1419
1420
  const parserServices = ESLintUtils.getParserServices(context);
1420
1421
  const typeChecker = parserServices.program.getTypeChecker();
@@ -1541,8 +1542,8 @@ var flatExports = createRule$9({
1541
1542
 
1542
1543
  const MESSAGE_ID$4 = 'invalid-injection-token-description';
1543
1544
  const ERROR_MESSAGE$3 = "InjectionToken's description should contain token's name";
1544
- const createRule$8 = ESLintUtils.RuleCreator((name) => name);
1545
- const rule$5 = createRule$8({
1545
+ const createRule$9 = ESLintUtils.RuleCreator((name) => name);
1546
+ const rule$6 = createRule$9({
1546
1547
  create(context) {
1547
1548
  return {
1548
1549
  'NewExpression[callee.name="InjectionToken"]'(node) {
@@ -1600,8 +1601,8 @@ const DEFAULT_OPTIONS = {
1600
1601
  importDeclaration: '^@taiga-ui*',
1601
1602
  projectName: String.raw `(?<=^@taiga-ui/)([-\w]+)`,
1602
1603
  };
1603
- const createRule$7 = ESLintUtils.RuleCreator((name) => name);
1604
- const rule$4 = createRule$7({
1604
+ const createRule$8 = ESLintUtils.RuleCreator((name) => name);
1605
+ const rule$5 = createRule$8({
1605
1606
  create(context) {
1606
1607
  const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
1607
1608
  const isDeepImport = (source) => !!source && new RegExp(deepImport, 'g').test(source);
@@ -1680,13 +1681,13 @@ const rule$4 = createRule$7({
1680
1681
  name: 'no-deep-imports',
1681
1682
  });
1682
1683
 
1683
- const createRule$6 = ESLintUtils.RuleCreator((name) => name);
1684
+ const createRule$7 = ESLintUtils.RuleCreator((name) => name);
1684
1685
  const resolveCacheByOptions = new WeakMap();
1685
1686
  const nearestFileUpCache = new Map();
1686
1687
  const markerCache = new Map();
1687
1688
  const indexFileCache = new Map();
1688
1689
  const indexExportsCache = new Map();
1689
- var noDeepImportsToIndexedPackages = createRule$6({
1690
+ var noDeepImportsToIndexedPackages = createRule$7({
1690
1691
  create(context) {
1691
1692
  const parserServices = ESLintUtils.getParserServices(context);
1692
1693
  const program = parserServices.program;
@@ -1929,8 +1930,8 @@ const config = {
1929
1930
  },
1930
1931
  };
1931
1932
 
1932
- const createRule$5 = ESLintUtils.RuleCreator((name) => name);
1933
- const rule$3 = createRule$5({
1933
+ const createRule$6 = ESLintUtils.RuleCreator((name) => name);
1934
+ const rule$4 = createRule$6({
1934
1935
  create(context) {
1935
1936
  const checkImplicitPublic = (node) => {
1936
1937
  const classRef = getClass(node);
@@ -2002,6 +2003,95 @@ function getClass(node) {
2002
2003
  return getClass(node.parent);
2003
2004
  }
2004
2005
 
2006
+ const createRule$5 = ESLintUtils.RuleCreator((name) => name);
2007
+ const rule$3 = createRule$5({
2008
+ create(context) {
2009
+ const services = ESLintUtils.getParserServices(context);
2010
+ const checker = services.program.getTypeChecker();
2011
+ return {
2012
+ CallExpression(node) {
2013
+ const callee = node.callee;
2014
+ if (callee.type !== AST_NODE_TYPES$1.MemberExpression) {
2015
+ return;
2016
+ }
2017
+ const property = callee.property;
2018
+ if (property.type !== AST_NODE_TYPES$1.Identifier ||
2019
+ property.name !== 'fill' ||
2020
+ node.arguments.length !== 1) {
2021
+ return;
2022
+ }
2023
+ const [argument] = node.arguments;
2024
+ if (!argument || !isEmptyString(argument)) {
2025
+ return;
2026
+ }
2027
+ const objectExpression = callee.object;
2028
+ const tsNode = services.esTreeNodeToTSNodeMap.get(objectExpression);
2029
+ const type = checker.getTypeAtLocation(tsNode);
2030
+ if (!isPlaywrightLocator(type, checker)) {
2031
+ return;
2032
+ }
2033
+ context.report({
2034
+ fix(fixer) {
2035
+ const objectText = context.sourceCode.getText(objectExpression);
2036
+ return fixer.replaceText(node, `${objectText}.clear()`);
2037
+ },
2038
+ messageId: 'useClear',
2039
+ node,
2040
+ });
2041
+ },
2042
+ };
2043
+ },
2044
+ defaultOptions: [],
2045
+ meta: {
2046
+ docs: {
2047
+ description: "Enforce Playwright clear() instead of fill('') for emptying fields",
2048
+ },
2049
+ fixable: 'code',
2050
+ messages: { useClear: "Use clear() instead of fill('') when emptying a field" },
2051
+ schema: [],
2052
+ type: 'suggestion',
2053
+ },
2054
+ name: 'no-playwright-empty-fill',
2055
+ });
2056
+ function isEmptyString(node) {
2057
+ return ((node.type === AST_NODE_TYPES$1.Literal && node.value === '') ||
2058
+ (node.type === AST_NODE_TYPES$1.TemplateLiteral &&
2059
+ node.expressions.length === 0 &&
2060
+ node.quasis.length === 1 &&
2061
+ node.quasis[0]?.value.cooked === ''));
2062
+ }
2063
+ function isPlaywrightLocator(type, checker) {
2064
+ if (isPlaywrightLocatorType(type)) {
2065
+ return true;
2066
+ }
2067
+ const locatorProp = type.getProperty('locator');
2068
+ if (!locatorProp) {
2069
+ return false;
2070
+ }
2071
+ const decl = locatorProp.valueDeclaration ?? locatorProp.declarations?.[0];
2072
+ if (!decl) {
2073
+ return false;
2074
+ }
2075
+ const locatorType = checker.getTypeOfSymbolAtLocation(locatorProp, decl);
2076
+ return isPlaywrightLocatorType(locatorType);
2077
+ }
2078
+ function isPlaywrightLocatorType(type) {
2079
+ if (type.isUnionOrIntersection()) {
2080
+ return type.types.some(isPlaywrightLocatorType);
2081
+ }
2082
+ const symbol = type.getSymbol() ?? type.aliasSymbol;
2083
+ if (symbol?.getName() !== 'Locator') {
2084
+ return false;
2085
+ }
2086
+ const declarations = symbol.getDeclarations() ?? [];
2087
+ return declarations.some((decl) => {
2088
+ const fileName = decl.getSourceFile().fileName.replaceAll('\\', '/');
2089
+ return (fileName.includes('/node_modules/playwright/') ||
2090
+ fileName.includes('/node_modules/@playwright/test/') ||
2091
+ fileName.includes('/node_modules/playwright-core/'));
2092
+ });
2093
+ }
2094
+
2005
2095
  const createRule$4 = ESLintUtils.RuleCreator((name) => name);
2006
2096
  const rule$2 = createRule$4({
2007
2097
  create(context, [{ printWidth }]) {
@@ -3105,11 +3195,12 @@ const plugin = {
3105
3195
  'class-property-naming': classPropertyNaming,
3106
3196
  'decorator-key-sort': config$1,
3107
3197
  'flat-exports': flatExports,
3108
- 'injection-token-description': rule$5,
3109
- 'no-deep-imports': rule$4,
3198
+ 'injection-token-description': rule$6,
3199
+ 'no-deep-imports': rule$5,
3110
3200
  'no-deep-imports-to-indexed-packages': noDeepImportsToIndexedPackages,
3111
3201
  'no-href-with-router-link': config,
3112
- 'no-implicit-public': rule$3,
3202
+ 'no-implicit-public': rule$4,
3203
+ 'no-playwright-empty-fill': rule$3,
3113
3204
  'object-single-line': rule$2,
3114
3205
  'prefer-deep-imports': preferDeepImports,
3115
3206
  'short-tui-imports': rule$1,
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@taiga-ui/eslint-plugin-experience-next",
3
- "version": "0.411.0",
3
+ "version": "0.413.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.54.0",
9
- "glob": "13.0.1"
8
+ "@typescript-eslint/rule-tester": "8.55.0",
9
+ "glob": "13.0.2"
10
10
  },
11
11
  "peerDependencies": {
12
12
  "@eslint/compat": "^2.0.2",
@@ -16,7 +16,7 @@
16
16
  "@smarttools/eslint-plugin-rxjs": "^1.0.22",
17
17
  "@stylistic/eslint-plugin": "^5.8.0",
18
18
  "@types/glob": "*",
19
- "@typescript-eslint/eslint-plugin": "^8.54.0",
19
+ "@typescript-eslint/eslint-plugin": "^8.55.0",
20
20
  "angular-eslint": "^20.7.0",
21
21
  "eslint": "^9.39.2",
22
22
  "eslint-config-prettier": "^10.1.7",
@@ -33,12 +33,12 @@
33
33
  "eslint-plugin-promise": "^7.2.1",
34
34
  "eslint-plugin-regexp": "^3.0.0",
35
35
  "eslint-plugin-simple-import-sort": "^12.1.1",
36
- "eslint-plugin-sonarjs": "^3.0.6",
36
+ "eslint-plugin-sonarjs": "^3.0.7",
37
37
  "eslint-plugin-unicorn": "^62.0.0",
38
38
  "eslint-plugin-unused-imports": "^4.4.1",
39
39
  "glob": "*",
40
40
  "globals": "^17.3.0",
41
- "typescript-eslint": "^8.54.0"
41
+ "typescript-eslint": "^8.55.0"
42
42
  },
43
43
  "publishConfig": {
44
44
  "access": "public"
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const rule: ESLintUtils.RuleModule<"useClear", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default rule;