Skip to content

Commit

Permalink
feat: add coffeescript support (#52)
Browse files Browse the repository at this point in the history
It's now possible to include your `.coffee` files into the result swagger specification.

Example:

```sh
swagger-jsdoc.js -d example/v2/swaggerDef.js example/v2/route.coffee
```
  • Loading branch information
Aslan11 authored Oct 13, 2020
1 parent 0bfe3dc commit 41c410f
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 21 deletions.
26 changes: 26 additions & 0 deletions example/v2/route.coffee
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions lib/helpers/parseApiFile.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down
40 changes: 30 additions & 10 deletions lib/helpers/parseApiFileContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
}

Expand Down
132 changes: 121 additions & 11 deletions test/helpers.spec.js
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -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",
},
],
},
],
});
});
});
});

0 comments on commit 41c410f

Please sign in to comment.