don't crash on UTF-8 JSON files with a leading BOM (#1256, #1297)

This commit is contained in:
Jeff Williams 2017-07-02 16:28:23 -07:00
parent c463524fce
commit 7fd0f590ee
10 changed files with 88 additions and 5 deletions

4
cli.js
View File

@ -1,4 +1,3 @@
/* global java */
/* eslint no-process-exit:0, strict: [2, "function"] */
/**
* Helper methods for running JSDoc on the command line.
@ -16,6 +15,7 @@ module.exports = (function() {
var app = require('jsdoc/app');
var env = require('jsdoc/env');
var logger = require('jsdoc/util/logger');
var stripBom = require('jsdoc/util/stripbom');
var stripJsonComments = require('strip-json-comments');
var Promise = require('bluebird');
@ -38,7 +38,7 @@ cli.setVersionInfo = function() {
var path = require('path');
// allow this to throw--something is really wrong if we can't read our own package file
var info = JSON.parse( fs.readFileSync(path.join(env.dirname, 'package.json'), 'utf8') );
var info = JSON.parse( stripBom.strip(fs.readFileSync(path.join(env.dirname, 'package.json'), 'utf8')) );
env.version = {
number: info.version,

View File

@ -9,6 +9,8 @@
*/
'use strict';
var stripBom = require('jsdoc/util/stripbom');
function mergeRecurse(target, source) {
Object.keys(source).forEach(function(p) {
if ( source[p].constructor === Object ) {
@ -48,7 +50,7 @@ var defaults = {
@param {string} [json] - The contents of config.json.
*/
function Config(json) {
json = JSON.parse( (json || '{}') );
json = JSON.parse( (stripBom.strip(json) || '{}') );
this._config = mergeRecurse(defaults, json);
}

View File

@ -1,6 +1,7 @@
'use strict';
var logger = require('jsdoc/util/logger');
var stripBom = require('jsdoc/util/stripbom');
/**
* Provides access to information about a JavaScript package.
@ -79,7 +80,7 @@ exports.Package = function(json) {
this.kind = 'package';
try {
packageInfo = JSON.parse(json || '{}');
packageInfo = JSON.parse(stripBom.strip(json) || '{}');
}
catch (e) {
logger.error('Unable to parse the package file: %s', e.message);

View File

@ -13,6 +13,7 @@ var env = require('jsdoc/env');
var fs = require('jsdoc/fs');
var logger = require('jsdoc/util/logger');
var path = require('path');
var stripBom = require('jsdoc/util/stripbom');
var tutorial = require('jsdoc/tutorial');
var hasOwnProp = Object.prototype.hasOwnProperty;
@ -138,7 +139,7 @@ exports.load = function(filepath) {
// configuration file
case 'json':
var meta = JSON.parse(content);
var meta = JSON.parse(stripBom.strip(content));
addTutorialConf(name, meta);
// don't add this as a tutorial
return;

View File

@ -0,0 +1,19 @@
'use strict';
/**
* Module to strip the leading BOM, if present, from UTF-8 files.
* @module
* @private
*/
/**
* Strip the leading BOM, if present, from a string.
*
* @private
* @param {string} text - The string to strip.
* @return {string} The stripped string.
*/
exports.strip = function(text) {
text = text || '';
return text.replace(/^\uFEFF/, '');
};

View File

@ -36,6 +36,16 @@ describe('jsdoc/config', function() {
});
});
describe('constructor with leading BOM', function() {
it('should be possible to construct a Config with JSON that has a leading BOM', function() {
function getConfig() {
return new Config('\uFEFF{}').get();
}
expect(getConfig).not.toThrow();
});
});
describe('constructor with plugins value', function() {
it('should be possible to construct a Config with JSON of an object literal that has a plugin value', function() {
var config = new Config('{"plugins":[42]}').get();

View File

@ -45,6 +45,14 @@ describe('jsdoc/package', function() {
expect(newPackage).not.toThrow();
});
it('should accept a JSON-format string with a leading BOM', function() {
function newPackage() {
return new Package('\uFEFF{}');
}
expect(newPackage).not.toThrow();
});
it('should work when called with no arguments', function() {
function newPackage() {
return new Package();

View File

@ -142,6 +142,16 @@ describe('jsdoc/tutorial/resolver', function() {
expect(test6.type).toBe(tutorial.TYPES.HTML);
expect(constr.type).toBe(tutorial.TYPES.MARKDOWN);
});
it('JSON files with a leading BOM are handled correctly', function() {
resetRootTutorial();
function loadBomTutorials() {
resolver.load(env.dirname + '/test/tutorials/bom');
}
expect(loadBomTutorials).not.toThrow();
});
});
// resolve

View File

@ -0,0 +1,31 @@
'use strict';
describe('jsdoc/util/stripbom', function() {
var stripBom = require('jsdoc/util/stripbom');
it('should exist', function() {
expect(typeof stripBom).toBe('object');
});
it('should export a "strip" method', function() {
expect(typeof stripBom.strip).toBe('function');
});
describe('strip', function() {
it('should strip the leading BOM when present', function() {
var result = stripBom.strip('\uFEFFHello there!');
expect(result).toBe('Hello there!');
});
it('should not change the text when no leading BOM is present', function() {
var result = stripBom.strip('Hello there!');
expect(result).toBe('Hello there!');
});
it('should return an empty string when the text is null or undefined', function() {
expect(stripBom.strip()).toBe('');
});
});
});

View File

@ -0,0 +1 @@
{}