Skip to content

Commit

Permalink
MAJOR 3.0.0 (breaking): fixes #17: introduce after-comma:prop comment…
Browse files Browse the repository at this point in the history
… tokens; fixes README.md
  • Loading branch information
kaelzhang committed Feb 12, 2020
1 parent d4a7b17 commit d6bef5b
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 33 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ If the `content` is:
{ // before:foo
// before:foo
/* before:foo */
"foo" /* after-prop:foo */: // after-comma:foo
"foo" /* after-prop:foo */: // after-colon:foo
1 // after-value:foo
// after-value:foo
, // before:bar
, // after-comma:foo
// before:bar
"bar": [ // before:0
// before:0
Expand Down Expand Up @@ -177,7 +177,7 @@ And the result will be:
}
```
There are **EIGHT** kinds of symbol properties:
There are **NINE** kinds of symbol properties:
```js
// Comments before everything
Expand All @@ -189,7 +189,7 @@ Symbol.for('before')
// comment tokens before
// - a property of an object
// - an item of an array
// and before the previous comma(`,`) or the opening bracket(`{` or `[`)
// and after the previous comma(`,`) or the opening bracket(`{` or `[`)
Symbol.for(`before:${prop}`)

// comment tokens after property key `prop` and before colon(`:`)
Expand All @@ -205,6 +205,9 @@ Symbol.for(`after-colon:${prop}`)
// or the closing bracket(`}` or `]`)
Symbol.for(`after-value:${prop}`)

// comment tokens after the comma(`,`)
Symbol.for(`after-comma:${prop}`)

// if comments after
// - the last key-value:pair of an object
// - the last item of an array
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "comment-json",
"version": "2.4.2",
"version": "3.0.0",
"description": "Parse and stringify JSON with comments. It will retain comments even after saved!",
"main": "src/index.js",
"scripts": {
Expand Down
5 changes: 4 additions & 1 deletion src/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ const PREFIX_BEFORE = 'before'
const PREFIX_AFTER_PROP = 'after-prop'
const PREFIX_AFTER_COLON = 'after-colon'
const PREFIX_AFTER_VALUE = 'after-value'
const PREFIX_AFTER_COMMA = 'after-comma'

const SYMBOL_PREFIXES = [
PREFIX_BEFORE,
PREFIX_AFTER_PROP,
PREFIX_AFTER_COLON,
PREFIX_AFTER_VALUE
PREFIX_AFTER_VALUE,
PREFIX_AFTER_COMMA
]

const COLON = ':'
Expand Down Expand Up @@ -303,6 +305,7 @@ module.exports = {
PREFIX_AFTER_PROP,
PREFIX_AFTER_COLON,
PREFIX_AFTER_VALUE,
PREFIX_AFTER_COMMA,

COLON,
UNDEFINED
Expand Down
46 changes: 46 additions & 0 deletions src/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
PREFIX_AFTER_PROP,
PREFIX_AFTER_COLON,
PREFIX_AFTER_VALUE,
PREFIX_AFTER_COMMA,
COLON,
UNDEFINED
} = require('./array')
Expand Down Expand Up @@ -135,6 +136,38 @@ const restore_comments_host = () => {
comments_host = previous_hosts.pop()
}

const assign_after_comma_comments = () => {
if (!unassigned_comments) {
return
}

const after_comma_comments = []

for (const comment of unassigned_comments) {
// If the comment is inline, then it is an after-comma comment
if (comment.inline) {
after_comma_comments.push(comment)
// Otherwise, all comments are before:<next-prop> comment
} else {
break
}
}

const {length} = after_comma_comments
if (!length) {
return
}

if (length === unassigned_comments.length) {
// If unassigned_comments are all consumed
unassigned_comments = null
} else {
unassigned_comments.splice(0, length)
}

comments_host[symbolFor(PREFIX_AFTER_COMMA)] = after_comma_comments
}

const assign_comments = prefix => {
if (!unassigned_comments) {
return
Expand Down Expand Up @@ -210,6 +243,14 @@ const parse_object = () => {
next()
parse_comments()

assign_after_comma_comments()

// If there is a trailing comma, we might reach the end
// ```
// {
// "a": 1,
// }
// ```
if (is(CURLY_BRACKET_CLOSE)) {
break
}
Expand All @@ -218,6 +259,7 @@ const parse_object = () => {
started = true
expect('String')
name = JSON.parse(current.value)

set_prop(name)
assign_comments(PREFIX_BEFORE)

Expand Down Expand Up @@ -267,12 +309,15 @@ const parse_array = () => {
next()
parse_comments()

assign_after_comma_comments()

if (is(BRACKET_CLOSE)) {
break
}
}

started = true

set_prop(i)
assign_comments(PREFIX_BEFORE)

Expand Down Expand Up @@ -393,6 +438,7 @@ module.exports = {
PREFIX_AFTER_PROP,
PREFIX_AFTER_COLON,
PREFIX_AFTER_VALUE,
PREFIX_AFTER_COMMA,
PREFIX_AFTER,
PREFIX_AFTER_ALL,

Expand Down
70 changes: 51 additions & 19 deletions src/stringify.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
PREFIX_AFTER_PROP,
PREFIX_AFTER_COLON,
PREFIX_AFTER_VALUE,
PREFIX_AFTER_COMMA,
PREFIX_AFTER,
PREFIX_AFTER_ALL,

Expand Down Expand Up @@ -36,6 +37,7 @@ const BEFORE = prop => `${PREFIX_BEFORE}:${prop}`
const AFTER_PROP = prop => `${PREFIX_AFTER_PROP}:${prop}`
const AFTER_COLON = prop => `${PREFIX_AFTER_COLON}:${prop}`
const AFTER_VALUE = prop => `${PREFIX_AFTER_VALUE}:${prop}`
const AFTER_COMMA = prop => `${PREFIX_AFTER_COMMA}:${prop}`

// table of character substitutions
const meta = {
Expand Down Expand Up @@ -111,20 +113,23 @@ const clean = () => {
indent = EMPTY
}

const join_content = (inside, value, gap) => {
const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true)

return comment
? inside
const join = (one, two, gap) =>
one
? two
// Symbol.for('before') and Symbol.for('before:prop')
// might both exist if user mannually add comments to the object
// and make a mistake.
// We trim to make sure the layout
? comment + inside.trim() + LF + gap
: comment.trimRight() + LF + gap
: inside
? inside.trimRight() + LF + gap
// SO, we are not to only trimRight but trim for both sides
? one + two.trim() + LF + gap
: one.trimRight() + LF + gap
: two
? two.trimRight() + LF + gap
: EMPTY

const join_content = (inside, value, gap) => {
const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true)

return join(comment, inside, gap)
}

// | deeper_gap |
Expand All @@ -140,25 +145,36 @@ const array_stringify = (value, gap) => {

// From the item to before close
let inside = EMPTY
let after_comma = EMPTY

// Never use Array.prototype.forEach,
// that we should iterate all items
for (let i = 0; i < length; i ++) {
const before = process_comments(value, BEFORE(i), deeper_gap, true)

if (i !== 0) {
inside += COMMA
}

const before = join(
after_comma,
process_comments(value, BEFORE(i), deeper_gap),
deeper_gap
)

inside += before || (LF + deeper_gap)

// JSON.stringify([undefined]) => [null]
inside += stringify(i, value, deeper_gap) || STR_NULL

inside += process_comments(value, AFTER_VALUE(i), deeper_gap)

after_comma = process_comments(value, AFTER_COMMA(i), deeper_gap)
}

inside += process_comments(value, PREFIX_AFTER, deeper_gap)
inside += join(
after_comma,
process_comments(value, PREFIX_AFTER, deeper_gap),
deeper_gap
)

return BRACKET_OPEN
+ join_content(inside, value, gap)
Expand Down Expand Up @@ -186,8 +202,13 @@ const object_stringify = (value, gap) => {

// From the first element to before close
let inside = EMPTY
let after_comma = EMPTY
let first = true

const keys = isArray(replacer)
? replacer
: Object.keys(value)

const iteratee = key => {
// Stringified value
const sv = stringify(key, value, deeper_gap)
Expand All @@ -197,13 +218,18 @@ const object_stringify = (value, gap) => {
return
}

// The treat ment
if (!first) {
inside += COMMA
}

first = false

const before = process_comments(value, BEFORE(key), deeper_gap, true)
const before = join(
after_comma,
process_comments(value, BEFORE(key), deeper_gap),
deeper_gap
)

inside += before || (LF + deeper_gap)

Expand All @@ -214,15 +240,21 @@ const object_stringify = (value, gap) => {
+ colon_value_gap
+ sv
+ process_comments(value, AFTER_VALUE(key), deeper_gap)
}

const keys = isArray(replacer)
? replacer
: Object.keys(value)
after_comma = process_comments(value, AFTER_COMMA(key), deeper_gap)
}

keys.forEach(iteratee)

inside += process_comments(value, PREFIX_AFTER, deeper_gap)
// if (after_comma) {
// inside += COMMA
// }

inside += join(
after_comma,
process_comments(value, PREFIX_AFTER, deeper_gap),
deeper_gap
)

return CURLY_BRACKET_OPEN
+ join_content(inside, value, gap)
Expand Down
23 changes: 20 additions & 3 deletions test/parse.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,26 @@ const parser = require('..')
// console.log(a);

const cases = [
{
d: '#17: after-comma comment, with trailing comma',
s: `//top
{
"foo": "bar", // after comma foo
"bar": "baz", // after comma bar
}`,
o: '{"foo":"bar","bar":"baz"}',
e (t, obj) {
t.is(obj.foo, 'bar')
t.is(obj[Symbol.for('after-comma:foo')][0].value, ' after comma foo')
t.is(obj[Symbol.for('after-comma:bar')][0].value, ' after comma bar')
}
},
{
d: '#8: object with trailing comma',
s: `//top
{
"foo": "bar", // after
"foo": "bar",
// after
}`,
o: '{"foo":"bar"}',
e (t, obj) {
Expand Down Expand Up @@ -139,7 +154,7 @@ const cases = [
e (t, obj) {
t.is(obj.a, 1)
t.is(obj.b, 2)
const [c] = obj[Symbol.for('before:b')]
const [c] = obj[Symbol.for('after-comma:a')]
t.is(c.value, ' a')
t.is(c.inline, true)
}
Expand Down Expand Up @@ -232,8 +247,10 @@ g*/ //g2
t.is(b.value, ' b')
t.is(c.value, 'c')

const [d, e] = obj.a[Symbol.for('before:1')]
const [d] = obj.a[Symbol.for('after-comma:0')]
t.is(d.value, ' d')

const [e] = obj.a[Symbol.for('before:1')]
t.is(e.value, ' e')

const [n] = obj.a[Symbol.for('after-value:1')]
Expand Down
Loading

0 comments on commit d6bef5b

Please sign in to comment.