json5-manifest-sync 0.3.5 → 0.3.7

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/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [0.3.7] - 2026-11-09
6
+
7
+ ### Added
8
+
9
+ - Added a read-only `--check` mode for validating manifest synchronization in CI.
10
+
11
+ ## [0.3.6] - 2026-11-09
12
+
13
+ ### Changed
14
+
15
+ - Reframed the README introduction around the documented companion-manifest use case.
16
+
5
17
  ## [0.3.5] - 2026-11-09
6
18
 
7
19
  ### Changed
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # json5-manifest-sync
2
2
 
3
- Document your npm package manifest with comments in a companion `package.json5`, then sync it with the standard `package.json` expected by npm and other tools. Choose either file as the source of truth for manifest values; `package.json` always remains valid strict JSON.
3
+ Add comments to your package manifest in a companion `package.json5`, then sync it with the standard `package.json` expected by npm and other tools. This works whether you use pnpm, which supports `package.json5` natively, or simply want a documented manifest while retaining compatibility with tools that require `package.json`. Choose either file as the source of truth for manifest values; `package.json` remains valid strict JSON.
4
4
 
5
- For example, document dependencies in `package.json5` and generate `package.json` from it:
5
+ For example, document dependencies in `package.json5` and generate `package.json` for npm or other tools that require it:
6
6
 
7
7
  <!-- prettier-ignore -->
8
8
  ```json5
@@ -157,6 +157,14 @@ Choose a blank-comment mode with one of the following:
157
157
 
158
158
  To warn after sync when blank `//` placeholders remain, add `--warn-blank-comments`. That warning applies to `preserve` and `fill`; `remove` strips those lines.
159
159
 
160
+ Use `--check` to compare each selected source manifest's generated output with its counterpart without writing files. It exits with status `1` if an output is missing or differs, and `0` if all checked outputs are current. It respects `--source` and `--blank-comments`; in the default `package.json` source direction, packages without a `package.json5` companion are skipped, as they are during sync.
161
+
162
+ For CI, run the check directly:
163
+
164
+ ```bash
165
+ npx --yes --package=json5-manifest-sync json5-manifest-sync --check
166
+ ```
167
+
160
168
  ### Pre-commit synchronization
161
169
 
162
170
  To keep both manifests aligned in each commit, run `json5-manifest-sync` from a Git pre-commit hook after manifest changes. It uses `JSON5_MANIFEST_SYNC_SOURCE_OF_TRUTH` or the `--source` option to choose the authoritative file. Make sure the hook stages the generated counterpart as well; if you use partial staging, stage only the file generated by the sync rather than blindly adding both manifests.
@@ -66,7 +66,7 @@ export declare function syncJson5(raw: string, source: Record<string, unknown>,
66
66
  *
67
67
  * @param args - Optional CLI arguments (defaults to process argv slice)
68
68
  */
69
- export declare function runCli(args?: string[]): void;
69
+ export declare function runCli(args?: string[]): number;
70
70
  /**
71
71
  * Determines whether this module is being executed as a CLI entrypoint.
72
72
  *
@@ -369,6 +369,7 @@ export function runCli(args = process.argv.slice(2)) {
369
369
  // Parse CLI flags for options
370
370
  let blankCommentMode = "preserve";
371
371
  let warnOnBlankComments = false;
372
+ let checkOnly = false;
372
373
  const dotenvPath = path.join(rootDir, ".env");
373
374
  const dotenvValues = fs.existsSync(dotenvPath)
374
375
  ? parseDotenv(fs.readFileSync(dotenvPath))
@@ -405,19 +406,23 @@ export function runCli(args = process.argv.slice(2)) {
405
406
  }
406
407
  else {
407
408
  console.error(`Invalid source "${v}". Use --source=${manifestSourceOptions.join(" or --source=")}.`);
408
- return;
409
+ return 2;
409
410
  }
410
411
  }
411
412
  else if (arg === "--warn-blank-comments") {
412
413
  warnOnBlankComments = true;
413
414
  }
415
+ else if (arg === "--check") {
416
+ checkOnly = true;
417
+ }
414
418
  }
415
419
  const configuredSourceValue = sourceOverride ?? configuredSource;
416
420
  if (!isManifestSource(configuredSourceValue)) {
417
421
  console.error(`Invalid JSON5_MANIFEST_SYNC_SOURCE_OF_TRUTH "${configuredSourceValue}". Use ${manifestSourceOptions.join(" or ")}.`);
418
- return;
422
+ return 2;
419
423
  }
420
424
  const source = configuredSourceValue;
425
+ let hasOutOfSyncManifest = false;
421
426
  const sourcePattern = source === "package.json" ? "**/package.json" : "**/package.json5";
422
427
  const sourcePaths = globSync(sourcePattern, {
423
428
  cwd: rootDir,
@@ -446,10 +451,20 @@ export function runCli(args = process.argv.slice(2)) {
446
451
  const updated = syncJson5(rawJson5, sourceJson, {
447
452
  blankCommentMode,
448
453
  });
449
- const changed = writeFileIfChanged(json5Path, updated);
450
- console.info(changed
451
- ? `✅ Synced ${relPath} → ${path.basename(json5Path)}`
452
- : `⏭️ Unchanged ${relPath} → ${path.basename(json5Path)}`);
454
+ if (checkOnly) {
455
+ const inSync = rawJson5 === updated;
456
+ if (!inSync)
457
+ hasOutOfSyncManifest = true;
458
+ console.info(inSync
459
+ ? `✅ In sync ${relPath} → ${path.basename(json5Path)}`
460
+ : `❌ Out of sync ${relPath} → ${path.basename(json5Path)}`);
461
+ }
462
+ else {
463
+ const changed = writeFileIfChanged(json5Path, updated);
464
+ console.info(changed
465
+ ? `✅ Synced ${relPath} → ${path.basename(json5Path)}`
466
+ : `⏭️ Unchanged ${relPath} → ${path.basename(json5Path)}`);
467
+ }
453
468
  if (warnOnBlankComments && blankCommentMode !== "remove") {
454
469
  warnAboutBlankComments(json5Path, collectBlankCommentLineNumbers(updated));
455
470
  }
@@ -457,12 +472,26 @@ export function runCli(args = process.argv.slice(2)) {
457
472
  else {
458
473
  const sourceJson5 = JSON5.parse(fs.readFileSync(json5Path, "utf8"));
459
474
  const updated = `${JSON.stringify(sourceJson5, null, 2)}\n`;
460
- const changed = writeFileIfChanged(packageJsonPath, updated);
461
- console.info(changed
462
- ? `✅ Synced ${path.basename(json5Path)} → ${relPath}`
463
- : `⏭️ Unchanged ${path.basename(json5Path)} → ${relPath}`);
475
+ const current = fs.existsSync(packageJsonPath)
476
+ ? fs.readFileSync(packageJsonPath, "utf8")
477
+ : undefined;
478
+ if (checkOnly) {
479
+ const inSync = current === updated;
480
+ if (!inSync)
481
+ hasOutOfSyncManifest = true;
482
+ console.info(inSync
483
+ ? `✅ In sync ${path.basename(json5Path)} → ${relPath}`
484
+ : `❌ Out of sync ${path.basename(json5Path)} → ${relPath}`);
485
+ }
486
+ else {
487
+ const changed = writeFileIfChanged(packageJsonPath, updated);
488
+ console.info(changed
489
+ ? `✅ Synced ${path.basename(json5Path)} → ${relPath}`
490
+ : `⏭️ Unchanged ${path.basename(json5Path)} → ${relPath}`);
491
+ }
464
492
  }
465
493
  }
494
+ return checkOnly && hasOutOfSyncManifest ? 1 : 0;
466
495
  }
467
496
  /**
468
497
  * Determines whether this module is being executed as a CLI entrypoint.
@@ -488,5 +517,5 @@ export function isCliInvocation(argv1 = process.argv[1], moduleUrl = import.meta
488
517
  }
489
518
  }
490
519
  if (isCliInvocation()) {
491
- runCli();
520
+ process.exitCode = runCli();
492
521
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json5-manifest-sync",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "Sync package.json and package.json5 manifests in either direction, preserving JSON5 comments where possible.",
5
5
  "author": {
6
6
  "name": "Bradley Baysinger",