Build and serve bundles or single files on-demand with Gulp.
serve-gulp [-v] [--gulpfile gulpfile] [path ...]
Environment variables: PORT
, HOST
serve-gulp will serve files at http://0.0.0.0:80
(configurable) according to the rules in your gulpfile. Only files residing in the paths given are allowed. If no paths are given, it will serve files from the current directory. See Options and How Requests Are Handled.
If you did not specify a gulpfile with --gulpfile
and there is a file named gulpfile.js
in the current directory, that will be used as your gulpfile. If neither is found, serve-gulp has a default internal gulpfile. See Bring Your Own Gulpfile and Default Gulpfile.
Requests with filename all.js
, recursive.js
, all.css
or recursive.css
will activate rollup mode. See How Requests Are Handled.
More details:
- Options
- Default Gulpfile
- Bring Your Own Gulpfile
- How Requests Are Handled
- Server Integrations and NodeJS API
npm install -g jtwb/serve-gulp
To try the included example, clone this repo and run the server in example/ directory like this: PORT=8081 serve-gulp assets/
. Then
try: curl -v localhost:8081/assets/css/site.css
.
~/src/myapp$ npm install -g serve-gulp
~/src/myapp$ sudo serve-gulp assets
[Serve-gulp] listening at { address: '0.0.0.0', family: 'IPv4', port: 80 }
GET /assets/css/all.css 200 ; 43kb
GET /assets/css/site.css 200 ; 5kb
GET /assets/js/components/recursive.js 200 ; 60kb
GET /assets/js/utils.js 200 ; 2kb
CLI Arguments
serve-gulp [-v] [--gulpfile gulpfile] [path ...]
-v, --verbose
yeah--gulpfile gulpfile
specify a custom gulpfile. If no custom gulpfile is given andgulpfile.js
is present in the current directory, that file will be selected implicitly.--basedir path
resolve request paths against this path. Default is.
, the current directory.[path ...]
Incoming requests must refer to directories which contain one or more of these whitelist paths as a prefix. Example: if I runcd /var/www/myapp; serve-gulp assets
then my whitelist paths are[ /var/www/myapp/assets/ ]
. A request toGET /assets/css/site.css
will resolve to/var/www/myapp/assets/css/
, which does contain/var/www/myapp/assets/
as a prefix.
Environment arguments
HOST
listen IP.127.0.0.1
will restrict the server to local requests.0.0.0.0
has no restrictions. Default is0.0.0.0
PORT
listen port. Default is80
. Note that0
will choose a random port which on some operating systems is guaranteed to be unused.
Implicit arguments
- If
gulpfile.js
is present in the current directory and--gulpfile
is not set, the local gulpfile will be used as a custom gulpfile. See Bring Your Own Gulpfile.
The default gulpfile applies these steps
taskname | rollup? | steps |
---|---|---|
jit:css | no | gulp-sass |
jit:*css | yes | gulp-sass | concat |
jit:**css | yes | gulp-sass | concat |
jit:js | no | none |
jit:*js | yes | concat |
jit:**js | yes | concat |
jit:static | no | none |
If you already use Gulp, and you have a gulpfile.js
in the current directory, serve-gulp can use that file.
Criteria Your gulpfile must define all of the tasks shown above in Default Gulpfile. See example/gulpfile.js.
If you can, try aliasing the jit:
tasks to your existing tasks like this:
gulp.task('jit:css', ['css']);
serve-gulp will run your gulp tasks with gulp.src
and gulp.dest
replaced with special just-in-time tools.
request extension | search | taskname | rollup? |
---|---|---|---|
file.css | file.{css,scss,sass} | jit:css | no |
all.css | *.{css,scss,sass} | jit:*css | yes |
recursive.css | **/*.{css,scss,sass} | jit:**css | yes |
file.js | file.js | jit:js | no |
all.js | *.js | jit:*js | yes |
recursive.js | **/*.js | jit:**js | yes |
file.html | file.html | jit:static |
cd /var/www/myapp; serve-gulp assets
- Verify request path is contained in at least one allowed path
allowed_paths := [ /var/www/myapp/assets/ ]
request_path := /var/www/myapp/assets/css/
-> OK
- Convert filename to glob
extension == .css
name ∉ [ all, recursive ]
-> /var/www/myapp/assets/css/site.{css,scss,sass}
- Select gulp task
3.
extension == .css
4.name ∉ [ all, recursive ]
4.-> jit:css
- Run gulp task with
gulp.src(...)
overriden to the glob above andgulp.dest(...)
overriden to serve the result
gulp.task('jit:css', function() {
gulp.src()
.pipe(gulp-sass())
.pipe(gulp.dest())
});
- Verify request path is contained in at least one allowed path
allowed_paths := [ /var/www/myapp/assets/ ]
request_path := /var/www/myapp/etc/passwd
2. Relative paths are resolved against a false root first-> Forbidden
- Verify request path is contained in at least one allowed path
allowed_paths := [ /var/www/myapp/assets/ ]
request_path := /var/www/myapp/assets/css/
-> OK
- Convert filename to glob
extension == .css
name == recursive
-> /var/www/myapp/assets/css/**/*.{css,scss,sass}
- Select gulp task
3.
extension == .css
4.name == recursive
4.-> jit:**css
- Run gulp task with
gulp.src(...)
overriden to the glob above andgulp.dest(...)
overriden to serve the result
gulp.task('jit:**css', function() {
gulp.src()
.pipe(gulp-sass())
.pipe(concat())
.pipe(gulp.dest())
});
In the example above, serve-gulp assets will only serve files from ./assets
relative to the current directory. The first argument to the script specifies the restriction path. Multiple restriction paths are allowed and combined via union.
Relative paths (e.g. /../../../etc/shadow) are first resolved against a fake / before being resolved from the current directory for safety.
Symlinks are handled like so: the realpath
of the request path must contain the realpath
of one or more restriction paths.
var serveGulp = require('serve-gulp');
var path = require('path');
serveGulp.start({
host: '0.0.0.0',
port: '80',
basedir: path.resolve('.'),
restrict: path.resolve('assets'),
gulpfile: path.resolve('./gulpfile.js')
});
If you want to use dynamic port allocation, pass an onBoot callback and fetch the address at that time.
var serveGulp = require('serve-gulp');
var path = require('path');
serveGulp.start({
host: '0.0.0.0',
port: '0',
basedir: path.resolve('.'),
restrict: path.resolve('assets'),
gulpfile: path.resolve('./gulpfile.js')
}, function() {
console.log(this.address());
});
With nginx you could set two upstreams, one mapping ^/assets to serve-gulp on 127.0.0.1:8081 and the other mapping everything else to your web server on 127.0.0.1:8080.
// in the future there will be an nginx conf example here
With the Express.js webserver you could start serve-gulp on a random port and proxy-mount that port to ^/assets.
var express = require('express');
var serveGulp = require('serve-gulp');
var path = require('path');
var app = express();
var GULP_PORT;
app.use('/assets/', function(req, res, next) {
req.pipe(request(GULP_PORT).pipe(res));
});
var onServeGulpStart = function() {
GULP_PORT = this.address();
app.start();
});
serveGulp.start({
port: 0, // random port
host: 'localhost',
basedir: path.resolve('.'),
restrict: path.resolve('assets'),
gulpfile: path.resolve('./gulpfile.js')
});
Or, you could do something similar to the above example with a custom middleware for ExpressJS, uWSGI, Rack, etc.
Yes. Serve-gulp runs one file at a time and captures the output that would be written to disk. Webpack Dev Server runs your whole build in an in-memory FS and serves files from the in-memory FS.
No. Gulp OnDemand Server triggers a full gulp build on each request and does not necessarily serve the latest version of the file.
serve-gulp will always return the freshest version, but only processes the files needed for your
GET /assets/js/all.js
-> gulp.run('js', 'all.js');
-> emit( 'function(){var a,b,c=0;for(a... ' );
Your project:
assets/
├─┬ js/
│ ├─┬ components/
│ │ ├── menu.jsx
│ │ ├── media.jsx
│ | └─┬ shoppingcart/
│ | ├── full.jsx
│ │ └── tiny.jsx
│ └─┬ utils/
│ ├── urls.js
│ └── util.js
└─┬ css/
├── homepage.scss
└── checkout.scss
In your HTML:
<link rel="stylesheet" href="/assets/css/all.css" />
<!--
Includes homepage.scss, checkout.scss compiled to css
-->
<script type="text/javascript" src="/assets/js/components/recursive.js"></script>
<!--
Includes menu.jsx, media.jsx, shoppingcart/full.jsx, shoppingcart/tiny.jsx
compiled to js with browserify and reactify
-->
<script type="text/javascript" src="/assets/js/utils/urls.js"></script>
<script type="text/javascript" src="/assets/js/utils/util.js"></script>
- In "watch mode", (eventually the default for systems with that capability), cache results in-memory and invalidate when any target inode changes.
- Support Memcached host/port instead of in-memory
- Accept fingerprints in filenames and query strings, e.g. file.css?b57b91e893fd6d68ce and file___909fe4bc7387f31fa24a8f.css are the same as file.css
- Send cache-control headers
- Gzip
- Etag
- How do I deploy my assets to production?
- Solution 1: create your own gulpfile.js and run it directly to create your production assets.
- Solution 2: run the asset server in prod. Activate memcached caching. Tools like Google Closure Compiler and Ruby-sass may be difficult or impossible to install on your production environment - if so, consider switching to fully npm-installable options like gulp-uglify (based on Uglify2) and gulp-sass (based on node-sass).