index.js

  1. "use strict";
  2. // TODO : ESM remove the following lines...
  3. const { exec } = require('child_process');
  4. const Path = require('path');
  5. const Fs = require('fs').promises;
  6. const Os = require('os');
  7. const tmpdir = Os.tmpdir();
  8. module.exports = publicize;
  9. // TODO : ESM uncomment the following lines...
  10. // import { exec } from 'child_process';
  11. // import Path from 'path';
  12. // import { promises } as Fs from 'fs';
  13. // import Os from 'os';
  14. // const tmpdir = Os.tmpdir();
  15. /**
  16. * Generates the JSDoc style results using any number of [template providers](https://github.com/jsdoc3/jsdoc#templates-and-tools). See [`README`](index.html)
  17. * for more details.
  18. * #### See [Getting Started]{@tutorial 1-start} documentation for default values that are ported over from `jsdoc-defaults.conf`
  19. * @async
  20. * @param {(String|Conf)} conf Either the path to the JSDoc configuration file or the actual JSDoc configuration itself.
  21. * @param {Boolean} [deploy=false] `true` to deploy via `git` after generating documentation
  22. * @param {Integer} [timeout=30000] The number of milliseconds before timing out for each underlying execution
  23. * @returns {Boolean} `true` when completed successfully
  24. */
  25. async function publicize(conf, deploy = false, timeout = 30000) {
  26. // TODO : ESM use... export async function publicize(conf, deploy) {
  27. const modulePath = Path.normalize(process.env.INIT_CWD || process.env.PWD); // npm run dir or proccess dir
  28. const pkgPath = Path.resolve(modulePath, 'package.json'), pkg = JSON.parse((await Fs.readFile(pkgPath)).toString());
  29. const jsdocpPath = pkg.name === 'jsdocp' ? modulePath : Path.resolve(modulePath, 'node_modules/jsdocp');
  30. const jsdocpConfPath = Path.resolve(jsdocpPath, 'jsdoc/jsdoc-defaults.json');
  31. const jsdocPath = Path.resolve(modulePath, 'node_modules/jsdoc');
  32. const jsdocCliPath = Path.resolve(modulePath, 'node_modules/.bin/jsdoc');
  33. /** @type {Conf} */
  34. let moduleConf;
  35. let jsdocpTmpdir = await Fs.mkdtemp(`${tmpdir}${Path.sep}`), tempConfPath = Path.resolve(jsdocpTmpdir, 'jsdoc.json');
  36. // publishing runs in a separate process and requires a path
  37. if (typeof conf === 'object') {
  38. moduleConf = conf;
  39. } else {
  40. const confPath = Path.resolve(modulePath, conf);
  41. moduleConf = JSON.parse((await Fs.readFile(confPath)).toString());
  42. }
  43. const meta = await writeConf(pkg, moduleConf, modulePath, jsdocpPath, jsdocpConfPath, tempConfPath, jsdocpTmpdir);
  44. const execOpts = {
  45. env: {
  46. JSDOC_PATH: jsdocPath,
  47. JSDOCP_MODULE_PATH: modulePath,
  48. JSDOCP_TMPDIR: jsdocpTmpdir,
  49. JSDOCP_CONF_PATH: tempConfPath,
  50. JSDOCP_PATH: jsdocpPath,
  51. JSDOCP_LAYOUT_PATH: meta.layout.path,
  52. JSDOCP_PUBLISH_VERSIONS: meta.publish.versions,
  53. JSDOCP_PUBLISH_LAST_VER_PUB: meta.publish.lastVersionPublished,
  54. JSDOCP_PUBLISH_LAST_VER: meta.publish.lastVersion,
  55. JSDOCP_PUBLISH_MODULE_URL: meta.publish.moduleURL,
  56. JSDOCP_PUBLISH_DATE: meta.publish.date
  57. },
  58. cwd: modulePath,
  59. timeout
  60. };
  61. if (process.env.Path) execOpts.env.Path = process.env.Path;
  62. if (process.env.PATH) execOpts.env.PATH = process.env.PATH;
  63. return new Promise((resolve, reject) => {
  64. try {
  65. const jsdocExec = `${jsdocCliPath} -c "${tempConfPath}" --verbose`;
  66. const jsdoc = exec(jsdocExec, execOpts);
  67. jsdoc.stdout.pipe(process.stdout);
  68. jsdoc.stderr.pipe(process.stderr);
  69. jsdoc.on('error', error => reject(error));
  70. jsdoc.on('exit', (code, signal) => {
  71. const conf = moduleConf;
  72. if (code !== 0) return reject(new Error(`jsdoc exited with code: ${code}${signal ? ` signal: ${signal}` : ''}`));
  73. if (deploy && !conf.opts.jsdocp.deploy) return reject(new Error(`Deployment flagged for execution, but no "opts.deploy" settings are defined`));
  74. if (deploy) deployer(resolve, reject, conf, pkg, modulePath, jsdocpPath, timeout);
  75. else resolve(true);
  76. });
  77. } catch (err) {
  78. err.message += ` (Failed to execute jsdoc)`;
  79. err.conf = conf;
  80. reject(err);
  81. }
  82. });
  83. }
  84. /**
  85. * Sanitizes JSDoc configuration options in order for publishing to take place
  86. * @private
  87. * @async
  88. * @param {Object} pkg The parsed `package.json`
  89. * @param {Conf} conf The JSDoc configuration
  90. * @param {String} modulePath The path to the module that the configuration is for
  91. * @param {String} jsdocpPath The path to the `jsdocp` module
  92. * @param {String} jsdocpConfPath The path to the `jsdocp` JSON file that contains the default configuration options
  93. * @param {String} tempConfPath The path where the sanitized JSDoc configuration options will be written
  94. * @param {String} jsdocpTmpdir The temporary working directory
  95. * @returns {Object} The _meta_ object that contains the `package` and `publish` objects defined in {@link publicize};
  96. */
  97. async function writeConf(pkg, conf, modulePath, jsdocpPath, jsdocpConfPath, tempConfPath, jsdocpTmpdir) {
  98. if (!conf.opts || !conf.opts.template) {
  99. const error = new Error('JSDoc configuration options must contain an "opts.template" property set to a path '
  100. + 'to a JSDoc template that will be used. For example, if "minami" is the template it should be a package '
  101. + '"devDependencies" and opts.template = "./node_modules/minami". See JSDoc configuration for more details.');
  102. error.conf = conf;
  103. throw error;
  104. }
  105. /** @type {Conf} */
  106. const jpConf = JSON.parse((await Fs.readFile(jsdocpConfPath)).toString());
  107. // template needs to be set to the internal template so it can be proxied
  108. conf.opts.jsdocp.templateProxy = Path.resolve(modulePath, conf.opts.template);
  109. conf.opts.template = Path.resolve(modulePath, sanitizePath(pkg, jpConf.opts.template));
  110. // make sure default plugins are included
  111. const intersectionPluginPath = Path.resolve(jsdocpPath, 'plugins', 'intersection.js');
  112. conf.plugins = conf.plugins || [];
  113. if (!conf.plugins) conf.plugins = jpConf.plugins;
  114. else if (jpConf.plugins) conf.plugins = [...new Set(jpConf.plugins.concat(intersectionPluginPath, ...conf.plugins))];
  115. // need the following
  116. conf.templates = conf.templates || {};
  117. conf.templates.default = conf.templates.default || {};
  118. conf.templates.default.layoutFile = conf.templates.default.layoutFile || jpConf.templates.default.layoutFile;
  119. // ensure include contains the include from jsdocp
  120. conf.templates.default.staticFiles = conf.templates.default.staticFiles || {};
  121. const incls = conf.templates.default.staticFiles.include;
  122. const jpIncls = jpConf.templates.default.staticFiles.include;
  123. for (let i = 0; i < jpIncls.length; i++) jpIncls[i] = Path.resolve(modulePath, sanitizePath(pkg, jpIncls[i]));
  124. conf.templates.default.staticFiles.include = Array.isArray(incls) ? jpIncls.concat(incls) : jpIncls;
  125. // add the static files required by the jsdocp
  126. const tmplStaticPath = Path.resolve(conf.opts.template, 'static');
  127. conf.templates.default.staticFiles.include.push(tmplStaticPath);
  128. // capture versions and sanitize/write temp conf
  129. return new Promise((resolve, reject) => {
  130. exec(`npm view ${pkg.name} versions --json`, async (error, stdout, stderr) => {
  131. try {
  132. // need to account for first-time publish where module does not exist in npm
  133. const versions = (!error && !stderr && JSON.parse(stdout)) || [], latestVersion = versions[versions.length - 1] || '';
  134. const meta = {
  135. package: pkg,
  136. publish: {
  137. lastVersionPublished: latestVersion || '',
  138. lastVersion: latestVersion || pkg.version,
  139. moduleURL: pkg.homepage.replace(/#[^\/]*/g, ''),
  140. date: formatedDate()
  141. },
  142. env: process.env
  143. };
  144. // for template liternals purposes only: make sure any ${package.author.name}, etc. don't get set to undefined
  145. if (typeof meta.package.author !== 'object') meta.package.author = { name: meta.package.author, email: '' };
  146. // ensure the default opts are set when missing
  147. conf.opts = merge(jpConf.opts, conf.opts);
  148. // replace any template literals in the conf
  149. template(conf, meta);
  150. // merge layout parts
  151. await mergeLayout(pkg, conf, meta, modulePath, jsdocpPath, jsdocpTmpdir);
  152. // set private meta after merge takes place
  153. // private since versions could be published after the current versions is published
  154. // should be captured on the client to get the latest copy at the root dir
  155. if (!versions.includes(pkg.version)) versions.push(pkg.version);
  156. meta.publish.versions = JSON.stringify(versions, 0);
  157. // write require files/dirs
  158. console.log(`Writting compiled configuration: ${tempConfPath}`);
  159. const wrConfProm = Fs.writeFile(tempConfPath, JSON.stringify(conf));
  160. try {
  161. await wrConfProm;
  162. } catch (err) {
  163. err.message += ` (Unable to write ${tempConfPath})`;
  164. return reject(err);
  165. }
  166. resolve(meta);
  167. } catch (err) {
  168. reject(err);
  169. }
  170. });
  171. });
  172. }
  173. /**
  174. * Merges the various layout template parts
  175. * @private
  176. * @async
  177. * @param {Object} pkg The parsed `package.json`
  178. * @param {Conf} conf The module JSDoc configuration
  179. * @param {Object} meta The meta where the `layout` will be stored
  180. * @param {String} modulePath The JSDoc configuration path
  181. * @param {String} jsdocpPath The path to the `jsdocp` module
  182. * @param {String} jsdocpTmpdir The temporary working directory where the parsed layout will be saved/set to
  183. */
  184. async function mergeLayout(pkg, conf, meta, modulePath, jsdocpPath, jsdocpTmpdir) {
  185. // paths should be added from deepest to shallow
  186. const dirs = conf.opts.jsdocp.layoutCheckTemplateDirs, base = Path.resolve(modulePath, conf.opts.jsdocp.templateProxy);
  187. const lyt = await getLayout(dirs, conf.templates.default.layoutFile, base);
  188. if (lyt.errors.length && (!lyt.path || !lyt.content)) {
  189. const error = lyt.errors.pop();
  190. error.message += ` (Unable to resolve tempalte layout. Checked the following paths under "${base}": ${dirs.join(', ')})`;
  191. error.conf = conf;
  192. throw error;
  193. }
  194. // extract jsdocp template fragments (special case when generating jsdoc for jsdocp itself)
  195. var hd, nv, ft, error, tmplPath = pkg.name === 'jsdocp' ? jsdocpPath : modulePath;
  196. const hdPath = Path.resolve(tmplPath, sanitizePath(pkg, conf.opts.jsdocp.layoutFrags.head));
  197. const nvPath = Path.resolve(tmplPath, sanitizePath(pkg, conf.opts.jsdocp.layoutFrags.nav));
  198. const ftPath = Path.resolve(tmplPath, sanitizePath(pkg, conf.opts.jsdocp.layoutFrags.foot));
  199. var error;
  200. try {
  201. hd = (await Fs.readFile(hdPath)).toString();
  202. } catch (err) {
  203. err.message += ` (Unable to find conf.opts.jsdocp.layoutFrags.head at "${hdPath}")`;
  204. err.conf = conf;
  205. error = err;
  206. error.head = err;
  207. }
  208. try {
  209. nv = (await Fs.readFile(nvPath)).toString();
  210. } catch (err) {
  211. err.message += ` (Unable to find conf.opts.jsdocp.layoutFrags.nav at "${nvPath}")`;
  212. err.conf = conf;
  213. if (error) error.nav = err;
  214. else error = err;
  215. }
  216. try {
  217. ft = (await Fs.readFile(ftPath)).toString();
  218. } catch (err) {
  219. err.message += ` (Unable to find conf.opts.jsdocp.layoutFrags.foot at "${ftPath}")`;
  220. err.conf = conf;
  221. if (error) error.foot = err;
  222. else error = err;
  223. }
  224. if (error) throw error;
  225. conf.opts.jsdocp.layoutFrags.head = hdPath;
  226. conf.opts.jsdocp.layoutFrags.nav = nvPath;
  227. conf.opts.jsdocp.layoutFrags.foot = ftPath;
  228. // merge the template layout with the jsdocp layout
  229. lyt.content = lyt.content.replace(/(<\s*head[^>]*>)([\s\S]*?)(<\s*\/\s*head>)/ig, (mtch, open, content, close) => {
  230. return `${open}${content}${hd}${close}`;
  231. }).replace(/(<\s*body[^>]*>)([\s\S]*?)(<\s*\/\s*body>)/ig, (mtch, open, content, close) => {
  232. return `${open}${nv}${content}${ft}${close}`;
  233. });
  234. meta.layout = { path: lyt.path, content: lyt.content };
  235. // write/set merged layout file
  236. conf.templates.default.layoutFile = Path.resolve(jsdocpTmpdir, 'layout.tmpl');
  237. return Fs.writeFile(conf.templates.default.layoutFile, lyt.content);
  238. }
  239. /**
  240. * Searches for a layout template in the specified directories in the order they were added. The first diectory that contains
  241. * the file name will be read and returned.
  242. * @private
  243. * @async
  244. * @param {String[]} dirs The directory paths to check if the `fileName`
  245. * @param {String} fileName The file name that will be checked/read from (using the directory entry from `dirs`)
  246. * @param {String} [base] The base path that will be resolved for each of the directories
  247. * @returns {Object} `{ errors: [], path: 'path/to/the/layout.tmpl', content: 'the layout content here' }`
  248. */
  249. async function getLayout(dirs, fileName, base) {
  250. const rtn = { errors: [] };
  251. if (base) dirs = [ ...dirs, '.' ]; // include the base in the search
  252. var lyt;
  253. for (let dir of dirs) {
  254. try {
  255. if (base) dir = Path.resolve(base, dir);
  256. lyt = await Fs.stat(dir);
  257. if (!lyt.isDirectory()) {
  258. errors.push(new Error(`Layout is not a valid directory: ${dir} stats: ${JSON.stringify(lyt)}`));
  259. continue;
  260. }
  261. rtn.path = Path.resolve(dir, fileName);;
  262. try {
  263. rtn.content = (await Fs.readFile(rtn.path)).toString();
  264. } catch (err) {
  265. err.message += ` (Unable to find layout template at "${rtn.path}")`;
  266. errors.push(err);
  267. rtn.path = '';
  268. continue;
  269. }
  270. return rtn;
  271. } catch (err) {
  272. err.message += ` (Unexpected error while looking for layout template at: ${dir})`;
  273. rtn.errors.push(err);
  274. }
  275. }
  276. return rtn;
  277. }
  278. /**
  279. * Handles deploying documentation to a `git` branch
  280. * @private
  281. * @ignore
  282. * @param {Function} resolve The promise resolver
  283. * @param {Function} reject The promise rejector
  284. * @param {Conf} conf The `jsdoc` configuration
  285. * @param {Object} pkg The `package.json`
  286. * @param {String} modulePath The JSDoc configuration path
  287. * @param {String} jsdocpPath The path to the `jsdocp` module
  288. * @param {Integer} timeout The execution timeout in milliseconds
  289. */
  290. function deployer(resolve, reject, conf, pkg, modulePath, jsdocpPath, timeout) {
  291. try {
  292. console.log(`Deploying v${pkg.version} pages...`);
  293. const deployCliPath = Path.resolve(jsdocpPath, 'deploy/.git_pages');
  294. const ver = sanitizeArg(`v${pkg.version}`), docPth = sanitizeArg(Path.resolve(modulePath, conf.opts.destination));
  295. const pubPth = sanitizeArg(Path.resolve(modulePath, conf.opts.jsdocp.deploy.path)), brch = sanitizeArg(conf.opts.jsdocp.deploy.branch);
  296. const host = sanitizeArg(conf.opts.jsdocp.deploy.host), usr = sanitizeArg(conf.opts.jsdocp.deploy.user.name);
  297. const email = sanitizeArg(conf.opts.jsdocp.deploy.user.email), msg = sanitizeArg(conf.opts.jsdocp.deploy.message, true);
  298. if (!brch) throw new Error('opts.jsdocp.deploy.branch is required');
  299. if (!host) throw new Error('opts.jsdocp.deploy.host is required');
  300. if (!usr) throw new Error('opts.jsdocp.deploy.user.name is required. Check that your package.json has an author.name'
  301. + ' or set a user name in your jsdoc configuration');
  302. if (!email) throw new Error('opts.jsdocp.deploy.user.email is required. Check that your package.json has an author.email'
  303. + ' or set an email in your jsdoc configuration');
  304. if (!msg) throw new Error('opts.jsdocp.deploy.message is required');
  305. process.env.PUB_PACKAGE_VERSION = ver;
  306. process.env.PUB_DOC_PATH = docPth;
  307. process.env.PUB_PATH = pubPth;
  308. process.env.PUB_BRANCH = brch;
  309. process.env.PUB_REPO_HOST = host;
  310. process.env.PUB_REPO_USER = pkg.author.name;
  311. process.env.PUB_REPO_NAME = pkg.name;
  312. process.env.PUB_USER = usr;
  313. process.env.PUB_EMAIL = email;
  314. process.env.PUB_MESSAGE = msg;
  315. const execOpts = { env: process.env, cwd: modulePath, timeout };
  316. const deployExec = `bash ${deployCliPath}`;
  317. const deploy = exec(deployExec, execOpts);
  318. deploy.stdout.pipe(process.stdout);
  319. deploy.stderr.pipe(process.stderr);
  320. deploy.on('error', error => reject(error));
  321. deploy.on('exit', (code, signal) => {
  322. if (code !== 0) return reject(new Error(`Deployment exited with code: ${code}${signal ? ` signal: ${signal}` : ''}`));
  323. resolve(true);
  324. });
  325. } catch (err) {
  326. err.message += ` (Failed to execute deployment)`;
  327. err.conf = conf;
  328. reject(err);
  329. }
  330. }
  331. /**
  332. * Performs a __deep__ merge on one or more sources. The result is similar to `Object.assign` or `{ ...src1, ... src2 }`,
  333. * but retains the accumulated property values expected during a _deep_ merge.
  334. * @private
  335. * @ignore
  336. * @param {...any} srcs The sources that will be merged
  337. * @returns {*} The merged entity
  338. */
  339. function merge(...srcs) {
  340. let rtn = {};
  341. for (const src of srcs) {
  342. if (src instanceof Array) {
  343. if (!(rtn instanceof Array)) rtn = [...src];
  344. else rtn = [...rtn, ...src];
  345. } else if (src instanceof Object) {
  346. for (let [key, value] of Object.entries(src)) {
  347. if (value instanceof Object && key in rtn) value = merge(rtn[key], value);
  348. rtn = { ...rtn, [key]: value };
  349. }
  350. }
  351. }
  352. return rtn;
  353. }
  354. /**
  355. * Processes any template strings into {@link literal} values using the source for path resolution
  356. * @private
  357. * @param {Object} dest The destination object where the properties will be evaluated
  358. * @param {Object} src The source object that will be used to replace tempalted references with
  359. * @returns {*} The destination
  360. */
  361. function template(dest, src) {
  362. if (dest instanceof Array) {
  363. for (let i = 0, ln = dest.length; i < ln; i++) {
  364. var dtyp = dest[i] ? typeof dest[i] : '';
  365. if (dtyp === 'object') dest[i] = template(dest[i], src);
  366. else if (dtyp === 'string') dest[i] = literal(dest[i], src);
  367. }
  368. } else if (dest instanceof Object) {
  369. for (let [key, value] of Object.entries(dest)) {
  370. if (value instanceof Object) dest[key] = template(value, src);
  371. else if (value && typeof value === 'string') dest[key] = literal(value, src);
  372. }
  373. }
  374. return dest;
  375. }
  376. /**
  377. * Replaces each `${}` that contains `.` delimited paths to values within a supplied object
  378. * (e.g. `Testing ${someObject.someValue}` using object `{ someObject: { sameValue: 123 }}`
  379. * results in `Testing 123`
  380. * @private
  381. * @param {String} str The templated string to perform the replacements on
  382. * @param {Object} obj The object where values will be extracted from
  383. * @returns {String} The parsed template string
  384. */
  385. function literal(str, obj) {
  386. return str.replace(/\$\{([^\}]*)\}/g, (mtch, path) => {
  387. var paths = path.split('.'), val = obj;
  388. if (!paths.length) return mtch;
  389. for (let i = 0, ln = paths.length; i < ln; i++) {
  390. if (i < ln - 1 && !val.hasOwnProperty(paths[i])) return mtch;
  391. val = val[paths[i]];
  392. }
  393. return typeof val === 'undefined' ? mtch : val;
  394. });
  395. }
  396. /**
  397. * Generates a formatted date string
  398. * @private
  399. * @ignore
  400. * @param {Date} [date=new Date()] The date to format
  401. * @param {String} [delimiter='-'] The date delimiter
  402. * @returns {String} The formmated date
  403. */
  404. function formatedDate(date, delimiter = '-') {
  405. date = date || new Date();
  406. return `${date.getFullYear()}${delimiter}${('0' + (date.getMonth() + 1)).slice(-2)}${delimiter}${('0' + date.getDate()).slice(-2)}`;
  407. }
  408. /**
  409. * Sanitizes a path to accommodate `jsdocp` self documentation
  410. * @private
  411. * @ignore
  412. * @param {Object} pkg The parsed `package.json`
  413. * @param {String} path The path to sanitize
  414. * @returns {String} The sanitized path
  415. */
  416. function sanitizePath(pkg, path) {
  417. return pkg.name === 'jsdocp' ? path.replace(/node_modules[\/\\]+jsdocp/ig, '') : path;
  418. }
  419. /**
  420. * Sanitizes a command line argument
  421. * @private
  422. * @ignore
  423. * @param {String} arg The argument to sanitize
  424. * @param {Boolean} [spaces] `true` allow spaces
  425. * @requires {String} The sanitized argument
  426. */
  427. function sanitizeArg(arg, spaces) {
  428. const sarg = arg.replace(/[^\\]'|"/g, function (mtch) {
  429. return mtch.slice(0, 1) + '\\\'';
  430. });
  431. return spaces ? sarg : sarg.replace(/\s/g, '');
  432. }
  433. /**
  434. * The namespace used by {@link publicize} for the {@link Conf}
  435. * @namespace jsdocp
  436. */
  437. /**
  438. * Section `grep` options
  439. * @typedef {Object} ChangeLogSectionGrep
  440. * @property {String} [regexp] The regular expression used as filter in the `git log -grep=`
  441. * @property {String} [ignoreCase] `true` for case-insensitive `git log -i`
  442. * @property {String} [extendedRegexp] `true` for _extended_ regular expressions `git log -E`
  443. * @property {String} [allMatch] `true` to limit all regular expressions in the `grep`
  444. * @memberof jsdocp
  445. */
  446. /**
  447. * Section options for breaking changes
  448. * @typedef {Object} ChangeLogSectionBreak
  449. * @property {String} [header] Markdown used as a _header_ when there are change log entries for breaking changes
  450. * @property {ChangeLogSectionGrep} [grep] Section `grep` options for breaking changes
  451. * @memberof jsdocp
  452. */
  453. /**
  454. * Section options for features
  455. * @typedef {Object} ChangeLogSectionFeatures
  456. * @property {String} [header] Markdown used as a _header_ when there are change log entries for features
  457. * @property {ChangeLogSectionGrep} [grep] Section `grep` options for features
  458. * @memberof jsdocp
  459. */
  460. /**
  461. * Section options for fixes
  462. * @typedef {Object} ChangeLogSectionFixes
  463. * @property {String} [header] Markdown used as a _header_ when there are change log entries for fixes
  464. * @property {ChangeLogSectionGrep} [grep] Section `grep` options for fixes
  465. * @memberof jsdocp
  466. */
  467. /**
  468. * Section options for merged/pull requests
  469. * @typedef {Object} ChangeLogSectionMerges
  470. * @property {String} [header] Markdown used as a _header_ when there are change log entries for merges
  471. * @property {ChangeLogSectionGrep} [grep] Section `grep` options for merges
  472. * @memberof jsdocp
  473. */
  474. /**
  475. * The sections within the change log which organize changes (omit output a list without sections)
  476. * @typedef {Object} ChangeLogSections
  477. * @property {ChangeLogSectionBreak} [breaks] Section options for breaking changes
  478. * @property {ChangeLogSectionFeatures} [features] Section options for features
  479. * @property {ChangeLogSectionFixes} [fixes] Section options for fixes
  480. * @property {ChangeLogSectionMerges} [merges] Section options for merged/pull requests
  481. * @memberof jsdocp
  482. */
  483. /**
  484. * The change log options used to generate the change log file and link
  485. * @typedef {Object} ChangeLog
  486. * @property {Object} [title] The change log page `title` of the generated HTML page
  487. * @property {String} [line] The _format_ for individual commit lines produced in the change log markdown.
  488. * @property {String} [header] The markdown that will be pre-pended to the change log.
  489. * @property {ChangeLogSections} [sections] The sections within the change log which organize changes (omit output a list without sections)
  490. * @memberof jsdocp
  491. */
  492. /**
  493. * The navigation menu options
  494. * @typedef {Object} MenuOptions
  495. * @property {String} [position] The navigation menu _position_ (`top`, `left`, `bottom`, `right`)
  496. * @property {String} [matchMedia] The CSS segment that will be passed into `window.matchMedia` in the client's browser
  497. * @property {String} [autoHide] `true` to auto hide while vertically scrolling _down_, show when scrolling _up_
  498. * @memberof jsdocp
  499. */
  500. /**
  501. * The options for the logo displayed in the navigation menu
  502. * @typedef {Object} MenuLogo
  503. * @property {String} [src] The source URL for the logo icon dsiplayed in the navigation menu (if not overridden by `inlineSvgPath`)
  504. * @property {String} [inlineSvgPath] A path to an `svg` logo that will be inserted inline within the navigation menu display. Will
  505. * override the logo `src`, but if present will fall back on the `src` when the `svg` content cannot be extracted.
  506. * @property {String} [anchorclassName] The CSS class name assigned to the logo icon's anchor tag
  507. * @property {String} [className] The CSS class name assigned to the logo icon loaded from the `src`
  508. * @memberof jsdocp
  509. */
  510. /**
  511. * The options that apply to __all__ of the navigation menu icons (excluding the `logo`)
  512. * @typedef {Object} MenuIcons
  513. * @property {String} [className] The CSS class name applied to __all__ of the icons in the navigation menu (excluding the `logo`)
  514. * @memberof jsdocp
  515. */
  516. /**
  517. * The options for the `npm` package icon that will appear in the navigation menu
  518. * @typedef {Object} MenuPackage
  519. * @property {String} [title] The `title` set on the `npm` package icon
  520. * @property {String} [src] The `src` used on the `img` in the navigation menu that links to the `npm` package (omit to use the
  521. * default icon)
  522. * @property {String} [className] The CSS class name assigned to the `npm` package icon
  523. * @memberof jsdocp
  524. */
  525. /**
  526. * The options for an icon that will appear in the navigation menu
  527. * @typedef {Object} MenuIcon
  528. * @property {String} [title] The `title` set on the icon
  529. * @property {String} [src] The `src` used on the `img` in the navigation menu that links to the icon (omit to use the default icon)
  530. * @property {String} [className] The CSS class name assigned to the icon
  531. * @memberof jsdocp
  532. */
  533. /**
  534. * The options for the generated pages naviagation menu
  535. * @typedef {Object} Menu
  536. * @property {MenuOptions} [SM] The navigation menu options for small displays
  537. * @property {MenuOptions} [MD] The navigation menu options for medium displays
  538. * @property {MenuOptions} [LG] The navigation menu options for large displays
  539. * @property {String} [className] The CSS class applied to the main menu
  540. * @property {MenuLogo} [logo] The options for the logo displayed in the navigation menu
  541. * @property {MenuIcons} [icons] The options that apply to __all__ of the navigation menu icons (excluding the `logo`)
  542. * @property {MenuPackage} [package] The options for the `npm` package icon that will appear in the navigation menu
  543. * @property {MenuIcon} [changelog] The options for the `CHANGELOG` icon that will appear in the navigation menu
  544. * @property {MenuIcon} [sourceCode] The options for the source `code` icon that will appear in the navigation menu
  545. * @property {MenuIcon} [versions] The options for the documentation version selection icon that will appear in the navigation menu
  546. * @memberof jsdocp
  547. */
  548. /**
  549. * The versions options used to generate links to previously published version docs
  550. * @typedef {Object} Versions
  551. * @property {String} [from] A Semantic Versioning compliant version that designates the first version to show
  552. * in the version drop-down selection for different docs (omit to list all of them)
  553. * @property {String} [type] A designation that inidcates what doc versions to show in the drop-down selection.
  554. * A designation of `major` will only show versions that have been released for __major___ version tags (i.e. the _first_
  555. * number in the version). A designation of `minor` will only show versions that have been released for __minor__ version
  556. * tags (i.e. the _second_ number in the version). `undefined` will cause the default value to be used. Any other value, or blank value will cause
  557. * all versions to be included.
  558. * @memberof jsdocp
  559. */
  560. /**
  561. * The layout fragments used. __Typically, none of the fragment values will be overridden since they are handled internally__
  562. * @typedef {Object} LayoutFrags
  563. * @property {String} [head] The path to the template fragment that will be inserted at the __end__ of the `head` section
  564. * @property {String} [nav] The path to the template fragment that will be inserted at the __beginning__ of the `body` section
  565. * @property {String} [foot] The path to the template fragment that will be inserted at the __end__ of the `body` section
  566. * @property {String[]} [layoutCheckTemplateDirs] The directories that the required `conf.templates.default.layoutFile` will be
  567. * searched for in the order they are defined. Unfortunately, template implementations may store the `conf.templates.default.layoutFile` in different
  568. * locations. By default, the most likley/typlical directories will be checked.
  569. * @property {String} [head] The path to the `template`/`*.tmpl` file used for the header at the top of the documentation page
  570. * @property {String} [nav] The path to the `template`/`*.tmpl` file used for the navigation bar
  571. * @property {String} [footer] The path to the `template`/`*.tmpl` file used for the footer at the bottom of the documenation page
  572. * @memberof jsdocp
  573. */
  574. /**
  575. * The user options
  576. * @typedef {Object} User
  577. * @property {String} [name] The `git` user name
  578. * @property {String} [email] The `git` email
  579. * @memberof jsdocp
  580. */
  581. /**
  582. * The documentation deployment options
  583. * @typedef {Object} Deploy
  584. * @property {Object} [message] The `commit` message used when deploying the documentation
  585. * @property {Object} [branch] The branch that where documentation will be _pushed_ during deployment
  586. * @property {Object} [path] The path where the `branch` will be _cloned_ to and _pushed_ from during deployment
  587. * @property {String} [host] The host name that will be used when _cloning_/_pushing_ during deployment (e.g. `github.com`)
  588. * @property {User} [user] The user options that will be used when deploying the documentation pages
  589. * @memberof jsdocp
  590. */
  591. /**
  592. * The `jsdocp` options
  593. * @typedef {Object} JSDocpOptions
  594. * @property {ChangeLog} [changelog] The change log options used to generate the change log file and link
  595. * @property {Menu} [menu] The options for the generated pages naviagation menu
  596. * @property {Versions} [versions] The versions options used to generate links to previously published version docs
  597. * @property {Boolean} [cleanDestination] `true` to remove the `jsdoc` assigned {@link Options} `destination` prior to publishing
  598. * @property {Object[]} [links] The definitions used to generate `link` tags in the `head` element. Each object can have any number of
  599. * properties/values that will get translated to an attribute on the `link` tag matching the property name and an attribute value for the value.
  600. * @property {Object[]} [metas] The definitions used to generate `meta` tags in the `head` element. Each object can have any number of
  601. * properties/values that will get translated to an attribute on the `meta` tag matching the property name and an attribute value for the value.
  602. * @property {Object[]} [scripts] The definitions used to generate `script` tags in the `head` element. Each object can have any number of
  603. * properties/values that will get translated to an attribute on the `script` tag matching the property name and an attribute value for the value.
  604. * @property {LayoutFrags} [layoutFrags] The layout fragments used. __Typically, none of the fragment values will be overridden since they are handled
  605. * internally__
  606. * @property {Deploy} [deploy] The documentation deployment options
  607. * @memberof jsdocp
  608. */
  609. /**
  610. * The configuration options
  611. * @typedef {Object} Options
  612. * @property {String} [destination=./docs] The destination directory where the documentation will be generated in
  613. * @property {JSDocpOptions} [jsdocp] The `jsdocp` options
  614. * @memberof jsdocp
  615. */
  616. /**
  617. * The configuration used by `jsdocp`
  618. * @typedef {Object} JSDocpConf
  619. * @property {Options} [opts] The configuration options
  620. * @memberof jsdocp
  621. */
  622. /**
  623. * Placeholder for [JSDoc Configuration Options](https://jsdoc.app/about-configuring-jsdoc.html)
  624. * @typedef {Object} JSDocConf
  625. * @memberof jsdocp
  626. */
  627. /**
  628. * The configuration used by `jsdocp`
  629. * @typedef {JSDocConf & JSDocpConf} Conf
  630. * @memberof jsdocp
  631. */

1.3.0 (2021-08-20)

Full Changelog

Features: