add @jsdoc/core package

This commit is contained in:
Jeff Williams 2019-07-28 15:14:16 -07:00
parent d4321a75b4
commit a291608954
9 changed files with 528 additions and 0 deletions

2
packages/jsdoc-core/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
# Dotfile detritus
.DS_Store

View File

@ -0,0 +1,14 @@
.editorconfig
.eslintignore
.eslintrc.js
.gitignore
.github/
.renovaterc.json
.travis.yml
CHANGES.md
CODE_OF_CONDUCT.md
CONTRIBUTING.md
gulpfile.js
lerna.json
packages/
test/

View File

@ -0,0 +1,3 @@
# `@jsdoc/core`
Core functionality for JSDoc.

View File

@ -0,0 +1,11 @@
/**
* Provides core functionality for JSDoc.
*
* @module @jsdoc/config
*/
const config = require('./lib/config');
module.exports = {
config
};

View File

@ -0,0 +1,134 @@
/**
* Manages configuration settings for JSDoc.
*
* @alias module:@jsdoc/core.config
*/
const _ = require('lodash');
const cosmiconfig = require('cosmiconfig');
const stripBom = require('strip-bom');
const stripJsonComments = require('strip-json-comments');
const MODULE_NAME = 'jsdoc';
const defaults = exports.defaults = {
// 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,
/**
* 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. Some JSDoc templates might ignore these
* settings.
*/
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
}
};
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)
);
};

134
packages/jsdoc-core/package-lock.json generated Normal file
View File

@ -0,0 +1,134 @@
{
"name": "@jsdoc/core",
"version": "0.1.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.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
"integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
"requires": {
"import-fresh": "^2.0.0",
"is-directory": "^0.3.1",
"js-yaml": "^3.13.1",
"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.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"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.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"mock-fs": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.10.1.tgz",
"integrity": "sha512-w22rOL5ZYu6HbUehB5deurghGM0hS/xBVyHMGKOuQctkk93J9z9VEOhDsiWrXOprVNQpP9uzGKdl8v9mFspKuw==",
"dev": true
},
"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": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w=="
},
"strip-json-comments": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
"integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw=="
}
}
}

View File

@ -0,0 +1,30 @@
{
"name": "@jsdoc/core",
"version": "0.1.0",
"description": "Core functionality for JSDoc.",
"keywords": [
"jsdoc"
],
"author": "Jeff Williams <jeffrey.l.williams@gmail.com>",
"homepage": "https://jsdoc.app/",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/jsdoc/jsdoc.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
},
"bugs": {
"url": "https://github.com/jsdoc/jsdoc/issues"
},
"dependencies": {
"cosmiconfig": "^5.2.1",
"lodash": "^4.17.15",
"strip-bom": "^4.0.0",
"strip-json-comments": "^3.0.1"
},
"devDependencies": {
"mock-fs": "^4.10.1"
}
}

View File

@ -0,0 +1,14 @@
const config = require('../../lib/config');
const core = require('../../index');
describe('@jsdoc/core', () => {
it('exists', () => {
expect(core).toBeObject();
});
describe('config', () => {
it('is lib/config', () => {
expect(core.config).toBe(config);
});
});
});

View File

@ -0,0 +1,186 @@
const { config } = require('@jsdoc/core');
const mockFs = require('mock-fs');
describe('@jsdoc/config', () => {
afterEach(() => mockFs.restore());
it('exists', () => {
expect(config).toBeObject();
});
describe('loadSync', () => {
it('exists', () => {
expect(config.loadSync).toBeFunction();
});
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(config.defaults);
});
it('provides the default config if there is no user config', () => {
const conf = config.loadSync();
expect(conf.config).toEqual(config.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(config.defaults.tags.allowUnknownTags);
expect(conf.config.tags.foo).toBe('bar');
});
});
describe('defaults', () => {
const { defaults } = config;
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();
});
});
});
});
});
});