Comparing version 0.8.0 to 0.8.1
// generate - generate blake site | ||
// cd example ; node generate.js | ||
// $ cd example ; node generate.js | ||
@@ -5,0 +5,0 @@ var blake = require('blake') |
@@ -5,5 +5,4 @@ | ||
var LRU = require("lru-cache") | ||
, options = { max: 50 | ||
, maxAge: 1000 * 60 * 3 } | ||
, options = { max: 50, maxAge: 1000 * 60 * 3 } | ||
module.exports = LRU(options) |
@@ -27,3 +27,3 @@ | ||
writer.on('end', function () { | ||
stream.emit('end') | ||
stream.push(null) | ||
if (callback) callback() | ||
@@ -34,3 +34,4 @@ }) | ||
if (entry.type === 'File') { | ||
stream.push(path.join(target, entry.path.split(source)[1] || '') + '\n') | ||
var p = path.join(target, entry.path.split(source)[1] || '') | ||
stream.push(p + '\n') | ||
} else { | ||
@@ -37,0 +38,0 @@ entry.on('entry', push) |
@@ -14,3 +14,3 @@ | ||
stream._transform = function (chunk, encoding, callback) { | ||
stream._transform = function (chunk, encoding, cb) { | ||
var filename = chunk.toString() | ||
@@ -20,3 +20,3 @@ read(filename, function (err, item) { | ||
stream.push(item.path) | ||
callback(err) | ||
cb(err) | ||
}) | ||
@@ -26,5 +26,5 @@ }) | ||
function process (item, callback) { | ||
function process (item, cb) { | ||
if (!item.bake) { | ||
callback(new Error('Undefined bake function for ' + item.name)) | ||
cb(new Error('Undefined bake function for ' + item.name)) | ||
return | ||
@@ -38,3 +38,3 @@ } | ||
} | ||
write(item.path, result, callback) | ||
write(item.path, result, cb) | ||
}) | ||
@@ -41,0 +41,0 @@ } |
@@ -0,1 +1,2 @@ | ||
// getReader - return stream of filenames | ||
@@ -9,10 +10,7 @@ | ||
var reader | ||
if (filenames.length) { | ||
var items = [] | ||
filenames.forEach(function (filename) { | ||
items.push({ path:filename }) | ||
}) | ||
reader = readArray(items) | ||
@@ -22,4 +20,3 @@ } else { | ||
} | ||
return reader | ||
} |
@@ -7,21 +7,22 @@ | ||
, fstream = require('fstream') | ||
, Stream = require('stream').Stream | ||
, Writable = require('stream').Writable | ||
, hash = require('./hash.js') | ||
, cache = require('./cache.js') | ||
, me | ||
, StringDecoder = require('string_decoder').StringDecoder | ||
var me = null | ||
module.exports = function (props) { | ||
if (me) return me | ||
me = {} | ||
me.read = function (path, callback) { | ||
me = Object.create(null) | ||
me.read = function (path, cb) { | ||
if (!path) { | ||
callback(new Error('No Path')) | ||
cb(new Error('No Path')) | ||
return | ||
} | ||
fs.stat(path, function (err, stats) { | ||
fs.stat(path, function (er, stats) { | ||
if (stats.isDirectory()) { | ||
readDirectory(path, callback) | ||
readDirectory(path, cb) | ||
} else { | ||
readFile(path, callback) | ||
readFile(path, cb) | ||
} | ||
@@ -31,26 +32,21 @@ }) | ||
function readFile (filename, callback) { | ||
var decoder = new StringDecoder('utf8') | ||
function readFile (filename, cb) { | ||
var key = hash(filename) | ||
, cached = cache.get(key) | ||
if (cached) { | ||
callback(null, cached) | ||
if ((cached = cache.get(key))) { | ||
cb(null, cached) | ||
return | ||
} | ||
fs.readFile(filename, function (err, data) { | ||
var item = getItem(props, filename, data.toString()) | ||
fs.readFile(filename, function (er, data) { | ||
var item = getItem(props, filename, decoder.write(data)) | ||
cache.set(key, item) | ||
return callback(err, item) | ||
return cb(er, item) | ||
}) | ||
} | ||
function readDirectory(path, callback) { | ||
function readDirectory(path, cb) { | ||
var reader = fstream.Reader({ path: path }) | ||
stream = new Stream() | ||
stream = new Writable() // pseudo | ||
, items = [] | ||
stream.writeable = true | ||
stream.readable = false | ||
stream.add = function (entry) { | ||
@@ -61,10 +57,4 @@ if (entry.type === 'Directory') { | ||
} | ||
readFile(entry.path, function (err, item) { | ||
var seen = items.some(function (it) { | ||
return it.path === item.path | ||
}) | ||
// not sure why we get repeats here | ||
if (!seen) items.push(item) | ||
readFile(entry.path, function (er, item) { | ||
items.push(item) | ||
reader.resume() | ||
@@ -74,14 +64,8 @@ }) | ||
} | ||
stream.end = function () { | ||
callback(null, items) | ||
cb(null, items) | ||
} | ||
reader.pipe(stream) | ||
} | ||
return me | ||
} | ||
{ | ||
"name": "blake", | ||
"version": "0.8.1", | ||
"description": "Simple, blog aware infrastructure to generate static sites", | ||
"version": "0.8.0", | ||
"homepage": "http://michaelnisi.github.com/blake/", | ||
"main": "index.js", | ||
"directories": { | ||
"lib": "lib", | ||
"example": "example", | ||
"test": "test" | ||
}, | ||
"scripts": { | ||
"test": "tap test/*.js" | ||
}, | ||
"repository": { | ||
@@ -10,2 +18,5 @@ "type": "git", | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/michaelnisi/blake/issues" | ||
}, | ||
"keywords": [ | ||
@@ -18,14 +29,5 @@ "blog", | ||
], | ||
"main": "index.js", | ||
"bin": { | ||
"blake": "bin/cli.js" | ||
}, | ||
"directories": { | ||
"lib": "lib", | ||
"example": "example", | ||
"test": "test" | ||
}, | ||
"scripts": { | ||
"test": "tap test/*.js" | ||
}, | ||
"dependencies": { | ||
@@ -49,3 +51,2 @@ "mkdirp": "0.3.x", | ||
}, | ||
"license": "MIT", | ||
"author": { | ||
@@ -55,3 +56,4 @@ "name": "Michael Nisi", | ||
"url": "http://michaelnisi.com" | ||
} | ||
}, | ||
"license": "MIT" | ||
} |
352
README.md
@@ -10,6 +10,6 @@ # blake - generate site | ||
## CLI Usage | ||
blake source_directory target_directory | ||
blake source_directory target_directory source_file ... | ||
``` | ||
blake source_directory target_directory | ||
blake source_directory target_directory source_file ... | ||
``` | ||
In the first synopsis form, `blake` writes all files generated from input data in the `source_directory` to the `target_directory`. In the second synopsis form, output is generated from the specified source files only. | ||
@@ -20,11 +20,31 @@ | ||
### Generate from directory | ||
```js | ||
var blake = require('blake') | ||
, source = 'blake-site' | ||
, target = '/tmp/blake-site' | ||
, join = require('path').join | ||
, Reader = require('fstream').Reader | ||
, props = { path:join(source, 'data') } | ||
, cop = require('cop') | ||
var blake = require('blake') | ||
, source = 'blake-site' | ||
, target = '/tmp/blake-site' | ||
, join = require('path').join | ||
, Reader = require('fstream').Reader | ||
, props = { path:join(source, 'data') } | ||
, cop = require('cop') | ||
new Reader(props) | ||
.pipe(cop('path')) | ||
.pipe(blake(source, target)) | ||
.pipe(cop(function (filename) { return filename + '\n' })) | ||
.pipe(process.stdout) | ||
``` | ||
### Copy static resources and generate from directory | ||
```js | ||
var blake = require('blake') | ||
, join = require('path').join | ||
, source = join(process.cwd(), './blake-site') | ||
, target = '/tmp/blake-site' | ||
, Reader = require('fstream').Reader | ||
, props = { path:join(source, 'data') } | ||
, cop = require('cop') | ||
, copy = require('../lib/copy.js') | ||
copy(join(source, 'resources'), target) | ||
.on('error', console.error) | ||
.on('end', function () { | ||
new Reader(props) | ||
@@ -35,108 +55,74 @@ .pipe(cop('path')) | ||
.pipe(process.stdout) | ||
### Copy static resources and generate from directory | ||
var blake = require('blake') | ||
, source = 'blake-site' | ||
, target = '/tmp/blake-site' | ||
, join = require('path').join | ||
, Reader = require('fstream').Reader | ||
, props = { path:join(source, 'data') } | ||
, cop = require('cop') | ||
, copy = require('blake/lib/copy.js') | ||
copy(join(source, 'resources'), target) | ||
.on('error', console.error) | ||
.on('end', function () { | ||
new Reader(props) | ||
.pipe(cop('path')) | ||
.pipe(blake(source, target)) | ||
.pipe(cop(function (filename) { return filename + '\n' })) | ||
.pipe(process.stdout) | ||
}) | ||
}) | ||
.pipe(process.stdout) | ||
``` | ||
### Generate from files | ||
```js | ||
var blake = require('blake') | ||
, cop = require('cop') | ||
, readArray = require('event-stream').readArray | ||
, filenames = ['first/file', 'second/file', 'third/file'] | ||
, source = 'source_directory' | ||
, target = 'target_directory' | ||
var blake = require('blake') | ||
, cop = require('cop') | ||
, readArray = require('event-stream').readArray | ||
, filenames = ['first/file', 'second/file', 'third/file'] | ||
, source = 'source_directory' | ||
, target = 'target_directory' | ||
readArray(filenames) | ||
.pipe(blake(source, target)) | ||
.pipe(cop(function (filename) { return filename + '\n' })) | ||
.pipe(process.stdout) | ||
readArray(filenames) | ||
.pipe(blake(source, target)) | ||
.pipe(cop(function (filename) { return filename + '\n' })) | ||
.pipe(process.stdout) | ||
``` | ||
### Generate and push to S3 | ||
Since blake returns a Stream that emits the paths of the generated artifacts, we can pipe to [pushup](https://github.com/michaelnisi/pushup), and upload the files directly to S3. | ||
Since the `blake` function returns a [Transform](http://nodejs.org/api/stream.html#stream_class_stream_transform) stream that emits the paths of the generated artifacts, we can pipe to [pushup](https://github.com/michaelnisi/pushup), and upload the files directly to S3. | ||
```js | ||
var resolve = require('path').resolve | ||
, cop = require('cop') | ||
, getProps = require('pushup/lib/getProps') | ||
, blake = require('blake') | ||
, pushup = require('pushup') | ||
, Reader = require('fstream').Reader | ||
, sep = require('path').sep | ||
, source = 'source_directory' | ||
, target = '/tmp/target_directory' | ||
, reader = new Reader({ path:resolve(source, 'data') }) | ||
, props = getProps() | ||
var resolve = require('path').resolve | ||
, cop = require('cop') | ||
, getProps = require('pushup/lib/getProps') | ||
, blake = require('blake') | ||
, pushup = require('pushup') | ||
, Reader = require('fstream').Reader | ||
, sep = require('path').sep | ||
, source = 'source_directory' | ||
, target = '/tmp/target_directory' | ||
, reader = new Reader({ path:resolve(source, 'data') }) | ||
, props = getProps() | ||
process.chdir(target) | ||
reader | ||
.pipe(cop('path')) | ||
.pipe(blake(source, target)) | ||
.pipe(cop(adjustPath)) | ||
.pipe(pushup(props)) | ||
.pipe(process.stdout) | ||
process.chdir(target) | ||
reader | ||
.pipe(cop('path')) | ||
.pipe(blake(source, target)) | ||
.pipe(cop(adjustPath)) | ||
.pipe(pushup(props)) | ||
.pipe(process.stdout) | ||
function adjustPath (p) { | ||
return p.split(sep).slice(3).join(sep) | ||
} | ||
``` | ||
## blake(source, target) | ||
function adjustPath (p) { | ||
return p.split(sep).slice(3).join(sep) | ||
} | ||
The `blake` module exports a single function that returns a [Transform](http://nodejs.org/api/stream.html#stream_class_stream_transform) stream. While writing source filenames to it, you can read target filenames (of written arfifacts) from it. | ||
## Events | ||
- `source` The source directory. | ||
- `target` The target directory. | ||
The blake function returns a readable and writable [Stream](http://nodejs.org/api/stream.html) that emits following events: | ||
### Event: 'data' | ||
function (path) { } | ||
The `data` event emits paths of generated artifacts. | ||
### Event: 'end' | ||
function () { } | ||
Emitted when blake is done. | ||
### Event: 'error' | ||
function (err) { } | ||
Emitted if an error occured. | ||
## Configuration | ||
blake requires a configuration module at `source_directory/config.js`, which exports `paths`, and `views`, a map of generator functions: | ||
`blake` requires a configuration module at `source_directory/config.js`, which exports `paths`, and `views`, a map of generator functions: | ||
```js | ||
exports.paths = { | ||
data: 'data' | ||
, templates: 'templates' | ||
, resources: 'resources' | ||
, posts: 'data/posts' | ||
} | ||
exports.paths = { | ||
data: 'data' | ||
, templates: 'templates' | ||
, resources: 'resources' | ||
, posts: 'data/posts' | ||
} | ||
exports.views = { | ||
'rss.jade': require('./rss.js') | ||
, 'article.jade': require('./article.js') | ||
, 'home.jade': require('./home.js') | ||
, 'about.jade': require('./about.js') | ||
, 'archive.jade': require('./archive.js') | ||
} | ||
exports.views = { | ||
'rss.jade': require('./rss.js') | ||
, 'article.jade': require('./article.js') | ||
, 'home.jade': require('./home.js') | ||
, 'about.jade': require('./about.js') | ||
, 'archive.jade': require('./archive.js') | ||
} | ||
``` | ||
The `paths` object defines input paths, with two required directories: `data` and `templates`. From `data` blake loads general input data, `templates` is the directory for templates. The two optional directories are `resources` and `posts`. The content of `resources` is copied to the `target_directory' unchanged. The `posts` directory hosts blog posts. | ||
@@ -149,3 +135,3 @@ | ||
At the top of each input file blake expects a JSON string that is interpreted as header providing transformation parameters. Besides it can contain additional user defined data—the `item` parameter, passed to the view functions, provides a reference to the raw header. Input data for a blog entry could look like so: | ||
```js | ||
{ | ||
@@ -158,4 +144,4 @@ "title": "Example", | ||
The content of the example article. | ||
<The content of the example article.> | ||
``` | ||
The end of the header is marked by an empty line. Everything that follows is interpreted as content and is passed to the views untouched. | ||
@@ -166,12 +152,12 @@ | ||
JSON at the top of an input file: | ||
{ | ||
"title": "Example", | ||
"description": "An example article", | ||
"template": "article.jade", | ||
"date": "2012-03-21", | ||
"path": "2012/03", | ||
"name": "example" | ||
} | ||
```js | ||
{ | ||
"title": "Example", | ||
"description": "An example article", | ||
"template": "article.jade", | ||
"date": "2012-03-21", | ||
"path": "2012/03", | ||
"name": "example" | ||
} | ||
``` | ||
* `title` is the title of the page (optional) | ||
@@ -187,72 +173,72 @@ * `description` is the description of the page or rather the post (optional) | ||
If you decide to mirror the input paths in your output, you can omit path and name. In that case a typical header of a blog post might look like the following: | ||
{ | ||
"title": "Example", | ||
"description": "An example article", | ||
"template": "article.jade", | ||
"date": "2012-03-21", | ||
} | ||
```js | ||
{ | ||
"title": "Example", | ||
"description": "An example article", | ||
"template": "article.jade", | ||
"date": "2012-03-21", | ||
} | ||
``` | ||
Input data with this header, located at `source_directory/data/posts/2012/03/example.md`, would produce `2012/03/article.html`. | ||
An input file can consist of just a header (without content) to generate, for example, an RSS feed. | ||
{ | ||
"title": "Blog", | ||
"description": "Stuff I write", | ||
"link": "http://my.blog", | ||
"template": "rss.jade", | ||
"name": "rss.xml" | ||
} | ||
```js | ||
{ | ||
"title": "Blog", | ||
"description": "Stuff I write", | ||
"link": "http://my.blog", | ||
"template": "rss.jade", | ||
"name": "rss.xml" | ||
} | ||
``` | ||
## Views | ||
The views—alternative naming would be: transformers, generators, or bakers—are the functions that generate your artifacts; they have the following signature: | ||
function (item, callback) | ||
```js | ||
function (item, callback) | ||
``` | ||
The passed in 'item' provides the input data to generate the artifact (or most likely the page). | ||
Here, for example, an `item` representing a blog post: | ||
{ header: | ||
{ title: 'Static Websites', | ||
description: '...', | ||
template: 'article.jade', | ||
data: Thu May 17 2012 02:00:00 GMT +0200 (CEST), | ||
path: '2012/05', | ||
name: 'static-websites.html' } | ||
body: '...', | ||
paths: | ||
{ target: '/tmp/michaelnisi-site', | ||
resources: '/Users/michael/workspace/michaelnisi/resources', | ||
data: '/Users/michael/workspace/michaelnisi/data', | ||
templates: '/Users/michael/workspace/michaelnisi/templates', | ||
posts: '/Users/michael/workspace/michaelnisi/data/posts' }, | ||
title: 'Static Websites', | ||
name: 'static-websites.html', | ||
date: Thu May 17 2012 02:00:00 GMT+0200 (CEST), | ||
templatePath: '/Users/michael/workspace/michaelnisi/templates/article.jade', | ||
path: '/tmp/michaelnisi-site/2012/05/static-websites.html', | ||
link: '2012/05/static-websites', | ||
dateString: 'Thu May 17 2012', | ||
bake: [Function], | ||
template: <Buffer 0a 20 20 20 20 64 69 76 ...> } | ||
```js | ||
{ header: | ||
{ title: 'Static Websites', | ||
description: '...', | ||
template: 'article.jade', | ||
data: Thu May 17 2012 02:00:00 GMT +0200 (CEST), | ||
path: '2012/05', | ||
name: 'static-websites.html' } | ||
body: '...', | ||
paths: | ||
{ target: '/tmp/michaelnisi-site', | ||
resources: '/Users/michael/workspace/michaelnisi/resources', | ||
data: '/Users/michael/workspace/michaelnisi/data', | ||
templates: '/Users/michael/workspace/michaelnisi/templates', | ||
posts: '/Users/michael/workspace/michaelnisi/data/posts' }, | ||
title: 'Static Websites', | ||
name: 'static-websites.html', | ||
date: Thu May 17 2012 02:00:00 GMT+0200 (CEST), | ||
templatePath: '/Users/michael/workspace/michaelnisi/templates/article.jade', | ||
path: '/tmp/michaelnisi-site/2012/05/static-websites.html', | ||
link: '2012/05/static-websites', | ||
dateString: 'Thu May 17 2012', | ||
bake: [Function], | ||
template: <Buffer 0a 20 20 20 20 64 69 76 ...> } | ||
``` | ||
To see a simple example: | ||
git clone git://github.com/michaelnisi/blake.git | ||
cd blake/example | ||
npm install | ||
node generate.js | ||
open /tmp/blake-site/index.html | ||
``` | ||
git clone git://github.com/michaelnisi/blake.git | ||
cd blake/example | ||
npm install | ||
node generate.js | ||
open /tmp/blake-site/index.html | ||
``` | ||
To evaluate a more elaborate example, you might generate my [blog](http://michaelnisi.com), for which I use [Jade](http://jade-lang.com/) and [Markdown](http://daringfireball.net/projects/markdown/): | ||
npm install -g blake | ||
git clone git://github.com/michaelnisi/troubled.git | ||
cd troubled | ||
npm install | ||
blake . /tmp/troubled-site | ||
``` | ||
npm install -g blake | ||
git clone git://github.com/michaelnisi/troubled.git | ||
cd troubled | ||
npm install | ||
blake . /tmp/troubled-site | ||
``` | ||
## Deployment | ||
@@ -264,12 +250,10 @@ | ||
Install with [npm](http://npmjs.org/): | ||
[![npm](https://nodei.co/npm/blake.png?compact=true)](https://npmjs.org/package/blake) | ||
npm install blake | ||
To `blake` from the command-line: | ||
npm install -g blake | ||
To use the CLI (as in the above examples): | ||
``` | ||
npm install -g blake | ||
``` | ||
## License | ||
[MIT License](https://raw.github.com/michaelnisi/blake/master/LICENSE) |
@@ -14,6 +14,2 @@ var test = require('tap').test | ||
copy(source, target, function (err) { | ||
t.ok(fs.statSync(target).isDirectory(), 'should exist') | ||
var reader = fstream.Reader({ path:target }) | ||
var paths = [ | ||
@@ -24,3 +20,8 @@ join(target, 'css', 'style.css') | ||
reader | ||
t.ok(fs.statSync(target).isDirectory(), 'should exist') | ||
paths.forEach(function (p) { | ||
t.ok(fs.statSync(p), 'should exist') | ||
}) | ||
fstream.Reader({ path:target }) | ||
.pipe(cop('path')) | ||
@@ -27,0 +28,0 @@ .pipe(es.writeArray(function (err, lines) { |
Sorry, the diff of this file is not supported yet
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
1
30793
541
252