json5-manifest-sync 0.1.3 → 0.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [0.2.0] - 2026-05-06
6
+
7
+ ### Breaking Changes
8
+
9
+ - The default blank-comment mode is now `preserve` instead of the previous placeholder-generating behavior. Existing users who want generated blank `//` lines must now pass `--blank-comments=fill`.
10
+
11
+ ### Changed
12
+
13
+ - Replaced the old blank-comment toggle with `--blank-comments=preserve|fill|remove`.
14
+ - Changed the default blank-comment mode to `preserve` so sync no longer introduces placeholder `//` lines unless requested.
15
+ - Added `--warn-blank-comments` to warn after sync when blank `//` placeholders remain.
16
+
17
+ ## [0.1.3] - 2026-02-27
18
+
19
+ ### Added
20
+
21
+ - Published `json5-manifest-sync` to the npm registry for direct install and global CLI usage.
22
+ - Added npm-first installation guidance in the README, including optional global install instructions.
23
+
24
+ ### Changed
25
+
26
+ - Updated README examples to use npm semver dependency references instead of GitHub-only install URLs.
27
+ - Kept GitHub install instructions as an alternative path for unreleased branch/tag consumption.
28
+
5
29
  ## [0.1.2] - 2026-02-25
6
30
 
7
31
  ### Fixed
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # json5-manifest-sync
2
2
 
3
- Keep a documented `package.json5` in sync with the real `package.json` used by Node and package managers.
3
+ Keep a documented `package.json5` in sync with the real `package.json` used by Node and package managers, including `version` updates.
4
4
 
5
5
  ## Quick Start
6
6
 
7
7
  ```bash
8
- npm install github:BBaysinger/json5-manifest-sync#v0.1.3
8
+ npm install --save-dev json5-manifest-sync
9
9
  ```
10
10
 
11
11
  Add a script in your project's `package.json`:
@@ -41,37 +41,51 @@ This tool solves that gap by letting you maintain both:
41
41
  - `package.json5` as the human-documented companion file
42
42
 
43
43
  Then it synchronizes `package.json5` from `package.json` while preserving mapped `//` comments where possible.
44
+ When values like `version`, `scripts`, or dependency versions change in `package.json`, those updates are propagated into `package.json5`.
44
45
 
45
46
  ## What it does
46
47
 
47
48
  - Finds `package.json` files recursively (excluding `node_modules`)
48
49
  - Skips paths ignored by your root `.gitignore`
49
- - For each matching `package.json5`, rewrites values from canonical `package.json`
50
+ - For each matching `package.json5`, rewrites values from canonical `package.json` (including `version`, scripts, dependencies, and other manifest fields)
50
51
  - Preserves/migrates mapped `//` comments for keys and supported array items
52
+ - Supports three blank-comment modes: `preserve`, `fill`, and `remove`
51
53
  - Writes stable JSON5 formatting with trailing commas for cleaner diffs
52
54
 
55
+ ### Current limitations
56
+
57
+ - Per-item comment preservation is most reliable for arrays of strings.
58
+ - Arrays of objects or numbers still serialize correctly, but item-level comments may be dropped.
59
+ - Use `--blank-comments=preserve` to keep existing blank `//` lines without adding new ones.
60
+ - Use `--blank-comments=fill` to add blank `//` placeholders where comments are omitted.
61
+ - Use `--blank-comments=remove` to strip blank `//` lines from the output.
62
+
53
63
  Repository: https://github.com/BBaysinger/json5-manifest-sync
54
64
 
55
65
  ## Author
56
66
 
57
67
  Bradley Baysinger ([@BBaysinger](https://github.com/BBaysinger))
58
68
 
59
- ## Install from GitHub
69
+ ## Install from npm (recommended)
70
+
71
+ ```bash
72
+ npm install --save-dev json5-manifest-sync
73
+ ```
60
74
 
61
- Install from a tag (recommended):
75
+ Global install (optional):
62
76
 
63
77
  ```bash
64
- npm install github:BBaysinger/json5-manifest-sync#v0.1.3
78
+ npm install -g json5-manifest-sync
65
79
  ```
66
80
 
67
- Or from a branch during iteration:
81
+ ## Install from GitHub (alternative)
82
+
83
+ Use this if you want an unreleased branch or tag:
68
84
 
69
85
  ```bash
70
86
  npm install github:BBaysinger/json5-manifest-sync#main
71
87
  ```
72
88
 
73
- Because this repo defines a `prepare` script, npm will build TypeScript during git-based install.
74
-
75
89
  ## Use in a project
76
90
 
77
91
  Add a script in your consuming project's `package.json`:
@@ -113,9 +127,15 @@ For the current full output style, see this repo's live example: [`package.json5
113
127
 
114
128
  ### Options
115
129
 
116
- - `--no-empty-comment` or `--no-empty-comments`
117
- - `--empty-comment=false`
118
- - env var: `SYNC_JSON5_ADD_EMPTY_COMMENT=false`
130
+ By default, the tool runs with `--blank-comments=preserve`.
131
+
132
+ Choose a blank-comment mode with one of the following:
133
+
134
+ - `--blank-comments=preserve`
135
+ - `--blank-comments=fill`
136
+ - `--blank-comments=remove`
137
+
138
+ To warn after sync when blank `//` placeholders remain, add `--warn-blank-comments`. That warning applies to `preserve` and `fill`; `remove` strips those lines.
119
139
 
120
140
  ### Formatting note
121
141
 
@@ -126,12 +146,22 @@ Prettier's JSON5 formatter can remove quotes from valid keys, which makes `packa
126
146
  > [!NOTE]
127
147
  > It would be nice if Prettier provided an option to preserve quoted keys in JSON5.
128
148
 
149
+ ### AI assist tip
150
+
151
+ After running `sync:json5`, you can ask an AI coding assistant to fill placeholder comment lines.
152
+
153
+ If you use this repo's `precommit` workflow, it warns when `package.json5` still contains blank `//` placeholder comments. Suppress just that warning for a given run with `npm run precommit -- --suppress-blank-comment-warning`.
154
+
155
+ Example prompt:
156
+
157
+ > "Complete empty `//` comment lines in `package.json5` with concise, field-specific comments."
158
+
129
159
  ## Example dependency block in consumer
130
160
 
131
161
  ```json
132
162
  {
133
163
  "dependencies": {
134
- "json5-manifest-sync": "github:BBaysinger/json5-manifest-sync#v0.1.3"
164
+ "json5-manifest-sync": "^0.1.3"
135
165
  }
136
166
  }
137
167
  ```
@@ -26,20 +26,18 @@
26
26
  * - Array item comment mapping is primarily intended for arrays of strings; arrays of
27
27
  * objects/numbers will serialize correctly but per-item comment preservation is limited.
28
28
  */
29
+ export type BlankCommentMode = "preserve" | "fill" | "remove";
29
30
  export type SyncOptions = {
30
31
  /**
31
- * When true, insert a blank comment line (`//`) before any key that has no
32
- * preceding comment captured from the existing JSON5. This helps visually
33
- * separate entries like:
32
+ * Controls how blank comment lines (`//`) are handled.
34
33
  *
35
- * "private": true,
36
- * //
37
- * "type": "module",
34
+ * - `preserve`: keep existing blank comment lines, but do not add new ones
35
+ * - `fill`: keep existing blank comment lines and add them where comments are omitted
36
+ * - `remove`: remove blank comment lines from the output
38
37
  *
39
- * Defaults to true. Can be disabled via CLI flag `--no-empty-comment` or the
40
- * environment variable `SYNC_JSON5_ADD_EMPTY_COMMENT=false`.
38
+ * Defaults to `preserve`.
41
39
  */
42
- addEmptyCommentIfMissing?: boolean;
40
+ blankCommentMode?: BlankCommentMode;
43
41
  };
44
42
  /**
45
43
  * Synchronizes a JSON5 file with its canonical JSON source while preserving formatting.
@@ -4,6 +4,32 @@ import path from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import { sync as globSync } from "glob";
6
6
  import ignore from "ignore";
7
+ function isBlankCommentLine(line) {
8
+ return /^\s*\/\/\s*$/.test(line);
9
+ }
10
+ function normalizePrecedingComments(preceding, blankCommentMode) {
11
+ const comments = preceding ?? [];
12
+ if (blankCommentMode !== "remove") {
13
+ return comments;
14
+ }
15
+ return comments.filter((line) => !isBlankCommentLine(line));
16
+ }
17
+ function collectBlankCommentLineNumbers(raw) {
18
+ return raw
19
+ .split("\n")
20
+ .map((line, index) => ({ line, lineNumber: index + 1 }))
21
+ .filter(({ line }) => isBlankCommentLine(line))
22
+ .map(({ lineNumber }) => lineNumber);
23
+ }
24
+ function warnAboutBlankComments(filePath, blankCommentLines) {
25
+ if (blankCommentLines.length === 0) {
26
+ return;
27
+ }
28
+ const preview = blankCommentLines.slice(0, 8).join(", ");
29
+ const remainder = blankCommentLines.length > 8 ? ", ..." : "";
30
+ const plural = blankCommentLines.length === 1 ? "placeholder" : "placeholders";
31
+ console.warn(`[warn:blank-comments] ${path.basename(filePath)} contains ${blankCommentLines.length} blank comment ${plural} at line${blankCommentLines.length === 1 ? "" : "s"} ${preview}${remainder}. Fill them in with real notes, or rerun with --blank-comments=remove if you do not want placeholders.`);
32
+ }
7
33
  /**
8
34
  * Quotes an object key for consistent JSON5 formatting.
9
35
  * Always returns a quoted key to maintain consistency across all properties.
@@ -202,12 +228,10 @@ function generateScriptComment() {
202
228
  * @param source - Parsed JSON object from the canonical package.json
203
229
  * @returns The updated JSON5 content as a formatted string
204
230
  */
205
- export function syncJson5(raw, source, options = { addEmptyCommentIfMissing: true }) {
231
+ export function syncJson5(raw, source, options = { blankCommentMode: "preserve" }) {
206
232
  const lines = raw.split("\n");
207
233
  const comments = buildCommentMap(lines);
208
- const addEmptyCommentIfMissing = options.addEmptyCommentIfMissing !== undefined
209
- ? options.addEmptyCommentIfMissing
210
- : true;
234
+ const blankCommentMode = options.blankCommentMode ?? "preserve";
211
235
  /**
212
236
  * Recursively writes an object in normalized JSON5 style with mapped comments.
213
237
  *
@@ -230,13 +254,14 @@ export function syncJson5(raw, source, options = { addEmptyCommentIfMissing: tru
230
254
  const val = obj[key];
231
255
  const comma = ","; // Always add trailing comma for version control benefits
232
256
  const commentInfo = comments[fullPath];
257
+ const precedingComments = normalizePrecedingComments(commentInfo?.preceding, blankCommentMode);
233
258
  // Add any preceding comments above this key
234
- if (commentInfo?.preceding && commentInfo.preceding.length > 0) {
235
- for (const c of commentInfo.preceding) {
259
+ if (precedingComments.length > 0) {
260
+ for (const c of precedingComments) {
236
261
  result.push(indent(c, level));
237
262
  }
238
263
  }
239
- else if (addEmptyCommentIfMissing &&
264
+ else if (blankCommentMode === "fill" &&
240
265
  !comments.hasOwnProperty(fullPath)) {
241
266
  // No preceding comment captured — optionally add a blank one for spacing
242
267
  // Applies to any section, not just scripts
@@ -265,13 +290,14 @@ export function syncJson5(raw, source, options = { addEmptyCommentIfMissing: tru
265
290
  for (let j = 0; j < val.length; j++) {
266
291
  const itemPath = [...path, key, j.toString()].join(".");
267
292
  const itemCommentInfo = comments[itemPath];
293
+ const itemPrecedingComments = normalizePrecedingComments(itemCommentInfo?.preceding, blankCommentMode);
268
294
  // Add preceding comments for this array item
269
- if (itemCommentInfo?.preceding) {
270
- for (const c of itemCommentInfo.preceding) {
295
+ if (itemPrecedingComments.length > 0) {
296
+ for (const c of itemPrecedingComments) {
271
297
  result.push(indent(c, level + indentSize));
272
298
  }
273
299
  }
274
- else if (addEmptyCommentIfMissing &&
300
+ else if (blankCommentMode === "fill" &&
275
301
  !comments.hasOwnProperty(itemPath)) {
276
302
  const autoComment = generateScriptComment();
277
303
  result.push(indent(autoComment, level + indentSize));
@@ -329,24 +355,19 @@ export function runCli(args = process.argv.slice(2)) {
329
355
  ignore: ["**/node_modules/**"],
330
356
  absolute: true,
331
357
  });
332
- // Parse simple CLI flags / env for options
333
- let addEmptyCommentIfMissing = true;
334
- // Env toggle: SYNC_JSON5_ADD_EMPTY_COMMENT=false
335
- if (typeof process.env.SYNC_JSON5_ADD_EMPTY_COMMENT === "string" &&
336
- process.env.SYNC_JSON5_ADD_EMPTY_COMMENT.toLowerCase() === "false") {
337
- addEmptyCommentIfMissing = false;
338
- }
339
- // CLI toggles: --no-empty-comment, --empty-comment=false
358
+ // Parse CLI flags for options
359
+ let blankCommentMode = "preserve";
360
+ let warnOnBlankComments = false;
340
361
  for (const arg of args) {
341
- if (arg === "--no-empty-comment" || arg === "--no-empty-comments") {
342
- addEmptyCommentIfMissing = false;
343
- }
344
- else if (arg.startsWith("--empty-comment=")) {
362
+ if (arg.startsWith("--blank-comments=")) {
345
363
  const v = arg.split("=", 2)[1]?.toLowerCase();
346
- if (v === "false" || v === "0" || v === "no") {
347
- addEmptyCommentIfMissing = false;
364
+ if (v === "preserve" || v === "fill" || v === "remove") {
365
+ blankCommentMode = v;
348
366
  }
349
367
  }
368
+ else if (arg === "--warn-blank-comments") {
369
+ warnOnBlankComments = true;
370
+ }
350
371
  }
351
372
  // Process each package.json to sync with its package.json5 counterpart
352
373
  for (const packageJsonPath of packageJsonPaths) {
@@ -364,11 +385,14 @@ export function runCli(args = process.argv.slice(2)) {
364
385
  const rawJson5 = fs.readFileSync(json5Path, "utf8");
365
386
  const sourceJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
366
387
  const updated = syncJson5(rawJson5, sourceJson, {
367
- addEmptyCommentIfMissing,
388
+ blankCommentMode,
368
389
  });
369
390
  // Write the synchronized result
370
391
  fs.writeFileSync(json5Path, updated);
371
392
  console.info(`✅ Synced ${relPath} → ${path.basename(json5Path)}`);
393
+ if (warnOnBlankComments && blankCommentMode !== "remove") {
394
+ warnAboutBlankComments(json5Path, collectBlankCommentLineNumbers(updated));
395
+ }
372
396
  }
373
397
  }
374
398
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json5-manifest-sync",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Sync package.json5 files from canonical package.json while preserving key comments.",
5
5
  "author": {
6
6
  "name": "Bradley Baysinger",
@@ -16,7 +16,7 @@
16
16
  }
17
17
  },
18
18
  "bin": {
19
- "json5-manifest-sync": "./dist/json5-manifest-sync.js"
19
+ "json5-manifest-sync": "dist/json5-manifest-sync.js"
20
20
  },
21
21
  "files": [
22
22
  "dist",
@@ -25,8 +25,11 @@
25
25
  ],
26
26
  "scripts": {
27
27
  "build": "tsc -p tsconfig.json",
28
+ "sync:json5": "node ./dist/json5-manifest-sync.js",
29
+ "warn:blank-comments": "node ./scripts/warn-blank-comments.mjs",
28
30
  "format": "prettier . --write",
29
31
  "format:check": "prettier . --check",
32
+ "precommit": "node ./scripts/precommit.mjs",
30
33
  "test": "vitest run",
31
34
  "test:watch": "vitest",
32
35
  "prepare": "npm run build"
@@ -35,13 +38,13 @@
35
38
  "node": ">=18"
36
39
  },
37
40
  "dependencies": {
38
- "glob": "^11.0.1",
39
- "ignore": "^7.0.4"
41
+ "glob": "^13.0.6",
42
+ "ignore": "^7.0.5"
40
43
  },
41
44
  "devDependencies": {
42
- "@types/node": "^22.13.10",
43
- "prettier": "^3.8.1",
44
- "typescript": "^5.8.2",
45
- "vitest": "^2.1.9"
45
+ "@types/node": "^25.6.0",
46
+ "prettier": "^3.8.3",
47
+ "typescript": "^6.0.3",
48
+ "vitest": "^4.1.5"
46
49
  }
47
50
  }