diff options
author | Samy Pessé <samypesse@gmail.com> | 2015-01-19 09:47:36 +0100 |
---|---|---|
committer | Samy Pessé <samypesse@gmail.com> | 2015-01-19 09:47:36 +0100 |
commit | ec586dd3cdf06e9567f5d3e4961022ddc3c94778 (patch) | |
tree | cc7825ab73110b4e6fbedee404427b052edffa17 /lib/generate | |
parent | 80432161708357bdcf0e00533d9e6d327636dab6 (diff) | |
download | gitbook-ec586dd3cdf06e9567f5d3e4961022ddc3c94778.zip gitbook-ec586dd3cdf06e9567f5d3e4961022ddc3c94778.tar.gz gitbook-ec586dd3cdf06e9567f5d3e4961022ddc3c94778.tar.bz2 |
Clear folder
Diffstat (limited to 'lib/generate')
-rw-r--r-- | lib/generate/config.js | 137 | ||||
-rw-r--r-- | lib/generate/ebook/index.js | 92 | ||||
-rw-r--r-- | lib/generate/fs.js | 92 | ||||
-rw-r--r-- | lib/generate/generator.js | 87 | ||||
-rw-r--r-- | lib/generate/index.js | 374 | ||||
-rw-r--r-- | lib/generate/init.js | 69 | ||||
-rw-r--r-- | lib/generate/json/index.js | 77 | ||||
-rw-r--r-- | lib/generate/page/index.js | 84 | ||||
-rw-r--r-- | lib/generate/plugin.js | 325 | ||||
-rw-r--r-- | lib/generate/site/glossary_indexer.js | 101 | ||||
-rw-r--r-- | lib/generate/site/index.js | 314 | ||||
-rw-r--r-- | lib/generate/site/search_indexer.js | 71 | ||||
-rw-r--r-- | lib/generate/template.js | 52 |
13 files changed, 0 insertions, 1875 deletions
diff --git a/lib/generate/config.js b/lib/generate/config.js deleted file mode 100644 index e198c91..0000000 --- a/lib/generate/config.js +++ /dev/null @@ -1,137 +0,0 @@ -var Q = require('q'); -var _ = require('lodash'); -var path = require('path'); - -// Default configuration for gitbook -var CONFIG = { - // Folders to use for output - // Caution: it overrides the value from the command line - // It's not advised this option in the book.json - "output": null, - - // Generator to use for building - // Caution: it overrides the value from the command line - // It's not advised this option in the book.json - "generator": "site", - - // Configuration file to use - "configFile": "book", - - // Book metadats (somes are extracted from the README by default) - "title": null, - "description": null, - "isbn": null, - - // For ebook format, the extension to use for generation (default is detected from output extension) - // "epub", "pdf", "mobi" - // Caution: it overrides the value from the command line - // It's not advised this option in the book.json - "extension": null, - - // Plugins list, can contain "-name" for removing default plugins - "plugins": [], - - // Global configuration for plugins - "pluginsConfig": { - "fontSettings": { - "theme": null, //"sepia", "night" or "white", - "family": "sans",// "serif" or "sans", - "size": 2 // 1 - 4 - } - }, - - // Variables for templating - "variables": {}, - - // Set another theme with your own layout - // It's recommended to use plugins or add more options for default theme, though - // See https://github.com/GitbookIO/gitbook/issues/209 - "theme": path.resolve(__dirname, '../../theme'), - - // Links in template (null: default, false: remove, string: new value) - "links": { - // Custom links at top of sidebar - "sidebar": { - //"Custom link name": "https://customlink.com" - }, - - // Sharing links - "sharing": { - "google": null, - "facebook": null, - "twitter": null, - "weibo": null, - "all": null - } - }, - - // CSS Styles - "styles": { - "website": "styles/website.css", - "ebook": "styles/ebook.css", - "pdf": "styles/pdf.css", - "mobi": "styles/mobi.css", - "epub": "styles/epub.css" - }, - - // Options for PDF generation - "pdf": { - // Add toc at the end of the file - "toc": true, - - // Add page numbers to the bottom of every page - "pageNumbers": false, - - // Font size for the file content - "fontSize": 12, - - // Paper size for the pdf - // Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’] - "paperSize": "a4", - - // Margin (in pts) - // Note: 72 pts equals 1 inch - "margin": { - "right": 62, - "left": 62, - "top": 36, - "bottom": 36 - }, - - //Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_. - "headerTemplate": "", - - //Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_. - "footerTemplate": "" - } -}; - -// Return complete configuration -var defaultsConfig = function(options) { - return _.merge(options || {}, CONFIG, _.defaults); -}; - -// Read configuration from book.json -var readConfig = function(options) { - options = defaultsConfig(options); - - return Q() - .then(function() { - try { - var _config = require(path.resolve(options.input, options.configFile)); - options = _.merge(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins', 'generator')); - } - catch(err) { - // No config file: not a big deal - return Q(); - } - }) - .thenResolve(options); -}; - -module.exports = { - CONFIG: CONFIG, - defaults: defaultsConfig, - read: readConfig -} - diff --git a/lib/generate/ebook/index.js b/lib/generate/ebook/index.js deleted file mode 100644 index c74ffcd..0000000 --- a/lib/generate/ebook/index.js +++ /dev/null @@ -1,92 +0,0 @@ -var util = require("util"); -var path = require("path"); -var Q = require("q"); -var _ = require("lodash"); -var exec = require('child_process').exec; - -var fs = require('graceful-fs'); -var parse = require("../../parse"); -var BaseGenerator = require("../page"); -var stringUtils = require("../../utils/string"); - -var Generator = function() { - BaseGenerator.apply(this, arguments); - - // eBook format - this.ebookFormat = this.options.extension || path.extname(this.options.output).replace("\.", "") || "pdf"; - - // Styles to use - this.styles = ["ebook", this.ebookFormat]; -}; -util.inherits(Generator, BaseGenerator); - -Generator.prototype.finish = function() { - var that = this; - - return BaseGenerator.prototype.finish.apply(this) - .then(function() { - var d = Q.defer(); - - if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) { - that.options.cover = path.join(that.options.output, "cover.jpg"); - } - - var _options = { - "--cover": that.options.cover, - "--title": that.options.title, - "--comments": that.options.description, - "--isbn": that.options.isbn, - "--authors": that.options.author, - "--publisher": "GitBook", - "--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]", - "--chapter-mark": "pagebreak", - "--page-breaks-before": "/", - "--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]", - "--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]", - "--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]", - "--no-chapters-in-toc": true, - "--max-levels": "1", - "--breadth-first": true - }; - - if (that.ebookFormat == "pdf") { - var pdfOptions = that.options.pdf; - - _.extend(_options, { - "--margin-left": String(pdfOptions.margin.left), - "--margin-right": String(pdfOptions.margin.right), - "--margin-top": String(pdfOptions.margin.top), - "--margin-bottom": String(pdfOptions.margin.bottom), - "--pdf-default-font-size": String(pdfOptions.fontSize), - "--pdf-mono-font-size": String(pdfOptions.fontSize), - "--paper-size": String(pdfOptions.paperSize), - "--pdf-page-numbers": Boolean(pdfOptions.pageNumbers), - "--pdf-header-template": String(pdfOptions.headerTemplate), - "--pdf-footer-template": String(pdfOptions.footerTemplate) - }); - } - - var command = [ - "ebook-convert", - path.join(that.options.output, "SUMMARY.html"), - path.join(that.options.output, "index."+that.ebookFormat), - stringUtils.optionsToShellArgs(_options) - ].join(" "); - - exec(command, function (error, stdout, stderr) { - if (error) { - if (error.code == 127) { - error.message = "Need to install ebook-convert from Calibre"; - } else { - error.message = error.message + " "+stdout; - } - return d.reject(error); - } - d.resolve(); - }); - - return d.promise; - }); -}; - -module.exports = Generator; diff --git a/lib/generate/fs.js b/lib/generate/fs.js deleted file mode 100644 index 371051c..0000000 --- a/lib/generate/fs.js +++ /dev/null @@ -1,92 +0,0 @@ -var Q = require("q"); -var fs = require('graceful-fs'); -var fsExtra = require("fs-extra"); -var Ignore = require("fstream-ignore"); - -var getFiles = function(path) { - var d = Q.defer(); - - // Our list of files - var files = []; - - var ig = Ignore({ - path: path, - ignoreFiles: ['.ignore', '.gitignore', '.bookignore'] - }); - - // Add extra rules to ignore common folders - ig.addIgnoreRules([ - // Skip Git stuff - '.git/', - '.gitignore', - - // Skip OS X meta data - '.DS_Store', - - // Skip stuff installed by plugins - 'node_modules', - - // Skip book outputs - '*.pdf', - '*.epub', - '*.mobi', - - // Skip config files - '.ignore', - '.bookignore', - 'book.json', - ], '__custom_stuff'); - - // Push each file to our list - ig.on('child', function (c) { - files.push( - c.path.substr(c.root.path.length + 1) + (c.props.Directory === true ? '/' : '') - ); - }); - - ig.on('end', function() { - // Normalize paths on Windows - if(process.platform === 'win32') { - return d.resolve(files.map(function(file) { - return file.replace(/\\/g, '/'); - })); - } - - // Simply return paths otherwise - return d.resolve(files); - }); - - ig.on('error', d.reject); - - return d.promise; -}; - -module.exports = { - list: getFiles, - readFile: Q.denodeify(fs.readFile), - //writeFile: Q.denodeify(fs.writeFile), - writeFile: function(filename, data, options) { - var d = Q.defer(); - - try { - fs.writeFileSync(filename, data, options) - } catch(err) { - d.reject(err); - } - d.resolve(); - - - return d.promise; - }, - mkdirp: Q.denodeify(fsExtra.mkdirp), - copy: Q.denodeify(fsExtra.copy), - remove: Q.denodeify(fsExtra.remove), - symlink: Q.denodeify(fsExtra.symlink), - exists: function(path) { - var d = Q.defer(); - fs.exists(path, d.resolve); - return d.promise; - }, - existsSync: fs.existsSync, - readFileSync: fs.readFileSync.bind(fs) -}; diff --git a/lib/generate/generator.js b/lib/generate/generator.js deleted file mode 100644 index 4791c98..0000000 --- a/lib/generate/generator.js +++ /dev/null @@ -1,87 +0,0 @@ -var _ = require("lodash"); -var path = require("path"); -var Q = require("q"); -var fs = require("./fs"); - -var Plugin = require("./plugin"); - -var BaseGenerator = function(options) { - this.options = options; - - // Base for assets in plugins - this.pluginAssetsBase = "book"; - - this.options.plugins = Plugin.normalizeNames(this.options.plugins); - this.options.plugins = _.union(this.options.plugins, this.options.defaultsPlugins); - this.plugins = []; -}; - -BaseGenerator.prototype.callHook = function(name, data) { - return this.plugins.hook(name, data); -}; - -// Sets up generator -BaseGenerator.prototype.load = function() { - return this.loadPlugins(); -}; - -BaseGenerator.prototype.loadPlugins = function() { - var that = this; - - return Plugin.fromList(this.options.plugins, this.options.input, this, { - assetsBase: this.pluginAssetsBase - }) - .then(function(_plugins) { - that.plugins = _plugins; - - return that.callHook("init"); - }); -}; - -BaseGenerator.prototype.convertFile = function(content, input) { - return Q.reject(new Error("Could not convert "+input)); -}; - -BaseGenerator.prototype.transferFile = function(input) { - return fs.copy( - path.join(this.options.input, input), - path.join(this.options.output, input) - ); -}; - -BaseGenerator.prototype.transferFolder = function(input) { - return fs.mkdirp( - path.join(this.options.output, input) - ); -}; - -BaseGenerator.prototype.copyCover = function() { - var that = this; - - return Q.all([ - fs.copy(path.join(this.options.input, "cover.jpg"), path.join(this.options.output, "cover.jpg")), - fs.copy(path.join(this.options.input, "cover_small.jpg"), path.join(this.options.output, "cover_small.jpg")) - ]) - .fail(function() { - // If orignally from multi-lang, try copy from originalInput - if (!that.options.originalInput) return; - - return Q.all([ - fs.copy(path.join(that.options.originalInput, "cover.jpg"), path.join(that.options.output, "cover.jpg")), - fs.copy(path.join(that.options.originalInput, "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg")) - ]); - }) - .fail(function(err) { - return Q(); - }); -}; - -BaseGenerator.prototype.langsIndex = function(langs) { - return Q.reject(new Error("Langs index is not supported in this generator")); -}; - -BaseGenerator.prototype.finish = function() { - return Q.reject(new Error("Could not finish generation")); -}; - -module.exports = BaseGenerator; diff --git a/lib/generate/index.js b/lib/generate/index.js deleted file mode 100644 index de1fc0e..0000000 --- a/lib/generate/index.js +++ /dev/null @@ -1,374 +0,0 @@ -var Q = require("q"); -var _ = require("lodash"); -var path = require("path"); -var tmp = require('tmp'); - -var swig = require('./template'); -var fs = require("./fs"); -var parse = require("../parse"); -var Plugin = require("./plugin"); -var defaultConfig = require("./config"); - -var generators = { - "site": require("./site"), - "page": require("./page"), - "ebook": require("./ebook"), - "json": require("./json") -}; - -var defaultDescription = "Book generated using GitBook"; - - -var containsFiles = function(dir, files) { - return Q.all(_.map(files, function(file) { - return fs.exists(path.join(dir, file)); - })) - .then(_.all); -}; - -// Test if generator exists -var checkGenerator = function(options) { - if (!generators[options.generator]) { - return Q.reject(new Error("Invalid generator (availables are: "+_.keys(generators).join(", ")+")")); - } - return Q(); -}; - -// Create the generator and load plugins -var loadGenerator = function(options) { - return checkGenerator(options) - .then(function() { - var generator = new generators[options.generator](options); - - return generator.load() - .then(_.constant(generator)); - }); -}; - - - -var generate = function(options) { - // Read config file - return defaultConfig.read(options) - .then(function(_options) { - options = _options; - - // Validate options - if (!options.input) { - return Q.reject(new Error("Need option input (book input directory)")); - } - - // Check files to get folder type (book, multilanguage book or neither) - return checkGenerator(options); - }) - - // Read readme - .then(function() { - return fs.readFile(path.join(options.input, "README.md"), "utf-8") - .then(function(_readme) { - _readme = parse.readme(_readme); - - options.title = options.title || _readme.title; - options.description = options.description || _readme.description || defaultDescription; - }); - }) - - // Detect multi-languages book - .then(function() { - return containsFiles(options.input, ['LANGS.md']) - }) - - .then(function(isMultiLang) { - // Multi language book - if(isMultiLang) { - return generateMultiLang(options); - } - - // Book - return generateBook(options); - }); -}; - -/* - * Generate a multilanguage book by generating a book for each folder. - */ -var generateMultiLang = function(options) { - var langsSummary; - options.output = options.output || path.join(options.input, "_book"); - - return checkGenerator(options) - - // Multi-languages book - .then(function() { - return fs.readFile(path.join(options.input, "LANGS.md"), "utf-8") - }) - - // Clean output folder - .then(function(_langsSummary) { - langsSummary = _langsSummary; - return fs.remove(options.output); - }) - .then(function() { - return fs.mkdirp(options.output); - }) - - // Generate sub-books - .then(function() { - options.langsSummary = parse.langs(langsSummary); - - // Generated a book for each valid entry - return _.reduce(options.langsSummary.list, function(prev, entry) { - return prev.then(function() { - return generate(_.extend({}, options, { - input: path.join(options.input, entry.path), - output: path.join(options.output, entry.path), - originalInput: options.input, - originalOutput: options.output - })); - }) - }, Q()); - }) - - .then(function() { - return loadGenerator(options); - }) - - // Generate languages index - .then(function(generator) { - return generator.langsIndex(options.langsSummary); - }) - - // Copy cover file - .then(function() { - return Q.all([ - fs.copy(path.join(options.input, "cover.jpg"), path.join(options.output, "cover.jpg")), - fs.copy(path.join(options.input, "cover_small.jpg"), path.join(options.output, "cover_small.jpg")) - ]) - .fail(function() { - return Q(); - }) - }) - - // Return options to caller - .then(_.constant(options)); -}; - -/* - * Use a specific generator to convert a gitbook to a site/pdf/ebook/ - * output is always a folder - */ -var generateBook = function(options) { - var files; - - options.output = options.output || path.join(options.input, "_book"); - - // Check if it's a book - return containsFiles(options.input, ['SUMMARY.md', 'README.md']) - - // Fail if not a book - .then(function(isBook) { - if(!isBook) { - return Q.reject(new Error("Invalid gitbook repository, need SUMMARY.md and README.md")); - } - }) - - // Clean output folder - .then(function() { - return fs.remove(options.output); - }) - - .then(function() { - return fs.mkdirp(options.output); - }) - - // List all files in the repository - .then(function() { - return fs.list(options.input) - .then(function(_files) { - files = _files; - }); - }) - - .then(function() { - return loadGenerator(options); - }) - - // Convert files - .then(function(generator) { - // Generate the book - return Q() - - // Get summary - .then(function() { - var summary = { - path: path.join(options.input, "SUMMARY.md") - }; - - var _callHook = function(name) { - return generator.callHook(name, summary) - .then(function(_summary) { - summary = _summary; - return summary; - }); - }; - - return fs.readFile(summary.path, "utf-8") - .then(function(_content) { - summary.content = _content; - return _callHook("summary:before"); - }) - .then(function() { - summary.content = parse.summary(summary.content); - return _callHook("summary:after"); - }) - .then(function() { - options.summary = summary.content; - options.navigation = parse.navigation(options.summary); - }); - }) - - // Read glossary - .then(function() { - var glossary = {}; - - var _callHook = function(name) { - return generator.callHook(name, glossary) - .then(function(_glossary) { - glossary = _glossary; - return glossary; - }); - }; - - return fs.readFile(path.join(options.input, "GLOSSARY.md"), "utf-8") - .fail(function() { - return ""; - }) - .then(function(_content) { - glossary.content = _content; - return _callHook("glossary:before"); - }) - .then(function() { - glossary.content = parse.glossary(glossary.content); - return _callHook("glossary:after"); - }) - .then(function() { - options.glossary = glossary.content; - }); - }) - - // Skip processing some files - .then(function() { - files = _.filter(files, function (file) { - return !( - file === 'SUMMARY.md' || - file === 'GLOSSARY.md' - ); - }); - }) - - // Copy file and replace markdown file - .then(function() { - return Q.all( - _.chain(files) - .map(function(file) { - if (!file) return; - - if (file[file.length -1] == "/") { - return Q(generator.transferFolder(file)); - } else if (path.extname(file) == ".md" && options.navigation[file] != null) { - return fs.readFile(path.join(options.input, file), "utf-8") - .then(function(content) { - return Q(generator.convertFile(content, file)); - }); - } else { - return Q(generator.transferFile(file)); - } - }) - .value() - ); - }) - - // Finish generation - .then(function() { - return generator.callHook("finish:before"); - }) - .then(function() { - return generator.finish(); - }) - .then(function() { - return generator.callHook("finish"); - }); - }) - - // Return all options - .then(function() { - return options; - }); -}; - -/* - * Extract files from generate output in a temporary folder - */ -var generateFile = function(options) { - options = _.defaults(options || {}, { - input: null, - output: null, - extension: null - }); - - return Q.nfcall(tmp.dir) - .then(function(tmpDir) { - return generate( - _.extend({}, - options, - { - output: tmpDir - }) - ) - .then(function(_options) { - var ext = options.extension; - var outputFile = options.output || path.resolve(options.input, "book."+ext); - - var copyFile = function(lang) { - var _outputFile = outputFile; - var _tmpDir = tmpDir; - - if (lang) { - _outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile); - _tmpDir = path.join(_tmpDir, lang); - } - - return fs.copy( - path.join(_tmpDir, "index."+ext), - _outputFile - ); - }; - - // Multi-langs book - return Q() - .then(function() { - if (_options.langsSummary) { - return Q.all( - _.map(_options.langsSummary.list, function(lang) { - return copyFile(lang.lang); - }) - ); - } else { - return copyFile(); - } - }) - .then(function() { - return fs.remove(tmpDir); - }); - }); - }); -}; - -module.exports = { - generators: generators, - folder: generate, - file: generateFile, - book: generateBook, - Plugin: Plugin, - config: defaultConfig -}; diff --git a/lib/generate/init.js b/lib/generate/init.js deleted file mode 100644 index 705e6e7..0000000 --- a/lib/generate/init.js +++ /dev/null @@ -1,69 +0,0 @@ -var Q = require('q'); -var _ = require('lodash'); - -var path = require('path'); - -var fs = require('./fs'); -var parse = require('../parse'); - - -// Extract paths out of a summary -function paths(summary) { - return _.reduce(summary.chapters, function(accu, chapter) { - return accu.concat( - _.filter([chapter.path].concat(_.pluck(chapter.articles, 'path'))) - ); - }, []); -} - -// Get the parent folders out of a group of files -function folders(files) { - return _.chain(files) - .map(function(file) { - return path.dirname(file); - }) - .uniq() - .value(); -} - -function initDir(dir) { - return fs.readFile(path.join(dir, 'SUMMARY.md'), 'utf8') - .then(function(src) { - // Parse summary - return parse.summary(src); - }) - .then(function(summary) { - // Extract paths from summary - return paths(summary); - }) - .then(function(paths) { - // Convert to absolute paths - return _.map(paths, function(file) { - return path.resolve(file); - }); - }) - .then(function(files) { - // Create folders - return Q.all(_.map(folders(files), function(folder) { - return fs.mkdirp(folder); - })) - .then(_.constant(files)); - }) - .then(function(files) { - // Create files that don't exist - return Q.all(_.map(files, function(file) { - return fs.exists(file) - .then(function(exists) { - if(exists) return; - return fs.writeFile(file, ''); - }); - })); - }) - .fail(function(err) { - console.error(err.stack); - }); -} - - -// Exports -module.exports = initDir; diff --git a/lib/generate/json/index.js b/lib/generate/json/index.js deleted file mode 100644 index a252ed3..0000000 --- a/lib/generate/json/index.js +++ /dev/null @@ -1,77 +0,0 @@ -var util = require("util"); -var path = require("path"); -var Q = require("q"); -var _ = require("lodash"); - -var fs = require("../fs"); -var parse = require("../../parse"); -var BaseGenerator = require("../generator"); - - -var Generator = function() { - BaseGenerator.apply(this, arguments); -}; -util.inherits(Generator, BaseGenerator); - -Generator.prototype.transferFile = function(input) { - // ignore -}; - -Generator.prototype.convertFile = function(content, input) { - var that = this; - var json = { - progress: parse.progress(this.options.navigation, input) - }; - - return Q() - .then(function() { - return parse.page(content, { - dir: path.dirname(input) || '/' - }); - }) - .then(function(parsed) { - json.lexed = parsed.lexed; - json.sections = parsed.sections; - }) - .then(function() { - return fs.writeFile( - path.join(that.options.output, input.replace(".md", ".json")), - JSON.stringify(json, null, 4) - ); - }); -}; - -// Generate languages index -// Contains the first languages readme and langs infos -Generator.prototype.langsIndex = function(langs) { - var that = this; - - if (langs.list.length == 0) return Q.reject("Need at least one language"); - - var mainLang = _.first(langs.list).lang; - console.log("Main language is", mainLang); - - return Q() - .then(function() { - return fs.readFile( - path.join(that.options.output, mainLang, "README.json") - ); - }) - .then(function(content) { - var json = JSON.parse(content); - _.extend(json, { - langs: langs.list - }); - - return fs.writeFile( - path.join(that.options.output, "README.json"), - JSON.stringify(json, null, 4) - ); - }); -}; - -Generator.prototype.finish = function() { - // ignore -}; - -module.exports = Generator; diff --git a/lib/generate/page/index.js b/lib/generate/page/index.js deleted file mode 100644 index 8054fe6..0000000 --- a/lib/generate/page/index.js +++ /dev/null @@ -1,84 +0,0 @@ -var _ = require("lodash"); -var util = require("util"); -var path = require("path"); -var Q = require("q"); -var swig = require("../template"); - -var fs = require("../fs"); -var parse = require("../../parse"); -var BaseGenerator = require("../site"); - -var Generator = function() { - BaseGenerator.apply(this, arguments); - - // Styles to use - this.styles = ["ebook"]; - - // Base for assets in plugins - this.pluginAssetsBase = "ebook"; - - // List of pages content - this.pages = {}; -}; -util.inherits(Generator, BaseGenerator); - -Generator.prototype.loadTemplates = function() { - this.template = swig.compileFile( - this.plugins.template("ebook:page") || path.resolve(this.options.theme, 'templates/ebook/page.html') - ); - this.summaryTemplate = swig.compileFile( - this.plugins.template("ebook:sumary") || path.resolve(this.options.theme, 'templates/ebook/summary.html') - ); - this.glossaryTemplate = swig.compileFile( - this.plugins.template("ebook:glossary") || path.resolve(this.options.theme, 'templates/ebook/glossary.html') - ); -}; - -// Generate table of contents -Generator.prototype.writeToc = function() { - var that = this; - var basePath = "."; - - return this._writeTemplate(this.summaryTemplate, { - toc: parse.progress(this.options.navigation, "README.md").chapters, - basePath: basePath, - staticBase: path.join(basePath, "gitbook"), - }, path.join(this.options.output, "SUMMARY.html")); -}; - -Generator.prototype.finish = function() { - var that = this; - var basePath = "."; - var output = path.join(this.options.output, "index.html"); - - var progress = parse.progress(this.options.navigation, "README.md"); - - return Q() - - // Write table of contents - .then(function() { - return that.writeToc(); - }) - - // Write glossary - .then(function() { - return that.writeGlossary(); - }) - - // Copy cover - .then(function() { - return that.copyCover(); - }) - - // Copy assets - .then(function() { - return that.copyAssets(); - }); -}; - -// Generate languages index -Generator.prototype.langsIndex = function(langs) { - return Q(); -}; - -module.exports = Generator; diff --git a/lib/generate/plugin.js b/lib/generate/plugin.js deleted file mode 100644 index 5ca5e92..0000000 --- a/lib/generate/plugin.js +++ /dev/null @@ -1,325 +0,0 @@ -var _ = require("lodash"); -var Q = require("q"); -var semver = require("semver"); -var path = require("path"); -var url = require("url"); -var fs = require("./fs"); -var npmi = require('npmi'); -var resolve = require('resolve'); - -var pkg = require("../../package.json"); - -var RESOURCES = ["js", "css"]; - -var Plugin = function(name, root, generator) { - this.name = name; - this.root = root; - this.packageInfos = {}; - this.infos = {}; - this.generator = generator; - - // Bind methods - _.bindAll(this); - - _.each([ - "gitbook-plugin-"+name, - "gitbook-theme-"+name, - "gitbook-"+name, - name, - ], function(_name) { - if (this.load(_name, __dirname)) return false; - if (this.load(_name, path.resolve(root))) return false; - }, this); -}; - -// Load from a name -Plugin.prototype.load = function(name, baseDir) { - try { - var res = resolve.sync(name+"/package.json", { basedir: baseDir }); - - this.baseDir = path.dirname(res); - this.packageInfos = require(res); - this.infos = require(resolve.sync(name, { basedir: baseDir })); - this.name = name; - - return true; - } catch (e) { - return false; - } -}; - -Plugin.prototype.normalizeResource = function(resource) { - // Parse the resource path - var parsed = url.parse(resource); - - // This is a remote resource - // so we will simply link to using it's URL - if (parsed.protocol) { - return { - "url": resource - }; - } - - // This will be copied over from disk - // and shipped with the book's build - return { "path": this.name+"/"+resource }; -}; - -// Return resources -Plugin.prototype._getResources = function(base) { - base = base || "book"; - var book = this.infos[base]; - - // Nothing specified, fallback to default - if (!book) { - return Q({}); - } - - // Dynamic function - if(typeof book === "function") { - // Call giving it the context of our generator - return Q().then(book.bind(this.generator)); - } - - // Plain data object - return Q(_.cloneDeep(book)); -}; - -// Normalize resources and return them -Plugin.prototype.getResources = function(base) { - var that = this; - - return this._getResources(base) - .then(function(resources) { - - _.each(RESOURCES, function(resourceType) { - resources[resourceType] = (resources[resourceType] || []).map(that.normalizeResource); - }); - - return resources; - }); -}; - -// Test if it's a valid plugin -Plugin.prototype.isValid = function() { - return ( - this.packageInfos && - this.packageInfos.name && - this.packageInfos.engines && - this.packageInfos.engines.gitbook && - semver.satisfies(pkg.version, this.packageInfos.engines.gitbook) - ); -}; - -// Resolve file path -Plugin.prototype.resolveFile = function(filename) { - return path.resolve(this.baseDir, filename); -}; - -// Resolve file path -Plugin.prototype.callHook = function(name, data) { - // Our generator will be the context to apply - var context = this.generator; - - var hookFunc = this.infos.hooks? this.infos.hooks[name] : null; - data = data || {}; - - if (!hookFunc) return Q(data); - - return Q() - .then(function() { - return hookFunc.apply(context, [data]); - }); -}; - -// Copy plugin assets fodler -Plugin.prototype.copyAssets = function(out, options) { - var that = this; - options = _.defaults(options || {}, { - base: "book" - }); - - return this.getResources(options.base) - .get('assets') - .then(function(assets) { - // Assets are undefined - if(!assets) return false; - - return fs.copy( - that.resolveFile(assets), - out - ).then(_.constant(true)); - }, _.constant(false)); -}; - - -// Install a list of plugin -Plugin.install = function(options) { - // Normalize list of plugins - var plugins = Plugin.normalizeList(options.plugins); - - // Install plugins one by one - return _.reduce(plugins, function(prev, plugin) { - return prev.then(function() { - var fullname = "gitbook-plugin-"+plugin.name; - console.log("Install plugin", plugin.name, "from npm ("+fullname+") with version", (plugin.version || "*")); - return Q.nfcall(npmi, { - 'name': fullname, - 'version': plugin.version, - 'path': options.input, - 'npmLoad': { - 'loglevel': 'silent', - 'loaded': false, - 'prefix': options.input - } - }); - }); - }, Q()); -}; - -// Normalize a list of plugins to use -Plugin.normalizeList = function(plugins) { - // Normalize list to an array - plugins = _.isString(plugins) ? plugins.split(",") : (plugins || []); - - // Divide as {name, version} to handle format like "myplugin@1.0.0" - plugins = _.map(plugins, function(plugin) { - var parts = plugin.split("@"); - return { - 'name': parts[0], - 'version': parts[1] // optional - } - }); - - // List plugins to remove - var toremove = _.chain(plugins) - .filter(function(plugin) { - return plugin.name.length > 0 && plugin.name[0] == "-"; - }) - .map(function(plugin) { - return plugin.name.slice(1); - }) - .value(); - - // Merge with defaults - plugins = _.chain(plugins) - .concat(_.map(Plugin.defaults, function(plugin) { - return { 'name': plugin } - })) - .uniq() - .value(); - - // Build final list - plugins = _.filter(plugins, function(plugin) { - return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == "-"); - }); - - return plugins; -}; - -// Normalize a list of plugin name to use -Plugin.normalizeNames = function(plugins) { - return _.pluck(Plugin.normalizeList(plugins), "name"); -}; - -// Extract data from a list of plugin -Plugin.fromList = function(names, root, generator, options) { - options = _.defaults(options || {}, { - assetsBase: "book" - }); - - var failed = []; - - // Load plugins - var plugins = _.map(names, function(name) { - var plugin = new Plugin(name, root, generator); - if (!plugin.isValid()) failed.push(name); - return plugin; - }); - - if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")+". Run 'gitbook install' to install plugins from NPM.")); - - // The raw resources extracted from each plugin - var pluginResources; - - // Get resources of plugins - return Q.all(_.map(plugins, function(plugin) { - return plugin.getResources(options.assetsBase); - })) - // Extract resources out - // css, js, etc ... - .then(function(resources) { - pluginResources = resources; - // Group by resource types - return _.chain(RESOURCES) - .map(function(resourceType) { - // Get resources from all the plugins for this current type - return [ - // Key - resourceType, - // Value - _.chain(resources) - .pluck(resourceType) - .compact() - .flatten() - .value() - ]; - }) - .object() - .value(); - }) - // Extract html snippets - .then(function(resources) { - // Map of html resources by name added by each plugin - resources.html = pluginResources.reduce(function(accu, resource) { - var html = (resource && resource.html) || {}; - _.each(html, function(code, key) { - // Turn into function if not one already - if (!_.isFunction(code)) code = _.constant(code); - // Append - accu[key] = (accu[key] || []).concat([code]); - }); - - return accu; - }, {}); - - return resources; - }) - // Return big multi-plugin object - .then(function(resources) { - return { - 'list': plugins, - 'resources': resources, - 'hook': function(name, data) { - return _.reduce(plugins, function(prev, plugin) { - return prev.then(function(ret) { - return plugin.callHook(name, ret); - }); - }, Q(data)); - }, - 'template': function(name) { - var withTpl = _.find(plugins, function(plugin) { - return ( - plugin.infos.templates && - plugin.infos.templates[name] - ); - }); - - if (!withTpl) return null; - return withTpl.resolveFile(withTpl.infos.templates[name]); - }, - 'html': function(tag, context, options) { - return _.map(resources.html[tag] || [], function(code) { - return code.call(context, options); - }).join("\n"); - } - }; - }); -}; - -// Default plugins added to each books -Plugin.defaults = [ - "mathjax" -]; - -module.exports = Plugin; diff --git a/lib/generate/site/glossary_indexer.js b/lib/generate/site/glossary_indexer.js deleted file mode 100644 index 46ac9a4..0000000 --- a/lib/generate/site/glossary_indexer.js +++ /dev/null @@ -1,101 +0,0 @@ -var _ = require("lodash"); -var kramed = require('kramed'); -var textRenderer = require('kramed-text-renderer'); - -var entryId = require('../../parse/glossary').entryId; - - -function Indexer(glossary) { - if(!(this instanceof Indexer)) { - return new Indexer(glossary); - } - - _.bindAll(this); - - this.glossary = glossary || []; - - this.glossaryTerms = _.pluck(this.glossary, "id"); - - // Regex for searching for terms through body - this.termsRegex = new RegExp( - // Match any of the terms - "("+ - this.glossaryTerms.map(regexEscape).join('|') + - ")", - - // Flags - "gi" - ); - - // page url => terms - this.idx = { - /* - "a/b.html": ["one word", "second word"] - */ - }; - - // term => page urls - this.invertedIdx = { - /* - "word1": ["page1.html", "page2.html"] - */ - }; - - // Use text renderer - this.renderer = textRenderer(); -} - -Indexer.prototype.text = function(nodes) { - // Copy section - var section = _.toArray(nodes); - - // kramed's Render expects this, we don't use it yet - section.links = {}; - - var options = _.extend({}, kramed.defaults, { - renderer: this.renderer - }); - - return kramed.parser(section, options); -}; - -// Add page to glossary index -Indexer.prototype.add = function(sections, url) { - if(!(this.glossary && this.glossary.length > 0)) { - return; - } - - var textblob = - _.where(sections, { type: 'normal' }) - .map(this.text) - .join('\n'); - - var matches = _(textblob.match(this.termsRegex) || []) - .map(entryId) - .uniq() - .value(); - - // Add idx for book - this.idx[url] = matches; - - // Add to inverted idx - matches.forEach(function(match) { - if(!this.invertedIdx[match]) { - this.invertedIdx[match] = []; - } - this.invertedIdx[match].push(url); - }.bind(this)); -}; - -// Dump index as a string -Indexer.prototype.dump = function() { - return JSON.stringify(this.idx); -}; - - -function regexEscape(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); -} - -// Exports -module.exports = Indexer; diff --git a/lib/generate/site/index.js b/lib/generate/site/index.js deleted file mode 100644 index dfdbf68..0000000 --- a/lib/generate/site/index.js +++ /dev/null @@ -1,314 +0,0 @@ -var util = require("util"); -var path = require("path"); -var Q = require("q"); -var _ = require("lodash"); -var swig = require("../template"); - -var fs = require("../fs"); -var parse = require("../../parse"); -var BaseGenerator = require("../generator"); -var links = require("../../utils/links"); -var indexer = require('./search_indexer'); -var glossaryIndexer = require('./glossary_indexer'); - - -var Generator = function() { - BaseGenerator.apply(this, arguments); - - // Attach methods to instance - _.bindAll(this); - - this.styles = ["website"]; - this.revision = Date.now(); - this.indexer = indexer(); -}; -util.inherits(Generator, BaseGenerator); - -// Add template loading to load -Generator.prototype.load = function() { - var that = this; - - return BaseGenerator.prototype.load.apply(this) - .then(function() { - return that.loadStyles(); - }) - .then(function() { - return that.loadTemplates(); - }); -}; - -// Load all styles -Generator.prototype.loadStyles = function() { - var that = this; - this.styles = _.chain(this.styles) - .map(function(style) { - var stylePath = that.options.styles[style]; - if (fs.existsSync(path.resolve(that.options.input, stylePath))) { - return stylePath; - } - return null; - }) - .compact() - .value(); -}; - -// Load all templates -Generator.prototype.loadTemplates = function() { - this.template = swig.compileFile( - this.plugins.template("site:page") || path.resolve(this.options.theme, 'templates/website/page.html') - ); - this.langsTemplate = swig.compileFile( - this.plugins.template("site:langs") || path.resolve(this.options.theme, 'templates/website/langs.html') - ); - this.glossaryTemplate = swig.compileFile( - this.plugins.template("site:glossary") || path.resolve(this.options.theme, 'templates/website/glossary.html') - ); -}; - -// Generate a template -Generator.prototype._writeTemplate = function(tpl, options, output, interpolate) { - var that = this; - - interpolate = interpolate || _.identity; - return Q() - .then(function(sections) { - return tpl(_.extend({ - styles: that.styles, - - revision: that.revision, - - title: that.options.title, - description: that.options.description, - - glossary: that.options.glossary, - - summary: that.options.summary, - allNavigation: that.options.navigation, - - plugins: that.plugins, - pluginsConfig: JSON.stringify(that.options.pluginsConfig), - htmlSnippet: _.partialRight(that.plugins.html, that, options), - - options: that.options - }, options)); - }) - .then(interpolate) - .then(function(html) { - return fs.writeFile( - output, - html - ); - }); -}; - -Generator.prototype.indexPage = function(lexed, pagePath) { - // Setup glossary indexer if not yet setup - if(!this.glossaryIndexer) { - this.glossaryIndexer = glossaryIndexer(this.options.glossary); - } - - this.indexer.add(lexed, pagePath); - this.glossaryIndexer.add(lexed, pagePath); - return Q(); -}; - -// Convert a markdown file into a normalized data set -Generator.prototype.prepareFile = function(content, _input) { - var that = this; - - var input = path.join(this.options.input, _input); - - var page = { - path: _input, - rawPath: input, - content: content, - progress: parse.progress(this.options.navigation, _input) - }; - - var _callHook = function(name) { - return that.callHook(name, page) - .then(function(_page) { - page = _page; - return page; - }); - }; - - return Q() - .then(function() { - // Send content to plugins - return _callHook("page:before"); - }) - .then(function() { - // Lex, parse includes and get - // Get HTML generated sections - return parse.page(page.content, { - // Local files path - dir: path.dirname(_input) || '/', - - // Output directory - outdir: path.dirname(_input) || '/', - - // Includer for templating - includer: parse.includer(that.options.variables, [ - path.dirname(_input) || '/', - path.join(that.options.input, '_includes'), - ], path.join, fs.readFileSync) - }); - }) - .then(function(parsed) { - page.lexed = parsed.lexed; - page.sections = parsed.sections; - - // Use plugin hook - return _callHook("page"); - }) - .then(function() { - return page; - }); -}; - -// Convert a markdown file to html -Generator.prototype.convertFile = function(content, _input) { - var that = this; - - var _output = _input.replace(".md", ".html"); - if (_output == "README.html") _output = "index.html"; - var output = path.join(this.options.output, _output); - var basePath = path.relative(path.dirname(output), this.options.output) || "."; - - // Bug fix for issue #493 which would occur when relative-links are 2-level or more deep in win32 - if (process.platform === 'win32') { - basePath = basePath.replace(/\\/g, '/'); - } - - return this.prepareFile(content, _input) - .then(function(page) { - // Index page in search - return that.indexPage(page.lexed, _output).thenResolve(page); - }) - .then(function(page) { - // Write file - return that._writeTemplate(that.template, { - progress: page.progress, - - _input: page.path, - content: page.sections, - - basePath: basePath, - staticBase: links.join(basePath, "gitbook"), - }, output, function(html) { - page.content = html; - - return that.callHook("page:after", page).get("content") - }); - }); -}; - -// Generate languages index -Generator.prototype.langsIndex = function(langs) { - var that = this; - var basePath = "."; - - return this._writeTemplate(this.langsTemplate, { - langs: langs.list, - - basePath: basePath, - staticBase: path.join(basePath, "gitbook"), - }, path.join(this.options.output, "index.html")) - .then(function() { - // Copy assets - return that.copyAssets(); - }); -}; - -// Generate glossary -Generator.prototype.writeGlossary = function() { - var that = this; - var basePath = "."; - - // No glossary - if (!this.glossaryIndexer) return Q(); - - // Transform the glossary to get term, description, files - var glossary = _.chain(this.glossaryIndexer.invertedIdx) - .map(function(links, id) { - var term = _.find(that.options.glossary, { 'id': id }); - - return { - id: id, - name: term.name, - description: term.description, - files: _.chain(links) - .map(function(link) { - var name = link.slice(0, -5); - - if (name == "index") { - name = "README"; - } - return that.options.navigation[name+".md"]; - }) - .sortBy("percent") - .value() - } - }) - .sortBy("name") - .value(); - - return this._writeTemplate(this.glossaryTemplate, { - glossaryIndex: glossary, - basePath: basePath, - staticBase: path.join(basePath, "gitbook"), - }, path.join(this.options.output, "GLOSSARY.html")); -}; - -// Copy assets -Generator.prototype.copyAssets = function() { - var that = this; - - // Copy gitbook assets - return fs.copy( - path.join(that.options.theme, "assets"), - path.join(that.options.output, "gitbook") - ) - - // Copy plugins assets - .then(function() { - return Q.all( - _.map(that.plugins.list, function(plugin) { - var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name); - return plugin.copyAssets(pluginAssets, { - base: that.pluginAssetsBase - }); - }) - ); - }); -}; - -// Dump search index to disk -Generator.prototype.writeSearchIndex = function() { - return fs.writeFile( - path.join(this.options.output, 'search_index.json'), - this.indexer.dump() - ); -}; - -// Dump glossary index to disk -Generator.prototype.writeGlossaryIndex = function() { - if (!this.glossaryIndexer) return Q(); - - return fs.writeFile( - path.join(this.options.output, 'glossary_index.json'), - JSON.stringify(this.options.glossary) - ); -}; - - -Generator.prototype.finish = function() { - return this.copyAssets() - .then(this.copyCover) - .then(this.writeGlossary) - .then(this.writeGlossaryIndex) - .then(this.writeSearchIndex); -}; - -module.exports = Generator; diff --git a/lib/generate/site/search_indexer.js b/lib/generate/site/search_indexer.js deleted file mode 100644 index 7cfe29a..0000000 --- a/lib/generate/site/search_indexer.js +++ /dev/null @@ -1,71 +0,0 @@ -var Q = require("q"); -var _ = require("lodash"); - -var lunr = require('lunr'); -var kramed = require('kramed'); -var textRenderer = require('kramed-text-renderer'); - - -function Indexer() { - if(!(this instanceof Indexer)) { - return new Indexer(); - } - - _.bindAll(this); - - // Setup lunr index - this.idx = lunr(function () { - this.ref('url'); - - this.field('title', { boost: 10 }); - this.field('body'); - }); - - this.renderer = textRenderer(); -} - -Indexer.prototype.text = function(nodes) { - // Copy section - var section = _.toArray(nodes); - - // kramed's Render expects this, we don't use it yet - section.links = {}; - - var options = _.extend({}, kramed.defaults, { - renderer: this.renderer - }); - - return kramed.parser(section, options); -}; - -Indexer.prototype.addSection = function(path, section) { - var url = [path, section.id].join('#'); - - var title = this.text( - _.filter(section, {'type': 'heading'}) - ); - - var body = this.text( - _.omit(section, {'type': 'heading'}) - ); - - // Add to lunr index - this.idx.add({ - url: url, - title: title, - body: body, - }); -}; - -Indexer.prototype.add = function(lexedPage, url) { - var sections = lexedPage; - - _.map(sections, _.partial(this.addSection, url)); -}; - -Indexer.prototype.dump = function() { - return JSON.stringify(this.idx); -}; - -// Exports -module.exports = Indexer; diff --git a/lib/generate/template.js b/lib/generate/template.js deleted file mode 100644 index acfa580..0000000 --- a/lib/generate/template.js +++ /dev/null @@ -1,52 +0,0 @@ -var path = require("path"); -var swig = require('swig'); -var hljs = require('highlight.js'); - -var links = require('../utils/').links; -var pkg = require('../../package.json'); - -swig.setDefaults({ - locals: { - gitbook: { - version: pkg.version - } - } -}); - -// Swig filter for returning the count of lines in a code section -swig.setFilter('lines', function(content) { - return content.split('\n').length; -}); - -// Swig filter for returning a link to the associated html file of a markdown file -swig.setFilter('mdLink', function(link) { - var link = link.replace(".md", ".html"); - if (link == "README.html") link = "index.html"; - return link; -}); - -// Swig filter: highlight coloration -swig.setFilter('code', function(code, lang) { - try { - return hljs.highlight(lang, code).value; - } catch(e) { - return hljs.highlightAuto(code).value; - } -}); - -// Convert a level into a deep level -swig.setFilter('lvl', function(lvl) { - return lvl.split(".").length; -}); - -// Join path -swig.setFilter('pathJoin', function(base, _path) { - return links.join(base, _path); -}); - -// Is a link an absolute link -swig.setFilter('isExternalLink', function(link) { - return links.isExternal(link); -}); - -module.exports = swig; |