ai-deslop 0.4.8 → 0.4.9
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
|
@@ -72,6 +72,7 @@ All rules below are enabled by the recommended config unless listed as opt-in.
|
|
|
72
72
|
| --- | --- |
|
|
73
73
|
| [`no-duplicate-consts`](docs/no-duplicate-consts.md) | Reports all-caps constants with the same static value in different files. |
|
|
74
74
|
| [`no-duplicate-types`](docs/no-duplicate-types.md) | Reports exact duplicate object contracts. No fixer. |
|
|
75
|
+
| [`no-pass-through-schema`](docs/no-pass-through-schema.md) | Reports const aliases that only rename known schemas. Supports Zod, ArkType, and configured imports. No fixer. |
|
|
75
76
|
| [`no-renamed-imports`](docs/no-renamed-imports.md) | Fixes unnecessary named-import aliases when the original name has no module-level conflict. |
|
|
76
77
|
| [`no-reexports-outside-entrypoints`](docs/no-reexports-outside-entrypoints.md) | Keeps re-exports in configured entrypoint files. No fixer. |
|
|
77
78
|
| [`canonical-class-names`](docs/tailwind.md) | Fixes non-canonical Tailwind CSS v4 class spellings. |
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { noCallOnlyAssertionsRule } from "./rules/no-call-only-assertions.js";
|
|
|
20
20
|
import { noPoorSubstringTestsRule } from "./rules/no-poor-substring-tests.js";
|
|
21
21
|
import { noRepeatedExpectCallRule } from "./rules/no-repeated-expect-call.js";
|
|
22
22
|
import { noPassThroughTypeAliasRule } from "./rules/no-pass-through-type-alias.js";
|
|
23
|
+
import { noPassThroughSchemaRule } from "./rules/no-pass-through-schema.js";
|
|
23
24
|
import { noDuplicateTypesRule } from "./rules/no-duplicate-types.js";
|
|
24
25
|
import { noDuplicateConstsRule } from "./rules/no-duplicate-consts.js";
|
|
25
26
|
import { noRenamedImportsRule } from "./rules/no-renamed-imports.js";
|
|
@@ -88,6 +89,7 @@ const deslopPlugin = eslintCompatPlugin({
|
|
|
88
89
|
"no-repeated-expect-call": noRepeatedExpectCallRule,
|
|
89
90
|
"no-constant-substring-tests": noPoorSubstringTestsRule,
|
|
90
91
|
"no-pass-through-type-alias": noPassThroughTypeAliasRule,
|
|
92
|
+
"no-pass-through-schema": noPassThroughSchemaRule,
|
|
91
93
|
"no-duplicate-types": noDuplicateTypesRule,
|
|
92
94
|
"no-duplicate-consts": noDuplicateConstsRule,
|
|
93
95
|
"no-renamed-imports": noRenamedImportsRule,
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { defineRule } from "@oxlint/plugins";
|
|
2
|
+
// Only known schema-producing APIs belong here. Parsing and inspection APIs do not.
|
|
3
|
+
const zodFactories = new Set([
|
|
4
|
+
"string", "number", "bigint", "boolean", "date", "symbol", "undefined", "null", "void",
|
|
5
|
+
"any", "unknown", "never", "literal", "enum", "nativeEnum", "object", "strictObject",
|
|
6
|
+
"looseObject", "array", "tuple", "record", "map", "set", "union", "discriminatedUnion",
|
|
7
|
+
"intersection", "optional", "nullable", "nullish", "lazy", "promise", "custom",
|
|
8
|
+
"preprocess", "instanceof", "templateLiteral", "file", "json",
|
|
9
|
+
]);
|
|
10
|
+
const zodMethods = new Set([
|
|
11
|
+
"optional", "nullable", "nullish", "array", "or", "and", "refine", "superRefine", "check",
|
|
12
|
+
"transform", "pipe", "default", "prefault", "catch", "brand", "readonly", "describe", "meta",
|
|
13
|
+
"extend", "safeExtend", "merge", "pick", "omit", "partial", "required", "keyof",
|
|
14
|
+
"strict", "strip", "passthrough", "catchall", "nonoptional", "unwrap",
|
|
15
|
+
"min", "max", "length", "nonempty", "email", "url", "uuid", "regex", "includes",
|
|
16
|
+
"startsWith", "endsWith", "trim", "toLowerCase", "toUpperCase", "int", "positive",
|
|
17
|
+
"negative", "nonnegative", "nonpositive", "finite", "multipleOf", "safe", "gt", "gte", "lt", "lte",
|
|
18
|
+
]);
|
|
19
|
+
const arkMethods = new Set([
|
|
20
|
+
"array", "or", "and", "pipe", "narrow", "brand", "describe", "configure", "optional",
|
|
21
|
+
"default", "pick", "omit", "partial", "required", "merge", "keyof", "extract", "exclude",
|
|
22
|
+
"atLeast", "atMost", "moreThan", "lessThan", "atLeastLength", "atMostLength", "exactlyLength",
|
|
23
|
+
"matching", "divisibleBy", "onUndeclaredKey", "onDeepUndeclaredKey",
|
|
24
|
+
]);
|
|
25
|
+
function memberName(node) {
|
|
26
|
+
if (!node.computed && node.property.type === "Identifier")
|
|
27
|
+
return node.property.name;
|
|
28
|
+
if (node.computed && node.property.type === "Literal" && typeof node.property.value === "string")
|
|
29
|
+
return node.property.value;
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
function memberKind(owner, name) {
|
|
33
|
+
if (name === null)
|
|
34
|
+
return null;
|
|
35
|
+
if ((owner === "zod-module" || owner === "zod-namespace") && name === "z")
|
|
36
|
+
return "zod-namespace";
|
|
37
|
+
if ((owner === "zod-module" || owner === "zod-namespace") && zodFactories.has(name))
|
|
38
|
+
return "zod-factory";
|
|
39
|
+
if (owner === "ark-module" && name === "type")
|
|
40
|
+
return "ark-factory";
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
/** Disallow const aliases that only rename a known schema. */
|
|
44
|
+
export const noPassThroughSchemaRule = defineRule({
|
|
45
|
+
meta: {
|
|
46
|
+
type: "suggestion",
|
|
47
|
+
docs: { description: "Disallow pass-through aliases of known schemas" },
|
|
48
|
+
messages: { passThroughSchema: "Use {{schemaName}} directly instead of creating a pass-through schema alias." },
|
|
49
|
+
schema: [{
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
libraries: { type: "array", items: { enum: ["zod", "arktype"] }, uniqueItems: true },
|
|
53
|
+
allowExportedAliases: { type: "boolean" },
|
|
54
|
+
schemaImports: {
|
|
55
|
+
type: "array",
|
|
56
|
+
items: {
|
|
57
|
+
type: "object",
|
|
58
|
+
properties: { from: { type: "string" }, names: { type: "array", items: { type: "string" } } },
|
|
59
|
+
required: ["from", "names"],
|
|
60
|
+
additionalProperties: false,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
additionalProperties: false,
|
|
65
|
+
}],
|
|
66
|
+
},
|
|
67
|
+
createOnce(context) {
|
|
68
|
+
let options;
|
|
69
|
+
const candidates = [];
|
|
70
|
+
const exported = new Set();
|
|
71
|
+
function variableFor(node) {
|
|
72
|
+
let scope = context.sourceCode.getScope(node);
|
|
73
|
+
while (true) {
|
|
74
|
+
const variable = scope.set.get(node.name);
|
|
75
|
+
if (variable !== undefined)
|
|
76
|
+
return variable;
|
|
77
|
+
if (scope.upper === null)
|
|
78
|
+
return null;
|
|
79
|
+
scope = scope.upper;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function kind(node, seen = new Set()) {
|
|
83
|
+
if (node.type === "Identifier") {
|
|
84
|
+
const variable = variableFor(node);
|
|
85
|
+
if (variable === null || seen.has(variable) || variable.defs.length !== 1)
|
|
86
|
+
return null;
|
|
87
|
+
seen.add(variable);
|
|
88
|
+
const definition = variable.defs[0];
|
|
89
|
+
if (definition.type === "ImportBinding") {
|
|
90
|
+
const specifier = definition.node;
|
|
91
|
+
const declaration = specifier.parent;
|
|
92
|
+
if (declaration?.type !== "ImportDeclaration" || declaration.importKind === "type")
|
|
93
|
+
return null;
|
|
94
|
+
if (specifier.type === "ImportSpecifier" && specifier.importKind === "type")
|
|
95
|
+
return null;
|
|
96
|
+
const source = declaration.source.value;
|
|
97
|
+
const name = specifier.type === "ImportSpecifier"
|
|
98
|
+
? specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value
|
|
99
|
+
: specifier.type === "ImportDefaultSpecifier" ? "default" : null;
|
|
100
|
+
if (name !== null && options.schemaImports?.some((entry) => entry.from === source && entry.names.includes(name)))
|
|
101
|
+
return "schema";
|
|
102
|
+
const libraries = options.libraries ?? ["zod", "arktype"];
|
|
103
|
+
if (libraries.includes("zod") && ["zod", "zod/v3", "zod/v4", "zod/v4/classic", "zod/v4/mini", "zod/mini"].includes(source)) {
|
|
104
|
+
if (name === null)
|
|
105
|
+
return "zod-module";
|
|
106
|
+
if (name === "z" || name === "default")
|
|
107
|
+
return "zod-namespace";
|
|
108
|
+
if (zodFactories.has(name))
|
|
109
|
+
return "zod-factory";
|
|
110
|
+
}
|
|
111
|
+
if (libraries.includes("arktype") && source === "arktype") {
|
|
112
|
+
if (name === null)
|
|
113
|
+
return "ark-module";
|
|
114
|
+
if (name === "type")
|
|
115
|
+
return "ark-factory";
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
if (definition.type !== "Variable" || definition.node.type !== "VariableDeclarator")
|
|
120
|
+
return null;
|
|
121
|
+
const declaration = definition.node;
|
|
122
|
+
if (declaration.parent.type !== "VariableDeclaration" || declaration.parent.kind !== "const" || declaration.id.type !== "Identifier" || declaration.init === null)
|
|
123
|
+
return null;
|
|
124
|
+
return kind(declaration.init, seen);
|
|
125
|
+
}
|
|
126
|
+
if (node.type === "MemberExpression") {
|
|
127
|
+
return memberKind(kind(node.object, seen), memberName(node));
|
|
128
|
+
}
|
|
129
|
+
if (node.type !== "CallExpression")
|
|
130
|
+
return null;
|
|
131
|
+
let factory;
|
|
132
|
+
if (node.callee.type === "MemberExpression") {
|
|
133
|
+
const owner = kind(node.callee.object, seen);
|
|
134
|
+
const name = memberName(node.callee);
|
|
135
|
+
if (owner === "zod-schema" && name === "meta" && node.arguments.length === 0)
|
|
136
|
+
return null;
|
|
137
|
+
if (name !== null && owner === "zod-schema" && zodMethods.has(name))
|
|
138
|
+
return owner;
|
|
139
|
+
if (name !== null && owner === "ark-schema" && arkMethods.has(name))
|
|
140
|
+
return owner;
|
|
141
|
+
factory = memberKind(owner, name);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
factory = kind(node.callee, seen);
|
|
145
|
+
}
|
|
146
|
+
if (factory === "zod-factory")
|
|
147
|
+
return "zod-schema";
|
|
148
|
+
if (factory === "ark-factory")
|
|
149
|
+
return "ark-schema";
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
function markExport(node) {
|
|
153
|
+
const variable = variableFor(node);
|
|
154
|
+
if (variable !== null)
|
|
155
|
+
exported.add(variable);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
before() {
|
|
159
|
+
options = context.options[0] ?? {};
|
|
160
|
+
candidates.length = 0;
|
|
161
|
+
exported.clear();
|
|
162
|
+
},
|
|
163
|
+
VariableDeclarator(node) {
|
|
164
|
+
if (node.parent.type === "VariableDeclaration" && node.parent.kind === "const" && node.id.type === "Identifier" && node.init?.type === "Identifier")
|
|
165
|
+
candidates.push(node);
|
|
166
|
+
},
|
|
167
|
+
ExportNamedDeclaration(node) {
|
|
168
|
+
if (node.exportKind === "type")
|
|
169
|
+
return;
|
|
170
|
+
if (node.declaration?.type === "VariableDeclaration") {
|
|
171
|
+
for (const declaration of node.declaration.declarations)
|
|
172
|
+
if (declaration.id.type === "Identifier")
|
|
173
|
+
markExport(declaration.id);
|
|
174
|
+
}
|
|
175
|
+
if (node.source !== null)
|
|
176
|
+
return;
|
|
177
|
+
for (const specifier of node.specifiers)
|
|
178
|
+
if (specifier.exportKind !== "type" && specifier.local.type === "Identifier")
|
|
179
|
+
markExport(specifier.local);
|
|
180
|
+
},
|
|
181
|
+
ExportDefaultDeclaration(node) {
|
|
182
|
+
if (node.declaration.type === "Identifier")
|
|
183
|
+
markExport(node.declaration);
|
|
184
|
+
},
|
|
185
|
+
"Program:exit"() {
|
|
186
|
+
for (const node of candidates) {
|
|
187
|
+
if (node.id.type !== "Identifier" || node.init?.type !== "Identifier")
|
|
188
|
+
continue;
|
|
189
|
+
const target = kind(node.init);
|
|
190
|
+
if (target !== "schema" && target !== "zod-schema" && target !== "ark-schema")
|
|
191
|
+
continue;
|
|
192
|
+
const variable = variableFor(node.id);
|
|
193
|
+
if (options.allowExportedAliases && variable !== null && exported.has(variable))
|
|
194
|
+
continue;
|
|
195
|
+
context.report({ node, messageId: "passThroughSchema", data: { schemaName: node.init.name } });
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
});
|