Skip to content

Instantly share code, notes, and snippets.

@0gust1
Last active November 5, 2016 22:18
Show Gist options
  • Save 0gust1/10752096 to your computer and use it in GitHub Desktop.
Save 0gust1/10752096 to your computer and use it in GitHub Desktop.
metalsmith static website build, with livreload
var extname = require('path').extname;
var Metalsmith = require('metalsmith');
var myth = require('myth');
var http = require('http');
var templates = require('metalsmith-templates');
var markdown = require('metalsmith-markdown');
var watch = require('metalsmith-watch');
/**
* Live reload server
*/
var tinylr = require('tiny-lr-fork');
// standard LiveReload port
var port = 35729;
// tinylr(opts) => new tinylr.Server(opts);
tinylr().listen(port, function(err) {
if(err) {
// deal with err
return;
}
console.log('... Tinylr (tiny-lr-fork), listening on port %s ...', port);
})
/**
* Local server, tied with livereload
*/
var connect = require('connect');
var app= connect();
app.use(require('connect-livereload')({
port: 35729
}));
app.use(connect.static(__dirname+'/build'));
http.createServer(app).listen(8080);
/**
* Build.
*/
var metalsmith = Metalsmith(__dirname)
.source('content')
.use(watch)
.use(markdown({
smartypants: true,
gfm: true,
tables: true
}))
.use(templates({
engine: 'swig',
directory: 'templates',
autoescape:false
}))
.use(mythify)
.build(function(err){
if (err) throw err;
});
/**
* mythify plugin.
*
* @param {Object} files
* @param {Metalsmith} metalsmith
* @param {Function} done
*/
function mythify(files, metalsmith, done){
var css = '';
for (var file in files) {
if ('.css' != extname(file)) continue;
css += files[file].contents.toString();
delete files[file];
}
css = myth(css);
files['persoo.css'] = {
contents: new Buffer(css)
};
done();
}
'use strict';
var relative = require('path').relative;
var fs = require('fs');
var path = require('path');
var utf8 = require('is-utf8');
var front = require('front-matter');
var Gaze = require('gaze').Gaze;
var chalk = require('chalk');
var request = require('request');
var watching = {};
/**
* Metalsmith isn't expsing its file reading method,
* so we need to reimplement it here. I hope in the
* future metalsmith will provide an interface for this.
*
* @param {Object} metalsmith a metalsmith instance.
* @return {Function}
*/
var rebuilder = function ( metalsmith ) {
return function(filepath, name){
// metalsmith isn't exposing the `read()` function
// reimplement it on our own.
fs.readFile(filepath, function(err, buffer){
var files = {};
files[name] = { contents : buffer };
if ( utf8(buffer) ){
var parsed = front(buffer.toString());
files[name] = parsed.attributes;
files[name].contents = new Buffer(parsed.body);
}
// Rerun the plugin-chain only for this one file.
metalsmith.ware.run(files, metalsmith, function(err){
if ( err ) { throw err; }
// metalsmith deletes the output directory when writing files, so we
// need to write the file outselves
for( var file in files ) {
var data = files[file];
var out = path.join(metalsmith.destination(), file);
return fs.writeFile(out, data.contents, function(err) {
if ( err ) { throw err; }
console.log ( chalk.green( '...rebuild' ));
//tell the livereload server which file have changed
request.get("http://localhost:35729/changed?files="+file);
});
}
});
});
};
};
/**
*
*
*
* @param {String} source The source directory
* @param {String} patterns The relative globing parameter
* @param {Function} rebuild metasmith rebuild function
* @param {Function} done metalsmith done callback.
*/
var startWatching = function ( source, patterns, rebuild, done) {
console.log ( chalk.green('Started watching ') + chalk.cyan(patterns));
var gaze = new Gaze(patterns);
gaze.on('ready', function(){
done();
});
gaze.on('all', function ( event, filepath ) {
var name = relative(source, filepath);
fs.lstat(filepath, function(err, stats){
// Ignore errors and directorys
if ( err || stats.isDirectory() === true ) { return; }
console.log ( chalk.cyan( name ) + ' was ' + chalk.green( event ) );
// rebuild the current file `filepath`
// using the metalsmith rebuilder.
rebuild(filepath, name);
});
});
};
/**
* Metalsmith watch plugin.
* This function is bind to an option object to allow
* it do have different options.
*/
var watch = function ( files, metalsmith, done ) {
/*jshint validthis:true */
var source = metalsmith.source();
var globbingSrc = source + '/' + this.pattern;
if ( watching[globbingSrc] === undefined ) {
watching[globbingSrc] = true;
startWatching (source, relative(metalsmith.dir, globbingSrc), rebuilder(metalsmith), done);
} else {
done();
}
};
// Default pattern
var defaults = {
pattern : '**/*'
};
// Expose
module.exports = function ( pattern ) {
if ( arguments.length === 3){
// direct usage like `.use(watch)`
watch.apply(defaults, [].slice.call(arguments));
} else {
// usage with pattern like `.use(watch('**'))`
var options = {};
if ( !!pattern ) {
options.pattern = pattern;
} else {
options.pattern = defaults.pattern;
}
return watch.bind(options);
}
};
@baldore
Copy link

baldore commented Jan 25, 2015

Hey, great work with the patched watcher (You should do a pull request). I'll try it this week with my Metalsmith template. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment