@knodes/typedoc-pluginutils
Version:
A set of utilities for TypeDoc plugins
180 lines • 7.17 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OptionGroup = void 0;
const assert_1 = __importDefault(require("assert"));
const fastest_levenshtein_1 = require("fastest-levenshtein");
const lodash_1 = require("lodash");
const typedoc_1 = require("typedoc");
const path_1 = require("@knodes/typedoc-pluginutils/path");
const option_1 = require("./option");
const events_extra_1 = require("../events-extra");
class OptionGroup {
/**
* Generate a type-helper factory to constraint the option to be of the given {@link T2 type}.
*
* TODO: change signature once https://github.com/microsoft/TypeScript/pull/26349 is merged.
*
* @param plugin - The plugin declaring the option.
* @returns a builder to use in order to generate the full option group.
*/
static factory(plugin) {
return this._build(plugin, {}, {});
}
/**
* Create the actual option builder.
*
* @param plugin - The plugin declaring the option.
* @param decs - The declarations so far.
* @param mappers - The mappers so far.
* @returns the builder to chain.
*/
static _build(plugin, decs, mappers) {
return {
add: (name, dec, ...[mapper]) => OptionGroup._build(plugin, Object.assign(Object.assign({}, decs), { [name]: Object.assign(Object.assign({}, dec), { name }) }), Object.assign(Object.assign({}, mappers), { [name]: mapper !== null && mapper !== void 0 ? mapper : lodash_1.identity })),
build: () => new OptionGroup(plugin, decs, mappers),
};
}
get _rootOption() {
const linkAppendix = 'documentation' in this.plugin.package ?
` See \u001b[96m${this.plugin.package.documentation}\u001b[0m for more informations.` : // Cyan
'';
return {
name: this.plugin.optionsPrefix,
type: typedoc_1.ParameterType.Mixed,
help: `[${this.plugin.package.name}]: Set all plugin options below as an object, a JSON string or from a file.${linkAppendix}`,
};
}
constructor(plugin, optionDeclarations, mappers) {
this.plugin = plugin;
this._options = Object.fromEntries(Object.entries(optionDeclarations)
.map(([k, v]) => {
(0, assert_1.default)(k !== 'options');
const fullDec = Object.assign(Object.assign({}, v), { name: k });
const opt = new option_1.Option(plugin, this, fullDec, mappers[k]);
return [k, opt];
}));
this.plugin.application.options.addDeclaration(this._rootOption);
events_extra_1.EventsExtra.for(this.plugin.application)
.beforeOptionsFreeze(this._onBeforeOptionsFreeze.bind(this));
}
/**
* Get the mapped values.
*
* @returns the group values.
*/
getValue() {
return this._mapOptions((k, o) => o.getValue());
}
setValue(...args) {
if (args.length === 2) {
const [key, value] = args;
return this._setValue({ [key]: value });
}
try {
this._setValue(args[0]);
}
catch (e) {
if (e.code !== 'MODULE_NOT_FOUND') {
throw e;
}
this.plugin.logger.error(`Config file ${args[0]} not found`);
}
}
/**
* Set the raw values.
*
* @param value - The value to set. Paths, JSON & partial options are authorized.
* @returns nothing.
*/
_setValue(value) {
if (typeof value === 'object') {
return this._setValueFromObject(value);
}
else if (value.startsWith('{') && value.endsWith('}')) {
const parsedValue = JSON.parse(value);
this._setValue(parsedValue);
}
else {
this._setValueFromFile(value);
}
}
/**
* Set the raw values from a POJO.
*
* @param value - The values to set as object.
*/
_setValueFromObject(value) {
const valKeys = Object.keys(value);
const optKeys = Object.keys(this._options);
for (const unknownOption of (0, lodash_1.difference)(valKeys, optKeys)) {
this.plugin.logger.warn(`Unknown option "${unknownOption}". Did you mean "${(0, fastest_levenshtein_1.closest)(unknownOption, optKeys)}" ?`);
}
const newOpts = this._mapOptions((k, o) => {
var _a;
if (k in value) {
try {
o.setValue(value[k]);
}
catch (err) {
throw new Error(`Could not set option "${o.fullName}": ${(_a = err.message) !== null && _a !== void 0 ? _a : err}`, { cause: err });
}
}
return o.getValue();
});
this.plugin.application.options.setValue(this.plugin.optionsPrefix, newOpts);
}
/**
* Load the given file as being the full plugin options.
*
* @param filename - The file containing options. Any `require`able file can be provided.
*/
_setValueFromFile(filename) {
const [filePath, objPath, ...left] = filename.split('#');
(0, assert_1.default)(left.length === 0);
this.plugin.logger.verbose(`Reading config file @ ${filePath}`);
const optsDirFile = this.plugin.application.options.getValue('options');
const resolved = require.resolve(filePath, { paths: [process.cwd(), optsDirFile, (0, path_1.dirname)(optsDirFile)] });
// eslint-disable-next-line @typescript-eslint/no-var-requires -- Rely in node require
const result = require(resolved);
if (objPath) {
this._setValue((0, lodash_1.get)(result, objPath));
}
else {
this._setValue(result);
}
}
/**
* Try loading different options sources, and update plugin options with default values if not set.
*/
_onBeforeOptionsFreeze() {
const defaultOpts = this.getValue();
// Try read default files
const generalOpts = this.plugin.application.options.getValue(this.plugin.optionsPrefix);
if (generalOpts) {
this._setValue(generalOpts);
}
else {
try {
this._setValueFromFile(`./typedoc-${(0, lodash_1.kebabCase)(this.plugin.optionsPrefix)}`);
// eslint-disable-next-line no-empty -- No-op error
}
catch (_err) { }
}
this.setValue((0, lodash_1.defaultsDeep)(this.getValue(), defaultOpts));
}
/**
* Execute a {@link cb callback} on each declared options, & return an object containing the resulting values.
*
* @param cb - The function to execute on each option. Called with the key & the {@link Option}.
* @returns the mapped values.
*/
_mapOptions(cb) {
return Object.fromEntries(Object.entries(this._options)
.map(([k, v]) => [k, cb(k, v)]));
}
}
exports.OptionGroup = OptionGroup;
//# sourceMappingURL=option-group.js.map