eslint-plugin-th-rules 1.17.1 → 1.19.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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.19.0](https://github.com/tomerh2001/eslint-plugin-th-rules/compare/v1.18.0...v1.19.0) (2026-01-05)
2
+
3
+
4
+ ### Features
5
+
6
+ * Added force for jsdoc in recommended configs ([6729b7a](https://github.com/tomerh2001/eslint-plugin-th-rules/commit/6729b7a4804f8a63356aff9e2ceef57119b7075d))
7
+
8
+ # [1.18.0](https://github.com/tomerh2001/eslint-plugin-th-rules/compare/v1.17.1...v1.18.0) (2026-01-05)
9
+
10
+
11
+ ### Features
12
+
13
+ * add 'schemas-in-schemas-file' rule to enforce Zod schema declarations in dedicated files ([92b54a0](https://github.com/tomerh2001/eslint-plugin-th-rules/commit/92b54a0b9ca4197c4e92c268d7d0cf0f8efc83cf))
14
+
1
15
  ## [1.17.1](https://github.com/tomerh2001/eslint-plugin-th-rules/compare/v1.17.0...v1.17.1) (2026-01-05)
2
16
 
3
17
 
package/README.md CHANGED
@@ -12,16 +12,19 @@ This repository contains custom ESLint rules to enhance code quality and consist
12
12
 
13
13
  💼 Configurations enabled in.\
14
14
  ✅ Set in the `recommended` configuration.\
15
+ ⚛️ Set in the `recommended-react` configuration.\
16
+ 🟦 Set in the `recommended-typescript` configuration.\
15
17
  🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).
16
18
 
17
- | Name                  | Description | 💼 | 🔧 |
18
- | :----------------------------------------------------------- | :---------------------------------------------------------------------------------------- | :--------------------------------------------------------------- | :- |
19
- | [no-comments](docs/rules/no-comments.md) | Disallow comments except for specified allowed patterns. | ✅ ![badge-recommended-react][] ![badge-recommended-typescript][] | 🔧 |
20
- | [no-default-export](docs/rules/no-default-export.md) | Convert unnamed default exports to named default exports based on the file name. | ✅ ![badge-recommended-react][] ![badge-recommended-typescript][] | 🔧 |
21
- | [no-destructuring](docs/rules/no-destructuring.md) | Disallow destructuring that does not meet certain conditions | ✅ ![badge-recommended-react][] ![badge-recommended-typescript][] | |
22
- | [styles-in-styles-file](docs/rules/styles-in-styles-file.md) | Require React-Native StyleSheet.create(...) to be placed in a .styles.ts/.styles.tsx file | ✅ ![badge-recommended-react][] ![badge-recommended-typescript][] | |
23
- | [top-level-functions](docs/rules/top-level-functions.md) | Require all top-level functions to be named/regular functions. | ✅ ![badge-recommended-react][] ![badge-recommended-typescript][] | 🔧 |
24
- | [types-in-dts](docs/rules/types-in-dts.md) | Require TypeScript type declarations (type/interface/enum) to be placed in .d.ts files | ✅ ![badge-recommended-react][] ![badge-recommended-typescript][] | |
19
+ | Name                    | Description | 💼 | 🔧 |
20
+ | :--------------------------------------------------------------- | :------------------------------------------------------------------------------------- | :------ | :- |
21
+ | [no-comments](docs/rules/no-comments.md) | Disallow comments except for specified allowed patterns. | ✅ ⚛️ 🟦 | 🔧 |
22
+ | [no-default-export](docs/rules/no-default-export.md) | Convert unnamed default exports to named default exports based on the file name. | ✅ ⚛️ 🟦 | 🔧 |
23
+ | [no-destructuring](docs/rules/no-destructuring.md) | Disallow destructuring that does not meet certain conditions | ✅ ⚛️ 🟦 | |
24
+ | [schemas-in-schemas-file](docs/rules/schemas-in-schemas-file.md) | Require Zod schema declarations to be placed in a .schemas.ts file | ✅ ⚛️ 🟦 | |
25
+ | [styles-in-styles-file](docs/rules/styles-in-styles-file.md) | Require React-Native StyleSheet.create(...) to be placed in a .styles.ts file | ✅ ⚛️ 🟦 | |
26
+ | [top-level-functions](docs/rules/top-level-functions.md) | Require all top-level functions to be named/regular functions. | ✅ ⚛️ 🟦 | 🔧 |
27
+ | [types-in-dts](docs/rules/types-in-dts.md) | Require TypeScript type declarations (type/interface/enum) to be placed in .d.ts files | ✅ ⚛️ 🟦 | |
25
28
 
26
29
  <!-- end auto-generated rules list -->
27
30
 
@@ -1,6 +1,8 @@
1
- # Disallow comments except for specified allowed patterns (`th-rules/no-comments`)
1
+ # th-rules/no-comments
2
2
 
3
- 💼 This rule is enabled in the following configs: ✅ `recommended`, `recommended-react`, `recommended-typescript`.
3
+ 📝 Disallow comments except for specified allowed patterns.
4
+
5
+ 💼 This rule is enabled in the following configs: ✅ `recommended`, ⚛️ `recommended-react`, 🟦 `recommended-typescript`.
4
6
 
5
7
  🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
6
8
 
@@ -1,6 +1,8 @@
1
- # Convert unnamed default exports to named default exports based on the file name (`th-rules/no-default-export`)
1
+ # th-rules/no-default-export
2
2
 
3
- 💼 This rule is enabled in the following configs: `recommended`, `recommended-react`, `recommended-typescript`.
3
+ 📝 Convert unnamed default exports to named default exports based on the file name.
4
+
5
+ 💼 This rule is enabled in the following configs: ✅ `recommended`, ⚛️ `recommended-react`, 🟦 `recommended-typescript`.
4
6
 
5
7
  🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
6
8
 
@@ -1,6 +1,8 @@
1
- # Disallow destructuring that does not meet certain conditions (`th-rules/no-destructuring`)
1
+ # th-rules/no-destructuring
2
2
 
3
- 💼 This rule is enabled in the following configs: ✅ `recommended`, `recommended-react`, `recommended-typescript`.
3
+ 📝 Disallow destructuring that does not meet certain conditions.
4
+
5
+ 💼 This rule is enabled in the following configs: ✅ `recommended`, ⚛️ `recommended-react`, 🟦 `recommended-typescript`.
4
6
 
5
7
  <!-- end auto-generated rule header -->
6
8
 
@@ -0,0 +1,170 @@
1
+ # th-rules/schemas-in-schemas-file
2
+
3
+ 📝 Require Zod schema declarations to be placed in a .schemas.ts file.
4
+
5
+ 💼 This rule is enabled in the following configs: ✅ `recommended`, ⚛️ `recommended-react`, 🟦 `recommended-typescript`.
6
+
7
+ <!-- end auto-generated rule header -->
8
+
9
+ 💼 This rule is enabled in the following configs: ✅ `recommended`, `recommended-react`, `recommended-typescript`.
10
+
11
+ &lt;!-- end auto-generated rule header --&gt;
12
+
13
+ This rule enforces that Zod schema **declarations** are defined in dedicated schema files (by default: `.schemas.ts` / `.schemas.tsx`), rather than inside implementation/component files.
14
+
15
+ The intent is to keep business logic and UI code focused, while consolidating validation/contract definitions into a predictable location.
16
+
17
+ ## Rationale
18
+
19
+ Keeping Zod schemas in dedicated files:
20
+ - reduces noise in implementation files (especially components and services),
21
+ - encourages reuse of validation contracts across the codebase,
22
+ - improves review quality by keeping diffs focused (schema changes vs logic changes),
23
+ - standardizes file layout in larger projects and monorepos.
24
+
25
+ ## What the rule reports
26
+
27
+ The rule reports Zod “schema builder” calls (e.g. `z.object(...)`, `z.string()`, `z.enum(...)`, `z.coerce.number()`, etc.) in files that do **not** match one of the allowed suffixes.
28
+
29
+ By default, it identifies Zod usage by tracking imports from `'zod'` (e.g. `import { z } from 'zod'`, `import { z as zod } from 'zod'`, `import * as zod from 'zod'`).
30
+
31
+ ## Examples
32
+
33
+ ### ❌ Incorrect
34
+
35
+ ```ts
36
+ // ArticleCard.tsx
37
+ import {z} from 'zod';
38
+
39
+ const articleSchema = z.object({
40
+ id: z.string(),
41
+ title: z.string().min(1),
42
+ });
43
+ ```
44
+
45
+ ```ts
46
+ // user.ts
47
+ import {z as zod} from 'zod';
48
+
49
+ export const userSchema = zod.object({
50
+ email: zod.string().email(),
51
+ });
52
+ ```
53
+
54
+ ### ✅ Correct
55
+
56
+ ```ts
57
+ // ArticleCard.schemas.ts
58
+ import {z} from 'zod';
59
+
60
+ export const articleSchema = z.object({
61
+ id: z.string(),
62
+ title: z.string().min(1),
63
+ });
64
+ ```
65
+
66
+ ```ts
67
+ // ArticleCard.tsx
68
+ import {articleSchema} from './ArticleCard.schemas';
69
+
70
+ export function ArticleCard() {
71
+ // articleSchema is used here (validation, inference, etc.)
72
+ return null;
73
+ }
74
+ ```
75
+
76
+ ### Allowed usage (always allowed)
77
+
78
+ Using schemas and type inference is allowed anywhere:
79
+
80
+ ```ts
81
+ import type {z} from 'zod';
82
+ import {articleSchema} from './ArticleCard.schemas';
83
+
84
+ export type Article = z.infer&lt;typeof articleSchema&gt;;
85
+ ```
86
+
87
+ ## Options
88
+
89
+ &lt;!-- begin auto-generated rule options list --&gt;
90
+
91
+ | Name | Type |
92
+ | :----------------- | :------- |
93
+ | `allowedSuffixes` | String[] |
94
+ | `allowInTests` | Boolean |
95
+ | `onlyWhenAssigned` | Boolean |
96
+
97
+ &lt;!-- end auto-generated rule options list --&gt;
98
+
99
+ ### `allowedSuffixes`
100
+
101
+ An array of filename suffixes that are allowed to contain Zod schema declarations.
102
+
103
+ Default:
104
+ - `.schemas.ts`
105
+ - `.schemas.tsx`
106
+
107
+ Example:
108
+
109
+ ```json
110
+ {
111
+ "rules": {
112
+ "th-rules/schemas-in-schemas-file": ["error", {
113
+ "allowedSuffixes": [".schemas.ts", ".schemas.tsx", ".validation.ts"]
114
+ }]
115
+ }
116
+ }
117
+ ```
118
+
119
+ ### `allowInTests`
120
+
121
+ When set to `true`, the rule allows Zod schema declarations in common test file patterns (e.g. `*.test.ts`, `*.spec.ts`, `*.test.tsx`, `*.spec.tsx`).
122
+
123
+ Default: `false`
124
+
125
+ Example:
126
+
127
+ ```json
128
+ {
129
+ "rules": {
130
+ "th-rules/schemas-in-schemas-file": ["error", {
131
+ "allowInTests": true
132
+ }]
133
+ }
134
+ }
135
+ ```
136
+
137
+ ### `onlyWhenAssigned`
138
+
139
+ When set to `true`, the rule reports only Zod schema builder calls that are assigned to a variable (or assigned via `=`), for example:
140
+
141
+ ```ts
142
+ const userSchema = z.object({ ... });
143
+ userSchema = z.object({ ... });
144
+ ```
145
+
146
+ If set to `false` (default), the rule reports any Zod schema builder call found in a non-allowed file, including inline schemas:
147
+
148
+ ```ts
149
+ foo(z.object({ ... }));
150
+ ```
151
+
152
+ Default: `false`
153
+
154
+ Example:
155
+
156
+ ```json
157
+ {
158
+ "rules": {
159
+ "th-rules/schemas-in-schemas-file": ["error", {
160
+ "onlyWhenAssigned": true
161
+ }]
162
+ }
163
+ }
164
+ ```
165
+
166
+ ## Notes
167
+
168
+ - This rule is filename-based; it enforces a convention rather than performing semantic analysis.
169
+ - The rule does not auto-fix, as moving schemas across files safely (and updating imports) is not generally safe to automate.
170
+ - If you define Zod builders through wrappers (e.g. `createSchema(...)` that internally calls Zod), this rule will not detect those unless extended to match your helper patterns.
@@ -1,11 +1,12 @@
1
- <pre>
2
- # Require React-Native StyleSheet.create(...) to be placed in a .styles.ts/.styles.tsx file (`th-rules/styles-in-styles-file`)
1
+ # th-rules/styles-in-styles-file
3
2
 
4
- 💼 This rule is enabled in the following configs: `recommended`, `recommended-react`, `recommended-typescript`.
3
+ 📝 Require React-Native StyleSheet.create(...) to be placed in a .styles.ts file.
4
+
5
+ 💼 This rule is enabled in the following configs: ✅ `recommended`, ⚛️ `recommended-react`, 🟦 `recommended-typescript`.
5
6
 
6
7
  <!-- end auto-generated rule header -->
7
8
 
8
- This rule enforces that React-Native stylesheet declarations created via `StyleSheet.create(...)` live in a dedicated styles file, typically ending with `.styles.ts` or `.styles.tsx`.
9
+ This rule enforces that React-Native stylesheet declarations created via `StyleSheet.create(...)` live in a dedicated styles file, typically ending with `.styles.ts`.
9
10
 
10
11
  In practice, this prevents implementation/component files from containing large style objects, and encourages consistent separation of concerns.
11
12
 
@@ -19,7 +20,7 @@ Keeping styles in dedicated files:
19
20
 
20
21
  ## What the rule reports
21
22
 
22
- The rule reports any `StyleSheet.create(...)` call in files whose names do **not** match one of the allowed suffixes (by default, `.styles.ts` and `.styles.tsx`).
23
+ The rule reports any `StyleSheet.create(...)` call in files whose names do **not** match one of the allowed suffixes (by default, `.styles.ts`).
23
24
 
24
25
  Optionally, it can also report `StyleSheet.compose(...)` calls.
25
26
 
@@ -100,7 +101,6 @@ An array of filename suffixes that are allowed to contain `StyleSheet.create(...
100
101
 
101
102
  Default:
102
103
  - `.styles.ts`
103
- - `.styles.tsx`
104
104
 
105
105
  Example:
106
106
 
@@ -108,7 +108,7 @@ Example:
108
108
  {
109
109
  "rules": {
110
110
  "th-rules/styles-in-styles-file": ["error", {
111
- "allowedSuffixes": [".styles.ts", ".styles.tsx", ".native-styles.ts"]
111
+ "allowedSuffixes": [".styles.ts", ".native-styles.ts"]
112
112
  }]
113
113
  }
114
114
  }
@@ -139,4 +139,3 @@ Example:
139
139
  - other styling systems (e.g., styled-components, Tamagui, Emotion),
140
140
  - calls to other `StyleSheet.*` helpers (e.g., `flatten`, `hairlineWidth`).
141
141
  - The rule is filename-based. If ESLint is invoked with `<input>` (stdin), the rule will treat it as not being an allowed styles file.
142
- </pre>
@@ -1,6 +1,8 @@
1
- # Require all top-level functions to be named/regular functions (`th-rules/top-level-functions`)
1
+ # th-rules/top-level-functions
2
2
 
3
- 💼 This rule is enabled in the following configs: ✅ `recommended`, `recommended-react`, `recommended-typescript`.
3
+ 📝 Require all top-level functions to be named/regular functions.
4
+
5
+ 💼 This rule is enabled in the following configs: ✅ `recommended`, ⚛️ `recommended-react`, 🟦 `recommended-typescript`.
4
6
 
5
7
  🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
6
8
 
@@ -1,7 +1,8 @@
1
- <pre>
2
- # Require TypeScript type declarations (type/interface/enum) to be placed in .d.ts files (`th-rules/types-in-dts`)
1
+ # th-rules/types-in-dts
3
2
 
4
- 💼 This rule is enabled in the following configs: `recommended`, `recommended-react`, `recommended-typescript`.
3
+ 📝 Require TypeScript type declarations (type/interface/enum) to be placed in .d.ts files.
4
+
5
+ 💼 This rule is enabled in the following configs: ✅ `recommended`, ⚛️ `recommended-react`, 🟦 `recommended-typescript`.
5
6
 
6
7
  <!-- end auto-generated rule header -->
7
8
 
@@ -109,4 +110,3 @@ This can be helpful in codebases that rely on runtime enums while still wanting
109
110
  - This rule does not enforce how types are imported or re-exported.
110
111
  - It does not attempt to auto-fix violations, as moving declarations across files is not safe to do automatically.
111
112
  - The rule operates purely at the syntax level and does not require type information.
112
- </pre>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-th-rules",
3
- "version": "1.17.1",
3
+ "version": "1.19.0",
4
4
  "description": "A List of custom ESLint rules created by Tomer Horowitz",
5
5
  "keywords": [
6
6
  "eslint",
@@ -15,46 +15,46 @@
15
15
  "lint:eslint-docs": "npm-run-all \"update:eslint-docs -- --check\"",
16
16
  "lint:js": "eslint .",
17
17
  "test": "xo && mocha tests --recursive",
18
- "update:eslint-docs": "eslint-doc-generator"
18
+ "update:eslint-docs": "eslint-doc-generator --config-emoji \"recommended,✅\" --config-emoji \"recommended-react,⚛️\" --config-emoji \"recommended-typescript,🟦\""
19
19
  },
20
20
  "dependencies": {
21
- "@babel/eslint-parser": "^7.25.9",
21
+ "@babel/eslint-parser": "^7.28.5",
22
22
  "eslint-config-jsdoc": "^15.4.0",
23
- "eslint-config-xo": "^0.46.0",
24
- "eslint-config-xo-react": "^0.27.0",
25
- "eslint-plugin-jsdoc": "^50.6.1",
26
- "eslint-plugin-react": "^7.37.3",
27
- "eslint-plugin-react-hooks": "^5.1.0",
23
+ "eslint-config-xo": "^0.49.0",
24
+ "eslint-config-xo-react": "^0.29.0",
25
+ "eslint-plugin-jsdoc": "^61.5.0",
26
+ "eslint-plugin-react": "^7.37.5",
27
+ "eslint-plugin-react-hooks": "^7.0.1",
28
28
  "eslint-plugin-react-native": "^5.0.0",
29
29
  "eslint-plugin-security": "^3.0.1",
30
- "eslint-plugin-sonarjs": "^3.0.1",
30
+ "eslint-plugin-sonarjs": "^3.0.5",
31
31
  "requireindex": "^1.2.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@codedependant/semantic-release-docker": "^5.1.0",
35
- "@eslint/js": "^9.17.0",
34
+ "@codedependant/semantic-release-docker": "^5.1.1",
35
+ "@eslint/js": "^9.39.2",
36
36
  "@semantic-release/changelog": "^6.0.3",
37
- "@semantic-release/commit-analyzer": "^13.0.0",
37
+ "@semantic-release/commit-analyzer": "^13.0.1",
38
38
  "@semantic-release/git": "^10.0.1",
39
- "@semantic-release/github": "^11.0.1",
40
- "@semantic-release/npm": "^12.0.1",
39
+ "@semantic-release/github": "^12.0.2",
40
+ "@semantic-release/npm": "^13.1.3",
41
41
  "@semantic-release/release-notes-generator": "^14.0.2",
42
42
  "@types/eslint-plugin-security": "^3.0.0",
43
- "@types/eslint__js": "^8.42.3",
43
+ "@types/eslint__js": "^9.14.0",
44
44
  "@types/requireindex": "^1.2.4",
45
45
  "@types/xo": "^0.39.9",
46
46
  "bun-types": "latest",
47
47
  "eslint": "^9.17.0",
48
- "eslint-doc-generator": "^2.0.2",
49
- "eslint-plugin-eslint-plugin": "^6.4.0",
48
+ "eslint-doc-generator": "^3.0.2",
49
+ "eslint-plugin-eslint-plugin": "^7.2.0",
50
50
  "eslint-plugin-node": "^11.1.0",
51
- "eslint-plugin-sonarjs": "^3.0.1",
52
- "eslint-plugin-th-rules": "1.15.6",
53
- "eslint-plugin-unicorn": "^56.0.1",
54
- "mocha": "^11.0.1",
51
+ "eslint-plugin-sonarjs": "^3.0.5",
52
+ "eslint-plugin-th-rules": "1.18.0",
53
+ "eslint-plugin-unicorn": "^62.0.0",
54
+ "mocha": "^11.7.5",
55
55
  "npm-run-all": "^4.1.5",
56
- "typescript": "^5.7.2",
57
- "typescript-eslint": "^8.19.0",
56
+ "typescript": "^5.9.3",
57
+ "typescript-eslint": "^8.51.0",
58
58
  "xo": "^0.60.0"
59
59
  },
60
60
  "peerDependencies": {
package/src/index.js CHANGED
@@ -9,10 +9,12 @@ const configs = {
9
9
  plugins: [
10
10
  'th-rules',
11
11
  'sonarjs',
12
+ 'jsdoc',
12
13
  ],
13
14
  extends: [
14
15
  'plugin:sonarjs/recommended-legacy',
15
16
  'plugin:security/recommended-legacy',
17
+ 'plugin:jsdoc/recommended',
16
18
  ],
17
19
  rules: {
18
20
  'th-rules/no-destructuring': 'error',
@@ -20,7 +22,9 @@ const configs = {
20
22
  'th-rules/no-comments': 'error',
21
23
  'th-rules/top-level-functions': 'error',
22
24
  'th-rules/styles-in-styles-file': 'error',
25
+ 'th-rules/schemas-in-schemas-file': 'error',
23
26
  'th-rules/types-in-dts': 'error',
27
+
24
28
  'unicorn/prefer-module': 'warn',
25
29
  'unicorn/filename-case': 'off',
26
30
  'unicorn/no-array-callback-reference': 'off',
@@ -52,7 +56,10 @@ for (const configName of Object.keys(configs)) {
52
56
  extends: [
53
57
  'plugin:@typescript-eslint/strict-type-checked',
54
58
  'plugin:@typescript-eslint/stylistic-type-checked',
55
- ...configs[configName].extends,
59
+
60
+ ...configs[configName].extends
61
+ .filter(x => x !== 'plugin:jsdoc/recommended'),
62
+ 'plugin:jsdoc/recommended-typescript',
56
63
  ],
57
64
  rules: {
58
65
  ...configs[configName].rules,
@@ -68,13 +75,13 @@ for (const configName of Object.keys(configs)) {
68
75
  '@typescript-eslint/no-unsafe-argument': 'off',
69
76
  },
70
77
  };
78
+
71
79
  configs[`${configName}-react`] = {
72
80
  ...configs[configName],
73
81
  extends: [
74
82
  'plugin:react/recommended',
75
83
  'plugin:react-hooks/recommended',
76
84
  ...configs[configName].extends,
77
-
78
85
  ],
79
86
  rules: {
80
87
  ...configs[configName].rules,
@@ -0,0 +1,191 @@
1
+ const meta = {
2
+ type: 'problem',
3
+ docs: {
4
+ description: 'Require Zod schema declarations to be placed in a .schemas.ts file',
5
+ category: 'Stylistic Issues',
6
+ recommended: false,
7
+ url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/schemas-in-schemas-file.md',
8
+ },
9
+ schema: [
10
+ {
11
+ type: 'object',
12
+ properties: {
13
+ allowedSuffixes: {
14
+ type: 'array',
15
+ items: {type: 'string', minLength: 1},
16
+ minItems: 1,
17
+ },
18
+ /**
19
+ * If true, only report when the Zod call is assigned to a variable
20
+ * (e.g. const userSchema = z.object(...)).
21
+ * If false, report any Zod schema-building call expression.
22
+ */
23
+ onlyWhenAssigned: {type: 'boolean'},
24
+
25
+ /**
26
+ * If true, allow Zod calls inside *.test.* / *.spec.* files.
27
+ */
28
+ allowInTests: {type: 'boolean'},
29
+ },
30
+ additionalProperties: false,
31
+ },
32
+ ],
33
+ messages: {
34
+ moveSchema:
35
+ 'Zod schemas must be defined in a dedicated schemas file ({{suffixes}}).',
36
+ },
37
+ };
38
+
39
+ function create(context) {
40
+ const options = context.options?.[0] ?? {};
41
+ const allowedSuffixes = options.allowedSuffixes ?? ['.schemas.ts'];
42
+ const onlyWhenAssigned = Boolean(options.onlyWhenAssigned);
43
+ const allowInTests = Boolean(options.allowInTests);
44
+
45
+ /** @type {Set<string>} */
46
+ const zodIdentifiers = new Set();
47
+
48
+ function filenameAllowed(filename) {
49
+ if (!filename || filename === '<input>') {
50
+ return false;
51
+ }
52
+
53
+ if (allowInTests
54
+ && /\.(test|spec)\.[jt]sx?$/.test(filename)) {
55
+ return true;
56
+ }
57
+
58
+ return allowedSuffixes.some(suffix => filename.endsWith(suffix));
59
+ }
60
+
61
+ function isZodModuleImport(node) {
62
+ return node?.source?.type === 'Literal' && node.source.value === 'zod';
63
+ }
64
+
65
+ function collectZodIdentifiersFromImport(node) {
66
+ if (!isZodModuleImport(node)) {
67
+ return;
68
+ }
69
+
70
+ for (const spec of node.specifiers ?? []) {
71
+ if (spec.type === 'ImportSpecifier') {
72
+ const imported = spec.imported;
73
+ const local = spec.local;
74
+
75
+ if (imported?.type === 'Identifier' && imported.name === 'z' && local?.type === 'Identifier') {
76
+ zodIdentifiers.add(local.name);
77
+ }
78
+ }
79
+
80
+ if (spec.type === 'ImportNamespaceSpecifier' && spec.local?.type === 'Identifier') {
81
+ zodIdentifiers.add(spec.local.name);
82
+ }
83
+ }
84
+ }
85
+
86
+ function isZodBuilderCall(node) {
87
+ const callee = node?.callee;
88
+ if (!callee || callee.type !== 'MemberExpression') {
89
+ return false;
90
+ }
91
+
92
+ if (callee.computed) {
93
+ return false;
94
+ }
95
+
96
+ const object = callee.object;
97
+ const property = callee.property;
98
+
99
+ if (object?.type !== 'Identifier') {
100
+ return false;
101
+ }
102
+
103
+ if (!zodIdentifiers.has(object.name)) {
104
+ return false;
105
+ }
106
+
107
+ return property?.type === 'Identifier';
108
+ }
109
+
110
+ function isZodChainedBuilderCall(node) {
111
+ const callee = node?.callee;
112
+ if (!callee || callee.type !== 'MemberExpression') {
113
+ return false;
114
+ }
115
+
116
+ if (callee.computed) {
117
+ return false;
118
+ }
119
+
120
+ let current = callee.object;
121
+ while (current && current.type === 'MemberExpression' && !current.computed) {
122
+ current = current.object;
123
+ }
124
+
125
+ return current?.type === 'Identifier' && zodIdentifiers.has(current.name);
126
+ }
127
+
128
+ function getAssignmentTargetName(callNode) {
129
+ const p = callNode.parent;
130
+
131
+ if (p?.type === 'VariableDeclarator') {
132
+ if (p.id?.type === 'Identifier') {
133
+ return p.id.name;
134
+ }
135
+
136
+ return null;
137
+ }
138
+
139
+ if (p?.type === 'AssignmentExpression') {
140
+ if (p.left?.type === 'Identifier') {
141
+ return p.left.name;
142
+ }
143
+
144
+ return null;
145
+ }
146
+
147
+ return null;
148
+ }
149
+
150
+ function report(node) {
151
+ const filename = context.getFilename();
152
+ if (filenameAllowed(filename)) {
153
+ return;
154
+ }
155
+
156
+ const targetName = getAssignmentTargetName(node);
157
+ if (onlyWhenAssigned && !targetName) {
158
+ return;
159
+ }
160
+
161
+ const target = targetName ? ` "${targetName}"` : '';
162
+
163
+ context.report({
164
+ node,
165
+ messageId: 'moveSchema',
166
+ data: {
167
+ filename,
168
+ suffixes: allowedSuffixes.join(' or '),
169
+ target,
170
+ },
171
+ });
172
+ }
173
+
174
+ return {
175
+ ImportDeclaration(node) {
176
+ collectZodIdentifiersFromImport(node);
177
+ },
178
+
179
+ CallExpression(node) {
180
+ if (zodIdentifiers.size === 0) {
181
+ return;
182
+ }
183
+
184
+ if (isZodBuilderCall(node) || isZodChainedBuilderCall(node)) {
185
+ report(node);
186
+ }
187
+ },
188
+ };
189
+ }
190
+
191
+ module.exports = {meta, create};
@@ -1,7 +1,7 @@
1
1
  const meta = {
2
2
  type: 'problem',
3
3
  docs: {
4
- description: 'Require React-Native StyleSheet.create(...) to be placed in a .styles.ts/.styles.tsx file',
4
+ description: 'Require React-Native StyleSheet.create(...) to be placed in a .styles.ts file',
5
5
  category: 'Stylistic Issues',
6
6
  recommended: false,
7
7
  url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/styles-in-styles-file.md',
@@ -22,13 +22,13 @@ const meta = {
22
22
  ],
23
23
  messages: {
24
24
  moveStyles:
25
- 'React-Native styles{{target}} must be defined in a dedicated styles file ({{suffixes}}). Current file: "{{filename}}".',
25
+ 'React-Native styles must be defined in a dedicated styles file ({{suffixes}}).',
26
26
  },
27
27
  };
28
28
 
29
29
  function create(context) {
30
30
  const options = context.options?.[0] ?? {};
31
- const allowedSuffixes = options.allowedSuffixes ?? ['.styles.ts', '.styles.tsx'];
31
+ const allowedSuffixes = options.allowedSuffixes ?? ['.styles.ts'];
32
32
  const includeCompose = Boolean(options.includeCompose);
33
33
 
34
34
  function isAllowedStyleFile(filename) {
@@ -22,7 +22,7 @@ const meta = {
22
22
  ],
23
23
  messages: {
24
24
  moveToDts:
25
- 'Type declarations must be moved to a .d.ts file (for example "{{filename}}.d.ts").',
25
+ 'Type declarations must be defined in a a .d.ts file.',
26
26
  },
27
27
  };
28
28
 
package/bun.lockb DELETED
Binary file