Update build script, template organization, cleanup of the general configs

This commit is contained in:
Thomas Schwery 2016-11-12 15:06:07 +01:00
parent 4dffc26b9c
commit eb6a17ff03
13 changed files with 191 additions and 226 deletions

168
build.js
View file

@ -1,24 +1,26 @@
var metalsmith = require('metalsmith') var metalsmith = require('metalsmith')
var layouts = require('metalsmith-layouts') var layouts = require('metalsmith-layouts')
var markdown = require('metalsmith-markdown') var markdown = require('metalsmith-markdown')
var collections = require('metalsmith-collections') var collections = require('metalsmith-collections')
var pagination = require('metalsmith-pagination') var pagination = require('metalsmith-pagination')
var paths = require('metalsmith-paths') var paths = require('metalsmith-paths')
var filemetadata = require('metalsmith-filemetadata') var filemetadata = require('metalsmith-filemetadata')
var matters = require('metalsmith-matters') var matters = require('metalsmith-matters')
var fs = require('fs'); var fs = require('fs');
var moment = require('moment'); var moment = require('moment');
var _ = require('underscore');
var handlebars = require('handlebars'); var handlebars = require('handlebars');
var hlayouts = require('handlebars-layouts'); var handlebars_layout = require('handlebars-layouts');
handlebars.registerHelper(hlayouts(handlebars)); moment.locale('fr');
handlebars.registerPartial('base', fs.readFileSync('templates/base.hbs', 'utf8'));
handlebars.registerPartial('index', fs.readFileSync('templates/index.hbs', 'utf8')); //--------------------------- Handlebars -----------------------------//
handlebars.registerHelper(handlebars_layout(handlebars));
handlebars.registerHelper('formatDate', function(date) { handlebars.registerHelper('formatDate', function(date) {
return moment(new Date(date)).format('MMMM D, YYYY HH:mm:ss'); return moment(new Date(date)).format('DD MMMM YYYY');
}); });
handlebars.registerHelper('formatTimestamp', function(date) { handlebars.registerHelper('formatTimestamp', function(date) {
@ -54,60 +56,50 @@ function compareWith(v1, v2, operator) {
return itm; return itm;
} }
handlebars.registerHelper('eachSorted', function(context, options) { handlebars.registerHelper('eachSorted', function(context, member, item, order, options) {
var ret = ''; var tot = '';
if (context == undefined) {
return ret; var entries = _.sortBy(context, function(item){
return item[member][item] === undefined? 0 : item[member][item];
})
if (order === 'desc') {
entries = entries.reverse();
} }
var keyList = Object.keys(context); for (var i = 0; i < entries.length; i++) {
tot += options.fn(entries[i]);
}
var sortFun = function(a,b) { return tot;
a = context[a]; });
b = context[b];
if (!a && !b) { handlebars.registerHelper('eachCenter', function(context, number, index, options) {
return 0; if (number > context.length) {
} number = context.length;
if (!a) { }
return -1;
}
if (!b) {
return 1;
}
a = a.data.title; var start = index - number / 2;
b = b.data.title; if (start < 0) {
start = 0;
}
if (b > a) { var end = start + number;
return -1; if (end > context.length) {
} end = context.length;
if (a > b) { start = end - number;
return 1; }
}
return 0;
};
keyList.sort(sortFun).forEach(function(key) {
if (key !== 'metadata') {
ret += options.fn(context[key]);
}
});
return ret;
})
handlebars.registerHelper('eachCond', function(context, member, operator, cond, options) {
var tot = ''; var tot = '';
for (var i = 0; i < context.length; i++) {
var itm = false;
var v1 = context[i][member];
for (var i = start; i < end; i++) {
if (compareWith(v1, cond, operator)) { if (i === index) {
tot += options.inverse(context[i]);
} else {
tot += options.fn(context[i]); tot += options.fn(context[i]);
} }
} }
return tot; return tot;
}); });
@ -119,6 +111,7 @@ handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
} }
}); });
//--------------------------- Build pipeline -------------------------//
metalsmith(__dirname) metalsmith(__dirname)
.source('recettes') .source('recettes')
.metadata(require('./config/metadata')) .metadata(require('./config/metadata'))
@ -141,6 +134,10 @@ metalsmith(__dirname)
} }
}) })
//--------------------- Helper functions -----------------------------//
// Generates an epub file from the list of recettes.
function epubGen() { function epubGen() {
var fs = require('fs'); var fs = require('fs');
var epubGenerator = require('epub-generator'); var epubGenerator = require('epub-generator');
@ -175,33 +172,8 @@ function epubGen() {
} }
} }
var recetteSortByTitle = function(a, b) {
a = a.data.title;
b = b.data.title;
if (!a && !b) {
return 0;
}
if (!a) {
return -1;
}
if (!b) {
return 1;
}
if (b > a) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}
function createCategories() { function createCategories() {
return function(files, metalsmith, done) { return function(files, metalsmith, done) {
var tagList = {}; var tagList = {};
for (var fileName in files) { for (var fileName in files) {
var data = files[fileName]; var data = files[fileName];
@ -238,7 +210,7 @@ function createCategories() {
for (var tag in tagList) { for (var tag in tagList) {
var posts = tagList[tag].map(function(fileName) { var posts = tagList[tag].map(function(fileName) {
return files[fileName]; return files[fileName];
}).sort(recetteSortByTitle); });
var tagPage = files[tag + '/index.md']; var tagPage = files[tag + '/index.md'];
@ -259,9 +231,9 @@ function createCategories() {
} }
} }
// Adds the category to the recette entry based on the parent directory
function addCategory() { function addCategory() {
return function(files, metalsmith, done) { return function(files, metalsmith, done) {
for (var f in files) { for (var f in files) {
if (f.indexOf('.md') > 0 && f.indexOf('index.md') < 0) { if (f.indexOf('.md') > 0 && f.indexOf('index.md') < 0) {
var idx = f.indexOf('/'); var idx = f.indexOf('/');
@ -271,37 +243,12 @@ function addCategory() {
files[f].title = files[f].data.title; files[f].title = files[f].data.title;
} }
} }
return done();
}
}
function debugCollection() {
return function(files, metalsmith, done) {
console.log(files);
for (var i in files) {
console.log(i + ' -> ' + ' (' + files[i].collection + ')');
console.log(files[i]);
}
return done();
}
}
function debugMeta(metaname) {
return function(files, metalsmith, done) {
if (metaname !== undefined) {
console.log(metalsmith.metadata()[metaname]);
} else {
console.log(metalsmith.metadata());
}
return done(); return done();
} }
} }
function copyVendor() { function copyVendor() {
return function(files, metalsmith, done){ return function(files, metalsmith, done){
var styleFiles = fs.readdirSync('styles'); var styleFiles = fs.readdirSync('styles');
for (var i = 0; i < styleFiles.length; i++) { for (var i = 0; i < styleFiles.length; i++) {
var stylePath = 'styles/' + styleFiles[i]; var stylePath = 'styles/' + styleFiles[i];
@ -323,4 +270,3 @@ function copyVendor() {
return done(); return done();
}; };
} }

View file

@ -1,7 +1,31 @@
module.exports = { module.exports = {
recettes: { recettes: {
pattern: '*/*.md', pattern: '*/*.md',
sortBy: 'title' sortBy: function(oa, ob) {
a = oa.data.publication;
b = ob.data.publication;
if (!a && !b) {
return 0;
}
if (!a) {
return -1;
}
if (!b) {
return 1;
}
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
ta = oa.data.title;
tb = ob.data.title;
return (ta < tb) ? -1 : 1;
},
reverse: true
}, },
categories: { categories: {
pattern: '*/index.md', pattern: '*/index.md',

View file

@ -1,32 +1,4 @@
var highlightjs = require('highlight.js');
highlightjs.registerLanguage('procedurelog', function(hljs) {
return {
case_insensitive: false,
keywords: {
keyword: 'return',
literal: 'true false null undefined NaN Infinity',
built_in: 'macroBreak callTask executeAction createNewPage',
assertSuccess: 'SUCCESS',
assertFail: 'FAIL',
info: 'PROCEDURE ENDPROCEDURE SCRIPT INIT',
action: 'ACTION ASSERT'
},
contains: [
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE
]
};
});
var highlighter = function (code) {
var hl = highlightjs.highlightAuto(code, ['procedurelog', 'xml']);
return hl.value;
}
module.exports = { module.exports = {
gfm: true, gfm: true,
tables: true, tables: true
highlight: highlighter
} }

View file

@ -4,5 +4,11 @@ module.exports = {
url: 'none', url: 'none',
authors: 'Anne-Catherine Portmann, Thomas Schwery', authors: 'Anne-Catherine Portmann, Thomas Schwery',
language: 'fr-CH' language: 'fr-CH'
},
partials: {
base: 'partials/base',
recetteList: 'partials/recetteList',
tagList: 'partials/tagList',
paginationList: 'partials/paginationList'
} }
} }

View file

@ -1,11 +1,8 @@
module.exports = { module.exports = {
'collections.recettes': { 'collections.recettes': {
perPage: 15, perPage: 10,
first: 'index.html', first: 'index.html',
layout: 'index.hbs', layout: 'index.hbs',
path: 'page/:num/index.html', path: 'page/:num/index.html',
groupBy: function(data) {
return data.title.normalize('NFD').charAt(0);
}
} }
} }

View file

@ -3,36 +3,34 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"bower": "^1.5.2", "bower": ">=1.5.2",
"epub-generator": "^1.0.1", "epub-generator": ">=1.0.1",
"handlebars": "^3.0.3", "handlebars": ">=3.0.3",
"handlebars-layouts": "^3.1.0", "handlebars-layouts": ">=3.1.0",
"highlight.js": "^9.4.0", "metalsmith": ">=1.0.1",
"metalsmith": "^1.0.1", "metalsmith-collections": ">=0.7.0",
"metalsmith-collections": "^0.7.0", "metalsmith-collections-paginate": ">=2.0.1",
"metalsmith-collections-paginate": "^2.0.1", "metalsmith-filemetadata": ">=1.0.0",
"metalsmith-filemetadata": "^1.0.0", "metalsmith-layouts": ">=1.6.5",
"metalsmith-layouts": "^1.6.5", "metalsmith-markdown": ">=0.2.1",
"metalsmith-markdown": "^0.2.1", "metalsmith-matters": ">=1.2.0",
"metalsmith-matters": "^1.2.0", "metalsmith-pagination": ">=1.0.0",
"metalsmith-pagination": "^1.0.0", "metalsmith-paths": ">=2.1.2",
"metalsmith-paths": "^2.1.2", "metalsmith-permalinks": "^0.5.0",
"moment": "^2.6.0", "moment": ">=2.6.0",
"serve": "^1.4.0" "underscore": "^1.8.3"
}, },
"scripts": { "scripts": {
"serve": "serve build -p ${PORT:-3000}",
"build": "node build.js", "build": "node build.js",
"watch": "npm-watch" "watch": "npm-watch"
}, },
"watch": { "watch": {
"build": { "build": {
"patterns": ["recettes/*/*.md"], "patterns": [
"extensions": "hbs,md" "recettes/*/*.md"
],
"extensions": "hbs,md"
} }
}, },
"license": "MIT", "license": "MIT"
"devDependencies": {
"npm-watch": "^0.1.4"
}
} }

View file

@ -20,51 +20,22 @@
{{#content "content"}} {{#content "content"}}
<div class="content row"> <div class="content row">
<div class="list-recette list col-sm-8"> <div class="list-recette list col-sm-8">
{{#eachSorted pagination.files}} {{#each pagination.files }}
<article class="content"> {{#ifCond type '==' 'recette'}}
<header class="header"> {{> recetteList }}
<h3 class="list-content-title"> {{/ifCond}}
<a href="{{ path.href }}"> {{/ each}}
{{#ifCond type '==' 'recette'}}
<span class="glyphicon glyphicon-apple"></span>
{{else}}
<span class="glyphicon glyphicon-list"></span>
{{/ifCond}}
{{ data.title }}
</a>
</h3>
</header>
<section class="content-article">
{{{ data.description }}}
</section>
</article>
{{/ eachSorted}}
</div> </div>
<div class="list-tags list col-sm-4"> <div class="list-tags list col-sm-4">
<ul class="list-group"> <ul class="list-group">
{{#each tags}} {{#each tags}}
<a href="/{{ @key }}/" class="list-group-item {{#ifCond @key '==' ../tag }}active{{/ifCond }}"> {{> tagList currentTag=../tag}}
<span class="badge">{{ posts.length }}</span>
{{ data.title }}
</a>
{{/each}} {{/each}}
</ul> </ul>
</div> </div>
</div> </div>
<nav class="navigation cf"> <nav class="navigation cf">
{{#if pagination.previous }} {{> paginationList currentPage=pagination.index }}
<a href="{{ pagination.previous.path.href }}" class="btn pull-right">
<i class="glyphicon glyphicon-chevron-right">
</i>Newer&nbsp;&nbsp;</a>
{{/ if}}
{{#each pagination.pages}}
<a href="{{ path.href }}" class="btn">{{ pagination.name }}</a>
{{/each}}
{{#if pagination.next }}
<a href="{{ pagination.next.path.href }}" class="btn pull-left">
<i class="glyphicon glyphicon-chevron-left">
</i>&nbsp;&nbsp;Older</a>
{{/ if}}
</nav> </nav>
{{/ content}} {{/ content}}

View file

@ -0,0 +1,29 @@
{{#if pagination.previous }}
<a href="{{ pagination.previous.path.href }}" class="btn pull-left">
<i class="glyphicon glyphicon-chevron-left"></i>
&nbsp;&nbsp;Newer
</a>
{{else }}
<a href="{{ pagination.previous.path.href }}" class="btn pull-left disabled">
<i class="glyphicon glyphicon-chevron-left"></i>
&nbsp;&nbsp;Newer
</a>
{{/ if}}
{{#eachCenter pagination.pages 10 currentPage}}
<a href="{{ path.href }}" class="btn">{{ pagination.name }}</a>
{{else }}
<a href="{{ path.href }}" class="btn disabled btn-primary">{{ pagination.name }}</a>
{{/eachCenter}}
{{#if pagination.next }}
<a href="{{ pagination.next.path.href }}" class="btn pull-right">
Older&nbsp;&nbsp;
<i class="glyphicon glyphicon-chevron-right"></i>
</a>
{{else }}
<a href="{{ pagination.next.path.href }}" class="btn pull-right disabled">
Older&nbsp;&nbsp;
<i class="glyphicon glyphicon-chevron-right"></i>
</a>
{{/ if}}

View file

@ -0,0 +1,30 @@
<article class="content">
<header class="header">
<div class="header-container row">
<h3 class="list-content-title">
<a href="{{ path.href }}">
<span class="glyphicon glyphicon-apple"></span>
{{ data.title }}
</a>
<small class="header-metadata">
<time datetime="{{ data.publication }}" class="timestamp" title="{{formatDate data.publication }}">{{formatDate data.publication }}</time>
{{ data.time }}
</small>
</h3>
</div>
</header>
<section class="content-article">
<div class="header-container row">
<div class="col-sm-4">
{{{ data.description }}}
</div>
<div class="col-sm-4">
{{#if data.image }}
<div class="recipe-image">
<img src="/{{ category }}/{{ data.image }}" />
</div>
{{/if }}
</div>
</div>
</section>
</article>

View file

@ -0,0 +1,4 @@
<a href="/{{ @key }}/" class="list-group-item {{#ifCond @key '==' currentTag }}active{{/ifCond }}">
<span class="badge">{{ posts.length }}</span>
{{ data.title }}
</a>

View file

@ -51,7 +51,7 @@
{{/each }} {{/each }}
{{#if data.image }} {{#if data.image }}
<div class="recipe-image"> <div class="recipe-image">
<img src="{{ data.image }}" /> <img src="/{{ category }}/{{ data.image }}" />
</div> </div>
{{/if }} {{/if }}

View file

@ -1,4 +1,4 @@
{{#extend "index"}} {{#extend "base"}}
{{#content "title"}} {{#content "title"}}
Recettes • {{ title }} Recettes • {{ title }}
@ -11,26 +11,14 @@
{{{ contents }}} {{{ contents }}}
{{/if }} {{/if }}
<h2>Recettes</h2> <h2>Recettes</h2>
{{#each posts}} {{#eachSorted posts 'data' 'title' 'asc' }}
<article class="content"> {{> recetteList }}
<header class="header"> {{/ eachSorted}}
<h3 class="list-content-title">
<a href="{{ path.href }}"><span class="glyphicon glyphicon-apple"></span>{{ data.title }}</a>
</h3>
</header>
<section class="content-article">
{{{ data.description }}}
</section>
</article>
{{/ each}}
</div> </div>
<div class="list-tags list col-sm-4"> <div class="list-tags list col-sm-4">
<ul class="list-group"> <ul class="list-group">
{{#each tags}} {{#each tags}}
<a href="/{{ @key }}/" class="list-group-item {{#ifCond @key '==' ../tag }}active{{/ifCond }}"> {{> tagList currentTag=../tag}}
<span class="badge">{{ posts.length }}</span>
{{ data.title }}
</a>
{{/each}} {{/each}}
</ul> </ul>
</div> </div>