Permalink
Browse files

Initial commit, it’s doing what it’s supposed to do.

  • Loading branch information...
zachleat committed Nov 27, 2017
0 parents commit 00ad9192605d5d501de6aae193701c5a2297ef2c
@@ -0,0 +1,2 @@
dist/
node_modules/
@@ -0,0 +1,34 @@
# elevenisland
Transform a directory of templates into HTML.
Works with:
* EJS (.ejs)
* Mustache (.mustache)
* Handlebars (.hbs)
* Markdown (.md)
* Haml (.haml)
* Pug (formerly Jade, .pug)
* Nunjucks (.njk)
## Usage
```
elevenisland
# Watch
elevenisland --watch
```
### Advanced
* Modify `data.json` to set global static data available to templates.
* Modify template format whitelist in `config.json`, the first one listed there is the default templating engine (default: `ejs`) and will be used to pre-process `data.json`.
* Markdown doesn’t render `data` and is also pre-processed using the default tempalting engine (default: `ejs`).
## Tests
```
npm run test
```
31 cmd.js
@@ -0,0 +1,31 @@
#!/usr/bin/env node
const watch = require("glob-watcher");
const argv = require( "minimist" )( process.argv.slice(2) );
const TemplateWriter = require("./src/TemplateWriter");
const cfg = require("./config.json");
// argv._ ? argv._ :
const dir = cfg.dir.templates;
let writer = new TemplateWriter(
cfg.templateFormats.map(function(extension) {
return dir + "/*" + extension;
}),
cfg.dataFileName
);
writer.write();
if( argv.watch ) {
console.log( "Watching…" );
var watcher = watch(["./" + cfg.dir.templates + "/**", cfg.dataFileName]);
watcher.on("change", function(path, stat) {
console.log("File changed:", path);
writer.write();
});
watcher.on("add", function(path, stat) {
console.log("File added:", path);
writer.write();
});
}
@@ -0,0 +1,8 @@
{
"dataFileName": "./data.json",
"templateFormats": ["ejs", "md", "hbs", "mustache", "haml", "pug", "njk"],
"dir": {
"templates": "templates",
"output": "dist"
}
}
@@ -0,0 +1,5 @@
{
"title": "title",
"globaltitle": "This is a globaltitle.",
"version": "<%= _package.version %>"
}
@@ -0,0 +1,39 @@
{
"name": "elevenisland",
"version": "1.0.0",
"description": "Transform a directory of templates into HTML.",
"main": "cmd.js",
"scripts": {
"default": "node cmd.js",
"watch": "node cmd.js --watch",
"test": "ava"
},
"author": {
"name": "Zach Leatherman",
"email": "zachleatherman@gmail.com",
"url": "https://zachleat.com/"
},
"bin": {
"elevenisland": "./cmd.js"
},
"devDependencies": {
"ava": "^0.23.0"
},
"dependencies": {
"consolidate": "^0.15.0",
"ejs": "^2.5.7",
"fs-extra": "^4.0.2",
"globby": "^7.1.1",
"glob-watcher": "^4.0.0",
"gray-matter": "^3.1.1",
"hamljs": "^0.6.2",
"handlebars": "^4.0.11",
"markdown-it": "^8.4.0",
"minimist": "^1.2.0",
"mustache": "^2.3.0",
"nunjucks": "^3.0.1",
"parse-filepath": "^1.0.1",
"promise": "^8.0.1",
"pug": "^2.0.0-rc.4"
}
}
@@ -0,0 +1,30 @@
const CFG = require( "../config.json" );
const fs = require("fs-extra");
function Layout( name, dir ) {
this.dir = dir;
this.name = name;
this.filename = this.findFileName();
this.fullPath = this.dir + "/" + this.filename;
}
Layout.prototype.getFullPath = function() {
return this.fullPath;
};
Layout.prototype.findFileName = function() {
let file;
if( !fs.existsSync(this.dir) ) {
throw Error( "Layout directory does not exist: " + this.dir );
}
CFG.templateFormats.forEach(function( extension ) {
let filename = this.name + "." + extension;
if(!file && fs.existsSync( this.dir + "/" + filename)) {
file = filename;
}
}.bind(this));
return file;
};
module.exports = Layout;
@@ -0,0 +1,73 @@
const ejs = require("ejs");
const fs = require("fs-extra");
const parsePath = require('parse-filepath');
const matter = require('gray-matter');
const TemplateRender = require( "./TemplateRender" );
const Layout = require( "./Layout" );
const cfg = require("../config.json");
function Template( path, globalData ) {
this.path = path;
this.inputContent = this.getInput();
this.parsed = parsePath( path );
this.dir = this.parsed.dir.replace( new RegExp( "^" + cfg.dir.templates ), "" );
this.frontMatter = this.getMatter();
this.data = this.mergeData( globalData, this.frontMatter.data );
this.outputPath = this.getOutputPath();
}
Template.prototype.getOutputPath = function() {
return cfg.dir.output + "/" + ( this.dir ? this.dir + "/" : "" ) + this.parsed.name + ".html";
};
Template.prototype.getInput = function() {
return fs.readFileSync(this.path, "utf-8");
};
Template.prototype.getMatter = function() {
return matter( this.inputContent );
};
Template.prototype.mergeData = function( globalData, pageData ) {
let data = {};
for( let j in globalData ) {
data[ j ] = globalData[ j ];
}
for( let j in pageData ) {
data[ j ] = pageData[ j ];
}
return data;
};
Template.prototype.getPreRender = function() {
return this.frontMatter.content;
};
Template.prototype.renderLayout = function(tmpl, data) {
let layoutPath = (new Layout( tmpl.data.layout, cfg.dir.templates + "/_layouts" )).getFullPath();
console.log( "Reading layout " + tmpl.data.layout + ":", layoutPath );
let layout = new Template( layoutPath, {} );
let layoutData = this.mergeData( layout.data, data );
layoutData._layoutContent = this.renderContent( tmpl.getPreRender(), data );
let rendered = layout.renderContent( layout.getPreRender(), layoutData );
if( layout.data.layout ) {
return this.renderLayout( layout, layoutData );
}
return rendered;
};
Template.prototype.renderContent = function( str, data ) {
return ( new TemplateRender( this.path )).getRenderFunction()( str, data );
};
Template.prototype.render = function() {
if( this.data.layout ) {
return this.renderLayout(this, this.data);
} else {
return this.renderContent(this.getPreRender(), this.data);
}
};
Template.prototype.write = function() {
let err = fs.outputFileSync(this.outputPath, this.render());
if(err) {
throw err;
}
console.log( "Writing", this.outputPath );
};
module.exports = Template;
@@ -0,0 +1,46 @@
const parsePath = require('parse-filepath');
const ejs = require( "ejs" );
const md = require('markdown-it')();
const Handlebars = require('handlebars');
const Mustache = require('mustache');
const haml = require('hamljs');
const pug = require('pug');
const nunjucks = require('nunjucks');
function TemplateRender( path ) {
this.parsed = path ? parsePath( path ) : undefined;
}
TemplateRender.prototype.getRenderFunction = function() {
if( !this.parsed || this.parsed.ext === ".ejs" ) {
return ejs.render;
} else if( this.parsed.ext === ".md" ) {
return function(str, data) {
var render = (new TemplateRender()).getRenderFunction();
return md.render(render(str, data)).trim();
};
} else if( this.parsed.ext === ".hbs" ) {
return function(str, data) {
return Handlebars.compile(str)(data).trim();
}
} else if( this.parsed.ext === ".mustache" ) {
return function(str, data) {
return Mustache.render(str, data).trim();
}
} else if( this.parsed.ext === ".haml" ) {
return function(str, data) {
return haml.compile(str)(data).trim();
};
} else if( this.parsed.ext === ".pug" ) {
return function(str, data) {
return pug.compile(str)(data).trim();
};
} else if( this.parsed.ext === ".njk" ) {
return function(str, data) {
nunjucks.configure({ autoescape: false });
return nunjucks.renderString(str, data);
};
}
};
module.exports = TemplateRender;
@@ -0,0 +1,36 @@
const fs = require("fs-extra");
const globby = require('globby');
const Template = require( "./Template" );
const TemplateRender = require( "./TemplateRender" );
const PKG = require("../package.json");
function TemplateWriter(files, globalDataPath) {
this.files = files;
this.globalRenderFunction = (new TemplateRender()).getRenderFunction();
this.globalDataPath = globalDataPath;
this.globalData = this.mergeDataImports(this.readJsonAsTemplate(globalDataPath));
}
TemplateWriter.prototype.mergeDataImports = function(data) {
data._package = PKG;
return data;
};
TemplateWriter.prototype.readJsonAsTemplate = function( path ) {
return JSON.parse( this.globalRenderFunction( fs.readFileSync( path, "utf-8" ), this.mergeDataImports({})));
};
TemplateWriter.prototype.write = function() {
let self = this;
globby(this.files).then(function(templates) {
templates.forEach(function(path) {
console.log( "Reading", path );
let tmpl = new Template( path, self.globalData );
tmpl.write();
});
});
};
module.exports = TemplateWriter;
@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
</head>
<body>
<%- _layoutContent %>
</body>
</html>
@@ -0,0 +1,6 @@
---
layout: default
---
<div id="post">
<%- _layoutContent %>
</div>
@@ -0,0 +1,7 @@
---
layout: post
---
<div id="post2">
This is post2
<%- _layoutContent %>
</div>
@@ -0,0 +1,20 @@
---
title: 'Font Aliasing, or How to Rename a Font in CSS'
author: Zach Leatherman
layout: post2
permalink: /rename-font/
postRank: 4
daysPosted: 152
yearsPosted: 0.4
---
This is a teslkjdfklsjfkl kjsldjfkla
<h1><%= _package.version %></h1>
<h1><%= version %></h1>
<h1><%= title %></h1>
<%= globaltitle %>
<%= daysPosted %>
<%= permalink %>
@@ -0,0 +1,20 @@
---
title: 'Font Aliasing, or How to Rename a Font in CSS'
permalink: /rename-font/
postRank: 4
daysPosted: 152
yearsPosted: 0.4
---
# This is a teslkjdfklsjfkl
## <%= _package.version %>
### <%= version %>
#### <%= title %>
<%= globaltitle %>
<%= daysPosted %>
<%= permalink %>
@@ -0,0 +1,2 @@
<h1>{{_package.version}}</h1>
<p>{{globaltitle}}</p>
No changes.
No changes.
No changes.
Oops, something went wrong.

0 comments on commit 00ad919

Please sign in to comment.