271 lines
7.7 KiB
JavaScript
271 lines
7.7 KiB
JavaScript
var _ = require('lodash');
|
|
var path = require('path');
|
|
var util = require('util');
|
|
var nunjucks = require('nunjucks');
|
|
var I18n = require('i18n-t');
|
|
|
|
var Promise = require('../utils/promise');
|
|
var location = require('../utils/location');
|
|
var fs = require('../utils/fs');
|
|
var defaultFilters = require('../template/filters');
|
|
var conrefsLoader = require('./conrefs');
|
|
var Output = require('./base');
|
|
|
|
// Tranform a theme ID into a plugin
|
|
function themeID(plugin) {
|
|
return 'theme-' + plugin;
|
|
}
|
|
|
|
// Directory for a theme with the templates
|
|
function templatesPath(dir) {
|
|
return path.join(dir, '_layouts');
|
|
}
|
|
|
|
function _WebsiteOutput() {
|
|
Output.apply(this, arguments);
|
|
|
|
// Nunjucks environment
|
|
this.env;
|
|
|
|
// Plugin instance for the main theme
|
|
this.theme;
|
|
|
|
// Plugin instance for the default theme
|
|
this.defaultTheme;
|
|
|
|
// Resources loaded from plugins
|
|
this.resources;
|
|
|
|
// i18n for themes
|
|
this.i18n = new I18n();
|
|
}
|
|
util.inherits(_WebsiteOutput, Output);
|
|
|
|
var WebsiteOutput = conrefsLoader(_WebsiteOutput);
|
|
|
|
// Name of the generator
|
|
// It's being used as a prefix for templates
|
|
WebsiteOutput.prototype.name = 'website';
|
|
|
|
// Load and setup the theme
|
|
WebsiteOutput.prototype.prepare = function() {
|
|
var that = this;
|
|
|
|
return Promise()
|
|
.then(function() {
|
|
return WebsiteOutput.super_.prototype.prepare.apply(that);
|
|
})
|
|
|
|
.then(function() {
|
|
var themeName = that.book.config.get('theme');
|
|
that.theme = that.plugins.get(themeID(themeName));
|
|
that.themeDefault = that.plugins.get(themeID('default'));
|
|
|
|
if (!that.theme) {
|
|
throw new Error('Theme "' + themeName + '" is not installed, add "' + themeID(themeName) + '" to your "book.json"');
|
|
}
|
|
|
|
if (that.themeDefault.root != that.theme.root) {
|
|
that.log.info.ln('build using theme "' + themeName + '"');
|
|
}
|
|
|
|
// This list is ordered to give priority to templates in the book
|
|
var searchPaths = _.chain([
|
|
// The book itself can contains a "_layouts" folder
|
|
that.book.root,
|
|
|
|
// Installed plugin (it can be identical to themeDefault.root)
|
|
that.theme.root,
|
|
|
|
// Is default theme still installed
|
|
that.themeDefault? that.themeDefault.root : null
|
|
])
|
|
.compact()
|
|
.uniq()
|
|
.value();
|
|
|
|
// Load i18n
|
|
_.each(searchPaths.concat().reverse(), function(searchPath) {
|
|
var i18nRoot = path.resolve(searchPath, '_i18n');
|
|
|
|
if (!fs.existsSync(i18nRoot)) return;
|
|
that.i18n.load(i18nRoot);
|
|
});
|
|
|
|
that.env = new nunjucks.Environment(new nunjucks.FileSystemLoader(_.map(searchPaths, templatesPath)));
|
|
|
|
// Add GitBook default filters
|
|
_.each(defaultFilters, function(fn, filter) {
|
|
that.env.addFilter(filter, fn);
|
|
});
|
|
|
|
// Translate using _i18n locales
|
|
that.env.addFilter('t', function(s) {
|
|
return that.i18n.t(that.book.config.get('language'), s);
|
|
});
|
|
|
|
// Transform an absolute path into a relative path
|
|
// using this.ctx.page.path
|
|
that.env.addFilter('resolveFile', function(href) {
|
|
return location.normalize(that.resolveForPage(this.ctx.file.path, href));
|
|
});
|
|
|
|
// Test if a file exists
|
|
that.env.addFilter('fileExists', function(href) {
|
|
return fs.existsSync(that.resolve(href));
|
|
});
|
|
|
|
// Transform a '.md' into a '.html' (README -> index)
|
|
that.env.addFilter('contentURL', function(s) {
|
|
return location.normalize(that.outputUrl(s));
|
|
});
|
|
|
|
// Relase path to an asset
|
|
that.env.addFilter('resolveAsset', function(href) {
|
|
href = path.join('gitbook', href);
|
|
|
|
// Resolve for current file
|
|
if (this.ctx.file) {
|
|
href = that.resolveForPage(this.ctx.file.path, '/' + href);
|
|
}
|
|
|
|
// Use assets from parent
|
|
if (that.book.isLanguageBook()) {
|
|
href = path.join('../', href);
|
|
}
|
|
|
|
return location.normalize(href);
|
|
});
|
|
})
|
|
|
|
// Copy assets from themes before copying files from book
|
|
.then(function() {
|
|
if (that.book.isLanguageBook()) return;
|
|
|
|
return Promise.serie([
|
|
// Assets from the book are already copied
|
|
// The order is reversed from the template's one
|
|
|
|
// Is default theme still installed
|
|
that.themeDefault && that.themeDefault.root != that.theme.root?
|
|
that.themeDefault.root : null,
|
|
|
|
// Installed plugin (it can be identical to themeDefault.root)
|
|
that.theme.root
|
|
], function(folder) {
|
|
if (!folder) return;
|
|
|
|
// Copy assets only if exists (don't fail otherwise)
|
|
var assetFolder = path.join(folder, '_assets', that.name);
|
|
if (!fs.existsSync(assetFolder)) return;
|
|
|
|
that.log.debug.ln('copy assets from theme', assetFolder);
|
|
return fs.copyDir(
|
|
assetFolder,
|
|
that.resolve('gitbook'),
|
|
{
|
|
deleteFirst: false, // Delete "to" before
|
|
overwrite: true,
|
|
confirm: true
|
|
}
|
|
);
|
|
});
|
|
})
|
|
|
|
// Load resources for plugins
|
|
.then(function() {
|
|
return that.plugins.getResources(that.name)
|
|
.then(function(resources) {
|
|
that.resources = resources;
|
|
});
|
|
});
|
|
};
|
|
|
|
// Write a page (parsable file)
|
|
WebsiteOutput.prototype.onPage = function(page) {
|
|
var that = this;
|
|
|
|
// Parse the page
|
|
return page.toHTML(this)
|
|
|
|
// Render the page template with the same context as the json output
|
|
.then(function() {
|
|
return that.render('page', page.getContext());
|
|
})
|
|
|
|
// Write the HTML file
|
|
.then(function(html) {
|
|
return that.writeFile(
|
|
that.outputPath(page.path),
|
|
html
|
|
);
|
|
});
|
|
};
|
|
|
|
// Finish generation, create ebook using ebook-convert
|
|
WebsiteOutput.prototype.finish = function() {
|
|
var that = this;
|
|
|
|
return Promise()
|
|
.then(function() {
|
|
return WebsiteOutput.super_.prototype.finish.apply(that);
|
|
})
|
|
|
|
// Copy assets from plugins
|
|
.then(function() {
|
|
if (that.book.isLanguageBook()) return;
|
|
return that.plugins.copyResources(that.name, that.resolve('gitbook'));
|
|
})
|
|
|
|
// Generate homepage to select languages
|
|
.then(function() {
|
|
if (!that.book.isMultilingual()) return;
|
|
return that.outputMultilingualIndex();
|
|
});
|
|
};
|
|
|
|
// ----- Utilities ----
|
|
|
|
// Write multi-languages index
|
|
WebsiteOutput.prototype.outputMultilingualIndex = function() {
|
|
var that = this;
|
|
|
|
return that.render('languages', that.getContext())
|
|
.then(function(html) {
|
|
return that.writeFile(
|
|
'index.html',
|
|
html
|
|
);
|
|
});
|
|
};
|
|
|
|
// Render a template using nunjucks
|
|
// Templates are stored in `_layouts` folders
|
|
WebsiteOutput.prototype.render = function(tpl, context) {
|
|
var filename = this.templateName(tpl);
|
|
context = _.extend(context, {
|
|
template: {
|
|
// Same template but in the default theme
|
|
default: this.themeDefault? path.resolve(templatesPath(this.themeDefault.root), filename) : null,
|
|
|
|
// Same template but in the theme
|
|
theme: path.resolve(templatesPath(this.theme.root), filename)
|
|
},
|
|
|
|
plugins: {
|
|
resources: this.resources
|
|
},
|
|
|
|
options: this.opts
|
|
});
|
|
|
|
return Promise.nfcall(this.env.render.bind(this.env), filename, context);
|
|
};
|
|
|
|
// Return a complete name for a template
|
|
WebsiteOutput.prototype.templateName = function(name) {
|
|
return path.join(this.name, name+'.html');
|
|
};
|
|
|
|
module.exports = WebsiteOutput;
|