{"id":16097,"library":"json5-writer","title":"Comment-Preserving JSON/JSON5 Writer","description":"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.","status":"active","version":"0.2.0","language":"javascript","source_language":"en","source_url":"ssh://git@github.com/noahsug/json5-writer","tags":["javascript","json5","preserve","comments","parse","parser","config","update","ast"],"install":[{"cmd":"npm install json5-writer","lang":"bash","label":"npm"},{"cmd":"yarn add json5-writer","lang":"bash","label":"yarn"},{"cmd":"pnpm add json5-writer","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core dependency for AST manipulation and transformation; advanced usage directly exposes its API.","package":"jscodeshift","optional":false}],"imports":[{"note":"While the README uses `require('json5-writer')` for CommonJS, modern Node.js and bundlers often resolve this to a default import. For direct CommonJS usage, `const json5Writer = require('json5-writer');` is correct. The import shown here is for ESM.","wrong":"const json5Writer = require('json5-writer').default;","symbol":"json5Writer","correct":"import json5Writer from 'json5-writer';"},{"note":"`load` is a method on the default export object, not a named export itself.","wrong":"import { load } from 'json5-writer';","symbol":"json5Writer.load","correct":"import json5Writer from 'json5-writer'; const writer = json5Writer.load(jsonStr);"},{"note":"jscodeshift's main export is often referred to as `j` by convention in its examples for AST traversal. This is a default import for jscodeshift itself, used for direct AST manipulation.","wrong":"const j = require('jscodeshift').j;","symbol":"j","correct":"import j from 'jscodeshift';"}],"quickstart":{"code":"import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport json5Writer from 'json5-writer';\n\nasync function updateConfigFile() {\n  const configPath = path.join(process.cwd(), 'config.json5');\n  const initialConfig = `{\n  // Main application settings\n  'app-name': 'My Awesome App',\n  // Database connection details\n  db: {\n    host: 'localhost',\n    port: 5432,\n    user: 'admin'\n  },\n  // Feature flags\n  features: {\n    beta: true,\n    analytics: false\n  }\n}`;\n\n  // Ensure config file exists for demonstration\n  await fs.writeFile(configPath, initialConfig, 'utf-8');\n\n  try {\n    const configContent = await fs.readFile(configPath, 'utf-8');\n    const writer = json5Writer.load(configContent);\n\n    writer.write({\n      'app-name': 'New App Name',\n      db: {\n        host: process.env.DB_HOST ?? 'prod.database.com', // Use env var or default\n        // 'port' will be preserved if not explicitly overwritten with undefined or a new value\n        user: 'deploy_user' // Update user\n      },\n      features: {\n        beta: undefined, // Preserve existing 'beta' value\n        analytics: true, // Update analytics flag\n        'new-feature': true // Add a new feature\n      }\n    });\n\n    const updatedConfigContent = writer.toSource({\n      quote: 'single',\n      trailingComma: true,\n      quoteKeys: undefined\n    });\n    console.log('Updated config.json5 content:\\n', updatedConfigContent);\n\n    await fs.writeFile(configPath, updatedConfigContent, 'utf-8');\n    console.log(`Successfully updated ${configPath}`);\n  } catch (error) {\n    console.error(`Failed to update config file: ${error.message}`);\n  }\n}\n\nupdateConfigFile();","lang":"typescript","description":"This quickstart demonstrates loading, modifying, and saving a JSON5 configuration file, preserving comments and formatting. It shows how to update existing properties, add new ones, and use `undefined` to explicitly retain a property's value when writing a new object, using environment variables for dynamic values."},"warnings":[{"fix":"Pass `undefined` for properties you wish to retain from the original document but not modify with the current `.write()` call. For example, `writer.write({ propertyToKeep: undefined, otherProperty: 'newValue' })`.","message":"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`.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Always explicitly specify options for `quote`, `trailingComma`, and `quoteKeys` in both `.toSource()` and `.toJSON()` if you require a consistent or specific output format across different calls or when converting between JSON5 and JSON.","message":"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.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Consult the `jscodeshift` documentation for AST node types and traversal methods. Start with simple `find` and `forEach` operations to understand the AST structure before attempting complex transformations.","message":"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.","severity":"gotcha","affected_versions":">=0.1.0"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"Install `jscodeshift` as a dependency: `npm install jscodeshift` or `yarn add 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.","error":"Error: Cannot find module 'jscodeshift'"},{"fix":"For CommonJS, use `const json5Writer = require('json5-writer');` then `json5Writer.load()`. For ESM, use `import json5Writer from 'json5-writer';` then `json5Writer.load()`.","cause":"Incorrect import of `json5-writer`. The library exports a default object, and `load` is a method on that object.","error":"TypeError: writer.load is not a function"},{"fix":"Ensure 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.","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.","error":"SyntaxError: Unexpected token / in JSON at position X"}],"ecosystem":"npm"}