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,164 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { extname, join, relative } from "node:path";
|
|
3
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
4
|
+
import { minimatch } from "minimatch";
|
|
5
|
+
import micromatch from "micromatch";
|
|
6
|
+
|
|
7
|
+
//#region src/utils/discover.ts
|
|
8
|
+
/** File extension → language mapping */
|
|
9
|
+
const EXT_MAP = {
|
|
10
|
+
".ts": "typescript",
|
|
11
|
+
".tsx": "typescript",
|
|
12
|
+
".js": "javascript",
|
|
13
|
+
".jsx": "javascript",
|
|
14
|
+
".mjs": "javascript",
|
|
15
|
+
".cjs": "javascript",
|
|
16
|
+
".py": "python",
|
|
17
|
+
".go": "go",
|
|
18
|
+
".rs": "rust",
|
|
19
|
+
".rb": "ruby",
|
|
20
|
+
".php": "php",
|
|
21
|
+
".java": "java"
|
|
22
|
+
};
|
|
23
|
+
/** Detect languages from file extensions in the project */
|
|
24
|
+
async function detectLanguages(rootDir) {
|
|
25
|
+
const langCounts = /* @__PURE__ */ new Map();
|
|
26
|
+
await walkDir(rootDir, async (filePath) => {
|
|
27
|
+
const lang = EXT_MAP[extname(filePath)];
|
|
28
|
+
if (lang) langCounts.set(lang, (langCounts.get(lang) ?? 0) + 1);
|
|
29
|
+
}, void 0);
|
|
30
|
+
return [...langCounts.entries()].sort((a, b) => b[1] - a[1]).map(([lang]) => lang);
|
|
31
|
+
}
|
|
32
|
+
/** Detect frameworks from config files and dependencies */
|
|
33
|
+
async function detectFrameworks(rootDir) {
|
|
34
|
+
const frameworks = [];
|
|
35
|
+
try {
|
|
36
|
+
const pkgPath = join(rootDir, "package.json");
|
|
37
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
38
|
+
const allDeps = {
|
|
39
|
+
...pkg.dependencies,
|
|
40
|
+
...pkg.devDependencies
|
|
41
|
+
};
|
|
42
|
+
if (allDeps["next"]) frameworks.push("next.js");
|
|
43
|
+
if (allDeps["react"]) frameworks.push("react");
|
|
44
|
+
if (allDeps["vue"] || allDeps["@vue/compiler-sfc"]) frameworks.push("vue");
|
|
45
|
+
if (allDeps["svelte"] || allDeps["@sveltejs/kit"]) frameworks.push("svelte");
|
|
46
|
+
if (allDeps["@angular/core"]) frameworks.push("angular");
|
|
47
|
+
if (allDeps["express"]) frameworks.push("express");
|
|
48
|
+
if (allDeps["fastify"]) frameworks.push("fastify");
|
|
49
|
+
if (allDeps["@nestjs/core"]) frameworks.push("nestjs");
|
|
50
|
+
} catch {}
|
|
51
|
+
try {
|
|
52
|
+
const req = await readFile(join(rootDir, "requirements.txt"), "utf-8");
|
|
53
|
+
if (req.includes("django")) frameworks.push("django");
|
|
54
|
+
if (req.includes("flask")) frameworks.push("flask");
|
|
55
|
+
if (req.includes("fastapi")) frameworks.push("fastapi");
|
|
56
|
+
} catch {}
|
|
57
|
+
try {
|
|
58
|
+
if ((await readFile(join(rootDir, "Gemfile"), "utf-8")).includes("rails")) frameworks.push("rails");
|
|
59
|
+
} catch {}
|
|
60
|
+
try {
|
|
61
|
+
const compPath = join(rootDir, "composer.json");
|
|
62
|
+
if (JSON.parse(await readFile(compPath, "utf-8")).require?.["laravel/framework"]) frameworks.push("laravel");
|
|
63
|
+
} catch {}
|
|
64
|
+
if (frameworks.length === 0) frameworks.push("none");
|
|
65
|
+
return frameworks;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Load .deep-slopignore patterns from the project root.
|
|
69
|
+
* Returns an array of glob patterns (lines that are not empty or comments).
|
|
70
|
+
*/
|
|
71
|
+
function loadDeepSlopIgnore(rootDir) {
|
|
72
|
+
const ignorePath = join(rootDir, ".deep-slopignore");
|
|
73
|
+
if (!existsSync(ignorePath)) return [];
|
|
74
|
+
try {
|
|
75
|
+
return readFileSync(ignorePath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#"));
|
|
76
|
+
} catch {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check if a relative file path matches any ignore pattern using minimatch.
|
|
82
|
+
* Supports gitignore-style glob patterns.
|
|
83
|
+
*/
|
|
84
|
+
function matchesIgnorePattern(relPath, patterns) {
|
|
85
|
+
for (const pattern of patterns) {
|
|
86
|
+
if (pattern.startsWith("!")) continue;
|
|
87
|
+
if (minimatch(relPath, pattern, { dot: true })) return true;
|
|
88
|
+
if (!pattern.includes("/")) {
|
|
89
|
+
if (minimatch(relPath.split("/").pop() ?? "", pattern, { dot: true })) return true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
/** Collect all source files to scan */
|
|
95
|
+
async function collectFiles(rootDir, languages, excludePatterns = [], includeFiles, ignorePatterns, includePatterns) {
|
|
96
|
+
if (includeFiles) {
|
|
97
|
+
let files = includeFiles.map((f) => f.startsWith("/") ? f : join(rootDir, f));
|
|
98
|
+
if (ignorePatterns && ignorePatterns.length > 0) files = files.filter((f) => {
|
|
99
|
+
return !matchesIgnorePattern(relative(rootDir, f), ignorePatterns);
|
|
100
|
+
});
|
|
101
|
+
return files;
|
|
102
|
+
}
|
|
103
|
+
const targetExts = new Set(Object.entries(EXT_MAP).filter(([, lang]) => languages.includes(lang)).map(([ext]) => ext));
|
|
104
|
+
const allIgnorePatterns = [...loadDeepSlopIgnore(rootDir), ...ignorePatterns ?? []];
|
|
105
|
+
const excludeSet = new Set(excludePatterns);
|
|
106
|
+
const files = [];
|
|
107
|
+
await walkDir(rootDir, async (filePath) => {
|
|
108
|
+
const relPath = relative(rootDir, filePath);
|
|
109
|
+
if (isExcluded(relPath, excludeSet)) return;
|
|
110
|
+
if (allIgnorePatterns.length > 0 && matchesIgnorePattern(relPath, allIgnorePatterns)) return;
|
|
111
|
+
const ext = extname(filePath);
|
|
112
|
+
if (targetExts.has(ext)) {
|
|
113
|
+
if (includePatterns && includePatterns.length > 0) {
|
|
114
|
+
if (!includePatterns.some((pat) => minimatch(relPath, pat, { dot: true }) || !pat.includes("/") && minimatch(filePath.split("/").pop() ?? "", pat, { dot: true }))) return;
|
|
115
|
+
}
|
|
116
|
+
files.push(filePath);
|
|
117
|
+
}
|
|
118
|
+
}, excludeSet);
|
|
119
|
+
return files;
|
|
120
|
+
}
|
|
121
|
+
/** Check if path matches any exclude pattern */
|
|
122
|
+
function isExcluded(relPath, excludes) {
|
|
123
|
+
const segments = relPath.split("/");
|
|
124
|
+
const patterns = [...excludes];
|
|
125
|
+
for (const pattern of patterns) if (segments.some((s) => s === pattern)) return true;
|
|
126
|
+
if (micromatch.isMatch(relPath, patterns)) return true;
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
/** Directories to always skip during walk (never recurse into) */
|
|
130
|
+
const SKIP_DIR_NAMES = new Set([
|
|
131
|
+
"node_modules",
|
|
132
|
+
".git",
|
|
133
|
+
"dist",
|
|
134
|
+
"build",
|
|
135
|
+
"coverage",
|
|
136
|
+
".next",
|
|
137
|
+
".nuxt",
|
|
138
|
+
".pnpm-store",
|
|
139
|
+
".turbo",
|
|
140
|
+
".vercel",
|
|
141
|
+
".cache",
|
|
142
|
+
"tmp-",
|
|
143
|
+
"__pycache__",
|
|
144
|
+
".venv",
|
|
145
|
+
"venv",
|
|
146
|
+
".tox",
|
|
147
|
+
"target",
|
|
148
|
+
"vendor",
|
|
149
|
+
"bower_components"
|
|
150
|
+
]);
|
|
151
|
+
/** Walk directory recursively, skipping excluded directories early */
|
|
152
|
+
async function walkDir(dir, visitor, excludeSet) {
|
|
153
|
+
try {
|
|
154
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
155
|
+
for (const entry of entries) if (entry.isDirectory()) {
|
|
156
|
+
if (SKIP_DIR_NAMES.has(entry.name)) continue;
|
|
157
|
+
if (excludeSet && excludeSet.has(entry.name)) continue;
|
|
158
|
+
await walkDir(join(dir, entry.name), visitor, excludeSet);
|
|
159
|
+
} else if (entry.isFile()) await visitor(join(dir, entry.name));
|
|
160
|
+
} catch {}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
//#endregion
|
|
164
|
+
export { detectFrameworks as n, detectLanguages as r, collectFiles as t };
|