Skip to content

Commit

Permalink
Handle JSON with Comment (#39)
Browse files Browse the repository at this point in the history
* chore: install strip-json-comment

* feat: strip json comment

* chore: install valibot

* feat: use valibot parsing

* feat: copy stripJsonComments

---------

Co-authored-by: farreldarian <[email protected]>
  • Loading branch information
fdarian and farreldarian authored Apr 2, 2024
1 parent 505eaf3 commit 965eaad
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 3 deletions.
Binary file modified bun.lockb
Binary file not shown.
3 changes: 2 additions & 1 deletion packages/generator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"@prisma/sdk": "4.0.0",
"fp-ts": "^2.16.2",
"lodash": "^4.17.21",
"pluralize": "^8.0.0"
"pluralize": "^8.0.0",
"valibot": "^0.30.0"
},
"devDependencies": {
"@types/lodash": "^4.14.202",
Expand Down
136 changes: 136 additions & 0 deletions packages/generator/src/lib/strip-json-comments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// @ts-nocheck

/**
* From https://github.com/sindresorhus/strip-json-comments/commit/f9a628340cf4134500352122cf42ab40b9de6734
*/

const singleComment = Symbol('singleComment')
const multiComment = Symbol('multiComment')

const stripWithoutWhitespace = () => ''
const stripWithWhitespace = (string, start, end) =>
string.slice(start, end).replace(/\S/g, ' ')

const isEscaped = (jsonString, quotePosition) => {
let index = quotePosition - 1
let backslashCount = 0

while (jsonString[index] === '\\') {
index -= 1
backslashCount += 1
}

return Boolean(backslashCount % 2)
}

export default function stripJsonComments(
jsonString,
{ whitespace = true, trailingCommas = false } = {}
) {
if (typeof jsonString !== 'string') {
throw new TypeError(
`Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\``
)
}

const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace

let isInsideString = false
let isInsideComment = false
let offset = 0
let buffer = ''
let result = ''
let commaIndex = -1

for (let index = 0; index < jsonString.length; index++) {
const currentCharacter = jsonString[index]
const nextCharacter = jsonString[index + 1]

if (!isInsideComment && currentCharacter === '"') {
// Enter or exit string
const escaped = isEscaped(jsonString, index)
if (!escaped) {
isInsideString = !isInsideString
}
}

if (isInsideString) {
continue
}

if (!isInsideComment && currentCharacter + nextCharacter === '//') {
// Enter single-line comment
buffer += jsonString.slice(offset, index)
offset = index
isInsideComment = singleComment
index++
} else if (
isInsideComment === singleComment &&
currentCharacter + nextCharacter === '\r\n'
) {
// Exit single-line comment via \r\n
index++
isInsideComment = false
buffer += strip(jsonString, offset, index)
offset = index
continue
} else if (isInsideComment === singleComment && currentCharacter === '\n') {
// Exit single-line comment via \n
isInsideComment = false
buffer += strip(jsonString, offset, index)
offset = index
} else if (!isInsideComment && currentCharacter + nextCharacter === '/*') {
// Enter multiline comment
buffer += jsonString.slice(offset, index)
offset = index
isInsideComment = multiComment
index++
continue
} else if (
isInsideComment === multiComment &&
currentCharacter + nextCharacter === '*/'
) {
// Exit multiline comment
index++
isInsideComment = false
buffer += strip(jsonString, offset, index + 1)
offset = index + 1
continue
} else if (trailingCommas && !isInsideComment) {
if (commaIndex !== -1) {
if (currentCharacter === '}' || currentCharacter === ']') {
// Strip trailing comma
buffer += jsonString.slice(offset, index)
result += strip(buffer, 0, 1) + buffer.slice(1)
buffer = ''
offset = index
commaIndex = -1
} else if (
currentCharacter !== ' ' &&
currentCharacter !== '\t' &&
currentCharacter !== '\r' &&
currentCharacter !== '\n'
) {
// Hit non-whitespace following a comma; comma is not trailing
buffer += jsonString.slice(offset, index)
offset = index
commaIndex = -1
}
} else if (currentCharacter === ',') {
// Flush buffer prior to this point, and save new comma index
result += buffer + jsonString.slice(offset, index)
buffer = ''
offset = index
commaIndex = index
}
}
}

return (
result +
buffer +
(isInsideComment
? strip(jsonString.slice(offset))
: jsonString.slice(offset))
)
}
18 changes: 16 additions & 2 deletions packages/generator/src/shared/generator-context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import fs from 'node:fs'
import path from 'node:path'
import type { GeneratorOptions } from '@prisma/generator-helper'
import { object, safeParse, string } from 'valibot'
import { getModuleResolution } from '~/lib/config'
import stripJsonComments from '~/lib/strip-json-comments'

type GeneratorContext = {
moduleResolution?: string
Expand Down Expand Up @@ -30,8 +32,20 @@ function resolveModuleResolution(options: GeneratorOptions) {
const tsConfigPath = findTsConfig()
if (!tsConfigPath) return

const tsConfig = JSON.parse(fs.readFileSync(tsConfigPath, 'utf-8'))
return tsConfig?.compilerOptions?.moduleResolution
const parsing = safeParse(TsConfig, readTsConfig(tsConfigPath))
if (!parsing.success) return

return parsing.output.compilerOptions.moduleResolution
}

const TsConfig = object({
compilerOptions: object({
moduleResolution: string(),
}),
})

function readTsConfig(tsConfigPath: string) {
return JSON.parse(stripJsonComments(fs.readFileSync(tsConfigPath, 'utf-8')))
}

function findTsConfig() {
Expand Down

0 comments on commit 965eaad

Please sign in to comment.