From 766b4c39b581b3bf4519d9b14618f172e2786ea5 Mon Sep 17 00:00:00 2001
From: Sebastian <sebastian.mihalache@thinslices.com>
Date: Tue, 10 Apr 2018 10:46:19 +0300
Subject: [PATCH] feat(component-invite): add apidoc to multiple components

---
 packages/component-email/.gitignore           |   3 +-
 packages/component-email/apidoc.json          |   8 +
 packages/component-email/package.json         |  13 +-
 packages/component-email/src/Emails.js        |  16 ++
 packages/component-invite/.gitignore          |   3 +-
 packages/component-invite/apidoc.json         |   8 +
 packages/component-invite/package.json        |  13 +-
 .../src/CollectionsInvitations.js             |  94 ++++++++++++
 packages/component-user-manager/.gitignore    |   3 +-
 packages/component-user-manager/apidoc.json   |   8 +
 packages/component-user-manager/package.json  |  13 +-
 .../src/CollectionsUsers.js                   | 137 +++++++++++++++++-
 packages/component-user-manager/src/Users.js  |  32 ++++
 yarn.lock                                     | 112 +++++++++++++-
 14 files changed, 444 insertions(+), 19 deletions(-)
 create mode 100644 packages/component-email/apidoc.json
 create mode 100644 packages/component-invite/apidoc.json
 create mode 100644 packages/component-user-manager/apidoc.json

diff --git a/packages/component-email/.gitignore b/packages/component-email/.gitignore
index 3614a8100..5ede6b07a 100644
--- a/packages/component-email/.gitignore
+++ b/packages/component-email/.gitignore
@@ -5,4 +5,5 @@ node_modules/
 uploads/
 .env.*
 .env
-config/local*.*
\ No newline at end of file
+config/local*.*
+public/apidoc
diff --git a/packages/component-email/apidoc.json b/packages/component-email/apidoc.json
new file mode 100644
index 000000000..d527afa74
--- /dev/null
+++ b/packages/component-email/apidoc.json
@@ -0,0 +1,8 @@
+{
+    "name": "Component Email API documentation",
+    "version": "0.0.1",
+    "description": "A list of APIs for the Email Component",
+    "template": {
+        "forceLanguage": "en"
+    }
+}
diff --git a/packages/component-email/package.json b/packages/component-email/package.json
index 408194784..f15e1b266 100644
--- a/packages/component-email/package.json
+++ b/packages/component-email/package.json
@@ -4,10 +4,14 @@
   "description": "email component for faraday",
   "license": "MIT",
   "author": "Collaborative Knowledge Foundation",
-  "files": ["src"],
+  "files": [
+    "src"
+  ],
   "main": "index.js",
   "scripts": {
-    "test": "jest"
+    "test": "jest",
+    "docs": "./node_modules/.bin/apidoc -e \"(node_modules|public)\" -o public/apidoc",
+    "open-docs": "open public/apidoc/index.html"
   },
   "repository": {
     "type": "git",
@@ -20,10 +24,11 @@
   },
   "peerDependencies": {
     "@pubsweet/logger": "^0.0.1",
-    "pubsweet-server": "^1.0.1",
-    "pubsweet-component-mail-service": "0.0.1"
+    "pubsweet-component-mail-service": "0.0.1",
+    "pubsweet-server": "^1.0.1"
   },
   "devDependencies": {
+    "apidoc": "^0.17.6",
     "jest": "^22.1.1",
     "supertest": "^3.0.0"
   },
diff --git a/packages/component-email/src/Emails.js b/packages/component-email/src/Emails.js
index d16e64581..41f51ed93 100644
--- a/packages/component-email/src/Emails.js
+++ b/packages/component-email/src/Emails.js
@@ -7,6 +7,22 @@ const CollectionsInvitations = app => {
   const authBearer = app.locals.passport.authenticate('bearer', {
     session: false,
   })
+  /**
+   * @api {post} /api/emails Send a new email
+   * @apiGroup Emails
+   * @apiParamExample {json} Body
+   *    {
+   *      "email": "email@example.com",
+   *      "type": "invite", [acceptedValues: invite, assign]
+   *      "role": "editorInChief" [acceptedValues: editorInChief, admin, author, handlingEditor]
+   *    }
+   * @apiSuccessExample {json} Success
+   *    HTTP/1.1 200 OK {}
+   * @apiErrorExample {json} Send email errors
+   *    HTTP/1.1 403 Forbidden
+   *    HTTP/1.1 400 Bad Request
+   *    HTTP/1.1 500 Internal Server Error
+   */
   app.post(
     basePath,
     authBearer,
diff --git a/packages/component-invite/.gitignore b/packages/component-invite/.gitignore
index 3614a8100..59ff5cd1f 100644
--- a/packages/component-invite/.gitignore
+++ b/packages/component-invite/.gitignore
@@ -5,4 +5,5 @@ node_modules/
 uploads/
 .env.*
 .env
-config/local*.*
\ No newline at end of file
+config/local*.*
+public/apidoc
\ No newline at end of file
diff --git a/packages/component-invite/apidoc.json b/packages/component-invite/apidoc.json
new file mode 100644
index 000000000..9aaf9771b
--- /dev/null
+++ b/packages/component-invite/apidoc.json
@@ -0,0 +1,8 @@
+{
+    "name": "Component Invite API documentation",
+    "version": "0.0.1",
+    "description": "A list of APIs for the Invite Component",
+    "template": {
+        "forceLanguage": "en"
+    }
+}
diff --git a/packages/component-invite/package.json b/packages/component-invite/package.json
index 898457935..4851637a0 100644
--- a/packages/component-invite/package.json
+++ b/packages/component-invite/package.json
@@ -4,10 +4,14 @@
   "description": "invite component for pubsweet",
   "license": "MIT",
   "author": "Collaborative Knowledge Foundation",
-  "files": ["src"],
+  "files": [
+    "src"
+  ],
   "main": "index.js",
   "scripts": {
-    "test": "jest"
+    "test": "jest",
+    "docs": "./node_modules/.bin/apidoc -e \"(node_modules|public)\" -o public/apidoc",
+    "open-docs": "open public/apidoc/index.html"
   },
   "repository": {
     "type": "git",
@@ -20,10 +24,11 @@
   },
   "peerDependencies": {
     "@pubsweet/logger": "^0.0.1",
-    "pubsweet-server": "^1.0.1",
-    "pubsweet-component-mail-service": "0.0.1"
+    "pubsweet-component-mail-service": "0.0.1",
+    "pubsweet-server": "^1.0.1"
   },
   "devDependencies": {
+    "apidoc": "^0.17.6",
     "jest": "^22.1.1",
     "supertest": "^3.0.0"
   },
diff --git a/packages/component-invite/src/CollectionsInvitations.js b/packages/component-invite/src/CollectionsInvitations.js
index a1b3fbc6e..2189f8182 100644
--- a/packages/component-invite/src/CollectionsInvitations.js
+++ b/packages/component-invite/src/CollectionsInvitations.js
@@ -7,21 +7,115 @@ const CollectionsInvitations = app => {
   const authBearer = app.locals.passport.authenticate('bearer', {
     session: false,
   })
+  /**
+   * @api {post} /api/collections/:collectionId/invitations Invite a user to a collection
+   * @apiGroup CollectionsInvitations
+   * @apiParam {collectionId} collectionId Collection id
+   * @apiParamExample {json} Body
+   *    {
+   *      "email": "email@example.com",
+   *      "role": "handlingEditor", [acceptedValues: handlingEditor]
+   *    }
+   * @apiSuccessExample {json} Success
+   *    HTTP/1.1 200 OK
+   *    {
+   *      "id": "a6184463-b17a-42f8-b02b-ae1d755cdc6b",
+   *      "type": "user",
+   *      "admin": false,
+   *      "email": "email@example.com",
+   *      "teams": [
+   *        "c576695a-7cda-4e27-8e9c-31f3a0e9d592"
+   *      ],
+   *      "username": "email@example.com",
+   *      "fragments": [],
+   *      "collections": [],
+   *      "isConfirmed": false,
+   *      "editorInChief": false,
+   *      "handlingEditor": true,
+   *      "passwordResetToken": "04590a2b7f6c1f37cb84881d529e516fa6fc309c205a07f1341b2bfaa6f2b46c"
+   *    }
+   * @apiErrorExample {json} Invite user errors
+   *    HTTP/1.1 403 Forbidden
+   *    HTTP/1.1 400 Bad Request
+   *    HTTP/1.1 404 Not Found
+   *    HTTP/1.1 500 Internal Server Error
+   */
   app.post(
     basePath,
     authBearer,
     require(`${routePath}/post`)(app.locals.models),
   )
+  /**
+   * @api {get} /api/collections/:collectionId/invitations List collections invitations
+   * @apiGroup CollectionsInvitations
+   * @apiParam {collectionId} id Collection id
+   * @apiSuccessExample {json} Success
+   *    HTTP/1.1 200 OK
+   *    [{
+   *      "name": "John Smith",
+   *      "timestamp": "123223121",
+   *      "email": "email@example.com",
+   *      "status": "pending",
+   *    }]
+   * @apiErrorExample {json} List errors
+   *    HTTP/1.1 403 Forbidden
+   *    HTTP/1.1 400 Bad Request
+   *    HTTP/1.1 404 Not Found
+   */
   app.get(
     `${basePath}/:invitationId?`,
     authBearer,
     require(`${routePath}/get`)(app.locals.models),
   )
+  /**
+   * @api {delete} /api/collections/:collectionId/invitations/:invitationId Delete invitation
+   * @apiGroup CollectionsInvitations
+   * @apiParam {collectionId} collectionId Collection id
+   * @apiParam {invitationId} invitationId Invitation id
+   * @apiSuccessExample {json} Success
+   *    HTTP/1.1 204 No Content
+   * @apiErrorExample {json} Delete errors
+   *    HTTP/1.1 403 Forbidden
+   *    HTTP/1.1 404 Not Found
+   *    HTTP/1.1 500 Internal Server Error
+   */
   app.delete(
     `${basePath}/:invitationId`,
     authBearer,
     require(`${routePath}/delete`)(app.locals.models),
   )
+  /**
+   * @api {patch} /api/collections/:collectionId/invitations/:invitationId Update an invitation
+   * @apiGroup CollectionsInvitations
+   * @apiParam {collectionId} collectionId Collection id
+   * @apiParam {invitationId} invitationId Invitation id
+   * @apiParamExample {json} Body
+   *    {
+   *      "isAccepted": false,
+   *      "reason": "I am not ready" [optional]
+   *    }
+   * @apiSuccessExample {json} Success
+   *    HTTP/1.1 200 OK
+   *    {
+   *      "id": "a6184463-b17a-42f8-b02b-ae1d755cdc6b",
+   *      "type": "user",
+   *      "admin": false,
+   *      "email": "email@example.com",
+   *      "teams": [],
+   *      "username": "email@example.com",
+   *      "fragments": [],
+   *      "collections": [],
+   *      "isConfirmed": false,
+   *      "editorInChief": false,
+   *      "handlingEditor": true,
+   *      "passwordResetToken": "04590a2b7f6c1f37cb84881d529e516fa6fc309c205a07f1341b2bfaa6f2b46c"
+   *    }
+   * @apiErrorExample {json} Update invitations errors
+   *    HTTP/1.1 403 Forbidden
+   *    HTTP/1.1 400 Bad Request
+   *    HTTP/1.1 404 Not Found
+   *    HTTP/1.1 500 Internal Server Error
+   */
   app.patch(
     `${basePath}/:invitationId`,
     authBearer,
diff --git a/packages/component-user-manager/.gitignore b/packages/component-user-manager/.gitignore
index 3614a8100..59ff5cd1f 100644
--- a/packages/component-user-manager/.gitignore
+++ b/packages/component-user-manager/.gitignore
@@ -5,4 +5,5 @@ node_modules/
 uploads/
 .env.*
 .env
-config/local*.*
\ No newline at end of file
+config/local*.*
+public/apidoc
\ No newline at end of file
diff --git a/packages/component-user-manager/apidoc.json b/packages/component-user-manager/apidoc.json
new file mode 100644
index 000000000..94464f430
--- /dev/null
+++ b/packages/component-user-manager/apidoc.json
@@ -0,0 +1,8 @@
+{
+    "name": "Component User Manager API documentation",
+    "version": "0.0.1",
+    "description": "A list of APIs for the User Manager Component",
+    "template": {
+        "forceLanguage": "en"
+    }
+}
diff --git a/packages/component-user-manager/package.json b/packages/component-user-manager/package.json
index 5adea979c..0d58cca77 100644
--- a/packages/component-user-manager/package.json
+++ b/packages/component-user-manager/package.json
@@ -4,10 +4,14 @@
   "description": "user manager component for faraday",
   "license": "MIT",
   "author": "Collaborative Knowledge Foundation",
-  "files": ["src"],
+  "files": [
+    "src"
+  ],
   "main": "index.js",
   "scripts": {
-    "test": "jest"
+    "test": "jest",
+    "docs": "./node_modules/.bin/apidoc -e \"(node_modules|public)\" -o public/apidoc",
+    "open-docs": "open public/apidoc/index.html"
   },
   "repository": {
     "type": "git",
@@ -20,10 +24,11 @@
   },
   "peerDependencies": {
     "@pubsweet/logger": "^0.0.1",
-    "pubsweet-server": "^1.0.1",
-    "pubsweet-component-mail-service": "0.0.1"
+    "pubsweet-component-mail-service": "0.0.1",
+    "pubsweet-server": "^1.0.1"
   },
   "devDependencies": {
+    "apidoc": "^0.17.6",
     "jest": "^22.1.1",
     "supertest": "^3.0.0"
   },
diff --git a/packages/component-user-manager/src/CollectionsUsers.js b/packages/component-user-manager/src/CollectionsUsers.js
index 93af60000..28d568991 100644
--- a/packages/component-user-manager/src/CollectionsUsers.js
+++ b/packages/component-user-manager/src/CollectionsUsers.js
@@ -1,23 +1,156 @@
 const bodyParser = require('body-parser')
 
-const CollectionsInvitations = app => {
+const CollectionsUsers = app => {
   app.use(bodyParser.json())
   const basePath = '/api/collections/:collectionId/users'
   const routePath = './routes/collectionsUsers'
   const authBearer = app.locals.passport.authenticate('bearer', {
     session: false,
   })
+  /**
+   * @api {post} /api/collections/:collectionId/users Add a user to a collection
+   * @apiGroup CollectionsUsers
+   * @apiParam {collectionId} collectionId Collection id
+   * @apiParamExample {json} Body
+   *    {
+   *      "email": "email@example.com",
+   *      "role": "author", [acceptedValues: author],
+   *      "isSubmitting": true,
+   *      "isCorresponding": false
+   *    }
+   * @apiSuccessExample {json} Success
+   *    HTTP/1.1 200 OK
+   *    {
+   *      "id": "a6184463-b17a-42f8-b02b-ae1d755cdc6b",
+   *      "type": "user",
+   *      "admin": false,
+   *      "email": "email@example.com",
+   *      "teams": [
+   *        "c576695a-7cda-4e27-8e9c-31f3a0e9d592"
+   *      ],
+   *      "username": "email@example.com",
+   *      "fragments": [],
+   *      "collections": [],
+   *      "isConfirmed": false,
+   *      "editorInChief": false,
+   *      "handlingEditor": false,
+   *      "passwordResetToken": "04590a2b7f6c1f37cb84881d529e516fa6fc309c205a07f1341b2bfaa6f2b46c"
+   *    }
+   * @apiErrorExample {json} Invite user errors
+   *    HTTP/1.1 400 Bad Request
+   *    HTTP/1.1 500 Internal Server Error
+   */
   app.post(
     basePath,
     authBearer,
     require(`${routePath}/post`)(app.locals.models),
   )
+  /**
+   * @api {delete} /api/collections/:collectionId/user/:userId Delete user from collection
+   * @apiGroup CollectionsUsers
+   * @apiParam {collectionId} collectionId Collection id
+   * @apiParam {userId} userId User id
+   * @apiSuccessExample {json} Success
+   *    HTTP/1.1 200 {}
+   * @apiErrorExample {json} Delete errors
+   *    HTTP/1.1 403 Forbidden
+   *    HTTP/1.1 404 Not Found
+   */
   app.delete(
     `${basePath}/:userId`,
     authBearer,
     require(`${routePath}/delete`)(app.locals.models),
   )
+  /**
+   * @api {get} /api/collections/:collectionId/users List collections users
+   * @apiGroup CollectionsUsers
+   * @apiParam {collectionId} collectionId Collection id
+   * @apiSuccessExample {json} Success
+   *    HTTP/1.1 200 OK
+   *    [{
+   *      "id": "a6184463-b17a-42f8-b02b-ae1d755cdc6b",
+   *      "type": "user",
+   *      "admin": false,
+   *      "email": "email@example.com",
+   *      "teams": [
+   *        "c576695a-7cda-4e27-8e9c-31f3a0e9d592"
+   *      ],
+   *      "username": "email@example.com",
+   *      "fragments": [],
+   *      "collections": [],
+   *      "isConfirmed": false,
+   *      "editorInChief": false,
+   *      "handlingEditor": false,
+   *      "passwordResetToken": "04590a2b7f6c1f37cb84881d529e516fa6fc309c205a07f1341b2bfaa6f2b46c"
+   *    },
+   *    {
+   *      "id": "a6184463-b17a-42f8-b02b-ae1d755cdc6b",
+   *      "type": "user",
+   *      "admin": false,
+   *      "email": "email2@example.com",
+   *      "teams": [
+   *        "c576695a-7cda-4e27-8e9c-31f3a0e9d592"
+   *      ],
+   *      "username": "email2@example.com",
+   *      "fragments": [],
+   *      "collections": [],
+   *      "isConfirmed": true,
+   *      "editorInChief": false,
+   *      "handlingEditor": false,
+   *    }]
+   * @apiErrorExample {json} List errors
+   *    HTTP/1.1 400 Bad Request
+   *    HTTP/1.1 404 Not Found
+   */
   app.get(basePath, authBearer, require(`${routePath}/get`)(app.locals.models))
+  /**
+   * @api {patch} /api/collections/:collectionId/users/:userId Update a user on a collection
+   * @apiGroup CollectionsUsers
+   * @apiParam {collectionId} collectionId Collection id
+   * @apiParam {userId} userId User id
+   * @apiParamExample {json} Body
+   *    {
+   *      "isSubmitting": false,
+   *      "isCorresponding": true
+   *    }
+   * @apiSuccessExample {json} Success
+   *    HTTP/1.1 200 OK
+   *    {
+   *      "id": "7e8a77f9-8e5c-4fa3-b717-8df9932df128",
+   *      "type": "collection",
+   *      "owners": [
+   *        {
+   *           "id": "69ac1ee9-08a8-4ee6-a57c-c6c8be8d3c4f",
+   *           "username": "admin"
+   *        }
+   *      ],
+   *      "authors": [
+   *        {
+   *          "userId": "a6184463-b17a-42f8-b02b-ae1d755cdc6b",
+   *          "isSubmitting": false,
+   *          "isCorresponding": true
+   *        }
+   *      ],
+   *      "created": 1522829424474,
+   *      "customId": "9424466",
+   *      "fragments": [
+   *        "c35d0bd8-be03-4c16-b869-bd69796c5a21"
+   *      ],
+   *      "invitations": [
+   *        {
+   *          "id": "9043a836-0d49-4b8d-be0b-df39071b5c57",
+   *          "hasAnswer": false,
+   *          "timestamp": 1522831123430,
+   *          "isAccepted": false
+   *        },
+   *      ]
+   *    }
+   * @apiErrorExample {json} Update invitations errors
+   *    HTTP/1.1 403 Forbidden
+   *    HTTP/1.1 400 Bad Request
+   *    HTTP/1.1 404 Not Found
+   *    HTTP/1.1 500 Internal Server Error
+   */
   app.patch(
     `${basePath}/:userId`,
     authBearer,
@@ -25,4 +158,4 @@ const CollectionsInvitations = app => {
   )
 }
 
-module.exports = CollectionsInvitations
+module.exports = CollectionsUsers
diff --git a/packages/component-user-manager/src/Users.js b/packages/component-user-manager/src/Users.js
index 0607e16b7..3fdfb1b60 100644
--- a/packages/component-user-manager/src/Users.js
+++ b/packages/component-user-manager/src/Users.js
@@ -2,6 +2,38 @@ const bodyParser = require('body-parser')
 
 const Invite = app => {
   app.use(bodyParser.json())
+  /**
+   * @api {post} /api/users/reset-password Reset password
+   * @apiGroup Users
+   * @apiParamExample {json} Body
+   *    {
+   *      "email": "email@example.com",
+   *      "token": "123123123",
+   *      "password": "password",
+   *      "firstName": "John",
+   *      "lastName": "Smith",
+   *      "affiliation": "UCLA",
+   *      "title": "Mr"
+   *    }
+   * @apiSuccessExample {json} Success
+   *    HTTP/1.1 200 OK
+   *    {
+   *      "id": "a6184463-b17a-42f8-b02b-ae1d755cdc6b",
+   *      "type": "user",
+   *      "admin": false,
+   *      "email": "email@example.com",
+   *      "teams": [],
+   *      "username": "email@example.com",
+   *      "fragments": [],
+   *      "collections": [],
+   *      "isConfirmed": true,
+   *      "editorInChief": false,
+   *      "handlingEditor": false
+   *    }
+   * @apiErrorExample {json} Invite user errors
+   *    HTTP/1.1 400 Bad Request
+   *    HTTP/1.1 404 Not Found
+   */
   app.post(
     '/api/users/reset-password',
     require('./routes/users/resetPassword')(app.locals.models),
diff --git a/yarn.lock b/yarn.lock
index e2cb01c74..82e1587a0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -365,6 +365,10 @@ ansi-styles@^3.2.0:
   dependencies:
     color-convert "^1.9.0"
 
+ansi-styles@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178"
+
 any-observable@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.2.0.tgz#c67870058003579009083f54ac0abafb5c33d242"
@@ -376,6 +380,28 @@ anymatch@^1.3.0:
     micromatch "^2.1.5"
     normalize-path "^2.0.0"
 
+apidoc-core@~0.8.2:
+  version "0.8.3"
+  resolved "https://registry.yarnpkg.com/apidoc-core/-/apidoc-core-0.8.3.tgz#d9d63545829df250d2cca049683a87e775364b96"
+  dependencies:
+    fs-extra "^3.0.1"
+    glob "^7.1.1"
+    iconv-lite "^0.4.17"
+    klaw-sync "^2.1.0"
+    lodash "~4.17.4"
+    semver "~5.3.0"
+
+apidoc@^0.17.6:
+  version "0.17.6"
+  resolved "https://registry.yarnpkg.com/apidoc/-/apidoc-0.17.6.tgz#4ee8ac610dedddcb1006c3e28fa7dd634b4a5ce6"
+  dependencies:
+    apidoc-core "~0.8.2"
+    fs-extra "~3.0.1"
+    lodash "~4.17.4"
+    markdown-it "^8.3.1"
+    nomnom "~1.8.1"
+    winston "~2.3.1"
+
 apollo-cache-control@^0.0.x:
   version "0.0.9"
   resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.0.9.tgz#77100f456fb19526d33b7f595c8ab1a2980dcbb4"
@@ -1787,6 +1813,14 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1:
     escape-string-regexp "^1.0.5"
     supports-color "^5.2.0"
 
+chalk@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f"
+  dependencies:
+    ansi-styles "~1.0.0"
+    has-color "~0.1.0"
+    strip-ansi "~0.1.0"
+
 chance@^1.0.13:
   version "1.0.13"
   resolved "https://registry.yarnpkg.com/chance/-/chance-1.0.13.tgz#666bec2db42b3084456a3e4f4c28a82db5ccb7e6"
@@ -3817,6 +3851,14 @@ from2@^2.1.0:
     inherits "^2.0.1"
     readable-stream "^2.0.0"
 
+fs-extra@^3.0.1, fs-extra@~3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291"
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^3.0.0"
+    universalify "^0.1.0"
+
 fs-extra@^4.0.1, fs-extra@^4.0.2:
   version "4.0.3"
   resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
@@ -4134,6 +4176,10 @@ has-ansi@^2.0.0:
   dependencies:
     ansi-regex "^2.0.0"
 
+has-color@~0.1.0:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f"
+
 has-flag@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
@@ -5325,6 +5371,12 @@ json5@^0.5.0, json5@^0.5.1:
   version "0.5.1"
   resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
 
+jsonfile@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66"
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
 jsonfile@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -5397,6 +5449,12 @@ kind-of@^4.0.0:
   dependencies:
     is-buffer "^1.1.5"
 
+klaw-sync@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-2.1.0.tgz#3d3bcd8600e7bfdef53231c739ff053aed560e44"
+  optionalDependencies:
+    graceful-fs "^4.1.11"
+
 known-css-properties@^0.5.0:
   version "0.5.0"
   resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.5.0.tgz#6ff66943ed4a5b55657ee095779a91f4536f8084"
@@ -5476,6 +5534,12 @@ levn@^0.3.0, levn@~0.3.0:
     prelude-ls "~1.1.2"
     type-check "~0.3.2"
 
+linkify-it@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f"
+  dependencies:
+    uc.micro "^1.0.1"
+
 lint-staged@^4.1.3:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-4.3.0.tgz#ed0779ad9a42c0dc62bb3244e522870b41125879"
@@ -5720,7 +5784,7 @@ lodash.uniq@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
 
-lodash@^4, lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.1, lodash@^4.8.0:
+lodash@^4, lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.1, lodash@^4.8.0, lodash@~4.17.4:
   version "4.17.5"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
 
@@ -5807,6 +5871,16 @@ markdown-escapes@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.1.tgz#1994df2d3af4811de59a6714934c2b2292734518"
 
+markdown-it@^8.3.1:
+  version "8.4.1"
+  resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.1.tgz#206fe59b0e4e1b78a7c73250af9b34a4ad0aaf44"
+  dependencies:
+    argparse "^1.0.7"
+    entities "~1.1.1"
+    linkify-it "^2.0.0"
+    mdurl "^1.0.1"
+    uc.micro "^1.0.5"
+
 markdown-table@^1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.1.tgz#4b3dd3a133d1518b8ef0dbc709bf2a1b4824bc8c"
@@ -5833,6 +5907,10 @@ mdast-util-compact@^1.0.0:
     unist-util-modify-children "^1.0.0"
     unist-util-visit "^1.1.0"
 
+mdurl@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
+
 media-typer@0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -6244,6 +6322,13 @@ nomnom@~1.6.2:
     colors "0.5.x"
     underscore "~1.4.4"
 
+nomnom@~1.8.1:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7"
+  dependencies:
+    chalk "~0.4.0"
+    underscore "~1.6.0"
+
 nopt@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
@@ -8361,7 +8446,7 @@ semver@4.3.2:
   version "4.3.2"
   resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7"
 
-semver@5.3.0:
+semver@5.3.0, semver@~5.3.0:
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
 
@@ -8727,6 +8812,10 @@ strip-ansi@^4.0.0:
   dependencies:
     ansi-regex "^3.0.0"
 
+strip-ansi@~0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991"
+
 strip-bom@3.0.0, strip-bom@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
@@ -9236,6 +9325,10 @@ ua-parser-js@^0.7.9:
   version "0.7.17"
   resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
 
+uc.micro@^1.0.1, uc.micro@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376"
+
 uglify-es@^3.3.4:
   version "3.3.9"
   resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
@@ -9302,6 +9395,10 @@ underscore@~1.4.4:
   version "1.4.4"
   resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604"
 
+underscore@~1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
+
 unherit@^1.0.4:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.0.tgz#6b9aaedfbf73df1756ad9e316dd981885840cd7d"
@@ -9706,6 +9803,17 @@ winston@2.x, winston@^2.2.0, winston@^2.4.0:
     isstream "0.1.x"
     stack-trace "0.0.x"
 
+winston@~2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/winston/-/winston-2.3.1.tgz#0b48420d978c01804cf0230b648861598225a119"
+  dependencies:
+    async "~1.0.0"
+    colors "1.0.x"
+    cycle "1.0.x"
+    eyes "0.1.x"
+    isstream "0.1.x"
+    stack-trace "0.0.x"
+
 word-wrap@^1.0.3:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
-- 
GitLab