atomism 0.1.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.
- package/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/chunk-34O5KJWR.js +81 -0
- package/dist/chunk-34O5KJWR.js.map +1 -0
- package/dist/chunk-55AP34JO.js +116 -0
- package/dist/chunk-55AP34JO.js.map +1 -0
- package/dist/chunk-6MDHM2B4.js +17 -0
- package/dist/chunk-6MDHM2B4.js.map +1 -0
- package/dist/chunk-GU2R4KLP.js +43 -0
- package/dist/chunk-GU2R4KLP.js.map +1 -0
- package/dist/chunk-H7WC3NXZ.js +39 -0
- package/dist/chunk-H7WC3NXZ.js.map +1 -0
- package/dist/chunk-P33CQFMY.js +329 -0
- package/dist/chunk-P33CQFMY.js.map +1 -0
- package/dist/chunk-P6X7T4KA.js +200 -0
- package/dist/chunk-P6X7T4KA.js.map +1 -0
- package/dist/chunk-PLQJM2KT.js +9 -0
- package/dist/chunk-PLQJM2KT.js.map +1 -0
- package/dist/chunk-RS2IEGW3.js +10 -0
- package/dist/chunk-RS2IEGW3.js.map +1 -0
- package/dist/chunk-S6Z5G5DB.js +84 -0
- package/dist/chunk-S6Z5G5DB.js.map +1 -0
- package/dist/chunk-UVUDQ4XP.js +259 -0
- package/dist/chunk-UVUDQ4XP.js.map +1 -0
- package/dist/chunk-UWVZQSP4.js +597 -0
- package/dist/chunk-UWVZQSP4.js.map +1 -0
- package/dist/chunk-YKJO3ZFY.js +308 -0
- package/dist/chunk-YKJO3ZFY.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +152 -0
- package/dist/cli.js.map +1 -0
- package/dist/create-atom-AXPDBYQL.js +153 -0
- package/dist/create-atom-AXPDBYQL.js.map +1 -0
- package/dist/escalate-BTEJT5NL.js +211 -0
- package/dist/escalate-BTEJT5NL.js.map +1 -0
- package/dist/extract-RPKCTINT.js +514 -0
- package/dist/extract-RPKCTINT.js.map +1 -0
- package/dist/graduate-453M7ZRQ.js +222 -0
- package/dist/graduate-453M7ZRQ.js.map +1 -0
- package/dist/helpers-PJPFPYBQ.js +11 -0
- package/dist/helpers-PJPFPYBQ.js.map +1 -0
- package/dist/history-OPD7NLZW.js +258 -0
- package/dist/history-OPD7NLZW.js.map +1 -0
- package/dist/import-generator-4CKRBMTE.js +1864 -0
- package/dist/import-generator-4CKRBMTE.js.map +1 -0
- package/dist/index.d.ts +230 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/init-2FINDMYK.js +741 -0
- package/dist/init-2FINDMYK.js.map +1 -0
- package/dist/list-NEBVBGG3.js +71 -0
- package/dist/list-NEBVBGG3.js.map +1 -0
- package/dist/parser-3BILOSOO.js +157 -0
- package/dist/parser-3BILOSOO.js.map +1 -0
- package/dist/plan-DNVARHWH.js +249 -0
- package/dist/plan-DNVARHWH.js.map +1 -0
- package/dist/register-XTRMSH7Y.js +91 -0
- package/dist/register-XTRMSH7Y.js.map +1 -0
- package/dist/revert-J4CRDE2K.js +87 -0
- package/dist/revert-J4CRDE2K.js.map +1 -0
- package/dist/run-3GI3SBYL.js +188 -0
- package/dist/run-3GI3SBYL.js.map +1 -0
- package/dist/scan-generators-ST4TBEY7.js +375 -0
- package/dist/scan-generators-ST4TBEY7.js.map +1 -0
- package/dist/signatures-K5QIL4WG.js +258 -0
- package/dist/signatures-K5QIL4WG.js.map +1 -0
- package/dist/skills-assign-IHOXX4AI.js +182 -0
- package/dist/skills-assign-IHOXX4AI.js.map +1 -0
- package/dist/skills-load-JSD5UG2K.js +20 -0
- package/dist/skills-load-JSD5UG2K.js.map +1 -0
- package/dist/skills-scan-WACJFRJN.js +25 -0
- package/dist/skills-scan-WACJFRJN.js.map +1 -0
- package/dist/skills-suggest-JFI2NUJI.js +269 -0
- package/dist/skills-suggest-JFI2NUJI.js.map +1 -0
- package/dist/status-KQVSAZFR.js +111 -0
- package/dist/status-KQVSAZFR.js.map +1 -0
- package/dist/suggest-IFFJQFIW.js +183 -0
- package/dist/suggest-IFFJQFIW.js.map +1 -0
- package/dist/test-HP3FG3MO.js +152 -0
- package/dist/test-HP3FG3MO.js.map +1 -0
- package/dist/test-gen-2ZGPOP35.js +347 -0
- package/dist/test-gen-2ZGPOP35.js.map +1 -0
- package/dist/trust-4R26DULG.js +248 -0
- package/dist/trust-4R26DULG.js.map +1 -0
- package/dist/validate-generator-46H2LYYQ.js +410 -0
- package/dist/validate-generator-46H2LYYQ.js.map +1 -0
- package/dist/workflow-5UVLBS7J.js +655 -0
- package/dist/workflow-5UVLBS7J.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import {
|
|
2
|
+
toErrorMessage
|
|
3
|
+
} from "./chunk-PLQJM2KT.js";
|
|
4
|
+
|
|
5
|
+
// src/scanner/index.ts
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { join, basename } from "path";
|
|
8
|
+
import { readdir, readFile, access, stat } from "fs/promises";
|
|
9
|
+
var GeneratorSystemSchema = z.enum([
|
|
10
|
+
"plop",
|
|
11
|
+
"hygen",
|
|
12
|
+
"yeoman",
|
|
13
|
+
"cookiecutter",
|
|
14
|
+
"custom"
|
|
15
|
+
]);
|
|
16
|
+
var ComplexitySchema = z.enum(["simple", "moderate", "complex"]);
|
|
17
|
+
var DetectedGeneratorSchema = z.object({
|
|
18
|
+
system: GeneratorSystemSchema,
|
|
19
|
+
name: z.string().min(1),
|
|
20
|
+
location: z.string().min(1),
|
|
21
|
+
complexity: ComplexitySchema,
|
|
22
|
+
description: z.string().optional(),
|
|
23
|
+
templates: z.array(z.string()).optional()
|
|
24
|
+
});
|
|
25
|
+
var ScanResultSchema = z.object({
|
|
26
|
+
generators: z.array(DetectedGeneratorSchema),
|
|
27
|
+
scannedPaths: z.array(z.string()),
|
|
28
|
+
errors: z.array(z.string()).optional()
|
|
29
|
+
});
|
|
30
|
+
async function pathExists(path) {
|
|
31
|
+
try {
|
|
32
|
+
await access(path);
|
|
33
|
+
return true;
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function isDirectory(path) {
|
|
39
|
+
try {
|
|
40
|
+
const stats = await stat(path);
|
|
41
|
+
return stats.isDirectory();
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function estimateComplexity(templateCount, hasPrompts) {
|
|
47
|
+
if (templateCount > 5 || hasPrompts) {
|
|
48
|
+
return "complex";
|
|
49
|
+
}
|
|
50
|
+
if (templateCount > 2) {
|
|
51
|
+
return "moderate";
|
|
52
|
+
}
|
|
53
|
+
return "simple";
|
|
54
|
+
}
|
|
55
|
+
async function scanPlop(projectRoot) {
|
|
56
|
+
const generators = [];
|
|
57
|
+
const plopFiles = ["plopfile.js", "plopfile.ts", "plopfile.mjs", "plopfile.cjs"];
|
|
58
|
+
for (const plopFile of plopFiles) {
|
|
59
|
+
const plopPath = join(projectRoot, plopFile);
|
|
60
|
+
if (await pathExists(plopPath)) {
|
|
61
|
+
try {
|
|
62
|
+
const content = await readFile(plopPath, "utf-8");
|
|
63
|
+
const generatorMatches = content.matchAll(
|
|
64
|
+
/setGenerator\s*\(\s*['"]([^'"]+)['"]/g
|
|
65
|
+
);
|
|
66
|
+
for (const match of generatorMatches) {
|
|
67
|
+
const name = match[1];
|
|
68
|
+
if (name) {
|
|
69
|
+
const hasPrompts = content.includes("prompts:") || content.includes("prompts(");
|
|
70
|
+
const templateMatches = content.match(/templateFile|templateFiles/g) || [];
|
|
71
|
+
generators.push({
|
|
72
|
+
system: "plop",
|
|
73
|
+
name,
|
|
74
|
+
location: plopPath,
|
|
75
|
+
complexity: estimateComplexity(templateMatches.length, hasPrompts),
|
|
76
|
+
description: `Plop generator: ${name}`
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (generators.length === 0) {
|
|
81
|
+
generators.push({
|
|
82
|
+
system: "plop",
|
|
83
|
+
name: "unknown",
|
|
84
|
+
location: plopPath,
|
|
85
|
+
complexity: "moderate",
|
|
86
|
+
description: "Plop file detected but generators could not be parsed"
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
} catch {
|
|
90
|
+
generators.push({
|
|
91
|
+
system: "plop",
|
|
92
|
+
name: "unknown",
|
|
93
|
+
location: plopPath,
|
|
94
|
+
complexity: "moderate",
|
|
95
|
+
description: "Plop file detected but could not be read"
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return generators;
|
|
102
|
+
}
|
|
103
|
+
async function scanHygen(projectRoot) {
|
|
104
|
+
const generators = [];
|
|
105
|
+
const templatesDir = join(projectRoot, "_templates");
|
|
106
|
+
if (!await pathExists(templatesDir) || !await isDirectory(templatesDir)) {
|
|
107
|
+
return generators;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const entries = await readdir(templatesDir, { withFileTypes: true });
|
|
111
|
+
for (const entry of entries) {
|
|
112
|
+
if (entry.isDirectory()) {
|
|
113
|
+
const generatorDir = join(templatesDir, entry.name);
|
|
114
|
+
const actions = await readdir(generatorDir, { withFileTypes: true });
|
|
115
|
+
const actionDirs = actions.filter((a) => a.isDirectory());
|
|
116
|
+
for (const action of actionDirs) {
|
|
117
|
+
const actionDir = join(generatorDir, action.name);
|
|
118
|
+
const templates = await readdir(actionDir);
|
|
119
|
+
const ejsFiles = templates.filter(
|
|
120
|
+
(t) => t.endsWith(".ejs.t") || t.endsWith(".t")
|
|
121
|
+
);
|
|
122
|
+
const hasPrompts = templates.some(
|
|
123
|
+
(t) => t === "prompt.js" || t === "index.js"
|
|
124
|
+
);
|
|
125
|
+
generators.push({
|
|
126
|
+
system: "hygen",
|
|
127
|
+
name: `${entry.name}/${action.name}`,
|
|
128
|
+
location: actionDir,
|
|
129
|
+
complexity: estimateComplexity(ejsFiles.length, hasPrompts),
|
|
130
|
+
description: `Hygen generator: ${entry.name} ${action.name}`,
|
|
131
|
+
templates: ejsFiles
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
138
|
+
return generators;
|
|
139
|
+
}
|
|
140
|
+
async function scanYeoman(projectRoot) {
|
|
141
|
+
const generators = [];
|
|
142
|
+
const generatorsDir = join(projectRoot, "generators");
|
|
143
|
+
if (await pathExists(generatorsDir) && await isDirectory(generatorsDir)) {
|
|
144
|
+
try {
|
|
145
|
+
const entries = await readdir(generatorsDir, { withFileTypes: true });
|
|
146
|
+
for (const entry of entries) {
|
|
147
|
+
if (entry.isDirectory()) {
|
|
148
|
+
const genDir = join(generatorsDir, entry.name);
|
|
149
|
+
const indexPath = join(genDir, "index.js");
|
|
150
|
+
if (await pathExists(indexPath)) {
|
|
151
|
+
const content = await readFile(indexPath, "utf-8");
|
|
152
|
+
const hasPrompting = content.includes("prompting");
|
|
153
|
+
const hasWriting = content.includes("writing");
|
|
154
|
+
generators.push({
|
|
155
|
+
system: "yeoman",
|
|
156
|
+
name: entry.name,
|
|
157
|
+
location: genDir,
|
|
158
|
+
complexity: hasPrompting && hasWriting ? "complex" : "moderate",
|
|
159
|
+
description: `Yeoman generator: ${entry.name}`
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
168
|
+
if (await pathExists(packageJsonPath)) {
|
|
169
|
+
try {
|
|
170
|
+
const content = await readFile(packageJsonPath, "utf-8");
|
|
171
|
+
const pkg = JSON.parse(content);
|
|
172
|
+
const allDeps = {
|
|
173
|
+
...pkg.dependencies || {},
|
|
174
|
+
...pkg.devDependencies || {}
|
|
175
|
+
};
|
|
176
|
+
for (const dep of Object.keys(allDeps)) {
|
|
177
|
+
if (dep.startsWith("generator-")) {
|
|
178
|
+
const name = dep.replace("generator-", "");
|
|
179
|
+
generators.push({
|
|
180
|
+
system: "yeoman",
|
|
181
|
+
name,
|
|
182
|
+
location: `node_modules/${dep}`,
|
|
183
|
+
complexity: "moderate",
|
|
184
|
+
description: `Yeoman generator package: ${dep}`
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return generators;
|
|
192
|
+
}
|
|
193
|
+
async function scanCookiecutter(projectRoot) {
|
|
194
|
+
const generators = [];
|
|
195
|
+
const cookiecutterPath = join(projectRoot, "cookiecutter.json");
|
|
196
|
+
if (await pathExists(cookiecutterPath)) {
|
|
197
|
+
try {
|
|
198
|
+
const content = await readFile(cookiecutterPath, "utf-8");
|
|
199
|
+
const config = JSON.parse(content);
|
|
200
|
+
const paramCount = Object.keys(config).length;
|
|
201
|
+
const templateDir = join(projectRoot, "{{cookiecutter.project_slug}}");
|
|
202
|
+
let templateCount = 0;
|
|
203
|
+
if (await pathExists(templateDir)) {
|
|
204
|
+
const templates = await readdir(templateDir, { recursive: true });
|
|
205
|
+
templateCount = templates.length;
|
|
206
|
+
}
|
|
207
|
+
generators.push({
|
|
208
|
+
system: "cookiecutter",
|
|
209
|
+
name: basename(projectRoot),
|
|
210
|
+
location: cookiecutterPath,
|
|
211
|
+
complexity: estimateComplexity(
|
|
212
|
+
templateCount,
|
|
213
|
+
paramCount > 3
|
|
214
|
+
),
|
|
215
|
+
description: `Cookiecutter template with ${paramCount} parameters`
|
|
216
|
+
});
|
|
217
|
+
} catch {
|
|
218
|
+
generators.push({
|
|
219
|
+
system: "cookiecutter",
|
|
220
|
+
name: basename(projectRoot),
|
|
221
|
+
location: cookiecutterPath,
|
|
222
|
+
complexity: "moderate",
|
|
223
|
+
description: "Cookiecutter template detected"
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return generators;
|
|
228
|
+
}
|
|
229
|
+
async function scanCustom(projectRoot) {
|
|
230
|
+
const generators = [];
|
|
231
|
+
const searchDirs = ["scripts", "bin", "tools", "generators"];
|
|
232
|
+
const patterns = ["generate", "scaffold", "create", "new"];
|
|
233
|
+
for (const dir of searchDirs) {
|
|
234
|
+
const dirPath = join(projectRoot, dir);
|
|
235
|
+
if (!await pathExists(dirPath) || !await isDirectory(dirPath)) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
const entries = await readdir(dirPath);
|
|
240
|
+
for (const entry of entries) {
|
|
241
|
+
const entryLower = entry.toLowerCase();
|
|
242
|
+
const matchesPattern = patterns.some(
|
|
243
|
+
(p) => entryLower.includes(p)
|
|
244
|
+
);
|
|
245
|
+
if (matchesPattern) {
|
|
246
|
+
const filePath = join(dirPath, entry);
|
|
247
|
+
const stats = await stat(filePath);
|
|
248
|
+
if (stats.isFile()) {
|
|
249
|
+
generators.push({
|
|
250
|
+
system: "custom",
|
|
251
|
+
name: entry.replace(/\.(js|ts|sh|py)$/, ""),
|
|
252
|
+
location: filePath,
|
|
253
|
+
complexity: "moderate",
|
|
254
|
+
description: `Custom generator script: ${entry}`
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return generators;
|
|
263
|
+
}
|
|
264
|
+
async function scanGenerators(projectRoot, options = {}) {
|
|
265
|
+
const systems = options.systems || ["plop", "hygen", "yeoman", "cookiecutter", "custom"];
|
|
266
|
+
const generators = [];
|
|
267
|
+
const scannedPaths = [];
|
|
268
|
+
const errors = [];
|
|
269
|
+
const scanners = {
|
|
270
|
+
plop: scanPlop,
|
|
271
|
+
hygen: scanHygen,
|
|
272
|
+
yeoman: scanYeoman,
|
|
273
|
+
cookiecutter: scanCookiecutter,
|
|
274
|
+
custom: scanCustom
|
|
275
|
+
};
|
|
276
|
+
for (const system of systems) {
|
|
277
|
+
const scanner = scanners[system];
|
|
278
|
+
if (scanner) {
|
|
279
|
+
try {
|
|
280
|
+
const found = await scanner(projectRoot);
|
|
281
|
+
generators.push(...found);
|
|
282
|
+
scannedPaths.push(`${system}: ${projectRoot}`);
|
|
283
|
+
} catch (err) {
|
|
284
|
+
const message = toErrorMessage(err);
|
|
285
|
+
errors.push(`Error scanning ${system}: ${message}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
generators,
|
|
291
|
+
scannedPaths,
|
|
292
|
+
errors: errors.length > 0 ? errors : void 0
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/commands/scan-generators.ts
|
|
297
|
+
async function scanGeneratorsCommand(options) {
|
|
298
|
+
const projectRoot = process.cwd();
|
|
299
|
+
let systems;
|
|
300
|
+
if (options.system) {
|
|
301
|
+
const parseResult = GeneratorSystemSchema.safeParse(options.system.toLowerCase());
|
|
302
|
+
if (!parseResult.success) {
|
|
303
|
+
const validSystems = GeneratorSystemSchema.options;
|
|
304
|
+
if (options.json) {
|
|
305
|
+
console.log(
|
|
306
|
+
JSON.stringify({
|
|
307
|
+
success: false,
|
|
308
|
+
error: `Invalid system: ${options.system}. Valid options: ${validSystems.join(", ")}`
|
|
309
|
+
})
|
|
310
|
+
);
|
|
311
|
+
} else {
|
|
312
|
+
console.error(
|
|
313
|
+
`Error: Invalid system '${options.system}'. Valid options: ${validSystems.join(", ")}`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
systems = [parseResult.data];
|
|
319
|
+
}
|
|
320
|
+
const result = await scanGenerators(projectRoot, { systems });
|
|
321
|
+
if (options.json) {
|
|
322
|
+
console.log(
|
|
323
|
+
JSON.stringify({
|
|
324
|
+
success: true,
|
|
325
|
+
...result
|
|
326
|
+
})
|
|
327
|
+
);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (result.generators.length === 0) {
|
|
331
|
+
console.log("No generators found.");
|
|
332
|
+
console.log("");
|
|
333
|
+
console.log("Scanned for:");
|
|
334
|
+
console.log(" \u2022 Plop (plopfile.js/ts)");
|
|
335
|
+
console.log(" \u2022 Hygen (_templates/)");
|
|
336
|
+
console.log(" \u2022 Yeoman (generators/, generator-* packages)");
|
|
337
|
+
console.log(" \u2022 Cookiecutter (cookiecutter.json)");
|
|
338
|
+
console.log(" \u2022 Custom scripts (scripts/, bin/, tools/)");
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
console.log(`Found ${result.generators.length} generator(s):
|
|
342
|
+
`);
|
|
343
|
+
const bySystem = /* @__PURE__ */ new Map();
|
|
344
|
+
for (const gen of result.generators) {
|
|
345
|
+
const existing = bySystem.get(gen.system) || [];
|
|
346
|
+
existing.push(gen);
|
|
347
|
+
bySystem.set(gen.system, existing);
|
|
348
|
+
}
|
|
349
|
+
for (const [system, generators] of bySystem) {
|
|
350
|
+
console.log(`${system.toUpperCase()}:`);
|
|
351
|
+
for (const gen of generators) {
|
|
352
|
+
const complexityIcon = gen.complexity === "complex" ? "\u25CF\u25CF\u25CF" : gen.complexity === "moderate" ? "\u25CF\u25CF\u25CB" : "\u25CF\u25CB\u25CB";
|
|
353
|
+
console.log(` ${gen.name}`);
|
|
354
|
+
console.log(` Location: ${gen.location}`);
|
|
355
|
+
console.log(` Complexity: ${complexityIcon} (${gen.complexity})`);
|
|
356
|
+
if (gen.description) {
|
|
357
|
+
console.log(` ${gen.description}`);
|
|
358
|
+
}
|
|
359
|
+
if (gen.templates && gen.templates.length > 0) {
|
|
360
|
+
console.log(` Templates: ${gen.templates.join(", ")}`);
|
|
361
|
+
}
|
|
362
|
+
console.log("");
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (result.errors && result.errors.length > 0) {
|
|
366
|
+
console.log("Warnings:");
|
|
367
|
+
for (const error of result.errors) {
|
|
368
|
+
console.log(` \u26A0 ${error}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
export {
|
|
373
|
+
scanGeneratorsCommand
|
|
374
|
+
};
|
|
375
|
+
//# sourceMappingURL=scan-generators-ST4TBEY7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/scanner/index.ts","../src/commands/scan-generators.ts"],"sourcesContent":["/**\n * Generator scanner for detecting existing generators.\n *\n * Detects generators from:\n * - Plop (plopfile.js)\n * - Hygen (_templates/)\n * - Yeoman (generators/)\n * - Cookiecutter (cookiecutter.json)\n * - Custom scripts\n *\n * @module scanner\n */\n\nimport { z } from 'zod';\nimport { join, basename } from 'node:path';\nimport { readdir, readFile, access, stat } from 'node:fs/promises';\nimport { toErrorMessage } from '../utils/errors.js';\n\n/**\n * Supported generator systems.\n */\nexport const GeneratorSystemSchema = z.enum([\n 'plop',\n 'hygen',\n 'yeoman',\n 'cookiecutter',\n 'custom',\n]);\n\nexport type GeneratorSystem = z.infer<typeof GeneratorSystemSchema>;\n\n/**\n * Complexity levels for generators.\n */\nexport const ComplexitySchema = z.enum(['simple', 'moderate', 'complex']);\n\nexport type Complexity = z.infer<typeof ComplexitySchema>;\n\n/**\n * Schema for a detected generator.\n */\nexport const DetectedGeneratorSchema = z.object({\n system: GeneratorSystemSchema,\n name: z.string().min(1),\n location: z.string().min(1),\n complexity: ComplexitySchema,\n description: z.string().optional(),\n templates: z.array(z.string()).optional(),\n});\n\nexport type DetectedGenerator = z.infer<typeof DetectedGeneratorSchema>;\n\n/**\n * Schema for scan results.\n */\nexport const ScanResultSchema = z.object({\n generators: z.array(DetectedGeneratorSchema),\n scannedPaths: z.array(z.string()),\n errors: z.array(z.string()).optional(),\n});\n\nexport type ScanResult = z.infer<typeof ScanResultSchema>;\n\n/**\n * Check if a path exists.\n */\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a path is a directory.\n */\nasync function isDirectory(path: string): Promise<boolean> {\n try {\n const stats = await stat(path);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Estimate complexity based on file count and content.\n */\nfunction estimateComplexity(\n templateCount: number,\n hasPrompts: boolean\n): Complexity {\n if (templateCount > 5 || hasPrompts) {\n return 'complex';\n }\n if (templateCount > 2) {\n return 'moderate';\n }\n return 'simple';\n}\n\n/**\n * Scan for Plop generators.\n *\n * Looks for plopfile.js or plopfile.ts in the project root.\n */\nexport async function scanPlop(projectRoot: string): Promise<DetectedGenerator[]> {\n const generators: DetectedGenerator[] = [];\n const plopFiles = ['plopfile.js', 'plopfile.ts', 'plopfile.mjs', 'plopfile.cjs'];\n\n for (const plopFile of plopFiles) {\n const plopPath = join(projectRoot, plopFile);\n if (await pathExists(plopPath)) {\n try {\n const content = await readFile(plopPath, 'utf-8');\n\n // Extract generator names from setGenerator calls\n const generatorMatches = content.matchAll(\n /setGenerator\\s*\\(\\s*['\"]([^'\"]+)['\"]/g\n );\n\n for (const match of generatorMatches) {\n const name = match[1];\n if (name) {\n // Check for prompts to estimate complexity\n const hasPrompts = content.includes('prompts:') || content.includes('prompts(');\n const templateMatches = content.match(/templateFile|templateFiles/g) || [];\n\n generators.push({\n system: 'plop',\n name,\n location: plopPath,\n complexity: estimateComplexity(templateMatches.length, hasPrompts),\n description: `Plop generator: ${name}`,\n });\n }\n }\n\n // If no generators found but plopfile exists, report the file\n if (generators.length === 0) {\n generators.push({\n system: 'plop',\n name: 'unknown',\n location: plopPath,\n complexity: 'moderate',\n description: 'Plop file detected but generators could not be parsed',\n });\n }\n } catch {\n // File exists but couldn't be read\n generators.push({\n system: 'plop',\n name: 'unknown',\n location: plopPath,\n complexity: 'moderate',\n description: 'Plop file detected but could not be read',\n });\n }\n break; // Only process first plopfile found\n }\n }\n\n return generators;\n}\n\n/**\n * Scan for Hygen templates.\n *\n * Looks for _templates/ directory with generator subdirectories.\n */\nexport async function scanHygen(projectRoot: string): Promise<DetectedGenerator[]> {\n const generators: DetectedGenerator[] = [];\n const templatesDir = join(projectRoot, '_templates');\n\n if (!(await pathExists(templatesDir)) || !(await isDirectory(templatesDir))) {\n return generators;\n }\n\n try {\n const entries = await readdir(templatesDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const generatorDir = join(templatesDir, entry.name);\n const actions = await readdir(generatorDir, { withFileTypes: true });\n const actionDirs = actions.filter((a) => a.isDirectory());\n\n for (const action of actionDirs) {\n const actionDir = join(generatorDir, action.name);\n const templates = await readdir(actionDir);\n const ejsFiles = templates.filter(\n (t) => t.endsWith('.ejs.t') || t.endsWith('.t')\n );\n\n // Check for prompt file\n const hasPrompts = templates.some(\n (t) => t === 'prompt.js' || t === 'index.js'\n );\n\n generators.push({\n system: 'hygen',\n name: `${entry.name}/${action.name}`,\n location: actionDir,\n complexity: estimateComplexity(ejsFiles.length, hasPrompts),\n description: `Hygen generator: ${entry.name} ${action.name}`,\n templates: ejsFiles,\n });\n }\n }\n }\n } catch {\n // Best-effort scanning: if directory can't be read, continue with partial results\n // This is intentional - we want to return as much as possible without failing\n }\n\n return generators;\n}\n\n/**\n * Scan for Yeoman generators.\n *\n * Looks for generators/ directory or generator-* packages.\n */\nexport async function scanYeoman(projectRoot: string): Promise<DetectedGenerator[]> {\n const generators: DetectedGenerator[] = [];\n\n // Check for generators/ directory\n const generatorsDir = join(projectRoot, 'generators');\n if (await pathExists(generatorsDir) && await isDirectory(generatorsDir)) {\n try {\n const entries = await readdir(generatorsDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const genDir = join(generatorsDir, entry.name);\n const indexPath = join(genDir, 'index.js');\n\n if (await pathExists(indexPath)) {\n const content = await readFile(indexPath, 'utf-8');\n const hasPrompting = content.includes('prompting');\n const hasWriting = content.includes('writing');\n\n generators.push({\n system: 'yeoman',\n name: entry.name,\n location: genDir,\n complexity: hasPrompting && hasWriting ? 'complex' : 'moderate',\n description: `Yeoman generator: ${entry.name}`,\n });\n }\n }\n }\n } catch {\n // Best-effort: continue scanning even if generators/ can't be read\n }\n }\n\n // Check package.json for generator-* dependencies\n const packageJsonPath = join(projectRoot, 'package.json');\n if (await pathExists(packageJsonPath)) {\n try {\n const content = await readFile(packageJsonPath, 'utf-8');\n const pkg = JSON.parse(content);\n const allDeps = {\n ...(pkg.dependencies || {}),\n ...(pkg.devDependencies || {}),\n };\n\n for (const dep of Object.keys(allDeps)) {\n if (dep.startsWith('generator-')) {\n const name = dep.replace('generator-', '');\n generators.push({\n system: 'yeoman',\n name,\n location: `node_modules/${dep}`,\n complexity: 'moderate',\n description: `Yeoman generator package: ${dep}`,\n });\n }\n }\n } catch {\n // Best-effort: continue even if package.json can't be parsed\n }\n }\n\n return generators;\n}\n\n/**\n * Scan for Cookiecutter templates.\n *\n * Looks for cookiecutter.json files.\n */\nexport async function scanCookiecutter(\n projectRoot: string\n): Promise<DetectedGenerator[]> {\n const generators: DetectedGenerator[] = [];\n const cookiecutterPath = join(projectRoot, 'cookiecutter.json');\n\n if (await pathExists(cookiecutterPath)) {\n try {\n const content = await readFile(cookiecutterPath, 'utf-8');\n const config = JSON.parse(content);\n const paramCount = Object.keys(config).length;\n\n // Look for template directory\n const templateDir = join(projectRoot, '{{cookiecutter.project_slug}}');\n let templateCount = 0;\n if (await pathExists(templateDir)) {\n const templates = await readdir(templateDir, { recursive: true });\n templateCount = templates.length;\n }\n\n generators.push({\n system: 'cookiecutter',\n name: basename(projectRoot),\n location: cookiecutterPath,\n complexity: estimateComplexity(\n templateCount,\n paramCount > 3\n ),\n description: `Cookiecutter template with ${paramCount} parameters`,\n });\n } catch {\n generators.push({\n system: 'cookiecutter',\n name: basename(projectRoot),\n location: cookiecutterPath,\n complexity: 'moderate',\n description: 'Cookiecutter template detected',\n });\n }\n }\n\n return generators;\n}\n\n/**\n * Scan for custom generator scripts.\n *\n * Looks for common patterns like scripts/generate-*, bin/generate-*, etc.\n */\nexport async function scanCustom(projectRoot: string): Promise<DetectedGenerator[]> {\n const generators: DetectedGenerator[] = [];\n const searchDirs = ['scripts', 'bin', 'tools', 'generators'];\n const patterns = ['generate', 'scaffold', 'create', 'new'];\n\n for (const dir of searchDirs) {\n const dirPath = join(projectRoot, dir);\n if (!(await pathExists(dirPath)) || !(await isDirectory(dirPath))) {\n continue;\n }\n\n try {\n const entries = await readdir(dirPath);\n\n for (const entry of entries) {\n const entryLower = entry.toLowerCase();\n const matchesPattern = patterns.some(\n (p) => entryLower.includes(p)\n );\n\n if (matchesPattern) {\n const filePath = join(dirPath, entry);\n const stats = await stat(filePath);\n\n if (stats.isFile()) {\n generators.push({\n system: 'custom',\n name: entry.replace(/\\.(js|ts|sh|py)$/, ''),\n location: filePath,\n complexity: 'moderate',\n description: `Custom generator script: ${entry}`,\n });\n }\n }\n }\n } catch {\n // Best-effort: continue with other directories if one can't be read\n }\n }\n\n return generators;\n}\n\n/**\n * Scan options.\n */\nexport interface ScanOptions {\n /** Systems to scan for. If empty, scans all. */\n systems?: GeneratorSystem[];\n /** Include hidden directories. */\n includeHidden?: boolean;\n}\n\n/**\n * Scan for all generators in a project.\n *\n * @param projectRoot - The project root directory\n * @param options - Scan options\n * @returns Scan results with detected generators\n */\nexport async function scanGenerators(\n projectRoot: string,\n options: ScanOptions = {}\n): Promise<ScanResult> {\n const systems = options.systems || ['plop', 'hygen', 'yeoman', 'cookiecutter', 'custom'];\n const generators: DetectedGenerator[] = [];\n const scannedPaths: string[] = [];\n const errors: string[] = [];\n\n const scanners: Record<GeneratorSystem, (root: string) => Promise<DetectedGenerator[]>> = {\n plop: scanPlop,\n hygen: scanHygen,\n yeoman: scanYeoman,\n cookiecutter: scanCookiecutter,\n custom: scanCustom,\n };\n\n for (const system of systems) {\n const scanner = scanners[system];\n if (scanner) {\n try {\n const found = await scanner(projectRoot);\n generators.push(...found);\n scannedPaths.push(`${system}: ${projectRoot}`);\n } catch (err) {\n const message = toErrorMessage(err);\n errors.push(`Error scanning ${system}: ${message}`);\n }\n }\n }\n\n return {\n generators,\n scannedPaths,\n errors: errors.length > 0 ? errors : undefined,\n };\n}\n","/**\n * Scan generators command.\n *\n * Detects existing generators from various systems.\n */\n\nimport { scanGenerators, GeneratorSystemSchema, type GeneratorSystem } from '../scanner/index.js';\n\nexport interface ScanGeneratorsOptions {\n system?: string;\n json?: boolean;\n}\n\n/**\n * Execute the scan-generators command.\n */\nexport async function scanGeneratorsCommand(\n options: ScanGeneratorsOptions\n): Promise<void> {\n const projectRoot = process.cwd();\n\n // Parse system filter using Zod schema for validation\n let systems: GeneratorSystem[] | undefined;\n if (options.system) {\n const parseResult = GeneratorSystemSchema.safeParse(options.system.toLowerCase());\n if (!parseResult.success) {\n const validSystems = GeneratorSystemSchema.options;\n if (options.json) {\n console.log(\n JSON.stringify({\n success: false,\n error: `Invalid system: ${options.system}. Valid options: ${validSystems.join(', ')}`,\n })\n );\n } else {\n console.error(\n `Error: Invalid system '${options.system}'. Valid options: ${validSystems.join(', ')}`\n );\n }\n process.exit(1);\n }\n systems = [parseResult.data];\n }\n\n const result = await scanGenerators(projectRoot, { systems });\n\n if (options.json) {\n console.log(\n JSON.stringify({\n success: true,\n ...result,\n })\n );\n return;\n }\n\n // Human-readable output\n if (result.generators.length === 0) {\n console.log('No generators found.');\n console.log('');\n console.log('Scanned for:');\n console.log(' • Plop (plopfile.js/ts)');\n console.log(' • Hygen (_templates/)');\n console.log(' • Yeoman (generators/, generator-* packages)');\n console.log(' • Cookiecutter (cookiecutter.json)');\n console.log(' • Custom scripts (scripts/, bin/, tools/)');\n return;\n }\n\n console.log(`Found ${result.generators.length} generator(s):\\n`);\n\n // Group by system\n const bySystem = new Map<string, typeof result.generators>();\n for (const gen of result.generators) {\n const existing = bySystem.get(gen.system) || [];\n existing.push(gen);\n bySystem.set(gen.system, existing);\n }\n\n for (const [system, generators] of bySystem) {\n console.log(`${system.toUpperCase()}:`);\n for (const gen of generators) {\n const complexityIcon =\n gen.complexity === 'complex'\n ? '●●●'\n : gen.complexity === 'moderate'\n ? '●●○'\n : '●○○';\n console.log(` ${gen.name}`);\n console.log(` Location: ${gen.location}`);\n console.log(` Complexity: ${complexityIcon} (${gen.complexity})`);\n if (gen.description) {\n console.log(` ${gen.description}`);\n }\n if (gen.templates && gen.templates.length > 0) {\n console.log(` Templates: ${gen.templates.join(', ')}`);\n }\n console.log('');\n }\n }\n\n if (result.errors && result.errors.length > 0) {\n console.log('Warnings:');\n for (const error of result.errors) {\n console.log(` ⚠ ${error}`);\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// src/parser/signatures.ts
|
|
2
|
+
function typeAnnotationToString(typeAnnotation) {
|
|
3
|
+
if (!typeAnnotation || typeof typeAnnotation !== "object") return void 0;
|
|
4
|
+
const ta = typeAnnotation;
|
|
5
|
+
const inner = ta["type"] === "TSTypeAnnotation" ? ta["typeAnnotation"] : ta;
|
|
6
|
+
if (!inner || typeof inner !== "object") return void 0;
|
|
7
|
+
switch (inner["type"]) {
|
|
8
|
+
case "TSStringKeyword":
|
|
9
|
+
return "string";
|
|
10
|
+
case "TSNumberKeyword":
|
|
11
|
+
return "number";
|
|
12
|
+
case "TSBooleanKeyword":
|
|
13
|
+
return "boolean";
|
|
14
|
+
case "TSVoidKeyword":
|
|
15
|
+
return "void";
|
|
16
|
+
case "TSAnyKeyword":
|
|
17
|
+
return "any";
|
|
18
|
+
case "TSUnknownKeyword":
|
|
19
|
+
return "unknown";
|
|
20
|
+
case "TSNullKeyword":
|
|
21
|
+
return "null";
|
|
22
|
+
case "TSUndefinedKeyword":
|
|
23
|
+
return "undefined";
|
|
24
|
+
case "TSNeverKeyword":
|
|
25
|
+
return "never";
|
|
26
|
+
case "TSBigIntKeyword":
|
|
27
|
+
return "bigint";
|
|
28
|
+
case "TSSymbolKeyword":
|
|
29
|
+
return "symbol";
|
|
30
|
+
case "TSObjectKeyword":
|
|
31
|
+
return "object";
|
|
32
|
+
case "TSArrayType": {
|
|
33
|
+
const elemType = typeAnnotationToString(inner["elementType"]);
|
|
34
|
+
return elemType ? `${elemType}[]` : "unknown[]";
|
|
35
|
+
}
|
|
36
|
+
case "TSTypeReference": {
|
|
37
|
+
const typeName = inner["typeName"];
|
|
38
|
+
const name = typeName?.["name"];
|
|
39
|
+
if (!name) return void 0;
|
|
40
|
+
const typeArgs = inner["typeArguments"];
|
|
41
|
+
if (typeArgs && Array.isArray(typeArgs["params"])) {
|
|
42
|
+
const args = typeArgs["params"].map((a) => typeAnnotationToString(a)).filter(Boolean);
|
|
43
|
+
if (args.length > 0) return `${name}<${args.join(", ")}>`;
|
|
44
|
+
}
|
|
45
|
+
return name;
|
|
46
|
+
}
|
|
47
|
+
case "TSUnionType": {
|
|
48
|
+
const types = inner["types"];
|
|
49
|
+
if (!types) return void 0;
|
|
50
|
+
const parts = types.map((t) => typeAnnotationToString(t)).filter(Boolean);
|
|
51
|
+
return parts.join(" | ");
|
|
52
|
+
}
|
|
53
|
+
case "TSIntersectionType": {
|
|
54
|
+
const types = inner["types"];
|
|
55
|
+
if (!types) return void 0;
|
|
56
|
+
const parts = types.map((t) => typeAnnotationToString(t)).filter(Boolean);
|
|
57
|
+
return parts.join(" & ");
|
|
58
|
+
}
|
|
59
|
+
case "TSLiteralType": {
|
|
60
|
+
const literal = inner["literal"];
|
|
61
|
+
if (!literal) return void 0;
|
|
62
|
+
if (literal["type"] === "StringLiteral") return `'${literal["value"]}'`;
|
|
63
|
+
if (literal["type"] === "NumericLiteral") return String(literal["value"]);
|
|
64
|
+
if (literal["type"] === "BooleanLiteral") return String(literal["value"]);
|
|
65
|
+
return void 0;
|
|
66
|
+
}
|
|
67
|
+
case "TSTupleType": {
|
|
68
|
+
const elems = inner["elementTypes"];
|
|
69
|
+
if (!elems) return void 0;
|
|
70
|
+
const parts = elems.map((t) => typeAnnotationToString(t)).filter(Boolean);
|
|
71
|
+
return `[${parts.join(", ")}]`;
|
|
72
|
+
}
|
|
73
|
+
case "TSFunctionType":
|
|
74
|
+
return "Function";
|
|
75
|
+
case "TSTypePredicate":
|
|
76
|
+
return "boolean";
|
|
77
|
+
default:
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function extractParam(param) {
|
|
82
|
+
switch (param["type"]) {
|
|
83
|
+
case "Identifier": {
|
|
84
|
+
return {
|
|
85
|
+
name: param["name"],
|
|
86
|
+
type: typeAnnotationToString(param["typeAnnotation"])
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
case "AssignmentPattern": {
|
|
90
|
+
const left = param["left"];
|
|
91
|
+
if (!left) return void 0;
|
|
92
|
+
const base = extractParam(left);
|
|
93
|
+
if (base) base.hasDefault = true;
|
|
94
|
+
return base;
|
|
95
|
+
}
|
|
96
|
+
case "RestElement": {
|
|
97
|
+
const arg = param["argument"];
|
|
98
|
+
if (!arg) return void 0;
|
|
99
|
+
const name = arg["name"];
|
|
100
|
+
const typeAnno = param["typeAnnotation"] ?? arg["typeAnnotation"];
|
|
101
|
+
return {
|
|
102
|
+
name: name ?? "...args",
|
|
103
|
+
type: typeAnnotationToString(typeAnno),
|
|
104
|
+
rest: true
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
case "ObjectPattern":
|
|
108
|
+
return { name: "options", type: typeAnnotationToString(param["typeAnnotation"]) };
|
|
109
|
+
case "ArrayPattern":
|
|
110
|
+
return { name: "items", type: typeAnnotationToString(param["typeAnnotation"]) };
|
|
111
|
+
default:
|
|
112
|
+
return void 0;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function hasJSDocBefore(nodeStart, sourceCode, comments) {
|
|
116
|
+
for (let i = comments.length - 1; i >= 0; i--) {
|
|
117
|
+
const comment = comments[i];
|
|
118
|
+
if (comment.type !== "Block" || !comment.value.startsWith("*")) continue;
|
|
119
|
+
if (comment.end > nodeStart) continue;
|
|
120
|
+
const between = sourceCode.substring(comment.end, nodeStart);
|
|
121
|
+
if (/^\s*(export\s+)?(default\s+)?(async\s+)?$/.test(between)) return true;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
function extractFunctionSignature(node, name, exported, exportDefault, sourceCode, comments) {
|
|
127
|
+
const params = node["params"] ?? [];
|
|
128
|
+
const extractedParams = [];
|
|
129
|
+
for (const p of params) {
|
|
130
|
+
if (p && typeof p === "object") {
|
|
131
|
+
const ep = extractParam(p);
|
|
132
|
+
if (ep) extractedParams.push(ep);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const start = node["start"];
|
|
136
|
+
const end = node["end"];
|
|
137
|
+
let line = 1;
|
|
138
|
+
let endLine = 1;
|
|
139
|
+
if (start !== void 0) {
|
|
140
|
+
line = sourceCode.substring(0, start).split("\n").length;
|
|
141
|
+
}
|
|
142
|
+
if (end !== void 0) {
|
|
143
|
+
endLine = sourceCode.substring(0, end).split("\n").length;
|
|
144
|
+
}
|
|
145
|
+
const hasJSDoc = start !== void 0 ? hasJSDocBefore(start, sourceCode, comments) : false;
|
|
146
|
+
return {
|
|
147
|
+
name,
|
|
148
|
+
line,
|
|
149
|
+
endLine,
|
|
150
|
+
exported,
|
|
151
|
+
exportDefault,
|
|
152
|
+
async: node["async"] ?? false,
|
|
153
|
+
params: extractedParams,
|
|
154
|
+
returnType: typeAnnotationToString(node["returnType"]),
|
|
155
|
+
hasJSDoc
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function extractSignatures(ast, sourceCode, comments = []) {
|
|
159
|
+
const signatures = [];
|
|
160
|
+
const body = ast["body"];
|
|
161
|
+
if (!Array.isArray(body)) return signatures;
|
|
162
|
+
const exportedNames = /* @__PURE__ */ new Set();
|
|
163
|
+
for (const node of body) {
|
|
164
|
+
if (!node || typeof node !== "object") continue;
|
|
165
|
+
const n = node;
|
|
166
|
+
if (n["type"] === "ExportNamedDeclaration" && !n["declaration"]) {
|
|
167
|
+
const specifiers = n["specifiers"];
|
|
168
|
+
if (specifiers) {
|
|
169
|
+
for (const spec of specifiers) {
|
|
170
|
+
const s = spec;
|
|
171
|
+
const exported = s["exported"];
|
|
172
|
+
const local = s["local"];
|
|
173
|
+
const name = exported?.["name"] ?? local?.["name"];
|
|
174
|
+
if (name) exportedNames.add(name);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const node of body) {
|
|
180
|
+
if (!node || typeof node !== "object") continue;
|
|
181
|
+
const n = node;
|
|
182
|
+
switch (n["type"]) {
|
|
183
|
+
case "ExportNamedDeclaration": {
|
|
184
|
+
const decl = n["declaration"];
|
|
185
|
+
if (!decl) break;
|
|
186
|
+
if (decl["type"] === "FunctionDeclaration") {
|
|
187
|
+
const id = decl["id"];
|
|
188
|
+
const name = id?.["name"];
|
|
189
|
+
if (name) {
|
|
190
|
+
const sig = extractFunctionSignature(decl, name, true, false, sourceCode, comments);
|
|
191
|
+
if (sig) signatures.push(sig);
|
|
192
|
+
}
|
|
193
|
+
} else if (decl["type"] === "VariableDeclaration") {
|
|
194
|
+
const declarators = decl["declarations"];
|
|
195
|
+
if (declarators) {
|
|
196
|
+
for (const d of declarators) {
|
|
197
|
+
const declarator = d;
|
|
198
|
+
const id = declarator["id"];
|
|
199
|
+
const init = declarator["init"];
|
|
200
|
+
const name = id?.["name"];
|
|
201
|
+
if (name && init && (init["type"] === "ArrowFunctionExpression" || init["type"] === "FunctionExpression")) {
|
|
202
|
+
const sig = extractFunctionSignature(init, name, true, false, sourceCode, comments);
|
|
203
|
+
if (sig) signatures.push(sig);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
case "ExportDefaultDeclaration": {
|
|
211
|
+
const decl = n["declaration"];
|
|
212
|
+
if (!decl) break;
|
|
213
|
+
if (decl["type"] === "FunctionDeclaration") {
|
|
214
|
+
const id = decl["id"];
|
|
215
|
+
const name = id?.["name"] ?? "default";
|
|
216
|
+
const sig = extractFunctionSignature(decl, name, true, true, sourceCode, comments);
|
|
217
|
+
if (sig) signatures.push(sig);
|
|
218
|
+
} else if (decl["type"] === "ArrowFunctionExpression" || decl["type"] === "FunctionExpression") {
|
|
219
|
+
const sig = extractFunctionSignature(decl, "default", true, true, sourceCode, comments);
|
|
220
|
+
if (sig) signatures.push(sig);
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
case "FunctionDeclaration": {
|
|
225
|
+
const id = n["id"];
|
|
226
|
+
const name = id?.["name"];
|
|
227
|
+
if (name) {
|
|
228
|
+
const exported = exportedNames.has(name);
|
|
229
|
+
const sig = extractFunctionSignature(n, name, exported, false, sourceCode, comments);
|
|
230
|
+
if (sig) signatures.push(sig);
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
case "VariableDeclaration": {
|
|
235
|
+
const declarators = n["declarations"];
|
|
236
|
+
if (declarators) {
|
|
237
|
+
for (const d of declarators) {
|
|
238
|
+
const declarator = d;
|
|
239
|
+
const id = declarator["id"];
|
|
240
|
+
const init = declarator["init"];
|
|
241
|
+
const name = id?.["name"];
|
|
242
|
+
if (name && init && (init["type"] === "ArrowFunctionExpression" || init["type"] === "FunctionExpression")) {
|
|
243
|
+
const exported = exportedNames.has(name);
|
|
244
|
+
const sig = extractFunctionSignature(init, name, exported, false, sourceCode, comments);
|
|
245
|
+
if (sig) signatures.push(sig);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return signatures;
|
|
254
|
+
}
|
|
255
|
+
export {
|
|
256
|
+
extractSignatures
|
|
257
|
+
};
|
|
258
|
+
//# sourceMappingURL=signatures-K5QIL4WG.js.map
|