A library of streaming helpers for tracing, watching and transforming JavaScript files.
npm install galvatron
require('galvatron');
Galvatron is written using a combination of Gulp and Vinyl streams. That said, you don't have to use it with Gulp. You can use it with anything that understands Streams 2 and Vinyl objects.
Galvatron will take any number of source files and trace their dependencies regardless of what module format they're using. This means you can use any combination of AMD, CommonJS or ES2015 modules within your project and Galvatron will figure out the dependency tree and insert each file into the stream.
Take the following files, for example:
src/a.js
import './b';
export default function () {};
src/b.js
define(['./c'], function (c) {
return function () {};
});
src/c.js
var _ = require('underscore');
module.exports = function () {};
node_modules/underscore/index.js
// Underscore source here.
If you used src/a.js
as your entry point, Galvatron would generate a dependency tree from this:
- src/a.js
-- src/b.js
--- src/c.js
---- node_modules/underscore/index.js
And insert them into the stream in the order in which they'd need to be included for concatenation:
node_modules/underscore/index.js
src/c.js
src/b.js
src/a.js
The code to do this might look something like:
var galv = require('galvatron');
var gulp = require('gulp');
gulp.task('dist', function () {
return gulp.src('src/a.js')
.pipe(galv.trace())
.pipe(gulp.dest('dist'));
});
That would trace all modules referenced by src/a.js
regardless of module format and move them to dist
. Elaborating on this example, say we wanted to concatenate the files together. All you'd have to do is pipe something like gulp-concat into the stream:
var galv = require('galvatron');
var gulp = require('gulp');
var gulpConcat = require('gulp-concat');
gulp.task('dist', function () {
return gulp.src('src/a.js')
.pipe(galv.trace())
.pipe(gulpConcat('all.js'))
.pipe(gulp.dest('dist'));
});
Doing so would create a single dist/all.js
with all of your dependencies in their proper order.
Galvatron supports the standard Node path resolution semantics of require()
:
However, it also applies those same semantics to bower_components
folders and their respective bower.json
files.
In the examples above, you saw how we can move modules and concatenate them together. However, you must BYO your own shim for the module format you're using. This isn't necessary if you use the globalize
transform. With the globalize
transform, any module format you're using will be automatically shimmed using unique, deterministic globals.
Just pipe in galv.globalize()
:
var galv = require('galvatron');
var gulp = require('gulp');
var gulpConcat = require('gulp-concat');
gulp.task('dist', function () {
return gulp.src('src/a.js')
.pipe(galv.trace())
.pipe(galv.globalize())
.pipe(gulpConcat('all.js'))
.pipe(gulp.dest('dist'));
});
The benefit of using the globalize
transform is that your code will work anywhere, no matter what module format you use, and no matter what module format your consumers / users are using.
You can even decide that you don't want to concatenate your dependencies, or that you want to split up your batches. Since globalize
creates deterministic global identifiers, if a module in app.js
refers to a module in common.js
, things will just work.
For example, if you wanted to split up your common dependencies from your app code, you could use gulp-filter:
var galv = require('galvatron');
var gulp = require('gulp');
var gulpConcat = require('gulp-concat');
var gulpFilter = require('gulp-filter');
gulp.task('dist', function () {
var filterCommon = gulpFilter('node_modules/**', { restore: true });
var filterApp = gulpFilter('src/**', { restore: true });
return gulp.src('src/a.js')
.pipe(galv.trace())
.pipe(galv.globalize())
// Common dependencies.
.pipe(filterCommon)
.pipe(gulpConcat('common.js'))
.pipe(filterCommon.restore)
// App code.
.pipe(filterApp)
.pipe(gulpConcat('app.js'))
.pipe(filterApp.restore)
// Write.
.pipe(gulp.dest('dist'));
});
That would:
- Trace.
- Globalize all files.
- Concatenate all depenencies in
node_modules
todist/common.js
. - Concatenate all dependencies in
src
todist/app.js
.
It would be up to whomever is consuming these batched files to load dist/common.js
before dist/app.js
, though.
Galvatron knows how to trace ES2015 files, but the Globalizer won't transform them for you. That's because there's more to it than just modules. In order to transpile ES2015 all you have to do is insert your transpiler of choice. For example, Babel:
var galv = require('galvatron');
var gulp = require('gulp');
var gulpBabel = require('gulp-babel');
var gulpConcat = require('gulp-concat');
gulp.task('dist', function () {
return gulp.src('src/index.js')
.pipe(galv.trace())
.pipe(gulpBabel())
.pipe(galv.globalize())
.pipe(gulpConcat('index.js'))
.pipe(gulp.dest('dist'));
});
That would:
- Trace
src/index.js
. - Transpile from ES6 to ES5.
- Globalize.
- Concatenate to
dist/index.js
.
You can also import CSS or Less files from within JavaScript files and Galvatron's tracer will insert the files you import into the stream. It will not, however, trace the Less files' @import
declarations because Less transpilers will do this for you.
src/index.js
import './index.less';
You can use gulp-filter
to insert this into the same stream as your JavaScript files:
var galv = require('galvatron');
var gulp = require('gulp');
var gulpBabel = require('gulp-babel');
var gulpConcat = require('gulp-concat');
var gulpFilter = require('gulp-filter');
var gulpLess = require('gulp-less');
gulp.task('dist', function () {
var filterLess = gulpFilter('{**/*,*}.less', { restore: true });
var filterJs = gulpFilter('{**/*,*}.js', { restore: true });
return gulp.src('src/index.js')
.pipe(galv.trace())
// JS.
.pipe(filterJs)
.pipe(gulpBabel())
.pipe(galv.globalize())
.pipe(gulpConcat('index.js'))
.pipe(filterJs.restore)
// Less.
.pipe(filterLess)
.pipe(gulpLess())
.pipe(gulpConcat('index.css'))
.pipe(filterLess.restore)
// Write.
.pipe(gulp.dest('dist'));
});
That would:
- Trace.
- Transpile, globalize and concat JS to
dist/index.js
. - Transpile and concat Less to
dist/index.css
.
Similar to importing styles, you can also import any file that exists in the file system. All Galvatron does, if it's not a JS file, is insert it into the stream. This allows you to be explicit about what resources a given module requires in order to function:
import './img.png';
This is very useful for scripts that contain a template that may reference that image. Since your dependencies are declared in one spot, your build logic is simplified. For example, a simple custom element:
my-img.js
import './img.png';
import './my-img.less';
export default document.registerElement('my-img', {
prototype: Object.create(window.HTMLElement.prototype, {
createdCallback: {
value: function () {
this.innerHTML = '<img src="img.png">';
}
}
})
});
Handling assets is handled in much the same way as everything else. The paths are just inserted into the stream and you can filter for them and then process them. For example, if you wanted to completely process the above component and put it in dist
:
var galv = require('galvatron');
var gulp = require('gulp');
var gulpBabel = require('gulp-babel');
var gulpConcat = require('gulp-concat');
var gulpFilter = require('gulp-filter');
var gulpImagemin = require('gulp-concat');
var gulpLess = require('gulp-less');
gulp.task('dist', function () {
var filterImg = gulpFilter('my-img.png', { restore: true });
var filterLess = gulpFilter('my-img.less', { restore: true });
var filterJs = gulpFilter('my-img.js', { restore: true });
return gulp.src('my-img.js')
.pipe(galv.trace())
// JS.
.pipe(filterJs)
.pipe(gulpBabel())
.pipe(galv.globalize())
.pipe(gulpConcat('my-img.js'))
.pipe(filterJs.restore)
// Less.
.pipe(filterLess)
.pipe(gulpLess())
.pipe(gulpConcat('my-img.css'))
.pipe(filterLess.restore)
// Images.
.pipe(filterImg)
.pipe(gulpImagemin())
.pipe(filterImg.restore)
// Write.
.pipe(gulp.dest('dist'));
});
There is also watch
helper that is just syntactic sugar around gulp.watch()
.
var galv = require('galvatron');
var gulp = require('gulp');
var gulpBabel = require('gulp-babel');
var gulpConcat = require('gulp-concat');
var gulpFilter = require('gulp-filter');
var gulpLess = require('gulp-less');
gulp.task('dist', function () {
var filterLess = gulpFilter('src/{**/*,*}.less', { restore: true });
var filterJs = gulpFilter('src/{**/*,*}.js', { restore: true });
return gulp.src('src/index.js')
.pipe(galv.trace())
// JS.
.pipe(filterJs)
.pipe(gulpBabel())
.pipe(galv.globalize())
.pipe(gulpConcat('index.js'))
.pipe(filterJs.restore)
// Less.
.pipe(filterLess)
.pipe(gulpLess())
.pipe(gulpConcat('index.css'))
.pipe(filterLess.restore)
// Write.
.pipe(gulp.dest('dist'));
});
gulp.task('dist-watch', function () {
galv.watch('src/**', ['dist']);
});
Galvatron's watcher does several things for you:
- Ensures the task is run immediately.
- Watches files matching your pattern.
- Clears the cache for the files that have changed.
- Re-runs the task when any of the files change.
Sometimes a build can take awhile. If you were watching the build in dev mode and you had to wait for a long running build to complete before you could test or view your changes, that would suck.
One of the things that watch()
does is automatically clear any cache that may have been added for a particular file. This is so that you can cache the output of a plugin. For example, if we were watching that dist
task with the dist-watch
task and found that things were taking too long, all you'd have to do is cache the parts taking awhile.
var galv = require('galvatron');
var gulp = require('gulp');
var gulpBabel = require('gulp-babel');
var gulpConcat = require('gulp-concat');
var gulpFilter = require('gulp-filter');
var gulpLess = require('gulp-less');
gulp.task('dist', function () {
var filterLess = gulpFilter('src/{**/*,*}.less', { restore: true });
var filterJs = gulpFilter('src/{**/*,*}.js', { restore: true });
return gulp.src('src/index.js')
.pipe(galv.trace())
// JS.
.pipe(filterJs)
.pipe(galv.cache('babel', gulpBabel()))
.pipe(galv.cache('globalize', galv.globalize()))
.pipe(gulpConcat('index.js'))
.pipe(filterJs.restore)
// Less.
.pipe(filterLess)
.pipe(galv.cache('less', gulpLess()))
.pipe(gulpConcat('index.css'))
.pipe(filterLess.restore)
// Write.
.pipe(gulp.dest('dist'));
});
gulp.task('dist-watch', function () {
galv.watch('src/**', ['dist']);
});
You can cache whatever you want, just ensure that your watch pattern is set to watch the files that you're caching.
Galvatron supports JSX out of the box. You don't need to do anything to enable it.