diff --git a/example/v2/route.coffee b/example/v2/route.coffee new file mode 100644 index 00000000..b11c1dea --- /dev/null +++ b/example/v2/route.coffee @@ -0,0 +1,26 @@ +# Coffeescript Example + +### +* @swagger +* /login: +* post: +* description: Login to the application +* produces: +* - application/json +* parameters: +* - name: username +* description: Username to use for login. +* in: formData +* required: true +* type: string +* - name: password +* description: User's password. +* in: formData +* required: true +* type: string +* responses: +* 200: +* description: login +### +app.post '/login', (req, res) -> + res.json req.body \ No newline at end of file diff --git a/lib/helpers/parseApiFile.js b/lib/helpers/parseApiFile.js index ca30d70b..8876f55e 100644 --- a/lib/helpers/parseApiFile.js +++ b/lib/helpers/parseApiFile.js @@ -1,6 +1,7 @@ const fs = require('fs'); const path = require('path'); const parseApiFileContent = require('./parseApiFileContent'); + /** * Parses the provided API file for JSDoc comments. * @function diff --git a/lib/helpers/parseApiFileContent.js b/lib/helpers/parseApiFileContent.js index 90feaa55..9c7833dd 100644 --- a/lib/helpers/parseApiFileContent.js +++ b/lib/helpers/parseApiFileContent.js @@ -12,24 +12,44 @@ const jsYaml = require('js-yaml'); */ function parseApiFileContent(fileContent, ext) { const jsDocRegex = /\/\*\*([\s\S]*?)\*\//gm; + const csDocRegex = /###([\s\S]*?)###/gm; const yaml = []; - const jsDocComments = []; + const jsdoc = []; + let regexResults = null; - if (ext === '.yaml' || ext === '.yml') { - yaml.push(jsYaml.safeLoad(fileContent)); - } else { - const regexResults = fileContent.match(jsDocRegex); - if (regexResults) { - for (let i = 0; i < regexResults.length; i += 1) { - const jsDocComment = doctrine.parse(regexResults[i], { unwrap: true }); - jsDocComments.push(jsDocComment); + switch (ext) { + case '.yml': + case '.yaml': + yaml.push(jsYaml.safeLoad(fileContent)); + break; + + case '.coffee': + regexResults = fileContent.match(csDocRegex); + if (regexResults) { + for (let i = 0; i < regexResults.length; i += 1) { + // Prepare input for doctrine + let part = regexResults[i].split('###'); + part[0] = `/**`; + part[regexResults.length - 1] = '*/'; + part = part.join(''); + jsdoc.push(doctrine.parse(part, { unwrap: true })); + } + } + break; + + default: { + regexResults = fileContent.match(jsDocRegex); + if (regexResults) { + for (let i = 0; i < regexResults.length; i += 1) { + jsdoc.push(doctrine.parse(regexResults[i], { unwrap: true })); + } } } } return { yaml, - jsdoc: jsDocComments, + jsdoc, }; } diff --git a/test/helpers.spec.js b/test/helpers.spec.js index db688e91..eaf7f145 100644 --- a/test/helpers.spec.js +++ b/test/helpers.spec.js @@ -1,6 +1,7 @@ /* eslint no-unused-expressions: 0 */ const specHelper = require('../lib/helpers/specification'); const hasEmptyProperty = require('../lib/helpers/hasEmptyProperty'); +const parseApiFileContent = require('../lib/helpers/parseApiFileContent'); const swaggerObject = require('./files/v2/swaggerObject.json'); const testData = require('./files/v2/testData'); @@ -91,17 +92,126 @@ describe('Helpers', () => { }); }); - it('hasEmptyProperty() identifies object with an empty object or array as property', () => { - const invalidA = { foo: {} }; - const invalidB = { foo: [] }; - const validA = { foo: { bar: 'baz' } }; - const validB = { foo: ['¯_(ツ)_/¯'] }; - const validC = { foo: '¯_(ツ)_/¯' }; + describe('hasEmptyProperty', () => { + it('identifies object with an empty object or array as property', () => { + const invalidA = { foo: {} }; + const invalidB = { foo: [] }; + const validA = { foo: { bar: 'baz' } }; + const validB = { foo: ['¯_(ツ)_/¯'] }; + const validC = { foo: '¯_(ツ)_/¯' }; - expect(hasEmptyProperty(invalidA)).toBe(true); - expect(hasEmptyProperty(invalidB)).toBe(true); - expect(hasEmptyProperty(validA)).toBe(false); - expect(hasEmptyProperty(validB)).toBe(false); - expect(hasEmptyProperty(validC)).toBe(false); + expect(hasEmptyProperty(invalidA)).toBe(true); + expect(hasEmptyProperty(invalidB)).toBe(true); + expect(hasEmptyProperty(validA)).toBe(false); + expect(hasEmptyProperty(validB)).toBe(false); + expect(hasEmptyProperty(validC)).toBe(false); + }); + }); + + describe('parseApiFileContent', () => { + it('should extract jsdoc comments inside .js files', () => { + const fileContent = ` + // Sets up the routes. + module.exports.setup = function (app) { + /** + * @swagger + * tags: + * name: Users + * description: User management and login + */ + + /** + * @swagger + * /users: + * post: + * description: Returns users + * tags: [Users] + * produces: + * - application/json + * parameters: + * - $ref: '#/parameters/username' + * responses: + * 200: + * description: users + */ + app.post('/users', (req, res) => { + res.json(req.body); + }); + }; + `; + + expect(parseApiFileContent(fileContent, '.js')).toEqual({ + yaml: [], + jsdoc: [ + { + description: '', + tags: [ + { + title: 'swagger', + description: + 'tags:\n name: Users\n description: User management and login', + }, + ], + }, + { + description: '', + tags: [ + { + title: 'swagger', + description: + "/users:\n post:\n description: Returns users\n tags: [Users]\n produces:\n - application/json\n parameters:\n - $ref: '#/parameters/username'\n responses:\n 200:\n description: users", + }, + ], + }, + ], + }); + }); + + it('should extract coffeescript comments inside .coffee files', () => { + const fileContent = ` + # Coffeescript Example + + ### + * @swagger + * /login: + * post: + * description: Login to the application + * produces: + * - application/json + * parameters: + * - name: username + * description: Username to use for login. + * in: formData + * required: true + * type: string + * - name: password + * description: User's password. + * in: formData + * required: true + * type: string + * responses: + * 200: + * description: login + ### + app.post '/login', (req, res) -> + res.json req.body + `; + + expect(parseApiFileContent(fileContent, '.coffee')).toEqual({ + yaml: [], + jsdoc: [ + { + description: '/', + tags: [ + { + title: 'swagger', + description: + "/login:\n post:\n description: Login to the application\n produces:\n - application/json\n parameters:\n - name: username\n description: Username to use for login.\n in: formData\n required: true\n type: string\n - name: password\n description: User's password.\n in: formData\n required: true\n type: string\n responses:\n 200:\n description: login", + }, + ], + }, + ], + }); + }); }); });