@ankurhalder/codebase-snapshot 2.0.0 → 2.1.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/README.md +42 -0
- package/dist/bin.js +2399 -372
- package/dist/{chunk-RGDVLQTT.js → chunk-SUOJL5EX.js} +2247 -355
- package/dist/cli.cjs +2417 -397
- package/dist/cli.d.cts +9 -1
- package/dist/cli.d.ts +9 -1
- package/dist/cli.js +160 -31
- package/dist/index.cjs +2260 -354
- package/dist/index.d.cts +183 -6
- package/dist/index.d.ts +183 -6
- package/dist/index.js +1 -1
- package/dist/{types-S-5eRuRc.d.cts → types-CJqkePM4.d.cts} +20 -1
- package/dist/{types-S-5eRuRc.d.ts → types-CJqkePM4.d.ts} +20 -1
- package/docs/configuration.md +97 -1
- package/docs/install.md +150 -0
- package/package.json +3 -2
package/dist/cli.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { O as OutputFormat } from './types-
|
|
1
|
+
import { O as OutputFormat } from './types-CJqkePM4.cjs';
|
|
2
2
|
|
|
3
3
|
type Command = "generate" | "validate" | "report" | "init" | "explain" | "diff";
|
|
4
4
|
/** A user-input problem: printed without a stack trace, exits 2. */
|
|
@@ -11,6 +11,14 @@ interface ParsedInvocation {
|
|
|
11
11
|
format: OutputFormat;
|
|
12
12
|
dryRun: boolean;
|
|
13
13
|
watch: boolean;
|
|
14
|
+
preset?: string;
|
|
15
|
+
/** The size exactly as typed, so the generated config can keep `120k`. */
|
|
16
|
+
maxPartBytesRaw?: string;
|
|
17
|
+
include?: string;
|
|
18
|
+
exclude?: string;
|
|
19
|
+
yes: boolean;
|
|
20
|
+
minimal: boolean;
|
|
21
|
+
force: boolean;
|
|
14
22
|
cwd: string;
|
|
15
23
|
outDir?: string;
|
|
16
24
|
includeMonolith?: boolean;
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { O as OutputFormat } from './types-
|
|
1
|
+
import { O as OutputFormat } from './types-CJqkePM4.js';
|
|
2
2
|
|
|
3
3
|
type Command = "generate" | "validate" | "report" | "init" | "explain" | "diff";
|
|
4
4
|
/** A user-input problem: printed without a stack trace, exits 2. */
|
|
@@ -11,6 +11,14 @@ interface ParsedInvocation {
|
|
|
11
11
|
format: OutputFormat;
|
|
12
12
|
dryRun: boolean;
|
|
13
13
|
watch: boolean;
|
|
14
|
+
preset?: string;
|
|
15
|
+
/** The size exactly as typed, so the generated config can keep `120k`. */
|
|
16
|
+
maxPartBytesRaw?: string;
|
|
17
|
+
include?: string;
|
|
18
|
+
exclude?: string;
|
|
19
|
+
yes: boolean;
|
|
20
|
+
minimal: boolean;
|
|
21
|
+
force: boolean;
|
|
14
22
|
cwd: string;
|
|
15
23
|
outDir?: string;
|
|
16
24
|
includeMonolith?: boolean;
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseByteSize, GENERATOR_VERSION, ARTIFACT_NAMES, MIN_MAX_PART_BYTES, loadConfigFileDetailed, resolveConfig, validateSnapshot, explainPath, compareManifests, loadManifest, renderDiffText, generateSnapshot, GENERATOR_NAME,
|
|
1
|
+
import { parseByteSize, GENERATOR_VERSION, ARTIFACT_NAMES, getPreset, unknownPresetMessage, MIN_MAX_PART_BYTES, loadConfigFileDetailed, resolveConfig, validateSnapshot, explainPath, compareManifests, loadManifest, renderDiffText, generateSnapshot, GENERATOR_NAME, runInit, PromptCancelled, renderExplainText } from './chunk-SUOJL5EX.js';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import { parseArgs } from 'util';
|
|
@@ -35,6 +35,12 @@ var CLI_OPTIONS = {
|
|
|
35
35
|
mode: { type: "string" },
|
|
36
36
|
"dry-run": { type: "boolean" },
|
|
37
37
|
watch: { type: "boolean" },
|
|
38
|
+
preset: { type: "string" },
|
|
39
|
+
include: { type: "string" },
|
|
40
|
+
exclude: { type: "string" },
|
|
41
|
+
yes: { type: "boolean" },
|
|
42
|
+
minimal: { type: "boolean" },
|
|
43
|
+
force: { type: "boolean" },
|
|
38
44
|
help: { type: "boolean", short: "h" },
|
|
39
45
|
version: { type: "boolean", short: "v" }
|
|
40
46
|
};
|
|
@@ -72,6 +78,14 @@ Options:
|
|
|
72
78
|
--format <text|json> CLI output format (default: text)
|
|
73
79
|
--dry-run Compute the snapshot without writing anything
|
|
74
80
|
--watch Re-generate whenever the repository changes
|
|
81
|
+
|
|
82
|
+
init only:
|
|
83
|
+
--preset <name> Force a project preset instead of detecting one
|
|
84
|
+
--include <globs> Comma-separated allow-list
|
|
85
|
+
--exclude <globs> Comma-separated exclude patterns
|
|
86
|
+
--minimal Write only the settings that differ from defaults
|
|
87
|
+
--yes Accept every default without prompting
|
|
88
|
+
--force Overwrite an existing config
|
|
75
89
|
-h, --help Display this help message
|
|
76
90
|
-v, --version Display version
|
|
77
91
|
|
|
@@ -128,9 +142,32 @@ function parseCliArgs(argv) {
|
|
|
128
142
|
strict: true
|
|
129
143
|
});
|
|
130
144
|
} catch (err) {
|
|
131
|
-
|
|
145
|
+
const message = err.message;
|
|
146
|
+
const ambiguous = /^Option '(--[^']+)' argument is ambiguous/i.exec(message);
|
|
147
|
+
if (ambiguous) {
|
|
148
|
+
throw new UsageError(
|
|
149
|
+
`${ambiguous[1]} received a value that looks like another flag (for example a negative number). Use the "--option=value" form, and note that sizes must be positive.`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
throw new UsageError(message);
|
|
132
153
|
}
|
|
133
154
|
const values = parsed.values;
|
|
155
|
+
for (const [name, spec] of Object.entries(CLI_OPTIONS)) {
|
|
156
|
+
if (spec.type !== "string") continue;
|
|
157
|
+
const raw = values[name];
|
|
158
|
+
if (raw !== void 0 && String(raw).trim() === "") {
|
|
159
|
+
throw new UsageError(`--${name} requires a value (received an empty string).`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (values.cwd !== void 0) {
|
|
163
|
+
const target = String(values.cwd);
|
|
164
|
+
if (!fs.existsSync(target)) {
|
|
165
|
+
throw new UsageError(`--cwd directory does not exist: ${target}`);
|
|
166
|
+
}
|
|
167
|
+
if (!fs.statSync(target).isDirectory()) {
|
|
168
|
+
throw new UsageError(`--cwd is not a directory: ${target}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
134
171
|
if (values.help) {
|
|
135
172
|
process.stdout.write(`${USAGE}
|
|
136
173
|
`);
|
|
@@ -196,12 +233,37 @@ function parseCliArgs(argv) {
|
|
|
196
233
|
} else if (rawSkip !== void 0) {
|
|
197
234
|
skipArtifacts = parseArtifactList(String(rawSkip), "--skip-artifacts");
|
|
198
235
|
}
|
|
236
|
+
if (command !== "init") {
|
|
237
|
+
const initOnly = [];
|
|
238
|
+
if (values.preset !== void 0) initOnly.push("--preset");
|
|
239
|
+
if (values.include !== void 0) initOnly.push("--include");
|
|
240
|
+
if (values.exclude !== void 0) initOnly.push("--exclude");
|
|
241
|
+
if (values.yes === true) initOnly.push("--yes");
|
|
242
|
+
if (values.minimal === true) initOnly.push("--minimal");
|
|
243
|
+
if (values.force === true) initOnly.push("--force");
|
|
244
|
+
if (initOnly.length > 0) {
|
|
245
|
+
throw new UsageError(
|
|
246
|
+
`${initOnly.join(", ")} ${initOnly.length === 1 ? "is" : "are"} only valid with \`init\`. To scope \`${command}\`, set include/excludePatterns/preset in snapshot.config.ts (or run \`snap init\` to generate one).`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (command === "init" && values.preset !== void 0) {
|
|
251
|
+
const requested = String(values.preset);
|
|
252
|
+
if (!getPreset(requested)) throw new UsageError(unknownPresetMessage(requested));
|
|
253
|
+
}
|
|
199
254
|
return {
|
|
200
255
|
command,
|
|
201
256
|
args,
|
|
202
257
|
format: rawFormat,
|
|
203
258
|
dryRun: values["dry-run"] === true,
|
|
204
259
|
watch: values.watch === true,
|
|
260
|
+
preset: values.preset === void 0 ? void 0 : String(values.preset),
|
|
261
|
+
maxPartBytesRaw: values["max-part-bytes"] === void 0 ? void 0 : String(values["max-part-bytes"]),
|
|
262
|
+
include: values.include === void 0 ? void 0 : String(values.include),
|
|
263
|
+
exclude: values.exclude === void 0 ? void 0 : String(values.exclude),
|
|
264
|
+
yes: values.yes === true,
|
|
265
|
+
minimal: values.minimal === true,
|
|
266
|
+
force: values.force === true,
|
|
205
267
|
cwd: values.cwd === void 0 ? process.cwd() : path.resolve(String(values.cwd)),
|
|
206
268
|
outDir: values["out-dir"] === void 0 ? void 0 : String(values["out-dir"]),
|
|
207
269
|
includeMonolith: values["include-monolith"] === true ? true : void 0,
|
|
@@ -242,32 +304,65 @@ function emit(format, payload, textRenderer) {
|
|
|
242
304
|
}
|
|
243
305
|
textRenderer();
|
|
244
306
|
}
|
|
245
|
-
function
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
307
|
+
function splitList(value) {
|
|
308
|
+
if (value === void 0) return void 0;
|
|
309
|
+
return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
310
|
+
}
|
|
311
|
+
function describeManifestShapeProblem(manifest) {
|
|
312
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
313
|
+
return "not a JSON object";
|
|
251
314
|
}
|
|
252
|
-
const
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
315
|
+
const candidate = manifest;
|
|
316
|
+
const counts = candidate.counts;
|
|
317
|
+
if (!counts || typeof counts !== "object") return "missing `counts`";
|
|
318
|
+
for (const key of ["totalFiles", "totalLines", "totalSizeBytes", "partsCount"]) {
|
|
319
|
+
if (typeof counts[key] !== "number") return `missing numeric \`counts.${key}\``;
|
|
320
|
+
}
|
|
321
|
+
if (!candidate.layers || typeof candidate.layers !== "object") return "missing `layers`";
|
|
322
|
+
if (!Array.isArray(candidate.files)) return "missing `files` array";
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
async function runInitCommand(invocation) {
|
|
326
|
+
try {
|
|
327
|
+
const result = await runInit({
|
|
328
|
+
cwd: invocation.cwd,
|
|
329
|
+
preset: invocation.preset,
|
|
330
|
+
maxPartBytes: invocation.maxPartBytesRaw ?? invocation.maxPartBytes,
|
|
331
|
+
include: splitList(invocation.include),
|
|
332
|
+
exclude: splitList(invocation.exclude),
|
|
333
|
+
skipArtifacts: invocation.skipArtifacts,
|
|
334
|
+
includeSensitiveFiles: invocation.includeSensitiveFiles,
|
|
335
|
+
yes: invocation.yes,
|
|
336
|
+
minimal: invocation.minimal,
|
|
337
|
+
force: invocation.force,
|
|
338
|
+
dryRun: invocation.dryRun
|
|
339
|
+
});
|
|
340
|
+
if (invocation.format === "json") {
|
|
341
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
342
|
+
`);
|
|
343
|
+
return 0;
|
|
344
|
+
}
|
|
345
|
+
if (!result.written) {
|
|
346
|
+
info(result.configText);
|
|
347
|
+
return 0;
|
|
348
|
+
}
|
|
349
|
+
info(`\u2705 Created ${path.basename(result.configPath)} (preset: ${result.preset})`);
|
|
350
|
+
if (result.scripts.length > 0) info(` Added npm scripts: ${result.scripts.join(", ")}`);
|
|
351
|
+
if (result.gitignoreEntries.length > 0) {
|
|
352
|
+
info(` Added to .gitignore: ${result.gitignoreEntries.join(", ")}`);
|
|
353
|
+
}
|
|
354
|
+
for (const note of result.notes) info(` \u26A0 ${note}`);
|
|
355
|
+
info("");
|
|
356
|
+
info(" Next: npm run snapshot");
|
|
357
|
+
return 0;
|
|
358
|
+
} catch (err) {
|
|
359
|
+
if (err instanceof PromptCancelled) {
|
|
360
|
+
warn("\u274C Cancelled.");
|
|
361
|
+
return 1;
|
|
362
|
+
}
|
|
363
|
+
warn(`\u274C ${err.message}`);
|
|
364
|
+
return 1;
|
|
269
365
|
}
|
|
270
|
-
return 0;
|
|
271
366
|
}
|
|
272
367
|
function renderValidateText(result) {
|
|
273
368
|
if (result.valid) {
|
|
@@ -290,6 +385,9 @@ function renderValidateText(result) {
|
|
|
290
385
|
for (const missing of result.drift.missingOnDisk.slice(0, 20)) {
|
|
291
386
|
warn(` - deleted: ${missing}`);
|
|
292
387
|
}
|
|
388
|
+
for (const mismatch of result.drift.totalsMismatches.slice(0, 20)) {
|
|
389
|
+
warn(` - counts: ${mismatch}`);
|
|
390
|
+
}
|
|
293
391
|
}
|
|
294
392
|
for (const warning of result.warnings) warn(` \u26A0 ${warning}`);
|
|
295
393
|
}
|
|
@@ -309,10 +407,14 @@ ${USAGE}
|
|
|
309
407
|
throw err;
|
|
310
408
|
}
|
|
311
409
|
if (invocation.command === "init") {
|
|
312
|
-
return
|
|
410
|
+
return await runInitCommand(invocation);
|
|
313
411
|
}
|
|
314
412
|
const loaded = await loadConfigFileDetailed(invocation.cwd);
|
|
315
413
|
for (const warning of loaded.warnings) warn(`\u26A0 Config: ${warning}`);
|
|
414
|
+
if (loaded.errors.length > 0) {
|
|
415
|
+
warn("\u274C The config file could not be read. Fix it, or delete it to use the defaults.");
|
|
416
|
+
return 1;
|
|
417
|
+
}
|
|
316
418
|
if (loaded.file) info(`\u2699 Using config ${path.relative(process.cwd(), loaded.file) || loaded.file}`);
|
|
317
419
|
const resolved = resolveConfig(
|
|
318
420
|
loaded.config,
|
|
@@ -337,7 +439,7 @@ ${USAGE}
|
|
|
337
439
|
return result.valid ? 0 : 1;
|
|
338
440
|
}
|
|
339
441
|
if (invocation.command === "report") {
|
|
340
|
-
const manifestPath = path.
|
|
442
|
+
const manifestPath = path.resolve(resolved.cwd, resolved.outDir, "manifest.json");
|
|
341
443
|
if (!fs.existsSync(manifestPath)) {
|
|
342
444
|
warn(`\u274C Manifest not found at ${manifestPath}. Run generate first.`);
|
|
343
445
|
return 1;
|
|
@@ -349,6 +451,11 @@ ${USAGE}
|
|
|
349
451
|
warn(`\u274C Could not read ${manifestPath}: ${err.message}`);
|
|
350
452
|
return 1;
|
|
351
453
|
}
|
|
454
|
+
const shapeError = describeManifestShapeProblem(manifest);
|
|
455
|
+
if (shapeError) {
|
|
456
|
+
warn(`\u274C ${manifestPath} is not a snapshot manifest: ${shapeError}`);
|
|
457
|
+
return 1;
|
|
458
|
+
}
|
|
352
459
|
const validation = validateSnapshot(resolved);
|
|
353
460
|
emit(invocation.format, { manifest, validation }, () => {
|
|
354
461
|
info(`
|
|
@@ -411,9 +518,23 @@ ${USAGE}
|
|
|
411
518
|
info(
|
|
412
519
|
` - Total size: ${(manifest.counts.totalSizeBytes / (1024 * 1024)).toFixed(2)} MB`
|
|
413
520
|
);
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
521
|
+
const { artifacts } = resolved;
|
|
522
|
+
if (artifacts.parts) {
|
|
523
|
+
info(` - Parts written: ${manifest.counts.partsCount} under ${resolved.outDir}/parts/`);
|
|
524
|
+
}
|
|
525
|
+
const reports = [
|
|
526
|
+
artifacts.projectTree && "project-tree",
|
|
527
|
+
artifacts.navigation && "navigation",
|
|
528
|
+
artifacts.audit && "audit"
|
|
529
|
+
].filter(Boolean);
|
|
530
|
+
if (reports.length > 0) {
|
|
531
|
+
info(` - Reports: ${resolved.outDir}/reports/ (${reports.join(", ")})`);
|
|
532
|
+
}
|
|
533
|
+
if (artifacts.manifest) info(` - Manifest: ${resolved.outDir}/manifest.json`);
|
|
534
|
+
if (artifacts.monolith) info(` - Monolith: ${resolved.outDir}/full-codebase.{json,md}`);
|
|
535
|
+
if (reports.length === 0 && !artifacts.parts && !artifacts.manifest && !artifacts.monolith) {
|
|
536
|
+
info(` - Artifacts: all disabled (only the ownership marker was written)`);
|
|
537
|
+
}
|
|
417
538
|
info(` - Fingerprint: ${manifest.fingerprint}`);
|
|
418
539
|
info(` - Integrity: ${manifest.validation.status}`);
|
|
419
540
|
if (manifest.warnings.length > 0) {
|
|
@@ -430,6 +551,14 @@ ${USAGE}
|
|
|
430
551
|
}
|
|
431
552
|
return manifest.validation.status === "passed" ? 0 : 1;
|
|
432
553
|
} catch (err) {
|
|
554
|
+
const code = err.code;
|
|
555
|
+
if (code === "EPERM" || code === "EACCES") {
|
|
556
|
+
warn(
|
|
557
|
+
`\u274C Cannot write the snapshot: permission denied under ${resolved.outDir}. Check the directory's permissions or choose another --out-dir.`
|
|
558
|
+
);
|
|
559
|
+
warn(` (${err.message})`);
|
|
560
|
+
return 1;
|
|
561
|
+
}
|
|
433
562
|
warn(`\u274C Failed to generate snapshot: ${err.message}`);
|
|
434
563
|
return 1;
|
|
435
564
|
}
|