diff --git a/README.md b/README.md index 10a6a7a..1129ec8 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ JSON Inline Doc ============================ -#### Add inline comments on stringified JSON, or generate from JSON schema +#### Add inline comments on stringified JSON (JSONC), or generate from JSON schema Use case: Using JSON for configuration and provide inline documentation as comments for users. -Note: JSON does not support comments. But some editors have the so-called 'JSON with comments' support. +JSONC is JSON with JavaScript style comments. Please note that original JSON does not support comments. Installation: -------------------------- @@ -113,6 +113,14 @@ The abstract base class of all writers. **`writer = new JSONCommentWriterBase(configuration)`** * Note: The above line of code is only for explanation. This class is abstract - do not try to `new` a instance by yourself! * `configuration`: object (optional) + * `emptyLineBeforeComments`: boolean (default true) + + Add a blank line before every `block` and `line` comment, except comments of the followings: + * The root object + * The first item in array + * The first key-value pair in object + + Not supported if `space` when calling `stringify` is 0. * `spaceAroundCommentSymbol`: boolean (default true) Add space around '//', '/\*' and '\*/'. diff --git a/package.json b/package.json index d2e8b47..d4bede1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "json-inline-doc", - "version": "1.0.2", - "description": "Add inline comments on stringified JSON, or generate from JSON schema", + "version": "2.0.0", + "description": "Add inline comments on stringified JSON (JSONC), or generate from JSON schema", "main": "./lib/index.js", "typings": "./lib/index.d.ts", "repository": { diff --git a/src/defaultCommentGenerator.ts b/src/defaultCommentGenerator.ts index 66342b0..f49bc66 100644 --- a/src/defaultCommentGenerator.ts +++ b/src/defaultCommentGenerator.ts @@ -22,8 +22,10 @@ export const defaultCommentGenerator: CommentGenerator = (schema) => { } } } - return contents.length === 0 ? undefined : { - type: 'block', - content: contents.join('\n') - }; + return contents.length === 0 + ? undefined + : { + type: 'block', + content: contents.join('\n') + }; }; \ No newline at end of file diff --git a/src/jsonCommentWriterBase.ts b/src/jsonCommentWriterBase.ts index d4cab38..f3e39f6 100644 --- a/src/jsonCommentWriterBase.ts +++ b/src/jsonCommentWriterBase.ts @@ -13,6 +13,7 @@ import { */ export abstract class JSONCommentWriterBase { private static readonly defaultConfiguration: IJSONCommentConfiguration = { + emptyLineBeforeComments: true, spaceAroundCommentSymbol: true, styledBlockComment: true, maxLineLength: 80 @@ -185,8 +186,7 @@ ${gap} */`; return 'null'; } const currGap: string = gap + this.indent; - const lineBreakCurrGap: string = - currGap ? '\n' + currGap : ''; + const lineBreakCurrGap: string = currGap ? '\n' + currGap : ''; const lineEndComments: { [index: number]: string } = {}; const partial: string[] = []; const fnPartialToLine: (value: string, i: number) => string = @@ -197,15 +197,27 @@ ${gap} */`; } for (let i: number = 0; i < value.length; i++) { const { comments: parts, childJSON, lineEndComment } = this.getChildJSON(value, i, currGap, node); - parts.push(childJSON || 'null'); + if (lineEndComment !== undefined) { lineEndComments[i] = lineEndComment; } - partial.push(parts.join(lineBreakCurrGap)); + + parts.push(childJSON || 'null'); + const currentItemWithComments: string = parts.join(lineBreakCurrGap); + + if (this.configuration.emptyLineBeforeComments && i > 0 && parts.length > 1 && currGap) { + // Not the first item in array && has comment && spaces != 0 + // Add a empty line + partial.push(lineBreakCurrGap + currentItemWithComments); + } else { + partial.push(currGap + currentItemWithComments); + } } - return currGap ? `[ -${currGap}${partial.map(fnPartialToLine).join(lineBreakCurrGap)} -${gap}]` : `[${partial.join(',')}]`; + return currGap + ? `[ +${partial.map(fnPartialToLine).join('\n')} +${gap}]` + : `[${partial.join(',')}]`; } else { const keys: (string | number)[] = Array.isArray(this.replacer) ? this.replacer : Object.keys(value); if (keys.length === 0) { @@ -214,16 +226,27 @@ ${gap}]` : `[${partial.join(',')}]`; for (const k of keys) { const { comments: parts, childJSON, lineEndComment } = this.getChildJSON(value, k, currGap, node); if (childJSON) { - parts.push(JSON.stringify(k) + (currGap ? ': ' : ':') + childJSON); if (lineEndComment !== undefined) { lineEndComments[partial.length] = lineEndComment; } - partial.push(parts.join(lineBreakCurrGap)); + + parts.push(JSON.stringify(k) + (currGap ? ': ' : ':') + childJSON); + const currentKVPairWithComments: string = parts.join(lineBreakCurrGap); + + if (this.configuration.emptyLineBeforeComments && partial.length > 0 && parts.length > 1 && currGap) { + // Not the first key-value pair in object && has comment && spaces != 0 + // Add a empty line + partial.push(lineBreakCurrGap + currentKVPairWithComments); + } else { + partial.push(currGap + currentKVPairWithComments); + } } } - return currGap ? `{ -${currGap}${partial.map(fnPartialToLine).join(lineBreakCurrGap)} -${gap}}` : `{${partial.join(',')}}`; + return currGap + ? `{ +${partial.map(fnPartialToLine).join('\n')} +${gap}}` + : `{${partial.join(',')}}`; } } else { return JSON.stringify(value); diff --git a/src/test/assets.ts b/src/test/assets.ts index 9f41b90..86d15ae 100644 --- a/src/test/assets.ts +++ b/src/test/assets.ts @@ -41,10 +41,12 @@ export const TEST_SCHEMA_COMMENTED = `/** * @description Description of the schema */ "description": "Cluster Configuration", + /** * @description Type of the schema */ "type": "object", + /** * @type object * @description Available properties of the schema @@ -60,6 +62,7 @@ export const TEST_SCHEMA_COMMENTED = `/** * @description Type of the schema */ "type": "string", + /** * @type string * @description Description of the schema @@ -67,6 +70,7 @@ export const TEST_SCHEMA_COMMENTED = `/** "description": "Cluster name" } }, + /** * @type array * @default [] diff --git a/src/test/blockComment.test.ts b/src/test/blockComment.test.ts index ff75138..52e8f8e 100644 --- a/src/test/blockComment.test.ts +++ b/src/test/blockComment.test.ts @@ -36,10 +36,12 @@ describe('block comments', () => { * test4 */ 1, + /** * test3 */ 2, + /** * test4 */ @@ -59,8 +61,12 @@ describe('block comments', () => { }`); }); - it('should reflect changes in configuration - no style and space', () => { - const w: CustomCommentWriter = new CustomCommentWriter({ spaceAroundCommentSymbol: false, styledBlockComment: false }); + it('should reflect changes in configuration - no empty line, style and space', () => { + const w: CustomCommentWriter = new CustomCommentWriter({ + emptyLineBeforeComments: false, + spaceAroundCommentSymbol: false, + styledBlockComment: false + }); w.addComments([], [{ type: 'block', content: 'test' }]); w.addComments(['test'], [{ type: 'block', content: 'test2' }]); w.addComments(['test', 1], [{ type: 'block', content: 'test3' }]); @@ -94,7 +100,7 @@ describe('block comments', () => { }`); }); - it('should reflect changes in configuration - no style but with space', () => { + it('should reflect changes in configuration - no style but with empty line and space', () => { const w: CustomCommentWriter = new CustomCommentWriter({ styledBlockComment: false }); w.addComments([], [{ type: 'block', content: 'test' }]); w.addComments(['test'], [{ type: 'block', content: 'test2' }]); @@ -114,8 +120,10 @@ describe('block comments', () => { "test": [ /* test4 */ 1, + /* test3 */ 2, + /* test4 */ 3 ] diff --git a/src/test/lineComment.test.ts b/src/test/lineComment.test.ts index 345fc53..b29e059 100644 --- a/src/test/lineComment.test.ts +++ b/src/test/lineComment.test.ts @@ -24,8 +24,10 @@ describe('line comments', () => { "test": [ // test4 1, + // test3 2, + // test4 3 ] @@ -40,7 +42,10 @@ describe('line comments', () => { }); it('should reflect changes in configuration', () => { - const w: CustomCommentWriter = new CustomCommentWriter({ spaceAroundCommentSymbol: false }); + const w: CustomCommentWriter = new CustomCommentWriter({ + emptyLineBeforeComments: false, + spaceAroundCommentSymbol: false + }); w.addComments([], [{ type: 'line', content: 'test' }]); w.addComments(['test'], [{ type: 'line', content: 'test2' }]); w.addComments(['test', 1], [{ type: 'line', content: 'test3' }]); diff --git a/src/types.ts b/src/types.ts index 2b56ea0..1f1ba5f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,6 +25,17 @@ export type IJSONComment = { } | string; export interface IJSONCommentConfiguration { + /** + * @description Add a blank line before every `block` and `line` comment, + * except comments of the followings: + * * The root object + * * The first item in array + * * The first key-value pair in object + * Not supported if `space` when calling `stringify` is 0. + * + * @default true + */ + emptyLineBeforeComments: boolean; /** * @description Add space around '//', '/\*' and '\*\/'. * '/\*' and '\*\/' will not be affected by this