Jekyll + Gulp.js = Metalsmith
Metalsmith(__dirname)
.use(markdown())
.use(sass())
// ...
.destination('./build')
.build();
$ npm install -D metalsmith
.
|– src/
|– content/
|– styles/
|_ scripts/
|– templates/
|_ build.js
var Metalsmith = require('metalsmith');
Metalsmith(__dirname)
.destination('./build')
.build();
This will move everything from __dirname/src
to __dirname/build
src/index.md
---
title: Home
---
##My Page
This is some content!
$ npm install -D metalsmith-markdown
var Metalsmith = require('metalsmith'),
markdown = require('metalsmith-markdown');
Metalsmith(__dirname)
.use(markdown())
.destination('./build')
.build();
$ node build.js
build/index.html
templates/index.hbt
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{{ title }} | Metalsmith Page</title>
<link rel="stylesheet"
href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
</head>
<body>
<div class="main-wrapper">
{{{ contents }}}
</div>
</body>
</html>
---
title: Home
template: index.hbt
---
$ npm install -D metalsmith-templates handlebars
(uses consolidate.js)
build.js
// ...
templates = require('metalsmith-templates');
Metalsmith(__dirname)
.use(markdown())
.use(templates('handlebars'))
// ...
.
|_ src/
|_ contents/
|– posts/
|_ my-frist-post.md
|_ pages/
|_ about.md
src/contents/posts/my-frist-post.md
---
title: My First Post
template: post.hbt
date: 2014-05-19
---
#My First Post
Hey, I'm blogging!
src/contents/pages/about.md
---
title: About
template: page.hbt
---
##About
Awesome, witty content here!
build/contents/pages/about.html
build/contents/posts/my-first-post.html
$ npm install -D metalsmith-collections
build.js
// ...
.use(collections({
blog: {
pattern: '*/posts/*',
oderBy: 'date',
reverse: true
},
pages: {
pattern: '*/pages/*'
}
}))
// ...
templates/home.hbt
<!-- ... -->
<div class="main-wrapper">
{{#each collections.blog}}
<article class="post">
<h3>{{title}}</h3>
<p>{{{contents}}}</p>
</article>
{{/each}}
</div>
<!-- ... -->
$ npm install -D metalsmith-permalinks
build.js
// ...
.use(permalinks({
pattern: ':collection/:title'
}))
// ...
build/pages/about/index.html
build/blog/my-first-post/index.html
{
'path/to/file': {
'title': 'FROM_THE_TITLE_KEY',
'template': 'TEMPLATE_NAME',
'contents': <Buffer()>,
'mode': 'HEX_FILE_PERM_CODE'
}
}
.build(function(err) {
if (err) { throw err; }
});
var plugin = function(opts) {
return function(files, metalsmith, done) {
done();
};
};
var autoTemplate = function(opts) {
var pattern = new RegExp(opts.pattern);
return function(files, metalsmith, done) {
for (var file in files) {
if (pattern.test(file)) {
var _f = files[file];
if (!_f.template) {
_f.template = opts.templateName;
}
}
}
done();
};
};
//...
.use(autoTemplate({
pattern: 'posts',
templateName: 'post.hbt'
}))
//...
var metadata = metalsmith.metadata(),
collections = metadata.collections;
$ npm install -D metalsmith-sass metalsmith-coffee
// ...
.use(sass({
outputStyle: 'compressed'
}))
.use(coffee())
// ...
Metalsmith(__dirname)
.use(collections({
blog: {
pattern: '*/posts/*',
oderBy: 'date',
revers: true
},
pages: {
pattern: '*/pages/*'
}
}))
.use(markdown())
.use(autoTemplate({
pattern: 'posts/',
templateName: 'post.hbt'
}))
.use(permalinks({
pattern: ':collection/:title'
}))
.use(templates('handlebars'))
.destination('./build')
.build(function(err) {
if (err) { throw err; }
});
{
"source": "src",
"destination": "build",
"plugins": {
"metalsmith-collections": {
"blog": {
"pattern": "*/posts/*",
"oderBy": "date",
"revers": true
},
"pages": {
"pattern": "*/pages/*"
}
},
"metalsmith-markdown": true,
"metalsmith-autoTemplate": {
"pattern": "posts/",
"templateName": "post.hbt"
},
"metalsmith-permalinks": ":collection/:title",
"metalsmith-templates": "handlebars"
}
}
var clone = require('clone');
var defaults = require('defaults');
var each = require('async').each;
var front = require('front-matter');
var fs = require('fs-extra');
var Mode = require('stat-mode');
var noop = function(){};
var path = require('path');
var readdir = require('recursive-readdir');
var rm = require('rimraf').sync;
var utf8 = require('is-utf8');
var Ware = require('ware');
/**
* Expose `Metalsmith`.
*/
module.exports = Metalsmith;
/**
* Initialize a new `Metalsmith` builder with a working `dir`.
*
* @param {String} dir
*/
function Metalsmith(dir){
if (!(this instanceof Metalsmith)) return new Metalsmith(dir);
this.dir = path.resolve(dir);
this.ware = new Ware();
this.data = {};
this.source('src');
this.destination('build');
this.clean(true);
}
/**
* Add a `plugin` to the middleware stack.
*
* @param {Function} plugin
* @return {Metalsmith}
*/
Metalsmith.prototype.use = function(plugin){
this.ware.use(plugin);
return this;
};
/**
* Get or set the global `metadata` to pass to templates.
*
* @param {Object} metadata
* @return {Object or Metalsmith}
*/
Metalsmith.prototype.metadata = function(metadata){
if (!arguments.length) return this.data;
this.data = clone(metadata);
return this;
};
/**
* Get or set the source directory.
*
* @param {String} path
* @return {String or Metalsmith}
*/
Metalsmith.prototype.source = function(path){
if (!arguments.length) return this.join(this._src);
this._src = path;
return this;
};
/**
* Get or set the destination directory.
*
* @param {String} path
* @return {String or Metalsmith}
*/
Metalsmith.prototype.destination = function(path){
if (!arguments.length) return this.join(this._dest);
this._dest = path;
return this;
};
/**
* Get or set whether the destination directory will be removed before writing.
* @param {Boolean} clean
* @return {Boolean or Metalsmith}
*/
Metalsmith.prototype.clean = function(clean){
if (!arguments.length) return this._clean;
this._clean = clean;
return this;
};
/**
* Join path `strs` with the working directory.
*
* @param {String} strs...
* @return {String}
*/
Metalsmith.prototype.join = function(){
var strs = [].slice.call(arguments);
strs.unshift(this.dir);
return path.join.apply(path, strs);
};
/**
* Build with the current settings to the dest directory.
*
* @param {Function} fn
*/
Metalsmith.prototype.build = function(fn){
fn = fn || noop;
var self = this;
this.read(function(err, files){
if (err) return fn(err);
self.run(files, function(err, files){
if (err) return fn(err);
self.write(files, function(err){
fn(err, files);
});
});
});
};
/**
* Run a set of `files` through the middleware stack.
*
* @param {Object} files
* @param {Function} fn
*/
Metalsmith.prototype.run = function(files, fn){
this.ware.run(files, this, fn);
};
/**
* Read the source directory, parsing front matter and call `fn(files)`.
*
* @param {Function} fn
* @api private
*/
Metalsmith.prototype.read = function(fn){
var files = {};
var src = this.source();
readdir(src, function(err, arr){
if (err) return fn(err);
each(arr, read, function(err){
fn(err, files);
});
});
function read(file, done){
var name = path.relative(src, file);
fs.stat(file, function(err, stats){
if (err) return done(err);
fs.readFile(file, function(err, buffer){
if (err) return done(err);
var file = {};
if (utf8(buffer)) {
var parsed = front(buffer.toString());
file = parsed.attributes;
file.contents = new Buffer(parsed.body);
} else {
file.contents = buffer;
}
file.mode = Mode(stats).toOctal();
files[name] = file;
done();
});
});
}
};
/**
* Write a dictionary of `files` to the dest directory.
*
* @param {Object} files
* @param {Function} fn
* @api private
*/
Metalsmith.prototype.write = function(files, fn){
var dest = this.destination();
var clean = this.clean();
if (clean) rm(dest);
each(Object.keys(files), write, fn);
function write(file, done){
var data = files[file];
var out = path.join(dest, file);
return fs.outputFile(out, data.contents, function(err){
if (err) done(err);
if (!data.mode) return done();
fs.chmod(out, data.mode, done);
});
}
};
.build()