diff --git a/.gitignore b/.gitignore index d5a4e223..ec87ffcd 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,5 @@ yarn.lock package-lock.json +static + diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..1ab79513 --- /dev/null +++ b/.npmignore @@ -0,0 +1,53 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency files and directories +node_modules +jspm_packages +yarn.lock + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# mac files +.DS_Store + +# vim swap files +*.swp + +package-lock.json + +# project specific +*.tgz +tap-snapshots +test +.travis.yml +var diff --git a/.travis.yml b/.travis.yml index 7d12f7fb..a69bea2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,9 @@ node_js: - "6" - "4" +before_script: + - npm run prepare:swagger-ui + notifications: email: on_success: never diff --git a/README.md b/README.md index 0f21d711..d204cd44 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,16 @@ Sometimes you may need to hide a certain route from the documentation, just pass ### Security Global security definitions and route level security provide documentation only. It does not implement authentication nor route security for you. Once your authentication is implemented, along with your defined security, users will be able to successfully authenticate and interact with your API using the user interfaces of the documentation. + +### Development +In order to start development run: +``` +npm i +npm run prepare:swagger-ui +``` + +So that [swagger-ui](https://github.com/swagger-api/swagger-ui) static folder will be generated for you. + ## Acknowledgements diff --git a/examples/dynamic.js b/examples/dynamic.js index b6261385..774475ba 100644 --- a/examples/dynamic.js +++ b/examples/dynamic.js @@ -2,7 +2,7 @@ const fastify = require('fastify')() -fastify.register(require('./index'), { +fastify.register(require('../index'), { swagger: { info: { title: 'Test swagger', diff --git a/package.json b/package.json index c2d37ef8..90aac33e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "Generate Swagger files automatically for Fastify.", "main": "index.js", "scripts": { - "test": "standard && tap test/*.js" + "prepare:swagger-ui": "node prepare-swagger-ui", + "test": "standard && tap test/*.js", + "prepublish": "npm run prepare:swagger-ui" }, "repository": { "type": "git", @@ -22,12 +24,15 @@ "homepage": "https://github.com/fastify/fastify-swagger#readme", "devDependencies": { "fastify": "^1.1.1", + "fs-extra": "^5.0.0", "standard": "^11.0.0", "swagger-parser": "^4.0.2", - "tap": "^11.1.0" + "tap": "^11.1.0", + "swagger-ui-dist": "3.13.0" }, "dependencies": { "fastify-plugin": "^0.2.2", + "fastify-static": "^0.8.0", "js-yaml": "^3.10.0" }, "standard": { diff --git a/prepare-swagger-ui.js b/prepare-swagger-ui.js new file mode 100644 index 00000000..612a7515 --- /dev/null +++ b/prepare-swagger-ui.js @@ -0,0 +1,39 @@ +const fs = require('fs') +const fse = require('fs-extra') +const swaggerUiAssetPath = require('swagger-ui-dist').getAbsoluteFSPath() +const resolve = require('path').resolve + +fse.emptyDirSync(resolve('./static')) + +// since the original swagger-ui-dist folder contains non UI files +const filesToCopy = ['favicon-16x16.png', + 'favicon-32x32.png', + 'index.html', + 'oauth2-redirect.html', + 'swagger-ui-bundle.js', + 'swagger-ui-bundle.js.map', + 'swagger-ui-standalone-preset.js', + 'swagger-ui-standalone-preset.js.map', + 'swagger-ui.css', + 'swagger-ui.css.map', + 'swagger-ui.js', + 'swagger-ui.js.map'] +filesToCopy.forEach(filename => { + fse.copySync(`${swaggerUiAssetPath}/${filename}`, resolve(`./static/${filename}`)) +}) + +const newIndex = fs.readFileSync(resolve('./static/index.html'), 'utf8') + .replace('window.ui = ui', `window.ui = ui + + function resolveUrl (url) { + const anchor = document.createElement('a') + anchor.href = url + return anchor.href + }`) + .replace( + /url: "(.*)",/, + `url: resolveUrl('./json'), + oauth2RedirectUrl: resolveUrl('./oauth2-redirect.html'),` + ) + +fse.writeFileSync(resolve('./static/index.html'), newIndex) diff --git a/routes.js b/routes.js index f5e0af1e..f47c1e89 100644 --- a/routes.js +++ b/routes.js @@ -1,23 +1,8 @@ 'use strict' const fp = require('fastify-plugin') -const readFileSync = require('fs').readFileSync const resolve = require('path').resolve -const files = { - 'index.html': {type: 'text/html'}, - 'oauth2-redirect.html': {type: 'text/html'}, - 'swagger-ui.css': {type: 'text/css'}, - 'swagger-ui.css.map': {type: 'application/json'}, - 'swagger-ui-bundle.js': {type: 'application/javascript'}, - 'swagger-ui-bundle.js.map': {type: 'application/json'}, - 'swagger-ui-standalone-preset.js': {type: 'application/javascript'}, - 'swagger-ui-standalone-preset.js.map': {type: 'application/json'} -} -Object.keys(files).forEach(filename => { - files[filename].contents = readFileSync(resolve(__dirname, 'static', filename), 'utf8') -}) - function fastifySwagger (fastify, opts, next) { fastify.route({ url: '/documentation/json', @@ -43,28 +28,15 @@ function fastifySwagger (fastify, opts, next) { url: '/documentation', method: 'GET', schema: { hide: true }, - handler: (request, reply) => reply.redirect(request.raw.url + '/') + handler: (request, reply) => reply.redirect('./documentation/') }) - fastify.route({ - url: '/documentation/:file', - method: 'GET', - schema: { hide: true }, - handler: sendStaticFiles + // serve swagger-ui with the help of fastify-static + fastify.register(require('fastify-static'), { + root: resolve('./static'), + prefix: `/documentation/` }) - function sendStaticFiles (req, reply) { - if (!req.params.file) { - const file = files['index.html'] - reply.type(file.type).send(file.contents) - } else if (files.hasOwnProperty(req.params.file)) { - const file = files[req.params.file] - reply.type(file.type).send(file.contents) - } else { - return reply.code(404).send(new Error('Not found')) - } - } - next() } diff --git a/static/index.html b/static/index.html deleted file mode 100644 index 18bc35a4..00000000 --- a/static/index.html +++ /dev/null @@ -1,99 +0,0 @@ - - - -
- ->>u&mn;if(_!==f>>>u&mn)break;_&&(l+=(1<i&&(c=c.removeBefore(r,u,a-l)),c&&f
a&&(a=c.size),o(u)||(c=c.map(function(e){return K(e)})),i.push(c)}return a>e.size&&(e=e.setSize(a)),Pe(e,t,i)}function $e(e){return es)return w();var e=i.next();return r||t===bn?e:t===_n?E(t,u-1,void 0,e):E(t,u-1,e.value[1],e)})},c}function dt(e,t,n){var r=Dt(e);return r.__iterateUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterate(r,i);var a=0;return e.__iterate(function(e,i,s){return t.call(n,e,i,s)&&++a&&r(e,i,o)}),a},r.__iteratorUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterator(r,i);var a=e.__iterator(xn,i),s=!0;return new x(function(){if(!s)return w();var e=a.next();if(e.done)return e;var i=e.value,u=i[0],c=i[1];return t.call(n,c,u,o)?r===xn?e:E(r,u,c,e):(s=!1,w())})},r}function mt(e,t,n,r){var i=Dt(e);return i.__iterateUncached=function(i,o){var a=this;if(o)return this.cacheResult().__iterate(i,o);var s=!0,u=0;return e.__iterate(function(e,o,c){if(!s||!(s=t.call(n,e,o,c)))return u++,i(e,r?o:u-1,a)}),u},i.__iteratorUncached=function(i,o){var a=this;if(o)return this.cacheResult().__iterator(i,o);var s=e.__iterator(xn,o),u=!0,c=0;return new x(function(){var e,o,l;do{if(e=s.next(),e.done)return r||i===bn?e:i===_n?E(i,c++,void 0,e):E(i,c++,e.value[1],e);var p=e.value;o=p[0],l=p[1],u&&(u=t.call(n,l,o,a))}while(u);return i===xn?e:E(i,o,l,e)})},i}function vt(e,t){var r=a(e),i=[e].concat(t).map(function(e){return o(e)?r&&(e=n(e)):e=r?L(e):z(Array.isArray(e)?e:[e]),e}).filter(function(e){return 0!==e.size});if(0===i.length)return e;if(1===i.length){var u=i[0];if(u===e||r&&a(u)||s(e)&&s(u))return u}var c=new P(i);return r?c=c.toKeyedSeq():s(e)||(c=c.toSetSeq()),c=c.flatten(!0),c.size=i.reduce(function(e,t){if(void 0!==e){var n=t.size;if(void 0!==n)return e+n}},0),c}function yt(e,t,n){var r=Dt(e);return r.__iterateUncached=function(r,i){function a(e,c){var l=this;e.__iterate(function(e,i){return(!t||c=Vn)return Te(e,h,c,s,d);if(l&&!d&&2===h.length&&Se(h[1^p]))return h[1^p];if(l&&d&&1===h.length&&Se(d))return d;var m=e&&e===this.ownerID,v=l?d?c:c^u:c|u,y=l?d?Ne(h,p,d,m):Re(h,p,m):je(h,p,d,m);return m?(this.bitmap=v,this.nodes=y,this):new de(e,v,y)},me.prototype.get=function(e,t,n,r){void 0===t&&(t=oe(n));var i=(0===e?t:t>>>e)&mn,o=this.nodes[i];return o?o.get(e+fn,t,n,r):r},me.prototype.update=function(e,t,n,r,i,o,a){void 0===n&&(n=oe(r));var s=(0===t?n:n>>>t)&mn,u=i===vn,c=this.nodes,l=c[s];if(u&&!l)return this;var p=ke(l,e,t+fn,n,r,i,o,a);if(p===l)return this;var h=this.count;if(l){if(!p&&--h5?c-5:0),p=5;p5?c-5:0),p=5;ps&&(n=s-u),c=n;c>=0;c--){for(var p=!0,h=0;hi&&(r=i):r=i;var o=t.length;if(o%2!=0)throw new TypeError("Invalid hex string");r>o/2&&(r=o/2);for(var a=0;a