Skip to content

Commit

Permalink
Merge ea0fbec into c340770
Browse files Browse the repository at this point in the history
  • Loading branch information
mbland committed Dec 30, 2023
2 parents c340770 + ea0fbec commit b91d9ff
Show file tree
Hide file tree
Showing 7 changed files with 371 additions and 8 deletions.
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,70 @@ $ pnpm jsdoc
../../../build/jsdoc/tomcat-servlet-testing-example-frontend/0.0.0/index.html
```

## Development

Uses [pnpm][] and [Vitest][] for building and testing.

### JSON with comments

You may want to configure your editor to recognize comments in JSON files, since
this project and [JSDoc][] both support them.

#### [Vim][]

Add to your `~/.vimrc`, based on advice from [Stack Overflow: Why does Vim
highlight all my JSON comments in red?][so-vim]:

```vim
" With a little help from:
" - https://stackoverflow.com/questions/55669954/why-does-vim-highlight-all-my-json-comments-in-red
autocmd FileType json syntax match Comment "//.*"
autocmd FileType json syn region jsonBlockComment start="/\*" end="\*/" fold
autocmd FileType json hi def link jsonBlockComment Comment
```

#### [Visual Studio Code][]

[VS Code supports JSON with Comments][vsc-jsonc]. Following the good advice from
[Stack Overflow: In VS Code, disable error "Comments are not permitted in
JSON"][so-vsc]:

##### Method 1, verbatim from <https://stackoverflow.com/a/47834826>

1. Click on the letters JSON in the bottom right corner. (A drop-down will
appear to "Select the Language Mode.")
2. Select "Configure File Association for '.json'..."
3. Type "jsonc" and press Enter.

##### Method 2, nearly verbatim from <https://stackoverflow.com/a/48773989>

Add this to your User Settings:

```json
"files.associations": {
"*.json": "jsonc"
},
```

If you don't already have a user settings file, you can create one. Hit
**&#8984;, or CTRL-,** (that's a comma) to open your settings, then hit
the Open Settings (JSON) button in the upper right. (It looks like a page with a
little curved arrow over it.)

- Or invoke the **[Preferences: Open User Settings (JSON)][vsc-settings]**
command.

#### [IntelliJ IDEA][]

You can effectively enable comments by [extending the JSON5 syntax to all JSON
files][idea-json5]:

1. In the Settings dialog (**&#8984;,** or **CTRL-,**), go to **Editor | File
Types**.
2. In the **Recognized File Types** list, select **JSON5**.
3. In the **File Name Patterns** area, click **&#65291; (Add)** and type `.json`
in the **Add Wildcard** dialog that opens.

## Background

I developed this while experimenting with JSDoc on
Expand All @@ -150,6 +214,15 @@ Node.js, JSDoc, and [npm packaging][] exercise as well.
[pnpm]: https://pnpm.io/
[mbland/tomcat-servlet-testing-example]: https://github.com/mbland/tomcat-servlet-testing-example
[Gradle]: https://gradle.org/
[Vitest]: https://vitest.dev/
[Vim]: https://www.vim.org/
[so-vim]: https://stackoverflow.com/questions/55669954/why-does-vim-highlight-all-my-json-comments-in-red
[Visual Studio Code]: https://code.visualstudio.com/
[vsc-jsonc]: https://code.visualstudio.com/Docs/languages/json#_json-with-comments
[so-vsc]: https://stackoverflow.com/questions/47834825/in-vs-code-disable-error-comments-are-not-permitted-in-json
[vsc-settings]: https://code.visualstudio.com/docs/getstarted/settings#_settingsjson
[IntelliJ IDEA]: https://www.jetbrains.com/idea/
[idea-json5]: https://www.jetbrains.com/help/idea/json.html#ws_json_choose_version_procedure
[Bash]: https://www.gnu.org/software/bash/
[Node.js]: https://nodejs.org/
[npm packaging]: https://docs.npmjs.com/packages-and-modules
86 changes: 80 additions & 6 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ export async function getPath(cmdName, env, platform) {

/**
* Analyzes JSDoc CLI args to determine if JSDoc will generate docs and where
*
* Expects any JSON config files specified via -c or --configure to be UTF-8
* encoded.
* @param {string[]} argv - JSDoc command line interface arguments
* @returns {Promise<ArgvResults>} analysis results
*/
Expand All @@ -108,22 +111,20 @@ export async function analyzeArgv(argv) {
for (let i = 0; i !== argv.length; ++i) {
const arg = argv[i]
const nextArg = argv[i+1]
let config = null

switch (arg) {
case '-c':
case '--configure':
if (!cmdLineDest && validArg(nextArg)) {
config = JSON.parse(await readFile(nextArg))
if (config.opts !== undefined) {
destination = config.opts.destination
}
const jsonSrc = await readFile(nextArg, {encoding: 'utf8'})
const config = JSON.parse(stripJsonComments(jsonSrc))
if (config.opts !== undefined) destination = config.opts.destination
}
break

case '-d':
case '--destination':
if (nextArg !== undefined && validArg(nextArg)) {
if (validArg(nextArg)) {
destination = nextArg
cmdLineDest = true
}
Expand All @@ -143,6 +144,79 @@ export async function analyzeArgv(argv) {
return {willGenerate, destination}
}

/**
* Replaces all comments and trailing commas in a JSON string with spaces
*
* Replaces rather than removes characters so that any JSON.parse() errors line
* up with the original. Preserves all existing whitespace as is, including
* newlines, carriage returns, and horizontal tabs.
*
* Details to be aware of:
*
* - Replaces trailing commas before the next ']' or '}' with a space.
* - "/* /" (without the space) is a complete block comment. (Since this
* documentation is in a block comment, the space is necessary here.)
* - If the next character after the end of a block comment ("* /" without the
* space) is:
* - '*': reopens the block comment
* - '/': opens a line comment
*
* If you really want to strip all the extra whitespace out:
*
* ```js
* JSON.stringify(JSON.parse(stripJsonComments(jsonStr)))
* ```
*
* If you want to reformat it to your liking, e.g., using two space indents:
*
* ```js
* JSON.stringify(JSON.parse(stripJsonComments(jsonStr)), null, 2)
* ```
*
* This function is necessary because the `jsdoc` command depends upon the
* extremely popular strip-json-comments npm. Otherwise analyzeArgs() would
* choke on config.json files containing comments.
*
* This implementation was inspired by strip-json-comments, but is a completely
* original implementation to avoid adding any dependencies. It may become its
* own separate package one day, likely scoped to avoid conflicts with
* strip-json-comments.
* @param {string} jsonStr - JSON text to strip
* @returns {string} jsonStr with comments, trailing commas replaced by space
*/
export function stripJsonComments(jsonStr) {
let inString = false
let escaped = false
let inComment = null
let result = ''

for (let i = 0; i !== jsonStr.length; ++i) {
const prevChar = i !== 0 ? jsonStr[i-1] : null
let curChar = jsonStr[i]

if (inString) {
inString = curChar !== '"' || escaped
escaped = curChar === '\\' && !escaped
} else if (inComment) {
if ((inComment === 'line' && curChar === '\n') ||
(inComment === 'block' && prevChar === '*' && curChar === '/')) {
inComment = null
}
if (curChar.trimStart() !== '') curChar = ' '
} else if (curChar === '"') {
inString = true
} else if (prevChar === '/') {
if (curChar === '/') inComment = 'line'
else if (curChar === '*') inComment = 'block'
if (inComment) curChar = ' ' // otherwise prevChar closed a block comment
} else if (curChar === '/') {
curChar = ' ' // opening a line or block comment
}
result += curChar
}
return result.replaceAll(/,(\s*)([\]}])/g, ' $1$2')
}

/**
* Searches for filename within a directory tree via breadth-first search
* @param {string} dirname - current directory to search
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/analyzeArgv/conf-bar.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{
"opts": {
"destination": "bar"
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/analyzeArgv/conf-foo.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{
"opts": {
"destination": "foo"
Expand Down
7 changes: 6 additions & 1 deletion test/fixtures/analyzeArgv/conf-undef-dest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{
"opts": {}
"opts": {} // This will override opts.destination, since opts is defined.
}
7 changes: 6 additions & 1 deletion test/fixtures/analyzeArgv/conf-undef-opts.json
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
{}
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{/* This won't override opts.destination, since opts is undefined. */}
Loading

0 comments on commit b91d9ff

Please sign in to comment.