eslint-plugin-th-rules 2.7.1 → 2.8.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.
Files changed (74) hide show
  1. package/README.md +171 -11
  2. package/dist/configs/bundles/recommended-react.d.ts +199 -0
  3. package/dist/configs/bundles/recommended-react.js +11 -0
  4. package/dist/configs/bundles/recommended-typescript.d.ts +190 -0
  5. package/dist/configs/bundles/recommended-typescript.js +6 -0
  6. package/dist/configs/bundles/recommended.d.ts +190 -0
  7. package/dist/configs/bundles/recommended.js +8 -0
  8. package/dist/configs/core/base.d.ts +170 -0
  9. package/dist/configs/core/base.js +23 -0
  10. package/dist/configs/core/react.d.ts +6 -0
  11. package/dist/configs/core/react.js +8 -0
  12. package/dist/configs/core/typescript.d.ts +2 -0
  13. package/dist/configs/core/typescript.js +19 -0
  14. package/dist/configs/externals/base.d.ts +15 -0
  15. package/dist/configs/externals/base.js +16 -0
  16. package/dist/configs/externals/opinionated.d.ts +10 -0
  17. package/dist/configs/externals/opinionated.js +12 -0
  18. package/dist/index.d.ts +1170 -0
  19. package/dist/index.js +21 -0
  20. package/dist/plugin.d.ts +5 -0
  21. package/dist/plugin.js +14 -0
  22. package/dist/rules/no-boolean-coercion.d.ts +5 -0
  23. package/dist/rules/no-boolean-coercion.js +98 -0
  24. package/dist/rules/no-comments.d.ts +11 -0
  25. package/dist/rules/no-comments.js +83 -0
  26. package/dist/rules/no-default-export.d.ts +5 -0
  27. package/dist/rules/no-default-export.js +61 -0
  28. package/dist/rules/no-destructuring.d.ts +8 -0
  29. package/dist/rules/no-destructuring.js +121 -0
  30. package/dist/rules/prefer-is-empty.d.ts +5 -0
  31. package/dist/rules/prefer-is-empty.js +101 -0
  32. package/dist/rules/schemas-in-schemas-file.d.ts +9 -0
  33. package/dist/rules/schemas-in-schemas-file.js +141 -0
  34. package/dist/rules/top-level-functions.d.ts +5 -0
  35. package/dist/rules/top-level-functions.js +153 -0
  36. package/dist/rules/types-in-dts.d.ts +8 -0
  37. package/dist/rules/types-in-dts.js +76 -0
  38. package/package.json +25 -14
  39. package/.github/dependabot.yml +0 -15
  40. package/.github/workflows/codecov.yml +0 -26
  41. package/.github/workflows/codeql.yml +0 -82
  42. package/.github/workflows/dependency-review.yml +0 -20
  43. package/.github/workflows/main.yml +0 -43
  44. package/.github/workflows/scorecard.yml +0 -72
  45. package/.github/workflows/snyk-security.yml +0 -67
  46. package/.releaserc +0 -13
  47. package/.vscode/settings.json +0 -8
  48. package/.yarn/releases/yarn-4.12.0.cjs +0 -942
  49. package/.yarnrc.yml +0 -3
  50. package/CHANGELOG.md +0 -628
  51. package/SECURITY.md +0 -48
  52. package/docs/rules/no-boolean-coercion.md +0 -9
  53. package/docs/rules/no-comments.md +0 -50
  54. package/docs/rules/no-default-export.md +0 -26
  55. package/docs/rules/no-destructuring.md +0 -40
  56. package/docs/rules/prefer-is-empty.md +0 -9
  57. package/docs/rules/schemas-in-schemas-file.md +0 -170
  58. package/docs/rules/top-level-functions.md +0 -48
  59. package/docs/rules/types-in-dts.md +0 -112
  60. package/renovate.json +0 -3
  61. package/scripts/verify.mjs +0 -16
  62. package/src/index.js +0 -144
  63. package/src/rules/no-boolean-coercion.js +0 -124
  64. package/src/rules/no-comments.js +0 -94
  65. package/src/rules/no-default-export.js +0 -64
  66. package/src/rules/no-destructuring.js +0 -114
  67. package/src/rules/prefer-is-empty.js +0 -104
  68. package/src/rules/schemas-in-schemas-file.js +0 -191
  69. package/src/rules/top-level-functions.js +0 -200
  70. package/src/rules/types-in-dts.js +0 -94
  71. package/tests/no-boolean-coercion.test.ts +0 -83
  72. package/tests/prefer-is-empty.test.ts +0 -148
  73. package/tsconfig.json +0 -22
  74. package/xo.config.ts +0 -2
@@ -1,124 +0,0 @@
1
- const meta = {
2
- type: 'problem',
3
- docs: {
4
- description:
5
- 'Disallow Boolean(value) or !!value. Enforce explicit checks: !_.isNil(value) for scalar values and !_.isEmpty(value) for strings, arrays, and objects.',
6
- category: 'Best Practices',
7
- recommended: true,
8
- url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-boolean-coercion.md',
9
- },
10
- hasSuggestions: true,
11
- schema: [],
12
- messages: {
13
- useIsEmpty:
14
- 'Boolean coercion is not allowed. Use !_.isEmpty(value) for strings, arrays, and objects.',
15
- useIsNil:
16
- 'Boolean coercion is not allowed. Use !_.isNil(value) for scalar values.',
17
- },
18
- };
19
-
20
- function create(context) {
21
- const sourceCode = context.getSourceCode();
22
- const services = context.parserServices;
23
- const checker = services?.program?.getTypeChecker?.();
24
-
25
- function isBooleanCall(node) {
26
- return (
27
- node.type === 'CallExpression'
28
- && node.callee.type === 'Identifier'
29
- && node.callee.name === 'Boolean'
30
- && node.arguments.length === 1
31
- );
32
- }
33
-
34
- function isDoubleNegation(node) {
35
- return (
36
- node.type === 'UnaryExpression'
37
- && node.operator === '!'
38
- && node.argument?.type === 'UnaryExpression'
39
- && node.argument.operator === '!'
40
- );
41
- }
42
-
43
- function isCollectionLikeByTS(node) {
44
- if (!checker || !services?.esTreeNodeToTSNodeMap) {
45
- return false;
46
- }
47
-
48
- const tsNode = services.esTreeNodeToTSNodeMap.get(node);
49
- if (!tsNode) {
50
- return false;
51
- }
52
-
53
- const type = checker.getTypeAtLocation(tsNode);
54
- const typeString = checker.typeToString(type);
55
-
56
- return (
57
- typeString.includes('[]')
58
- || typeString === 'string'
59
- || typeString === 'object'
60
- || typeString.startsWith('Array<')
61
- || typeString.startsWith('ReadonlyArray<')
62
- );
63
- }
64
-
65
- function isCollectionLikeBySyntax(node) {
66
- return (
67
- node.type === 'ArrayExpression'
68
- || node.type === 'ObjectExpression'
69
- || (node.type === 'Literal' && typeof node.value === 'string')
70
- );
71
- }
72
-
73
- function report(node, valueNode) {
74
- const isCollection
75
- = isCollectionLikeBySyntax(valueNode)
76
- || isCollectionLikeByTS(valueNode);
77
-
78
- const suggestedFn = isCollection ? '_.isEmpty' : '_.isNil';
79
- const replacement = `!${suggestedFn}(${sourceCode.getText(valueNode)})`;
80
-
81
- context.report({
82
- node,
83
- messageId: isCollection ? 'useIsEmpty' : 'useIsNil',
84
- suggest: [
85
- {
86
- desc: `Replace with ${replacement}`,
87
- fix(fixer) {
88
- return fixer.replaceText(node, replacement);
89
- },
90
- },
91
- ],
92
- });
93
- }
94
-
95
- return {
96
- CallExpression(node) {
97
- if (!isBooleanCall(node)) {
98
- return;
99
- }
100
-
101
- const arg = node.arguments[0];
102
- if (!arg) {
103
- return;
104
- }
105
-
106
- report(node, arg);
107
- },
108
-
109
- UnaryExpression(node) {
110
- if (!isDoubleNegation(node)) {
111
- return;
112
- }
113
-
114
- const valueNode = node.argument.argument;
115
- if (!valueNode) {
116
- return;
117
- }
118
-
119
- report(node, valueNode);
120
- },
121
- };
122
- }
123
-
124
- module.exports = {meta, create};
@@ -1,94 +0,0 @@
1
- /* eslint-disable unicorn/prefer-module */
2
-
3
- const allowedPatterns = [
4
- /todo/i, // Allow TODO (case-insensitive)
5
- /warning/i, // Allow WARNING (case-insensitive)
6
- /error/i, // Allow ERROR (case-insensitive)
7
- /info/i, // Allow INFO (case-insensitive)
8
- /^\s*eslint-(disable|enable|env|globals|ignore|directive)/,
9
- ];
10
-
11
- const meta = {
12
- type: 'problem',
13
- docs: {
14
- description: 'Disallow comments except for specified allowed patterns.',
15
- url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-comments.md',
16
- },
17
- fixable: 'code',
18
- schema: [
19
- {
20
- type: 'object',
21
- properties: {
22
- allow: {
23
- type: 'array',
24
- items: {
25
- type: 'string',
26
- },
27
- description: 'Additional patterns to allow in comments.',
28
- },
29
- disallow: {
30
- type: 'array',
31
- items: {
32
- type: 'string',
33
- },
34
- description: 'Additional patterns to disallow in comments.',
35
- },
36
- },
37
- additionalProperties: false,
38
- },
39
- ],
40
- };
41
-
42
- function create(context) {
43
- const options = context.options[0] || {};
44
- const userAllowedPatterns = (options.allow || []).map(pattern => new RegExp(pattern));
45
- const userDisallowedPatterns = (options.disallow || []).map(pattern => new RegExp(pattern));
46
-
47
- function isCommentAllowed(comment) {
48
- const text = comment.value.trim();
49
-
50
- if (comment.type === 'Block' && comment.value.startsWith('*')) {
51
- return true;
52
- }
53
-
54
- for (const pattern of [...allowedPatterns, ...userAllowedPatterns]) {
55
- if (pattern.test(text)) {
56
- return true;
57
- }
58
- }
59
-
60
- for (const pattern of userDisallowedPatterns) {
61
- if (pattern.test(text)) {
62
- return false;
63
- }
64
- }
65
-
66
- return false;
67
- }
68
-
69
- return {
70
- Program() {
71
- const sourceCode = context.getSourceCode();
72
- const comments = sourceCode.getAllComments();
73
-
74
- for (const comment of comments) {
75
- if (!isCommentAllowed(comment)) {
76
- context.report({
77
- node: comment,
78
- message: 'Comment not allowed.',
79
- fix(fixer) {
80
- return fixer.remove(comment);
81
- },
82
- });
83
- }
84
- }
85
- },
86
- };
87
- }
88
-
89
- const rule = {
90
- meta,
91
- create,
92
- };
93
-
94
- module.exports = rule;
@@ -1,64 +0,0 @@
1
- /* eslint-disable unicorn/prefer-module */
2
-
3
- const path = require('node:path');
4
-
5
- const meta = {
6
- type: 'problem',
7
- docs: {
8
- description: 'Convert unnamed default exports to named default exports based on the file name.',
9
- url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-default-export.md',
10
- },
11
- fixable: 'code',
12
- schema: [],
13
- };
14
-
15
- function create(context) {
16
- function generateExportNameFromFileName(fileName) {
17
- return fileName.replaceAll(/^\w|[A-Z]|\b\w|\s+/g, (match, index) => {
18
- if (match === ' ') {
19
- return '';
20
- }
21
-
22
- if (index === 0) {
23
- return match.toLowerCase();
24
- }
25
-
26
- return match.toUpperCase();
27
- })
28
- .replaceAll(/[-_<>\\. ]/g, '');
29
- }
30
-
31
- return {
32
- ExportDefaultDeclaration(node) {
33
- if (node.declaration.type === 'Identifier') {
34
- return;
35
- }
36
-
37
- if (node.declaration.id) {
38
- return;
39
- }
40
-
41
- const fileName = context.getFilename();
42
- const exportName = generateExportNameFromFileName(path.basename(fileName, path.extname(fileName)));
43
-
44
- context.report({
45
- node,
46
- message: 'Unnamed default export should be named based on the file name.',
47
- fix(fixer) {
48
- const sourceCode = context.getSourceCode();
49
- const declarationText = sourceCode.getText(node.declaration);
50
- const fixedCode = `const ${exportName} = ${declarationText};\nexport default ${exportName};`;
51
-
52
- return fixer.replaceText(node, fixedCode);
53
- },
54
- });
55
- },
56
- };
57
- }
58
-
59
- const rule = {
60
- meta,
61
- create,
62
- };
63
-
64
- module.exports = rule;
@@ -1,114 +0,0 @@
1
- const MAX_TAB_COUNT = 3;
2
-
3
- const meta = {
4
- type: 'problem',
5
- docs: {
6
- description: 'Disallow destructuring that does not meet certain conditions',
7
- category: 'Possible Errors',
8
- recommended: true,
9
- url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-destructuring.md',
10
- },
11
- schema: [
12
- {
13
- type: 'object',
14
- properties: {
15
- maximumDestructuredVariables: {type: 'integer', minimum: 0},
16
- maximumLineLength: {type: 'integer', minimum: 0},
17
- },
18
- additionalProperties: false,
19
- },
20
- ],
21
- };
22
-
23
- function create(context) {
24
- const MAX_VARIABLES = context?.options?.[0]?.maximumDestructuredVariables ?? 2;
25
- const MAX_LINE_LENGTH = context?.options?.[0]?.maximumLineLength ?? 100;
26
-
27
- const sourceCode = context.getSourceCode();
28
-
29
- function reportIfNeeded(patternNode, reportNode = patternNode) {
30
- if (!patternNode || patternNode.type !== 'ObjectPattern' || !patternNode.loc) {
31
- return;
32
- }
33
-
34
- const lineText = sourceCode.lines[patternNode.loc.start.line - 1] ?? '';
35
- const indentCount = lineText.search(/\S|$/);
36
-
37
- const propertyCount = patternNode.properties?.length ?? 0;
38
-
39
- const startLine = patternNode.loc.start.line;
40
- const endLine = patternNode.loc.end.line;
41
- let maxSpannedLineLength = 0;
42
- for (let i = startLine; i <= endLine; i++) {
43
- const t = sourceCode.lines[i - 1] ?? '';
44
- if (t.length > maxSpannedLineLength) {
45
- maxSpannedLineLength = t.length;
46
- }
47
- }
48
-
49
- if (indentCount > MAX_TAB_COUNT) {
50
- context.report({
51
- node: reportNode,
52
- message: `destructuring at a nesting level above ${MAX_TAB_COUNT} is not allowed, instead saw ${indentCount} levels of nesting`,
53
- });
54
- }
55
-
56
- if (propertyCount > MAX_VARIABLES) {
57
- context.report({
58
- node: reportNode,
59
- message: `destructuring of more than ${MAX_VARIABLES} variables is not allowed`,
60
- });
61
- }
62
-
63
- if (maxSpannedLineLength > MAX_LINE_LENGTH) {
64
- context.report({
65
- node: reportNode,
66
- message: `destructuring on a line exceeding ${MAX_LINE_LENGTH} characters is not allowed`,
67
- });
68
- }
69
- }
70
-
71
- function checkParameters(parameters) {
72
- for (const p of parameters || []) {
73
- if (!p) {
74
- continue;
75
- }
76
-
77
- if (p.type === 'AssignmentPattern') {
78
- reportIfNeeded(p.left, p);
79
- continue;
80
- }
81
-
82
- reportIfNeeded(p, p);
83
- }
84
- }
85
-
86
- return {
87
-
88
- VariableDeclarator(node) {
89
- reportIfNeeded(node?.id, node);
90
- },
91
-
92
- FunctionDeclaration(node) {
93
- checkParameters(node.params);
94
- },
95
- FunctionExpression(node) {
96
- checkParameters(node.params);
97
- },
98
- ArrowFunctionExpression(node) {
99
- checkParameters(node.params);
100
- },
101
-
102
- MethodDefinition(node) {
103
- if (node?.value?.params) {
104
- checkParameters(node.value.params);
105
- }
106
- },
107
-
108
- TSDeclareFunction(node) {
109
- checkParameters(node.params);
110
- },
111
- };
112
- }
113
-
114
- module.exports = {meta, create};
@@ -1,104 +0,0 @@
1
- const meta = {
2
- type: 'problem',
3
- docs: {
4
- description: 'Require _.isEmpty instead of length comparisons',
5
- category: 'Best Practices',
6
- recommended: true,
7
- url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/prefer-is-empty.md',
8
- },
9
- hasSuggestions: true,
10
- schema: [],
11
- };
12
-
13
- function create(context) {
14
- const sourceCode = context.getSourceCode();
15
-
16
- function isLengthAccess(node) {
17
- return (
18
- node
19
- && node.type === 'MemberExpression'
20
- && !node.computed
21
- && node.property.type === 'Identifier'
22
- && node.property.name === 'length'
23
- );
24
- }
25
-
26
- function isNumericLiteral(node) {
27
- return node && node.type === 'Literal' && typeof node.value === 'number';
28
- }
29
-
30
- function report(node, collectionNode, operator, value, isEmptyCheck) {
31
- const collectionText = sourceCode.getText(collectionNode.object);
32
- const replacement = isEmptyCheck
33
- ? `_.isEmpty(${collectionText})`
34
- : `!_.isEmpty(${collectionText})`;
35
-
36
- context.report({
37
- node,
38
- message: `Use _.isEmpty(${collectionText}) instead of checking ${collectionText}.length ${operator} ${value}`,
39
- suggest: [
40
- {
41
- desc: `Replace with ${replacement}`,
42
- fix(fixer) {
43
- return fixer.replaceText(node, replacement);
44
- },
45
- },
46
- ],
47
- });
48
- }
49
-
50
- return {
51
- BinaryExpression(node) {
52
- const {left, right, operator} = node;
53
-
54
- // Values.length <op> N
55
- if (isLengthAccess(left) && isNumericLiteral(right)) {
56
- const {value} = right;
57
-
58
- // EMPTY checks
59
- if (
60
- (operator === '===' && value === 0)
61
- || (operator === '<=' && value === 0)
62
- || (operator === '<' && value === 1)
63
- ) {
64
- report(node, left, operator, value, true);
65
- return;
66
- }
67
-
68
- // NOT EMPTY checks
69
- if (
70
- (operator === '>' && value === 0)
71
- || (operator === '>=' && value === 1)
72
- || ((operator === '!=' || operator === '!==') && value === 0)
73
- ) {
74
- report(node, left, operator, value, false);
75
- }
76
- }
77
-
78
- // N <op> values.length (reversed)
79
- if (isNumericLiteral(left) && isLengthAccess(right)) {
80
- const {value} = left;
81
-
82
- // EMPTY checks
83
- if (
84
- (operator === '===' && value === 0)
85
- || (operator === '>=' && value === 0)
86
- || (operator === '>' && value === 0)
87
- ) {
88
- report(node, right, operator, value, true);
89
- return;
90
- }
91
-
92
- // NOT EMPTY checks
93
- if (
94
- (operator === '<' && value === 1)
95
- || (operator === '<=' && value === 0)
96
- ) {
97
- report(node, right, operator, value, false);
98
- }
99
- }
100
- },
101
- };
102
- }
103
-
104
- module.exports = {meta, create};
@@ -1,191 +0,0 @@
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};