Setting the file. One moment. Detect Plugins · Wp Plugin Development · WordPress/agent-skills · Skills Docsscripts/detect_plugins.mjs
scripts/detect_plugins.mjs
JavaScript·121 lines·3 KB
11
".next"
,
12 ".turbo",
13]);
14
15function statSafe(p) {
16 try {
17 return fs.statSync(p);
18 } catch {
19 return null;
20 }
21}
22
23function readFileSafe(p, maxBytes = 128 * 1024) {
24 try {
25 const buf = fs.readFileSync(p);
26 if (buf.byteLength > maxBytes) return buf.subarray(0, maxBytes).toString("utf8");
27 return buf.toString("utf8");
28 } catch {
29 return null;
30 }
31}
32
33function findFilesRecursive(repoRoot, predicate, { maxFiles = 6000, maxDepth = 10 } = {}) {
34 const results = [];
35 const queue = [{ dir: repoRoot, depth: 0 }];
36 let visited = 0;
37
38 while (queue.length > 0) {
39 const { dir, depth } = queue.shift();
40 if (depth > maxDepth) continue;
41
42 let entries;
43 try {
44 entries = fs.readdirSync(dir, { withFileTypes: true });
45 } catch {
46 continue;
47 }
48
49 for (const ent of entries) {
50 const fullPath = path.join(dir, ent.name);
51 if (ent.isDirectory()) {
52 if (DEFAULT_IGNORES.has(ent.name)) continue;
53 queue.push({ dir: fullPath, depth: depth + 1 });
54 continue;
55 }
56 if (!ent.isFile()) continue;
57
58 visited += 1;
59 if (visited > maxFiles) return { results, truncated: true };
60 if (predicate(fullPath)) results.push(fullPath);
61 }
62 }
63
64 return { results, truncated: false };
65}
66
67function parsePluginHeader(contents) {
68 // WordPress reads plugin headers from the top of the file. We only need key fields.
69 const header = {};
70 const pairs = [
71 ["Plugin Name", "name"],
72 ["Plugin URI", "uri"],
73 ["Description", "description"],
74 ["Version", "version"],
75 ["Author", "author"],
76 ["Author URI", "authorUri"],
77 ["Text Domain", "textDomain"],
78 ["Domain Path", "domainPath"],
79 ];
80 for (const [label, key] of pairs) {
81 const m = contents.match(new RegExp(`^\\s*\\*?\\s*${label}:\\s*(.+)\\s*$`, "im"));
82 if (m) header[key] = m[1].trim();
83 }
84 if (!header.name) return null;
85 return header;
86}
87
88function main() {
89 const repoRoot = process.cwd();
90
91 const { results: phpFiles, truncated } = findFilesRecursive(repoRoot, (p) => p.toLowerCase().endsWith(".php"), {
92 maxFiles: 5000,
93 maxDepth: 10,
94 });
95
96 const plugins = [];
97
98 for (const phpPath of phpFiles) {
99 const txt = readFileSafe(phpPath);
100 if (!txt) continue;
101 if (!/Plugin Name:/i.test(txt)) continue;
102 const header = parsePluginHeader(txt);
103 if (!header) continue;
104 plugins.push({
105 pluginFile: path.relative(repoRoot, phpPath),
106 ...header,
107 });
108 }
109
110 const report = {
111 tool: { name: "detect_plugins", version: "0.1.0" },
112 repoRoot,
113 truncated,
114 count: plugins.length,
115 plugins,
116 };
117
118 process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
119}
120
121main();
122