Test Failed
Branch master (e44a8a)
by Sergii
03:10 queued 01:34
created

DocGenerator.js ➔ ???   A

Complexity

Conditions 2
Paths 64

Size

Total Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 18
Bugs 0 Features 18
Metric Value
c 18
b 0
f 18
nc 64
dl 0
loc 47
rs 9.0303
nop 3
cc 2
1
const _             = require('lodash'),
2
      fs            = require('fs'),
3
      Handlebars    = require('handlebars'),
4
      HTML          = 'HTML',
5
      path          = require('path'),
6
      BaseGenerator = require('./BaseGenerator'),
7
      SwaggerParser = require('./SwaggerParser'),
8
      Markdown      = 'Markdown'
9
10
/**
11
 * @class DocGenerator
12
 * Documentation generator (html or MarkDown)
13
 *
14
 * @property {String} type Output documentation type
15
 * @property {String} version Swagger version
16
 * @property {String} destination Output destination
17
 * @property {String} templatePath Path to template
18
 * @property {ParserOptions} parserOptions Swagger parser options
19
 * @property {Function} generatorCallback Custom generator callback
20
 * @property {Object.<Object>} additionalLayouts Additional layouts for Handlebars in format <code><file-name-or-relative-path>: <template-name></code>
21
 * @property {Object.<Object>} additionalHelpers Additional helpers for Handlebars in format <code><helper-name>: function (Handlebars) {
22
 *  // Some helper detail
23
 * }</code>
24
 * @property {String} moduleName Module name
25
 * @property {String} className Class name
26
 * @property {String} destination Output doc destination
27
 * @property {String} docsPath Method definitions path
28
 * @property {String} modelPath Model path
29
 */
30
class DocGenerator extends BaseGenerator {
31
32
  /**
33
   * DocGenerator constructor
34
   *
35
   * @param {String} type Documentation type (html or Markdown). One of (Markdown or HTML)
36
   * @param {DocGeneratorOptionsObject} options Swagger documentation generator options
37
   * @param {String} version Swagger version (default is 2.0)
38
   */
39
  constructor (type, options, version) {
40
    super()
41
42
    version                = version || '2.0'
43
    options                = options || {}
44
    this.type              = type
45
    this.version           = version
46
    this.generatorCallback = options.generatorCallback
47
    this.templatePath      = (options.templatePath || (__dirname + '/' + this.version +
0 ignored issues
show
Compatibility introduced by
Consider using the path module for constructing paths since they are otherwise not cross-OS compatible.
Loading history...
48
      '/templates/' + this.type.toLowerCase() + '/')).replace(/\/\//g, '/')
49
    this.data              = options.swagger
50
    this.additionalLayouts = options.additionalLayouts || {}
51
    this.parserOptions     = options.parserOptions
52
    this.destination       = options.destination || __dirname + '/../dist'
0 ignored issues
show
Compatibility introduced by
Consider using the path module for constructing paths since they are otherwise not cross-OS compatible.
Loading history...
53
    this._checkFile(false, 'destination', 'Destination folder not found')
54
    this.additionalHelpers = options.additionalHelpers || {}
55
    this.filename          = options.outFile
56
    this.moduleName        = options.moduleName
57
    this.className         = options.className
58
    this.modelPath         = options.modelPath
59
    this.docsPath          = options.docsPath
60
61
    this._checkFile()
62
    this._content = fs.readFileSync(this.filename).toString()
63
64
    this.data = this._parseData()
65
66
    this._checkFile(false, 'templatePath', 'Template path does not exists')
67
68
    let parser                  = new SwaggerParser(this.version, this.data, this.parserOptions || {
69
      className     : this.className,
70
      moduleName    : this.moduleName,
71
      packageName   : options.packageName,
72
      packageVersion: options.packageVersion,
73
      modelPath     : this.modelPath,
74
      docsPath      : this.docsPath
75
    })
76
    this.swaggerData            = parser.parse()
77
    this.swaggerData.moduleName = this.moduleName
78
    this.swaggerData.className  = this.className
79
80
    // fs.writeFileSync(path.join(process.cwd(), 'api-data.json'), JSON.stringify(this.swaggerData))
81
82
    if (!_.size(this.data)) {
83
      throw new Error('Swagger data is empty')
84
    }
85
  }
86
87
  /**
88
   * @const {String} HTML
89
   *
90
   * @return {string}
91
   */
92
  static get HTML () {
93
    return HTML
94
  }
95
96
  /**
97
   * @const {String} Markdown
98
   *
99
   * @return {string}
100
   */
101
  static get Markdown () {
102
    return Markdown
103
  }
104
105
  /**
106
   * Generate documentation
107
   */
108
  generate () {
109
    if (typeof this.generatorCallback === 'function') {
110
      return this.generatorCallback.apply(this, [this.data])
111
    }
112
113
    this._prepareHandlebars()
114
115
    this._generateMain()
116
117
    this._generateMethods()
118
    this._generateModels()
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
119
  }
120
121
  _generateMain () {
122
    let template = fs.readFileSync(this.templatePath + 'md.hbs').toString(),
123
        content  = Handlebars.compile(template)(this.swaggerData)
124
125
    this._writeContent(content, this._getMainFileName())
126
  }
127
128
  _getMainFileName () {
129
    return this.type === DocGenerator.HTML ? 'index.html' : 'README.MD'
130
  }
131
132
  _getExt () {
133
    return this.type === DocGenerator.HTML ? 'html' : 'md'
134
  }
135
136
  _writeContent (content, fileName) {
137
    fs.writeFileSync(path.join(this.destination, fileName), content)
138
  }
139
140
  _generateModels () {
141
    let _self = this,
142
        template = fs.readFileSync(path.join(this.templatePath, 'model.hbs')).toString(),
143
        content = ''
144
145
    _.each(this.swaggerData.definitions, (config, name) => {
146
      config.modelName = name
147
      content = Handlebars.compile(template)(config)
148
      _self._writeContent(content, path.join(this.modelPath, name + '.' + _self._getExt()))
149
    })
150
  }
151
152
  _generateMethods () {
153
    let template = fs.readFileSync(path.join(this.templatePath, 'method.hbs')).toString(),
154
        responseTemplate = fs.readFileSync(path.join(this.templatePath, 'response.hbs')).toString(),
155
        content  = '',
156
        _self    = this
157
    Handlebars.registerPartial('response', responseTemplate)
158
    _.each(this.swaggerData.methods, (config, index) => {
0 ignored issues
show
Unused Code introduced by
The parameter index is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
159
      content = Handlebars.compile(template)(config)
160
      _self._writeContent(content, path.join(_self.docsPath, config.methodName + '.' + _self._getExt()))
161
    })
162
  }
163
164
  _prepareHandlebars () {
165
    this._registerPartial('methodList')
166
    this._registerPartial('securityDefinitions')
167
168
    let _self = this
169
170
    _.each(this.additionalLayouts, (file, name) => {
171
      _self._registerPartial(file, name)
172
    })
173
174
    this._registerHelpers()
175
  }
176
177
  _registerPartial (filename, templateName) {
178
    templateName = templateName || filename
179
    if (_.isNumber(templateName)) {
180
      templateName = filename.split('/')
181
182
      let clearTemplate = ''
183
184
      for (let i = 0; i < templateName.length; i++) {
185
        if ((clearTemplate = _.trim(templateName[i])).length) {
186
          templateName = clearTemplate
187
        }
188
      }
189
    }
190
    let fileContent = fs.readFileSync(this.templatePath + filename + '.hbs').toString()
191
192
    Handlebars.registerPartial(templateName, fileContent)
193
  }
194
195
  _registerHelpers () {
196
197
    Handlebars.registerHelper('ifCond', function (v1, v2, options) {
198
      if (v1 === v2) {
199
        return options.fn(this)
200
      }
201
      return options.inverse(this)
202
    })
203
204
    Handlebars.registerHelper('ifHas', function (object, paramName, value, options) {
205
      for (let i in object) {
206
        if (!object.hasOwnProperty(i)) {
207
          continue
208
        }
209
210
        let has = object[i][paramName] === value
211
212
        if (has) {
213
          return options.fn(this)
214
        }
215
      }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
216
    })
217
218
    Handlebars.registerHelper('breaklines', function(text) {
219
      text = Handlebars.Utils.escapeExpression(text);
220
      text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
221
      return new Handlebars.SafeString(text);
222
    });
223
224
    Handlebars.registerHelper('requireFilter', function (require) {
225
      return require ? 'required' : 'optional'
226
    })
227
228
    Handlebars.registerHelper('ifDefine', function (object, paramName, options) {
229
      if (typeof object[paramName] !== 'undefined') {
230
        return options.fn(this)
231
      }
232
233
      return options.inverse(this)
234
    })
235
236
    Handlebars.registerHelper('ifLength', function (object, options) {
237
      if (typeof object === 'undefined') {
238
        return options.inverse(this)
239
      }
240
      return object.length ? options.fn(this) : options.inverse(this)
241
    })
242
243
    _.each(this.additionalHelpers, (cb, name) => {
244
      if (typeof cb === 'function') {
245
        cb(name, Handlebars)
246
      }
247
    })
248
  }
249
}
250
251
/**
252
 * @ignore module
253
 * @ignore module.exports
254
 */
255
module.exports = DocGenerator