plugin.js

  1. /**
  2. * @module plugin
  3. */
  4. const fs = require('fs')
  5. const path = require('path')
  6. const utils = require('./utils.js')
  7. /* this allows this module to be tested outside of the jsdoc context */
  8. let logger
  9. try {
  10. logger = require('jsdoc/util/logger')
  11. } catch (e) {
  12. logger = { info: function () {} } // console.log
  13. }
  14. // The directory from which following examples will be taken
  15. let _examplesDir = ''
  16. // Holds contents of examples file (JSON) for each class or module
  17. let _examples = {}
  18. // used to avoid double file loads -- jsdoc seems to duplicate tags
  19. let previousValue = null
  20. let previousFilename = null
  21. /**
  22. * Adds example text to '@examples'-tagged doclets.
  23. * If examples already exist, they are appended to.
  24. * @param {Object} doclet - the current JSDoc doclet
  25. */
  26. function newDoclet ({ doclet }) {
  27. // console.log(JSON.stringify(doclet, null, 2))
  28. const name = doclet.name
  29. let examples = (Array.isArray(_examples)) ? _examples : _examples[name]
  30. examples = examples || _readExamples(name)
  31. if (!examples) return
  32. doclet.examples = doclet.examples || [] // ensure doclet has examples list
  33. // add each example to the doclet's list
  34. for (const example of examples) {
  35. const lines = utils.bodyString(example.code)
  36. if ('expect' in example) { // add the result at the end, if needed
  37. lines.push('// <= ' + JSON.stringify(example.expect))
  38. }
  39. if (lines[0].startsWith('// caption:')) {
  40. const str = lines[0].replace('// caption:', '').trim()
  41. lines[0] = '<caption>' + str + '</caption>'
  42. }
  43. doclet.examples.push(lines.join('\n'))
  44. }
  45. if (Array.isArray(_examples)) _examples = false
  46. }
  47. /**
  48. * Defines the '@examples' tag, and the action to be taken
  49. * when the tag is encountered
  50. * @param {*} dictionary
  51. */
  52. function defineTags (dictionary) {
  53. dictionary.defineTag('examples', {
  54. mustHaveValue: true,
  55. onTagged: function (doclet, tag) {
  56. const filename = doclet.meta.filename
  57. if (filename !== previousFilename) _examplesDir = ''
  58. previousFilename = filename
  59. const value = tag.value
  60. if (value === previousValue) return // avoid double-processing
  61. previousValue = value
  62. if (fs.existsSync(value) && fs.lstatSync(value).isDirectory()) {
  63. logger.info('examples directory:', value)
  64. _examplesDir = value
  65. _examples = {} // null
  66. } else { // read example file
  67. _readExamples(doclet.name, value)
  68. }
  69. }
  70. })
  71. }
  72. function _readExamples (name, fn) {
  73. const expath = path.resolve(_examplesDir, fn || name + '.js')
  74. if (fs.existsSync(expath)) {
  75. logger.info('Reading @examples: ', expath)
  76. _examples = utils.examples(expath)
  77. if (_examples.examples) {
  78. if (_examples.setup) {
  79. _examples.examples[name] = [{ code: _examples.setup }]
  80. }
  81. _examples = _examples.examples
  82. }
  83. }
  84. return (Array.isArray(_examples)) ? _examples : _examples[name]
  85. }
  86. module.exports = {
  87. handlers: { newDoclet: newDoclet },
  88. defineTags: defineTags
  89. }