deep-slop 1.4.1
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/.deep-slop/.deep-slop-ignore +13 -0
- package/LICENSE +21 -0
- package/README.md +1170 -0
- package/dist/arch-constraints-C7s1E_bc.js +450 -0
- package/dist/arch-rules-DI1SYPqu.js +358 -0
- package/dist/ast-slop-BGdr58wZ.js +1839 -0
- package/dist/config-lint-ph3vMUbg.js +371 -0
- package/dist/dead-flow-DHRkyxZT.js +1422 -0
- package/dist/deep-slop-bundled.js +33140 -0
- package/dist/discover-B_S_Fy2S.js +164 -0
- package/dist/dup-detect-DKRXM04q.js +709 -0
- package/dist/file-utils-B_HFXhCs.js +93 -0
- package/dist/format-lint-DeElllNm.js +445 -0
- package/dist/framework-lint-CqdlF9hX.js +782 -0
- package/dist/i18n-lint-CPzx7V8Q.js +605 -0
- package/dist/import-intelligence-SK4F7XpL.js +966 -0
- package/dist/index.d.ts +233 -0
- package/dist/index.js +1030 -0
- package/dist/knip-CgxnnTBZ.js +93 -0
- package/dist/lint-external-ZbW3jGvB.js +326 -0
- package/dist/markup-lint-DKVEDz9M.js +805 -0
- package/dist/mcp.js +35939 -0
- package/dist/meta-quality-Dai1W5iC.js +224 -0
- package/dist/perf-hints-BnWFMFff.js +500 -0
- package/dist/security-deep-DJRINs10.js +1198 -0
- package/dist/syntax-deep-ZQYMutky.js +624 -0
- package/dist/tree-sitter-CM-cP0nl.js +661 -0
- package/dist/type-safety-Dboj2C1t.js +519 -0
- package/package.json +92 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { r as readFileContent } from "./file-utils-B_HFXhCs.js";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
import { readdir, stat } from "node:fs/promises";
|
|
4
|
+
|
|
5
|
+
//#region src/engines/config-lint/index.ts
|
|
6
|
+
/** Create a config-lint diagnostic */
|
|
7
|
+
function diag(overrides) {
|
|
8
|
+
return {
|
|
9
|
+
engine: "config-lint",
|
|
10
|
+
category: "config",
|
|
11
|
+
line: 1,
|
|
12
|
+
column: 1,
|
|
13
|
+
fixable: false,
|
|
14
|
+
help: "",
|
|
15
|
+
...overrides
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Try to read and parse a JSON file; returns undefined if missing or invalid */
|
|
19
|
+
async function readJsonFile(filePath) {
|
|
20
|
+
try {
|
|
21
|
+
const content = await readFileContent(filePath);
|
|
22
|
+
return JSON.parse(content);
|
|
23
|
+
} catch {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Check whether a file exists */
|
|
28
|
+
async function fileExists(filePath) {
|
|
29
|
+
try {
|
|
30
|
+
return (await stat(filePath)).isFile();
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Find the first file matching a set of candidate names in a directory */
|
|
36
|
+
async function findFile(dir, candidates) {
|
|
37
|
+
for (const name of candidates) {
|
|
38
|
+
const fullPath = join(dir, name);
|
|
39
|
+
if (await fileExists(fullPath)) return fullPath;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Find all files matching a glob-like prefix pattern (e.g. ".eslintrc.*") */
|
|
43
|
+
async function findFilesWithPrefix(dir, prefix) {
|
|
44
|
+
try {
|
|
45
|
+
return (await readdir(dir)).filter((e) => e.startsWith(prefix)).map((e) => join(dir, e));
|
|
46
|
+
} catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Find files matching a glob-like suffix pattern (e.g. "eslint.config.*") */
|
|
51
|
+
async function findFilesWithName(dir, name) {
|
|
52
|
+
try {
|
|
53
|
+
return (await readdir(dir)).filter((e) => e === name || e.startsWith(name + ".")).map((e) => join(dir, e));
|
|
54
|
+
} catch {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function checkTsconfigStrict(root) {
|
|
59
|
+
const diagnostics = [];
|
|
60
|
+
const tsconfigPath = await findFile(root, ["tsconfig.json"]);
|
|
61
|
+
if (!tsconfigPath) return diagnostics;
|
|
62
|
+
const tsconfig = await readJsonFile(tsconfigPath);
|
|
63
|
+
if (!tsconfig) return diagnostics;
|
|
64
|
+
const co = tsconfig.compilerOptions ?? {};
|
|
65
|
+
const relPath = "tsconfig.json";
|
|
66
|
+
if (co.strict !== true) diagnostics.push(diag({
|
|
67
|
+
filePath: relPath,
|
|
68
|
+
rule: "config-lint/tsconfig-strict",
|
|
69
|
+
severity: "info",
|
|
70
|
+
message: `compilerOptions.strict is not enabled (current: ${JSON.stringify(co.strict) ?? "unset"})`,
|
|
71
|
+
help: "Set \"strict\": true in compilerOptions to enable all strict type-checking options.",
|
|
72
|
+
detail: {
|
|
73
|
+
key: "strict",
|
|
74
|
+
current: co.strict ?? null,
|
|
75
|
+
recommended: true
|
|
76
|
+
}
|
|
77
|
+
}));
|
|
78
|
+
if (co.strict !== true) {
|
|
79
|
+
for (const { key, label } of [
|
|
80
|
+
{
|
|
81
|
+
key: "noImplicitAny",
|
|
82
|
+
label: "noImplicitAny"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
key: "strictNullChecks",
|
|
86
|
+
label: "strictNullChecks"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
key: "noUncheckedIndexedAccess",
|
|
90
|
+
label: "noUncheckedIndexedAccess"
|
|
91
|
+
}
|
|
92
|
+
]) if (co[key] !== true) diagnostics.push(diag({
|
|
93
|
+
filePath: relPath,
|
|
94
|
+
rule: "config-lint/tsconfig-strict",
|
|
95
|
+
severity: "info",
|
|
96
|
+
message: `compilerOptions.${label} is not enabled`,
|
|
97
|
+
help: `Set "${label}": true in compilerOptions to catch more type errors at compile time.`,
|
|
98
|
+
detail: {
|
|
99
|
+
key,
|
|
100
|
+
current: co[key] ?? null,
|
|
101
|
+
recommended: true
|
|
102
|
+
}
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
return diagnostics;
|
|
106
|
+
}
|
|
107
|
+
const MODERN_TARGETS = new Set([
|
|
108
|
+
"es2022",
|
|
109
|
+
"es2023",
|
|
110
|
+
"esnext",
|
|
111
|
+
"es2024",
|
|
112
|
+
"es2025"
|
|
113
|
+
]);
|
|
114
|
+
const OLD_TARGETS = new Set([
|
|
115
|
+
"es3",
|
|
116
|
+
"es5",
|
|
117
|
+
"es6",
|
|
118
|
+
"es2015",
|
|
119
|
+
"es2016",
|
|
120
|
+
"es2017",
|
|
121
|
+
"es2018",
|
|
122
|
+
"es2019",
|
|
123
|
+
"es2020",
|
|
124
|
+
"es2021"
|
|
125
|
+
]);
|
|
126
|
+
async function checkTsconfigTarget(root) {
|
|
127
|
+
const diagnostics = [];
|
|
128
|
+
const tsconfigPath = await findFile(root, ["tsconfig.json"]);
|
|
129
|
+
if (!tsconfigPath) return diagnostics;
|
|
130
|
+
const tsconfig = await readJsonFile(tsconfigPath);
|
|
131
|
+
if (!tsconfig) return diagnostics;
|
|
132
|
+
const target = (tsconfig.compilerOptions?.target)?.toLowerCase();
|
|
133
|
+
const relPath = "tsconfig.json";
|
|
134
|
+
if (!target) diagnostics.push(diag({
|
|
135
|
+
filePath: relPath,
|
|
136
|
+
rule: "config-lint/tsconfig-target",
|
|
137
|
+
severity: "info",
|
|
138
|
+
message: "compilerOptions.target is not set (defaults to ES3)",
|
|
139
|
+
help: "Set \"target\": \"ES2022\" or later in compilerOptions for modern JavaScript output.",
|
|
140
|
+
detail: {
|
|
141
|
+
key: "target",
|
|
142
|
+
current: null,
|
|
143
|
+
recommended: "ES2022"
|
|
144
|
+
}
|
|
145
|
+
}));
|
|
146
|
+
else if (OLD_TARGETS.has(target)) diagnostics.push(diag({
|
|
147
|
+
filePath: relPath,
|
|
148
|
+
rule: "config-lint/tsconfig-target",
|
|
149
|
+
severity: "info",
|
|
150
|
+
message: `compilerOptions.target is set to "${target}", which is outdated`,
|
|
151
|
+
help: `Upgrade "target" to "ES2022" or later for modern JavaScript features and better optimization.`,
|
|
152
|
+
detail: {
|
|
153
|
+
key: "target",
|
|
154
|
+
current: target,
|
|
155
|
+
recommended: "ES2022"
|
|
156
|
+
}
|
|
157
|
+
}));
|
|
158
|
+
else if (!MODERN_TARGETS.has(target)) diagnostics.push(diag({
|
|
159
|
+
filePath: relPath,
|
|
160
|
+
rule: "config-lint/tsconfig-target",
|
|
161
|
+
severity: "info",
|
|
162
|
+
message: `compilerOptions.target is set to "${target}" — consider using ES2022+ for modern output`,
|
|
163
|
+
help: "Consider setting \"target\" to \"ES2022\" or \"ESNext\" for modern JavaScript output.",
|
|
164
|
+
detail: {
|
|
165
|
+
key: "target",
|
|
166
|
+
current: target,
|
|
167
|
+
recommended: "ES2022"
|
|
168
|
+
}
|
|
169
|
+
}));
|
|
170
|
+
return diagnostics;
|
|
171
|
+
}
|
|
172
|
+
async function checkMissingEslint(root) {
|
|
173
|
+
if ((await findFilesWithPrefix(root, ".eslintrc")).length > 0) return [];
|
|
174
|
+
if ((await findFilesWithName(root, "eslint.config")).length > 0) return [];
|
|
175
|
+
const pkgJson = await readJsonFile(join(root, "package.json"));
|
|
176
|
+
if (pkgJson && "eslintConfig" in pkgJson) return [];
|
|
177
|
+
return [diag({
|
|
178
|
+
filePath: "package.json",
|
|
179
|
+
rule: "config-lint/missing-eslint",
|
|
180
|
+
severity: "info",
|
|
181
|
+
message: "No ESLint configuration found in the project",
|
|
182
|
+
help: "Add ESLint for code quality enforcement. Run: npm init @eslint/config@latest or create an eslint.config.js file.",
|
|
183
|
+
detail: { checked: [
|
|
184
|
+
".eslintrc.*",
|
|
185
|
+
"eslint.config.*",
|
|
186
|
+
"package.json#eslintConfig"
|
|
187
|
+
] }
|
|
188
|
+
})];
|
|
189
|
+
}
|
|
190
|
+
const REQUIRED_SCRIPTS = [
|
|
191
|
+
"build",
|
|
192
|
+
"test",
|
|
193
|
+
"lint"
|
|
194
|
+
];
|
|
195
|
+
async function checkPackageJsonScripts(root) {
|
|
196
|
+
const diagnostics = [];
|
|
197
|
+
const pkgJson = await readJsonFile(join(root, "package.json"));
|
|
198
|
+
if (!pkgJson) return diagnostics;
|
|
199
|
+
const scripts = pkgJson.scripts ?? {};
|
|
200
|
+
const missing = [];
|
|
201
|
+
for (const name of REQUIRED_SCRIPTS) if (!scripts[name]) missing.push(name);
|
|
202
|
+
if (missing.length > 0) diagnostics.push(diag({
|
|
203
|
+
filePath: "package.json",
|
|
204
|
+
rule: "config-lint/package-json-scripts",
|
|
205
|
+
severity: "warning",
|
|
206
|
+
message: `Missing recommended scripts in package.json: ${missing.join(", ")}`,
|
|
207
|
+
help: `Add the following scripts to package.json: ${missing.map((s) => `"${s}"`).join(", ")}. Standard scripts improve CI/CD integration and developer experience.`,
|
|
208
|
+
detail: {
|
|
209
|
+
missing,
|
|
210
|
+
present: Object.keys(scripts)
|
|
211
|
+
}
|
|
212
|
+
}));
|
|
213
|
+
return diagnostics;
|
|
214
|
+
}
|
|
215
|
+
async function checkMissingPrettier(root) {
|
|
216
|
+
if ((await findFilesWithPrefix(root, ".prettierrc")).length > 0) return [];
|
|
217
|
+
if ((await findFilesWithName(root, "prettier.config")).length > 0) return [];
|
|
218
|
+
const pkgJson = await readJsonFile(join(root, "package.json"));
|
|
219
|
+
if (pkgJson && "prettier" in pkgJson) return [];
|
|
220
|
+
return [diag({
|
|
221
|
+
filePath: "package.json",
|
|
222
|
+
rule: "config-lint/missing-prettier",
|
|
223
|
+
severity: "info",
|
|
224
|
+
message: "No Prettier configuration found in the project",
|
|
225
|
+
help: "Add Prettier for consistent code formatting. Run: npm install --save-dev prettier && echo {} > .prettierrc",
|
|
226
|
+
detail: { checked: [
|
|
227
|
+
".prettierrc.*",
|
|
228
|
+
"prettier.config.*",
|
|
229
|
+
"package.json#prettier"
|
|
230
|
+
] }
|
|
231
|
+
})];
|
|
232
|
+
}
|
|
233
|
+
async function checkViteConfig(root) {
|
|
234
|
+
const diagnostics = [];
|
|
235
|
+
const viteConfigPath = await findFile(root, [
|
|
236
|
+
"vite.config.ts",
|
|
237
|
+
"vite.config.js",
|
|
238
|
+
"vite.config.mts",
|
|
239
|
+
"vite.config.mjs"
|
|
240
|
+
]);
|
|
241
|
+
if (!viteConfigPath) return diagnostics;
|
|
242
|
+
const relPath = basename(viteConfigPath);
|
|
243
|
+
const content = await readFileContent(viteConfigPath);
|
|
244
|
+
if (!(/build\s*:\s*\{[^}]*sourcemap\s*:/s.test(content) || /sourcemap\s*:\s*true/.test(content))) diagnostics.push(diag({
|
|
245
|
+
filePath: relPath,
|
|
246
|
+
rule: "config-lint/vite-config",
|
|
247
|
+
severity: "info",
|
|
248
|
+
message: `Vite config does not enable build.sourcemap`,
|
|
249
|
+
help: "Add `build: { sourcemap: true }` to your Vite config for better debugging with source maps.",
|
|
250
|
+
detail: {
|
|
251
|
+
key: "build.sourcemap",
|
|
252
|
+
current: null,
|
|
253
|
+
recommended: true
|
|
254
|
+
}
|
|
255
|
+
}));
|
|
256
|
+
if (!/server\s*:\s*\{[^}]*port\s*:/s.test(content)) diagnostics.push(diag({
|
|
257
|
+
filePath: relPath,
|
|
258
|
+
rule: "config-lint/vite-config",
|
|
259
|
+
severity: "info",
|
|
260
|
+
message: `Vite config does not set server.port`,
|
|
261
|
+
help: "Add `server: { port: 3000 }` (or your preferred port) to your Vite config for a consistent dev server port.",
|
|
262
|
+
detail: {
|
|
263
|
+
key: "server.port",
|
|
264
|
+
current: null,
|
|
265
|
+
recommended: 3e3
|
|
266
|
+
}
|
|
267
|
+
}));
|
|
268
|
+
return diagnostics;
|
|
269
|
+
}
|
|
270
|
+
async function checkNextConfig(root) {
|
|
271
|
+
const diagnostics = [];
|
|
272
|
+
const nextConfigPath = await findFile(root, [
|
|
273
|
+
"next.config.ts",
|
|
274
|
+
"next.config.js",
|
|
275
|
+
"next.config.mjs"
|
|
276
|
+
]);
|
|
277
|
+
if (!nextConfigPath) return diagnostics;
|
|
278
|
+
const relPath = basename(nextConfigPath);
|
|
279
|
+
const content = await readFileContent(nextConfigPath);
|
|
280
|
+
if (!/reactStrictMode\s*:\s*true/.test(content)) diagnostics.push(diag({
|
|
281
|
+
filePath: relPath,
|
|
282
|
+
rule: "config-lint/next-config",
|
|
283
|
+
severity: "info",
|
|
284
|
+
message: `Next.js config does not enable reactStrictMode`,
|
|
285
|
+
help: "Add `reactStrictMode: true` to your Next.js config. This helps identify unsafe lifecycle methods and deprecated patterns.",
|
|
286
|
+
detail: {
|
|
287
|
+
key: "reactStrictMode",
|
|
288
|
+
current: false,
|
|
289
|
+
recommended: true
|
|
290
|
+
}
|
|
291
|
+
}));
|
|
292
|
+
if (!/poweredByHeader\s*:\s*false/.test(content)) diagnostics.push(diag({
|
|
293
|
+
filePath: relPath,
|
|
294
|
+
rule: "config-lint/next-config",
|
|
295
|
+
severity: "info",
|
|
296
|
+
message: `Next.js config does not disable X-Powered-By header`,
|
|
297
|
+
help: "Add `poweredByHeader: false` to your Next.js config to hide the \"X-Powered-By: Next.js\" response header for security.",
|
|
298
|
+
detail: {
|
|
299
|
+
key: "poweredByHeader",
|
|
300
|
+
current: true,
|
|
301
|
+
recommended: false
|
|
302
|
+
}
|
|
303
|
+
}));
|
|
304
|
+
return diagnostics;
|
|
305
|
+
}
|
|
306
|
+
async function checkEditorconfig(root) {
|
|
307
|
+
if (await fileExists(join(root, ".editorconfig"))) return [];
|
|
308
|
+
return [diag({
|
|
309
|
+
filePath: ".editorconfig",
|
|
310
|
+
rule: "config-lint/editorconfig",
|
|
311
|
+
severity: "info",
|
|
312
|
+
message: "No .editorconfig file found in the project root",
|
|
313
|
+
help: "Add an .editorconfig file to enforce consistent coding style (indentation, charset, line endings) across editors and contributors.",
|
|
314
|
+
suggestion: {
|
|
315
|
+
type: "insert",
|
|
316
|
+
text: [
|
|
317
|
+
"root = true",
|
|
318
|
+
"",
|
|
319
|
+
"[*]",
|
|
320
|
+
"indent_style = space",
|
|
321
|
+
"indent_size = 2",
|
|
322
|
+
"end_of_line = lf",
|
|
323
|
+
"charset = utf-8",
|
|
324
|
+
"trim_trailing_whitespace = true",
|
|
325
|
+
"insert_final_newline = true",
|
|
326
|
+
"",
|
|
327
|
+
"[*.md]",
|
|
328
|
+
"trim_trailing_whitespace = false"
|
|
329
|
+
].join("\n"),
|
|
330
|
+
confidence: .9,
|
|
331
|
+
reason: "A standard .editorconfig ensures consistent formatting across all contributors and editors."
|
|
332
|
+
}
|
|
333
|
+
})];
|
|
334
|
+
}
|
|
335
|
+
const configLintEngine = {
|
|
336
|
+
name: "config-lint",
|
|
337
|
+
description: "Configuration validation: tsconfig strictness & target, ESLint/Prettier presence, package.json scripts, Vite/Next config checks, editorconfig",
|
|
338
|
+
supportedLanguages: ["typescript", "javascript"],
|
|
339
|
+
async run(context) {
|
|
340
|
+
const start = performance.now();
|
|
341
|
+
const diagnostics = [];
|
|
342
|
+
const root = context.rootDirectory;
|
|
343
|
+
if (!(context.languages.includes("typescript") || context.languages.includes("javascript"))) return {
|
|
344
|
+
engine: this.name,
|
|
345
|
+
diagnostics: [],
|
|
346
|
+
elapsed: performance.now() - start,
|
|
347
|
+
skipped: true,
|
|
348
|
+
skipReason: "No TypeScript or JavaScript detected in project"
|
|
349
|
+
};
|
|
350
|
+
const results = await Promise.all([
|
|
351
|
+
checkTsconfigStrict(root),
|
|
352
|
+
checkTsconfigTarget(root),
|
|
353
|
+
checkMissingEslint(root),
|
|
354
|
+
checkPackageJsonScripts(root),
|
|
355
|
+
checkMissingPrettier(root),
|
|
356
|
+
checkViteConfig(root),
|
|
357
|
+
checkNextConfig(root),
|
|
358
|
+
checkEditorconfig(root)
|
|
359
|
+
]);
|
|
360
|
+
for (const ruleDiagnostics of results) diagnostics.push(...ruleDiagnostics);
|
|
361
|
+
return {
|
|
362
|
+
engine: this.name,
|
|
363
|
+
diagnostics,
|
|
364
|
+
elapsed: performance.now() - start,
|
|
365
|
+
skipped: false
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
//#endregion
|
|
371
|
+
export { configLintEngine };
|