better config loading (#1550)

+ Use `cosmiconfig` instead of rolling our own code (which gives us YAML support)
+ Look for the config in these locations, and in this order:
    + A `jsdoc` property in `package.json`
    + `.jsdocrc` (can be JSON or YAML; comments not allowed for JSON)
    + `.jsdocrc.json` (comments allowed)
    + `.jsdocrc.yaml`
    + `.jsdocrc.yml`
    + `.jsdocrc.js`
    + `jsdoc.config.js`
This commit is contained in:
Jeff Williams 2019-01-25 19:33:18 -08:00
parent cd4dcce9e5
commit 4badaef5fd
14 changed files with 636 additions and 208 deletions

51
cli.js
View File

@ -48,18 +48,8 @@ module.exports = (() => {
cli.loadConfig = () => { cli.loadConfig = () => {
const _ = require('lodash'); const _ = require('lodash');
const args = require('jsdoc/opts/args'); const args = require('jsdoc/opts/args');
const Config = require('jsdoc/config'); let conf;
let config; const config = require('@jsdoc/config');
const fs = require('jsdoc/fs');
const path = require('jsdoc/path');
let confPath;
let isFile;
const defaultOpts = {
destination: './out/',
encoding: 'utf8'
};
try { try {
env.opts = args.parse(env.args); env.opts = args.parse(env.args);
@ -71,37 +61,18 @@ module.exports = (() => {
}); });
} }
confPath = env.opts.configure || path.join(env.dirname, 'conf.json');
try { try {
isFile = fs.statSync(confPath).isFile(); conf = config.loadSync(env.opts.configure);
} env.conf = conf.config;
catch (e) { } catch (e) {
isFile = false; cli.exit(
1,
`Cannot parse the config file ${conf.filepath}: ${e}\n${FATAL_ERROR_MESSAGE}`
);
} }
if ( !isFile && !env.opts.configure ) { // look for options on the command line, then in the config
confPath = path.join(env.dirname, 'conf.json.EXAMPLE'); env.opts = _.defaults(env.opts, env.conf.opts);
}
try {
switch ( path.extname(confPath) ) {
case '.js':
config = require( path.resolve(confPath) ) || {};
break;
case '.json':
case '.EXAMPLE':
default:
config = fs.readFileSync(confPath, 'utf8');
break;
}
env.conf = new Config(config).get();
}
catch (e) {
cli.exit(1, `Cannot parse the config file ${confPath}: ${e}\n${FATAL_ERROR_MESSAGE}`);
}
// look for options on the command line, in the config file, and in the defaults, in that order
env.opts = _.defaults(env.opts, env.conf.opts, defaultOpts);
return cli; return cli;
}; };

View File

@ -1,74 +0,0 @@
/**
* @module jsdoc/config
*/
const stripBom = require('jsdoc/util/stripbom');
const stripJsonComments = require('strip-json-comments');
function mergeRecurse(target, source) {
Object.keys(source).forEach(p => {
if ( source[p].constructor === Object ) {
if ( !target[p] ) {
target[p] = {};
}
mergeRecurse(target[p], source[p]);
}
else {
target[p] = source[p];
}
});
return target;
}
// required config values, override these defaults in your config.json if necessary
const defaults = {
plugins: [],
recurseDepth: 10,
source: {
includePattern: '.+\\.js(doc|x)?$',
excludePattern: ''
},
sourceType: 'module',
tags: {
allowUnknownTags: true,
dictionaries: ['jsdoc', 'closure']
},
templates: {
monospaceLinks: false,
cleverLinks: false
}
};
/**
* Represents a JSDoc application configuration.
*/
class Config {
/**
* @param {(string|object)} [jsonOrObject] - The contents of config.json, or a JavaScript object
* exported from a .js config file.
*/
constructor(jsonOrObject) {
if (typeof jsonOrObject === 'undefined') {
jsonOrObject = {};
}
if (typeof jsonOrObject === 'string') {
jsonOrObject = JSON.parse( (stripJsonComments(stripBom.strip(jsonOrObject)) || '{}') );
}
if (typeof jsonOrObject !== 'object') {
jsonOrObject = {};
}
this._config = mergeRecurse(defaults, jsonOrObject);
}
/**
* Get the merged configuration values.
*/
get() {
return this._config;
}
}
module.exports = Config;

View File

@ -38,7 +38,7 @@ const parserOptions = exports.parserOptions = {
'throwExpressions' 'throwExpressions'
], ],
ranges: true, ranges: true,
sourceType: env.conf.sourceType sourceType: env.conf.source.type
}; };
function parse(source, filename) { function parse(source, filename) {

126
package-lock.json generated
View File

@ -127,6 +127,111 @@
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
} }
}, },
"@jsdoc/config": {
"version": "file:packages/jsdoc-config",
"requires": {
"cosmiconfig": "^5.0.7",
"lodash": "^4.17.11",
"strip-bom": "^3.0.0",
"strip-json-comments": "^2.0.1"
},
"dependencies": {
"argparse": {
"version": "1.0.10",
"bundled": true,
"requires": {
"sprintf-js": "~1.0.2"
}
},
"caller-callsite": {
"version": "2.0.0",
"bundled": true,
"requires": {
"callsites": "^2.0.0"
}
},
"caller-path": {
"version": "2.0.0",
"bundled": true,
"requires": {
"caller-callsite": "^2.0.0"
}
},
"callsites": {
"version": "2.0.0",
"bundled": true
},
"cosmiconfig": {
"version": "5.0.7",
"bundled": true,
"requires": {
"import-fresh": "^2.0.0",
"is-directory": "^0.3.1",
"js-yaml": "^3.9.0",
"parse-json": "^4.0.0"
}
},
"error-ex": {
"version": "1.3.2",
"bundled": true,
"requires": {
"is-arrayish": "^0.2.1"
}
},
"esprima": {
"version": "4.0.1",
"bundled": true
},
"import-fresh": {
"version": "2.0.0",
"bundled": true,
"requires": {
"caller-path": "^2.0.0",
"resolve-from": "^3.0.0"
}
},
"is-arrayish": {
"version": "0.2.1",
"bundled": true
},
"is-directory": {
"version": "0.3.1",
"bundled": true
},
"js-yaml": {
"version": "3.12.1",
"bundled": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"json-parse-better-errors": {
"version": "1.0.2",
"bundled": true
},
"parse-json": {
"version": "4.0.0",
"bundled": true,
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"resolve-from": {
"version": "3.0.0",
"bundled": true
},
"sprintf-js": {
"version": "1.0.3",
"bundled": true
},
"strip-bom": {
"version": "3.0.0",
"bundled": true
}
}
},
"@jsdoc/logger": { "@jsdoc/logger": {
"version": "file:packages/jsdoc-logger" "version": "file:packages/jsdoc-logger"
}, },
@ -1170,6 +1275,12 @@
"integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
"dev": true "dev": true
}, },
"add-matchers": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/add-matchers/-/add-matchers-0.6.2.tgz",
"integrity": "sha512-hVO2wodMei9RF00qe+506MoeJ/NEOdCMEkSJ12+fC3hx/5Z4zmhNiP92nJEF6XhmXokeB0hOtuQrjHCx2vmXrQ==",
"dev": true
},
"agent-base": { "agent-base": {
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
@ -5424,6 +5535,15 @@
"integrity": "sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==", "integrity": "sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==",
"dev": true "dev": true
}, },
"jasmine-expect": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jasmine-expect/-/jasmine-expect-4.0.1.tgz",
"integrity": "sha512-tn+sdVx04MRHpW6ltIkEKANRO29C7AUdmkeupFrwbAawd8ICA/IZT9YsdIljGuxU+wLYoOJsaics6swnw2lO2g==",
"dev": true,
"requires": {
"add-matchers": "0.6.2"
}
},
"js-beautify": { "js-beautify": {
"version": "1.8.9", "version": "1.8.9",
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.8.9.tgz", "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.8.9.tgz",
@ -6401,6 +6521,12 @@
"minimist": "0.0.8" "minimist": "0.0.8"
} }
}, },
"mock-fs": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.7.0.tgz",
"integrity": "sha512-WlQNtUlzMRpvLHf8dqeUmNqfdPjGY29KrJF50Ldb4AcL+vQeR8QH3wQcFMgrhTwb1gHjZn9xggho+84tBskLgA==",
"dev": true
},
"modify-values": { "modify-values": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz",

View File

@ -14,6 +14,7 @@
}, },
"dependencies": { "dependencies": {
"@babel/parser": "~7.2.3", "@babel/parser": "~7.2.3",
"@jsdoc/config": "file:packages/jsdoc-config",
"@jsdoc/logger": "file:packages/jsdoc-logger", "@jsdoc/logger": "file:packages/jsdoc-logger",
"@jsdoc/syntax": "file:packages/jsdoc-syntax", "@jsdoc/syntax": "file:packages/jsdoc-syntax",
"@jsdoc/template-original": "file:packages/jsdoc-template-original", "@jsdoc/template-original": "file:packages/jsdoc-template-original",
@ -28,7 +29,6 @@
"marked": "~0.6.0", "marked": "~0.6.0",
"mkdirp": "~0.5.1", "mkdirp": "~0.5.1",
"requizzle": "~0.2.1", "requizzle": "~0.2.1",
"strip-json-comments": "~2.0.1",
"taffydb": "2.6.2" "taffydb": "2.6.2"
}, },
"devDependencies": { "devDependencies": {
@ -37,8 +37,10 @@
"gulp-eslint": "~5.0.0", "gulp-eslint": "~5.0.0",
"gulp-json-editor": "~2.5.0", "gulp-json-editor": "~2.5.0",
"jasmine": "^3.3.1", "jasmine": "^3.3.1",
"jasmine-expect": "^4.0.1",
"klaw-sync": "^6.0.0", "klaw-sync": "^6.0.0",
"lerna": "^3.10.6", "lerna": "^3.10.6",
"mock-fs": "^4.7.0",
"nyc": "~13.1.0" "nyc": "~13.1.0"
}, },
"greenkeeper": { "greenkeeper": {

View File

@ -0,0 +1,11 @@
# @jsdoc/config
Loads configuration settings for JSDoc.
## Installing the package
Using npm:
```shell
npm install --save @jsdoc/config
```

View File

@ -0,0 +1,78 @@
/**
* The default configuration settings for JSDoc.
*
* @module @jsdoc/config/defaults
*/
module.exports = {
// TODO(hegemonic): integrate CLI options with other options
opts: {
destination: './out',
encoding: 'utf8'
},
/**
* The JSDoc plugins to load.
*/
plugins: [],
// TODO(hegemonic): move to `source` or remove
recurseDepth: 10,
// TODO(hegemonic): switch to glob patterns
/**
* Settings for loading and parsing source files.
*/
source: {
/**
* A regular expression that matches source files to exclude from processing.
*
* To exclude files if any portion of their path begins with an underscore, use the value
* `(^|\\/|\\\\)_`.
*/
excludePattern: '',
/**
* A regular expression that matches source files that JSDoc should process.
*
* By default, all source files with the extensions `.js`, `.jsdoc`, and `.jsx` are
* processed.
*/
includePattern: '.+\\.js(doc|x)?$',
/**
* The type of source file. In general, you should use the value `module`. If none of your
* source files use ECMAScript >=2015 syntax, you can use the value `script`.
*/
type: 'module'
},
/**
* Settings for interpreting JSDoc tags.
*/
tags: {
/**
* Set to `true` to allow tags that JSDoc does not recognize.
*/
allowUnknownTags: true,
// TODO(hegemonic): use module paths, not magic strings
/**
* The JSDoc tag dictionaries to load.
*
* If you specify two or more tag dictionaries, and a tag is defined in multiple
* dictionaries, JSDoc uses the definition from the first dictionary that includes that tag.
*/
dictionaries: [
'jsdoc',
'closure'
]
},
/**
* Settings for generating output with JSDoc templates.
*/
templates: {
/**
* Set to `true` to use a monospaced font for links to other code symbols, but not links to
* websites.
*/
cleverLinks: false,
/**
* Set to `true` to use a monospaced font for all links.
*/
monospaceLinks: false
}
};

View File

@ -0,0 +1,60 @@
/**
* @module @jsdoc/config
*/
const _ = require('lodash');
const cosmiconfig = require('cosmiconfig');
const defaults = require('./defaults');
const stripBom = require('strip-bom');
const stripJsonComments = require('strip-json-comments');
const MODULE_NAME = 'jsdoc';
class Config {
constructor(filepath, config) {
this.config = config;
this.filepath = filepath;
}
}
function loadJson(filepath, content) {
return cosmiconfig.loadJson(filepath, stripBom(stripJsonComments(content)));
}
function loadYaml(filepath, content) {
return cosmiconfig.loadYaml(filepath, stripBom(content));
}
const explorer = cosmiconfig(MODULE_NAME, {
cache: false,
loaders: {
'.json': loadJson,
'.yaml': loadYaml,
'.yml': loadYaml,
noExt: loadYaml
},
searchPlaces: [
'package.json',
`.${MODULE_NAME}rc`,
`.${MODULE_NAME}rc.json`,
`.${MODULE_NAME}rc.yaml`,
`.${MODULE_NAME}rc.yml`,
`.${MODULE_NAME}rc.js`,
`${MODULE_NAME}.config.js`
]
});
exports.loadSync = (filepath) => {
let loaded;
if (filepath) {
loaded = explorer.loadSync(filepath);
} else {
loaded = explorer.searchSync() || {};
}
return new Config(
loaded.filepath,
_.defaultsDeep({}, loaded.config, defaults)
);
};

128
packages/jsdoc-config/package-lock.json generated Normal file
View File

@ -0,0 +1,128 @@
{
"name": "@jsdoc/config",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
}
},
"caller-callsite": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
"integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
"requires": {
"callsites": "^2.0.0"
}
},
"caller-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
"integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
"requires": {
"caller-callsite": "^2.0.0"
}
},
"callsites": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
"integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
},
"cosmiconfig": {
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.7.tgz",
"integrity": "sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA==",
"requires": {
"import-fresh": "^2.0.0",
"is-directory": "^0.3.1",
"js-yaml": "^3.9.0",
"parse-json": "^4.0.0"
}
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"requires": {
"is-arrayish": "^0.2.1"
}
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"import-fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
"integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
"requires": {
"caller-path": "^2.0.0",
"resolve-from": "^3.0.0"
}
},
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
},
"is-directory": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
},
"js-yaml": {
"version": "3.12.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
"integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"resolve-from": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
}
}
}

View File

@ -0,0 +1,29 @@
{
"name": "@jsdoc/config",
"version": "1.0.0",
"description": "Loads configuration settings for JSDoc.",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/jsdoc3/jsdoc"
},
"keywords": [
"jsdoc"
],
"author": {
"name": "Jeff Williams",
"email": "jeffrey.l.williams@gmail.com"
},
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/jsdoc3/jsdoc/issues"
},
"homepage": "https://github.com/jsdoc3/jsdoc#readme",
"dependencies": {
"cosmiconfig": "^5.0.7",
"lodash": "^4.17.11",
"strip-bom": "^3.0.0",
"strip-json-comments": "^2.0.1"
},
"devDependencies": {}
}

View File

@ -0,0 +1,81 @@
const defaults = require('../../defaults');
describe('@jsdoc/config/defaults', () => {
it('exists', () => {
expect(defaults).toBeObject();
});
describe('plugins', () => {
it('is an array', () => {
expect(defaults.plugins).toBeArray();
});
});
describe('source', () => {
it('is an object', () => {
expect(defaults.source).toBeObject();
});
describe('excludePattern', () => {
it('is a string', () => {
expect(defaults.source.excludePattern).toBeString();
});
it('represents a valid regexp', () => {
expect(() => new RegExp(defaults.source.excludePattern)).not.toThrow();
});
});
describe('includePattern', () => {
it('is a string', () => {
expect(defaults.source.includePattern).toBeString();
});
it('represents a valid regexp', () => {
expect(() => new RegExp(defaults.source.includePattern)).not.toThrow();
});
});
describe('type', () => {
it('is a string', () => {
expect(defaults.source.type).toBeString();
});
});
describe('tags', () => {
it('is an object', () => {
expect(defaults.tags).toBeObject();
});
describe('allowUnknownTags', () => {
it('is a boolean', () => {
expect(defaults.tags.allowUnknownTags).toBeBoolean();
});
});
describe('dictionaries', () => {
it('is an array of strings', () => {
expect(defaults.tags.dictionaries).toBeArrayOfStrings();
});
});
});
describe('templates', () => {
it('is an object', () => {
expect(defaults.templates).toBeObject();
});
describe('cleverLinks', () => {
it('is a boolean', () => {
expect(defaults.templates.cleverLinks).toBeBoolean();
});
});
describe('monospaceLinks', () => {
it('is a boolean', () => {
expect(defaults.templates.monospaceLinks).toBeBoolean();
});
});
});
});
});

View File

@ -0,0 +1,105 @@
describe('@jsdoc/config', () => {
const mockFs = require('mock-fs');
const config = require('../../index');
const defaults = require('../../defaults');
afterEach(() => mockFs.restore());
it('exists', () => {
expect(config).toBeObject();
});
it('has a loadSync method', () => {
expect(config.loadSync).toBeFunction();
});
describe('loadSync', () => {
it('returns an object with `config` and `filepath` properties', () => {
mockFs({
'conf.json': '{}'
});
const conf = config.loadSync('conf.json');
expect(conf.config).toBeObject();
expect(conf.filepath).toEndWith('conf.json');
});
it('loads settings from the specified filepath if there is one', () => {
mockFs({
'conf.json': '{"foo":"bar"}'
});
const conf = config.loadSync('conf.json');
expect(conf.config.foo).toBe('bar');
});
it('finds the config file when no filepath is specified', () => {
mockFs({
'package.json': '{"jsdoc":{"foo":"bar"}}'
});
const conf = config.loadSync();
expect(conf.config.foo).toBe('bar');
});
it('parses JSON config files that have an extension and contain comments', () => {
mockFs({
'.jsdocrc.json': '// comment\n{"foo":"bar"}'
});
const conf = config.loadSync();
expect(conf.config.foo).toBe('bar');
});
it('parses JSON files that start with a BOM', () => {
mockFs({
'.jsdocrc.json': '\uFEFF{"foo":"bar"}'
});
const conf = config.loadSync();
expect(conf.config.foo).toBe('bar');
});
it('parses YAML files that start with a BOM', () => {
mockFs({
'.jsdocrc.yaml': '\uFEFF{"foo":"bar"}'
});
const conf = config.loadSync();
expect(conf.config.foo).toBe('bar');
});
it('provides the default config if the user config is an empty object', () => {
mockFs({
'.jsdocrc.json': '{}'
});
const conf = config.loadSync();
expect(conf.config).toEqual(defaults);
});
it('provides the default config if there is no user config', () => {
const conf = config.loadSync();
expect(conf.config).toEqual(defaults);
});
it('merges nested defaults with nested user settings as expected', () => {
mockFs({
'.jsdocrc.json': '{"tags":{"foo":"bar"}}'
});
const conf = config.loadSync();
expect(conf.config.tags.allowUnknownTags).toBe(defaults.tags.allowUnknownTags);
expect(conf.config.tags.foo).toBe('bar');
});
});
});

View File

@ -23,10 +23,13 @@ const SPEC_FILES = (() => {
module.exports = () => { module.exports = () => {
const jasmine = new Jasmine(); const jasmine = new Jasmine();
const matcher = env.opts.matcher; const matcher = env.opts.matcher;
/* eslint-disable no-empty-function */
const promise = new Promise(() => {}); const promise = new Promise(() => {});
/* eslint-enable no-empty-function */
jasmine.loadConfig({ jasmine.loadConfig({
helpers: [ helpers: [
'node_modules/jasmine-expect/index.js',
'test/helpers/**/*.js' 'test/helpers/**/*.js'
], ],
random: false random: false

View File

@ -1,92 +0,0 @@
describe('jsdoc/config', () => {
const Config = require('jsdoc/config');
it('should exist', () => {
expect(Config).toBeDefined();
expect(typeof Config).toBe('function');
});
it('should provide a "get" instance function', () => {
const conf = new Config();
expect(conf.get).toBeDefined();
expect(typeof conf.get).toBe('function');
});
describe('constructor with empty', () => {
it('should be possible to construct a Config with an empty arguments', () => {
const conf = new Config().get();
expect(Array.isArray(conf.plugins)).toBe(true);
expect(conf.plugins.length).toBe(0);
});
});
describe('constructor with {}', () => {
it('should be possible to construct a Config with JSON of an object literal that is empty', () => {
const conf = new Config('{}').get();
expect(Array.isArray(conf.plugins)).toBe(true);
expect(conf.plugins.length).toBe(0);
});
it('should be possible to construct a Config with an empty JavaScript object', () => {
const conf = new Config({}).get();
expect(Array.isArray(conf.plugins)).toBe(true);
expect(conf.plugins.length).toBe(0);
});
});
describe('constructor with leading BOM', () => {
it('should be possible to construct a Config with JSON that has a leading BOM', () => {
function getConfig() {
return new Config('\uFEFF{}').get();
}
expect(getConfig).not.toThrow();
});
});
describe('constructor with comments', () => {
it('should be possible to construct a Config with JSON that includes comments', () => {
function getConfig() {
return new Config('{\n// comment\n}').get();
}
expect(getConfig).not.toThrow();
});
});
describe('constructor with plugins value', () => {
it('should be possible to construct a Config with JSON of an object literal that has a plugin value', () => {
const conf = new Config('{"plugins":[42]}').get();
expect(Array.isArray(conf.plugins)).toBe(true);
expect(conf.plugins.length).toBe(1);
expect(conf.plugins[0]).toBe(42);
});
it('should be possible to construct a Config with a JavaScript object that has a plugin value', () => {
const conf = new Config({'plugins': [42]}).get();
expect(Array.isArray(conf.plugins)).toBe(true);
expect(conf.plugins.length).toBe(1);
expect(conf.plugins[0]).toBe(42);
});
});
describe('constructor with source value', () => {
it('should be possible to construct a Config with JSON of an object literal that has a source value', () => {
const conf = new Config('{"source":{"includePattern":"hello"}}').get();
expect(conf.source.includePattern).toBe('hello');
});
it('should be possible to construct a Config with a JavaScript object that has a source value', () => {
const conf = new Config({source: {includePattern: 'hello'}}).get();
expect(conf.source.includePattern).toBe('hello');
});
});
});