Comment-Preserving JSON/JSON5 Writer
json5-writer is a JavaScript utility designed to parse and modify JSON and JSON5 files while meticulously preserving comments, whitespace, and original formatting. Unlike typical JSON parsers that discard non-data elements, this library converts JSON5 input into a JavaScript Abstract Syntax Tree (AST) using jscodeshift, allowing programmatic updates to values without disturbing surrounding comments or formatting. It is particularly useful for configuration file management where human-readable comments are critical. The current stable version is 0.2.0. The package does not explicitly state its release cadence, but its unique AST-based approach provides fine-grained control over output, distinguishing it from simpler JSON modification tools. It supports both JSON and JSON5 syntax for input and can output standard JSON or JSON5 with configurable options for quoting and trailing commas.
Common errors
-
Error: Cannot find module 'jscodeshift'
cause The `jscodeshift` package is a peer dependency or a required runtime dependency for `json5-writer`'s core functionality and must be installed separately.fixInstall `jscodeshift` as a dependency: `npm install jscodeshift` or `yarn add jscodeshift`. -
TypeError: writer.load is not a function
cause Incorrect import of `json5-writer`. The library exports a default object, and `load` is a method on that object.fixFor CommonJS, use `const json5Writer = require('json5-writer');` then `json5Writer.load()`. For ESM, use `import json5Writer from 'json5-writer';` then `json5Writer.load()`. -
SyntaxError: Unexpected token / in JSON at position X
cause Attempting to parse a JSON5 string (which allows comments) with the native `JSON.parse()` method before passing it to `json5-writer`, or when `.toJSON()` is expected but comments are still present.fixEnsure you are passing the raw JSON5 string directly to `json5Writer.load()`. If outputting JSON, use `.toJSON()` and verify comments are stripped, or ensure no comments are added if the target system only accepts strict JSON.
Warnings
- gotcha When using the `.write(value)` method, any property or field present in the original document but not in the `value` object passed to `.write()` will be removed. To explicitly preserve an existing value while updating other fields, you must set its corresponding property in the `value` object to `undefined`.
- gotcha The default output options for `.toSource()` (which outputs JSON5) and `.toJSON()` (which outputs standard JSON) differ. `.toSource()` by default uses single quotes, trailing commas, and infers key quoting, whereas `.toJSON()` defaults to double quotes, no trailing commas, and quotes all keys.
- gotcha Advanced manipulation of the parsed document requires direct interaction with the underlying jscodeshift AST via the `.ast` property. This necessitates familiarity with jscodeshift's API and AST structures, which can have a learning curve.
Install
-
npm install json5-writer -
yarn add json5-writer -
pnpm add json5-writer
Imports
- json5Writer
const json5Writer = require('json5-writer').default;import json5Writer from 'json5-writer';
- json5Writer.load
import { load } from 'json5-writer';import json5Writer from 'json5-writer'; const writer = json5Writer.load(jsonStr);
- j
const j = require('jscodeshift').j;import j from 'jscodeshift';
Quickstart
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import json5Writer from 'json5-writer';
async function updateConfigFile() {
const configPath = path.join(process.cwd(), 'config.json5');
const initialConfig = `{
// Main application settings
'app-name': 'My Awesome App',
// Database connection details
db: {
host: 'localhost',
port: 5432,
user: 'admin'
},
// Feature flags
features: {
beta: true,
analytics: false
}
}`;
// Ensure config file exists for demonstration
await fs.writeFile(configPath, initialConfig, 'utf-8');
try {
const configContent = await fs.readFile(configPath, 'utf-8');
const writer = json5Writer.load(configContent);
writer.write({
'app-name': 'New App Name',
db: {
host: process.env.DB_HOST ?? 'prod.database.com', // Use env var or default
// 'port' will be preserved if not explicitly overwritten with undefined or a new value
user: 'deploy_user' // Update user
},
features: {
beta: undefined, // Preserve existing 'beta' value
analytics: true, // Update analytics flag
'new-feature': true // Add a new feature
}
});
const updatedConfigContent = writer.toSource({
quote: 'single',
trailingComma: true,
quoteKeys: undefined
});
console.log('Updated config.json5 content:\n', updatedConfigContent);
await fs.writeFile(configPath, updatedConfigContent, 'utf-8');
console.log(`Successfully updated ${configPath}`);
} catch (error) {
console.error(`Failed to update config file: ${error.message}`);
}
}
updateConfigFile();