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 +24 -0
- package/README.md +43 -13
- package/dist/json5-manifest-sync.d.ts +7 -9
- package/dist/json5-manifest-sync.js +49 -25
- package/package.json +11 -8
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
|
|
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
|
|
69
|
+
## Install from npm (recommended)
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm install --save-dev json5-manifest-sync
|
|
73
|
+
```
|
|
60
74
|
|
|
61
|
-
|
|
75
|
+
Global install (optional):
|
|
62
76
|
|
|
63
77
|
```bash
|
|
64
|
-
npm install
|
|
78
|
+
npm install -g json5-manifest-sync
|
|
65
79
|
```
|
|
66
80
|
|
|
67
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
-
|
|
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": "
|
|
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
|
-
*
|
|
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
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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
|
|
40
|
-
* environment variable `SYNC_JSON5_ADD_EMPTY_COMMENT=false`.
|
|
38
|
+
* Defaults to `preserve`.
|
|
41
39
|
*/
|
|
42
|
-
|
|
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 = {
|
|
231
|
+
export function syncJson5(raw, source, options = { blankCommentMode: "preserve" }) {
|
|
206
232
|
const lines = raw.split("\n");
|
|
207
233
|
const comments = buildCommentMap(lines);
|
|
208
|
-
const
|
|
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 (
|
|
235
|
-
for (const c of
|
|
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 (
|
|
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 (
|
|
270
|
-
for (const c of
|
|
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 (
|
|
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
|
|
333
|
-
let
|
|
334
|
-
|
|
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
|
|
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 === "
|
|
347
|
-
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
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": "^
|
|
39
|
-
"ignore": "^7.0.
|
|
41
|
+
"glob": "^13.0.6",
|
|
42
|
+
"ignore": "^7.0.5"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
|
-
"@types/node": "^
|
|
43
|
-
"prettier": "^3.8.
|
|
44
|
-
"typescript": "^
|
|
45
|
-
"vitest": "^
|
|
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
|
}
|