Sigh
multi-process expressive build system for the web and node.js, built using baconjs observables
Install / Use
/learn @sighjs/SighREADME
sigh

Sigh is a declarative functional reactive build system for the web and io.js/node.js.
Sigh combines the best features of the best asset pipelines with unique features including best speed by delegating tasks to multiple processes and perfect source maps even in production builds. With sigh sub-second incremental production rebuilds are a reality, including source map support allowing you to debug production issues happening in minified transpiled source against the original code.
- Pipelines are written in JavaScript with a very neat tree-based syntax, no more grunt spaghetti or verbose gulp files: plumber.
- Supports gulp plugins gulp.
- Uses Functional Reactive Programming via bacon.js, your asset pipelines are bacon streams (plumber uses Microsoft's rxjs, gulp uses node's built-in stream API).
- Support source maps at every stage of the pipeline: plumber and gulp (see gulp issue).
- Schedules work over multiple CPU cores to reduce build times and make better use of available processing resources.
- Caches all data in memory where possible rather than the filesystem: gulp.
- Easy to write plugins in a small number of lines of code: gobble.
- Includes a plugin generator (
sigh -p plugin-name) that asks user about their plugin and generates a scaffolded project ready fornpm publish. - Support watching files and updating the pipeline as files change: plumber (and gulp when coupled with a couple of extra plugins). No special code or plugins are necessary for file watching, just use the
-wflag. - Support incremental rebuilds (only perform the minimum work necessary on file changes): broccoli.
- Inputs are based on simple glob expressions. Recursive glob expressions can be used when you want to speak in terms of directory trees rather than files.
- Supports
n:n,n:1and1:noperations: broccoli. The stream payload is an array of objects each representing fileupdate,addandremoveevents,1:1plugins emit and consume single element arrays. - Sigh has automated tests (using mocha/chai) that cover all functionality.
Check out this presentation about JavaScript build systems and sigh.
Using sigh
Install sigh-cli globally:
% sudo npm install -g sigh-cli
Install sigh and sigh/gulp plugins in your project:
% npm install --save-dev sigh sigh-babel sigh-mocha gulp-uglify
Write a file called sigh.js (or Sigh.js) and put it in the root of the project:
// To use a plugin it must be declared as a global variable, some plugins are
// built-in and others are loaded by scanning package.json for entries
// beginning with "sigh-" or "gulp-".
var merge, glob, concat, write, env, pipeline
var uglify, mocha, babel
module.exports = function(pipelines) {
pipelines['build-source'] = [
merge(
[ glob('src/**/*.js'), babel() ],
glob('vendor/*.js', 'bootstrap.js')
),
debounce(500),
concat('combined.js'),
env(uglify(), ['production', 'staging']),
write('build/assets')
]
pipelines['build-tests'] = [
glob({ basePath: 'test' }, '*.js'),
babel(),
write('build/test')
]
pipelines.alias.build = ['build-source', 'build-tests']
pipelines['tests-run'] = [
pipeline('build-source', 'build-tests'),
debounce(500),
mocha({ files: 'lib/**/*.spec.js' })
]
}
The pipeline build-source globs files matching src/**/*.js (recursive glob) and transpiles them with babel, this transpiled output is concatenated together with the files matching the glob pattern vendor/*.js followed by the file bootstrap.js (concat operators sort files by the depth-first index of the source stream that produced their untransformed content). The concatenated resource is uglified (using gulp-uglify) but only during builds for production and staging environments. The resulting file is written to the directory build/assets.
The pipeline build-tests takes the files in test, compiles them with babel and writes each compiled file to the directory build/test. Each file's path relative to its basePath becomes its offset within the output directory, in this case only the filename is used.
The pipeline tests-run runs mocha when either the build-tests or build-source pipelines complete. tests-run is delayed until neither pipeline completes for 500ms to avoid wasting CPU time.
Running sigh -w would compile all the files then watch the directories and files matching the glob patterns for changes. Each plugin caches resources and only recompiles the files that have changed.
sigh plugins are injected into the variables defined at the top of the file. Some of the plugins are built-in (for now) and others are found by scanning package.json for dependency and devDependency entries of the format sigh-*. Sigh also searches for plugins of the format gulp-* and adapts them to work with sigh.
Running sigh
Running sigh with no arguments will run all pipelines.
% sigh
Compile all pipelines and then watch files for changes compiling those that have changed:
% sigh -w
Compile/watch only the specified pipeline (with the sigh.js shown above the source and tests would be compiled but the tests would never be run).
% sigh -w build-source build-tests
This is equivalent to using the alias defined in sigh.js:
% sigh -w build
It is also possible to create pipelines on the pipeline.explicit object that only run if specifically requested:
pipelines.explicit['tests-run'] = mocha({ files: 'lib/**/*.spec.js' })
This pipeline would only run if sigh tests-run is used but not with sigh.
Built-in plugins
glob
The glob plugin takes a list of glob expressions as arguments starting with an optional object containing options.
module.exports = function(pipelines) {
pipelines.js = [
glob('test/*.js', 'src/**/*.js', 'bootstrap.js'),
write('build')
]
}
The glob plugin also forwards input events down the stream:
module.exports = function(pipelines) {
pipelines.js = [
glob('test/*.js'),
glob('src/**/*.js'), // forwards glob events from test directory
write('build')
]
}
options
-
basePath: restricts the glob to operate within basePath and also attaches the property to all resources (affecting their projectPath field).
glob({ basePath: 'src' }, '*.js') // similar to glob('src/*.js') -
debounce: Debounce file updates, defaults to 120 (milliseconds). Ideally it should not be set lower than 120, this interval is also used to iron out bad events reported by the underlying file watching plugin Sigh uses.
glob({ debounce: 500 }, '*.js') -
encoding: Set
encodingattribute of all generated events to this, this attribute is used by thewriteplugin.glob({ encoding: 'binary' }, '*.png')
sigh does not curretly support events representing directories, please avoid globbing directory paths for now.
write
The write plugin is responsible for writing data to the filesystem. It adds files corresponding to Event objects with type add, updates files for events with type change and removes files corresponding to events with type remove. The output path of each file is determined by prefixing its projectPath with the argument to write. Operations that produce events (such as glob) take a basePath option so that the output path can be easily manipulated.
module.exports = function(pipelines) {
pipelines.js = [
glob({ basePath: 'src' }, '**/*.js'),
write('build')
]
}
This pipeline takes all files with the extension js recursively reachable from src and writes each one to build directory (without the src prefix due to basePath).
The write plugin passes events representing the written files down the stream, this is useful in combination with the pipeline plugin.
The clobber option can be used to recursively remove the contents of the output directory when the plugin is initialised:
module.exports = function(pipelines) {
pipelines.js = [
glob({ basePath: 'src' }, '**/*.js'),
write({ clobber: true }, 'build')
]
}
A glob pattern or list of glob patterns (according to node-glob syntax) can be supplied to clobber to restrict which files get removed.
module.exports = function(pipelines) {
pipelines.js = [
glob({ basePath: 'src' }, '**/*.js'),
write({ clobber: '!(jspm_packages|config.js)' }, 'build')
]
}
merge
The merge plugin combines many streams together.
pipelines.js = [
merge(
[ glob({ basePath: 'src' }, '*.js'), babel() ],
[ glob('vendor/*.js'), concat('vendor.js') ],
glob('bootstrap.js')
),
write('build')
]
This would transpile files matching src/*.js using babel and copy them to the directory build. Files matching vendor/*.js will all be concatenated together into a single file at build/vendor.js. The file bootstrap.js will be copied to build without being modified beyond adding a source map comment.
The merge plugin forwards events as they come by default (so if the

