├── client.js ├── .travis.yml ├── index.html ├── example.js ├── LICENSE ├── .gitignore ├── package.json ├── test.js ├── README.md └── index.js /client.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | document.write('hello world!') 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | - "6" 5 | - "5" 6 | - "4" 7 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fastify = require('fastify')() 4 | 5 | fastify.register(require('./index'), { 6 | entry: './client.js', 7 | html: './index.html' 8 | }) 9 | 10 | fastify.listen(3000, err => { 11 | if (err) throw err 12 | console.log('Server listenting on localhost:', fastify.server.address().port) 13 | }) 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Fastify 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # mac files 61 | .DS_Store 62 | 63 | # vim swap files 64 | *.swp 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-bankai", 3 | "version": "0.3.1", 4 | "description": "Bankai assets compiler for Fastify", 5 | "main": "index.js", 6 | "scripts": { 7 | "deps": "dependency-check . && dependency-check . --extra --no-dev", 8 | "test": "standard && npm run deps && tap test.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/fastify/fastify-bankai.git" 13 | }, 14 | "keywords": [ 15 | "browserify", 16 | "bankai", 17 | "assets", 18 | "html", 19 | "css", 20 | "js", 21 | "fastify", 22 | "compile" 23 | ], 24 | "author": "Tomas Della Vedova - @delvedor (http://delved.org)", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/fastify/fastify-bankai/issues" 28 | }, 29 | "homepage": "https://github.com/fastify/fastify-bankai#readme", 30 | "devDependencies": { 31 | "dependency-check": "^2.9.2", 32 | "fastify": "^0.37.0", 33 | "pre-commit": "^1.2.2", 34 | "shot": "^4.0.4", 35 | "standard": "^10.0.3", 36 | "tap": "^11.0.1" 37 | }, 38 | "dependencies": { 39 | "bankai": "^9.1.0", 40 | "fastify-plugin": "^0.2.1", 41 | "http-gzip-maybe": "^1.0.0", 42 | "pump": "^2.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = require('tap') 4 | const test = t.test 5 | const Fastify = require('fastify') 6 | 7 | test('Should expose a route with the assets', t => { 8 | t.plan(6) 9 | 10 | const fastify = Fastify() 11 | fastify.register( 12 | require('./index'), 13 | { entry: './client.js', watch: false, reload: false } 14 | ) 15 | 16 | fastify.inject({ 17 | url: '/', 18 | method: 'GET' 19 | }, res => { 20 | t.equal(res.statusCode, 200) 21 | t.equal(res.headers['content-type'], 'text/html') 22 | }) 23 | fastify.inject({ 24 | url: '/index.html', 25 | method: 'GET' 26 | }, res => { 27 | t.equal(res.statusCode, 200) 28 | t.equal(res.headers['content-type'], 'text/html') 29 | }) 30 | fastify.inject({ 31 | url: '/bundle.js', 32 | method: 'GET' 33 | }, res => { 34 | t.equal(res.statusCode, 200) 35 | t.equal(res.headers['content-type'], 'application/javascript') 36 | }) 37 | }) 38 | 39 | test('should return 404 if an assets is not found', t => { 40 | t.plan(1) 41 | 42 | const fastify = Fastify() 43 | fastify.register( 44 | require('./index'), 45 | { entry: './client.js', watch: false, reload: false } 46 | ) 47 | 48 | fastify.inject({ 49 | url: '/file.php', 50 | method: 'GET' 51 | }, res => { 52 | t.equal(res.statusCode, 404) 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fastify-bankai 2 | [](http://standardjs.com/) [](https://travis-ci.org/fastify/fastify-bankai) [](https://greenkeeper.io/) 3 | 4 | If you need to compile (browserify style!) your code, this plugin is for you! 5 | Internally it uses [bankai](https://github.com/yoshuawuyts/bankai), so refer to its documentation for the options. 6 | 7 | **fastify-bankai** will automatically live-reload your HTML and 8 | regenerate your bundle whenever your code change. This can be disabled 9 | in test or in production. 10 | 11 | ## Install 12 | ``` 13 | npm i fastify-bankai --save 14 | ``` 15 | 16 | ## Usage 17 | Simply require this plugin, pass the entry file and you are done! 18 | ```js 19 | const fastify = require('fastify')() 20 | 21 | fastify.register(require('fastify-bankai'), { 22 | entry: './client.js' 23 | }) 24 | 25 | fastify.listen(3000, err => { 26 | if (err) throw err 27 | console.log('Server listenting on localhost:', fastify.server.address().port) 28 | }) 29 | ``` 30 | 31 | ### In tests or in production 32 | 33 | If you are including fastify-bankai in any test run or in production, you **must** disable 34 | the automatic watch mode: 35 | 36 | ```js 37 | const fastify = require('fastify')() 38 | 39 | fastify.register(require('fastify-bankai'), { 40 | entry: './client.js', 41 | watch: false 42 | }) 43 | 44 | fastify.listen(3000, err => { 45 | if (err) throw err 46 | console.log('Server listenting on localhost:', fastify.server.address().port) 47 | }) 48 | ``` 49 | 50 | ## Options 51 | 52 | - `entry`: Your application entry point 53 | - `prefix`: prefix all paths served by fastify-bankai with the given 54 | path 55 | 56 | The option object is passed directly to bankai. 57 | 58 | ## Acknowledgements 59 | 60 | This project is kindly sponsored by: 61 | - [nearForm](http://nearform.com) 62 | - [LetzDoIt](http://www.letzdoitapp.com/) 63 | 64 | ## License 65 | 66 | Licensed under [MIT](./LICENSE). 67 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const resolve = require('path').resolve 4 | const bankai = require('bankai') 5 | const gzipMaybe = require('http-gzip-maybe') 6 | const pump = require('pump') 7 | const fp = require('fastify-plugin') 8 | 9 | function assetsCompiler (fastify, opts, next) { 10 | if (!opts.entry) { 11 | return next(new Error('Missing entry file!')) 12 | } 13 | if (typeof opts.entry !== 'string') { 14 | return next(new Error('entry must be a string')) 15 | } 16 | 17 | var ssr 18 | delete opts.prefix 19 | const compiler = bankai(resolve(opts.entry || ''), opts) 20 | compiler.on('error', function (topic, sub, err) { 21 | fastify.setErrorHandler((error, reply) => { 22 | error = err 23 | if (err.pretty) reply.send(err.pretty) 24 | else reply.send(`${topic}:${sub} ${err.message}\n${err.stack}`) 25 | }) 26 | }) 27 | compiler.on('ssr', function (result) { 28 | ssr = result 29 | }) 30 | 31 | fastify.get('/', (req, reply) => { 32 | var url = req.req.url 33 | 34 | if (ssr && ssr.renderRoute) { 35 | ssr.renderRoute(url, function (err, buffer) { 36 | if (err) { 37 | ssr.success = false 38 | ssr.error = err 39 | return sendDocument(url, req.req, reply.res, next) 40 | } 41 | 42 | reply.header('content-type', 'text/html') 43 | gzip(buffer, req.req, reply.res) 44 | }) 45 | } else { 46 | return sendDocument(url, req.req, reply.res, next) 47 | } 48 | 49 | function sendDocument (url, req, res, next) { 50 | compiler.documents(url, function (err, node) { 51 | if (err) { 52 | return compiler.documents('/404', function (err, node) { 53 | if (err) return next() // No matches found, call next 54 | res.statusCode = 404 55 | res.setHeader('content-type', 'text/html') 56 | gzip(node.buffer, req, res) 57 | }) 58 | } 59 | res.setHeader('content-type', 'text/html') 60 | gzip(node.buffer, req, res) 61 | }) 62 | } 63 | }) 64 | 65 | fastify.get('/:file', (req, reply) => { 66 | var filename = req.params.file 67 | 68 | var extension = req.params.file.split('.').pop() 69 | if (extension === 'js') { 70 | compiler.scripts(filename, (err, node) => { 71 | if (err) { 72 | return reply 73 | .code(404) 74 | .send(err.message) 75 | } 76 | reply.header('content-type', 'application/javascript') 77 | gzip(node.buffer, req.req, reply.res) 78 | }) 79 | } else if (extension === 'html') { 80 | compiler.documents(filename, (err, node) => { 81 | if (err) { 82 | return reply 83 | .code(404) 84 | .send(err.message) 85 | } 86 | reply.header('content-type', 'text/html') 87 | gzip(node.buffer, req.req, reply.res) 88 | }) 89 | } else { 90 | reply.redirect(404, '/') 91 | } 92 | }) 93 | 94 | next() 95 | } 96 | 97 | module.exports = fp(assetsCompiler, '>= 0.37.0') 98 | 99 | function gzip (buffer, req, res) { 100 | var zipper = gzipMaybe(req, res) 101 | pump(zipper, res) 102 | zipper.end(buffer) 103 | } 104 | --------------------------------------------------------------------------------