@ankurhalder/codebase-snapshot 2.0.0 → 2.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/dist/cli.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { O as OutputFormat } from './types-S-5eRuRc.cjs';
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-S-5eRuRc.js';
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, supportsTypeScriptImports, renderExplainText } from './chunk-RGDVLQTT.js';
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-QEIOXPMU.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
 
@@ -196,12 +210,37 @@ function parseCliArgs(argv) {
196
210
  } else if (rawSkip !== void 0) {
197
211
  skipArtifacts = parseArtifactList(String(rawSkip), "--skip-artifacts");
198
212
  }
213
+ if (command !== "init") {
214
+ const initOnly = [];
215
+ if (values.preset !== void 0) initOnly.push("--preset");
216
+ if (values.include !== void 0) initOnly.push("--include");
217
+ if (values.exclude !== void 0) initOnly.push("--exclude");
218
+ if (values.yes === true) initOnly.push("--yes");
219
+ if (values.minimal === true) initOnly.push("--minimal");
220
+ if (values.force === true) initOnly.push("--force");
221
+ if (initOnly.length > 0) {
222
+ throw new UsageError(
223
+ `${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).`
224
+ );
225
+ }
226
+ }
227
+ if (command === "init" && values.preset !== void 0) {
228
+ const requested = String(values.preset);
229
+ if (!getPreset(requested)) throw new UsageError(unknownPresetMessage(requested));
230
+ }
199
231
  return {
200
232
  command,
201
233
  args,
202
234
  format: rawFormat,
203
235
  dryRun: values["dry-run"] === true,
204
236
  watch: values.watch === true,
237
+ preset: values.preset === void 0 ? void 0 : String(values.preset),
238
+ maxPartBytesRaw: values["max-part-bytes"] === void 0 ? void 0 : String(values["max-part-bytes"]),
239
+ include: values.include === void 0 ? void 0 : String(values.include),
240
+ exclude: values.exclude === void 0 ? void 0 : String(values.exclude),
241
+ yes: values.yes === true,
242
+ minimal: values.minimal === true,
243
+ force: values.force === true,
205
244
  cwd: values.cwd === void 0 ? process.cwd() : path.resolve(String(values.cwd)),
206
245
  outDir: values["out-dir"] === void 0 ? void 0 : String(values["out-dir"]),
207
246
  includeMonolith: values["include-monolith"] === true ? true : void 0,
@@ -242,32 +281,65 @@ function emit(format, payload, textRenderer) {
242
281
  }
243
282
  textRenderer();
244
283
  }
245
- function runInit(cwd) {
246
- const targetName = supportsTypeScriptImports() ? "snapshot.config.ts" : "snapshot.config.mjs";
247
- const targetPath = path.join(cwd, targetName);
248
- if (fs.existsSync(targetPath)) {
249
- warn(`\u274C ${targetName} already exists in ${cwd}.`);
250
- return 1;
284
+ function splitList(value) {
285
+ if (value === void 0) return void 0;
286
+ return value.split(",").map((entry) => entry.trim()).filter(Boolean);
287
+ }
288
+ function describeManifestShapeProblem(manifest) {
289
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
290
+ return "not a JSON object";
251
291
  }
252
- const importPath = JSON.stringify(GENERATOR_NAME);
253
- const starter = `import { defineConfig } from ${importPath};
254
-
255
- export default defineConfig({
256
- branch: "main",
257
- outDir: "snapshot",
258
- maxPartBytes: 300 * 1024,
259
- largeFileBytes: 200 * 1024,
260
- excludePatterns: [],
261
- });
262
- `;
263
- fs.writeFileSync(targetPath, starter, "utf8");
264
- info(`\u2705 Created ${targetName} in ${cwd}`);
265
- if (targetName.endsWith(".mjs")) {
266
- info(
267
- " (a .ts config needs Node >= 22.6 to import natively; this Node writes a .mjs instead)"
268
- );
292
+ const candidate = manifest;
293
+ const counts = candidate.counts;
294
+ if (!counts || typeof counts !== "object") return "missing `counts`";
295
+ for (const key of ["totalFiles", "totalLines", "totalSizeBytes", "partsCount"]) {
296
+ if (typeof counts[key] !== "number") return `missing numeric \`counts.${key}\``;
297
+ }
298
+ if (!candidate.layers || typeof candidate.layers !== "object") return "missing `layers`";
299
+ if (!Array.isArray(candidate.files)) return "missing `files` array";
300
+ return null;
301
+ }
302
+ async function runInitCommand(invocation) {
303
+ try {
304
+ const result = await runInit({
305
+ cwd: invocation.cwd,
306
+ preset: invocation.preset,
307
+ maxPartBytes: invocation.maxPartBytesRaw ?? invocation.maxPartBytes,
308
+ include: splitList(invocation.include),
309
+ exclude: splitList(invocation.exclude),
310
+ skipArtifacts: invocation.skipArtifacts,
311
+ includeSensitiveFiles: invocation.includeSensitiveFiles,
312
+ yes: invocation.yes,
313
+ minimal: invocation.minimal,
314
+ force: invocation.force,
315
+ dryRun: invocation.dryRun
316
+ });
317
+ if (invocation.format === "json") {
318
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
319
+ `);
320
+ return 0;
321
+ }
322
+ if (!result.written) {
323
+ info(result.configText);
324
+ return 0;
325
+ }
326
+ info(`\u2705 Created ${path.basename(result.configPath)} (preset: ${result.preset})`);
327
+ if (result.scripts.length > 0) info(` Added npm scripts: ${result.scripts.join(", ")}`);
328
+ if (result.gitignoreEntries.length > 0) {
329
+ info(` Added to .gitignore: ${result.gitignoreEntries.join(", ")}`);
330
+ }
331
+ for (const note of result.notes) info(` \u26A0 ${note}`);
332
+ info("");
333
+ info(" Next: npm run snapshot");
334
+ return 0;
335
+ } catch (err) {
336
+ if (err instanceof PromptCancelled) {
337
+ warn("\u274C Cancelled.");
338
+ return 1;
339
+ }
340
+ warn(`\u274C ${err.message}`);
341
+ return 1;
269
342
  }
270
- return 0;
271
343
  }
272
344
  function renderValidateText(result) {
273
345
  if (result.valid) {
@@ -309,10 +381,14 @@ ${USAGE}
309
381
  throw err;
310
382
  }
311
383
  if (invocation.command === "init") {
312
- return runInit(invocation.cwd);
384
+ return await runInitCommand(invocation);
313
385
  }
314
386
  const loaded = await loadConfigFileDetailed(invocation.cwd);
315
387
  for (const warning of loaded.warnings) warn(`\u26A0 Config: ${warning}`);
388
+ if (loaded.errors.length > 0) {
389
+ warn("\u274C The config file could not be read. Fix it, or delete it to use the defaults.");
390
+ return 1;
391
+ }
316
392
  if (loaded.file) info(`\u2699 Using config ${path.relative(process.cwd(), loaded.file) || loaded.file}`);
317
393
  const resolved = resolveConfig(
318
394
  loaded.config,
@@ -337,7 +413,7 @@ ${USAGE}
337
413
  return result.valid ? 0 : 1;
338
414
  }
339
415
  if (invocation.command === "report") {
340
- const manifestPath = path.join(resolved.cwd, resolved.outDir, "manifest.json");
416
+ const manifestPath = path.resolve(resolved.cwd, resolved.outDir, "manifest.json");
341
417
  if (!fs.existsSync(manifestPath)) {
342
418
  warn(`\u274C Manifest not found at ${manifestPath}. Run generate first.`);
343
419
  return 1;
@@ -349,6 +425,11 @@ ${USAGE}
349
425
  warn(`\u274C Could not read ${manifestPath}: ${err.message}`);
350
426
  return 1;
351
427
  }
428
+ const shapeError = describeManifestShapeProblem(manifest);
429
+ if (shapeError) {
430
+ warn(`\u274C ${manifestPath} is not a snapshot manifest: ${shapeError}`);
431
+ return 1;
432
+ }
352
433
  const validation = validateSnapshot(resolved);
353
434
  emit(invocation.format, { manifest, validation }, () => {
354
435
  info(`