Skip to content

Commit 0d70197

Browse files
marioneblLinusU
authored andcommitted
fix: throw better error when failing to parse json (#304)
* test: be more specific about expected exception * test: cover error cases for JSON.parse in configLoader * fix: throw better error when failing to parse json Fixes #266
1 parent a666ce8 commit 0d70197

File tree

7 files changed

+89
-45
lines changed

7 files changed

+89
-45
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
"inquirer": "1.1.2",
7878
"lodash": "4.14.1",
7979
"minimist": "1.2.0",
80+
"path-exists": "2.1.0",
8081
"shelljs": "0.5.3",
8182
"strip-json-comments": "2.0.1"
8283
},

src/configLoader/getContent.js

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,63 @@
11
import fs from 'fs';
22
import path from 'path';
3+
import {sync as existsSync} from 'path-exists';
34
import stripJSONComments from 'strip-json-comments';
45

56
import {getNormalizedConfig} from '../configLoader';
67

7-
export default getContent;
8+
export default getConfigContent;
9+
10+
/**
11+
* Read the content of a configuration file
12+
* - if not js or json: strip any comments
13+
* - if js or json: require it
14+
* @param {String} configPath - full path to configuration file
15+
* @return {Object}
16+
*/
17+
function readConfigContent(configPath) {
18+
const parsedPath = path.parse(configPath)
19+
const isRcFile = parsedPath.ext !== '.js' && parsedPath.ext !== '.json';
20+
const jsonString = fs.readFileSync(configPath, 'utf-8');
21+
const parse = isRcFile ?
22+
(contents) => JSON.parse(stripJSONComments(contents)) :
23+
(contents) => JSON.parse(contents);
24+
25+
try {
26+
const parsed = parse(jsonString);
27+
28+
Object.defineProperty(parsed, 'configPath', {
29+
value: configPath
30+
});
31+
32+
return parsed;
33+
} catch (error) {
34+
error.message = [
35+
`Parsing JSON at ${configPath} for commitizen config failed:`,
36+
error.mesasge
37+
].join('\n');
38+
39+
throw error;
40+
}
41+
}
842

943
/**
1044
* Get content of the configuration file
11-
* @param {String} config - partial path to configuration file
45+
* @param {String} configPath - partial path to configuration file
1246
* @param {String} directory - directory path which will be joined with config argument
1347
* @return {Object}
1448
*/
15-
function getContent(config, directory) {
16-
if (!config) {
17-
return;
49+
function getConfigContent(configPath, baseDirectory) {
50+
if (!configPath) {
51+
return;
1852
}
1953

20-
var configPath = path.resolve(directory, config);
21-
var ext;
22-
var content;
23-
24-
config = path.basename(config);
25-
26-
if (fs.existsSync(configPath)) {
27-
ext = path.extname(configPath);
28-
29-
if (ext === '.js' || ext === '.json') {
30-
content = JSON.parse(fs.readFileSync(configPath, 'utf8'));
31-
} else {
32-
content = JSON.parse(
33-
stripJSONComments(
34-
fs.readFileSync(configPath, 'utf8')
35-
)
36-
);
37-
}
38-
39-
// Adding property via Object.defineProperty makes it
40-
// non-enumerable and avoids warning for unsupported rules
41-
Object.defineProperty(content, 'configPath', {
42-
value: configPath
43-
});
54+
const resolvedPath = path.resolve(baseDirectory, configPath);
55+
const configBasename = path.basename(resolvedPath);
56+
57+
if (!existsSync(resolvedPath)) {
58+
return getNormalizedConfig(resolvedPath);
4459
}
45-
return getNormalizedConfig(config, content);
46-
47-
};
60+
61+
const content = readConfigContent(resolvedPath);
62+
return getNormalizedConfig(configBasename, content);
63+
};

test/fixtures/invalid-json-rc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"some": "json"
3+
"that": "is invalid"
4+
}

test/fixtures/invalid-json.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"some": "json"
3+
"that": "is invalid"
4+
}

test/fixtures/valid-json-rc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"some": "json"
3+
// with comments
4+
}

test/tests/configLoader.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
1+
import path from 'path';
12
import {expect} from 'chai';
2-
import {getNormalizedConfig} from '../../src/configLoader';
3+
import {getContent, getNormalizedConfig} from '../../src/configLoader';
4+
5+
const fixturesPath = path.resolve(__dirname, '..', 'fixtures');
36

47
describe('configLoader', function() {
58

9+
it('errors appropriately for invalid json', function() {
10+
expect(() => getContent('invalid-json.json', fixturesPath))
11+
.to.throw(/parsing json at/i);
12+
expect(() => getContent('invalid-json-rc', fixturesPath))
13+
.to.throw(/parsing json at/i);
14+
});
15+
16+
it('parses json files with comments', function() {
17+
expect(getContent('valid-json-rc', fixturesPath))
18+
.to.deep.equal({'some': 'json'});
19+
});
20+
621
it('normalizes package.json configs', function() {
722

823
let config = 'package.json';
@@ -46,4 +61,4 @@ describe('configLoader', function() {
4661

4762
});
4863

49-
});
64+
});

test/tests/init.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describe('init', function() {
3535
// Install an adapter
3636
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog');
3737

38-
// TEST
38+
// TEST
3939

4040
// Check resulting json
4141
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
@@ -70,12 +70,12 @@ describe('init', function() {
7070
sh.cd(config.paths.endUserRepo);
7171
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { saveDev:true });
7272

73-
// TEST
74-
sh.cd(config.paths.endUserRepo);
73+
// TEST
74+
sh.cd(config.paths.endUserRepo);
7575
// Adding a second adapter
7676
expect(function() {
7777
commitizenInit(sh, config.paths.endUserRepo, 'cz-jira-smart-commit', { saveDev:true });
78-
}).to.throw();
78+
}).to.throw(/already configured/);
7979

8080
// Check resulting json
8181
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
@@ -92,7 +92,7 @@ describe('init', function() {
9292
// SETUP
9393

9494
// Add a first adapter
95-
sh.cd(config.paths.endUserRepo);
95+
sh.cd(config.paths.endUserRepo);
9696
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { saveDev:true });
9797

9898
// TEST
@@ -115,7 +115,7 @@ describe('init', function() {
115115
// SETUP
116116

117117
// Add a first adapter
118-
sh.cd(config.paths.endUserRepo);
118+
sh.cd(config.paths.endUserRepo);
119119
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog');
120120
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
121121

@@ -129,7 +129,7 @@ describe('init', function() {
129129
// // But you CAN NOT increment a range
130130
// expect(semver.inc(range, 'major')).to.equal(null);
131131
// TODO: We need to figure out how to check if the repo has save exact set
132-
// in the config before we can re-enable this. The --save-exact setting
132+
// in the config before we can re-enable this. The --save-exact setting
133133
// in our package.json breaks this test
134134

135135
});
@@ -141,7 +141,7 @@ describe('init', function() {
141141
// SETUP
142142

143143
// Add a first adapter
144-
sh.cd(config.paths.endUserRepo);
144+
sh.cd(config.paths.endUserRepo);
145145
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', {saveExact: true});
146146
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
147147

@@ -161,10 +161,10 @@ describe('init', function() {
161161
afterEach(function() {
162162
// All this should do is archive the tmp path to the artifacts
163163
clean.afterEach(sh, config.paths.tmp, config.preserve);
164-
});
164+
});
165165

166166
after(function() {
167-
// Once everything is done, the artifacts should be cleaned up based on
167+
// Once everything is done, the artifacts should be cleaned up based on
168168
// the preserve setting in the config
169169
clean.after(sh, config.paths.tmp, config.preserve);
170-
});
170+
});

0 commit comments

Comments
 (0)