diff --git a/package-lock.json b/package-lock.json index d296ee47..dd0a7578 100644 --- a/package-lock.json +++ b/package-lock.json @@ -284,6 +284,12 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==" }, + "@types/json5": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.30.tgz", + "integrity": "sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -2192,6 +2198,14 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "requires": { + "minimist": "^1.2.5" + } + }, "jsonfile": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", diff --git a/package.json b/package.json index 5396121b..702160fb 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "eslint-plugin-prettier": "^3.1.4", "execa": "^4.0.3", "inquirer": "^7.3.3", + "json5": "^2.1.3", "meow": "^7.1.1", "ncp": "^2.0.0", "prettier": "^2.1.2", @@ -65,6 +66,7 @@ "@types/eslint": "^7.2.3", "@types/fs-extra": "^9.0.1", "@types/inquirer": "^7.3.1", + "@types/json5": "0.0.30", "@types/mocha": "^8.0.3", "@types/ncp": "^2.0.4", "@types/node": "^14.11.2", diff --git a/src/util.ts b/src/util.ts index b86a6232..a377c9ff 100644 --- a/src/util.ts +++ b/src/util.ts @@ -20,6 +20,7 @@ import * as rimraf from 'rimraf'; import {promisify} from 'util'; import * as ncp from 'ncp'; import * as writeFileAtomic from 'write-file-atomic'; +import * as JSON5 from 'json5'; export const readFilep = promisify(fs.readFile); export const rimrafp = promisify(rimraf); @@ -38,7 +39,7 @@ export interface DefaultPackage extends Bag { export async function readJsonp(jsonPath: string) { const contents = await readFilep(jsonPath, {encoding: 'utf8'}); - return JSON.parse(contents); + return JSON5.parse(contents); } export interface ReadFileP { @@ -76,7 +77,14 @@ async function getBase( readFiles.add(filePath); try { const json = await customReadFilep(filePath, 'utf8'); - let contents = JSON.parse(json); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let contents: any; + try { + contents = JSON5.parse(json); + } catch (e) { + e.message = `Unable to parse ${filePath}!\n${e.message}`; + throw e; + } if (contents.extends) { const nextFile = await getBase( @@ -85,7 +93,6 @@ async function getBase( readFiles, path.dirname(filePath) ); - // eslint-disable-next-line @typescript-eslint/no-use-before-define contents = combineTSConfig(nextFile, contents); } diff --git a/test/test-clean.ts b/test/test-clean.ts index 2bdf2f88..276c5eee 100644 --- a/test/test-clean.ts +++ b/test/test-clean.ts @@ -49,6 +49,24 @@ describe('clean', () => { }); }); + it('should gracefully handle JSON with comments', () => { + const invalidJson = ` + { + // hah, comments in JSON, what a world + compilerOptions: {outDir: '.'} + }`; + return withFixtures({'tsconfig.json': invalidJson}, async () => { + await clean(OPTIONS); + }); + }); + + it('should gracefully error if tsconfig has invalid JSON', () => { + const invalidJson = "silly bear, this isn't JSON!"; + return withFixtures({'tsconfig.json': invalidJson}, async () => { + assert.rejects(clean(OPTIONS), /Unable to parse/); + }); + }); + it('should avoid deleting .', () => { return withFixtures( {'tsconfig.json': JSON.stringify({compilerOptions: {outDir: '.'}})},