• Stars
    star
    138
  • Rank 263,056 (Top 6 %)
  • Language
    JavaScript
  • License
    Other
  • Created over 10 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Parse and stringify JSON with comments. It will retain comments even when after saved!

Build Status Coverage npm module downloads per month

comment-json

Parse and stringify JSON with comments. It will retain comments even after saved!

  • Parse JSON strings with comments into JavaScript objects and MAINTAIN comments
    • supports comments everywhere, yes, EVERYWHERE in a JSON file, eventually 😆
    • fixes the known issue about comments inside arrays.
  • Stringify the objects into JSON strings with comments if there are

The usage of comment-json is exactly the same as the vanilla JSON object.

Table of Contents

Why?

There are many other libraries that can deal with JSON with comments, such as json5, or strip-json-comments, but none of them can stringify the parsed object and return back a JSON string the same as the original content.

Imagine that if the user settings are saved in ${library}.json, and the user has written a lot of comments to improve readability. If the library library need to modify the user setting, such as modifying some property values and adding new fields, and if the library uses json5 to read the settings, all comments will disappear after modified which will drive people insane.

So, if you want to parse a JSON string with comments, modify it, then save it back, comment-json is your must choice!

How?

comment-json parse JSON strings with comments and save comment tokens into symbol properties.

For JSON array with comments, comment-json extends the vanilla Array object into CommentArray whose instances could handle comments changes even after a comment array is modified.

Install

$ npm i comment-json

For TypeScript developers, @types/comment-json could be used

Since 2.4.1, comment-json contains typescript declarations, so you might as well remove @types/comment-json.

Usage

package.json:

{
  // package name
  "name": "comment-json"
}
const {
  parse,
  stringify,
  assign
} = require('comment-json')
const fs = require('fs')

const obj = parse(fs.readFileSync('package.json').toString())

console.log(obj.name) // comment-json

stringify(obj, null, 2)
// Will be the same as package.json, Oh yeah! 😆
// which will be very useful if we use a json file to store configurations.

Sort keys

It is a common use case to sort the keys of a JSON file

const parsed = parse(`{
  // b
  "b": 2,
  // a
  "a": 1
}`)

// Copy the properties including comments from `parsed` to the new object `{}`
// according to the sequence of the given keys
const sorted = assign(
  {},
  parsed,
  // You could also use your custom sorting function
  Object.keys(parsed).sort()
)

console.log(stringify(sorted, null, 2))
// {
//   // a
//   "a": 1,
//   // b
//   "b": 2
// }

For details about assign, see here.

parse()

parse(text, reviver? = null, remove_comments? = false)
  : object | string | number | boolean | null
  • text string The string to parse as JSON. See the JSON object for a description of JSON syntax.
  • reviver? Function() | null Default to null. It acts the same as the second parameter of JSON.parse. If a function, prescribes how the value originally produced by parsing is transformed, before being returned.
  • remove_comments? boolean = false If true, the comments won't be maintained, which is often used when we want to get a clean object.

Returns CommentJSONValue (object | string | number | boolean | null) corresponding to the given JSON text.

If the content is:

/**
 before-all
 */
// before-all
{ // before:foo
  // before:foo
  /* before:foo */
  "foo" /* after-prop:foo */: // after-colon:foo
  1 // after-value:foo
  // after-value:foo
  , // after:foo
  // before:bar
  "bar": [ // before:0
    // before:0
    "baz" // after-value:0
    // after-value:0
    , // after:0
    "quux"
    // after:1
  ] // after:bar
  // after:bar
}
// after-all
const {inspect} = require('util')

const parsed = parse(content)

console.log(
  inspect(parsed, {
    // Since 4.0.0, symbol properties of comments are not enumerable,
    // use `showHidden: true` to print them
    showHidden: true
  })
)

console.log(Object.keys(parsed))
// > ['foo', 'bar']

console.log(stringify(parsed, null, 2))
// 🚀 Exact as the content above! 🚀

And the value of parsed will be:

{
  // Comments before the JSON object
  [Symbol.for('before-all')]: [{
    type: 'BlockComment',
    value: '\n before-all\n ',
    inline: false,
    loc: {
      // The start location of `/**`
      start: {
        line: 1,
        column: 0
      },
      // The end location of `*/`
      end: {
        line: 3,
        column: 3
      }
    }
  }, {
    type: 'LineComment',
    value: ' before-all',
    inline: false,
    loc: ...
  }],
  ...

  [Symbol.for('after-prop:foo')]: [{
    type: 'BlockComment',
    value: ' after-prop:foo ',
    inline: true,
    loc: ...
  }],

  // The real value
  foo: 1,
  bar: [
    "baz",
    "quux",

    // The property of the array
    [Symbol.for('after-value:0')]: [{
      type: 'LineComment',
      value: ' after-value:0',
      inline: true,
    loc: ...
    }, ...],
    ...
  ]
}

There are EIGHT kinds of symbol properties:

// Comments before everything
Symbol.for('before-all')

// If all things inside an object or an array are comments
Symbol.for('before')

// comment tokens before
// - a property of an object
// - an item of an array
// and after the previous comma(`,`) or the opening bracket(`{` or `[`)
Symbol.for(`before:${prop}`)

// comment tokens after property key `prop` and before colon(`:`)
Symbol.for(`after-prop:${prop}`)

// comment tokens after the colon(`:`) of property `prop` and before property value
Symbol.for(`after-colon:${prop}`)

// comment tokens after
// - the value of property `prop` inside an object
// - the item of index `prop` inside an array
// and before the next key-value/item delimiter(`,`)
// or the closing bracket(`}` or `]`)
Symbol.for(`after-value:${prop}`)

// comment tokens after
// - comma(`,`)
// - the value of property `prop` if it is the last property
Symbol.for(`after:${prop}`)

// Comments after everything
Symbol.for('after-all')

And the value of each symbol property is an array of CommentToken

interface CommentToken {
  type: 'BlockComment' | 'LineComment'
  // The content of the comment, including whitespaces and line breaks
  value: string
  // If the start location is the same line as the previous token,
  // then `inline` is `true`
  inline: boolean

  // But pay attention that,
  // locations will NOT be maintained when stringified
  loc: CommentLocation
}

interface CommentLocation {
  // The start location begins at the `//` or `/*` symbol
  start: Location
  // The end location of multi-line comment ends at the `*/` symbol
  end: Location
}

interface Location {
  line: number
  column: number
}

Query comments in TypeScript

comment-json provides a symbol-type called CommentSymbol which can be used for querying comments. Furthermore, a type CommentDescriptor is provided for enforcing properly formatted symbol names:

import {
  CommentDescriptor, CommentSymbol, parse, CommentArray
} from 'comment-json'

const parsed = parse(`{ /* test */ "foo": "bar" }`)
 // typescript only allows properly formatted symbol names here
const symbolName: CommentDescriptor = 'before:foo'

console.log((parsed as CommentArray<string>)[Symbol.for(symbolName) as CommentSymbol][0].value)

In this example, casting to Symbol.for(symbolName) to CommentSymbol is mandatory. Otherwise, TypeScript won't detect that you're trying to query comments.

Parse into an object without comments

console.log(parse(content, null, true))

And the result will be:

{
  foo: 1,
  bar: [
    "baz",
    "quux"
  ]
}

Special cases

const parsed = parse(`
// comment
1
`)

console.log(parsed === 1)
// false

If we parse a JSON of primative type with remove_comments:false, then the return value of parse() will be of object type.

The value of parsed is equivalent to:

const parsed = new Number(1)

parsed[Symbol.for('before-all')] = [{
  type: 'LineComment',
  value: ' comment',
  inline: false,
  loc: ...
}]

Which is similar for:

  • Boolean type
  • String type

For example

const parsed = parse(`
"foo" /* comment */
`)

Which is equivalent to

const parsed = new String('foo')

parsed[Symbol.for('after-all')] = [{
  type: 'BlockComment',
  value: ' comment ',
  inline: true,
  loc: ...
}]

But there is one exception:

const parsed = parse(`
// comment
null
`)

console.log(parsed === null) // true

stringify()

stringify(object: any, replacer?, space?): string

The arguments are the same as the vanilla JSON.stringify.

And it does the similar thing as the vanilla one, but also deal with extra properties and convert them into comments.

console.log(stringify(parsed, null, 2))
// Exactly the same as `content`

space

If space is not specified, or the space is an empty string, the result of stringify() will have no comments.

For the case above:

console.log(stringify(result)) // {"a":1}
console.log(stringify(result, null, 2)) // is the same as `code`

assign(target: object, source?: object, keys?: Array)

  • target object the target object
  • source? object the source object. This parameter is optional but it is silly to not pass this argument.
  • keys? Array<string> If not specified, all enumerable own properties of source will be used.

This method is used to copy the enumerable own properties and their corresponding comment symbol properties to the target object.

const parsed = parse(`// before all
{
  // This is a comment
  "foo": "bar"
}`)

const obj = assign({
  bar: 'baz'
}, parsed)

stringify(obj, null, 2)
// // before all
// {
//   "bar": "baz",
//   // This is a comment
//   "foo": "bar"
// }

Special cases about keys

But if argument keys is specified and is not empty, then comment before all, which belongs to no properties, will NOT be copied.

const obj = assign({
  bar: 'baz'
}, parsed, ['foo'])

stringify(obj, null, 2)
// {
//   "bar": "baz",
//   // This is a comment
//   "foo": "bar"
// }

Specifying the argument keys as an empty array indicates that it will only copy non-property symbols of comments

const obj = assign({
  bar: 'baz'
}, parsed, [])

stringify(obj, null, 2)
// // before all
// {
//   "bar": "baz",
// }

Non-property symbols include:

Symbol.for('before-all')
Symbol.for('before')
Symbol.for('after-all')

CommentArray

Advanced Section

All arrays of the parsed object are CommentArrays.

The constructor of CommentArray could be accessed by:

const {CommentArray} = require('comment-json')

If we modify a comment array, its comment symbol properties could be handled automatically.

const parsed = parse(`{
  "foo": [
    // bar
    "bar",
    // baz,
    "baz"
  ]
}`)

parsed.foo.unshift('qux')

stringify(parsed, null, 2)
// {
//   "foo": [
//     "qux",
//     // bar
//     "bar",
//     // baz
//     "baz"
//   ]
// }

Oh yeah! 😆

But pay attention, if you reassign the property of a comment array with a normal array, all comments will be gone:

parsed.foo = ['quux'].concat(parsed.foo)
stringify(parsed, null, 2)
// {
//   "foo": [
//     "quux",
//     "qux",
//     "bar",
//     "baz"
//   ]
// }

// Whoooops!! 😩 Comments are gone

Instead, we should:

parsed.foo = new CommentArray('quux').concat(parsed.foo)
stringify(parsed, null, 2)
// {
//   "foo": [
//     "quux",
//     "qux",
//     // bar
//     "bar",
//     // baz
//     "baz"
//   ]
// }

Special Cases about Trailing Comma

If we have a JSON string str

{
  "foo": "bar", // comment
}
// When stringify, trailing commas will be eliminated
const stringified = stringify(parse(str), null, 2)
console.log(stringified)

And it will print:

{
  "foo": "bar" // comment
}

License

MIT

Change Logs

See releases

More Repositories

1

node-ignore

🔍 node-ignore is the manager and filter for .gitignore rules, the one used by eslint, prettier and many others.
JavaScript
393
star
2

shell-safe-rm

😎 Safe-rm: A drop-in and much safer replacement of bash rm with nearly full functionalities and options of the rm command! Safe-rm will act exactly the same as the original rm command.
Shell
379
star
3

skema

🛰 Skema provides a handy & composable way to validate / transform / purify the input data.
JavaScript
359
star
4

stock-pandas

🚀 The production-ready subclass of `pandas.DataFrame` to support stock statistics and indicators
Python
114
star
5

penteract-ocr

⭐️ The native node.js bindings to the Tesseract OCR project.
C++
108
star
6

neuron.js

:shipit: A Full Feature CommonJS Module Manager, Dependency Graph Handler and Loader for Browsers
JavaScript
63
star
7

ctrip-apollo

The most delightful and handy Node.js client for ctrip apollo configuration service.
JavaScript
56
star
8

finmath

The collections of simple, weighted, exponential, smoothed moving averages.
JavaScript
53
star
9

egg-wechat-pay

Wechat pay plugin for egg(WIP)
JavaScript
34
star
10

DA-RNN-in-Tensorflow-2-and-PyTorch

A Tensorflow 2 (Keras) implementation of DA-RNN (A Dual-Stage Attention-Based Recurrent Neural Network for Time Series Prediction, arXiv:1704.02971)
Jupyter Notebook
26
star
11

node-dubbo

Dubbo client for node.
JavaScript
25
star
12

moving-averages

The collections of simple, weighted, exponential, smoothed moving averages.
JavaScript
23
star
13

macd

FinTech utility to calculate MACD, the Moving Average Convergence / Divergence.
JavaScript
22
star
14

b2a

btoa and atob support for node.js or old browsers, with the Unicode Problems fixed
JavaScript
20
star
15

document

Create your document site by one command.
CSS
19
star
16

bollinger-bands

Utilities to draw and calculate bollinger bands
JavaScript
19
star
17

express-to-koa

Use express middlewares in Koa2, the one that really works.
JavaScript
19
star
18

node-glob-gitignore

Extends `glob` with support for filtering files according to gitignore rules and exposes an optional Promise API
JavaScript
16
star
19

git-cloc

Count Lines of Code for Git
Shell
14
star
20

node-fs-sync

Synchronous fs with more fun
JavaScript
13
star
21

cert-manager-webhook-dnspod

Cert-manager webhook for DNSPod
Go
12
star
22

dianping-bootcamp-summer-2013

Resources, homework, and discussion
JavaScript
11
star
23

python-aioretry

Asyncio retry utility for Python 3.7+
Python
10
star
24

node-argv-split

Split argv(argument vector) and handle special cases.
JavaScript
10
star
25

node-scaffold-generator

Generates a repo from a specified template and data.
JavaScript
10
star
26

helm-stable-charts-mirror

国内镜像 A mirror of helm stable charts, the drop-in replacement of https://kubernetes-charts.storage.googleapis.com
JavaScript
9
star
27

178manga-reader

你,懂的!Manga reader!
JavaScript
9
star
28

egg-snowflake

Egg plugin to generate unique and increased twitter-snowflake uuid.
JavaScript
8
star
29

node-socket-pool

Persistent socket connections with pool for node server side.
JavaScript
8
star
30

easing-functions

Robert Penner's easing functions
JavaScript
8
star
31

comfort-legacy

Comfort is a much better node.js commander solution for sub commands, such as `git xxx` or `npm xxx`
JavaScript
7
star
32

node-json-parser

JSON parser to parse JSON object and MAINTAIN comments.
JavaScript
7
star
33

stock-pandas-examples

Examples for stock-pandas
Jupyter Notebook
6
star
34

node-finmath

Fintech mathematics
JavaScript
6
star
35

ngx

Data-driven nginx configuration manager.
JavaScript
5
star
36

node-code-stringify

The node.js module that converts JavaScript variables into source codes. Unlike `JSON.stringify`, code-stringify also deals with reference(object) types of variables.
JavaScript
5
star
37

node-semver-extra

semver-extra contains methods that aren't included in the vanilla semver package.
JavaScript
5
star
38

node-commonjs-walker

Analyzer and tree walker for commonjs
JavaScript
5
star
39

bot-state-machine

Finite state machine for chat bot
JavaScript
5
star
40

node-engine-x

engine-x, nginx the node version.
JavaScript
4
star
41

node-semver-stable

Manage stable semver versions
JavaScript
4
star
42

node-modified

Modified is a simple request client to deal with http local cache.
JavaScript
4
star
43

hippo

Simple analytics client.
JavaScript
4
star
44

stock-charts

Stock charts based on D3(STILL WORKING IN PROGRESS)
JavaScript
4
star
45

aya

Just a wrapped tap, more fun.
JavaScript
3
star
46

lua-gaia

Gaia, the NginX cache addons in Lua based on OpenResty.
Harbour
3
star
47

gaia

Gaia, the framework to make gRPC services
JavaScript
3
star
48

generator-py

Yeoman generator to create a python project with test/coverage ready
Python
3
star
49

promise-faker

Provides promise-like APIs but does the synchronous things.
JavaScript
3
star
50

node-array-timsort

Fast JavaScript array sorting by implementing Python's Timsort algorithm
JavaScript
3
star
51

node-typo

typo is an extendable template engine designed for the future
JavaScript
3
star
52

node-ambassador

Ambassador provides a way to communicate between node.js processes
JavaScript
3
star
53

Kubernetes-Tips

Tips for usage of kubernetes and helm
3
star
54

error-stack

Parse and manipulate error-stack
JavaScript
3
star
55

node-ssh-url

Utilities to resolute and parse ssh url.
JavaScript
3
star
56

node-hashed-fs

Handle file system with content hashing
JavaScript
3
star
57

vue-stock-chart

Vue stock chart component
JavaScript
3
star
58

node-home

Resolves home directories, `resolve('~/path/to')`
JavaScript
3
star
59

atta

Server-side UI rendering framework in Go
2
star
60

node-candlesticks

The thing to manage candlesticks
JavaScript
2
star
61

docker-image-futuopend

Docker image for FutuOpenD
Dockerfile
2
star
62

node-time-spans

Time spans especially for financial technology.
JavaScript
2
star
63

atta.js

(WIP) The node graphics library
JavaScript
2
star
64

tpl

A simple JavaScript template engine
JavaScript
2
star
65

node-util-inherits

node util.inherits with compatibility
JavaScript
2
star
66

contenthash-html-webpack-plugin

Makes [contenthash] available for html-webpack-plugin
JavaScript
2
star
67

node-math-array

Math utility to calculate with two arrays.
JavaScript
2
star
68

promise.extra

Promise.series, Promise.waterfall with vanilla Promise.
JavaScript
2
star
69

node-deferrer

Deferrer is a fast node.js promise-object generator.
JavaScript
2
star
70

node-fs-expand

An extended fs glob
JavaScript
2
star
71

node-err-object

Custom error object.
JavaScript
2
star
72

s-deviation

Utility to calculate standard deviation.
JavaScript
2
star
73

node-githuburl

Parse a github repo url into an object of repo information and convert to several types of clone URL.
JavaScript
2
star
74

node-tmp-sync

The sync version of tmp just for test cases, making it much easier.
JavaScript
2
star
75

graceful-instanceof

The instanceof mechanism cross package/module versions.
JavaScript
2
star
76

git-perm-rm

Permanently remove a file or directory from a git repo.
Shell
2
star
77

node-logical-promise

Javascript logical operators on Promises
JavaScript
2
star
78

babel-transform-dir

Transforms javascript files within a directory by babel, and expose a Promise API.
JavaScript
2
star
79

node-ip-address

Get the local ip address
JavaScript
2
star
80

node-cookie-store

An RFC-6265 cookie store to implement the mechanism of HTTP cookie and Set-Cookie header fields as a browser does.
JavaScript
2
star
81

node-object-access

Access(read and write) an object hierachically.
JavaScript
2
star
82

node-ocr

Optical character recognition for node.js
JavaScript
2
star
83

node-rar

Node utility and command-line tool to pack and unpack rar files.
JavaScript
2
star
84

js-bridge

A JavaScript bridge for sending messages between JavaScript and native code(Obj-C/Java) in WebViews.
Shell
2
star
85

node-nodeinit

Creates a most frequent scaffold of your node.js project for the first commit.
JavaScript
2
star
86

modified-lru-cache

Lru-cache for `modified`: npmjs.org/package/modified
JavaScript
1
star
87

module-walker

JavaScript module traverser
JavaScript
1
star
88

node-print-code

Print visualized slice of code from its content, line and column for CLI
JavaScript
1
star
89

node-xconfig

Node.js Configurations
JavaScript
1
star
90

weixin-auth

Wechat authorization
JavaScript
1
star
91

neuron-cli

Command line tools for neuron
JavaScript
1
star
92

nginx-ingress-controller

Just wrap the original nginx-ingress-controller for cloud registry
Dockerfile
1
star
93

tiller

Rewrap the gcr.io/kubernetes-helm/tiller to cross the wall
Shell
1
star
94

p-async-cache

Cache the async promise lookups and avoid fetching the same thing more than necessary.
JavaScript
1
star
95

cortex-dashboard

GUI dashboard to play with [cortex](https://github.com/kaelzhang/cortex)
1
star
96

model

JavaScript
1
star
97

node-booker

Booker is a lovely logger for node.js which is SMART, SMALL, and EASY-TO-USE.
JavaScript
1
star
98

creeper

Creepy spider
1
star
99

egg-ctrip-apollo

Egg plugin for Ctrip's apollo configuration service
JavaScript
1
star
100

node-asks

A collection of common interactive command line user interfaces. An altered version of inquirer.js
JavaScript
1
star