diff options
Diffstat (limited to 'lib')
278 files changed, 0 insertions, 14294 deletions
diff --git a/lib/__tests__/gitbook.js b/lib/__tests__/gitbook.js deleted file mode 100644 index c3669bb..0000000 --- a/lib/__tests__/gitbook.js +++ /dev/null @@ -1,9 +0,0 @@ -var gitbook = require('../gitbook'); - -describe('satisfies', function() { - - it('should return true for *', function() { - expect(gitbook.satisfies('*')).toBe(true); - }); - -}); diff --git a/lib/__tests__/init.js b/lib/__tests__/init.js deleted file mode 100644 index 66188a3..0000000 --- a/lib/__tests__/init.js +++ /dev/null @@ -1,16 +0,0 @@ -var tmp = require('tmp'); -var initBook = require('../init'); - -describe('initBook', function() { - - it('should create a README and SUMMARY for empty book', function() { - var dir = tmp.dirSync(); - - return initBook(dir.name) - .then(function() { - expect(dir.name).toHaveFile('README.md'); - expect(dir.name).toHaveFile('SUMMARY.md'); - }); - }); - -}); diff --git a/lib/__tests__/module.js b/lib/__tests__/module.js deleted file mode 100644 index d9220f5..0000000 --- a/lib/__tests__/module.js +++ /dev/null @@ -1,6 +0,0 @@ - -describe('GitBook', function() { - it('should correctly export', function() { - require('../'); - }); -}); diff --git a/lib/api/decodeConfig.js b/lib/api/decodeConfig.js deleted file mode 100644 index 5e00df5..0000000 --- a/lib/api/decodeConfig.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - Decode changes from a JS API to a config object - - @param {Config} config - @param {Object} result: result from API - @return {Config} -*/ -function decodeGlobal(config, result) { - var values = result.values; - - delete values.generator; - delete values.output; - - return config.updateValues(values); -} - -module.exports = decodeGlobal; diff --git a/lib/api/decodeGlobal.js b/lib/api/decodeGlobal.js deleted file mode 100644 index 118afb2..0000000 --- a/lib/api/decodeGlobal.js +++ /dev/null @@ -1,22 +0,0 @@ -var decodeConfig = require('./decodeConfig'); - -/** - Decode changes from a JS API to a output object. - Only the configuration can be edited by plugin's hooks - - @param {Output} output - @param {Object} result: result from API - @return {Output} -*/ -function decodeGlobal(output, result) { - var book = output.getBook(); - var config = book.getConfig(); - - // Update config - config = decodeConfig(config, result.config); - book = book.set('config', config); - - return output.set('book', book); -} - -module.exports = decodeGlobal; diff --git a/lib/api/decodePage.js b/lib/api/decodePage.js deleted file mode 100644 index c85dd1b..0000000 --- a/lib/api/decodePage.js +++ /dev/null @@ -1,44 +0,0 @@ -var deprecate = require('./deprecate'); - -/** - Decode changes from a JS API to a page object. - Only the content can be edited by plugin's hooks. - - @param {Output} output - @param {Page} page: page instance to edit - @param {Object} result: result from API - @return {Page} -*/ -function decodePage(output, page, result) { - var originalContent = page.getContent(); - - // No returned value - // Existing content will be used - if (!result) { - return page; - } - - deprecate.disable('page.sections'); - - // GitBook 3 - // Use returned page.content if different from original content - if (result.content != originalContent) { - page = page.set('content', result.content); - } - - // GitBook 2 compatibility - // Finally, use page.sections - else if (result.sections) { - page = page.set('content', - result.sections.map(function(section) { - return section.content; - }).join('\n') - ); - } - - deprecate.enable('page.sections'); - - return page; -} - -module.exports = decodePage; diff --git a/lib/api/deprecate.js b/lib/api/deprecate.js deleted file mode 100644 index 7a93a91..0000000 --- a/lib/api/deprecate.js +++ /dev/null @@ -1,122 +0,0 @@ -var is = require('is'); -var objectPath = require('object-path'); - -var logged = {}; -var disabled = {}; - -/** - Log a deprecated notice - - @param {Book|Output} book - @param {String} key - @param {String} message -*/ -function logNotice(book, key, message) { - if (logged[key] || disabled[key]) return; - - logged[key] = true; - - var logger = book.getLogger(); - logger.warn.ln(message); -} - -/** - Deprecate a function - - @param {Book|Output} book - @param {String} key: unique identitifer for the deprecated - @param {Function} fn - @param {String} msg: message to print when called - @return {Function} -*/ -function deprecateMethod(book, key, fn, msg) { - return function() { - logNotice(book, key, msg); - - return fn.apply(this, arguments); - }; -} - -/** - Deprecate a property of an object - - @param {Book|Output} book - @param {String} key: unique identitifer for the deprecated - @param {Object} instance - @param {String|Function} property - @param {String} msg: message to print when called - @return {Function} -*/ -function deprecateField(book, key, instance, property, value, msg) { - var store = undefined; - - var prepare = function() { - if (!is.undefined(store)) return; - - if (is.fn(value)) store = value(); - else store = value; - }; - - var getter = function(){ - prepare(); - - logNotice(book, key, msg); - return store; - }; - var setter = function(v) { - prepare(); - - logNotice(book, key, msg); - store = v; - return store; - }; - - Object.defineProperty(instance, property, { - get: getter, - set: setter, - enumerable: true, - configurable: true - }); -} - -/** - Enable a deprecation - - @param {String} key: unique identitifer -*/ -function enableDeprecation(key) { - disabled[key] = false; -} - -/** - Disable a deprecation - - @param {String} key: unique identitifer -*/ -function disableDeprecation(key) { - disabled[key] = true; -} - -/** - Deprecate a method in favor of another one - - @param {Book} book - @param {String} key - @param {Object} instance - @param {String} oldName - @param {String} newName -*/ -function deprecateRenamedMethod(book, key, instance, oldName, newName, msg) { - msg = msg || ('"' + oldName + '" is deprecated, use "' + newName + '()" instead'); - var fn = objectPath.get(instance, newName); - - instance[oldName] = deprecateMethod(book, key, fn, msg); -} - -module.exports = { - method: deprecateMethod, - renamedMethod: deprecateRenamedMethod, - field: deprecateField, - enable: enableDeprecation, - disable: disableDeprecation -}; diff --git a/lib/api/encodeConfig.js b/lib/api/encodeConfig.js deleted file mode 100644 index 2a05528..0000000 --- a/lib/api/encodeConfig.js +++ /dev/null @@ -1,36 +0,0 @@ -var objectPath = require('object-path'); -var deprecate = require('./deprecate'); - -/** - Encode a config object into a JS config api - - @param {Output} output - @param {Config} config - @return {Object} -*/ -function encodeConfig(output, config) { - var result = { - values: config.getValues().toJS(), - - get: function(key, defaultValue) { - return objectPath.get(result.values, key, defaultValue); - }, - - set: function(key, value) { - return objectPath.set(result.values, key, value); - } - }; - - deprecate.field(output, 'config.options', result, 'options', - result.values, '"config.options" property is deprecated, use "config.get(key)" instead'); - - deprecate.field(output, 'config.options.generator', result.values, 'generator', - output.getGenerator(), '"options.generator" property is deprecated, use "output.name" instead'); - - deprecate.field(output, 'config.options.generator', result.values, 'output', - output.getRoot(), '"options.output" property is deprecated, use "output.root()" instead'); - - return result; -} - -module.exports = encodeConfig; diff --git a/lib/api/encodeGlobal.js b/lib/api/encodeGlobal.js deleted file mode 100644 index a366526..0000000 --- a/lib/api/encodeGlobal.js +++ /dev/null @@ -1,257 +0,0 @@ -var path = require('path'); -var Promise = require('../utils/promise'); -var PathUtils = require('../utils/path'); -var fs = require('../utils/fs'); - -var Plugins = require('../plugins'); -var deprecate = require('./deprecate'); -var fileToURL = require('../output/helper/fileToURL'); -var defaultBlocks = require('../constants/defaultBlocks'); -var gitbook = require('../gitbook'); -var parsers = require('../parsers'); - -var encodeConfig = require('./encodeConfig'); -var encodeSummary = require('./encodeSummary'); -var encodeNavigation = require('./encodeNavigation'); -var encodePage = require('./encodePage'); - -/** - Encode a global context into a JS object - It's the context for page's hook, etc - - @param {Output} output - @return {Object} -*/ -function encodeGlobal(output) { - var book = output.getBook(); - var bookFS = book.getContentFS(); - var logger = output.getLogger(); - var outputFolder = output.getRoot(); - var plugins = output.getPlugins(); - var blocks = Plugins.listBlocks(plugins); - - var result = { - log: logger, - config: encodeConfig(output, book.getConfig()), - summary: encodeSummary(output, book.getSummary()), - - /** - Check if the book is a multilingual book - - @return {Boolean} - */ - isMultilingual: function() { - return book.isMultilingual(); - }, - - /** - Check if the book is a language book for a multilingual book - - @return {Boolean} - */ - isLanguageBook: function() { - return book.isLanguageBook(); - }, - - /** - Read a file from the book - - @param {String} fileName - @return {Promise<Buffer>} - */ - readFile: function(fileName) { - return bookFS.read(fileName); - }, - - /** - Read a file from the book as a string - - @param {String} fileName - @return {Promise<String>} - */ - readFileAsString: function(fileName) { - return bookFS.readAsString(fileName); - }, - - /** - Resolve a file from the book root - - @param {String} fileName - @return {String} - */ - resolve: function(fileName) { - return path.resolve(book.getContentRoot(), fileName); - }, - - /** - Resolve a page by it path - - @param {String} filePath - @return {String} - */ - getPageByPath: function(filePath) { - var page = output.getPage(filePath); - if (!page) return undefined; - - return encodePage(output, page); - }, - - /** - Render a block of text (markdown/asciidoc) - - @param {String} type - @param {String} text - @return {Promise<String>} - */ - renderBlock: function(type, text) { - var parser = parsers.get(type); - - return parser.parsePage(text) - .get('content'); - }, - - /** - Render an inline text (markdown/asciidoc) - - @param {String} type - @param {String} text - @return {Promise<String>} - */ - renderInline: function(type, text) { - var parser = parsers.get(type); - - return parser.parseInline(text) - .get('content'); - }, - - template: { - /** - Apply a templating block and returns its result - - @param {String} name - @param {Object} blockData - @return {Promise|Object} - */ - applyBlock: function(name, blockData) { - var block = blocks.get(name) || defaultBlocks.get(name); - return Promise(block.applyBlock(blockData, result)); - } - }, - - output: { - /** - Name of the generator being used - {String} - */ - name: output.getGenerator(), - - /** - Return absolute path to the root folder of output - @return {String} - */ - root: function() { - return outputFolder; - }, - - /** - Resolve a file from the output root - - @param {String} fileName - @return {String} - */ - resolve: function(fileName) { - return path.resolve(outputFolder, fileName); - }, - - /** - Convert a filepath into an url - @return {String} - */ - toURL: function(filePath) { - return fileToURL(output, filePath); - }, - - /** - Check that a file exists. - - @param {String} fileName - @return {Promise} - */ - hasFile: function(fileName, content) { - return Promise() - .then(function() { - var filePath = PathUtils.resolveInRoot(outputFolder, fileName); - - return fs.exists(filePath); - }); - }, - - /** - Write a file to the output folder, - It creates the required folder - - @param {String} fileName - @param {Buffer} content - @return {Promise} - */ - writeFile: function(fileName, content) { - return Promise() - .then(function() { - var filePath = PathUtils.resolveInRoot(outputFolder, fileName); - - return fs.ensureFile(filePath) - .then(function() { - return fs.writeFile(filePath, content); - }); - }); - }, - - /** - Copy a file to the output folder - It creates the required folder. - - @param {String} inputFile - @param {String} outputFile - @param {Buffer} content - @return {Promise} - */ - copyFile: function(inputFile, outputFile, content) { - return Promise() - .then(function() { - var outputFilePath = PathUtils.resolveInRoot(outputFolder, outputFile); - - return fs.ensureFile(outputFilePath) - .then(function() { - return fs.copy(inputFile, outputFilePath); - }); - }); - } - }, - - gitbook: { - version: gitbook.version - } - }; - - // Deprecated properties - - deprecate.renamedMethod(output, 'this.isSubBook', result, 'isSubBook', 'isLanguageBook'); - deprecate.renamedMethod(output, 'this.contentLink', result, 'contentLink', 'output.toURL'); - - deprecate.field(output, 'this.generator', result, 'generator', - output.getGenerator(), '"this.generator" property is deprecated, use "this.output.name" instead'); - - deprecate.field(output, 'this.navigation', result, 'navigation', function() { - return encodeNavigation(output); - }, '"navigation" property is deprecated'); - - deprecate.field(output, 'this.book', result, 'book', - result, '"book" property is deprecated, use "this" directly instead'); - - deprecate.field(output, 'this.options', result, 'options', - result.config.values, '"options" property is deprecated, use config.get(key) instead'); - - return result; -} - -module.exports = encodeGlobal; diff --git a/lib/api/encodeNavigation.js b/lib/api/encodeNavigation.js deleted file mode 100644 index 8e329a1..0000000 --- a/lib/api/encodeNavigation.js +++ /dev/null @@ -1,64 +0,0 @@ -var Immutable = require('immutable'); - -/** - Encode an article for next/prev - - @param {Map<String:Page>} - @param {Article} - @return {Object} -*/ -function encodeArticle(pages, article) { - var articlePath = article.getPath(); - - return { - path: articlePath, - title: article.getTitle(), - level: article.getLevel(), - exists: (articlePath && pages.has(articlePath)), - external: article.isExternal() - }; -} - -/** - this.navigation is a deprecated property from GitBook v2 - - @param {Output} - @return {Object} -*/ -function encodeNavigation(output) { - var book = output.getBook(); - var pages = output.getPages(); - var summary = book.getSummary(); - var articles = summary.getArticlesAsList(); - - - var navigation = articles - .map(function(article, i) { - var ref = article.getRef(); - if (!ref) { - return undefined; - } - - var prev = articles.get(i - 1); - var next = articles.get(i + 1); - - return [ - ref, - { - index: i, - title: article.getTitle(), - introduction: (i === 0), - prev: prev? encodeArticle(pages, prev) : undefined, - next: next? encodeArticle(pages, next) : undefined, - level: article.getLevel() - } - ]; - }) - .filter(function(e) { - return Boolean(e); - }); - - return Immutable.Map(navigation).toJS(); -} - -module.exports = encodeNavigation; diff --git a/lib/api/encodePage.js b/lib/api/encodePage.js deleted file mode 100644 index 379d3d5..0000000 --- a/lib/api/encodePage.js +++ /dev/null @@ -1,39 +0,0 @@ -var JSONUtils = require('../json'); -var deprecate = require('./deprecate'); -var encodeProgress = require('./encodeProgress'); - -/** - Encode a page in a context to a JS API - - @param {Output} output - @param {Page} page - @return {Object} -*/ -function encodePage(output, page) { - var book = output.getBook(); - var summary = book.getSummary(); - var fs = book.getContentFS(); - var file = page.getFile(); - - // JS Page is based on the JSON output - var result = JSONUtils.encodePage(page, summary); - - result.type = file.getType(); - result.path = file.getPath(); - result.rawPath = fs.resolve(result.path); - - deprecate.field(output, 'page.progress', result, 'progress', function() { - return encodeProgress(output, page); - }, '"page.progress" property is deprecated'); - - deprecate.field(output, 'page.sections', result, 'sections', [ - { - content: result.content, - type: 'normal' - } - ], '"sections" property is deprecated, use page.content instead'); - - return result; -} - -module.exports = encodePage; diff --git a/lib/api/encodeProgress.js b/lib/api/encodeProgress.js deleted file mode 100644 index afa0341..0000000 --- a/lib/api/encodeProgress.js +++ /dev/null @@ -1,63 +0,0 @@ -var Immutable = require('immutable'); -var encodeNavigation = require('./encodeNavigation'); - -/** - page.progress is a deprecated property from GitBook v2 - - @param {Output} - @param {Page} - @return {Object} -*/ -function encodeProgress(output, page) { - var current = page.getPath(); - var navigation = encodeNavigation(output); - navigation = Immutable.Map(navigation); - - var n = navigation.size; - var percent = 0, prevPercent = 0, currentChapter = null; - var done = true; - - var chapters = navigation - .map(function(nav, chapterPath) { - nav.path = chapterPath; - return nav; - }) - .valueSeq() - .sortBy(function(nav) { - return nav.index; - }) - .map(function(nav, i) { - // Calcul percent - nav.percent = (i * 100) / Math.max((n - 1), 1); - - // Is it done - nav.done = done; - if (nav.path == current) { - currentChapter = nav; - percent = nav.percent; - done = false; - } else if (done) { - prevPercent = nav.percent; - } - - return nav; - }) - .toJS(); - - return { - // Previous percent - prevPercent: prevPercent, - - // Current percent - percent: percent, - - // List of chapter with progress - chapters: chapters, - - // Current chapter - current: currentChapter - }; -} - -module.exports = encodeProgress; - diff --git a/lib/api/encodeSummary.js b/lib/api/encodeSummary.js deleted file mode 100644 index 0d66ded..0000000 --- a/lib/api/encodeSummary.js +++ /dev/null @@ -1,51 +0,0 @@ -var encodeSummaryArticle = require('../json/encodeSummaryArticle'); - -/** - Encode summary to provide an API to plugin - - @param {Output} output - @param {Config} config - @return {Object} -*/ -function encodeSummary(output, summary) { - var result = { - /** - Iterate over the summary, it stops when the "iter" returns false - - @param {Function} iter - */ - walk: function (iter) { - summary.getArticle(function(article) { - var jsonArticle = encodeSummaryArticle(article, false); - - return iter(jsonArticle); - }); - }, - - /** - Get an article by its level - - @param {String} level - @return {Object} - */ - getArticleByLevel: function(level) { - var article = summary.getByLevel(level); - return (article? encodeSummaryArticle(article) : undefined); - }, - - /** - Get an article by its path - - @param {String} level - @return {Object} - */ - getArticleByPath: function(level) { - var article = summary.getByPath(level); - return (article? encodeSummaryArticle(article) : undefined); - } - }; - - return result; -} - -module.exports = encodeSummary; diff --git a/lib/api/index.js b/lib/api/index.js deleted file mode 100644 index 5e67525..0000000 --- a/lib/api/index.js +++ /dev/null @@ -1,8 +0,0 @@ - -module.exports = { - encodePage: require('./encodePage'), - decodePage: require('./decodePage'), - - encodeGlobal: require('./encodeGlobal'), - decodeGlobal: require('./decodeGlobal') -}; diff --git a/lib/browser.js b/lib/browser.js deleted file mode 100644 index 87a4dc4..0000000 --- a/lib/browser.js +++ /dev/null @@ -1,26 +0,0 @@ -var Modifiers = require('./modifiers'); - -module.exports = { - Parse: require('./parse'), - - // Models - Book: require('./models/book'), - FS: require('./models/fs'), - File: require('./models/file'), - Summary: require('./models/summary'), - Glossary: require('./models/glossary'), - Config: require('./models/config'), - Page: require('./models/page'), - PluginDependency: require('./models/pluginDependency'), - - // Modifiers - SummaryModifier: Modifiers.Summary, - ConfigModifier: Modifiers.Config, - - // Constants - CONFIG_FILES: require('./constants/configFiles.js'), - IGNORE_FILES: require('./constants/ignoreFiles.js'), - DEFAULT_PLUGINS: require('./constants/defaultPlugins'), - EXTENSIONS_MARKDOWN: require('./constants/extsMarkdown'), - EXTENSIONS_ASCIIDOC: require('./constants/extsAsciidoc') -}; diff --git a/lib/cli/build.js b/lib/cli/build.js deleted file mode 100644 index 023901e..0000000 --- a/lib/cli/build.js +++ /dev/null @@ -1,34 +0,0 @@ -var Parse = require('../parse'); -var Output = require('../output'); -var timing = require('../utils/timing'); - -var options = require('./options'); -var getBook = require('./getBook'); -var getOutputFolder = require('./getOutputFolder'); - - -module.exports = { - name: 'build [book] [output]', - description: 'build a book', - options: [ - options.log, - options.format, - options.timing - ], - exec: function(args, kwargs) { - var book = getBook(args, kwargs); - var outputFolder = getOutputFolder(args); - - var Generator = Output.getGenerator(kwargs.format); - - return Parse.parseBook(book) - .then(function(resultBook) { - return Output.generate(Generator, resultBook, { - root: outputFolder - }); - }) - .fin(function() { - if (kwargs.timing) timing.dump(book.getLogger()); - }); - } -}; diff --git a/lib/cli/buildEbook.js b/lib/cli/buildEbook.js deleted file mode 100644 index a87fac7..0000000 --- a/lib/cli/buildEbook.js +++ /dev/null @@ -1,78 +0,0 @@ -var path = require('path'); -var tmp = require('tmp'); - -var Promise = require('../utils/promise'); -var fs = require('../utils/fs'); -var Parse = require('../parse'); -var Output = require('../output'); - -var options = require('./options'); -var getBook = require('./getBook'); - - -module.exports = function(format) { - return { - name: (format + ' [book] [output]'), - description: 'build a book into an ebook file', - options: [ - options.log - ], - exec: function(args, kwargs) { - var extension = '.' + format; - - // Output file will be stored in - var outputFile = args[1] || ('book' + extension); - - // Create temporary directory - var outputFolder = tmp.dirSync().name; - - var book = getBook(args, kwargs); - var logger = book.getLogger(); - var Generator = Output.getGenerator('ebook'); - - return Parse.parseBook(book) - .then(function(resultBook) { - return Output.generate(Generator, resultBook, { - root: outputFolder, - format: format - }); - }) - - // Extract ebook file - .then(function(output) { - var book = output.getBook(); - var languages = book.getLanguages(); - - if (book.isMultilingual()) { - return Promise.forEach(languages.getList(), function(lang) { - var langID = lang.getID(); - - var langOutputFile = path.join( - path.dirname(outputFile), - path.basename(outputFile, extension) + '_' + langID + extension - ); - - return fs.copy( - path.resolve(outputFolder, langID, 'index' + extension), - langOutputFile - ); - }) - .thenResolve(languages.getCount()); - } else { - return fs.copy( - path.resolve(outputFolder, 'index' + extension), - outputFile - ).thenResolve(1); - } - }) - - // Log end - .then(function(count) { - logger.info.ok(count + ' file(s) generated'); - - logger.debug('cleaning up... '); - return logger.debug.promise(fs.rmDir(outputFolder)); - }); - } - }; -}; diff --git a/lib/cli/getBook.js b/lib/cli/getBook.js deleted file mode 100644 index ac82187..0000000 --- a/lib/cli/getBook.js +++ /dev/null @@ -1,23 +0,0 @@ -var path = require('path'); -var Book = require('../models/book'); -var createNodeFS = require('../fs/node'); - -/** - Return a book instance to work on from - command line args/kwargs - - @param {Array} args - @param {Object} kwargs - @return {Book} -*/ -function getBook(args, kwargs) { - var input = path.resolve(args[0] || process.cwd()); - var logLevel = kwargs.log; - - var fs = createNodeFS(input); - var book = Book.createForFS(fs); - - return book.setLogLevel(logLevel); -} - -module.exports = getBook; diff --git a/lib/cli/getOutputFolder.js b/lib/cli/getOutputFolder.js deleted file mode 100644 index 272dff9..0000000 --- a/lib/cli/getOutputFolder.js +++ /dev/null @@ -1,17 +0,0 @@ -var path = require('path'); - -/** - Return path to output folder - - @param {Array} args - @return {String} -*/ -function getOutputFolder(args) { - var bookRoot = path.resolve(args[0] || process.cwd()); - var defaultOutputRoot = path.join(bookRoot, '_book'); - var outputFolder = args[1]? path.resolve(process.cwd(), args[1]) : defaultOutputRoot; - - return outputFolder; -} - -module.exports = getOutputFolder; diff --git a/lib/cli/index.js b/lib/cli/index.js deleted file mode 100644 index f1fca1d..0000000 --- a/lib/cli/index.js +++ /dev/null @@ -1,12 +0,0 @@ -var buildEbook = require('./buildEbook'); - -module.exports = [ - require('./build'), - require('./serve'), - require('./install'), - require('./parse'), - require('./init'), - buildEbook('pdf'), - buildEbook('epub'), - buildEbook('mobi') -]; diff --git a/lib/cli/init.js b/lib/cli/init.js deleted file mode 100644 index 55f1b15..0000000 --- a/lib/cli/init.js +++ /dev/null @@ -1,17 +0,0 @@ -var path = require('path'); - -var options = require('./options'); -var initBook = require('../init'); - -module.exports = { - name: 'init [book]', - description: 'setup and create files for chapters', - options: [ - options.log - ], - exec: function(args, kwargs) { - var bookRoot = path.resolve(process.cwd(), args[0] || './'); - - return initBook(bookRoot); - } -}; diff --git a/lib/cli/install.js b/lib/cli/install.js deleted file mode 100644 index c001711..0000000 --- a/lib/cli/install.js +++ /dev/null @@ -1,21 +0,0 @@ -var options = require('./options'); -var getBook = require('./getBook'); - -var Parse = require('../parse'); -var Plugins = require('../plugins'); - -module.exports = { - name: 'install [book]', - description: 'install all plugins dependencies', - options: [ - options.log - ], - exec: function(args, kwargs) { - var book = getBook(args, kwargs); - - return Parse.parseConfig(book) - .then(function(resultBook) { - return Plugins.installPlugins(resultBook); - }); - } -}; diff --git a/lib/cli/options.js b/lib/cli/options.js deleted file mode 100644 index 72961ab..0000000 --- a/lib/cli/options.js +++ /dev/null @@ -1,31 +0,0 @@ -var Logger = require('../utils/logger'); - -var logOptions = { - name: 'log', - description: 'Minimum log level to display', - values: Logger.LEVELS - .keySeq() - .map(function(s) { - return s.toLowerCase(); - }).toJS(), - defaults: 'info' -}; - -var formatOption = { - name: 'format', - description: 'Format to build to', - values: ['website', 'json', 'ebook'], - defaults: 'website' -}; - -var timingOption = { - name: 'timing', - description: 'Print timing debug information', - defaults: false -}; - -module.exports = { - log: logOptions, - format: formatOption, - timing: timingOption -}; diff --git a/lib/cli/parse.js b/lib/cli/parse.js deleted file mode 100644 index 0fa509a..0000000 --- a/lib/cli/parse.js +++ /dev/null @@ -1,79 +0,0 @@ -var options = require('./options'); -var getBook = require('./getBook'); - -var Parse = require('../parse'); - -function printBook(book) { - var logger = book.getLogger(); - - var config = book.getConfig(); - var configFile = config.getFile(); - - var summary = book.getSummary(); - var summaryFile = summary.getFile(); - - var readme = book.getReadme(); - var readmeFile = readme.getFile(); - - var glossary = book.getGlossary(); - var glossaryFile = glossary.getFile(); - - if (configFile.exists()) { - logger.info.ln('Configuration file is', configFile.getPath()); - } - - if (readmeFile.exists()) { - logger.info.ln('Introduction file is', readmeFile.getPath()); - } - - if (glossaryFile.exists()) { - logger.info.ln('Glossary file is', glossaryFile.getPath()); - } - - if (summaryFile.exists()) { - logger.info.ln('Table of Contents file is', summaryFile.getPath()); - } -} - -function printMultingualBook(book) { - var logger = book.getLogger(); - var languages = book.getLanguages(); - var books = book.getBooks(); - - logger.info.ln(languages.size + ' languages'); - - languages.forEach(function(lang) { - logger.info.ln('Language:', lang.getTitle()); - printBook(books.get(lang.getID())); - logger.info.ln(''); - }); -} - -module.exports = { - name: 'parse [book]', - description: 'parse and print debug information about a book', - options: [ - options.log - ], - exec: function(args, kwargs) { - var book = getBook(args, kwargs); - var logger = book.getLogger(); - - return Parse.parseBook(book) - .then(function(resultBook) { - var rootFolder = book.getRoot(); - var contentFolder = book.getContentRoot(); - - logger.info.ln('Book located in:', rootFolder); - if (contentFolder != rootFolder) { - logger.info.ln('Content located in:', contentFolder); - } - - if (resultBook.isMultilingual()) { - printMultingualBook(resultBook); - } else { - printBook(resultBook); - } - }); - } -}; diff --git a/lib/cli/serve.js b/lib/cli/serve.js deleted file mode 100644 index 5340851..0000000 --- a/lib/cli/serve.js +++ /dev/null @@ -1,159 +0,0 @@ -/* eslint-disable no-console */ - -var tinylr = require('tiny-lr'); -var open = require('open'); - -var Parse = require('../parse'); -var Output = require('../output'); -var ConfigModifier = require('../modifiers').Config; - -var Promise = require('../utils/promise'); - -var options = require('./options'); -var getBook = require('./getBook'); -var getOutputFolder = require('./getOutputFolder'); -var Server = require('./server'); -var watch = require('./watch'); - -var server, lrServer, lrPath; - -function waitForCtrlC() { - var d = Promise.defer(); - - process.on('SIGINT', function() { - d.resolve(); - }); - - return d.promise; -} - - -function generateBook(args, kwargs) { - var port = kwargs.port; - var outputFolder = getOutputFolder(args); - var book = getBook(args, kwargs); - var Generator = Output.getGenerator(kwargs.format); - var browser = kwargs['browser']; - - var hasWatch = kwargs['watch']; - var hasLiveReloading = kwargs['live']; - var hasOpen = kwargs['open']; - - // Stop server if running - if (server.isRunning()) console.log('Stopping server'); - - return server.stop() - .then(function() { - return Parse.parseBook(book) - .then(function(resultBook) { - if (hasLiveReloading) { - // Enable livereload plugin - var config = resultBook.getConfig(); - config = ConfigModifier.addPlugin(config, 'livereload'); - resultBook = resultBook.set('config', config); - } - - return Output.generate(Generator, resultBook, { - root: outputFolder - }); - }); - }) - .then(function() { - console.log(); - console.log('Starting server ...'); - return server.start(outputFolder, port); - }) - .then(function() { - console.log('Serving book on http://localhost:'+port); - - if (lrPath && hasLiveReloading) { - // trigger livereload - lrServer.changed({ - body: { - files: [lrPath] - } - }); - } - - if (hasOpen) { - open('http://localhost:'+port, browser); - } - }) - .then(function() { - if (!hasWatch) { - return waitForCtrlC(); - } - - return watch(book.getRoot()) - .then(function(filepath) { - // set livereload path - lrPath = filepath; - console.log('Restart after change in file', filepath); - console.log(''); - return generateBook(args, kwargs); - }); - }); -} - -module.exports = { - name: 'serve [book] [output]', - description: 'serve the book as a website for testing', - options: [ - { - name: 'port', - description: 'Port for server to listen on', - defaults: 4000 - }, - { - name: 'lrport', - description: 'Port for livereload server to listen on', - defaults: 35729 - }, - { - name: 'watch', - description: 'Enable file watcher and live reloading', - defaults: true - }, - { - name: 'live', - description: 'Enable live reloading', - defaults: true - }, - { - name: 'open', - description: 'Enable opening book in browser', - defaults: false - }, - { - name: 'browser', - description: 'Specify browser for opening book', - defaults: '' - }, - options.log, - options.format - ], - exec: function(args, kwargs) { - server = new Server(); - var hasWatch = kwargs['watch']; - var hasLiveReloading = kwargs['live']; - - return Promise() - .then(function() { - if (!hasWatch || !hasLiveReloading) { - return; - } - - lrServer = tinylr({}); - return Promise.nfcall(lrServer.listen.bind(lrServer), kwargs.lrport) - .then(function() { - console.log('Live reload server started on port:', kwargs.lrport); - console.log('Press CTRL+C to quit ...'); - console.log(''); - - }); - }) - .then(function() { - return generateBook(args, kwargs); - }); - } -}; diff --git a/lib/cli/server.js b/lib/cli/server.js deleted file mode 100644 index 752f867..0000000 --- a/lib/cli/server.js +++ /dev/null @@ -1,128 +0,0 @@ -var events = require('events'); -var http = require('http'); -var send = require('send'); -var util = require('util'); -var url = require('url'); - -var Promise = require('../utils/promise'); - -function Server() { - this.running = null; - this.dir = null; - this.port = 0; - this.sockets = []; -} -util.inherits(Server, events.EventEmitter); - -/** - Return true if the server is running - - @return {Boolean} -*/ -Server.prototype.isRunning = function() { - return !!this.running; -}; - -/** - Stop the server - - @return {Promise} -*/ -Server.prototype.stop = function() { - var that = this; - if (!this.isRunning()) return Promise(); - - var d = Promise.defer(); - this.running.close(function(err) { - that.running = null; - that.emit('state', false); - - if (err) d.reject(err); - else d.resolve(); - }); - - for (var i = 0; i < this.sockets.length; i++) { - this.sockets[i].destroy(); - } - - return d.promise; -}; - -/** - Start the server - - @return {Promise} -*/ -Server.prototype.start = function(dir, port) { - var that = this, pre = Promise(); - port = port || 8004; - - if (that.isRunning()) pre = this.stop(); - return pre - .then(function() { - var d = Promise.defer(); - - that.running = http.createServer(function(req, res){ - // Render error - function error(err) { - res.statusCode = err.status || 500; - res.end(err.message); - } - - // Redirect to directory's index.html - function redirect() { - var resultURL = urlTransform(req.url, function(parsed) { - parsed.pathname += '/'; - return parsed; - }); - - res.statusCode = 301; - res.setHeader('Location', resultURL); - res.end('Redirecting to ' + resultURL); - } - - res.setHeader('X-Current-Location', req.url); - - // Send file - send(req, url.parse(req.url).pathname, { - root: dir - }) - .on('error', error) - .on('directory', redirect) - .pipe(res); - }); - - that.running.on('connection', function (socket) { - that.sockets.push(socket); - socket.setTimeout(4000); - socket.on('close', function () { - that.sockets.splice(that.sockets.indexOf(socket), 1); - }); - }); - - that.running.listen(port, function(err) { - if (err) return d.reject(err); - - that.port = port; - that.dir = dir; - that.emit('state', true); - d.resolve(); - }); - - return d.promise; - }); -}; - -/** - urlTransform is a helper function that allows a function to transform - a url string in it's parsed form and returns the new url as a string - - @param {String} uri - @param {Function} fn - @return {String} -*/ -function urlTransform(uri, fn) { - return url.format(fn(url.parse(uri))); -} - -module.exports = Server; diff --git a/lib/cli/watch.js b/lib/cli/watch.js deleted file mode 100644 index 14434ab..0000000 --- a/lib/cli/watch.js +++ /dev/null @@ -1,46 +0,0 @@ -var path = require('path'); -var chokidar = require('chokidar'); - -var Promise = require('../utils/promise'); -var parsers = require('../parsers'); - -/** - Watch a folder and resolve promise once a file is modified - - @param {String} dir - @return {Promise} -*/ -function watch(dir) { - var d = Promise.defer(); - dir = path.resolve(dir); - - var toWatch = [ - 'book.json', 'book.js', '_layouts/**' - ]; - - // Watch all parsable files - parsers.extensions.forEach(function(ext) { - toWatch.push('**/*'+ext); - }); - - var watcher = chokidar.watch(toWatch, { - cwd: dir, - ignored: '_book/**', - ignoreInitial: true - }); - - watcher.once('all', function(e, filepath) { - watcher.close(); - - d.resolve(filepath); - }); - watcher.once('error', function(err) { - watcher.close(); - - d.reject(err); - }); - - return d.promise; -} - -module.exports = watch; diff --git a/lib/constants/__tests__/configSchema.js b/lib/constants/__tests__/configSchema.js deleted file mode 100644 index efc99b9..0000000 --- a/lib/constants/__tests__/configSchema.js +++ /dev/null @@ -1,46 +0,0 @@ -var jsonschema = require('jsonschema'); -var schema = require('../configSchema'); - -describe('configSchema', function() { - - function validate(cfg) { - var v = new jsonschema.Validator(); - return v.validate(cfg, schema, { - propertyName: 'config' - }); - } - - describe('structure', function() { - - it('should accept dot in filename', function() { - var result = validate({ - structure: { - readme: 'book-intro.adoc' - } - }); - - expect(result.errors.length).toBe(0); - }); - - it('should accept uppercase in filename', function() { - var result = validate({ - structure: { - readme: 'BOOK.adoc' - } - }); - - expect(result.errors.length).toBe(0); - }); - - it('should not accept filepath', function() { - var result = validate({ - structure: { - readme: 'folder/myFile.md' - } - }); - - expect(result.errors.length).toBe(1); - }); - - }); -}); diff --git a/lib/constants/configDefault.js b/lib/constants/configDefault.js deleted file mode 100644 index 0d95883..0000000 --- a/lib/constants/configDefault.js +++ /dev/null @@ -1,6 +0,0 @@ -var Immutable = require('immutable'); -var jsonSchemaDefaults = require('json-schema-defaults'); - -var schema = require('./configSchema'); - -module.exports = Immutable.fromJS(jsonSchemaDefaults(schema)); diff --git a/lib/constants/configFiles.js b/lib/constants/configFiles.js deleted file mode 100644 index a67fd74..0000000 --- a/lib/constants/configFiles.js +++ /dev/null @@ -1,5 +0,0 @@ -// Configuration files to test (sorted) -module.exports = [ - 'book.js', - 'book.json' -]; diff --git a/lib/constants/configSchema.js b/lib/constants/configSchema.js deleted file mode 100644 index d2126c6..0000000 --- a/lib/constants/configSchema.js +++ /dev/null @@ -1,194 +0,0 @@ -var FILENAME_REGEX = '^[a-zA-Z-._\d,\s]+$'; - -module.exports = { - '$schema': 'http://json-schema.org/schema#', - 'id': 'https://gitbook.com/schemas/book.json', - 'title': 'GitBook Configuration', - 'type': 'object', - 'properties': { - 'root': { - 'type': 'string', - 'title': 'Path fro the root folder containing the book\'s content' - }, - 'title': { - 'type': 'string', - 'title': 'Title of the book, default is extracted from README' - }, - 'isbn': { - 'type': 'string', - 'title': 'ISBN for published book' - }, - 'language': { - 'type': 'string', - 'title': 'Language of the book' - }, - 'author': { - 'type': 'string', - 'title': 'Name of the author' - }, - 'gitbook': { - 'type': 'string', - 'default': '*', - 'title': 'GitBook version to match' - }, - 'direction': { - 'type': 'string', - 'enum': ['ltr', 'rtl'], - 'title': 'Direction of texts, default is detected in the pages' - }, - 'theme': { - 'type': 'string', - 'default': 'default', - 'title': 'Name of the theme plugin to use' - }, - 'variables': { - 'type': 'object', - 'title': 'Templating context variables' - }, - 'plugins': { - 'oneOf': [ - { '$ref': '#/definitions/pluginsArray' }, - { '$ref': '#/definitions/pluginsString' } - ], - 'default': [] - }, - 'pluginsConfig': { - 'type': 'object', - 'title': 'Configuration for plugins' - }, - 'structure': { - 'type': 'object', - 'properties': { - 'langs': { - 'default': 'LANGS.md', - 'type': 'string', - 'title': 'File to use as languages index', - 'pattern': FILENAME_REGEX - }, - 'readme': { - 'default': 'README.md', - 'type': 'string', - 'title': 'File to use as preface', - 'pattern': FILENAME_REGEX - }, - 'glossary': { - 'default': 'GLOSSARY.md', - 'type': 'string', - 'title': 'File to use as glossary index', - 'pattern': FILENAME_REGEX - }, - 'summary': { - 'default': 'SUMMARY.md', - 'type': 'string', - 'title': 'File to use as table of contents', - 'pattern': FILENAME_REGEX - } - }, - 'additionalProperties': false - }, - 'pdf': { - 'type': 'object', - 'title': 'PDF specific configurations', - 'properties': { - 'pageNumbers': { - 'type': 'boolean', - 'default': true, - 'title': 'Add page numbers to the bottom of every page' - }, - 'fontSize': { - 'type': 'integer', - 'minimum': 8, - 'maximum': 30, - 'default': 12, - 'title': 'Font size for the PDF output' - }, - 'fontFamily': { - 'type': 'string', - 'default': 'Arial', - 'title': 'Font family for the PDF output' - }, - 'paperSize': { - 'type': 'string', - 'enum': ['a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'legal', 'letter'], - 'default': 'a4', - 'title': 'Paper size for the PDF' - }, - 'chapterMark': { - 'type': 'string', - 'enum': ['pagebreak', 'rule', 'both', 'none'], - 'default': 'pagebreak', - 'title': 'How to mark detected chapters' - }, - 'pageBreaksBefore': { - 'type': 'string', - 'default': '/', - 'title': 'An XPath expression. Page breaks are inserted before the specified elements. To disable use the expression: "/"' - }, - 'margin': { - 'type': 'object', - 'properties': { - 'right': { - 'type': 'integer', - 'title': 'Right Margin', - 'minimum': 0, - 'maximum': 100, - 'default': 62 - }, - 'left': { - 'type': 'integer', - 'title': 'Left Margin', - 'minimum': 0, - 'maximum': 100, - 'default': 62 - }, - 'top': { - 'type': 'integer', - 'title': 'Top Margin', - 'minimum': 0, - 'maximum': 100, - 'default': 56 - }, - 'bottom': { - 'type': 'integer', - 'title': 'Bottom Margin', - 'minimum': 0, - 'maximum': 100, - 'default': 56 - } - } - } - } - } - }, - 'required': [], - 'definitions': { - 'pluginsArray': { - 'type': 'array', - 'items': { - 'oneOf': [ - { '$ref': '#/definitions/pluginObject' }, - { '$ref': '#/definitions/pluginString' } - ] - } - }, - 'pluginsString': { - 'type': 'string' - }, - 'pluginString': { - 'type': 'string' - }, - 'pluginObject': { - 'type': 'object', - 'properties': { - 'name': { - 'type': 'string' - }, - 'version': { - 'type': 'string' - } - }, - 'additionalProperties': false, - 'required': ['name'] - } - } -}; diff --git a/lib/constants/defaultBlocks.js b/lib/constants/defaultBlocks.js deleted file mode 100644 index 74d1f1f..0000000 --- a/lib/constants/defaultBlocks.js +++ /dev/null @@ -1,51 +0,0 @@ -var Immutable = require('immutable'); -var TemplateBlock = require('../models/templateBlock'); - -module.exports = Immutable.Map({ - html: TemplateBlock({ - name: 'html', - process: function(blk) { - return blk; - } - }), - - code: TemplateBlock({ - name: 'code', - process: function(blk) { - return { - html: false, - body: blk.body - }; - } - }), - - markdown: TemplateBlock({ - name: 'markdown', - process: function(blk) { - return this.book.renderInline('markdown', blk.body) - .then(function(out) { - return { body: out }; - }); - } - }), - - asciidoc: TemplateBlock({ - name: 'asciidoc', - process: function(blk) { - return this.book.renderInline('asciidoc', blk.body) - .then(function(out) { - return { body: out }; - }); - } - }), - - markup: TemplateBlock({ - name: 'markup', - process: function(blk) { - return this.book.renderInline(this.ctx.file.type, blk.body) - .then(function(out) { - return { body: out }; - }); - } - }) -}); diff --git a/lib/constants/defaultFilters.js b/lib/constants/defaultFilters.js deleted file mode 100644 index 35025cc..0000000 --- a/lib/constants/defaultFilters.js +++ /dev/null @@ -1,15 +0,0 @@ -var Immutable = require('immutable'); -var moment = require('moment'); - -module.exports = Immutable.Map({ - // Format a date - // ex: 'MMMM Do YYYY, h:mm:ss a - date: function(time, format) { - return moment(time).format(format); - }, - - // Relative Time - dateFromNow: function(time) { - return moment(time).fromNow(); - } -}); diff --git a/lib/constants/defaultPlugins.js b/lib/constants/defaultPlugins.js deleted file mode 100644 index 6d15971..0000000 --- a/lib/constants/defaultPlugins.js +++ /dev/null @@ -1,29 +0,0 @@ -var Immutable = require('immutable'); -var PluginDependency = require('../models/pluginDependency'); - -var pkg = require('../../package.json'); - -/** - * Create a PluginDependency from a dependency of gitbook - * @param {String} pluginName - * @return {PluginDependency} - */ -function createFromDependency(pluginName) { - var npmID = PluginDependency.nameToNpmID(pluginName); - var version = pkg.dependencies[npmID]; - - return PluginDependency.create(pluginName, version); -} - -/* - * List of default plugins for all books, - * default plugins should be installed in node dependencies of GitBook - */ -module.exports = Immutable.List([ - 'highlight', - 'search', - 'lunr', - 'sharing', - 'fontsettings', - 'theme-default' -]).map(createFromDependency); diff --git a/lib/constants/extsAsciidoc.js b/lib/constants/extsAsciidoc.js deleted file mode 100644 index b2f4ce4..0000000 --- a/lib/constants/extsAsciidoc.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = [ - '.adoc', - '.asciidoc' -]; diff --git a/lib/constants/extsMarkdown.js b/lib/constants/extsMarkdown.js deleted file mode 100644 index 44bf36b..0000000 --- a/lib/constants/extsMarkdown.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = [ - '.md', - '.markdown', - '.mdown' -]; diff --git a/lib/constants/ignoreFiles.js b/lib/constants/ignoreFiles.js deleted file mode 100644 index aac225e..0000000 --- a/lib/constants/ignoreFiles.js +++ /dev/null @@ -1,6 +0,0 @@ -// Files containing ignore pattner (sorted by priority) -module.exports = [ - '.ignore', - '.gitignore', - '.bookignore' -]; diff --git a/lib/constants/pluginAssetsFolder.js b/lib/constants/pluginAssetsFolder.js deleted file mode 100644 index cd44722..0000000 --- a/lib/constants/pluginAssetsFolder.js +++ /dev/null @@ -1,2 +0,0 @@ - -module.exports = '_assets'; diff --git a/lib/constants/pluginHooks.js b/lib/constants/pluginHooks.js deleted file mode 100644 index 2d5dcaa..0000000 --- a/lib/constants/pluginHooks.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = [ - 'init', - 'finish', - 'finish:before', - 'config', - 'page', - 'page:before' -]; diff --git a/lib/constants/pluginPrefix.js b/lib/constants/pluginPrefix.js deleted file mode 100644 index c7f2dd0..0000000 --- a/lib/constants/pluginPrefix.js +++ /dev/null @@ -1,5 +0,0 @@ - -/* - All GitBook plugins are NPM packages starting with this prefix. -*/ -module.exports = 'gitbook-plugin-'; diff --git a/lib/constants/pluginResources.js b/lib/constants/pluginResources.js deleted file mode 100644 index ae283bf..0000000 --- a/lib/constants/pluginResources.js +++ /dev/null @@ -1,6 +0,0 @@ -var Immutable = require('immutable'); - -module.exports = Immutable.List([ - 'js', - 'css' -]); diff --git a/lib/constants/templatesFolder.js b/lib/constants/templatesFolder.js deleted file mode 100644 index aad6a72..0000000 --- a/lib/constants/templatesFolder.js +++ /dev/null @@ -1,2 +0,0 @@ - -module.exports = '_layouts'; diff --git a/lib/constants/themePrefix.js b/lib/constants/themePrefix.js deleted file mode 100644 index 99428de..0000000 --- a/lib/constants/themePrefix.js +++ /dev/null @@ -1,4 +0,0 @@ -/* - All GitBook themes plugins name start with this prefix once shorted. -*/ -module.exports = 'theme-';
\ No newline at end of file diff --git a/lib/fs/__tests__/mock.js b/lib/fs/__tests__/mock.js deleted file mode 100644 index 04bd46a..0000000 --- a/lib/fs/__tests__/mock.js +++ /dev/null @@ -1,82 +0,0 @@ -var createMockFS = require('../mock'); - -describe('MockFS', function() { - var fs = createMockFS({ - 'README.md': 'Hello World', - 'SUMMARY.md': '# Summary', - 'folder': { - 'test.md': 'Cool', - 'folder2': { - 'hello.md': 'Hello', - 'world.md': 'World' - } - } - }); - - describe('exists', function() { - it('must return true for a file', function() { - return fs.exists('README.md') - .then(function(result) { - expect(result).toBeTruthy(); - }); - }); - - it('must return false for a non existing file', function() { - return fs.exists('README_NOTEXISTS.md') - .then(function(result) { - expect(result).toBeFalsy(); - }); - }); - - it('must return true for a directory', function() { - return fs.exists('folder') - .then(function(result) { - expect(result).toBeTruthy(); - }); - }); - - it('must return true for a deep file', function() { - return fs.exists('folder/test.md') - .then(function(result) { - expect(result).toBeTruthy(); - }); - }); - - it('must return true for a deep file (2)', function() { - return fs.exists('folder/folder2/hello.md') - .then(function(result) { - expect(result).toBeTruthy(); - }); - }); - }); - - describe('readAsString', function() { - it('must return content for a file', function() { - return fs.readAsString('README.md') - .then(function(result) { - expect(result).toBe('Hello World'); - }); - }); - - it('must return content for a deep file', function() { - return fs.readAsString('folder/test.md') - .then(function(result) { - expect(result).toBe('Cool'); - }); - }); - }); - - describe('readDir', function() { - it('must return content for a directory', function() { - return fs.readDir('./') - .then(function(files) { - expect(files.size).toBe(3); - expect(files.includes('README.md')).toBeTruthy(); - expect(files.includes('SUMMARY.md')).toBeTruthy(); - expect(files.includes('folder/')).toBeTruthy(); - }); - }); - }); -}); - - diff --git a/lib/fs/mock.js b/lib/fs/mock.js deleted file mode 100644 index 784c533..0000000 --- a/lib/fs/mock.js +++ /dev/null @@ -1,95 +0,0 @@ -var path = require('path'); -var is = require('is'); -var Buffer = require('buffer').Buffer; -var Immutable = require('immutable'); - -var FS = require('../models/fs'); -var error = require('../utils/error'); - -/** - Create a fake filesystem for unit testing GitBook. - - @param {Map<String:String|Map>} -*/ -function createMockFS(files) { - files = Immutable.fromJS(files); - var mtime = new Date(); - - function getFile(filePath) { - var parts = path.normalize(filePath).split(path.sep); - return parts.reduce(function(list, part, i) { - if (!list) return null; - - var file; - - if (!part || part === '.') file = list; - else file = list.get(part); - - if (!file) return null; - - if (is.string(file)) { - if (i === (parts.length - 1)) return file; - else return null; - } - - return file; - }, files); - } - - function fsExists(filePath) { - return Boolean(getFile(filePath) !== null); - } - - function fsReadFile(filePath) { - var file = getFile(filePath); - if (!is.string(file)) { - throw error.FileNotFoundError({ - filename: filePath - }); - } - - return new Buffer(file, 'utf8'); - } - - function fsStatFile(filePath) { - var file = getFile(filePath); - if (!file) { - throw error.FileNotFoundError({ - filename: filePath - }); - } - - return { - mtime: mtime - }; - } - - function fsReadDir(filePath) { - var dir = getFile(filePath); - if (!dir || is.string(dir)) { - throw error.FileNotFoundError({ - filename: filePath - }); - } - - return dir - .map(function(content, name) { - if (!is.string(content)) { - name = name + '/'; - } - - return name; - }) - .valueSeq(); - } - - return FS.create({ - root: '', - fsExists: fsExists, - fsReadFile: fsReadFile, - fsStatFile: fsStatFile, - fsReadDir: fsReadDir - }); -} - -module.exports = createMockFS; diff --git a/lib/fs/node.js b/lib/fs/node.js deleted file mode 100644 index dfe9fae..0000000 --- a/lib/fs/node.js +++ /dev/null @@ -1,42 +0,0 @@ -var path = require('path'); -var Immutable = require('immutable'); -var fresh = require('fresh-require'); - -var fs = require('../utils/fs'); -var FS = require('../models/fs'); - -function fsReadDir(folder) { - return fs.readdir(folder) - .then(function(files) { - files = Immutable.List(files); - - return files - .map(function(file) { - if (file == '.' || file == '..') return; - - var stat = fs.statSync(path.join(folder, file)); - if (stat.isDirectory()) file = file + path.sep; - return file; - }) - .filter(function(file) { - return Boolean(file); - }); - }); -} - -function fsLoadObject(filename) { - return fresh(filename, require); -} - -module.exports = function createNodeFS(root) { - return FS.create({ - root: root, - - fsExists: fs.exists, - fsReadFile: fs.readFile, - fsStatFile: fs.stat, - fsReadDir: fsReadDir, - fsLoadObject: fsLoadObject, - fsReadAsStream: fs.readStream - }); -}; diff --git a/lib/gitbook.js b/lib/gitbook.js deleted file mode 100644 index bafd3b8..0000000 --- a/lib/gitbook.js +++ /dev/null @@ -1,28 +0,0 @@ -var semver = require('semver'); -var pkg = require('../package.json'); - -var VERSION = pkg.version; -var VERSION_STABLE = VERSION.replace(/\-(\S+)/g, ''); - -var START_TIME = new Date(); - -/** - Verify that this gitbook version satisfies a requirement - We can't directly use samver.satisfies since it will break all plugins when gitbook version is a prerelease (beta, alpha) - - @param {String} condition - @return {Boolean} -*/ -function satisfies(condition) { - // Test with real version - if (semver.satisfies(VERSION, condition)) return true; - - // Test with future stable release - return semver.satisfies(VERSION_STABLE, condition); -} - -module.exports = { - version: pkg.version, - satisfies: satisfies, - START_TIME: START_TIME -}; diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index 1f683e2..0000000 --- a/lib/index.js +++ /dev/null @@ -1,10 +0,0 @@ -var extend = require('extend'); - -var common = require('./browser'); - -module.exports = extend({ - initBook: require('./init'), - createNodeFS: require('./fs/node'), - Output: require('./output'), - commands: require('./cli') -}, common); diff --git a/lib/init.js b/lib/init.js deleted file mode 100644 index c112d4d..0000000 --- a/lib/init.js +++ /dev/null @@ -1,83 +0,0 @@ -var path = require('path'); - -var createNodeFS = require('./fs/node'); -var fs = require('./utils/fs'); -var Promise = require('./utils/promise'); -var File = require('./models/file'); -var Readme = require('./models/readme'); -var Book = require('./models/book'); -var Parse = require('./parse'); - -/** - Initialize folder structure for a book - Read SUMMARY to created the right chapter - - @param {Book} - @param {String} - @return {Promise} -*/ -function initBook(rootFolder) { - var extension = '.md'; - - return fs.mkdirp(rootFolder) - - // Parse the summary and readme - .then(function() { - var fs = createNodeFS(rootFolder); - var book = Book.createForFS(fs); - - return Parse.parseReadme(book) - - // Setup default readme if doesn't found one - .fail(function() { - var readmeFile = File.createWithFilepath('README' + extension); - var readme = Readme.create(readmeFile); - return book.setReadme(readme); - }); - }) - .then(Parse.parseSummary) - - .then(function(book) { - var logger = book.getLogger(); - var summary = book.getSummary(); - var summaryFile = summary.getFile(); - var summaryFilename = summaryFile.getPath() || ('SUMMARY' + extension); - - var articles = summary.getArticlesAsList(); - - // Write pages - return Promise.forEach(articles, function(article) { - var articlePath = article.getPath(); - var filePath = articlePath? path.join(rootFolder, articlePath) : null; - if (!filePath) { - return; - } - - return fs.assertFile(filePath, function() { - return fs.ensureFile(filePath) - .then(function() { - logger.info.ln('create', article.getPath()); - return fs.writeFile(filePath, '# ' + article.getTitle() + '\n\n'); - }); - }); - }) - - // Write summary - .then(function() { - var filePath = path.join(rootFolder, summaryFilename); - - return fs.ensureFile(filePath) - .then(function() { - logger.info.ln('create ' + path.basename(filePath)); - return fs.writeFile(filePath, summary.toText(extension)); - }); - }) - - // Log end - .then(function() { - logger.info.ln('initialization is finished'); - }); - }); -} - -module.exports = initBook; diff --git a/lib/json/encodeBook.js b/lib/json/encodeBook.js deleted file mode 100644 index 9d7ec77..0000000 --- a/lib/json/encodeBook.js +++ /dev/null @@ -1,39 +0,0 @@ -var extend = require('extend'); - -var gitbook = require('../gitbook'); -var encodeSummary = require('./encodeSummary'); -var encodeGlossary = require('./encodeGlossary'); -var encodeReadme = require('./encodeReadme'); -var encodeLanguages = require('./encodeLanguages'); - -/** - Encode a book to JSON - - @param {Book} - @return {Object} -*/ -function encodeBookToJson(book) { - var config = book.getConfig(); - var language = book.getLanguage(); - - var variables = config.getValue('variables', {}); - - return { - summary: encodeSummary(book.getSummary()), - glossary: encodeGlossary(book.getGlossary()), - readme: encodeReadme(book.getReadme()), - config: book.getConfig().getValues().toJS(), - - languages: book.isMultilingual()? encodeLanguages(book.getLanguages()) : undefined, - - gitbook: { - version: gitbook.version, - time: gitbook.START_TIME - }, - book: extend({ - language: language? language : undefined - }, variables.toJS()) - }; -} - -module.exports = encodeBookToJson; diff --git a/lib/json/encodeBookWithPage.js b/lib/json/encodeBookWithPage.js deleted file mode 100644 index 1c5c7a3..0000000 --- a/lib/json/encodeBookWithPage.js +++ /dev/null @@ -1,22 +0,0 @@ -var encodeBook = require('./encodeBook'); -var encodePage = require('./encodePage'); -var encodeFile = require('./encodeFile'); - -/** - * Return a JSON representation of a book with a specific file - * - * @param {Book} output - * @param {Page} page - * @return {Object} - */ -function encodeBookWithPage(book, page) { - var file = page.getFile(); - - var result = encodeBook(book); - result.page = encodePage(page, book.getSummary()); - result.file = encodeFile(file); - - return result; -} - -module.exports = encodeBookWithPage; diff --git a/lib/json/encodeFile.js b/lib/json/encodeFile.js deleted file mode 100644 index d2c9e8a..0000000 --- a/lib/json/encodeFile.js +++ /dev/null @@ -1,21 +0,0 @@ - -/** - Return a JSON representation of a file - - @param {File} file - @return {Object} -*/ -function encodeFileToJson(file) { - var filePath = file.getPath(); - if (!filePath) { - return undefined; - } - - return { - path: filePath, - mtime: file.getMTime(), - type: file.getType() - }; -} - -module.exports = encodeFileToJson; diff --git a/lib/json/encodeGlossary.js b/lib/json/encodeGlossary.js deleted file mode 100644 index e9bcfc9..0000000 --- a/lib/json/encodeGlossary.js +++ /dev/null @@ -1,21 +0,0 @@ -var encodeFile = require('./encodeFile'); -var encodeGlossaryEntry = require('./encodeGlossaryEntry'); - -/** - Encode a glossary to JSON - - @param {Glossary} - @return {Object} -*/ -function encodeGlossary(glossary) { - var file = glossary.getFile(); - var entries = glossary.getEntries(); - - return { - file: encodeFile(file), - entries: entries - .map(encodeGlossaryEntry).toJS() - }; -} - -module.exports = encodeGlossary; diff --git a/lib/json/encodeGlossaryEntry.js b/lib/json/encodeGlossaryEntry.js deleted file mode 100644 index d163f45..0000000 --- a/lib/json/encodeGlossaryEntry.js +++ /dev/null @@ -1,16 +0,0 @@ - -/** - Encode a SummaryArticle to JSON - - @param {GlossaryEntry} - @return {Object} -*/ -function encodeGlossaryEntry(entry) { - return { - id: entry.getID(), - name: entry.getName(), - description: entry.getDescription() - }; -} - -module.exports = encodeGlossaryEntry; diff --git a/lib/json/encodeLanguages.js b/lib/json/encodeLanguages.js deleted file mode 100644 index 8447e80..0000000 --- a/lib/json/encodeLanguages.js +++ /dev/null @@ -1,26 +0,0 @@ -var encodeFile = require('./encodeFile'); - -/** - Encode a languages listing to JSON - - @param {Languages} - @return {Object} -*/ -function encodeLanguages(languages) { - var file = languages.getFile(); - var list = languages.getList(); - - return { - file: encodeFile(file), - list: list - .valueSeq() - .map(function(lang) { - return { - id: lang.getID(), - title: lang.getTitle() - }; - }).toJS() - }; -} - -module.exports = encodeLanguages; diff --git a/lib/json/encodeOutput.js b/lib/json/encodeOutput.js deleted file mode 100644 index 7347e57..0000000 --- a/lib/json/encodeOutput.js +++ /dev/null @@ -1,25 +0,0 @@ -var encodeBook = require('./encodeBook'); - -/** - * Encode an output to JSON - * - * @param {Output} - * @return {Object} - */ -function encodeOutputToJson(output) { - var book = output.getBook(); - var generator = output.getGenerator(); - var options = output.getOptions(); - - var result = encodeBook(book); - - result.output = { - name: generator - }; - - result.options = options.toJS(); - - return result; -} - -module.exports = encodeOutputToJson; diff --git a/lib/json/encodeOutputWithPage.js b/lib/json/encodeOutputWithPage.js deleted file mode 100644 index 8b21e3d..0000000 --- a/lib/json/encodeOutputWithPage.js +++ /dev/null @@ -1,23 +0,0 @@ -var encodeOutput = require('./encodeOutput'); -var encodePage = require('./encodePage'); -var encodeFile = require('./encodeFile'); - -/** - * Return a JSON representation of a book with a specific file - * - * @param {Book} output - * @param {Page} page - * @return {Object} - */ -function encodeOutputWithPage(output, page) { - var file = page.getFile(); - var book = output.getBook(); - - var result = encodeOutput(output); - result.page = encodePage(page, book.getSummary()); - result.file = encodeFile(file); - - return result; -} - -module.exports = encodeOutputWithPage; diff --git a/lib/json/encodePage.js b/lib/json/encodePage.js deleted file mode 100644 index be92117..0000000 --- a/lib/json/encodePage.js +++ /dev/null @@ -1,39 +0,0 @@ -var encodeSummaryArticle = require('./encodeSummaryArticle'); - -/** - Return a JSON representation of a page - - @param {Page} page - @param {Summary} summary - @return {Object} -*/ -function encodePage(page, summary) { - var file = page.getFile(); - var attributes = page.getAttributes(); - var article = summary.getByPath(file.getPath()); - - var result = attributes.toJS(); - - if (article) { - result.title = article.getTitle(); - result.level = article.getLevel(); - result.depth = article.getDepth(); - - var nextArticle = summary.getNextArticle(article); - if (nextArticle) { - result.next = encodeSummaryArticle(nextArticle); - } - - var prevArticle = summary.getPrevArticle(article); - if (prevArticle) { - result.previous = encodeSummaryArticle(prevArticle); - } - } - - result.content = page.getContent(); - result.dir = page.getDir(); - - return result; -} - -module.exports = encodePage; diff --git a/lib/json/encodeReadme.js b/lib/json/encodeReadme.js deleted file mode 100644 index 96176a3..0000000 --- a/lib/json/encodeReadme.js +++ /dev/null @@ -1,17 +0,0 @@ -var encodeFile = require('./encodeFile'); - -/** - Encode a readme to JSON - - @param {Readme} - @return {Object} -*/ -function encodeReadme(readme) { - var file = readme.getFile(); - - return { - file: encodeFile(file) - }; -} - -module.exports = encodeReadme; diff --git a/lib/json/encodeSummary.js b/lib/json/encodeSummary.js deleted file mode 100644 index 97db910..0000000 --- a/lib/json/encodeSummary.js +++ /dev/null @@ -1,20 +0,0 @@ -var encodeFile = require('./encodeFile'); -var encodeSummaryPart = require('./encodeSummaryPart'); - -/** - Encode a summary to JSON - - @param {Summary} - @return {Object} -*/ -function encodeSummary(summary) { - var file = summary.getFile(); - var parts = summary.getParts(); - - return { - file: encodeFile(file), - parts: parts.map(encodeSummaryPart).toJS() - }; -} - -module.exports = encodeSummary; diff --git a/lib/json/encodeSummaryArticle.js b/lib/json/encodeSummaryArticle.js deleted file mode 100644 index 2fc5144..0000000 --- a/lib/json/encodeSummaryArticle.js +++ /dev/null @@ -1,28 +0,0 @@ - -/** - Encode a SummaryArticle to JSON - - @param {SummaryArticle} - @return {Object} -*/ -function encodeSummaryArticle(article, recursive) { - var articles = undefined; - if (recursive !== false) { - articles = article.getArticles() - .map(encodeSummaryArticle) - .toJS(); - } - - return { - title: article.getTitle(), - level: article.getLevel(), - depth: article.getDepth(), - anchor: article.getAnchor(), - url: article.getUrl(), - path: article.getPath(), - ref: article.getRef(), - articles: articles - }; -} - -module.exports = encodeSummaryArticle; diff --git a/lib/json/encodeSummaryPart.js b/lib/json/encodeSummaryPart.js deleted file mode 100644 index a5e7218..0000000 --- a/lib/json/encodeSummaryPart.js +++ /dev/null @@ -1,17 +0,0 @@ -var encodeSummaryArticle = require('./encodeSummaryArticle'); - -/** - Encode a SummaryPart to JSON - - @param {SummaryPart} - @return {Object} -*/ -function encodeSummaryPart(part) { - return { - title: part.getTitle(), - articles: part.getArticles() - .map(encodeSummaryArticle).toJS() - }; -} - -module.exports = encodeSummaryPart; diff --git a/lib/json/index.js b/lib/json/index.js deleted file mode 100644 index 3b68f5e..0000000 --- a/lib/json/index.js +++ /dev/null @@ -1,13 +0,0 @@ - -module.exports = { - encodeOutput: require('./encodeOutput'), - encodeBookWithPage: require('./encodeBookWithPage'), - encodeOutputWithPage: require('./encodeOutputWithPage'), - encodeBook: require('./encodeBook'), - encodeFile: require('./encodeFile'), - encodePage: require('./encodePage'), - encodeSummary: require('./encodeSummary'), - encodeSummaryArticle: require('./encodeSummaryArticle'), - encodeReadme: require('./encodeReadme'), - encodeLanguages: require('./encodeLanguages') -}; diff --git a/lib/models/__tests__/config.js b/lib/models/__tests__/config.js deleted file mode 100644 index abad754..0000000 --- a/lib/models/__tests__/config.js +++ /dev/null @@ -1,90 +0,0 @@ -var Immutable = require('immutable'); -var Config = require('../config'); - -describe('Config', function() { - var config = Config.createWithValues({ - hello: { - world: 1, - test: 'Hello', - isFalse: false - } - }); - - describe('getValue', function() { - it('must return value as immutable', function() { - var value = config.getValue('hello'); - expect(Immutable.Map.isMap(value)).toBeTruthy(); - }); - - it('must return deep value', function() { - var value = config.getValue('hello.world'); - expect(value).toBe(1); - }); - - it('must return default value if non existant', function() { - var value = config.getValue('hello.nonExistant', 'defaultValue'); - expect(value).toBe('defaultValue'); - }); - - it('must not return default value for falsy values', function() { - var value = config.getValue('hello.isFalse', 'defaultValue'); - expect(value).toBe(false); - }); - }); - - describe('setValue', function() { - it('must set value as immutable', function() { - var testConfig = config.setValue('hello', { - 'cool': 1 - }); - var value = testConfig.getValue('hello'); - - expect(Immutable.Map.isMap(value)).toBeTruthy(); - expect(value.size).toBe(1); - expect(value.has('cool')).toBeTruthy(); - }); - - it('must set deep value', function() { - var testConfig = config.setValue('hello.world', 2); - var hello = testConfig.getValue('hello'); - var world = testConfig.getValue('hello.world'); - - expect(Immutable.Map.isMap(hello)).toBeTruthy(); - expect(hello.size).toBe(3); - - expect(world).toBe(2); - }); - }); - - describe('toReducedVersion', function() { - it('must only return diffs for simple values', function() { - var _config = Config.createWithValues({ - gitbook: '3.0.0' - }); - - var reducedVersion = _config.toReducedVersion(); - - expect(reducedVersion.toJS()).toEqual({ - gitbook: '3.0.0' - }); - }); - - it('must only return diffs for deep values', function() { - var _config = Config.createWithValues({ - structure: { - readme: 'intro.md' - } - }); - - var reducedVersion = _config.toReducedVersion(); - - expect(reducedVersion.toJS()).toEqual({ - structure: { - readme: 'intro.md' - } - }); - }); - }); -}); - - diff --git a/lib/models/__tests__/glossary.js b/lib/models/__tests__/glossary.js deleted file mode 100644 index 5bf64dc..0000000 --- a/lib/models/__tests__/glossary.js +++ /dev/null @@ -1,40 +0,0 @@ -var File = require('../file'); -var Glossary = require('../glossary'); -var GlossaryEntry = require('../glossaryEntry'); - -describe('Glossary', function() { - var glossary = Glossary.createFromEntries(File(), [ - { - name: 'Hello World', - description: 'Awesome!' - }, - { - name: 'JavaScript', - description: 'This is a cool language' - } - ]); - - describe('createFromEntries', function() { - it('must add all entries', function() { - var entries = glossary.getEntries(); - expect(entries.size).toBe(2); - }); - - it('must add entries as GlossaryEntries', function() { - var entries = glossary.getEntries(); - var entry = entries.get('hello-world'); - expect(entry instanceof GlossaryEntry).toBeTruthy(); - }); - }); - - describe('toText', function() { - it('return as markdown', function() { - return glossary.toText('.md') - .then(function(text) { - expect(text).toContain('# Glossary'); - }); - }); - }); -}); - - diff --git a/lib/models/__tests__/glossaryEntry.js b/lib/models/__tests__/glossaryEntry.js deleted file mode 100644 index 833115d..0000000 --- a/lib/models/__tests__/glossaryEntry.js +++ /dev/null @@ -1,15 +0,0 @@ -var GlossaryEntry = require('../glossaryEntry'); - -describe('GlossaryEntry', function() { - describe('getID', function() { - it('must return a normalized ID', function() { - var entry = new GlossaryEntry({ - name: 'Hello World' - }); - - expect(entry.getID()).toBe('hello-world'); - }); - }); -}); - - diff --git a/lib/models/__tests__/page.js b/lib/models/__tests__/page.js deleted file mode 100644 index 479d276..0000000 --- a/lib/models/__tests__/page.js +++ /dev/null @@ -1,28 +0,0 @@ -var Immutable = require('immutable'); -var Page = require('../page'); - -describe('Page', function() { - - describe('toText', function() { - it('must not prepend frontmatter if no attributes', function() { - var page = Page().merge({ - content: 'Hello World' - }); - - expect(page.toText()).toBe('Hello World'); - }); - - it('must prepend frontmatter if attributes', function() { - var page = Page().merge({ - content: 'Hello World', - attributes: Immutable.fromJS({ - hello: 'world' - }) - }); - - expect(page.toText()).toBe('---\nhello: world\n---\n\nHello World'); - }); - }); -}); - - diff --git a/lib/models/__tests__/plugin.js b/lib/models/__tests__/plugin.js deleted file mode 100644 index b229664..0000000 --- a/lib/models/__tests__/plugin.js +++ /dev/null @@ -1,27 +0,0 @@ -describe('Plugin', function() { - var Plugin = require('../plugin'); - - describe('createFromString', function() { - it('must parse name', function() { - var plugin = Plugin.createFromString('hello'); - expect(plugin.getName()).toBe('hello'); - expect(plugin.getVersion()).toBe('*'); - }); - - it('must parse version', function() { - var plugin = Plugin.createFromString('hello@1.0.0'); - expect(plugin.getName()).toBe('hello'); - expect(plugin.getVersion()).toBe('1.0.0'); - }); - }); - - describe('isLoaded', function() { - it('must return false for empty plugin', function() { - var plugin = Plugin.createFromString('hello'); - expect(plugin.isLoaded()).toBe(false); - }); - - }); -}); - - diff --git a/lib/models/__tests__/pluginDependency.js b/lib/models/__tests__/pluginDependency.js deleted file mode 100644 index cb04cf2..0000000 --- a/lib/models/__tests__/pluginDependency.js +++ /dev/null @@ -1,80 +0,0 @@ -var Immutable = require('immutable'); -var PluginDependency = require('../pluginDependency'); - -describe('PluginDependency', function() { - describe('createFromString', function() { - it('must parse name', function() { - var plugin = PluginDependency.createFromString('hello'); - expect(plugin.getName()).toBe('hello'); - expect(plugin.getVersion()).toBe('*'); - }); - - it('must parse state', function() { - var plugin = PluginDependency.createFromString('-hello'); - expect(plugin.getName()).toBe('hello'); - expect(plugin.isEnabled()).toBe(false); - }); - - describe('Version', function() { - it('must parse version', function() { - var plugin = PluginDependency.createFromString('hello@1.0.0'); - expect(plugin.getName()).toBe('hello'); - expect(plugin.getVersion()).toBe('1.0.0'); - }); - - it('must parse semver', function() { - var plugin = PluginDependency.createFromString('hello@>=4.0.0'); - expect(plugin.getName()).toBe('hello'); - expect(plugin.getVersion()).toBe('>=4.0.0'); - }); - }); - - describe('GIT Version', function() { - it('must handle HTTPS urls', function() { - var plugin = PluginDependency.createFromString('hello@git+https://github.com/GitbookIO/plugin-ga.git'); - expect(plugin.getName()).toBe('hello'); - expect(plugin.getVersion()).toBe('git+https://github.com/GitbookIO/plugin-ga.git'); - }); - - it('must handle SSH urls', function() { - var plugin = PluginDependency.createFromString('hello@git+ssh://samy@github.com/GitbookIO/plugin-ga.git'); - expect(plugin.getName()).toBe('hello'); - expect(plugin.getVersion()).toBe('git+ssh://samy@github.com/GitbookIO/plugin-ga.git'); - }); - }); - - describe('listToArray', function() { - it('must create an array from a list of plugin dependencies', function() { - var list = PluginDependency.listToArray(Immutable.List([ - PluginDependency.createFromString('hello@1.0.0'), - PluginDependency.createFromString('noversion'), - PluginDependency.createFromString('-disabled') - ])); - - expect(list).toEqual([ - 'hello@1.0.0', - 'noversion', - '-disabled' - ]); - }); - }); - - describe('listFromArray', function() { - it('must create an array from a list of plugin dependencies', function() { - var arr = Immutable.fromJS([ - 'hello@1.0.0', - { - 'name': 'plugin-ga', - 'version': 'git+ssh://samy@github.com/GitbookIO/plugin-ga.git' - } - ]); - var list = PluginDependency.listFromArray(arr); - - expect(list.first().getName()).toBe('hello'); - expect(list.first().getVersion()).toBe('1.0.0'); - expect(list.last().getName()).toBe('plugin-ga'); - expect(list.last().getVersion()).toBe('git+ssh://samy@github.com/GitbookIO/plugin-ga.git'); - }); - }); - }); -}); diff --git a/lib/models/__tests__/summary.js b/lib/models/__tests__/summary.js deleted file mode 100644 index 29c9330..0000000 --- a/lib/models/__tests__/summary.js +++ /dev/null @@ -1,94 +0,0 @@ - -describe('Summary', function() { - var File = require('../file'); - var Summary = require('../summary'); - - var summary = Summary.createFromParts(File(), [ - { - articles: [ - { - title: 'My First Article', - ref: 'README.md' - }, - { - title: 'My Second Article', - ref: 'article.md' - }, - { - title: 'Article without ref' - }, - { - title: 'Article with absolute ref', - ref: 'https://google.fr' - } - ] - }, - { - title: 'Test' - } - ]); - - describe('createFromEntries', function() { - it('must add all parts', function() { - var parts = summary.getParts(); - expect(parts.size).toBe(2); - }); - }); - - describe('getByLevel', function() { - it('can return a Part', function() { - var part = summary.getByLevel('1'); - - expect(part).toBeDefined(); - expect(part.getArticles().size).toBe(4); - }); - - it('can return a Part (2)', function() { - var part = summary.getByLevel('2'); - - expect(part).toBeDefined(); - expect(part.getTitle()).toBe('Test'); - expect(part.getArticles().size).toBe(0); - }); - - it('can return an Article', function() { - var article = summary.getByLevel('1.1'); - - expect(article).toBeDefined(); - expect(article.getTitle()).toBe('My First Article'); - }); - }); - - describe('getByPath', function() { - it('return correct article', function() { - var article = summary.getByPath('README.md'); - - expect(article).toBeDefined(); - expect(article.getTitle()).toBe('My First Article'); - }); - - it('return correct article', function() { - var article = summary.getByPath('article.md'); - - expect(article).toBeDefined(); - expect(article.getTitle()).toBe('My Second Article'); - }); - - it('return undefined if not found', function() { - var article = summary.getByPath('NOT_EXISTING.md'); - - expect(article).toBeFalsy(); - }); - }); - - describe('toText', function() { - it('return as markdown', function() { - return summary.toText('.md') - .then(function(text) { - expect(text).toContain('# Summary'); - }); - }); - }); -}); - - diff --git a/lib/models/__tests__/summaryArticle.js b/lib/models/__tests__/summaryArticle.js deleted file mode 100644 index 22a7a20..0000000 --- a/lib/models/__tests__/summaryArticle.js +++ /dev/null @@ -1,53 +0,0 @@ -var SummaryArticle = require('../summaryArticle'); -var File = require('../file'); - -describe('SummaryArticle', function() { - describe('createChildLevel', function() { - it('must create the right level', function() { - var article = SummaryArticle.create({}, '1.1'); - expect(article.createChildLevel()).toBe('1.1.1'); - }); - - it('must create the right level when has articles', function() { - var article = SummaryArticle.create({ - articles: [ - { - title: 'Test' - } - ] - }, '1.1'); - expect(article.createChildLevel()).toBe('1.1.2'); - }); - }); - - describe('isFile', function() { - it('must return true when exactly the file', function() { - var article = SummaryArticle.create({ - ref: 'hello.md' - }, '1.1'); - var file = File.createWithFilepath('hello.md'); - - expect(article.isFile(file)).toBe(true); - }); - - it('must return true when path is not normalized', function() { - var article = SummaryArticle.create({ - ref: '/hello.md' - }, '1.1'); - var file = File.createWithFilepath('hello.md'); - - expect(article.isFile(file)).toBe(true); - }); - - it('must return false when has anchor', function() { - var article = SummaryArticle.create({ - ref: 'hello.md#world' - }, '1.1'); - var file = File.createWithFilepath('hello.md'); - - expect(article.isFile(file)).toBe(false); - }); - }); -}); - - diff --git a/lib/models/__tests__/summaryPart.js b/lib/models/__tests__/summaryPart.js deleted file mode 100644 index 8ee50b6..0000000 --- a/lib/models/__tests__/summaryPart.js +++ /dev/null @@ -1,23 +0,0 @@ -var SummaryPart = require('../summaryPart'); - -describe('SummaryPart', function() { - describe('createChildLevel', function() { - it('must create the right level', function() { - var article = SummaryPart.create({}, '1'); - expect(article.createChildLevel()).toBe('1.1'); - }); - - it('must create the right level when has articles', function() { - var article = SummaryPart.create({ - articles: [ - { - title: 'Test' - } - ] - }, '1'); - expect(article.createChildLevel()).toBe('1.2'); - }); - }); -}); - - diff --git a/lib/models/__tests__/templateBlock.js b/lib/models/__tests__/templateBlock.js deleted file mode 100644 index e5f7666..0000000 --- a/lib/models/__tests__/templateBlock.js +++ /dev/null @@ -1,205 +0,0 @@ -var nunjucks = require('nunjucks'); -var Immutable = require('immutable'); -var Promise = require('../../utils/promise'); - -describe('TemplateBlock', function() { - var TemplateBlock = require('../templateBlock'); - - describe('create', function() { - it('must initialize a simple TemplateBlock from a function', function() { - var templateBlock = TemplateBlock.create('sayhello', function(block) { - return { - body: '<p>Hello, World!</p>', - parse: true - }; - }); - - // Check basic templateBlock properties - expect(templateBlock.getName()).toBe('sayhello'); - expect(templateBlock.getEndTag()).toBe('endsayhello'); - expect(templateBlock.getBlocks().size).toBe(0); - expect(templateBlock.getExtensionName()).toBe('BlocksayhelloExtension'); - - // Check result of applying block - return Promise() - .then(function() { - return templateBlock.applyBlock(); - }) - .then(function(result) { - expect(result.name).toBe('sayhello'); - expect(result.body).toBe('<p>Hello, World!</p>'); - }); - }); - }); - - describe('getShortcuts', function() { - it('must return undefined if no shortcuts', function() { - var templateBlock = TemplateBlock.create('sayhello', function(block) { - return { - body: '<p>Hello, World!</p>', - parse: true - }; - }); - - expect(templateBlock.getShortcuts()).toNotExist(); - }); - - it('must return complete shortcut', function() { - var templateBlock = TemplateBlock.create('sayhello', { - process: function(block) { - return '<p>Hello, World!</p>'; - }, - shortcuts: { - parsers: ['markdown'], - start: '$', - end: '-' - } - }); - - var shortcut = templateBlock.getShortcuts(); - - expect(shortcut).toBeDefined(); - expect(shortcut.getStart()).toEqual('$'); - expect(shortcut.getEnd()).toEqual('-'); - expect(shortcut.getStartTag()).toEqual('sayhello'); - expect(shortcut.getEndTag()).toEqual('endsayhello'); - }); - }); - - describe('toNunjucksExt()', function() { - it('should replace by block anchor', function() { - var templateBlock = TemplateBlock.create('sayhello', function(block) { - return 'Hello'; - }); - - var blocks = {}; - - // Create a fresh Nunjucks environment - var env = new nunjucks.Environment(null, { autoescape: false }); - - // Add template block to environement - var Ext = templateBlock.toNunjucksExt({}, blocks); - env.addExtension(templateBlock.getExtensionName(), new Ext()); - - // Render a template using the block - var src = '{% sayhello %}{% endsayhello %}'; - return Promise.nfcall(env.renderString.bind(env), src) - .then(function(res) { - blocks = Immutable.fromJS(blocks); - expect(blocks.size).toBe(1); - - var blockId = blocks.keySeq().get(0); - var block = blocks.get(blockId); - - expect(res).toBe('{{-%' + blockId + '%-}}'); - expect(block.get('body')).toBe('Hello'); - expect(block.get('name')).toBe('sayhello'); - }); - }); - - it('must create a valid nunjucks extension', function() { - var templateBlock = TemplateBlock.create('sayhello', function(block) { - return { - body: '<p>Hello, World!</p>', - parse: true - }; - }); - - // Create a fresh Nunjucks environment - var env = new nunjucks.Environment(null, { autoescape: false }); - - // Add template block to environement - var Ext = templateBlock.toNunjucksExt(); - env.addExtension(templateBlock.getExtensionName(), new Ext()); - - // Render a template using the block - var src = '{% sayhello %}{% endsayhello %}'; - return Promise.nfcall(env.renderString.bind(env), src) - .then(function(res) { - expect(res).toBe('<p>Hello, World!</p>'); - }); - }); - - it('must apply block arguments correctly', function() { - var templateBlock = TemplateBlock.create('sayhello', function(block) { - return { - body: '<'+block.kwargs.tag+'>Hello, '+block.kwargs.name+'!</'+block.kwargs.tag+'>', - parse: true - }; - }); - - // Create a fresh Nunjucks environment - var env = new nunjucks.Environment(null, { autoescape: false }); - - // Add template block to environement - var Ext = templateBlock.toNunjucksExt(); - env.addExtension(templateBlock.getExtensionName(), new Ext()); - - // Render a template using the block - var src = '{% sayhello name="Samy", tag="p" %}{% endsayhello %}'; - return Promise.nfcall(env.renderString.bind(env), src) - .then(function(res) { - expect(res).toBe('<p>Hello, Samy!</p>'); - }); - }); - - it('must accept an async function', function() { - var templateBlock = TemplateBlock.create('sayhello', function(block) { - return Promise() - .then(function() { - return { - body: 'Hello ' + block.body, - parse: true - }; - }); - }); - - // Create a fresh Nunjucks environment - var env = new nunjucks.Environment(null, { autoescape: false }); - - // Add template block to environement - var Ext = templateBlock.toNunjucksExt(); - env.addExtension(templateBlock.getExtensionName(), new Ext()); - - // Render a template using the block - var src = '{% sayhello %}Samy{% endsayhello %}'; - return Promise.nfcall(env.renderString.bind(env), src) - .then(function(res) { - expect(res).toBe('Hello Samy'); - }); - }); - - it('must handle nested blocks', function() { - var templateBlock = new TemplateBlock({ - name: 'yoda', - blocks: Immutable.List(['start', 'end']), - process: function(block) { - var nested = {}; - - block.blocks.forEach(function(blk) { - nested[blk.name] = blk.body.trim(); - }); - - return { - body: '<p class="yoda">'+nested.end+' '+nested.start+'</p>', - parse: true - }; - } - }); - - // Create a fresh Nunjucks environment - var env = new nunjucks.Environment(null, { autoescape: false }); - - // Add template block to environement - var Ext = templateBlock.toNunjucksExt(); - env.addExtension(templateBlock.getExtensionName(), new Ext()); - - // Render a template using the block - var src = '{% yoda %}{% start %}this sentence should be{% end %}inverted{% endyoda %}'; - return Promise.nfcall(env.renderString.bind(env), src) - .then(function(res) { - expect(res).toBe('<p class="yoda">inverted this sentence should be</p>'); - }); - }); - }); -});
\ No newline at end of file diff --git a/lib/models/__tests__/templateEngine.js b/lib/models/__tests__/templateEngine.js deleted file mode 100644 index 6f18b18..0000000 --- a/lib/models/__tests__/templateEngine.js +++ /dev/null @@ -1,51 +0,0 @@ - -describe('TemplateBlock', function() { - var TemplateEngine = require('../templateEngine'); - - describe('create', function() { - it('must initialize with a list of filters', function() { - var engine = TemplateEngine.create({ - filters: { - hello: function(name) { - return 'Hello ' + name + '!'; - } - } - }); - var env = engine.toNunjucks(); - var res = env.renderString('{{ "Luke"|hello }}'); - - expect(res).toBe('Hello Luke!'); - }); - - it('must initialize with a list of globals', function() { - var engine = TemplateEngine.create({ - globals: { - hello: function(name) { - return 'Hello ' + name + '!'; - } - } - }); - var env = engine.toNunjucks(); - var res = env.renderString('{{ hello("Luke") }}'); - - expect(res).toBe('Hello Luke!'); - }); - - it('must pass context to filters and blocks', function() { - var engine = TemplateEngine.create({ - filters: { - hello: function(name) { - return 'Hello ' + name + ' ' + this.lastName + '!'; - } - }, - context: { - lastName: 'Skywalker' - } - }); - var env = engine.toNunjucks(); - var res = env.renderString('{{ "Luke"|hello }}'); - - expect(res).toBe('Hello Luke Skywalker!'); - }); - }); -});
\ No newline at end of file diff --git a/lib/models/book.js b/lib/models/book.js deleted file mode 100644 index f774ee8..0000000 --- a/lib/models/book.js +++ /dev/null @@ -1,364 +0,0 @@ -var path = require('path'); -var Immutable = require('immutable'); - -var Logger = require('../utils/logger'); - -var FS = require('./fs'); -var Config = require('./config'); -var Readme = require('./readme'); -var Summary = require('./summary'); -var Glossary = require('./glossary'); -var Languages = require('./languages'); -var Ignore = require('./ignore'); - -var Book = Immutable.Record({ - // Logger for outptu message - logger: Logger(), - - // Filesystem binded to the book scope to read files/directories - fs: FS(), - - // Ignore files parser - ignore: Ignore(), - - // Structure files - config: Config(), - readme: Readme(), - summary: Summary(), - glossary: Glossary(), - languages: Languages(), - - // ID of the language for language books - language: String(), - - // List of children, if multilingual (String -> Book) - books: Immutable.OrderedMap() -}); - -Book.prototype.getLogger = function() { - return this.get('logger'); -}; - -Book.prototype.getFS = function() { - return this.get('fs'); -}; - -Book.prototype.getIgnore = function() { - return this.get('ignore'); -}; - -Book.prototype.getConfig = function() { - return this.get('config'); -}; - -Book.prototype.getReadme = function() { - return this.get('readme'); -}; - -Book.prototype.getSummary = function() { - return this.get('summary'); -}; - -Book.prototype.getGlossary = function() { - return this.get('glossary'); -}; - -Book.prototype.getLanguages = function() { - return this.get('languages'); -}; - -Book.prototype.getBooks = function() { - return this.get('books'); -}; - -Book.prototype.getLanguage = function() { - return this.get('language'); -}; - -/** - Return FS instance to access the content - - @return {FS} -*/ -Book.prototype.getContentFS = function() { - var fs = this.getFS(); - var config = this.getConfig(); - var rootFolder = config.getValue('root'); - - if (rootFolder) { - return FS.reduceScope(fs, rootFolder); - } - - return fs; -}; - -/** - Return root of the book - - @return {String} -*/ -Book.prototype.getRoot = function() { - var fs = this.getFS(); - return fs.getRoot(); -}; - -/** - Return root for content of the book - - @return {String} -*/ -Book.prototype.getContentRoot = function() { - var fs = this.getContentFS(); - return fs.getRoot(); -}; - -/** - Check if a file is ignore (should not being parsed, etc) - - @param {String} ref - @return {Page|undefined} -*/ -Book.prototype.isFileIgnored = function(filename) { - var ignore = this.getIgnore(); - var language = this.getLanguage(); - - // Ignore is always relative to the root of the main book - if (language) { - filename = path.join(language, filename); - } - - return ignore.isFileIgnored(filename); -}; - -/** - Check if a content file is ignore (should not being parsed, etc) - - @param {String} ref - @return {Page|undefined} -*/ -Book.prototype.isContentFileIgnored = function(filename) { - var config = this.getConfig(); - var rootFolder = config.getValue('root'); - - if (rootFolder) { - filename = path.join(rootFolder, filename); - } - - return this.isFileIgnored(filename); -}; - -/** - Return a page from a book by its path - - @param {String} ref - @return {Page|undefined} -*/ -Book.prototype.getPage = function(ref) { - return this.getPages().get(ref); -}; - -/** - Is this book the parent of language's books - - @return {Boolean} -*/ -Book.prototype.isMultilingual = function() { - return (this.getLanguages().getCount() > 0); -}; - -/** - Return true if book is associated to a language - - @return {Boolean} -*/ -Book.prototype.isLanguageBook = function() { - return Boolean(this.getLanguage()); -}; - -/** - Return a languages book - - @param {String} language - @return {Book} -*/ -Book.prototype.getLanguageBook = function(language) { - var books = this.getBooks(); - return books.get(language); -}; - -/** - Add a new language book - - @param {String} language - @param {Book} book - @return {Book} -*/ -Book.prototype.addLanguageBook = function(language, book) { - var books = this.getBooks(); - books = books.set(language, book); - - return this.set('books', books); -}; - -/** - Set the summary for this book - - @param {Summary} - @return {Book} -*/ -Book.prototype.setSummary = function(summary) { - return this.set('summary', summary); -}; - -/** - Set the readme for this book - - @param {Readme} - @return {Book} -*/ -Book.prototype.setReadme = function(readme) { - return this.set('readme', readme); -}; - -/** - Set the configuration for this book - - @param {Config} - @return {Book} -*/ -Book.prototype.setConfig = function(config) { - return this.set('config', config); -}; - -/** - Set the ignore instance for this book - - @param {Ignore} - @return {Book} -*/ -Book.prototype.setIgnore = function(ignore) { - return this.set('ignore', ignore); -}; - -/** - Change log level - - @param {String} level - @return {Book} -*/ -Book.prototype.setLogLevel = function(level) { - this.getLogger().setLevel(level); - return this; -}; - -/** - Create a book using a filesystem - - @param {FS} fs - @return {Book} -*/ -Book.createForFS = function createForFS(fs) { - return new Book({ - fs: fs - }); -}; - -/** - Infers the default extension for files - @return {String} -*/ -Book.prototype.getDefaultExt = function() { - // Inferring sources - var clues = [ - this.getReadme(), - this.getSummary(), - this.getGlossary() - ]; - - // List their extensions - var exts = clues.map(function (clue) { - var file = clue.getFile(); - if (file.exists()) { - return file.getParser().getExtensions().first(); - } else { - return null; - } - }); - // Adds the general default extension - exts.push('.md'); - - // Choose the first non null - return exts.find(function (e) { return e !== null; }); -}; - -/** - Infer the default path for a Readme. - @param {Boolean} [absolute=false] False for a path relative to - this book's content root - @return {String} -*/ -Book.prototype.getDefaultReadmePath = function(absolute) { - var defaultPath = 'README'+this.getDefaultExt(); - if (absolute) { - return path.join(this.getContentRoot(), defaultPath); - } else { - return defaultPath; - } -}; - -/** - Infer the default path for a Summary. - @param {Boolean} [absolute=false] False for a path relative to - this book's content root - @return {String} -*/ -Book.prototype.getDefaultSummaryPath = function(absolute) { - var defaultPath = 'SUMMARY'+this.getDefaultExt(); - if (absolute) { - return path.join(this.getContentRoot(), defaultPath); - } else { - return defaultPath; - } -}; - -/** - Infer the default path for a Glossary. - @param {Boolean} [absolute=false] False for a path relative to - this book's content root - @return {String} -*/ -Book.prototype.getDefaultGlossaryPath = function(absolute) { - var defaultPath = 'GLOSSARY'+this.getDefaultExt(); - if (absolute) { - return path.join(this.getContentRoot(), defaultPath); - } else { - return defaultPath; - } -}; - -/** - Create a language book from a parent - - @param {Book} parent - @param {String} language - @return {Book} -*/ -Book.createFromParent = function createFromParent(parent, language) { - var ignore = parent.getIgnore(); - var config = parent.getConfig(); - - // Set language in configuration - config = config.setValue('language', language); - - return new Book({ - // Inherits config. logegr and list of ignored files - logger: parent.getLogger(), - config: config, - ignore: ignore, - - language: language, - fs: FS.reduceScope(parent.getContentFS(), language) - }); -}; - -module.exports = Book; diff --git a/lib/models/config.js b/lib/models/config.js deleted file mode 100644 index 6de52f9..0000000 --- a/lib/models/config.js +++ /dev/null @@ -1,181 +0,0 @@ -var is = require('is'); -var Immutable = require('immutable'); - -var File = require('./file'); -var PluginDependency = require('./pluginDependency'); -var configDefault = require('../constants/configDefault'); -var reducedObject = require('../utils/reducedObject'); - -var Config = Immutable.Record({ - file: File(), - values: configDefault -}, 'Config'); - -Config.prototype.getFile = function() { - return this.get('file'); -}; - -Config.prototype.getValues = function() { - return this.get('values'); -}; - -/** - * Return minimum version of configuration, - * Basically it returns the current config minus the default one - * @return {Map} - */ -Config.prototype.toReducedVersion = function() { - return reducedObject(configDefault, this.getValues()); -}; - -/** - * Render config as text - * @return {Promise<String>} - */ -Config.prototype.toText = function() { - return JSON.stringify(this.toReducedVersion().toJS(), null, 4); -}; - -/** - * Change the file for the configuration - * @param {File} file - * @return {Config} - */ -Config.prototype.setFile = function(file) { - return this.set('file', file); -}; - -/** - * Return a configuration value by its key path - * @param {String} key - * @return {Mixed} - */ -Config.prototype.getValue = function(keyPath, def) { - var values = this.getValues(); - keyPath = Config.keyToKeyPath(keyPath); - - if (!values.hasIn(keyPath)) { - return Immutable.fromJS(def); - } - - return values.getIn(keyPath); -}; - -/** - * Update a configuration value - * @param {String} key - * @param {Mixed} value - * @return {Config} - */ -Config.prototype.setValue = function(keyPath, value) { - keyPath = Config.keyToKeyPath(keyPath); - - value = Immutable.fromJS(value); - - var values = this.getValues(); - values = values.setIn(keyPath, value); - - return this.set('values', values); -}; - -/** - * Return a list of plugin dependencies - * @return {List<PluginDependency>} - */ -Config.prototype.getPluginDependencies = function() { - var plugins = this.getValue('plugins'); - - if (is.string(plugins)) { - return PluginDependency.listFromString(plugins); - } else { - return PluginDependency.listFromArray(plugins); - } -}; - -/** - * Return a plugin dependency by its name - * @param {String} name - * @return {PluginDependency} - */ -Config.prototype.getPluginDependency = function(name) { - var plugins = this.getPluginDependencies(); - - return plugins.find(function(dep) { - return dep.getName() === name; - }); -}; - -/** - * Update the list of plugins dependencies - * @param {List<PluginDependency>} - * @return {Config} - */ -Config.prototype.setPluginDependencies = function(deps) { - var plugins = PluginDependency.listToArray(deps); - - return this.setValue('plugins', plugins); -}; - - -/** - * Update values for an existing configuration - * @param {Object} values - * @returns {Config} - */ -Config.prototype.updateValues = function(values) { - values = Immutable.fromJS(values); - - return this.set('values', values); -}; - -/** - * Update values for an existing configuration - * @param {Config} config - * @param {Object} values - * @returns {Config} - */ -Config.prototype.mergeValues = function(values) { - var currentValues = this.getValues(); - values = Immutable.fromJS(values); - - currentValues = currentValues.mergeDeep(values); - - return this.set('values', currentValues); -}; - -/** - * Create a new config for a file - * @param {File} file - * @param {Object} values - * @returns {Config} - */ -Config.create = function(file, values) { - return new Config({ - file: file, - values: Immutable.fromJS(values) - }); -}; - -/** - * Create a new config - * @param {Object} values - * @returns {Config} - */ -Config.createWithValues = function(values) { - return new Config({ - values: Immutable.fromJS(values) - }); -}; - - -/** - * Convert a keyPath to an array of keys - * @param {String|Array} - * @return {Array} - */ -Config.keyToKeyPath = function(keyPath) { - if (is.string(keyPath)) keyPath = keyPath.split('.'); - return keyPath; -}; - -module.exports = Config; diff --git a/lib/models/file.js b/lib/models/file.js deleted file mode 100644 index 8ddd4af..0000000 --- a/lib/models/file.js +++ /dev/null @@ -1,89 +0,0 @@ -var path = require('path'); -var Immutable = require('immutable'); - -var parsers = require('../parsers'); - -var File = Immutable.Record({ - // Path of the file, relative to the FS - path: String(), - - // Time when file data last modified - mtime: Date() -}); - -File.prototype.getPath = function() { - return this.get('path'); -}; - -File.prototype.getMTime = function() { - return this.get('mtime'); -}; - -/** - Does the file exists / is set - - @return {Boolean} -*/ -File.prototype.exists = function() { - return Boolean(this.getPath()); -}; - -/** - Return type of file ('markdown' or 'asciidoc') - - @return {String} -*/ -File.prototype.getType = function() { - var parser = this.getParser(); - if (parser) { - return parser.getName(); - } else { - return undefined; - } -}; - -/** - Return extension of this file (lowercased) - - @return {String} -*/ -File.prototype.getExtension = function() { - return path.extname(this.getPath()).toLowerCase(); -}; - -/** - Return parser for this file - - @return {Parser} -*/ -File.prototype.getParser = function() { - return parsers.getByExt(this.getExtension()); -}; - -/** - Create a file from stats informations - - @param {String} filepath - @param {Object|fs.Stats} stat - @return {File} -*/ -File.createFromStat = function createFromStat(filepath, stat) { - return new File({ - path: filepath, - mtime: stat.mtime - }); -}; - -/** - Create a file with only a path - - @param {String} filepath - @return {File} -*/ -File.createWithFilepath = function createWithFilepath(filepath) { - return new File({ - path: filepath - }); -}; - -module.exports = File; diff --git a/lib/models/fs.js b/lib/models/fs.js deleted file mode 100644 index 16bd4ea..0000000 --- a/lib/models/fs.js +++ /dev/null @@ -1,307 +0,0 @@ -var path = require('path'); -var Immutable = require('immutable'); -var stream = require('stream'); - -var File = require('./file'); -var Promise = require('../utils/promise'); -var error = require('../utils/error'); -var PathUtil = require('../utils/path'); - -var FS = Immutable.Record({ - root: String(), - - fsExists: Function(), - fsReadFile: Function(), - fsStatFile: Function(), - fsReadDir: Function(), - - fsLoadObject: null, - fsReadAsStream: null -}); - -/** - Return path to the root - - @return {String} -*/ -FS.prototype.getRoot = function() { - return this.get('root'); -}; - -/** - Verify that a file is in the fs scope - - @param {String} filename - @return {Boolean} -*/ -FS.prototype.isInScope = function(filename) { - var rootPath = this.getRoot(); - filename = path.join(rootPath, filename); - - return PathUtil.isInRoot(rootPath, filename); -}; - -/** - Resolve a file in this FS - - @param {String} - @return {String} -*/ -FS.prototype.resolve = function() { - var rootPath = this.getRoot(); - var args = Array.prototype.slice.call(arguments); - var filename = path.join.apply(path, [rootPath].concat(args)); - filename = path.normalize(filename); - - if (!this.isInScope(filename)) { - throw error.FileOutOfScopeError({ - filename: filename, - root: this.root - }); - } - - return filename; -}; - -/** - Check if a file exists, run a Promise(true) if that's the case, Promise(false) otherwise - - @param {String} filename - @return {Promise<Boolean>} -*/ -FS.prototype.exists = function(filename) { - var that = this; - - return Promise() - .then(function() { - filename = that.resolve(filename); - var exists = that.get('fsExists'); - - return exists(filename); - }); -}; - -/** - Read a file and returns a promise with the content as a buffer - - @param {String} filename - @return {Promise<Buffer>} -*/ -FS.prototype.read = function(filename) { - var that = this; - - return Promise() - .then(function() { - filename = that.resolve(filename); - var read = that.get('fsReadFile'); - - return read(filename); - }); -}; - -/** - Read a file as a string (utf-8) - - @param {String} filename - @return {Promise<String>} -*/ -FS.prototype.readAsString = function(filename, encoding) { - encoding = encoding || 'utf8'; - - return this.read(filename) - .then(function(buf) { - return buf.toString(encoding); - }); -}; - -/** - Read file as a stream - - @param {String} filename - @return {Promise<Stream>} -*/ -FS.prototype.readAsStream = function(filename) { - var that = this; - var filepath = that.resolve(filename); - var fsReadAsStream = this.get('fsReadAsStream'); - - if (fsReadAsStream) { - return Promise(fsReadAsStream(filepath)); - } - - return this.read(filename) - .then(function(buf) { - var bufferStream = new stream.PassThrough(); - bufferStream.end(buf); - - return bufferStream; - }); -}; - -/** - Read stat infos about a file - - @param {String} filename - @return {Promise<File>} -*/ -FS.prototype.statFile = function(filename) { - var that = this; - - return Promise() - .then(function() { - var filepath = that.resolve(filename); - var stat = that.get('fsStatFile'); - - return stat(filepath); - }) - .then(function(stat) { - return File.createFromStat(filename, stat); - }); -}; - -/** - List files/directories in a directory. - Directories ends with '/' - - @param {String} dirname - @return {Promise<List<String>>} -*/ -FS.prototype.readDir = function(dirname) { - var that = this; - - return Promise() - .then(function() { - var dirpath = that.resolve(dirname); - var readDir = that.get('fsReadDir'); - - return readDir(dirpath); - }) - .then(function(files) { - return Immutable.List(files); - }); -}; - -/** - List only files in a diretcory - Directories ends with '/' - - @param {String} dirname - @return {Promise<List<String>>} -*/ -FS.prototype.listFiles = function(dirname) { - return this.readDir(dirname) - .then(function(files) { - return files.filterNot(pathIsFolder); - }); -}; - -/** - List all files in a directory - - @param {String} dirName - @param {Function(dirName)} filterFn: call it for each file/directory to test if it should stop iterating - @return {Promise<List<String>>} -*/ -FS.prototype.listAllFiles = function(dirName, filterFn) { - var that = this; - dirName = dirName || '.'; - - return this.readDir(dirName) - .then(function(files) { - return Promise.reduce(files, function(out, file) { - var isDirectory = pathIsFolder(file); - var newDirName = path.join(dirName, file); - - if (filterFn && filterFn(newDirName) === false) { - return out; - } - - if (!isDirectory) { - return out.push(newDirName); - } - - return that.listAllFiles(newDirName, filterFn) - .then(function(inner) { - return out.concat(inner); - }); - }, Immutable.List()); - }); -}; - -/** - Find a file in a folder (case insensitive) - Return the found filename - - @param {String} dirname - @param {String} filename - @return {Promise<String>} -*/ -FS.prototype.findFile = function(dirname, filename) { - return this.listFiles(dirname) - .then(function(files) { - return files.find(function(file) { - return (file.toLowerCase() == filename.toLowerCase()); - }); - }); -}; - -/** - Load a JSON file - By default, fs only supports JSON - - @param {String} filename - @return {Promise<Object>} -*/ -FS.prototype.loadAsObject = function(filename) { - var that = this; - var fsLoadObject = this.get('fsLoadObject'); - - return this.exists(filename) - .then(function(exists) { - if (!exists) { - var err = new Error('Module doesn\'t exist'); - err.code = 'MODULE_NOT_FOUND'; - - throw err; - } - - if (fsLoadObject) { - return fsLoadObject(that.resolve(filename)); - } else { - return that.readAsString(filename) - .then(function(str) { - return JSON.parse(str); - }); - } - }); -}; - -/** - Create a FS instance - - @param {Object} def - @return {FS} -*/ -FS.create = function create(def) { - return new FS(def); -}; - -/** - Create a new FS instance with a reduced scope - - @param {FS} fs - @param {String} scope - @return {FS} -*/ -FS.reduceScope = function reduceScope(fs, scope) { - return fs.set('root', path.join(fs.getRoot(), scope)); -}; - - -// .readdir return files/folder as a list of string, folder ending with '/' -function pathIsFolder(filename) { - var lastChar = filename[filename.length - 1]; - return lastChar == '/' || lastChar == '\\'; -} - -module.exports = FS;
\ No newline at end of file diff --git a/lib/models/glossary.js b/lib/models/glossary.js deleted file mode 100644 index 0033248..0000000 --- a/lib/models/glossary.js +++ /dev/null @@ -1,109 +0,0 @@ -var Immutable = require('immutable'); - -var error = require('../utils/error'); -var File = require('./file'); -var GlossaryEntry = require('./glossaryEntry'); -var parsers = require('../parsers'); - -var Glossary = Immutable.Record({ - file: File(), - entries: Immutable.OrderedMap() -}); - -Glossary.prototype.getFile = function() { - return this.get('file'); -}; - -Glossary.prototype.getEntries = function() { - return this.get('entries'); -}; - -/** - Return an entry by its name - - @param {String} name - @return {GlossaryEntry} -*/ -Glossary.prototype.getEntry = function(name) { - var entries = this.getEntries(); - var id = GlossaryEntry.nameToID(name); - - return entries.get(id); -}; - -/** - Render glossary as text - - @return {Promise<String>} -*/ -Glossary.prototype.toText = function(parser) { - var file = this.getFile(); - var entries = this.getEntries(); - - parser = parser? parsers.getByExt(parser) : file.getParser(); - - if (!parser) { - throw error.FileNotParsableError({ - filename: file.getPath() - }); - } - - return parser.renderGlossary(entries.toJS()); -}; - - -/** - Add/Replace an entry to a glossary - - @param {Glossary} glossary - @param {GlossaryEntry} entry - @return {Glossary} -*/ -Glossary.addEntry = function addEntry(glossary, entry) { - var id = entry.getID(); - var entries = glossary.getEntries(); - - entries = entries.set(id, entry); - return glossary.set('entries', entries); -}; - -/** - Add/Replace an entry to a glossary by name/description - - @param {Glossary} glossary - @param {GlossaryEntry} entry - @return {Glossary} -*/ -Glossary.addEntryByName = function addEntryByName(glossary, name, description) { - var entry = new GlossaryEntry({ - name: name, - description: description - }); - - return Glossary.addEntry(glossary, entry); -}; - -/** - Create a glossary from a list of entries - - @param {String} filename - @param {Array|List} entries - @return {Glossary} -*/ -Glossary.createFromEntries = function createFromEntries(file, entries) { - entries = entries.map(function(entry) { - if (!(entry instanceof GlossaryEntry)) { - entry = new GlossaryEntry(entry); - } - - return [entry.getID(), entry]; - }); - - return new Glossary({ - file: file, - entries: Immutable.OrderedMap(entries) - }); -}; - - -module.exports = Glossary; diff --git a/lib/models/glossaryEntry.js b/lib/models/glossaryEntry.js deleted file mode 100644 index 10791db..0000000 --- a/lib/models/glossaryEntry.js +++ /dev/null @@ -1,43 +0,0 @@ -var Immutable = require('immutable'); -var slug = require('github-slugid'); - -/* - A definition represents an entry in the glossary -*/ - -var GlossaryEntry = Immutable.Record({ - name: String(), - description: String() -}); - -GlossaryEntry.prototype.getName = function() { - return this.get('name'); -}; - -GlossaryEntry.prototype.getDescription = function() { - return this.get('description'); -}; - - -/** - Get identifier for this entry - - @retrun {Boolean} -*/ -GlossaryEntry.prototype.getID = function() { - return GlossaryEntry.nameToID(this.getName()); -}; - - -/** - Normalize a glossary entry name into a unique id - - @param {String} - @return {String} -*/ -GlossaryEntry.nameToID = function nameToID(name) { - return slug(name); -}; - - -module.exports = GlossaryEntry; diff --git a/lib/models/ignore.js b/lib/models/ignore.js deleted file mode 100644 index 499195e..0000000 --- a/lib/models/ignore.js +++ /dev/null @@ -1,42 +0,0 @@ -var Immutable = require('immutable'); -var IgnoreMutable = require('ignore'); - -/* - Immutable version of node-ignore -*/ -var Ignore = Immutable.Record({ - ignore: new IgnoreMutable() -}, 'Ignore'); - -Ignore.prototype.getIgnore = function() { - return this.get('ignore'); -}; - -/** - Test if a file is ignored by these rules - - @param {String} filePath - @return {Boolean} -*/ -Ignore.prototype.isFileIgnored = function(filename) { - var ignore = this.getIgnore(); - return ignore.filter([filename]).length == 0; -}; - -/** - Add rules - - @param {String} - @return {Ignore} -*/ -Ignore.prototype.add = function(rule) { - var ignore = this.getIgnore(); - var newIgnore = new IgnoreMutable(); - - newIgnore.add(ignore); - newIgnore.add(rule); - - return this.set('ignore', newIgnore); -}; - -module.exports = Ignore; diff --git a/lib/models/language.js b/lib/models/language.js deleted file mode 100644 index dcefbf6..0000000 --- a/lib/models/language.js +++ /dev/null @@ -1,21 +0,0 @@ -var path = require('path'); -var Immutable = require('immutable'); - -var Language = Immutable.Record({ - title: String(), - path: String() -}); - -Language.prototype.getTitle = function() { - return this.get('title'); -}; - -Language.prototype.getPath = function() { - return this.get('path'); -}; - -Language.prototype.getID = function() { - return path.basename(this.getPath()); -}; - -module.exports = Language; diff --git a/lib/models/languages.js b/lib/models/languages.js deleted file mode 100644 index 42f05f9..0000000 --- a/lib/models/languages.js +++ /dev/null @@ -1,71 +0,0 @@ -var Immutable = require('immutable'); - -var File = require('./file'); -var Language = require('./language'); - -var Languages = Immutable.Record({ - file: File(), - list: Immutable.OrderedMap() -}); - -Languages.prototype.getFile = function() { - return this.get('file'); -}; - -Languages.prototype.getList = function() { - return this.get('list'); -}; - -/** - Get default languages - - @return {Language} -*/ -Languages.prototype.getDefaultLanguage = function() { - return this.getList().first(); -}; - -/** - Get a language by its ID - - @param {String} lang - @return {Language} -*/ -Languages.prototype.getLanguage = function(lang) { - return this.getList().get(lang); -}; - -/** - Return count of langs - - @return {Number} -*/ -Languages.prototype.getCount = function() { - return this.getList().size; -}; - -/** - Create a languages list from a JS object - - @param {File} - @param {Array} - @return {Language} -*/ -Languages.createFromList = function(file, langs) { - var list = Immutable.OrderedMap(); - - langs.forEach(function(lang) { - lang = Language({ - title: lang.title, - path: lang.ref - }); - list = list.set(lang.getID(), lang); - }); - - return Languages({ - file: file, - list: list - }); -}; - -module.exports = Languages; diff --git a/lib/models/output.js b/lib/models/output.js deleted file mode 100644 index 0f008ec..0000000 --- a/lib/models/output.js +++ /dev/null @@ -1,107 +0,0 @@ -var Immutable = require('immutable'); - -var Book = require('./book'); -var LocationUtils = require('../utils/location'); - -var Output = Immutable.Record({ - book: Book(), - - // Name of the generator being used - generator: String(), - - // Map of plugins to use (String -> Plugin) - plugins: Immutable.OrderedMap(), - - // Map pages to generation (String -> Page) - pages: Immutable.OrderedMap(), - - // List assets (String) - assets: Immutable.List(), - - // Option for the generation - options: Immutable.Map(), - - // Internal state for the generation - state: Immutable.Map() -}); - -Output.prototype.getBook = function() { - return this.get('book'); -}; - -Output.prototype.getGenerator = function() { - return this.get('generator'); -}; - -Output.prototype.getPlugins = function() { - return this.get('plugins'); -}; - -Output.prototype.getPages = function() { - return this.get('pages'); -}; - -Output.prototype.getOptions = function() { - return this.get('options'); -}; - -Output.prototype.getAssets = function() { - return this.get('assets'); -}; - -Output.prototype.getState = function() { - return this.get('state'); -}; - -/** - Return a page byt its file path - - @param {String} filePath - @return {Page|undefined} -*/ -Output.prototype.getPage = function(filePath) { - filePath = LocationUtils.normalize(filePath); - - var pages = this.getPages(); - return pages.get(filePath); -}; - -/** - Get root folder for output - - @return {String} -*/ -Output.prototype.getRoot = function() { - return this.getOptions().get('root'); -}; - -/** - Update state of output - - @param {Map} newState - @return {Output} -*/ -Output.prototype.setState = function(newState) { - return this.set('state', newState); -}; - -/** - Update options - - @param {Map} newOptions - @return {Output} -*/ -Output.prototype.setOptions = function(newOptions) { - return this.set('options', newOptions); -}; - -/** - Return logegr for this output (same as book) - - @return {Logger} -*/ -Output.prototype.getLogger = function() { - return this.getBook().getLogger(); -}; - -module.exports = Output; diff --git a/lib/models/page.js b/lib/models/page.js deleted file mode 100644 index 275a034..0000000 --- a/lib/models/page.js +++ /dev/null @@ -1,70 +0,0 @@ -var Immutable = require('immutable'); -var yaml = require('js-yaml'); - -var File = require('./file'); - -var Page = Immutable.Record({ - file: File(), - - // Attributes extracted from the YAML header - attributes: Immutable.Map(), - - // Content of the page - content: String(), - - // Direction of the text - dir: String('ltr') -}); - -Page.prototype.getFile = function() { - return this.get('file'); -}; - -Page.prototype.getAttributes = function() { - return this.get('attributes'); -}; - -Page.prototype.getContent = function() { - return this.get('content'); -}; - -Page.prototype.getDir = function() { - return this.get('dir'); -}; - -/** - * Return page as text - * @return {String} -*/ -Page.prototype.toText = function() { - var attrs = this.getAttributes(); - var content = this.getContent(); - - if (attrs.size === 0) { - return content; - } - - var frontMatter = '---\n' + yaml.safeDump(attrs.toJS(), { skipInvalid: true }) + '---\n\n'; - return (frontMatter + content); -}; - -/** - * Return path of the page - * @return {String} -*/ -Page.prototype.getPath = function() { - return this.getFile().getPath(); -}; - -/** - * Create a page for a file - * @param {File} file - * @return {Page} -*/ -Page.createForFile = function(file) { - return new Page({ - file: file - }); -}; - -module.exports = Page; diff --git a/lib/models/parser.js b/lib/models/parser.js deleted file mode 100644 index d64542f..0000000 --- a/lib/models/parser.js +++ /dev/null @@ -1,122 +0,0 @@ -var Immutable = require('immutable'); -var Promise = require('../utils/promise'); - -var Parser = Immutable.Record({ - name: String(), - - // List of extensions that can be processed using this parser - extensions: Immutable.List(), - - // Parsing functions - readme: Function(), - langs: Function(), - summary: Function(), - glossary: Function(), - page: Function(), - inline: Function() -}); - -Parser.prototype.getName = function() { - return this.get('name'); -}; - -Parser.prototype.getExtensions = function() { - return this.get('extensions'); -}; - -// PARSE - -Parser.prototype.parseReadme = function(content) { - var readme = this.get('readme'); - return Promise(readme(content)); -}; - -Parser.prototype.parseSummary = function(content) { - var summary = this.get('summary'); - return Promise(summary(content)); -}; - -Parser.prototype.parseGlossary = function(content) { - var glossary = this.get('glossary'); - return Promise(glossary(content)); -}; - -Parser.prototype.preparePage = function(content) { - var page = this.get('page'); - if (!page.prepare) { - return Promise(content); - } - - return Promise(page.prepare(content)); -}; - -Parser.prototype.parsePage = function(content) { - var page = this.get('page'); - return Promise(page(content)); -}; - -Parser.prototype.parseInline = function(content) { - var inline = this.get('inline'); - return Promise(inline(content)); -}; - -Parser.prototype.parseLanguages = function(content) { - var langs = this.get('langs'); - return Promise(langs(content)); -}; - -Parser.prototype.parseInline = function(content) { - var inline = this.get('inline'); - return Promise(inline(content)); -}; - -// TO TEXT - -Parser.prototype.renderLanguages = function(content) { - var langs = this.get('langs'); - return Promise(langs.toText(content)); -}; - -Parser.prototype.renderSummary = function(content) { - var summary = this.get('summary'); - return Promise(summary.toText(content)); -}; - -Parser.prototype.renderGlossary = function(content) { - var glossary = this.get('glossary'); - return Promise(glossary.toText(content)); -}; - -/** - Test if this parser matches an extension - - @param {String} ext - @return {Boolean} -*/ -Parser.prototype.matchExtension = function(ext) { - var exts = this.getExtensions(); - return exts.includes(ext.toLowerCase()); -}; - -/** - Create a new parser using a module (gitbook-markdown, etc) - - @param {String} name - @param {Array<String>} extensions - @param {Object} module - @return {Parser} -*/ -Parser.create = function(name, extensions, module) { - return new Parser({ - name: name, - extensions: Immutable.List(extensions), - readme: module.readme, - langs: module.langs, - summary: module.summary, - glossary: module.glossary, - page: module.page, - inline: module.inline - }); -}; - -module.exports = Parser; diff --git a/lib/models/plugin.js b/lib/models/plugin.js deleted file mode 100644 index acabba9..0000000 --- a/lib/models/plugin.js +++ /dev/null @@ -1,169 +0,0 @@ -var Immutable = require('immutable'); - -var TemplateBlock = require('./templateBlock'); -var PluginDependency = require('./pluginDependency'); -var THEME_PREFIX = require('../constants/themePrefix'); - -var DEFAULT_VERSION = '*'; - -var Plugin = Immutable.Record({ - name: String(), - - // Requirement version (ex: ">1.0.0") - version: String(DEFAULT_VERSION), - - // Path to load this plugin - path: String(), - - // Depth of this plugin in the dependency tree - depth: Number(0), - - // Parent depending on this plugin - parent: String(), - - // Content of the "package.json" - package: Immutable.Map(), - - // Content of the package itself - content: Immutable.Map() -}, 'Plugin'); - -Plugin.prototype.getName = function() { - return this.get('name'); -}; - -Plugin.prototype.getPath = function() { - return this.get('path'); -}; - -Plugin.prototype.getVersion = function() { - return this.get('version'); -}; - -Plugin.prototype.getPackage = function() { - return this.get('package'); -}; - -Plugin.prototype.getContent = function() { - return this.get('content'); -}; - -Plugin.prototype.getDepth = function() { - return this.get('depth'); -}; - -Plugin.prototype.getParent = function() { - return this.get('parent'); -}; - -/** - * Return the ID on NPM for this plugin - * @return {String} - */ -Plugin.prototype.getNpmID = function() { - return PluginDependency.nameToNpmID(this.getName()); -}; - -/** - * Check if a plugin is loaded - * @return {Boolean} - */ -Plugin.prototype.isLoaded = function() { - return Boolean(this.getPackage().size > 0); -}; - -/** - * Check if a plugin is a theme given its name - * @return {Boolean} - */ -Plugin.prototype.isTheme = function() { - var name = this.getName(); - return (name && name.indexOf(THEME_PREFIX) === 0); -}; - -/** - * Return map of hooks - * @return {Map<String:Function>} - */ -Plugin.prototype.getHooks = function() { - return this.getContent().get('hooks') || Immutable.Map(); -}; - -/** - * Return infos about resources for a specific type - * @param {String} type - * @return {Map<String:Mixed>} - */ -Plugin.prototype.getResources = function(type) { - if (type != 'website' && type != 'ebook') { - throw new Error('Invalid assets type ' + type); - } - - var content = this.getContent(); - return (content.get(type) - || (type == 'website'? content.get('book') : null) - || Immutable.Map()); -}; - -/** - * Return map of filters - * @return {Map<String:Function>} - */ -Plugin.prototype.getFilters = function() { - return this.getContent().get('filters'); -}; - -/** - * Return map of blocks - * @return {Map<String:TemplateBlock>} - */ -Plugin.prototype.getBlocks = function() { - var blocks = this.getContent().get('blocks'); - blocks = blocks || Immutable.Map(); - - return blocks - .map(function(block, blockName) { - return TemplateBlock.create(blockName, block); - }); -}; - -/** - * Return a specific hook - * @param {String} name - * @return {Function|undefined} - */ -Plugin.prototype.getHook = function(name) { - return this.getHooks().get(name); -}; - -/** - * Create a plugin from a string - * @param {String} - * @return {Plugin} - */ -Plugin.createFromString = function(s) { - var parts = s.split('@'); - var name = parts[0]; - var version = parts.slice(1).join('@'); - - return new Plugin({ - name: name, - version: version || DEFAULT_VERSION - }); -}; - -/** - * Create a plugin from a dependency - * @param {PluginDependency} - * @return {Plugin} - */ -Plugin.createFromDep = function(dep) { - return new Plugin({ - name: dep.getName(), - version: dep.getVersion() - }); -}; - -Plugin.nameToNpmID = PluginDependency.nameToNpmID; - -module.exports = Plugin; diff --git a/lib/models/pluginDependency.js b/lib/models/pluginDependency.js deleted file mode 100644 index 8866294..0000000 --- a/lib/models/pluginDependency.js +++ /dev/null @@ -1,168 +0,0 @@ -var is = require('is'); -var semver = require('semver'); -var Immutable = require('immutable'); - -var PREFIX = require('../constants/pluginPrefix'); -var DEFAULT_VERSION = '*'; - -/* - * PluginDependency represents the informations about a plugin - * stored in config.plugins - */ -var PluginDependency = Immutable.Record({ - name: String(), - - // Requirement version (ex: ">1.0.0") - version: String(DEFAULT_VERSION), - - // Is this plugin enabled or disabled? - enabled: Boolean(true) -}, 'PluginDependency'); - -PluginDependency.prototype.getName = function() { - return this.get('name'); -}; - -PluginDependency.prototype.getVersion = function() { - return this.get('version'); -}; - -PluginDependency.prototype.isEnabled = function() { - return this.get('enabled'); -}; - -/** - * Toggle this plugin state - * @param {Boolean} - * @return {PluginDependency} - */ -PluginDependency.prototype.toggle = function(state) { - if (is.undef(state)) { - state = !this.isEnabled(); - } - - return this.set('enabled', state); -}; - -/** - * Return NPM ID for the dependency - * @return {String} - */ -PluginDependency.prototype.getNpmID = function() { - return PluginDependency.nameToNpmID(this.getName()); -}; - -/** - * Is the plugin using a git dependency - * @return {Boolean} - */ -PluginDependency.prototype.isGitDependency = function() { - return !semver.validRange(this.getVersion()); -}; - -/** - * Create a plugin with a name and a plugin - * @param {String} - * @return {Plugin|undefined} - */ -PluginDependency.create = function(name, version, enabled) { - if (is.undefined(enabled)) { - enabled = true; - } - - return new PluginDependency({ - name: name, - version: version || DEFAULT_VERSION, - enabled: Boolean(enabled) - }); -}; - -/** - * Create a plugin from a string - * @param {String} - * @return {Plugin|undefined} - */ -PluginDependency.createFromString = function(s) { - var parts = s.split('@'); - var name = parts[0]; - var version = parts.slice(1).join('@'); - var enabled = true; - - if (name[0] === '-') { - enabled = false; - name = name.slice(1); - } - - return new PluginDependency({ - name: name, - version: version || DEFAULT_VERSION, - enabled: enabled - }); -}; - -/** - * Create a PluginDependency from a string - * @param {String} - * @return {List<PluginDependency>} - */ -PluginDependency.listFromString = function(s) { - var parts = s.split(','); - return PluginDependency.listFromArray(parts); -}; - -/** - * Create a PluginDependency from an array - * @param {Array} - * @return {List<PluginDependency>} - */ -PluginDependency.listFromArray = function(arr) { - return Immutable.List(arr) - .map(function(entry) { - if (is.string(entry)) { - return PluginDependency.createFromString(entry); - } else { - return PluginDependency({ - name: entry.get('name'), - version: entry.get('version') - }); - } - }) - .filter(function(dep) { - return Boolean(dep.getName()); - }); -}; - -/** - * Export plugin dependencies as an array - * @param {List<PluginDependency>} list - * @return {Array<String>} - */ -PluginDependency.listToArray = function(list) { - return list - .map(function(dep) { - var result = ''; - - if (!dep.isEnabled()) { - result += '-'; - } - - result += dep.getName(); - if (dep.getVersion() !== DEFAULT_VERSION) { - result += '@' + dep.getVersion(); - } - - return result; - }) - .toJS(); -}; - -/** - * Return NPM id for a plugin name - * @param {String} - * @return {String} - */ -PluginDependency.nameToNpmID = function(s) { - return PREFIX + s; -}; - -module.exports = PluginDependency; diff --git a/lib/models/readme.js b/lib/models/readme.js deleted file mode 100644 index c655c82..0000000 --- a/lib/models/readme.js +++ /dev/null @@ -1,40 +0,0 @@ -var Immutable = require('immutable'); - -var File = require('./file'); - -var Readme = Immutable.Record({ - file: File(), - title: String(), - description: String() -}); - -Readme.prototype.getFile = function() { - return this.get('file'); -}; - -Readme.prototype.getTitle = function() { - return this.get('title'); -}; - -Readme.prototype.getDescription = function() { - return this.get('description'); -}; - -/** - Create a new readme - - @param {File} file - @param {Object} def - @return {Readme} -*/ -Readme.create = function(file, def) { - def = def || {}; - - return new Readme({ - file: file, - title: def.title || '', - description: def.description || '' - }); -}; - -module.exports = Readme; diff --git a/lib/models/summary.js b/lib/models/summary.js deleted file mode 100644 index 70f0535..0000000 --- a/lib/models/summary.js +++ /dev/null @@ -1,228 +0,0 @@ -var is = require('is'); -var Immutable = require('immutable'); - -var error = require('../utils/error'); -var LocationUtils = require('../utils/location'); -var File = require('./file'); -var SummaryPart = require('./summaryPart'); -var SummaryArticle = require('./summaryArticle'); -var parsers = require('../parsers'); - -var Summary = Immutable.Record({ - file: File(), - parts: Immutable.List() -}, 'Summary'); - -Summary.prototype.getFile = function() { - return this.get('file'); -}; - -Summary.prototype.getParts = function() { - return this.get('parts'); -}; - -/** - Return a part by its index - - @param {Number} - @return {Part} -*/ -Summary.prototype.getPart = function(i) { - var parts = this.getParts(); - return parts.get(i); -}; - -/** - Return an article using an iterator to find it. - if "partIter" is set, it can also return a Part. - - @param {Function} iter - @param {Function} partIter - @return {Article|Part} -*/ -Summary.prototype.getArticle = function(iter, partIter) { - var parts = this.getParts(); - - return parts.reduce(function(result, part) { - if (result) return result; - - if (partIter && partIter(part)) return part; - return SummaryArticle.findArticle(part, iter); - }, null); -}; - - -/** - Return a part/article by its level - - @param {String} level - @return {Article|Part} -*/ -Summary.prototype.getByLevel = function(level) { - function iterByLevel(article) { - return (article.getLevel() === level); - } - - return this.getArticle(iterByLevel, iterByLevel); -}; - -/** - Return an article by its path - - @param {String} filePath - @return {Article} -*/ -Summary.prototype.getByPath = function(filePath) { - return this.getArticle(function(article) { - var articlePath = article.getPath(); - - return ( - articlePath && - LocationUtils.areIdenticalPaths(articlePath, filePath) - ); - }); -}; - -/** - Return the first article - - @return {Article} -*/ -Summary.prototype.getFirstArticle = function() { - return this.getArticle(function(article) { - return true; - }); -}; - -/** - Return next article of an article - - @param {Article} current - @return {Article} -*/ -Summary.prototype.getNextArticle = function(current) { - var level = is.string(current)? current : current.getLevel(); - var wasPrev = false; - - return this.getArticle(function(article) { - if (wasPrev) return true; - - wasPrev = article.getLevel() == level; - return false; - }); -}; - -/** - Return previous article of an article - - @param {Article} current - @return {Article} -*/ -Summary.prototype.getPrevArticle = function(current) { - var level = is.string(current)? current : current.getLevel(); - var prev = undefined; - - this.getArticle(function(article) { - if (article.getLevel() == level) { - return true; - } - - prev = article; - return false; - }); - - return prev; -}; - -/** - Return the parent article, or parent part of an article - - @param {String|Article} current - @return {Article|Part|Null} -*/ -Summary.prototype.getParent = function (level) { - // Coerce to level - level = is.string(level)? level : level.getLevel(); - - // Get parent level - var parentLevel = getParentLevel(level); - if (!parentLevel) { - return null; - } - - // Get parent of the position - var parentArticle = this.getByLevel(parentLevel); - return parentArticle || null; -}; - -/** - Render summary as text - - @param {String} parseExt Extension of the parser to use - @return {Promise<String>} -*/ -Summary.prototype.toText = function(parseExt) { - var file = this.getFile(); - var parts = this.getParts(); - - var parser = parseExt? parsers.getByExt(parseExt) : file.getParser(); - - if (!parser) { - throw error.FileNotParsableError({ - filename: file.getPath() - }); - } - - return parser.renderSummary({ - parts: parts.toJS() - }); -}; - -/** - Return all articles as a list - - @return {List<Article>} -*/ -Summary.prototype.getArticlesAsList = function() { - var accu = []; - - this.getArticle(function(article) { - accu.push(article); - }); - - return Immutable.List(accu); -}; - -/** - Create a new summary for a list of parts - - @param {Lust|Array} parts - @return {Summary} -*/ -Summary.createFromParts = function createFromParts(file, parts) { - parts = parts.map(function(part, i) { - if (part instanceof SummaryPart) { - return part; - } - - return SummaryPart.create(part, i + 1); - }); - - return new Summary({ - file: file, - parts: new Immutable.List(parts) - }); -}; - -/** - Returns parent level of a level - - @param {String} level - @return {String} -*/ -function getParentLevel(level) { - var parts = level.split('.'); - return parts.slice(0, -1).join('.'); -} - -module.exports = Summary; diff --git a/lib/models/summaryArticle.js b/lib/models/summaryArticle.js deleted file mode 100644 index 6da8d1d..0000000 --- a/lib/models/summaryArticle.js +++ /dev/null @@ -1,189 +0,0 @@ -var Immutable = require('immutable'); - -var location = require('../utils/location'); - -/* - An article represents an entry in the Summary / table of Contents -*/ - -var SummaryArticle = Immutable.Record({ - level: String(), - title: String(), - ref: String(), - articles: Immutable.List() -}, 'SummaryArticle'); - -SummaryArticle.prototype.getLevel = function() { - return this.get('level'); -}; - -SummaryArticle.prototype.getTitle = function() { - return this.get('title'); -}; - -SummaryArticle.prototype.getRef = function() { - return this.get('ref'); -}; - -SummaryArticle.prototype.getArticles = function() { - return this.get('articles'); -}; - -/** - * Return how deep the article is. - * The README has a depth of 1 - * - * @return {Number} - */ -SummaryArticle.prototype.getDepth = function() { - return (this.getLevel().split('.').length - 1); -}; - -/** - * Get path (without anchor) to the pointing file. - * It also normalizes the file path. - * - * @return {String} - */ -SummaryArticle.prototype.getPath = function() { - if (this.isExternal()) { - return undefined; - } - - var ref = this.getRef(); - if (!ref) { - return undefined; - } - - var parts = ref.split('#'); - - var pathname = (parts.length > 1? parts.slice(0, -1).join('#') : ref); - - // Normalize path to remove ('./', '/...', etc) - return location.flatten(pathname); -}; - -/** - * Return url if article is external - * - * @return {String} - */ -SummaryArticle.prototype.getUrl = function() { - return this.isExternal()? this.getRef() : undefined; -}; - -/** - * Get anchor for this article (or undefined) - * - * @return {String} - */ -SummaryArticle.prototype.getAnchor = function() { - var ref = this.getRef(); - var parts = ref.split('#'); - - var anchor = (parts.length > 1? '#' + parts[parts.length - 1] : undefined); - return anchor; -}; - -/** - * Create a new level for a new child article - * - * @return {String} - */ -SummaryArticle.prototype.createChildLevel = function() { - var level = this.getLevel(); - var subArticles = this.getArticles(); - var childLevel = level + '.' + (subArticles.size + 1); - - return childLevel; -}; - -/** - * Is article pointing to a page of an absolute url - * - * @return {Boolean} - */ -SummaryArticle.prototype.isPage = function() { - return !this.isExternal() && this.getRef(); -}; - -/** - * Check if this article is a file (exatcly) - * - * @param {File} file - * @return {Boolean} - */ -SummaryArticle.prototype.isFile = function(file) { - return ( - file.getPath() === this.getPath() - && this.getAnchor() === undefined - ); -}; - -/** - * Check if this article is the introduction of the book - * - * @param {Book|Readme} book - * @return {Boolean} - */ -SummaryArticle.prototype.isReadme = function(book) { - var readme = book.getFile? book : book.getReadme(); - var file = readme.getFile(); - - return this.isFile(file); -}; - -/** - * Is article pointing to aan absolute url - * - * @return {Boolean} - */ -SummaryArticle.prototype.isExternal = function() { - return location.isExternal(this.getRef()); -}; - -/** - * Create a SummaryArticle - * - * @param {Object} def - * @return {SummaryArticle} - */ -SummaryArticle.create = function(def, level) { - var articles = (def.articles || []).map(function(article, i) { - if (article instanceof SummaryArticle) { - return article; - } - return SummaryArticle.create(article, [level, i + 1].join('.')); - }); - - return new SummaryArticle({ - level: level, - title: def.title, - ref: def.ref || def.path || '', - articles: Immutable.List(articles) - }); -}; - -/** - * Find an article from a base one - * - * @param {Article|Part} base - * @param {Function(article)} iter - * @return {Article} - */ -SummaryArticle.findArticle = function(base, iter) { - var articles = base.getArticles(); - - return articles.reduce(function(result, article) { - if (result) return result; - - if (iter(article)) { - return article; - } - - return SummaryArticle.findArticle(article, iter); - }, null); -}; - - -module.exports = SummaryArticle; diff --git a/lib/models/summaryPart.js b/lib/models/summaryPart.js deleted file mode 100644 index f0e6f57..0000000 --- a/lib/models/summaryPart.js +++ /dev/null @@ -1,61 +0,0 @@ -var Immutable = require('immutable'); - -var SummaryArticle = require('./summaryArticle'); - -/* - A part represents a section in the Summary / table of Contents -*/ - -var SummaryPart = Immutable.Record({ - level: String(), - title: String(), - articles: Immutable.List() -}); - -SummaryPart.prototype.getLevel = function() { - return this.get('level'); -}; - -SummaryPart.prototype.getTitle = function() { - return this.get('title'); -}; - -SummaryPart.prototype.getArticles = function() { - return this.get('articles'); -}; - -/** - * Create a new level for a new child article - * - * @return {String} - */ -SummaryPart.prototype.createChildLevel = function() { - var level = this.getLevel(); - var subArticles = this.getArticles(); - var childLevel = level + '.' + (subArticles.size + 1); - - return childLevel; -}; - -/** - * Create a SummaryPart - * - * @param {Object} def - * @return {SummaryPart} - */ -SummaryPart.create = function(def, level) { - var articles = (def.articles || []).map(function(article, i) { - if (article instanceof SummaryArticle) { - return article; - } - return SummaryArticle.create(article, [level, i + 1].join('.')); - }); - - return new SummaryPart({ - level: String(level), - title: def.title, - articles: Immutable.List(articles) - }); -}; - -module.exports = SummaryPart; diff --git a/lib/models/templateBlock.js b/lib/models/templateBlock.js deleted file mode 100644 index 458f084..0000000 --- a/lib/models/templateBlock.js +++ /dev/null @@ -1,281 +0,0 @@ -var is = require('is'); -var extend = require('extend'); -var Immutable = require('immutable'); - -var Promise = require('../utils/promise'); -var genKey = require('../utils/genKey'); -var TemplateShortcut = require('./templateShortcut'); - -var NODE_ENDARGS = '%%endargs%%'; - -var TemplateBlock = Immutable.Record({ - // Name of block, also the start tag - name: String(), - - // End tag, default to "end<name>" - end: String(), - - // Function to process the block content - process: Function(), - - // List of String, for inner block tags - blocks: Immutable.List(), - - // List of shortcuts to replace with this block - shortcuts: Immutable.Map() -}, 'TemplateBlock'); - -TemplateBlock.prototype.getName = function() { - return this.get('name'); -}; - -TemplateBlock.prototype.getEndTag = function() { - return this.get('end') || ('end' + this.getName()); -}; - -TemplateBlock.prototype.getProcess = function() { - return this.get('process'); -}; - -TemplateBlock.prototype.getBlocks = function() { - return this.get('blocks'); -}; - - -/** - * Return shortcuts associated with this block or undefined - * @return {TemplateShortcut|undefined} - */ -TemplateBlock.prototype.getShortcuts = function() { - var shortcuts = this.get('shortcuts'); - if (shortcuts.size === 0) { - return undefined; - } - - return TemplateShortcut.createForBlock(this, shortcuts); -}; - -/** - * Return name for the nunjucks extension - * @return {String} - */ -TemplateBlock.prototype.getExtensionName = function() { - return 'Block' + this.getName() + 'Extension'; -}; - -/** - * Return a nunjucks extension to represents this block - * @return {Nunjucks.Extension} - */ -TemplateBlock.prototype.toNunjucksExt = function(mainContext, blocksOutput) { - blocksOutput = blocksOutput || {}; - - var that = this; - var name = this.getName(); - var endTag = this.getEndTag(); - var blocks = this.getBlocks().toJS(); - - function Ext() { - this.tags = [name]; - - this.parse = function(parser, nodes) { - var lastBlockName = null; - var lastBlockArgs = null; - var allBlocks = blocks.concat([endTag]); - - // Parse first block - var tok = parser.nextToken(); - lastBlockArgs = parser.parseSignature(null, true); - parser.advanceAfterBlockEnd(tok.value); - - var args = new nodes.NodeList(); - var bodies = []; - var blockNamesNode = new nodes.Array(tok.lineno, tok.colno); - var blockArgCounts = new nodes.Array(tok.lineno, tok.colno); - - // Parse while we found "end<block>" - do { - // Read body - var currentBody = parser.parseUntilBlocks.apply(parser, allBlocks); - - // Handle body with previous block name and args - blockNamesNode.addChild(new nodes.Literal(args.lineno, args.colno, lastBlockName)); - blockArgCounts.addChild(new nodes.Literal(args.lineno, args.colno, lastBlockArgs.children.length)); - bodies.push(currentBody); - - // Append arguments of this block as arguments of the run function - lastBlockArgs.children.forEach(function(child) { - args.addChild(child); - }); - - // Read new block - lastBlockName = parser.nextToken().value; - - // Parse signature and move to the end of the block - if (lastBlockName != endTag) { - lastBlockArgs = parser.parseSignature(null, true); - } - - parser.advanceAfterBlockEnd(lastBlockName); - } while (lastBlockName != endTag); - - args.addChild(blockNamesNode); - args.addChild(blockArgCounts); - args.addChild(new nodes.Literal(args.lineno, args.colno, NODE_ENDARGS)); - - return new nodes.CallExtensionAsync(this, 'run', args, bodies); - }; - - this.run = function(context) { - var fnArgs = Array.prototype.slice.call(arguments, 1); - - var args; - var blocks = []; - var bodies = []; - var blockNames; - var blockArgCounts; - var callback; - - // Extract callback - callback = fnArgs.pop(); - - // Detect end of arguments - var endArgIndex = fnArgs.indexOf(NODE_ENDARGS); - - // Extract arguments and bodies - args = fnArgs.slice(0, endArgIndex); - bodies = fnArgs.slice(endArgIndex + 1); - - // Extract block counts - blockArgCounts = args.pop(); - blockNames = args.pop(); - - // Recreate list of blocks - blockNames.forEach(function(name, i) { - var countArgs = blockArgCounts[i]; - var blockBody = bodies.shift(); - - var blockArgs = countArgs > 0? args.slice(0, countArgs) : []; - args = args.slice(countArgs); - var blockKwargs = extractKwargs(blockArgs); - - blocks.push({ - name: name, - body: blockBody(), - args: blockArgs, - kwargs: blockKwargs - }); - }); - - var mainBlock = blocks.shift(); - mainBlock.blocks = blocks; - - Promise() - .then(function() { - var ctx = extend({ - ctx: context - }, mainContext || {}); - - return that.applyBlock(mainBlock, ctx); - }) - .then(function(result) { - return that.blockResultToHtml(result, blocksOutput); - }) - .nodeify(callback); - }; - } - - return Ext; -}; - -/** - * Apply a block to a content - * @param {Object} inner - * @param {Object} context - * @return {Promise<String>|String} - */ -TemplateBlock.prototype.applyBlock = function(inner, context) { - var processFn = this.getProcess(); - - inner = inner || {}; - inner.args = inner.args || []; - inner.kwargs = inner.kwargs || {}; - inner.blocks = inner.blocks || []; - - var r = processFn.call(context, inner); - - if (Promise.isPromiseAlike(r)) { - return r.then(this.normalizeBlockResult.bind(this)); - } else { - return this.normalizeBlockResult(r); - } -}; - -/** - * Normalize result from a block process function - * @param {Object|String} result - * @return {Object} - */ -TemplateBlock.prototype.normalizeBlockResult = function(result) { - if (is.string(result)) { - result = { body: result }; - } - result.name = this.getName(); - - return result; -}; - -/** - * Convert a block result to HTML - * @param {Object} result - * @param {Object} blocksOutput: stored post processing blocks in this object - * @return {String} - */ -TemplateBlock.prototype.blockResultToHtml = function(result, blocksOutput) { - var indexedKey; - var toIndex = (!result.parse) || (result.post !== undefined); - - if (toIndex) { - indexedKey = genKey(); - blocksOutput[indexedKey] = result; - } - - // Parsable block, just return it - if (result.parse) { - return result.body; - } - - // Return it as a position marker - return '{{-%' + indexedKey + '%-}}'; - -}; - -/** - * Create a template block from a function or an object - * @param {String} blockName - * @param {Object} block - * @return {TemplateBlock} - */ -TemplateBlock.create = function(blockName, block) { - if (is.fn(block)) { - block = new Immutable.Map({ - process: block - }); - } - - block = new TemplateBlock(block); - block = block.set('name', blockName); - return block; -}; - -/** - * Extract kwargs from an arguments array - * @param {Array} args - * @return {Object} - */ -function extractKwargs(args) { - var last = args[args.length - 1]; - return (is.object(last) && last.__keywords)? args.pop() : {}; -} - -module.exports = TemplateBlock; diff --git a/lib/models/templateEngine.js b/lib/models/templateEngine.js deleted file mode 100644 index 5724d55..0000000 --- a/lib/models/templateEngine.js +++ /dev/null @@ -1,139 +0,0 @@ -var nunjucks = require('nunjucks'); -var Immutable = require('immutable'); - -var TemplateEngine = Immutable.Record({ - // Map of {TemplateBlock} - blocks: Immutable.Map(), - - // Map of Extension - extensions: Immutable.Map(), - - // Map of filters: {String} name -> {Function} fn - filters: Immutable.Map(), - - // Map of globals: {String} name -> {Mixed} - globals: Immutable.Map(), - - // Context for filters / blocks - context: Object(), - - // Nunjucks loader - loader: nunjucks.FileSystemLoader('views') -}, 'TemplateEngine'); - -TemplateEngine.prototype.getBlocks = function() { - return this.get('blocks'); -}; - -TemplateEngine.prototype.getGlobals = function() { - return this.get('globals'); -}; - -TemplateEngine.prototype.getFilters = function() { - return this.get('filters'); -}; - -TemplateEngine.prototype.getShortcuts = function() { - return this.get('shortcuts'); -}; - -TemplateEngine.prototype.getLoader = function() { - return this.get('loader'); -}; - -TemplateEngine.prototype.getContext = function() { - return this.get('context'); -}; - -TemplateEngine.prototype.getExtensions = function() { - return this.get('extensions'); -}; - -/** - Return a block by its name (or undefined) - - @param {String} name - @return {TemplateBlock} -*/ -TemplateEngine.prototype.getBlock = function(name) { - var blocks = this.getBlocks(); - return blocks.find(function(block) { - return block.getName() === name; - }); -}; - -/** - Return a nunjucks environment from this configuration - - @return {Nunjucks.Environment} -*/ -TemplateEngine.prototype.toNunjucks = function(blocksOutput) { - var loader = this.getLoader(); - var blocks = this.getBlocks(); - var filters = this.getFilters(); - var globals = this.getGlobals(); - var extensions = this.getExtensions(); - var context = this.getContext(); - - var env = new nunjucks.Environment( - loader, - { - // Escaping is done after by the asciidoc/markdown parser - autoescape: false, - - // Syntax - tags: { - blockStart: '{%', - blockEnd: '%}', - variableStart: '{{', - variableEnd: '}}', - commentStart: '{###', - commentEnd: '###}' - } - } - ); - - // Add filters - filters.forEach(function(filterFn, filterName) { - env.addFilter(filterName, filterFn.bind(context)); - }); - - // Add blocks - blocks.forEach(function(block) { - var extName = block.getExtensionName(); - var Ext = block.toNunjucksExt(context, blocksOutput); - - env.addExtension(extName, new Ext()); - }); - - // Add globals - globals.forEach(function(globalValue, globalName) { - env.addGlobal(globalName, globalValue); - }); - - // Add other extensions - extensions.forEach(function(ext, extName) { - env.addExtension(extName, ext); - }); - - return env; -}; - -/** - Create a template engine - - @param {Object} def - @return {TemplateEngine} -*/ -TemplateEngine.create = function(def) { - return new TemplateEngine({ - blocks: Immutable.List(def.blocks || []), - extensions: Immutable.Map(def.extensions || {}), - filters: Immutable.Map(def.filters || {}), - globals: Immutable.Map(def.globals || {}), - context: def.context, - loader: def.loader - }); -}; - -module.exports = TemplateEngine; diff --git a/lib/models/templateOutput.js b/lib/models/templateOutput.js deleted file mode 100644 index ae63c06..0000000 --- a/lib/models/templateOutput.js +++ /dev/null @@ -1,42 +0,0 @@ -var Immutable = require('immutable'); - -var TemplateOutput = Immutable.Record({ - // Text content of the template - content: String(), - - // Map of blocks to replace / post process - blocks: Immutable.Map() -}, 'TemplateOutput'); - -TemplateOutput.prototype.getContent = function() { - return this.get('content'); -}; - -TemplateOutput.prototype.getBlocks = function() { - return this.get('blocks'); -}; - -/** - * Update content of this output - * @param {String} content - * @return {TemplateContent} - */ -TemplateOutput.prototype.setContent = function(content) { - return this.set('content', content); -}; - -/** - * Create a TemplateOutput from a text content - * and an object containing block definition - * @param {String} content - * @param {Object} blocks - * @return {TemplateOutput} - */ -TemplateOutput.create = function(content, blocks) { - return new TemplateOutput({ - content: content, - blocks: Immutable.fromJS(blocks) - }); -}; - -module.exports = TemplateOutput; diff --git a/lib/models/templateShortcut.js b/lib/models/templateShortcut.js deleted file mode 100644 index 309fa6d..0000000 --- a/lib/models/templateShortcut.js +++ /dev/null @@ -1,73 +0,0 @@ -var Immutable = require('immutable'); -var is = require('is'); - -/* - A TemplateShortcut is defined in plugin's template blocks - to replace content with a templating block using delimiters. -*/ -var TemplateShortcut = Immutable.Record({ - // List of parser names accepting this shortcut - parsers: Immutable.Map(), - - start: String(), - end: String(), - - startTag: String(), - endTag: String() -}, 'TemplateShortcut'); - -TemplateShortcut.prototype.getStart = function() { - return this.get('start'); -}; - -TemplateShortcut.prototype.getEnd = function() { - return this.get('end'); -}; - -TemplateShortcut.prototype.getStartTag = function() { - return this.get('startTag'); -}; - -TemplateShortcut.prototype.getEndTag = function() { - return this.get('endTag'); -}; - -TemplateShortcut.prototype.getParsers = function() { - return this.get('parsers'); -}; - -/** - Test if this shortcut accept a parser - - @param {Parser|String} parser - @return {Boolean} -*/ -TemplateShortcut.prototype.acceptParser = function(parser) { - if (!is.string(parser)) { - parser = parser.getName(); - } - - var parserNames = this.get('parsers'); - return parserNames.includes(parser); -}; - -/** - Create a shortcut for a block - - @param {TemplateBlock} block - @param {Map} details - @return {TemplateShortcut} -*/ -TemplateShortcut.createForBlock = function(block, details) { - details = Immutable.fromJS(details); - - return new TemplateShortcut({ - parsers: details.get('parsers'), - start: details.get('start'), - end: details.get('end'), - startTag: block.getName(), - endTag: block.getEndTag() - }); -}; - -module.exports = TemplateShortcut; diff --git a/lib/modifiers/config/__tests__/addPlugin.js b/lib/modifiers/config/__tests__/addPlugin.js deleted file mode 100644 index 61082c9..0000000 --- a/lib/modifiers/config/__tests__/addPlugin.js +++ /dev/null @@ -1,32 +0,0 @@ -var addPlugin = require('../addPlugin'); -var Config = require('../../../models/config'); - -describe('addPlugin', function() { - var config = Config.createWithValues({ - plugins: ['hello', 'world', '-disabled'] - }); - - it('should have correct state of dependencies', function() { - var disabledDep = config.getPluginDependency('disabled'); - - expect(disabledDep).toBeDefined(); - expect(disabledDep.getVersion()).toEqual('*'); - expect(disabledDep.isEnabled()).toBeFalsy(); - }); - - it('should add the plugin to the list', function() { - var newConfig = addPlugin(config, 'test'); - - var testDep = newConfig.getPluginDependency('test'); - expect(testDep).toBeDefined(); - expect(testDep.getVersion()).toEqual('*'); - expect(testDep.isEnabled()).toBeTruthy(); - - var disabledDep = newConfig.getPluginDependency('disabled'); - expect(disabledDep).toBeDefined(); - expect(disabledDep.getVersion()).toEqual('*'); - expect(disabledDep.isEnabled()).toBeFalsy(); - }); -}); - - diff --git a/lib/modifiers/config/__tests__/removePlugin.js b/lib/modifiers/config/__tests__/removePlugin.js deleted file mode 100644 index 253cc39..0000000 --- a/lib/modifiers/config/__tests__/removePlugin.js +++ /dev/null @@ -1,33 +0,0 @@ -var removePlugin = require('../removePlugin'); -var Config = require('../../../models/config'); - -describe('removePlugin', function() { - var config = Config.createWithValues({ - plugins: ['hello', 'world', '-disabled'] - }); - - it('should remove the plugin from the list', function() { - var newConfig = removePlugin(config, 'hello'); - - var testDep = newConfig.getPluginDependency('hello'); - expect(testDep).toNotBeDefined(); - }); - - it('should remove the disabled plugin from the list', function() { - var newConfig = removePlugin(config, 'disabled'); - - var testDep = newConfig.getPluginDependency('disabled'); - expect(testDep).toNotBeDefined(); - }); - - it('should disable default plugin', function() { - var newConfig = removePlugin(config, 'search'); - - var disabledDep = newConfig.getPluginDependency('search'); - expect(disabledDep).toBeDefined(); - expect(disabledDep.getVersion()).toEqual('*'); - expect(disabledDep.isEnabled()).toBeFalsy(); - }); -}); - - diff --git a/lib/modifiers/config/__tests__/togglePlugin.js b/lib/modifiers/config/__tests__/togglePlugin.js deleted file mode 100644 index 4127853..0000000 --- a/lib/modifiers/config/__tests__/togglePlugin.js +++ /dev/null @@ -1,28 +0,0 @@ -var togglePlugin = require('../togglePlugin'); -var Config = require('../../../models/config'); - -describe('togglePlugin', function() { - var config = Config.createWithValues({ - plugins: ['hello', 'world', '-disabled'] - }); - - it('should enable plugin', function() { - var newConfig = togglePlugin(config, 'disabled'); - - var testDep = newConfig.getPluginDependency('disabled'); - expect(testDep).toBeDefined(); - expect(testDep.getVersion()).toEqual('*'); - expect(testDep.isEnabled()).toBeTruthy(); - }); - - it('should disable plugin', function() { - var newConfig = togglePlugin(config, 'world'); - - var testDep = newConfig.getPluginDependency('world'); - expect(testDep).toBeDefined(); - expect(testDep.getVersion()).toEqual('*'); - expect(testDep.isEnabled()).toBeFalsy(); - }); -}); - - diff --git a/lib/modifiers/config/addPlugin.js b/lib/modifiers/config/addPlugin.js deleted file mode 100644 index b8d4ea1..0000000 --- a/lib/modifiers/config/addPlugin.js +++ /dev/null @@ -1,25 +0,0 @@ -var PluginDependency = require('../../models/pluginDependency'); -var togglePlugin = require('./togglePlugin'); -var isDefaultPlugin = require('./isDefaultPlugin'); - -/** - * Add a plugin to a book's configuration - * @param {Config} config - * @param {String} pluginName - * @param {String} version (optional) - * @return {Config} - */ -function addPlugin(config, pluginName, version) { - // For default plugin, we only ensure it is enabled - if (isDefaultPlugin(pluginName, version)) { - return togglePlugin(config, pluginName, true); - } - - var deps = config.getPluginDependencies(); - var dep = PluginDependency.create(pluginName, version); - - deps = deps.push(dep); - return config.setPluginDependencies(deps); -} - -module.exports = addPlugin; diff --git a/lib/modifiers/config/editPlugin.js b/lib/modifiers/config/editPlugin.js deleted file mode 100644 index a792acd..0000000 --- a/lib/modifiers/config/editPlugin.js +++ /dev/null @@ -1,13 +0,0 @@ - -/** - * Edit configuration of a plugin - * @param {Config} config - * @param {String} plugin - * @param {Object} pluginConfig - * @return {Config} - */ -function editPlugin(config, pluginName, pluginConfig) { - return config.setValue('pluginsConfig.'+pluginName, pluginConfig); -} - -module.exports = editPlugin; diff --git a/lib/modifiers/config/getPluginConfig.js b/lib/modifiers/config/getPluginConfig.js deleted file mode 100644 index ae76de8..0000000 --- a/lib/modifiers/config/getPluginConfig.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Return the configuration for a plugin - * @param {Config} config - * @param {String} pluginName - * @return {Object} - */ -function getPluginConfig(config, pluginName) { - var pluginsConfig = config.getValues().get('pluginsConfig'); - if (pluginsConfig === undefined) { - return {}; - } - var pluginConf = pluginsConfig.get(pluginName); - if (pluginConf === undefined) { - return {}; - } else { - return pluginConf.toJS(); - } -} - -module.exports = getPluginConfig; diff --git a/lib/modifiers/config/hasPlugin.js b/lib/modifiers/config/hasPlugin.js deleted file mode 100644 index 9aab4f2..0000000 --- a/lib/modifiers/config/hasPlugin.js +++ /dev/null @@ -1,15 +0,0 @@ - -/** - * Test if a plugin is listed - * @param { {List<PluginDependency}} deps - * @param {String} plugin - * @param {String} version - * @return {Boolean} - */ -function hasPlugin(deps, pluginName, version) { - return !!deps.find(function(dep) { - return dep.getName() === pluginName && (!version || dep.getVersion() === version); - }); -} - -module.exports = hasPlugin; diff --git a/lib/modifiers/config/index.js b/lib/modifiers/config/index.js deleted file mode 100644 index b3de0b0..0000000 --- a/lib/modifiers/config/index.js +++ /dev/null @@ -1,10 +0,0 @@ - -module.exports = { - addPlugin: require('./addPlugin'), - removePlugin: require('./removePlugin'), - togglePlugin: require('./togglePlugin'), - editPlugin: require('./editPlugin'), - hasPlugin: require('./hasPlugin'), - getPluginConfig: require('./getPluginConfig'), - isDefaultPlugin: require('./isDefaultPlugin') -}; diff --git a/lib/modifiers/config/isDefaultPlugin.js b/lib/modifiers/config/isDefaultPlugin.js deleted file mode 100644 index 63a141d..0000000 --- a/lib/modifiers/config/isDefaultPlugin.js +++ /dev/null @@ -1,14 +0,0 @@ -var DEFAULT_PLUGINS = require('../../constants/defaultPlugins'); -var hasPlugin = require('./hasPlugin'); - -/** - * Test if a plugin is a default one - * @param {String} plugin - * @param {String} version - * @return {Boolean} - */ -function isDefaultPlugin(pluginName, version) { - return hasPlugin(DEFAULT_PLUGINS, pluginName, version); -} - -module.exports = isDefaultPlugin; diff --git a/lib/modifiers/config/removePlugin.js b/lib/modifiers/config/removePlugin.js deleted file mode 100644 index ec06d1e..0000000 --- a/lib/modifiers/config/removePlugin.js +++ /dev/null @@ -1,25 +0,0 @@ -var togglePlugin = require('./togglePlugin'); -var isDefaultPlugin = require('./isDefaultPlugin'); - -/** - * Remove a plugin from a book's configuration - * @param {Config} config - * @param {String} plugin - * @return {Config} - */ -function removePlugin(config, pluginName) { - var deps = config.getPluginDependencies(); - - // For default plugin, we have to disable it instead of removing from the list - if (isDefaultPlugin(pluginName)) { - return togglePlugin(config, pluginName, false); - } - - // Remove the dependency from the list - deps = deps.filterNot(function(dep) { - return dep.getName() === pluginName; - }); - return config.setPluginDependencies(deps); -} - -module.exports = removePlugin; diff --git a/lib/modifiers/config/togglePlugin.js b/lib/modifiers/config/togglePlugin.js deleted file mode 100644 index a49e3b9..0000000 --- a/lib/modifiers/config/togglePlugin.js +++ /dev/null @@ -1,31 +0,0 @@ -var PluginDependency = require('../../models/pluginDependency'); -var hasPlugin = require('./hasPlugin'); -var isDefaultPlugin = require('./isDefaultPlugin'); - -/** - * Enable/disable a plugin dependency - * @param {Config} config - * @param {String} pluginName - * @param {Boolean} state (optional) - * @return {Config} - */ -function togglePlugin(config, pluginName, state) { - var deps = config.getPluginDependencies(); - - // For default plugin, we should ensure it's listed first - if (isDefaultPlugin(pluginName) && !hasPlugin(deps, pluginName)) { - deps = deps.push(PluginDependency.create(pluginName)); - } - - deps = deps.map(function(dep) { - if (dep.getName() === pluginName) { - return dep.toggle(state); - } - - return dep; - }); - - return config.setPluginDependencies(deps); -} - -module.exports = togglePlugin; diff --git a/lib/modifiers/index.js b/lib/modifiers/index.js deleted file mode 100644 index ad24604..0000000 --- a/lib/modifiers/index.js +++ /dev/null @@ -1,5 +0,0 @@ - -module.exports = { - Summary: require('./summary'), - Config: require('./config') -}; diff --git a/lib/modifiers/summary/__tests__/editArticle.js b/lib/modifiers/summary/__tests__/editArticle.js deleted file mode 100644 index e69de29..0000000 --- a/lib/modifiers/summary/__tests__/editArticle.js +++ /dev/null diff --git a/lib/modifiers/summary/__tests__/editPartTitle.js b/lib/modifiers/summary/__tests__/editPartTitle.js deleted file mode 100644 index d1b916b..0000000 --- a/lib/modifiers/summary/__tests__/editPartTitle.js +++ /dev/null @@ -1,44 +0,0 @@ -var Summary = require('../../../models/summary'); -var File = require('../../../models/file'); - -describe('editPartTitle', function() { - var editPartTitle = require('../editPartTitle'); - var summary = Summary.createFromParts(File(), [ - { - articles: [ - { - title: 'My First Article', - path: 'README.md' - }, - { - title: 'My Second Article', - path: 'article.md' - } - ] - }, - { - title: 'Test' - } - ]); - - it('should correctly set title of first part', function() { - var newSummary = editPartTitle(summary, 0, 'Hello World'); - var part = newSummary.getPart(0); - - expect(part.getTitle()).toBe('Hello World'); - }); - - it('should correctly set title of second part', function() { - var newSummary = editPartTitle(summary, 1, 'Hello'); - var part = newSummary.getPart(1); - - expect(part.getTitle()).toBe('Hello'); - }); - - it('should not fail if part doesn\'t exist', function() { - var newSummary = editPartTitle(summary, 3, 'Hello'); - expect(newSummary.getParts().size).toBe(2); - }); -}); - - diff --git a/lib/modifiers/summary/__tests__/insertArticle.js b/lib/modifiers/summary/__tests__/insertArticle.js deleted file mode 100644 index 1ee1c8a..0000000 --- a/lib/modifiers/summary/__tests__/insertArticle.js +++ /dev/null @@ -1,78 +0,0 @@ -var Summary = require('../../../models/summary'); -var SummaryArticle = require('../../../models/summaryArticle'); -var File = require('../../../models/file'); - -describe('insertArticle', function() { - var insertArticle = require('../insertArticle'); - var summary = Summary.createFromParts(File(), [ - { - articles: [ - { - title: '1.1', - path: '1.1' - }, - { - title: '1.2', - path: '1.2' - } - ] - }, - { - title: 'Part I', - articles: [ - { - title: '2.1', - path: '2.1', - articles: [ - { - title: '2.1.1', - path: '2.1.1' - }, - { - title: '2.1.2', - path: '2.1.2' - } - ] - }, - { - title: '2.2', - path: '2.2' - } - ] - } - ]); - - it('should insert an article at a given level', function() { - var article = SummaryArticle.create({ - title: 'Inserted' - }, 'fake.level'); - - var newSummary = insertArticle(summary, article, '2.1.1'); - - var inserted = newSummary.getByLevel('2.1.1'); - var nextOne = newSummary.getByLevel('2.1.2'); - - expect(inserted.getTitle()).toBe('Inserted'); - expect(inserted.getLevel()).toBe('2.1.1'); - - expect(nextOne.getTitle()).toBe('2.1.1'); - expect(nextOne.getLevel()).toBe('2.1.2'); - }); - - it('should insert an article in last position', function() { - var article = SummaryArticle.create({ - title: 'Inserted' - }, 'fake.level'); - - var newSummary = insertArticle(summary, article, '2.2'); - - var inserted = newSummary.getByLevel('2.2'); - var previousOne = newSummary.getByLevel('2.1'); - - expect(inserted.getTitle()).toBe('Inserted'); - expect(inserted.getLevel()).toBe('2.2'); - - expect(previousOne.getTitle()).toBe('2.1'); // Unchanged - expect(previousOne.getLevel()).toBe('2.1'); - }); -}); diff --git a/lib/modifiers/summary/__tests__/insertPart.js b/lib/modifiers/summary/__tests__/insertPart.js deleted file mode 100644 index 11c2cbc..0000000 --- a/lib/modifiers/summary/__tests__/insertPart.js +++ /dev/null @@ -1,60 +0,0 @@ -var Summary = require('../../../models/summary'); -var SummaryPart = require('../../../models/summaryPart'); - -var File = require('../../../models/file'); - -describe('insertPart', function() { - var insertPart = require('../insertPart'); - var summary = Summary.createFromParts(File(), [ - { - articles: [ - { - title: '1.1', - path: '1.1' - } - ] - }, - { - title: 'Part I', - articles: [ - { - title: '2.1', - path: '2.1', - articles: [] - }, - { - title: '2.2', - path: '2.2' - } - ] - } - ]); - - it('should insert an part at a given level', function() { - var part = SummaryPart.create({ - title: 'Inserted' - }, 'meaningless.level'); - - var newSummary = insertPart(summary, part, 1); - - var inserted = newSummary.getPart(1); - expect(inserted.getTitle()).toBe('Inserted'); - expect(newSummary.getParts().count()).toBe(3); - - var otherArticle = newSummary.getByLevel('3.1'); - expect(otherArticle.getTitle()).toBe('2.1'); - expect(otherArticle.getLevel()).toBe('3.1'); - }); - - it('should insert an part in last position', function() { - var part = SummaryPart.create({ - title: 'Inserted' - }, 'meaningless.level'); - - var newSummary = insertPart(summary, part, 2); - - var inserted = newSummary.getPart(2); - expect(inserted.getTitle()).toBe('Inserted'); - expect(newSummary.getParts().count()).toBe(3); - }); -}); diff --git a/lib/modifiers/summary/__tests__/mergeAtLevel.js b/lib/modifiers/summary/__tests__/mergeAtLevel.js deleted file mode 100644 index e2635ec..0000000 --- a/lib/modifiers/summary/__tests__/mergeAtLevel.js +++ /dev/null @@ -1,45 +0,0 @@ -var Immutable = require('immutable'); -var Summary = require('../../../models/summary'); -var File = require('../../../models/file'); - -describe('mergeAtLevel', function() { - var mergeAtLevel = require('../mergeAtLevel'); - var summary = Summary.createFromParts(File(), [ - { - articles: [ - { - title: '1.1', - path: '1.1' - }, - { - title: '1.2', - path: '1.2' - } - ] - }, - { - title: 'Part I', - articles: [] - } - ]); - - it('should edit a part', function() { - var beforeChildren = summary.getByLevel('1').getArticles(); - var newSummary = mergeAtLevel(summary, '1', {title: 'Part O'}); - var edited = newSummary.getByLevel('1'); - - expect(edited.getTitle()).toBe('Part O'); - // Same children - expect(Immutable.is(beforeChildren, edited.getArticles())).toBe(true); - }); - - it('should edit a part', function() { - var beforePath = summary.getByLevel('1.2').getPath(); - var newSummary = mergeAtLevel(summary, '1.2', {title: 'Renamed article'}); - var edited = newSummary.getByLevel('1.2'); - - expect(edited.getTitle()).toBe('Renamed article'); - // Same children - expect(Immutable.is(beforePath, edited.getPath())).toBe(true); - }); -}); diff --git a/lib/modifiers/summary/__tests__/moveArticle.js b/lib/modifiers/summary/__tests__/moveArticle.js deleted file mode 100644 index aed0b94..0000000 --- a/lib/modifiers/summary/__tests__/moveArticle.js +++ /dev/null @@ -1,68 +0,0 @@ -var Immutable = require('immutable'); -var Summary = require('../../../models/summary'); -var File = require('../../../models/file'); - -describe('moveArticle', function() { - var moveArticle = require('../moveArticle'); - var summary = Summary.createFromParts(File(), [ - { - articles: [ - { - title: '1.1', - path: '1.1' - }, - { - title: '1.2', - path: '1.2' - } - ] - }, - { - title: 'Part I', - articles: [ - { - title: '2.1', - path: '2.1', - articles: [ - { - title: '2.1.1', - path: '2.1.1' - }, - { - title: '2.1.2', - path: '2.1.2' - } - ] - }, - { - title: '2.2', - path: '2.2' - } - ] - } - ]); - - it('should move an article to the same place', function() { - var newSummary = moveArticle(summary, '2.1', '2.1'); - - expect(Immutable.is(summary, newSummary)).toBe(true); - }); - - it('should move an article to an previous level', function() { - var newSummary = moveArticle(summary, '2.2', '2.1'); - var moved = newSummary.getByLevel('2.1'); - var other = newSummary.getByLevel('2.2'); - - expect(moved.getTitle()).toBe('2.2'); - expect(other.getTitle()).toBe('2.1'); - }); - - it('should move an article to a next level', function() { - var newSummary = moveArticle(summary, '2.1', '2.2'); - var moved = newSummary.getByLevel('2.1'); - var other = newSummary.getByLevel('2.2'); - - expect(moved.getTitle()).toBe('2.2'); - expect(other.getTitle()).toBe('2.1'); - }); -}); diff --git a/lib/modifiers/summary/__tests__/moveArticleAfter.js b/lib/modifiers/summary/__tests__/moveArticleAfter.js deleted file mode 100644 index c380575..0000000 --- a/lib/modifiers/summary/__tests__/moveArticleAfter.js +++ /dev/null @@ -1,82 +0,0 @@ -var Immutable = require('immutable'); -var Summary = require('../../../models/summary'); -var File = require('../../../models/file'); - -describe('moveArticleAfter', function() { - var moveArticleAfter = require('../moveArticleAfter'); - var summary = Summary.createFromParts(File(), [ - { - articles: [ - { - title: '1.1', - path: '1.1' - }, - { - title: '1.2', - path: '1.2' - } - ] - }, - { - title: 'Part I', - articles: [ - { - title: '2.1', - path: '2.1', - articles: [ - { - title: '2.1.1', - path: '2.1.1' - }, - { - title: '2.1.2', - path: '2.1.2' - } - ] - }, - { - title: '2.2', - path: '2.2' - } - ] - } - ]); - - it('moving right after itself should be invariant', function() { - var newSummary = moveArticleAfter(summary, '2.1', '2.1'); - - expect(Immutable.is(summary, newSummary)).toBe(true); - }); - - it('moving after previous one should be invariant too', function() { - var newSummary = moveArticleAfter(summary, '2.1', '2.0'); - - expect(Immutable.is(summary, newSummary)).toBe(true); - }); - - it('should move an article after a previous level', function() { - var newSummary = moveArticleAfter(summary, '2.2', '2.0'); - var moved = newSummary.getByLevel('2.1'); - - expect(moved.getTitle()).toBe('2.2'); - expect(newSummary.getByLevel('2.2').getTitle()).toBe('2.1'); - }); - - it('should move an article after a previous and less deep level', function() { - var newSummary = moveArticleAfter(summary, '2.1.1', '2.0'); - var moved = newSummary.getByLevel('2.1'); - - expect(moved.getTitle()).toBe('2.1.1'); - expect(newSummary.getByLevel('2.2.1').getTitle()).toBe('2.1.2'); - expect(newSummary.getByLevel('2.2').getTitle()).toBe('2.1'); - }); - - it('should move an article after a next level', function() { - var newSummary = moveArticleAfter(summary, '2.1', '2.2'); - var moved = newSummary.getByLevel('2.2'); - - expect(moved.getTitle()).toBe('2.1'); - expect(newSummary.getByLevel('2.1').getTitle()).toBe('2.2'); - }); - -}); diff --git a/lib/modifiers/summary/__tests__/removeArticle.js b/lib/modifiers/summary/__tests__/removeArticle.js deleted file mode 100644 index b45fb49..0000000 --- a/lib/modifiers/summary/__tests__/removeArticle.js +++ /dev/null @@ -1,53 +0,0 @@ -var Summary = require('../../../models/summary'); -var File = require('../../../models/file'); - -describe('removeArticle', function() { - var removeArticle = require('../removeArticle'); - var summary = Summary.createFromParts(File(), [ - { - articles: [ - { - title: '1.1', - path: '1.1' - }, - { - title: '1.2', - path: '1.2' - } - ] - }, - { - title: 'Part I', - articles: [ - { - title: '2.1', - path: '2.1', - articles: [ - { - title: '2.1.1', - path: '2.1.1' - }, - { - title: '2.1.2', - path: '2.1.2' - } - ] - }, - { - title: '2.2', - path: '2.2' - } - ] - } - ]); - - it('should remove an article at a given level', function() { - var newSummary = removeArticle(summary, '2.1.1'); - - var removed = newSummary.getByLevel('2.1.1'); - var nextOne = newSummary.getByLevel('2.1.2'); - - expect(removed.getTitle()).toBe('2.1.2'); - expect(nextOne).toBe(null); - }); -}); diff --git a/lib/modifiers/summary/editArticleRef.js b/lib/modifiers/summary/editArticleRef.js deleted file mode 100644 index 7106960..0000000 --- a/lib/modifiers/summary/editArticleRef.js +++ /dev/null @@ -1,17 +0,0 @@ -var mergeAtLevel = require('./mergeAtLevel'); - -/** - Edit the ref of an article - - @param {Summary} summary - @param {String} level - @param {String} newRef - @return {Summary} -*/ -function editArticleRef(summary, level, newRef) { - return mergeAtLevel(summary, level, { - ref: newRef - }); -} - -module.exports = editArticleRef; diff --git a/lib/modifiers/summary/editArticleTitle.js b/lib/modifiers/summary/editArticleTitle.js deleted file mode 100644 index 4edee83..0000000 --- a/lib/modifiers/summary/editArticleTitle.js +++ /dev/null @@ -1,17 +0,0 @@ -var mergeAtLevel = require('./mergeAtLevel'); - -/** - Edit title of an article - - @param {Summary} summary - @param {String} level - @param {String} newTitle - @return {Summary} -*/ -function editArticleTitle(summary, level, newTitle) { - return mergeAtLevel(summary, level, { - title: newTitle - }); -} - -module.exports = editArticleTitle; diff --git a/lib/modifiers/summary/editPartTitle.js b/lib/modifiers/summary/editPartTitle.js deleted file mode 100644 index b79ac1e..0000000 --- a/lib/modifiers/summary/editPartTitle.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - Edit title of a part in the summary - - @param {Summary} summary - @param {Number} index - @param {String} newTitle - @return {Summary} -*/ -function editPartTitle(summary, index, newTitle) { - var parts = summary.getParts(); - - var part = parts.get(index); - if (!part) { - return summary; - } - - part = part.set('title', newTitle); - parts = parts.set(index, part); - - return summary.set('parts', parts); -} - -module.exports = editPartTitle; diff --git a/lib/modifiers/summary/index.js b/lib/modifiers/summary/index.js deleted file mode 100644 index f91fdb6..0000000 --- a/lib/modifiers/summary/index.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - insertArticle: require('./insertArticle'), - moveArticle: require('./moveArticle'), - moveArticleAfter: require('./moveArticleAfter'), - removeArticle: require('./removeArticle'), - unshiftArticle: require('./unshiftArticle'), - editArticleTitle: require('./editArticleTitle'), - editArticleRef: require('./editArticleRef'), - - insertPart: require('./insertPart'), - removePart: require('./removePart'), - editPartTitle: require('./editPartTitle') -}; diff --git a/lib/modifiers/summary/indexArticleLevels.js b/lib/modifiers/summary/indexArticleLevels.js deleted file mode 100644 index f311f74..0000000 --- a/lib/modifiers/summary/indexArticleLevels.js +++ /dev/null @@ -1,23 +0,0 @@ - -/** - Index levels in an article tree - - @param {Article} - @param {String} baseLevel - @return {Article} -*/ -function indexArticleLevels(article, baseLevel) { - baseLevel = baseLevel || article.getLevel(); - var articles = article.getArticles(); - - articles = articles.map(function(inner, i) { - return indexArticleLevels(inner, baseLevel + '.' + (i + 1)); - }); - - return article.merge({ - level: baseLevel, - articles: articles - }); -} - -module.exports = indexArticleLevels; diff --git a/lib/modifiers/summary/indexLevels.js b/lib/modifiers/summary/indexLevels.js deleted file mode 100644 index 604e9ff..0000000 --- a/lib/modifiers/summary/indexLevels.js +++ /dev/null @@ -1,17 +0,0 @@ -var indexPartLevels = require('./indexPartLevels'); - -/** - Index all levels in the summary - - @param {Summary} - @return {Summary} -*/ -function indexLevels(summary) { - var parts = summary.getParts(); - parts = parts.map(indexPartLevels); - - return summary.set('parts', parts); -} - - -module.exports = indexLevels; diff --git a/lib/modifiers/summary/indexPartLevels.js b/lib/modifiers/summary/indexPartLevels.js deleted file mode 100644 index d19c70a..0000000 --- a/lib/modifiers/summary/indexPartLevels.js +++ /dev/null @@ -1,24 +0,0 @@ -var indexArticleLevels = require('./indexArticleLevels'); - -/** - Index levels in a part - - @param {Part} - @param {Number} index - @return {Part} -*/ -function indexPartLevels(part, index) { - var baseLevel = String(index + 1); - var articles = part.getArticles(); - - articles = articles.map(function(inner, i) { - return indexArticleLevels(inner, baseLevel + '.' + (i + 1)); - }); - - return part.merge({ - level: baseLevel, - articles: articles - }); -} - -module.exports = indexPartLevels; diff --git a/lib/modifiers/summary/insertArticle.js b/lib/modifiers/summary/insertArticle.js deleted file mode 100644 index 3a084b3..0000000 --- a/lib/modifiers/summary/insertArticle.js +++ /dev/null @@ -1,49 +0,0 @@ -var is = require('is'); -var SummaryArticle = require('../../models/summaryArticle'); -var mergeAtLevel = require('./mergeAtLevel'); -var indexArticleLevels = require('./indexArticleLevels'); - -/** - Returns a new Summary with the article at the given level, with - subsequent article shifted. - - @param {Summary} summary - @param {Article} article - @param {String|Article} level: level to insert at - @return {Summary} -*/ -function insertArticle(summary, article, level) { - article = SummaryArticle(article); - level = is.string(level)? level : level.getLevel(); - - var parent = summary.getParent(level); - if (!parent) { - return summary; - } - - // Find the index to insert at - var articles = parent.getArticles(); - var index = getLeafIndex(level); - - // Insert the article at the right index - articles = articles.insert(index, article); - - // Reindex the level from here - parent = parent.set('articles', articles); - parent = indexArticleLevels(parent); - - return mergeAtLevel(summary, parent.getLevel(), parent); -} - -/** - @param {String} - @return {Number} The index of this level within its parent's children - */ -function getLeafIndex(level) { - var arr = level.split('.').map(function (char) { - return parseInt(char, 10); - }); - return arr[arr.length - 1] - 1; -} - -module.exports = insertArticle; diff --git a/lib/modifiers/summary/insertPart.js b/lib/modifiers/summary/insertPart.js deleted file mode 100644 index 199cba7..0000000 --- a/lib/modifiers/summary/insertPart.js +++ /dev/null @@ -1,19 +0,0 @@ -var SummaryPart = require('../../models/summaryPart'); -var indexLevels = require('./indexLevels'); - -/** - Returns a new Summary with a part inserted at given index - - @param {Summary} summary - @param {Part} part - @param {Number} index - @return {Summary} -*/ -function insertPart(summary, part, index) { - part = SummaryPart(part); - - var parts = summary.getParts().insert(index, part); - return indexLevels(summary.set('parts', parts)); -} - -module.exports = insertPart; diff --git a/lib/modifiers/summary/mergeAtLevel.js b/lib/modifiers/summary/mergeAtLevel.js deleted file mode 100644 index 9a95ffc..0000000 --- a/lib/modifiers/summary/mergeAtLevel.js +++ /dev/null @@ -1,75 +0,0 @@ - -/** - Edit a list of articles - - @param {List<Article>} articles - @param {String} level - @param {Article} newArticle - @return {List<Article>} -*/ -function editArticleInList(articles, level, newArticle) { - return articles.map(function(article) { - var articleLevel = article.getLevel(); - - if (articleLevel === level) { - // it is the article to edit - return article.merge(newArticle); - } else if (level.indexOf(articleLevel) === 0) { - // it is a parent - var articles = editArticleInList(article.getArticles(), level, newArticle); - return article.set('articles', articles); - } else { - // This is not the article you are looking for - return article; - } - }); -} - - -/** - Edit an article in a part - - @param {Part} part - @param {String} level - @param {Article} newArticle - @return {Part} -*/ -function editArticleInPart(part, level, newArticle) { - var articles = part.getArticles(); - articles = editArticleInList(articles, level, newArticle); - - return part.set('articles', articles); -} - - -/** - Edit an article, or a part, in a summary. Does a shallow merge. - - @param {Summary} summary - @param {String} level - @param {Article|Part} newValue - @return {Summary} -*/ -function mergeAtLevel(summary, level, newValue) { - var levelParts = level.split('.'); - var partIndex = Number(levelParts[0]) -1; - - var parts = summary.getParts(); - var part = parts.get(partIndex); - if (!part) { - return summary; - } - - var isEditingPart = levelParts.length < 2; - if (isEditingPart) { - part = part.merge(newValue); - } else { - part = editArticleInPart(part, level, newValue); - } - - parts = parts.set(partIndex, part); - return summary.set('parts', parts); -} - - -module.exports = mergeAtLevel; diff --git a/lib/modifiers/summary/moveArticle.js b/lib/modifiers/summary/moveArticle.js deleted file mode 100644 index 5cb1868..0000000 --- a/lib/modifiers/summary/moveArticle.js +++ /dev/null @@ -1,25 +0,0 @@ -var is = require('is'); -var removeArticle = require('./removeArticle'); -var insertArticle = require('./insertArticle'); - -/** - Returns a new summary, with the given article removed from its - origin level, and placed at the given target level. - - @param {Summary} summary - @param {String|SummaryArticle} origin: level to remove - @param {String|SummaryArticle} target: the level where the article will be found - @return {Summary} -*/ -function moveArticle(summary, origin, target) { - // Coerce to level - var originLevel = is.string(origin)? origin : origin.getLevel(); - var targetLevel = is.string(target)? target : target.getLevel(); - var article = summary.getByLevel(originLevel); - - // Remove first - var removed = removeArticle(summary, originLevel); - return insertArticle(removed, article, targetLevel); -} - -module.exports = moveArticle; diff --git a/lib/modifiers/summary/moveArticleAfter.js b/lib/modifiers/summary/moveArticleAfter.js deleted file mode 100644 index e268f73..0000000 --- a/lib/modifiers/summary/moveArticleAfter.js +++ /dev/null @@ -1,60 +0,0 @@ -var is = require('is'); -var removeArticle = require('./removeArticle'); -var insertArticle = require('./insertArticle'); - -/** - Returns a new summary, with the an article moved after another - article. Unlike `moveArticle`, does not ensure that the article - will be found at the target's level plus one. - - @param {Summary} summary - @param {String|SummaryArticle} origin - @param {String|SummaryArticle} afterTarget - @return {Summary} -*/ -function moveArticleAfter(summary, origin, afterTarget) { - // Coerce to level - var originLevel = is.string(origin)? origin : origin.getLevel(); - var afterTargetLevel = is.string(afterTarget)? afterTarget : afterTarget.getLevel(); - var article = summary.getByLevel(originLevel); - - var targetLevel = increment(afterTargetLevel); - - if (targetLevel < origin) { - // Remove first - var removed = removeArticle(summary, originLevel); - // Insert then - return insertArticle(removed, article, targetLevel); - } else { - // Insert right after first - var inserted = insertArticle(summary, article, targetLevel); - // Remove old one - return removeArticle(inserted, originLevel); - } -} - -/** - @param {String} - @return {Array<Number>} - */ -function levelToArray(l) { - return l.split('.').map(function (char) { - return parseInt(char, 10); - }); -} - -/** - @param {Array<Number>} - @return {String} - */ -function arrayToLevel(a) { - return a.join('.'); -} - -function increment(level) { - level = levelToArray(level); - level[level.length - 1]++; - return arrayToLevel(level); -} - -module.exports = moveArticleAfter; diff --git a/lib/modifiers/summary/removeArticle.js b/lib/modifiers/summary/removeArticle.js deleted file mode 100644 index 8a30d0a..0000000 --- a/lib/modifiers/summary/removeArticle.js +++ /dev/null @@ -1,37 +0,0 @@ -var is = require('is'); -var mergeAtLevel = require('./mergeAtLevel'); -var indexArticleLevels = require('./indexArticleLevels'); - -/** - Remove an article from a level. - - @param {Summary} summary - @param {String|SummaryArticle} level: level to remove - @return {Summary} -*/ -function removeArticle(summary, level) { - // Coerce to level - level = is.string(level)? level : level.getLevel(); - - var parent = summary.getParent(level); - - var articles = parent.getArticles(); - // Find the index to remove - var index = articles.findIndex(function(art) { - return art.getLevel() === level; - }); - if (index === -1) { - return summary; - } - - // Remove from children - articles = articles.remove(index); - parent = parent.set('articles', articles); - - // Reindex the level from here - parent = indexArticleLevels(parent); - - return mergeAtLevel(summary, parent.getLevel(), parent); -} - -module.exports = removeArticle; diff --git a/lib/modifiers/summary/removePart.js b/lib/modifiers/summary/removePart.js deleted file mode 100644 index 2f8affc..0000000 --- a/lib/modifiers/summary/removePart.js +++ /dev/null @@ -1,15 +0,0 @@ -var indexLevels = require('./indexLevels'); - -/** - Remove a part at given index - - @param {Summary} summary - @param {Number|} index - @return {Summary} -*/ -function removePart(summary, index) { - var parts = summary.getParts().remove(index); - return indexLevels(summary.set('parts', parts)); -} - -module.exports = removePart; diff --git a/lib/modifiers/summary/unshiftArticle.js b/lib/modifiers/summary/unshiftArticle.js deleted file mode 100644 index d1ebc05..0000000 --- a/lib/modifiers/summary/unshiftArticle.js +++ /dev/null @@ -1,29 +0,0 @@ -var SummaryArticle = require('../../models/summaryArticle'); -var SummaryPart = require('../../models/summaryPart'); - -var indexLevels = require('./indexLevels'); - -/** - Insert an article at the beginning of summary - - @param {Summary} summary - @param {Article} article - @return {Summary} -*/ -function unshiftArticle(summary, article) { - article = SummaryArticle(article); - - var parts = summary.getParts(); - var part = parts.get(0) || SummaryPart(); - - var articles = part.getArticles(); - articles = articles.unshift(article); - part = part.set('articles', articles); - - parts = parts.set(0, part); - summary = summary.set('parts', parts); - - return indexLevels(summary); -} - -module.exports = unshiftArticle; diff --git a/lib/output/__tests__/createMock.js b/lib/output/__tests__/createMock.js deleted file mode 100644 index f21c544..0000000 --- a/lib/output/__tests__/createMock.js +++ /dev/null @@ -1,38 +0,0 @@ -var Immutable = require('immutable'); - -var Output = require('../../models/output'); -var Book = require('../../models/book'); -var parseBook = require('../../parse/parseBook'); -var createMockFS = require('../../fs/mock'); -var preparePlugins = require('../preparePlugins'); - -/** - * Create an output using a generator - * - * FOR TESTING PURPOSE ONLY - * - * @param {Generator} generator - * @param {Map<String:String|Map>} files - * @return {Promise<Output>} - */ -function createMockOutput(generator, files, options) { - var fs = createMockFS(files); - var book = Book.createForFS(fs); - var state = generator.State? generator.State({}) : Immutable.Map(); - - book = book.setLogLevel('disabled'); - options = generator.Options(options); - - return parseBook(book) - .then(function(resultBook) { - return new Output({ - book: resultBook, - options: options, - state: state, - generator: generator.name - }); - }) - .then(preparePlugins); -} - -module.exports = createMockOutput; diff --git a/lib/output/__tests__/ebook.js b/lib/output/__tests__/ebook.js deleted file mode 100644 index 9266e9f..0000000 --- a/lib/output/__tests__/ebook.js +++ /dev/null @@ -1,16 +0,0 @@ -var generateMock = require('./generateMock'); -var EbookGenerator = require('../ebook'); - -describe('EbookGenerator', function() { - - it('should generate a SUMMARY.html', function() { - return generateMock(EbookGenerator, { - 'README.md': 'Hello World' - }) - .then(function(folder) { - expect(folder).toHaveFile('SUMMARY.html'); - expect(folder).toHaveFile('index.html'); - }); - }); -}); - diff --git a/lib/output/__tests__/generateMock.js b/lib/output/__tests__/generateMock.js deleted file mode 100644 index 691ee2d..0000000 --- a/lib/output/__tests__/generateMock.js +++ /dev/null @@ -1,40 +0,0 @@ -var tmp = require('tmp'); - -var Book = require('../../models/book'); -var createMockFS = require('../../fs/mock'); -var parseBook = require('../../parse/parseBook'); -var generateBook = require('../generateBook'); - -/** - * Generate a book using a generator - * And returns the path to the output dir. - * - * FOR TESTING PURPOSE ONLY - * - * @param {Generator} - * @param {Map<String:String|Map>} files - * @return {Promise<String>} - */ -function generateMock(Generator, files) { - var fs = createMockFS(files); - var book = Book.createForFS(fs); - var dir; - - try { - dir = tmp.dirSync(); - } catch(err) { - throw err; - } - - book = book.setLogLevel('disabled'); - - return parseBook(book) - .then(function(resultBook) { - return generateBook(Generator, resultBook, { - root: dir.name - }); - }) - .thenResolve(dir.name); -} - -module.exports = generateMock; diff --git a/lib/output/__tests__/json.js b/lib/output/__tests__/json.js deleted file mode 100644 index 12ab567..0000000 --- a/lib/output/__tests__/json.js +++ /dev/null @@ -1,46 +0,0 @@ -var generateMock = require('./generateMock'); -var JSONGenerator = require('../json'); - -describe('JSONGenerator', function() { - - it('should generate a README.json', function() { - return generateMock(JSONGenerator, { - 'README.md': 'Hello World' - }) - .then(function(folder) { - expect(folder).toHaveFile('README.json'); - }); - }); - - it('should generate a json file for each articles', function() { - return generateMock(JSONGenerator, { - 'README.md': 'Hello World', - 'SUMMARY.md': '# Summary\n\n* [Page](test/page.md)', - 'test': { - 'page.md': 'Hello 2' - } - }) - .then(function(folder) { - expect(folder).toHaveFile('README.json'); - expect(folder).toHaveFile('test/page.json'); - }); - }); - - it('should generate a multilingual book', function() { - return generateMock(JSONGenerator, { - 'LANGS.md': '# Languages\n\n* [en](en)\n* [fr](fr)', - 'en': { - 'README.md': 'Hello' - }, - 'fr': { - 'README.md': 'Bonjour' - } - }) - .then(function(folder) { - expect(folder).toHaveFile('en/README.json'); - expect(folder).toHaveFile('fr/README.json'); - expect(folder).toHaveFile('README.json'); - }); - }); -}); - diff --git a/lib/output/__tests__/website.js b/lib/output/__tests__/website.js deleted file mode 100644 index 1f8c3c0..0000000 --- a/lib/output/__tests__/website.js +++ /dev/null @@ -1,144 +0,0 @@ -var fs = require('fs'); -var generateMock = require('./generateMock'); -var WebsiteGenerator = require('../website'); - -describe('WebsiteGenerator', function() { - - it('should generate an index.html', function() { - return generateMock(WebsiteGenerator, { - 'README.md': 'Hello World' - }) - .then(function(folder) { - expect(folder).toHaveFile('index.html'); - }); - }); - - describe('Glossary', function() { - var folder; - - before(function() { - return generateMock(WebsiteGenerator, { - 'README.md': 'Hello World', - 'SUMMARY.md': '* [Deep](folder/page.md)', - 'folder': { - 'page.md': 'Hello World' - }, - 'GLOSSARY.md': '# Glossary\n\n## Hello\n\nHello World' - }) - .then(function(_folder) { - folder = _folder; - }); - }); - - it('should generate a GLOSSARY.html', function() { - expect(folder).toHaveFile('GLOSSARY.html'); - }); - - it('should correctly resolve glossary links in README', function() { - var html = fs.readFileSync(folder + '/index.html', 'utf8'); - expect(html).toHaveDOMElement('.page-inner a[href="GLOSSARY.html#hello"]'); - }); - - it('should correctly resolve glossary links in directory', function() { - var html = fs.readFileSync(folder + '/folder/page.html', 'utf8'); - expect(html).toHaveDOMElement('.page-inner a[href="../GLOSSARY.html#hello"]'); - }); - - it('should accept a custom glossary file', function() { - return generateMock(WebsiteGenerator, { - 'README.md': 'Hello World', - 'book.json': '{ "structure": { "glossary": "custom.md" } }', - 'custom.md': '# Glossary\n\n## Hello\n\nHello World' - }) - .then(function(folder) { - expect(folder).toHaveFile('custom.html'); - expect(folder).toNotHaveFile('GLOSSARY.html'); - - var html = fs.readFileSync(folder + '/index.html', 'utf8'); - expect(html).toHaveDOMElement('.page-inner a[href="custom.html#hello"]'); - }); - }); - }); - - - it('should copy asset files', function() { - return generateMock(WebsiteGenerator, { - 'README.md': 'Hello World', - 'myJsFile.js': 'var a = "test";', - 'folder': { - 'AnotherAssetFile.md': '# Even md' - } - }) - .then(function(folder) { - expect(folder).toHaveFile('index.html'); - expect(folder).toHaveFile('myJsFile.js'); - expect(folder).toHaveFile('folder/AnotherAssetFile.md'); - }); - }); - - it('should generate an index.html for AsciiDoc', function() { - return generateMock(WebsiteGenerator, { - 'README.adoc': 'Hello World' - }) - .then(function(folder) { - expect(folder).toHaveFile('index.html'); - }); - }); - - it('should generate an HTML file for each articles', function() { - return generateMock(WebsiteGenerator, { - 'README.md': 'Hello World', - 'SUMMARY.md': '# Summary\n\n* [Page](test/page.md)', - 'test': { - 'page.md': 'Hello 2' - } - }) - .then(function(folder) { - expect(folder).toHaveFile('index.html'); - expect(folder).toHaveFile('test/page.html'); - }); - }); - - it('should not generate file if entry file doesn\'t exist', function() { - return generateMock(WebsiteGenerator, { - 'README.md': 'Hello World', - 'SUMMARY.md': '# Summary\n\n* [Page 1](page.md)\n* [Page 2](test/page.md)', - 'test': { - 'page.md': 'Hello 2' - } - }) - .then(function(folder) { - expect(folder).toHaveFile('index.html'); - expect(folder).toNotHaveFile('page.html'); - expect(folder).toHaveFile('test/page.html'); - }); - }); - - it('should generate a multilingual book', function() { - return generateMock(WebsiteGenerator, { - 'LANGS.md': '# Languages\n\n* [en](en)\n* [fr](fr)', - 'en': { - 'README.md': 'Hello' - }, - 'fr': { - 'README.md': 'Bonjour' - } - }) - .then(function(folder) { - // It should generate languages - expect(folder).toHaveFile('en/index.html'); - expect(folder).toHaveFile('fr/index.html'); - - // Should not copy languages as assets - expect(folder).toNotHaveFile('en/README.md'); - expect(folder).toNotHaveFile('fr/README.md'); - - // Should copy assets only once - expect(folder).toHaveFile('gitbook/style.css'); - expect(folder).toNotHaveFile('en/gitbook/style.css'); - - expect(folder).toHaveFile('index.html'); - }); - }); -}); - diff --git a/lib/output/callHook.js b/lib/output/callHook.js deleted file mode 100644 index 4914e52..0000000 --- a/lib/output/callHook.js +++ /dev/null @@ -1,60 +0,0 @@ -var Promise = require('../utils/promise'); -var timing = require('../utils/timing'); -var Api = require('../api'); - -function defaultGetArgument() { - return undefined; -} - -function defaultHandleResult(output, result) { - return output; -} - -/** - Call a "global" hook for an output - - @param {String} name - @param {Function(Output) -> Mixed} getArgument - @param {Function(Output, result) -> Output} handleResult - @param {Output} output - @return {Promise<Output>} -*/ -function callHook(name, getArgument, handleResult, output) { - getArgument = getArgument || defaultGetArgument; - handleResult = handleResult || defaultHandleResult; - - var logger = output.getLogger(); - var plugins = output.getPlugins(); - - logger.debug.ln('calling hook "' + name + '"'); - - // Create the JS context for plugins - var context = Api.encodeGlobal(output); - - return timing.measure( - 'call.hook.' + name, - - // Get the arguments - Promise(getArgument(output)) - - // Call the hooks in serie - .then(function(arg) { - return Promise.reduce(plugins, function(prev, plugin) { - var hook = plugin.getHook(name); - if (!hook) { - return prev; - } - - return hook.call(context, prev); - }, arg); - }) - - // Handle final result - .then(function(result) { - output = Api.decodeGlobal(output, context); - return handleResult(output, result); - }) - ); -} - -module.exports = callHook; diff --git a/lib/output/callPageHook.js b/lib/output/callPageHook.js deleted file mode 100644 index c66cef0..0000000 --- a/lib/output/callPageHook.js +++ /dev/null @@ -1,28 +0,0 @@ -var Api = require('../api'); -var callHook = require('./callHook'); - -/** - Call a hook for a specific page - - @param {String} name - @param {Output} output - @param {Page} page - @return {Promise<Page>} -*/ -function callPageHook(name, output, page) { - return callHook( - name, - - function(out) { - return Api.encodePage(out, page); - }, - - function(out, result) { - return Api.decodePage(out, page, result); - }, - - output - ); -} - -module.exports = callPageHook; diff --git a/lib/output/createTemplateEngine.js b/lib/output/createTemplateEngine.js deleted file mode 100644 index 8cf320e..0000000 --- a/lib/output/createTemplateEngine.js +++ /dev/null @@ -1,45 +0,0 @@ -var Templating = require('../templating'); -var TemplateEngine = require('../models/templateEngine'); - -var Api = require('../api'); -var Plugins = require('../plugins'); - -var defaultBlocks = require('../constants/defaultBlocks'); -var defaultFilters = require('../constants/defaultFilters'); - -/** - Create template engine for an output. - It adds default filters/blocks, then add the ones from plugins - - @param {Output} output - @return {TemplateEngine} -*/ -function createTemplateEngine(output) { - var plugins = output.getPlugins(); - var book = output.getBook(); - var rootFolder = book.getContentRoot(); - var logger = book.getLogger(); - - var filters = Plugins.listFilters(plugins); - var blocks = Plugins.listBlocks(plugins); - - // Extend with default - blocks = defaultBlocks.merge(blocks); - filters = defaultFilters.merge(filters); - - // Create loader - var transformFn = Templating.replaceShortcuts.bind(null, blocks); - var loader = new Templating.ConrefsLoader(rootFolder, transformFn, logger); - - // Create API context - var context = Api.encodeGlobal(output); - - return new TemplateEngine({ - filters: filters, - blocks: blocks, - loader: loader, - context: context - }); -} - -module.exports = createTemplateEngine; diff --git a/lib/output/ebook/getConvertOptions.js b/lib/output/ebook/getConvertOptions.js deleted file mode 100644 index bc80493..0000000 --- a/lib/output/ebook/getConvertOptions.js +++ /dev/null @@ -1,73 +0,0 @@ -var extend = require('extend'); - -var Promise = require('../../utils/promise'); -var getPDFTemplate = require('./getPDFTemplate'); -var getCoverPath = require('./getCoverPath'); - -/** - Generate options for ebook-convert - - @param {Output} - @return {Promise<Object>} -*/ -function getConvertOptions(output) { - var options = output.getOptions(); - var format = options.get('format'); - - var book = output.getBook(); - var config = book.getConfig(); - - return Promise() - .then(function() { - var coverPath = getCoverPath(output); - var options = { - '--cover': coverPath, - '--title': config.getValue('title'), - '--comments': config.getValue('description'), - '--isbn': config.getValue('isbn'), - '--authors': config.getValue('author'), - '--language': book.getLanguage() || config.getValue('language'), - '--book-producer': 'GitBook', - '--publisher': 'GitBook', - '--chapter': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter \')]', - '--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 \')]', - '--max-levels': '1', - '--no-chapters-in-toc': true, - '--breadth-first': true, - '--dont-split-on-page-breaks': format === 'epub'? true : undefined - }; - - if (format !== 'pdf') { - return options; - } - - return Promise.all([ - getPDFTemplate(output, 'header'), - getPDFTemplate(output, 'footer') - ]) - .spread(function(headerTpl, footerTpl) { - var pdfOptions = config.getValue('pdf').toJS(); - - return options = extend(options, { - '--chapter-mark': String(pdfOptions.chapterMark), - '--page-breaks-before': String(pdfOptions.pageBreaksBefore), - '--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-sans-family': String(pdfOptions.fontFamily), - '--pdf-header-template': headerTpl, - '--pdf-footer-template': footerTpl - }); - }); - }); -} - - -module.exports = getConvertOptions; diff --git a/lib/output/ebook/getCoverPath.js b/lib/output/ebook/getCoverPath.js deleted file mode 100644 index ab6b579..0000000 --- a/lib/output/ebook/getCoverPath.js +++ /dev/null @@ -1,30 +0,0 @@ -var path = require('path'); -var fs = require('../../utils/fs'); - -/** - Resolve path to cover file to use - - @param {Output} - @return {String} -*/ -function getCoverPath(output) { - var outputRoot = output.getRoot(); - var book = output.getBook(); - var config = book.getConfig(); - var coverName = config.getValue('cover', 'cover.jpg'); - - // Resolve to absolute - var cover = fs.pickFile(outputRoot, coverName); - if (cover) { - return cover; - } - - // Multilingual? try parent folder - if (book.isLanguageBook()) { - cover = fs.pickFile(path.join(outputRoot, '..'), coverName); - } - - return cover; -} - -module.exports = getCoverPath; diff --git a/lib/output/ebook/getPDFTemplate.js b/lib/output/ebook/getPDFTemplate.js deleted file mode 100644 index b767daf..0000000 --- a/lib/output/ebook/getPDFTemplate.js +++ /dev/null @@ -1,41 +0,0 @@ -var juice = require('juice'); - -var WebsiteGenerator = require('../website'); -var JSONUtils = require('../../json'); -var Templating = require('../../templating'); -var Promise = require('../../utils/promise'); - - -/** - Generate PDF header/footer templates - - @param {Output} output - @param {String} type - @return {String} -*/ -function getPDFTemplate(output, type) { - var filePath = 'pdf_' + type + '.html'; - var outputRoot = output.getRoot(); - var engine = WebsiteGenerator.createTemplateEngine(output, filePath); - - // Generate context - var context = JSONUtils.encodeOutput(output); - context.page = { - num: '_PAGENUM_', - title: '_SECTION_' - }; - - // Render the theme - return Templating.renderFile(engine, 'ebook/' + filePath, context) - - // Inline css and assets - .then(function(tplOut) { - return Promise.nfcall(juice.juiceResources, tplOut.getContent(), { - webResources: { - relativeTo: outputRoot - } - }); - }); -} - -module.exports = getPDFTemplate; diff --git a/lib/output/ebook/index.js b/lib/output/ebook/index.js deleted file mode 100644 index 786a10a..0000000 --- a/lib/output/ebook/index.js +++ /dev/null @@ -1,9 +0,0 @@ -var extend = require('extend'); -var WebsiteGenerator = require('../website'); - -module.exports = extend({}, WebsiteGenerator, { - name: 'ebook', - Options: require('./options'), - onPage: require('./onPage'), - onFinish: require('./onFinish') -}); diff --git a/lib/output/ebook/onFinish.js b/lib/output/ebook/onFinish.js deleted file mode 100644 index 7f21548..0000000 --- a/lib/output/ebook/onFinish.js +++ /dev/null @@ -1,91 +0,0 @@ -var path = require('path'); - -var WebsiteGenerator = require('../website'); -var JSONUtils = require('../../json'); -var Templating = require('../../templating'); -var Promise = require('../../utils/promise'); -var error = require('../../utils/error'); -var command = require('../../utils/command'); -var writeFile = require('../helper/writeFile'); - -var getConvertOptions = require('./getConvertOptions'); -var SUMMARY_FILE = 'SUMMARY.html'; - -/** - Write the SUMMARY.html - - @param {Output} - @return {Output} -*/ -function writeSummary(output) { - var options = output.getOptions(); - var prefix = options.get('prefix'); - - var filePath = SUMMARY_FILE; - var engine = WebsiteGenerator.createTemplateEngine(output, filePath); - var context = JSONUtils.encodeOutput(output); - - // Render the theme - return Templating.renderFile(engine, prefix + '/summary.html', context) - - // Write it to the disk - .then(function(tplOut) { - return writeFile(output, filePath, tplOut.getContent()); - }); -} - -/** - Generate the ebook file as "index.pdf" - - @param {Output} - @return {Output} -*/ -function runEbookConvert(output) { - var logger = output.getLogger(); - var options = output.getOptions(); - var format = options.get('format'); - var outputFolder = output.getRoot(); - - if (!format) { - return Promise(output); - } - - return getConvertOptions(output) - .then(function(options) { - var cmd = [ - 'ebook-convert', - path.resolve(outputFolder, SUMMARY_FILE), - path.resolve(outputFolder, 'index.' + format), - command.optionsToShellArgs(options) - ].join(' '); - - return command.exec(cmd) - .progress(function(data) { - logger.debug(data); - }) - .fail(function(err) { - if (err.code == 127) { - throw error.RequireInstallError({ - cmd: 'ebook-convert', - install: 'Install it from Calibre: https://calibre-ebook.com' - }); - } - - throw error.EbookError(err); - }); - }) - .thenResolve(output); -} - -/** - Finish the generation, generates the SUMMARY.html - - @param {Output} - @return {Output} -*/ -function onFinish(output) { - return writeSummary(output) - .then(runEbookConvert); -} - -module.exports = onFinish; diff --git a/lib/output/ebook/onPage.js b/lib/output/ebook/onPage.js deleted file mode 100644 index b7b9b42..0000000 --- a/lib/output/ebook/onPage.js +++ /dev/null @@ -1,24 +0,0 @@ -var WebsiteGenerator = require('../website'); -var Modifiers = require('../modifiers'); - -/** - Write a page for ebook output - - @param {Output} output - @param {Output} -*/ -function onPage(output, page) { - var options = output.getOptions(); - - // Inline assets - return Modifiers.modifyHTML(page, [ - Modifiers.inlineAssets(options.get('root'), page.getFile().getPath()) - ]) - - // Write page using website generator - .then(function(resultPage) { - return WebsiteGenerator.onPage(output, resultPage); - }); -} - -module.exports = onPage; diff --git a/lib/output/ebook/options.js b/lib/output/ebook/options.js deleted file mode 100644 index ea7b8b4..0000000 --- a/lib/output/ebook/options.js +++ /dev/null @@ -1,17 +0,0 @@ -var Immutable = require('immutable'); - -var Options = Immutable.Record({ - // Root folder for the output - root: String(), - - // Prefix for generation - prefix: String('ebook'), - - // Format to generate using ebook-convert - format: String(), - - // Force use of absolute urls ("index.html" instead of "/") - directoryIndex: Boolean(false) -}); - -module.exports = Options; diff --git a/lib/output/generateAssets.js b/lib/output/generateAssets.js deleted file mode 100644 index 7a6e104..0000000 --- a/lib/output/generateAssets.js +++ /dev/null @@ -1,26 +0,0 @@ -var Promise = require('../utils/promise'); - -/** - Output all assets using a generator - - @param {Generator} generator - @param {Output} output - @return {Promise<Output>} -*/ -function generateAssets(generator, output) { - var assets = output.getAssets(); - var logger = output.getLogger(); - - // Is generator ignoring assets? - if (!generator.onAsset) { - return Promise(output); - } - - return Promise.reduce(assets, function(out, assetFile) { - logger.debug.ln('copy asset "' + assetFile + '"'); - - return generator.onAsset(out, assetFile); - }, output); -} - -module.exports = generateAssets; diff --git a/lib/output/generateBook.js b/lib/output/generateBook.js deleted file mode 100644 index 46712bd..0000000 --- a/lib/output/generateBook.js +++ /dev/null @@ -1,193 +0,0 @@ -var path = require('path'); -var Immutable = require('immutable'); - -var Output = require('../models/output'); -var Promise = require('../utils/promise'); -var fs = require('../utils/fs'); - -var callHook = require('./callHook'); -var preparePlugins = require('./preparePlugins'); -var preparePages = require('./preparePages'); -var prepareAssets = require('./prepareAssets'); -var generateAssets = require('./generateAssets'); -var generatePages = require('./generatePages'); - -/** - * Process an output to generate the book - * - * @param {Generator} generator - * @param {Output} output - * @return {Promise<Output>} - */ -function processOutput(generator, startOutput) { - return Promise(startOutput) - .then(preparePlugins) - .then(preparePages) - .then(prepareAssets) - - .then( - callHook.bind(null, - 'config', - function(output) { - var book = output.getBook(); - var config = book.getConfig(); - var values = config.getValues(); - - return values.toJS(); - }, - function(output, result) { - var book = output.getBook(); - var config = book.getConfig(); - - config = config.updateValues(result); - book = book.set('config', config); - return output.set('book', book); - } - ) - ) - - .then( - callHook.bind(null, - 'init', - function(output) { - return {}; - }, - function(output) { - return output; - } - ) - ) - - .then(function(output) { - if (!generator.onInit) { - return output; - } - - return generator.onInit(output); - }) - - .then(generateAssets.bind(null, generator)) - .then(generatePages.bind(null, generator)) - - .tap(function(output) { - var book = output.getBook(); - - if (!book.isMultilingual()) { - return; - } - - var logger = book.getLogger(); - var books = book.getBooks(); - var outputRoot = output.getRoot(); - var plugins = output.getPlugins(); - var state = output.getState(); - var options = output.getOptions(); - - return Promise.forEach(books, function(langBook) { - // Inherits plugins list, options and state - var langOptions = options.set('root', path.join(outputRoot, langBook.getLanguage())); - var langOutput = new Output({ - book: langBook, - options: langOptions, - state: state, - generator: generator.name, - plugins: plugins - }); - - logger.info.ln(''); - logger.info.ln('generating language "' + langBook.getLanguage() + '"'); - return processOutput(generator, langOutput); - }); - }) - - .then(callHook.bind(null, - 'finish:before', - function(output) { - return {}; - }, - function(output) { - return output; - } - ) - ) - - .then(function(output) { - if (!generator.onFinish) { - return output; - } - - return generator.onFinish(output); - }) - - .then(callHook.bind(null, - 'finish', - function(output) { - return {}; - }, - function(output) { - return output; - } - ) - ); -} - -/** - * Generate a book using a generator. - * - * The overall process is: - * 1. List and load plugins for this book - * 2. Call hook "config" - * 3. Call hook "init" - * 4. Initialize generator - * 5. List all assets and pages - * 6. Copy all assets to output - * 7. Generate all pages - * 8. Call hook "finish:before" - * 9. Finish generation - * 10. Call hook "finish" - * - * - * @param {Generator} generator - * @param {Book} book - * @param {Object} options - * @return {Promise<Output>} - */ -function generateBook(generator, book, options) { - options = generator.Options(options); - var state = generator.State? generator.State({}) : Immutable.Map(); - var start = Date.now(); - - return Promise( - new Output({ - book: book, - options: options, - state: state, - generator: generator.name - }) - ) - - // Cleanup output folder - .then(function(output) { - var logger = output.getLogger(); - var rootFolder = output.getRoot(); - - logger.debug.ln('cleanup folder "' + rootFolder + '"'); - return fs.ensureFolder(rootFolder) - .thenResolve(output); - }) - - .then(processOutput.bind(null, generator)) - - // Log duration and end message - .then(function(output) { - var logger = output.getLogger(); - var end = Date.now(); - var duration = (end - start)/1000; - - logger.info.ok('generation finished with success in ' + duration.toFixed(1) + 's !'); - - return output; - }); -} - -module.exports = generateBook; diff --git a/lib/output/generatePage.js b/lib/output/generatePage.js deleted file mode 100644 index 090a870..0000000 --- a/lib/output/generatePage.js +++ /dev/null @@ -1,79 +0,0 @@ -var path = require('path'); - -var Promise = require('../utils/promise'); -var error = require('../utils/error'); -var timing = require('../utils/timing'); - -var Templating = require('../templating'); -var JSONUtils = require('../json'); -var createTemplateEngine = require('./createTemplateEngine'); -var callPageHook = require('./callPageHook'); - -/** - * Prepare and generate HTML for a page - * - * @param {Output} output - * @param {Page} page - * @return {Promise<Page>} - */ -function generatePage(output, page) { - var book = output.getBook(); - var engine = createTemplateEngine(output); - - return timing.measure( - 'page.generate', - Promise(page) - .then(function(resultPage) { - var file = resultPage.getFile(); - var filePath = file.getPath(); - var parser = file.getParser(); - var context = JSONUtils.encodeOutputWithPage(output, resultPage); - - if (!parser) { - return Promise.reject(error.FileNotParsableError({ - filename: filePath - })); - } - - // Call hook "page:before" - return callPageHook('page:before', output, resultPage) - - // Escape code blocks with raw tags - .then(function(currentPage) { - return parser.preparePage(currentPage.getContent()); - }) - - // Render templating syntax - .then(function(content) { - var absoluteFilePath = path.join(book.getContentRoot(), filePath); - return Templating.render(engine, absoluteFilePath, content, context); - }) - - .then(function(output) { - var content = output.getContent(); - - return parser.parsePage(content) - .then(function(result) { - return output.setContent(result.content); - }); - }) - - // Post processing for templating syntax - .then(function(output) { - return Templating.postRender(engine, output); - }) - - // Return new page - .then(function(content) { - return resultPage.set('content', content); - }) - - // Call final hook - .then(function(currentPage) { - return callPageHook('page', output, currentPage); - }); - }) - ); -} - -module.exports = generatePage; diff --git a/lib/output/generatePages.js b/lib/output/generatePages.js deleted file mode 100644 index 73c5c09..0000000 --- a/lib/output/generatePages.js +++ /dev/null @@ -1,36 +0,0 @@ -var Promise = require('../utils/promise'); -var generatePage = require('./generatePage'); - -/** - Output all pages using a generator - - @param {Generator} generator - @param {Output} output - @return {Promise<Output>} -*/ -function generatePages(generator, output) { - var pages = output.getPages(); - var logger = output.getLogger(); - - // Is generator ignoring assets? - if (!generator.onPage) { - return Promise(output); - } - - return Promise.reduce(pages, function(out, page) { - var file = page.getFile(); - - logger.debug.ln('generate page "' + file.getPath() + '"'); - - return generatePage(out, page) - .then(function(resultPage) { - return generator.onPage(out, resultPage); - }) - .fail(function(err) { - logger.error.ln('error while generating page "' + file.getPath() + '":'); - throw err; - }); - }, output); -} - -module.exports = generatePages; diff --git a/lib/output/getModifiers.js b/lib/output/getModifiers.js deleted file mode 100644 index bb44e80..0000000 --- a/lib/output/getModifiers.js +++ /dev/null @@ -1,73 +0,0 @@ -var Modifiers = require('./modifiers'); -var resolveFileToURL = require('./helper/resolveFileToURL'); -var Api = require('../api'); -var Plugins = require('../plugins'); -var Promise = require('../utils/promise'); -var defaultBlocks = require('../constants/defaultBlocks'); -var fileToOutput = require('./helper/fileToOutput'); - -var CODEBLOCK = 'code'; - -/** - * Return default modifier to prepare a page for - * rendering. - * - * @return {Array<Modifier>} - */ -function getModifiers(output, page) { - var book = output.getBook(); - var plugins = output.getPlugins(); - var glossary = book.getGlossary(); - var file = page.getFile(); - - // Glossary entries - var entries = glossary.getEntries(); - var glossaryFile = glossary.getFile(); - var glossaryFilename = fileToOutput(output, glossaryFile.getPath()); - - // Current file path - var currentFilePath = file.getPath(); - - // Get TemplateBlock for highlighting - var blocks = Plugins.listBlocks(plugins); - var code = blocks.get(CODEBLOCK) || defaultBlocks.get(CODEBLOCK); - - // Current context - var context = Api.encodeGlobal(output); - - return [ - // Normalize IDs on headings - Modifiers.addHeadingId, - - // Annotate text with glossary entries - Modifiers.annotateText.bind(null, entries, glossaryFilename), - - // Resolve images - Modifiers.resolveImages.bind(null, currentFilePath), - - // Resolve links (.md -> .html) - Modifiers.resolveLinks.bind(null, - currentFilePath, - resolveFileToURL.bind(null, output) - ), - - // Highlight code blocks using "code" block - Modifiers.highlightCode.bind(null, function(lang, source) { - return Promise(code.applyBlock({ - body: source, - kwargs: { - language: lang - } - }, context)) - .then(function(result) { - if (result.html === false) { - return { text: result.body }; - } else { - return { html: result.body }; - } - }); - }) - ]; -} - -module.exports = getModifiers; diff --git a/lib/output/helper/fileToOutput.js b/lib/output/helper/fileToOutput.js deleted file mode 100644 index 361c6eb..0000000 --- a/lib/output/helper/fileToOutput.js +++ /dev/null @@ -1,32 +0,0 @@ -var path = require('path'); - -var PathUtils = require('../../utils/path'); -var LocationUtils = require('../../utils/location'); - -var OUTPUT_EXTENSION = '.html'; - -/** - * Convert a filePath (absolute) to a filename for output - * - * @param {Output} output - * @param {String} filePath - * @return {String} - */ -function fileToOutput(output, filePath) { - var book = output.getBook(); - var readme = book.getReadme(); - var fileReadme = readme.getFile(); - - if ( - path.basename(filePath, path.extname(filePath)) == 'README' || - (fileReadme.exists() && filePath == fileReadme.getPath()) - ) { - filePath = path.join(path.dirname(filePath), 'index' + OUTPUT_EXTENSION); - } else { - filePath = PathUtils.setExtension(filePath, OUTPUT_EXTENSION); - } - - return LocationUtils.normalize(filePath); -} - -module.exports = fileToOutput; diff --git a/lib/output/helper/fileToURL.js b/lib/output/helper/fileToURL.js deleted file mode 100644 index 44ad2d8..0000000 --- a/lib/output/helper/fileToURL.js +++ /dev/null @@ -1,31 +0,0 @@ -var path = require('path'); -var LocationUtils = require('../../utils/location'); - -var fileToOutput = require('./fileToOutput'); - -/** - Convert a filePath (absolute) to an url (without hostname). - It returns an absolute path. - - "README.md" -> "/" - "test/hello.md" -> "test/hello.html" - "test/README.md" -> "test/" - - @param {Output} output - @param {String} filePath - @return {String} -*/ -function fileToURL(output, filePath) { - var options = output.getOptions(); - var directoryIndex = options.get('directoryIndex'); - - filePath = fileToOutput(output, filePath); - - if (directoryIndex && path.basename(filePath) == 'index.html') { - filePath = path.dirname(filePath) + '/'; - } - - return LocationUtils.normalize(filePath); -} - -module.exports = fileToURL; diff --git a/lib/output/helper/index.js b/lib/output/helper/index.js deleted file mode 100644 index f8bc109..0000000 --- a/lib/output/helper/index.js +++ /dev/null @@ -1,2 +0,0 @@ - -module.exports = {}; diff --git a/lib/output/helper/resolveFileToURL.js b/lib/output/helper/resolveFileToURL.js deleted file mode 100644 index 3f52713..0000000 --- a/lib/output/helper/resolveFileToURL.js +++ /dev/null @@ -1,26 +0,0 @@ -var LocationUtils = require('../../utils/location'); - -var fileToURL = require('./fileToURL'); - -/** - * Resolve an absolute path (extracted from a link) - * - * @param {Output} output - * @param {String} filePath - * @return {String} - */ -function resolveFileToURL(output, filePath) { - // Convert /test.png -> test.png - filePath = LocationUtils.toAbsolute(filePath, '', ''); - - var page = output.getPage(filePath); - - // if file is a page, return correct .html url - if (page) { - filePath = fileToURL(output, filePath); - } - - return LocationUtils.normalize(filePath); -} - -module.exports = resolveFileToURL; diff --git a/lib/output/helper/writeFile.js b/lib/output/helper/writeFile.js deleted file mode 100644 index a6d4645..0000000 --- a/lib/output/helper/writeFile.js +++ /dev/null @@ -1,23 +0,0 @@ -var path = require('path'); -var fs = require('../../utils/fs'); - -/** - Write a file to the output folder - - @param {Output} output - @param {String} filePath - @param {Buffer|String} content - @return {Promise} -*/ -function writeFile(output, filePath, content) { - var rootFolder = output.getRoot(); - filePath = path.join(rootFolder, filePath); - - return fs.ensureFile(filePath) - .then(function() { - return fs.writeFile(filePath, content); - }) - .thenResolve(output); -} - -module.exports = writeFile; diff --git a/lib/output/index.js b/lib/output/index.js deleted file mode 100644 index 9b8ec17..0000000 --- a/lib/output/index.js +++ /dev/null @@ -1,24 +0,0 @@ -var Immutable = require('immutable'); - -var generators = Immutable.List([ - require('./json'), - require('./website'), - require('./ebook') -]); - -/** - Return a specific generator by its name - - @param {String} - @return {Generator} -*/ -function getGenerator(name) { - return generators.find(function(generator) { - return generator.name == name; - }); -} - -module.exports = { - generate: require('./generateBook'), - getGenerator: getGenerator -}; diff --git a/lib/output/json/index.js b/lib/output/json/index.js deleted file mode 100644 index 361da06..0000000 --- a/lib/output/json/index.js +++ /dev/null @@ -1,7 +0,0 @@ - -module.exports = { - name: 'json', - Options: require('./options'), - onPage: require('./onPage'), - onFinish: require('./onFinish') -}; diff --git a/lib/output/json/onFinish.js b/lib/output/json/onFinish.js deleted file mode 100644 index d41d778..0000000 --- a/lib/output/json/onFinish.js +++ /dev/null @@ -1,47 +0,0 @@ -var path = require('path'); - -var Promise = require('../../utils/promise'); -var fs = require('../../utils/fs'); -var JSONUtils = require('../../json'); - -/** - Finish the generation - - @param {Output} - @return {Output} -*/ -function onFinish(output) { - var book = output.getBook(); - var outputRoot = output.getRoot(); - - if (!book.isMultilingual()) { - return Promise(output); - } - - // Get main language - var languages = book.getLanguages(); - var mainLanguage = languages.getDefaultLanguage(); - - // Read the main JSON - return fs.readFile(path.resolve(outputRoot, mainLanguage.getID(), 'README.json'), 'utf8') - - // Extend the JSON - .then(function(content) { - var json = JSON.parse(content); - - json.languages = JSONUtils.encodeLanguages(languages); - - return json; - }) - - .then(function(json) { - return fs.writeFile( - path.resolve(outputRoot, 'README.json'), - JSON.stringify(json, null, 4) - ); - }) - - .thenResolve(output); -} - -module.exports = onFinish; diff --git a/lib/output/json/onPage.js b/lib/output/json/onPage.js deleted file mode 100644 index 2315ba0..0000000 --- a/lib/output/json/onPage.js +++ /dev/null @@ -1,43 +0,0 @@ -var JSONUtils = require('../../json'); -var PathUtils = require('../../utils/path'); -var Modifiers = require('../modifiers'); -var writeFile = require('../helper/writeFile'); -var getModifiers = require('../getModifiers'); - -var JSON_VERSION = '3'; - -/** - * Write a page as a json file - * - * @param {Output} output - * @param {Page} page - */ -function onPage(output, page) { - var file = page.getFile(); - var readme = output.getBook().getReadme().getFile(); - - return Modifiers.modifyHTML(page, getModifiers(output, page)) - .then(function(resultPage) { - // Generate the JSON - var json = JSONUtils.encodeBookWithPage(output.getBook(), resultPage); - - // Delete some private properties - delete json.config; - - // Specify JSON output version - json.version = JSON_VERSION; - - // File path in the output folder - var filePath = file.getPath() == readme.getPath()? 'README.json' : file.getPath(); - filePath = PathUtils.setExtension(filePath, '.json'); - - // Write it to the disk - return writeFile( - output, - filePath, - JSON.stringify(json, null, 4) - ); - }); -} - -module.exports = onPage; diff --git a/lib/output/json/options.js b/lib/output/json/options.js deleted file mode 100644 index 79167b1..0000000 --- a/lib/output/json/options.js +++ /dev/null @@ -1,8 +0,0 @@ -var Immutable = require('immutable'); - -var Options = Immutable.Record({ - // Root folder for the output - root: String() -}); - -module.exports = Options; diff --git a/lib/output/modifiers/__tests__/addHeadingId.js b/lib/output/modifiers/__tests__/addHeadingId.js deleted file mode 100644 index a3b1d81..0000000 --- a/lib/output/modifiers/__tests__/addHeadingId.js +++ /dev/null @@ -1,26 +0,0 @@ -var cheerio = require('cheerio'); -var addHeadingId = require('../addHeadingId'); - -describe('addHeadingId', function() { - it('should add an ID if none', function() { - var $ = cheerio.load('<h1>Hello World</h1><h2>Cool !!</h2>'); - - return addHeadingId($) - .then(function() { - var html = $.html(); - expect(html).toBe('<h1 id="hello-world">Hello World</h1><h2 id="cool-">Cool !!</h2>'); - }); - }); - - it('should not change existing IDs', function() { - var $ = cheerio.load('<h1 id="awesome">Hello World</h1>'); - - return addHeadingId($) - .then(function() { - var html = $.html(); - expect(html).toBe('<h1 id="awesome">Hello World</h1>'); - }); - }); -}); - - diff --git a/lib/output/modifiers/__tests__/annotateText.js b/lib/output/modifiers/__tests__/annotateText.js deleted file mode 100644 index 67e7a10..0000000 --- a/lib/output/modifiers/__tests__/annotateText.js +++ /dev/null @@ -1,46 +0,0 @@ -var Immutable = require('immutable'); -var cheerio = require('cheerio'); -var GlossaryEntry = require('../../../models/glossaryEntry'); -var annotateText = require('../annotateText'); - -describe('annotateText', function() { - var entries = Immutable.List([ - GlossaryEntry({ name: 'Word' }), - GlossaryEntry({ name: 'Multiple Words' }) - ]); - - it('should annotate text', function() { - var $ = cheerio.load('<p>This is a word, and multiple words</p>'); - - annotateText(entries, 'GLOSSARY.md', $); - - var links = $('a'); - expect(links.length).toBe(2); - - var word = $(links.get(0)); - expect(word.attr('href')).toBe('/GLOSSARY.md#word'); - expect(word.text()).toBe('word'); - expect(word.hasClass('glossary-term')).toBeTruthy(); - - var words = $(links.get(1)); - expect(words.attr('href')).toBe('/GLOSSARY.md#multiple-words'); - expect(words.text()).toBe('multiple words'); - expect(words.hasClass('glossary-term')).toBeTruthy(); - }); - - it('should not annotate scripts', function() { - var $ = cheerio.load('<script>This is a word, and multiple words</script>'); - - annotateText(entries, 'GLOSSARY.md', $); - expect($('a').length).toBe(0); - }); - - it('should not annotate when has class "no-glossary"', function() { - var $ = cheerio.load('<p class="no-glossary">This is a word, and multiple words</p>'); - - annotateText(entries, 'GLOSSARY.md', $); - expect($('a').length).toBe(0); - }); -}); - - diff --git a/lib/output/modifiers/__tests__/fetchRemoteImages.js b/lib/output/modifiers/__tests__/fetchRemoteImages.js deleted file mode 100644 index bc1704d..0000000 --- a/lib/output/modifiers/__tests__/fetchRemoteImages.js +++ /dev/null @@ -1,40 +0,0 @@ -var cheerio = require('cheerio'); -var tmp = require('tmp'); -var path = require('path'); - -var URL = 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/280px-PNG_transparency_demonstration_1.png'; - -describe('fetchRemoteImages', function() { - var dir; - var fetchRemoteImages = require('../fetchRemoteImages'); - - beforeEach(function() { - dir = tmp.dirSync(); - }); - - it('should download image file', function() { - var $ = cheerio.load('<img src="' + URL + '" />'); - - return fetchRemoteImages(dir.name, 'index.html', $) - .then(function() { - var $img = $('img'); - var src = $img.attr('src'); - - expect(dir.name).toHaveFile(src); - }); - }); - - it('should download image file and replace with relative path', function() { - var $ = cheerio.load('<img src="' + URL + '" />'); - - return fetchRemoteImages(dir.name, 'test/index.html', $) - .then(function() { - var $img = $('img'); - var src = $img.attr('src'); - - expect(dir.name).toHaveFile(path.join('test', src)); - }); - }); -}); - - diff --git a/lib/output/modifiers/__tests__/highlightCode.js b/lib/output/modifiers/__tests__/highlightCode.js deleted file mode 100644 index 75d9902..0000000 --- a/lib/output/modifiers/__tests__/highlightCode.js +++ /dev/null @@ -1,60 +0,0 @@ -var cheerio = require('cheerio'); -var Promise = require('../../../utils/promise'); -var highlightCode = require('../highlightCode'); - -describe('highlightCode', function() { - function doHighlight(lang, code) { - return { - text: '' + (lang || '') + '$' + code - }; - } - - function doHighlightAsync(lang, code) { - return Promise() - .then(function() { - return doHighlight(lang, code); - }); - } - - it('should call it for normal code element', function() { - var $ = cheerio.load('<p>This is a <code>test</code></p>'); - - return highlightCode(doHighlight, $) - .then(function() { - var $code = $('code'); - expect($code.text()).toBe('$test'); - }); - }); - - it('should call it for markdown code block', function() { - var $ = cheerio.load('<pre><code class="lang-js">test</code></pre>'); - - return highlightCode(doHighlight, $) - .then(function() { - var $code = $('code'); - expect($code.text()).toBe('js$test'); - }); - }); - - it('should call it for asciidoc code block', function() { - var $ = cheerio.load('<pre><code class="language-python">test</code></pre>'); - - return highlightCode(doHighlight, $) - .then(function() { - var $code = $('code'); - expect($code.text()).toBe('python$test'); - }); - }); - - it('should accept async highlighter', function() { - var $ = cheerio.load('<pre><code class="language-python">test</code></pre>'); - - return highlightCode(doHighlightAsync, $) - .then(function() { - var $code = $('code'); - expect($code.text()).toBe('python$test'); - }); - }); -}); - - diff --git a/lib/output/modifiers/__tests__/inlinePng.js b/lib/output/modifiers/__tests__/inlinePng.js deleted file mode 100644 index 0073cff..0000000 --- a/lib/output/modifiers/__tests__/inlinePng.js +++ /dev/null @@ -1,25 +0,0 @@ -var cheerio = require('cheerio'); -var tmp = require('tmp'); -var inlinePng = require('../inlinePng'); - -describe('inlinePng', function() { - var dir; - - beforeEach(function() { - dir = tmp.dirSync(); - }); - - it('should write an inline PNG using data URI as a file', function() { - var $ = cheerio.load('<img alt="GitBook Logo 20x20" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUEAYAAADdGcFOAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAAF+klEQVRIDY3Wf5CVVR3H8c9z791fyI9dQwdQ4TTI7wEWnQZZAa/mJE4Z0OaKUuN1KoaykZxUGGHay+iIVFMoEYrUPhDCKEKW2ChT8dA0RCSxWi6EW3sYYpcfxq5C+4O9957O+7m7O/qHQ9/XzH1+nHuec57z8wkWTsKw0y6N/LxXN6KzTnEUHi8eP/l3YStSU/MdsYvBbGh8six2YXcbcgc++QkfTQkWz/81KtqDA0hlUoWnsX+5uxe5X365BB9my2bjrHNHccLk16BpS9CExjcmXMDbD6wehdyEjxbjz1uK1zn9qga6dcfnMLXeXY/qjuQqTF4W1MKke8ZgeNhjMCxMPIWSd4OF78C55CFI/1kF6WwXpMqjkAZ/CKniNDrCsmU4lE1YbPlgR2x7R39FF23D4mq3A1+Z35PGTNs1E1XhxcGQOh6HNPwXkK56BVJhOaRg/pvoHXNxHFw410B25EYE2RMvI0i/twFJvXcrFObykEa+DmnQGLwYqR0l2a6JqItaj8C/4E2QxtZCofkC8tF1t8HZc/fAZaLnIF2xEsoEtW1w7vBSSFtfhDTnCki9cSi81Ain1uko2Ld+Dmf2rkUq0/5t+PYbFtPQdkjzNiAXTWtDEF49FgkzJInAVPwNyhzcDOmrdZCm/Rn+ebWtcPs+/U24hmg2XL0rRkPPELh9R8fDtXR2oC/VuZbGaci79Ajkb6lZgfyYtyzy/X9s6T/pO/ZfN/RdNxxIwTWM2wbX8KVmuIaEqmKm6zEondwGpd0SyOy5DrJ//TFkX9kMhd3XQHbEVCSsm4OECV5HIv2p15CwfWPSntoHRbv2Q1HzSvSlSqZwATIuBxk/zZBOBbdB+u9hSKU3Q7pwAjInZkFm6U8hu7MSMqe/Dqn8fUj5GVCmpxK+4N/F1LMa0p5eSOPqIPP7NGSunAI/+R6GnzQzIBt8A1LC/QZ+6HwLst1rITv0n5CtXgSZ78yFTNkR+FdeDZneJkip3fAtsQ5Scilkek7CH9dAmjIWvkK7IXXOh6/IzZDNPQdZXR1TQmdjKv0ZfEu0YKDpNflpyG5aDtnRv8VAuu3dBV+huyBbvgdS97tQNLQc0mfugKy5Cb4BipPIXvsUpK5N8Mvao/Bd3QDZRH9Rrtj3Cl6FHwPFMLmNkKrj8BnHoT+XX6f2wl+XxFS4Ab7C72Dgf7bi+5DpTkNm8kQMpCs/BzIlz8LfPxnzLdh3EjwMX4GX4Ju4GNb9A1L7k/D3J8b6kv2LFCtmCmcgUzoJsr2z4MfwFsh87xikZefg188fYaAhpPUxm3ge/vFnYkoED0HqeQiyJYcwkNGWnoNv6s9C1p1Bf/389VYoCjohW7UfMms3wXdpBv7+FEiPLIHs4DIMNERUNhbSpY3wk6QOsqlCDVx2xCrInMpBmfNPQOnzKxBkkrugdOl9GKigSZZCUWIm/GqwDtLUI5D+WAOlb9wKP0YvQLbjZSjsaYaL/n0/FA3fDtnCGihK5UYjCK+ZDr+TDIKLdm2Fs1UOzo76F5wO74XSZj0S6d7RCMLkCshcXALZxaWQRjXDZQ62oRAdCeG/Ju5HELX2QFH3C0hkRy6GovyfwF58AoVbguOxyB2H7/I34Gf11yANnQSp7Vr4MbQH0vg7kbNNp5AM3UrIVDchnz56B1Jm573wW9gZSFVPwO/hefg5FsIvN09CchtQCIOFw/F5U8ii3CZn4cqo7C8YlXEPYkx9cacZl00+iwnprrtwVdj1Q/gXmAs/pu6LZc9XQOGgSvh19n2cDZN341g2EcfxTEGwH/RewqlMsUfbbWIGLjUG+j/j9nokD1beiOvLS5dhjr30Gu6ZnivgdtM/6VJvY1+6pBHbH+h9CX84vfMxNJtisYVFlys+WNCIZJNmIsjohlhNSQC3f8R55H+y/hjkN8GPR9ndCLJxT4/3n0Px51ay8XQnNrYfDJHf//Fc0oMrEZSeeQGJ7+Z+gKCgLbHNWgXnB9FlYt5JaN38JIINC95EakjtAqQeuUx21c5B6tEFf0fSfbEFQf28Z6D6y+X/H0jf40QQJhYwAAAAAElFTkSuQmCC"/>'); - - return inlinePng(dir.name, 'index.html', $) - .then(function() { - var $img = $('img'); - var src = $img.attr('src'); - - expect(dir.name).toHaveFile(src); - }); - }); -}); - - diff --git a/lib/output/modifiers/__tests__/resolveLinks.js b/lib/output/modifiers/__tests__/resolveLinks.js deleted file mode 100644 index 8904c11..0000000 --- a/lib/output/modifiers/__tests__/resolveLinks.js +++ /dev/null @@ -1,104 +0,0 @@ -var path = require('path'); -var cheerio = require('cheerio'); -var resolveLinks = require('../resolveLinks'); - -describe('resolveLinks', function() { - function resolveFileBasic(href) { - return 'fakeDir/' + href; - } - - function resolveFileCustom(href) { - if (path.extname(href) == '.md') { - return href.slice(0, -3) + '.html'; - } - - return href; - } - - describe('Absolute path', function() { - var TEST = '<p>This is a <a href="/test/cool.md"></a></p>'; - - it('should resolve path starting by "/" in root directory', function() { - var $ = cheerio.load(TEST); - - return resolveLinks('hello.md', resolveFileBasic, $) - .then(function() { - var link = $('a'); - expect(link.attr('href')).toBe('fakeDir/test/cool.md'); - }); - }); - - it('should resolve path starting by "/" in child directory', function() { - var $ = cheerio.load(TEST); - - return resolveLinks('afolder/hello.md', resolveFileBasic, $) - .then(function() { - var link = $('a'); - expect(link.attr('href')).toBe('../fakeDir/test/cool.md'); - }); - }); - }); - - describe('Anchor', function() { - it('should prevent anchors in resolution', function() { - var TEST = '<p>This is a <a href="test/cool.md#an-anchor"></a></p>'; - var $ = cheerio.load(TEST); - - return resolveLinks('hello.md', resolveFileCustom, $) - .then(function() { - var link = $('a'); - expect(link.attr('href')).toBe('test/cool.html#an-anchor'); - }); - }); - - it('should ignore pure anchor links', function() { - var TEST = '<p>This is a <a href="#an-anchor"></a></p>'; - var $ = cheerio.load(TEST); - - return resolveLinks('hello.md', resolveFileCustom, $) - .then(function() { - var link = $('a'); - expect(link.attr('href')).toBe('#an-anchor'); - }); - }); - }); - - describe('Custom Resolver', function() { - var TEST = '<p>This is a <a href="/test/cool.md"></a> <a href="afile.png"></a></p>'; - - it('should resolve path correctly for absolute path', function() { - var $ = cheerio.load(TEST); - - return resolveLinks('hello.md', resolveFileCustom, $) - .then(function() { - var link = $('a').first(); - expect(link.attr('href')).toBe('test/cool.html'); - }); - }); - - it('should resolve path correctly for absolute path (2)', function() { - var $ = cheerio.load(TEST); - - return resolveLinks('afodler/hello.md', resolveFileCustom, $) - .then(function() { - var link = $('a').first(); - expect(link.attr('href')).toBe('../test/cool.html'); - }); - }); - }); - - describe('External link', function() { - var TEST = '<p>This is a <a href="http://www.github.com">external link</a></p>'; - - it('should have target="_blank" attribute', function() { - var $ = cheerio.load(TEST); - - return resolveLinks('hello.md', resolveFileBasic, $) - .then(function() { - var link = $('a'); - expect(link.attr('target')).toBe('_blank'); - }); - }); - }); - -}); diff --git a/lib/output/modifiers/__tests__/svgToImg.js b/lib/output/modifiers/__tests__/svgToImg.js deleted file mode 100644 index 5fe9796..0000000 --- a/lib/output/modifiers/__tests__/svgToImg.js +++ /dev/null @@ -1,25 +0,0 @@ -var cheerio = require('cheerio'); -var tmp = require('tmp'); - -describe('svgToImg', function() { - var dir; - var svgToImg = require('../svgToImg'); - - beforeEach(function() { - dir = tmp.dirSync(); - }); - - it('should write svg as a file', function() { - var $ = cheerio.load('<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" version="1.1"><rect width="200" height="100" stroke="black" stroke-width="6" fill="green"/></svg>'); - - return svgToImg(dir.name, 'index.html', $) - .then(function() { - var $img = $('img'); - var src = $img.attr('src'); - - expect(dir.name).toHaveFile(src); - }); - }); -}); - - diff --git a/lib/output/modifiers/__tests__/svgToPng.js b/lib/output/modifiers/__tests__/svgToPng.js deleted file mode 100644 index dbb3502..0000000 --- a/lib/output/modifiers/__tests__/svgToPng.js +++ /dev/null @@ -1,33 +0,0 @@ -var cheerio = require('cheerio'); -var tmp = require('tmp'); -var path = require('path'); - -var svgToImg = require('../svgToImg'); -var svgToPng = require('../svgToPng'); - -describe('svgToPng', function() { - var dir; - - beforeEach(function() { - dir = tmp.dirSync(); - }); - - it('should write svg as png file', function() { - var $ = cheerio.load('<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" version="1.1"><rect width="200" height="100" stroke="black" stroke-width="6" fill="green"/></svg>'); - var fileName = 'index.html'; - - return svgToImg(dir.name, fileName, $) - .then(function() { - return svgToPng(dir.name, fileName, $); - }) - .then(function() { - var $img = $('img'); - var src = $img.attr('src'); - - expect(dir.name).toHaveFile(src); - expect(path.extname(src)).toBe('.png'); - }); - }); -}); - - diff --git a/lib/output/modifiers/addHeadingId.js b/lib/output/modifiers/addHeadingId.js deleted file mode 100644 index e2e2720..0000000 --- a/lib/output/modifiers/addHeadingId.js +++ /dev/null @@ -1,23 +0,0 @@ -var slug = require('github-slugid'); -var editHTMLElement = require('./editHTMLElement'); - -/** - Add ID to an heading - - @param {HTMLElement} heading -*/ -function addId(heading) { - if (heading.attr('id')) return; - heading.attr('id', slug(heading.text())); -} - -/** - Add ID to all headings - - @param {HTMLDom} $ -*/ -function addHeadingId($) { - return editHTMLElement($, 'h1,h2,h3,h4,h5,h6', addId); -} - -module.exports = addHeadingId; diff --git a/lib/output/modifiers/annotateText.js b/lib/output/modifiers/annotateText.js deleted file mode 100644 index 490c228..0000000 --- a/lib/output/modifiers/annotateText.js +++ /dev/null @@ -1,94 +0,0 @@ -var escape = require('escape-html'); - -// Selector to ignore -var ANNOTATION_IGNORE = '.no-glossary,code,pre,a,script,h1,h2,h3,h4,h5,h6'; - -function pregQuote( str ) { - return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1'); -} - -function replaceText($, el, search, replace, text_only ) { - return $(el).each(function(){ - var node = this.firstChild, - val, - new_val, - - // Elements to be removed at the end. - remove = []; - - // Only continue if firstChild exists. - if ( node ) { - - // Loop over all childNodes. - while (node) { - - // Only process text nodes. - if ( node.nodeType === 3 ) { - - // The original node value. - val = node.nodeValue; - - // The new value. - new_val = val.replace( search, replace ); - - // Only replace text if the new value is actually different! - if ( new_val !== val ) { - - if ( !text_only && /</.test( new_val ) ) { - // The new value contains HTML, set it in a slower but far more - // robust way. - $(node).before( new_val ); - - // Don't remove the node yet, or the loop will lose its place. - remove.push( node ); - } else { - // The new value contains no HTML, so it can be set in this - // very fast, simple way. - node.nodeValue = new_val; - } - } - } - - node = node.nextSibling; - } - } - - // Time to remove those elements! - if (remove.length) $(remove).remove(); - }); -} - -/** - * Annotate text using a list of GlossaryEntry - * - * @param {List<GlossaryEntry>} - * @param {String} glossaryFilePath - * @param {HTMLDom} $ - */ -function annotateText(entries, glossaryFilePath, $) { - entries.forEach(function(entry) { - var entryId = entry.getID(); - var name = entry.getName(); - var description = entry.getDescription(); - var searchRegex = new RegExp( '\\b(' + pregQuote(name.toLowerCase()) + ')\\b' , 'gi' ); - - $('*').each(function() { - var $this = $(this); - - if ( - $this.is(ANNOTATION_IGNORE) || - $this.parents(ANNOTATION_IGNORE).length > 0 - ) return; - - replaceText($, this, searchRegex, function(match) { - return '<a href="/' + glossaryFilePath + '#' + entryId + '" ' - + 'class="glossary-term" title="' + escape(description) + '">' - + match - + '</a>'; - }); - }); - - }); -} - -module.exports = annotateText; diff --git a/lib/output/modifiers/editHTMLElement.js b/lib/output/modifiers/editHTMLElement.js deleted file mode 100644 index 755598e..0000000 --- a/lib/output/modifiers/editHTMLElement.js +++ /dev/null @@ -1,15 +0,0 @@ -var Promise = require('../../utils/promise'); - -/** - Edit all elements matching a selector -*/ -function editHTMLElement($, selector, fn) { - var $elements = $(selector); - - return Promise.forEach($elements, function(el) { - var $el = $(el); - return fn($el); - }); -} - -module.exports = editHTMLElement; diff --git a/lib/output/modifiers/fetchRemoteImages.js b/lib/output/modifiers/fetchRemoteImages.js deleted file mode 100644 index ef868b9..0000000 --- a/lib/output/modifiers/fetchRemoteImages.js +++ /dev/null @@ -1,44 +0,0 @@ -var path = require('path'); -var crc = require('crc'); - -var editHTMLElement = require('./editHTMLElement'); -var fs = require('../../utils/fs'); -var LocationUtils = require('../../utils/location'); - -/** - Fetch all remote images - - @param {String} rootFolder - @param {String} currentFile - @param {HTMLDom} $ - @return {Promise} -*/ -function fetchRemoteImages(rootFolder, currentFile, $) { - var currentDirectory = path.dirname(currentFile); - - return editHTMLElement($, 'img', function($img) { - var src = $img.attr('src'); - var extension = path.extname(src); - - if (!LocationUtils.isExternal(src)) { - return; - } - - // We avoid generating twice the same PNG - var hash = crc.crc32(src).toString(16); - var fileName = hash + extension; - var filePath = path.join(rootFolder, fileName); - - return fs.assertFile(filePath, function() { - return fs.download(src, filePath); - }) - .then(function() { - // Convert to relative - src = LocationUtils.relative(currentDirectory, fileName); - - $img.replaceWith('<img src="' + src + '" />'); - }); - }); -} - -module.exports = fetchRemoteImages; diff --git a/lib/output/modifiers/highlightCode.js b/lib/output/modifiers/highlightCode.js deleted file mode 100644 index 5d397bb..0000000 --- a/lib/output/modifiers/highlightCode.js +++ /dev/null @@ -1,58 +0,0 @@ -var is = require('is'); -var Immutable = require('immutable'); - -var Promise = require('../../utils/promise'); -var editHTMLElement = require('./editHTMLElement'); - -/** - Return language for a code blocks from a list of class names - - @param {Array<String>} - @return {String} -*/ -function getLanguageForClass(classNames) { - return Immutable.List(classNames) - .map(function(cl) { - // Markdown - if (cl.search('lang-') === 0) { - return cl.slice('lang-'.length); - } - - // Asciidoc - if (cl.search('language-') === 0) { - return cl.slice('language-'.length); - } - - return null; - }) - .find(function(cl) { - return Boolean(cl); - }); -} - - -/** - Highlight all code elements - - @param {Function(lang, body) -> String} highlight - @param {HTMLDom} $ - @return {Promise} -*/ -function highlightCode(highlight, $) { - return editHTMLElement($, 'code', function($code) { - var classNames = ($code.attr('class') || '').split(' '); - var lang = getLanguageForClass(classNames); - var source = $code.text(); - - return Promise(highlight(lang, source)) - .then(function(r) { - if (is.string(r.html)) { - $code.html(r.html); - } else { - $code.text(r.text); - } - }); - }); -} - -module.exports = highlightCode; diff --git a/lib/output/modifiers/index.js b/lib/output/modifiers/index.js deleted file mode 100644 index f1daa2b..0000000 --- a/lib/output/modifiers/index.js +++ /dev/null @@ -1,15 +0,0 @@ - -module.exports = { - modifyHTML: require('./modifyHTML'), - inlineAssets: require('./inlineAssets'), - - // HTML transformations - addHeadingId: require('./addHeadingId'), - svgToImg: require('./svgToImg'), - fetchRemoteImages: require('./fetchRemoteImages'), - svgToPng: require('./svgToPng'), - resolveLinks: require('./resolveLinks'), - resolveImages: require('./resolveImages'), - annotateText: require('./annotateText'), - highlightCode: require('./highlightCode') -}; diff --git a/lib/output/modifiers/inlineAssets.js b/lib/output/modifiers/inlineAssets.js deleted file mode 100644 index 7cd874b..0000000 --- a/lib/output/modifiers/inlineAssets.js +++ /dev/null @@ -1,29 +0,0 @@ -var svgToImg = require('./svgToImg'); -var svgToPng = require('./svgToPng'); -var inlinePng = require('./inlinePng'); -var resolveImages = require('./resolveImages'); -var fetchRemoteImages = require('./fetchRemoteImages'); - -var Promise = require('../../utils/promise'); - -/** - Inline all assets in a page - - @param {String} rootFolder -*/ -function inlineAssets(rootFolder, currentFile) { - return function($) { - return Promise() - - // Resolving images and fetching external images should be - // done before svg conversion - .then(resolveImages.bind(null, currentFile, $)) - .then(fetchRemoteImages.bind(null, rootFolder, currentFile, $)) - - .then(svgToImg.bind(null, rootFolder, currentFile, $)) - .then(svgToPng.bind(null, rootFolder, currentFile, $)) - .then(inlinePng.bind(null, rootFolder, currentFile, $)); - }; -} - -module.exports = inlineAssets; diff --git a/lib/output/modifiers/inlinePng.js b/lib/output/modifiers/inlinePng.js deleted file mode 100644 index 161f164..0000000 --- a/lib/output/modifiers/inlinePng.js +++ /dev/null @@ -1,47 +0,0 @@ -var crc = require('crc'); -var path = require('path'); - -var imagesUtil = require('../../utils/images'); -var fs = require('../../utils/fs'); -var LocationUtils = require('../../utils/location'); - -var editHTMLElement = require('./editHTMLElement'); - -/** - Convert all inline PNG images to PNG file - - @param {String} rootFolder - @param {HTMLDom} $ - @return {Promise} -*/ -function inlinePng(rootFolder, currentFile, $) { - var currentDirectory = path.dirname(currentFile); - - return editHTMLElement($, 'img', function($img) { - var src = $img.attr('src'); - if (!LocationUtils.isDataURI(src)) { - return; - } - - // We avoid generating twice the same PNG - var hash = crc.crc32(src).toString(16); - var fileName = hash + '.png'; - - // Result file path - var filePath = path.join(rootFolder, fileName); - - return fs.assertFile(filePath, function() { - return imagesUtil.convertInlinePNG(src, filePath); - }) - .then(function() { - // Convert filename to a relative filename - fileName = LocationUtils.relative(currentDirectory, fileName); - - // Replace src - $img.attr('src', fileName); - }); - }); -} - - -module.exports = inlinePng; diff --git a/lib/output/modifiers/modifyHTML.js b/lib/output/modifiers/modifyHTML.js deleted file mode 100644 index cd3d6e5..0000000 --- a/lib/output/modifiers/modifyHTML.js +++ /dev/null @@ -1,25 +0,0 @@ -var cheerio = require('cheerio'); -var Promise = require('../../utils/promise'); - -/** - Apply a list of operations to a page and - output the new page. - - @param {Page} - @param {List|Array<Transformation>} - @return {Promise<Page>} -*/ -function modifyHTML(page, operations) { - var html = page.getContent(); - var $ = cheerio.load(html); - - return Promise.forEach(operations, function(op) { - return op($); - }) - .then(function() { - var resultHTML = $.html(); - return page.set('content', resultHTML); - }); -} - -module.exports = modifyHTML; diff --git a/lib/output/modifiers/resolveImages.js b/lib/output/modifiers/resolveImages.js deleted file mode 100644 index cc25cfa..0000000 --- a/lib/output/modifiers/resolveImages.js +++ /dev/null @@ -1,33 +0,0 @@ -var path = require('path'); - -var LocationUtils = require('../../utils/location'); -var editHTMLElement = require('./editHTMLElement'); - -/** - Resolve all HTML images: - - /test.png in hello -> ../test.html - - @param {String} currentFile - @param {HTMLDom} $ -*/ -function resolveImages(currentFile, $) { - var currentDirectory = path.dirname(currentFile); - - return editHTMLElement($, 'img', function($img) { - var src = $img.attr('src'); - - if (LocationUtils.isExternal(src) || LocationUtils.isDataURI(src)) { - return; - } - - // Calcul absolute path for this - src = LocationUtils.toAbsolute(src, currentDirectory, '.'); - - // Convert back to relative - src = LocationUtils.relative(currentDirectory, src); - - $img.attr('src', src); - }); -} - -module.exports = resolveImages; diff --git a/lib/output/modifiers/resolveLinks.js b/lib/output/modifiers/resolveLinks.js deleted file mode 100644 index 9d15e5e..0000000 --- a/lib/output/modifiers/resolveLinks.js +++ /dev/null @@ -1,53 +0,0 @@ -var path = require('path'); -var url = require('url'); - -var LocationUtils = require('../../utils/location'); -var editHTMLElement = require('./editHTMLElement'); - -/** - Resolve all HTML links: - - /test.md in hello -> ../test.html - - @param {String} currentFile - @param {Function(String) -> String} resolveFile - @param {HTMLDom} $ -*/ -function resolveLinks(currentFile, resolveFile, $) { - var currentDirectory = path.dirname(currentFile); - - return editHTMLElement($, 'a', function($a) { - var href = $a.attr('href'); - - // Don't change a tag without href - if (!href) { - return; - } - - if (LocationUtils.isExternal(href)) { - $a.attr('target', '_blank'); - return; - } - - // Split anchor - var parsed = url.parse(href); - href = parsed.pathname || ''; - - if (href) { - // Calcul absolute path for this - href = LocationUtils.toAbsolute(href, currentDirectory, '.'); - - // Resolve file - href = resolveFile(href); - - // Convert back to relative - href = LocationUtils.relative(currentDirectory, href); - } - - // Add back anchor - href = href + (parsed.hash || ''); - - $a.attr('href', href); - }); -} - -module.exports = resolveLinks; diff --git a/lib/output/modifiers/svgToImg.js b/lib/output/modifiers/svgToImg.js deleted file mode 100644 index f31b06d..0000000 --- a/lib/output/modifiers/svgToImg.js +++ /dev/null @@ -1,56 +0,0 @@ -var path = require('path'); -var crc = require('crc'); -var domSerializer = require('dom-serializer'); - -var editHTMLElement = require('./editHTMLElement'); -var fs = require('../../utils/fs'); -var LocationUtils = require('../../utils/location'); - -/** - Render a cheerio DOM as html - - @param {HTMLDom} $ - @param {HTMLElement} dom - @param {Object} - @return {String} -*/ -function renderDOM($, dom, options) { - if (!dom && $._root && $._root.children) { - dom = $._root.children; - } - options = options|| dom.options || $._options; - return domSerializer(dom, options); -} - -/** - Replace SVG tag by IMG - - @param {String} baseFolder - @param {HTMLDom} $ -*/ -function svgToImg(baseFolder, currentFile, $) { - var currentDirectory = path.dirname(currentFile); - - return editHTMLElement($, 'svg', function($svg) { - var content = '<?xml version="1.0" encoding="UTF-8"?>' + - renderDOM($, $svg); - - // We avoid generating twice the same PNG - var hash = crc.crc32(content).toString(16); - var fileName = hash + '.svg'; - var filePath = path.join(baseFolder, fileName); - - // Write the svg to the file - return fs.assertFile(filePath, function() { - return fs.writeFile(filePath, content, 'utf8'); - }) - - // Return as image - .then(function() { - var src = LocationUtils.relative(currentDirectory, fileName); - $svg.replaceWith('<img src="' + src + '" />'); - }); - }); -} - -module.exports = svgToImg; diff --git a/lib/output/modifiers/svgToPng.js b/lib/output/modifiers/svgToPng.js deleted file mode 100644 index 1093106..0000000 --- a/lib/output/modifiers/svgToPng.js +++ /dev/null @@ -1,53 +0,0 @@ -var crc = require('crc'); -var path = require('path'); - -var imagesUtil = require('../../utils/images'); -var fs = require('../../utils/fs'); -var LocationUtils = require('../../utils/location'); - -var editHTMLElement = require('./editHTMLElement'); - -/** - Convert all SVG images to PNG - - @param {String} rootFolder - @param {HTMLDom} $ - @return {Promise} -*/ -function svgToPng(rootFolder, currentFile, $) { - var currentDirectory = path.dirname(currentFile); - - return editHTMLElement($, 'img', function($img) { - var src = $img.attr('src'); - if (path.extname(src) !== '.svg') { - return; - } - - // Calcul absolute path for this - src = LocationUtils.toAbsolute(src, currentDirectory, '.'); - - // We avoid generating twice the same PNG - var hash = crc.crc32(src).toString(16); - var fileName = hash + '.png'; - - // Input file path - var inputPath = path.join(rootFolder, src); - - // Result file path - var filePath = path.join(rootFolder, fileName); - - return fs.assertFile(filePath, function() { - return imagesUtil.convertSVGToPNG(inputPath, filePath); - }) - .then(function() { - // Convert filename to a relative filename - fileName = LocationUtils.relative(currentDirectory, fileName); - - // Replace src - $img.attr('src', fileName); - }); - }); -} - - -module.exports = svgToPng; diff --git a/lib/output/prepareAssets.js b/lib/output/prepareAssets.js deleted file mode 100644 index ae9b55a..0000000 --- a/lib/output/prepareAssets.js +++ /dev/null @@ -1,22 +0,0 @@ -var Parse = require('../parse'); - -/** - List all assets in the book - - @param {Output} - @return {Promise<Output>} -*/ -function prepareAssets(output) { - var book = output.getBook(); - var pages = output.getPages(); - var logger = output.getLogger(); - - return Parse.listAssets(book, pages) - .then(function(assets) { - logger.info.ln('found', assets.size, 'asset files'); - - return output.set('assets', assets); - }); -} - -module.exports = prepareAssets; diff --git a/lib/output/preparePages.js b/lib/output/preparePages.js deleted file mode 100644 index 83944ed..0000000 --- a/lib/output/preparePages.js +++ /dev/null @@ -1,26 +0,0 @@ -var Parse = require('../parse'); -var Promise = require('../utils/promise'); - -/** - List and prepare all pages - - @param {Output} - @return {Promise<Output>} -*/ -function preparePages(output) { - var book = output.getBook(); - var logger = book.getLogger(); - - if (book.isMultilingual()) { - return Promise(output); - } - - return Parse.parsePagesList(book) - .then(function(pages) { - logger.info.ln('found', pages.size, 'pages'); - - return output.set('pages', pages); - }); -} - -module.exports = preparePages; diff --git a/lib/output/preparePlugins.js b/lib/output/preparePlugins.js deleted file mode 100644 index 5c4be93..0000000 --- a/lib/output/preparePlugins.js +++ /dev/null @@ -1,36 +0,0 @@ -var Plugins = require('../plugins'); -var Promise = require('../utils/promise'); - -/** - * Load and setup plugins - * - * @param {Output} - * @return {Promise<Output>} - */ -function preparePlugins(output) { - var book = output.getBook(); - - return Promise() - - // Only load plugins for main book - .then(function() { - if (book.isLanguageBook()) { - return output.getPlugins(); - } else { - return Plugins.loadForBook(book); - } - }) - - // Update book's configuration using the plugins - .then(function(plugins) { - return Plugins.validateConfig(book, plugins) - .then(function(newBook) { - return output.merge({ - book: newBook, - plugins: plugins - }); - }); - }); -} - -module.exports = preparePlugins; diff --git a/lib/output/website/__tests__/i18n.js b/lib/output/website/__tests__/i18n.js deleted file mode 100644 index fd610fb..0000000 --- a/lib/output/website/__tests__/i18n.js +++ /dev/null @@ -1,38 +0,0 @@ -var createMockOutput = require('../../__tests__/createMock'); -var prepareI18n = require('../prepareI18n'); -var createTemplateEngine = require('../createTemplateEngine'); - -var WebsiteGenerator = require('../'); - -describe('i18n', function() { - it('should correctly use english as default language', function() { - return createMockOutput(WebsiteGenerator, { - 'README.md': 'Hello World' - }) - .then(function(output) { - return prepareI18n(output); - }) - .then(function(output) { - var engine = createTemplateEngine(output, 'README.md'); - var t = engine.getFilters().get('t'); - - expect(t('SUMMARY_INTRODUCTION')).toEqual('Introduction'); - }); - }); - - it('should correctly use language from book.json', function() { - return createMockOutput(WebsiteGenerator, { - 'README.md': 'Hello World', - 'book.json': JSON.stringify({ language: 'fr' }) - }) - .then(function(output) { - return prepareI18n(output); - }) - .then(function(output) { - var engine = createTemplateEngine(output, 'README.md'); - var t = engine.getFilters().get('t'); - - expect(t('GITBOOK_LINK')).toEqual('Publié avec GitBook'); - }); - }); -}); diff --git a/lib/output/website/copyPluginAssets.js b/lib/output/website/copyPluginAssets.js deleted file mode 100644 index 9150636..0000000 --- a/lib/output/website/copyPluginAssets.js +++ /dev/null @@ -1,117 +0,0 @@ -var path = require('path'); - -var ASSET_FOLDER = require('../../constants/pluginAssetsFolder'); -var Promise = require('../../utils/promise'); -var fs = require('../../utils/fs'); - -/** - Copy all assets from plugins. - Assets are files stored in "_assets" - nd resources declared in the plugin itself. - - @param {Output} - @return {Promise} -*/ -function copyPluginAssets(output) { - var book = output.getBook(); - - // Don't copy plugins assets for language book - // It'll be resolved to the parent folder - if (book.isLanguageBook()) { - return Promise(output); - } - - var plugins = output.getPlugins() - - // We reverse the order of plugins to copy - // so that first plugins can replace assets from other plugins. - .reverse(); - - return Promise.forEach(plugins, function(plugin) { - return copyAssets(output, plugin) - .then(function() { - return copyResources(output, plugin); - }); - }) - .thenResolve(output); -} - -/** - Copy assets from a plugin - - @param {Plugin} - @return {Promise} -*/ -function copyAssets(output, plugin) { - var logger = output.getLogger(); - var pluginRoot = plugin.getPath(); - var options = output.getOptions(); - - var outputRoot = options.get('root'); - var assetOutputFolder = path.join(outputRoot, 'gitbook'); - var prefix = options.get('prefix'); - - var assetFolder = path.join(pluginRoot, ASSET_FOLDER, prefix); - - if (!fs.existsSync(assetFolder)) { - return Promise(); - } - - logger.debug.ln('copy assets from theme', assetFolder); - return fs.copyDir( - assetFolder, - assetOutputFolder, - { - deleteFirst: false, - overwrite: true, - confirm: true - } - ); -} - -/** - Copy resources from a plugin - - @param {Plugin} - @return {Promise} -*/ -function copyResources(output, plugin) { - var logger = output.getLogger(); - - var options = output.getOptions(); - var outputRoot = options.get('root'); - - var state = output.getState(); - var resources = state.getResources(); - - var pluginRoot = plugin.getPath(); - var pluginResources = resources.get(plugin.getName()); - - var assetsFolder = pluginResources.get('assets'); - var assetOutputFolder = path.join(outputRoot, 'gitbook', plugin.getNpmID()); - - if (!assetsFolder) { - return Promise(); - } - - // Resolve assets folder - assetsFolder = path.resolve(pluginRoot, assetsFolder); - if (!fs.existsSync(assetsFolder)) { - logger.warn.ln('assets folder for plugin "' + plugin.getName() + '" doesn\'t exist'); - return Promise(); - } - - logger.debug.ln('copy resources from plugin', assetsFolder); - - return fs.copyDir( - assetsFolder, - assetOutputFolder, - { - deleteFirst: false, - overwrite: true, - confirm: true - } - ); -} - -module.exports = copyPluginAssets; diff --git a/lib/output/website/createTemplateEngine.js b/lib/output/website/createTemplateEngine.js deleted file mode 100644 index 02ec796..0000000 --- a/lib/output/website/createTemplateEngine.js +++ /dev/null @@ -1,151 +0,0 @@ -var path = require('path'); -var nunjucks = require('nunjucks'); -var DoExtension = require('nunjucks-do')(nunjucks); - -var Api = require('../../api'); -var deprecate = require('../../api/deprecate'); -var JSONUtils = require('../../json'); -var LocationUtils = require('../../utils/location'); -var fs = require('../../utils/fs'); -var PathUtils = require('../../utils/path'); -var TemplateEngine = require('../../models/templateEngine'); -var templatesFolder = require('../../constants/templatesFolder'); -var defaultFilters = require('../../constants/defaultFilters'); -var Templating = require('../../templating'); -var listSearchPaths = require('./listSearchPaths'); - -var fileToURL = require('../helper/fileToURL'); -var resolveFileToURL = require('../helper/resolveFileToURL'); - -/** - * Directory for a theme with the templates - */ -function templateFolder(dir) { - return path.join(dir, templatesFolder); -} - -/** - * Create templating engine to render themes - * - * @param {Output} output - * @param {String} currentFile - * @return {TemplateEngine} - */ -function createTemplateEngine(output, currentFile) { - var book = output.getBook(); - var state = output.getState(); - var i18n = state.getI18n(); - var config = book.getConfig(); - var summary = book.getSummary(); - var outputFolder = output.getRoot(); - - // Search paths for templates - var searchPaths = listSearchPaths(output); - var tplSearchPaths = searchPaths.map(templateFolder); - - // Create loader - var loader = new Templating.ThemesLoader(tplSearchPaths); - - // Get languages - var language = config.getValue('language'); - - // Create API context - var context = Api.encodeGlobal(output); - - - /** - * Check if a file exists - * @param {String} fileName - * @return {Boolean} - */ - function fileExists(fileName) { - if (!fileName) { - return false; - } - - var filePath = PathUtils.resolveInRoot(outputFolder, fileName); - return fs.existsSync(filePath); - } - - /** - * Return an article by its path - * @param {String} filePath - * @return {Object|undefined} - */ - function getArticleByPath(filePath) { - var article = summary.getByPath(filePath); - if (!article) return undefined; - - return JSONUtils.encodeSummaryArticle(article); - } - - /** - * Return a page by its path - * @param {String} filePath - * @return {Object|undefined} - */ - function getPageByPath(filePath) { - var page = output.getPage(filePath); - if (!page) return undefined; - - return JSONUtils.encodePage(page, summary); - } - - return TemplateEngine.create({ - loader: loader, - - context: context, - - globals: { - getArticleByPath: getArticleByPath, - getPageByPath: getPageByPath, - fileExists: fileExists - }, - - filters: defaultFilters.merge({ - /** - * Translate a sentence - */ - t: function t(s) { - return i18n.t(language, s); - }, - - /** - * Resolve an absolute file path into a - * relative path. - * it also resolve pages - */ - resolveFile: function(filePath) { - filePath = resolveFileToURL(output, filePath); - return LocationUtils.relativeForFile(currentFile, filePath); - }, - - resolveAsset: function(filePath) { - filePath = LocationUtils.toAbsolute(filePath, '', ''); - filePath = path.join('gitbook', filePath); - filePath = LocationUtils.relativeForFile(currentFile, filePath); - - // Use assets from parent if language book - if (book.isLanguageBook()) { - filePath = path.join('../', filePath); - } - - return LocationUtils.normalize(filePath); - }, - - - fileExists: deprecate.method(book, 'fileExists', fileExists, 'Filter "fileExists" is deprecated, use "fileExists(filename)" '), - getArticleByPath: deprecate.method(book, 'getArticleByPath', fileExists, 'Filter "getArticleByPath" is deprecated, use "getArticleByPath(filename)" '), - - contentURL: function(filePath) { - return fileToURL(output, filePath); - } - }), - - extensions: { - 'DoExtension': new DoExtension() - } - }); -} - -module.exports = createTemplateEngine; diff --git a/lib/output/website/index.js b/lib/output/website/index.js deleted file mode 100644 index 7818a28..0000000 --- a/lib/output/website/index.js +++ /dev/null @@ -1,11 +0,0 @@ - -module.exports = { - name: 'website', - State: require('./state'), - Options: require('./options'), - onInit: require('./onInit'), - onFinish: require('./onFinish'), - onPage: require('./onPage'), - onAsset: require('./onAsset'), - createTemplateEngine: require('./createTemplateEngine') -}; diff --git a/lib/output/website/listSearchPaths.js b/lib/output/website/listSearchPaths.js deleted file mode 100644 index c45f39c..0000000 --- a/lib/output/website/listSearchPaths.js +++ /dev/null @@ -1,23 +0,0 @@ - -/** - List search paths for templates / i18n, etc - - @param {Output} output - @return {List<String>} -*/ -function listSearchPaths(output) { - var book = output.getBook(); - var plugins = output.getPlugins(); - - var searchPaths = plugins - .valueSeq() - .map(function(plugin) { - return plugin.getPath(); - }) - .toList(); - - return searchPaths.unshift(book.getContentRoot()); -} - - -module.exports = listSearchPaths; diff --git a/lib/output/website/onAsset.js b/lib/output/website/onAsset.js deleted file mode 100644 index 69dfc4f..0000000 --- a/lib/output/website/onAsset.js +++ /dev/null @@ -1,28 +0,0 @@ -var path = require('path'); -var fs = require('../../utils/fs'); - -/** - Copy an asset to the output folder - - @param {Output} output - @param {Page} page -*/ -function onAsset(output, asset) { - var book = output.getBook(); - var options = output.getOptions(); - var bookFS = book.getContentFS(); - - var outputFolder = options.get('root'); - var outputPath = path.resolve(outputFolder, asset); - - return fs.ensureFile(outputPath) - .then(function() { - return bookFS.readAsStream(asset) - .then(function(stream) { - return fs.writeStream(outputPath, stream); - }); - }) - .thenResolve(output); -} - -module.exports = onAsset; diff --git a/lib/output/website/onFinish.js b/lib/output/website/onFinish.js deleted file mode 100644 index 5267458..0000000 --- a/lib/output/website/onFinish.js +++ /dev/null @@ -1,35 +0,0 @@ -var Promise = require('../../utils/promise'); -var JSONUtils = require('../../json'); -var Templating = require('../../templating'); -var writeFile = require('../helper/writeFile'); -var createTemplateEngine = require('./createTemplateEngine'); - -/** - Finish the generation, write the languages index - - @param {Output} - @return {Output} -*/ -function onFinish(output) { - var book = output.getBook(); - var options = output.getOptions(); - var prefix = options.get('prefix'); - - if (!book.isMultilingual()) { - return Promise(output); - } - - var filePath = 'index.html'; - var engine = createTemplateEngine(output, filePath); - var context = JSONUtils.encodeOutput(output); - - // Render the theme - return Templating.renderFile(engine, prefix + '/languages.html', context) - - // Write it to the disk - .then(function(tplOut) { - return writeFile(output, filePath, tplOut.getContent()); - }); -} - -module.exports = onFinish; diff --git a/lib/output/website/onInit.js b/lib/output/website/onInit.js deleted file mode 100644 index 3465eef..0000000 --- a/lib/output/website/onInit.js +++ /dev/null @@ -1,20 +0,0 @@ -var Promise = require('../../utils/promise'); - -var copyPluginAssets = require('./copyPluginAssets'); -var prepareI18n = require('./prepareI18n'); -var prepareResources = require('./prepareResources'); - -/** - Initialize the generator - - @param {Output} - @return {Output} -*/ -function onInit(output) { - return Promise(output) - .then(prepareI18n) - .then(prepareResources) - .then(copyPluginAssets); -} - -module.exports = onInit; diff --git a/lib/output/website/onPage.js b/lib/output/website/onPage.js deleted file mode 100644 index 5fb40a7..0000000 --- a/lib/output/website/onPage.js +++ /dev/null @@ -1,76 +0,0 @@ -var path = require('path'); -var omit = require('omit-keys'); - -var Templating = require('../../templating'); -var Plugins = require('../../plugins'); -var JSONUtils = require('../../json'); -var LocationUtils = require('../../utils/location'); -var Modifiers = require('../modifiers'); -var writeFile = require('../helper/writeFile'); -var getModifiers = require('../getModifiers'); -var createTemplateEngine = require('./createTemplateEngine'); -var fileToOutput = require('../helper/fileToOutput'); - -/** - * Write a page as a json file - * - * @param {Output} output - * @param {Page} page - */ -function onPage(output, page) { - var options = output.getOptions(); - var prefix = options.get('prefix'); - - var file = page.getFile(); - - var book = output.getBook(); - var plugins = output.getPlugins(); - var state = output.getState(); - var resources = state.getResources(); - - var engine = createTemplateEngine(output, page.getPath()); - - // Output file path - var filePath = fileToOutput(output, file.getPath()); - - // Calcul relative path to the root - var outputDirName = path.dirname(filePath); - var basePath = LocationUtils.normalize(path.relative(outputDirName, './')); - - return Modifiers.modifyHTML(page, getModifiers(output, page)) - .then(function(resultPage) { - // Generate the context - var context = JSONUtils.encodeOutputWithPage(output, resultPage); - context.plugins = { - resources: Plugins.listResources(plugins, resources).toJS() - }; - - context.template = { - getJSContext: function() { - return { - page: omit(context.page, 'content'), - config: context.config, - file: context.file, - gitbook: context.gitbook, - basePath: basePath, - book: { - language: book.getLanguage() - } - }; - } - }; - - // We should probabbly move it to "template" or a "site" namespace - context.basePath = basePath; - - // Render the theme - return Templating.renderFile(engine, prefix + '/page.html', context) - - // Write it to the disk - .then(function(tplOut) { - return writeFile(output, filePath, tplOut.getContent()); - }); - }); -} - -module.exports = onPage; diff --git a/lib/output/website/options.js b/lib/output/website/options.js deleted file mode 100644 index ac9cdad..0000000 --- a/lib/output/website/options.js +++ /dev/null @@ -1,14 +0,0 @@ -var Immutable = require('immutable'); - -var Options = Immutable.Record({ - // Root folder for the output - root: String(), - - // Prefix for generation - prefix: String('website'), - - // Use directory index url instead of "index.html" - directoryIndex: Boolean(true) -}); - -module.exports = Options; diff --git a/lib/output/website/prepareI18n.js b/lib/output/website/prepareI18n.js deleted file mode 100644 index cedd3b9..0000000 --- a/lib/output/website/prepareI18n.js +++ /dev/null @@ -1,30 +0,0 @@ -var path = require('path'); - -var fs = require('../../utils/fs'); -var Promise = require('../../utils/promise'); -var listSearchPaths = require('./listSearchPaths'); - -/** - * Prepare i18n, load translations from plugins and book - * - * @param {Output} - * @return {Promise<Output>} - */ -function prepareI18n(output) { - var state = output.getState(); - var i18n = state.getI18n(); - var searchPaths = listSearchPaths(output); - - searchPaths - .reverse() - .forEach(function(searchPath) { - var i18nRoot = path.resolve(searchPath, '_i18n'); - - if (!fs.existsSync(i18nRoot)) return; - i18n.load(i18nRoot); - }); - - return Promise(output); -} - -module.exports = prepareI18n; diff --git a/lib/output/website/prepareResources.js b/lib/output/website/prepareResources.js deleted file mode 100644 index 4e6835d..0000000 --- a/lib/output/website/prepareResources.js +++ /dev/null @@ -1,54 +0,0 @@ -var is = require('is'); -var Immutable = require('immutable'); -var Promise = require('../../utils/promise'); - -var Api = require('../../api'); - -/** - Prepare plugins resources, add all output corresponding type resources - - @param {Output} - @return {Promise<Output>} -*/ -function prepareResources(output) { - var plugins = output.getPlugins(); - var options = output.getOptions(); - var type = options.get('prefix'); - var state = output.getState(); - var context = Api.encodeGlobal(output); - - var result = Immutable.Map(); - - return Promise.forEach(plugins, function(plugin) { - var pluginResources = plugin.getResources(type); - - return Promise() - .then(function() { - // Apply resources if is a function - if (is.fn(pluginResources)) { - return Promise() - .then(pluginResources.bind(context)); - } - else { - return pluginResources; - } - }) - .then(function(resources) { - result = result.set(plugin.getName(), Immutable.Map(resources)); - }); - }) - .then(function() { - // Set output resources - state = state.merge({ - resources: result - }); - - output = output.merge({ - state: state - }); - - return output; - }); -} - -module.exports = prepareResources;
\ No newline at end of file diff --git a/lib/output/website/state.js b/lib/output/website/state.js deleted file mode 100644 index cb8f750..0000000 --- a/lib/output/website/state.js +++ /dev/null @@ -1,19 +0,0 @@ -var I18n = require('i18n-t'); -var Immutable = require('immutable'); - -var GeneratorState = Immutable.Record({ - i18n: I18n(), - - // List of plugins' resources - resources: Immutable.Map() -}); - -GeneratorState.prototype.getI18n = function() { - return this.get('i18n'); -}; - -GeneratorState.prototype.getResources = function() { - return this.get('resources'); -}; - -module.exports = GeneratorState; diff --git a/lib/parse/__tests__/listAssets.js b/lib/parse/__tests__/listAssets.js deleted file mode 100644 index 4c5b0a0..0000000 --- a/lib/parse/__tests__/listAssets.js +++ /dev/null @@ -1,29 +0,0 @@ -var Immutable = require('immutable'); - -var Book = require('../../models/book'); -var createMockFS = require('../../fs/mock'); -var listAssets = require('../listAssets'); -var parseGlossary = require('../parseGlossary'); - -describe('listAssets', function() { - it('should not list glossary as asset', function() { - var fs = createMockFS({ - 'GLOSSARY.md': '# Glossary\n\n## Hello\nDescription for hello', - 'assetFile.js': '', - 'assets': { - 'file.js': '' - } - }); - var book = Book.createForFS(fs); - - return parseGlossary(book) - .then(function(resultBook) { - return listAssets(resultBook, Immutable.Map()); - }) - .then(function(assets) { - expect(assets.size).toBe(2); - expect(assets.includes('assetFile.js')); - expect(assets.includes('assets/file.js')); - }); - }); -}); diff --git a/lib/parse/__tests__/parseBook.js b/lib/parse/__tests__/parseBook.js deleted file mode 100644 index b1236c9..0000000 --- a/lib/parse/__tests__/parseBook.js +++ /dev/null @@ -1,90 +0,0 @@ -var Book = require('../../models/book'); -var createMockFS = require('../../fs/mock'); - -describe('parseBook', function() { - var parseBook = require('../parseBook'); - - it('should parse multilingual book', function() { - var fs = createMockFS({ - 'LANGS.md': '# Languages\n\n* [en](en)\n* [fr](fr)', - 'en': { - 'README.md': 'Hello' - }, - 'fr': { - 'README.md': 'Bonjour' - } - }); - var book = Book.createForFS(fs); - - return parseBook(book) - .then(function(resultBook) { - var languages = resultBook.getLanguages(); - var books = resultBook.getBooks(); - - expect(resultBook.isMultilingual()).toBe(true); - expect(languages.getList().size).toBe(2); - expect(books.size).toBe(2); - }); - }); - - it('should extend configuration for multilingual book', function() { - var fs = createMockFS({ - 'LANGS.md': '# Languages\n\n* [en](en)\n* [fr](fr)', - 'book.json': '{ "title": "Test", "author": "GitBook" }', - 'en': { - 'README.md': 'Hello', - 'book.json': '{ "title": "Test EN" }' - }, - 'fr': { - 'README.md': 'Bonjour' - } - }); - var book = Book.createForFS(fs); - - return parseBook(book) - .then(function(resultBook) { - var books = resultBook.getBooks(); - - expect(resultBook.isMultilingual()).toBe(true); - expect(books.size).toBe(2); - - var en = books.get('en'); - var fr = books.get('fr'); - - var enConfig = en.getConfig(); - var frConfig = fr.getConfig(); - - expect(enConfig.getValue('title')).toBe('Test EN'); - expect(enConfig.getValue('author')).toBe('GitBook'); - - expect(frConfig.getValue('title')).toBe('Test'); - expect(frConfig.getValue('author')).toBe('GitBook'); - }); - }); - - it('should parse book in a directory', function() { - var fs = createMockFS({ - 'book.json': JSON.stringify({ - root: './test' - }), - 'test': { - 'README.md': 'Hello World', - 'SUMMARY.md': '# Summary\n\n* [Page](page.md)\n', - 'page.md': 'Page' - } - }); - var book = Book.createForFS(fs); - - return parseBook(book) - .then(function(resultBook) { - var readme = resultBook.getReadme(); - var summary = resultBook.getSummary(); - var articles = summary.getArticlesAsList(); - - expect(summary.getFile().exists()).toBe(true); - expect(readme.getFile().exists()).toBe(true); - expect(articles.size).toBe(2); - }); - }); - -}); diff --git a/lib/parse/__tests__/parseGlossary.js b/lib/parse/__tests__/parseGlossary.js deleted file mode 100644 index 9069af6..0000000 --- a/lib/parse/__tests__/parseGlossary.js +++ /dev/null @@ -1,36 +0,0 @@ -var Book = require('../../models/book'); -var createMockFS = require('../../fs/mock'); - -describe('parseGlossary', function() { - var parseGlossary = require('../parseGlossary'); - - it('should parse glossary if exists', function() { - var fs = createMockFS({ - 'GLOSSARY.md': '# Glossary\n\n## Hello\nDescription for hello' - }); - var book = Book.createForFS(fs); - - return parseGlossary(book) - .then(function(resultBook) { - var glossary = resultBook.getGlossary(); - var file = glossary.getFile(); - var entries = glossary.getEntries(); - - expect(file.exists()).toBeTruthy(); - expect(entries.size).toBe(1); - }); - }); - - it('should not fail if doesn\'t exist', function() { - var fs = createMockFS({}); - var book = Book.createForFS(fs); - - return parseGlossary(book) - .then(function(resultBook) { - var glossary = resultBook.getGlossary(); - var file = glossary.getFile(); - - expect(file.exists()).toBeFalsy(); - }); - }); -}); diff --git a/lib/parse/__tests__/parseIgnore.js b/lib/parse/__tests__/parseIgnore.js deleted file mode 100644 index 54e7dae..0000000 --- a/lib/parse/__tests__/parseIgnore.js +++ /dev/null @@ -1,40 +0,0 @@ -var Book = require('../../models/book'); -var createMockFS = require('../../fs/mock'); - -describe('parseIgnore', function() { - var parseIgnore = require('../parseIgnore'); - var fs = createMockFS({ - '.ignore': 'test-1.js', - '.gitignore': 'test-2.js\ntest-3.js', - '.bookignore': '!test-3.js', - 'test-1.js': '1', - 'test-2.js': '2', - 'test-3.js': '3' - }); - - function getBook() { - var book = Book.createForFS(fs); - return parseIgnore(book); - } - - it('should load rules from .ignore', function() { - return getBook() - .then(function(book) { - expect(book.isFileIgnored('test-1.js')).toBeTruthy(); - }); - }); - - it('should load rules from .gitignore', function() { - return getBook() - .then(function(book) { - expect(book.isFileIgnored('test-2.js')).toBeTruthy(); - }); - }); - - it('should load rules from .bookignore', function() { - return getBook() - .then(function(book) { - expect(book.isFileIgnored('test-3.js')).toBeFalsy(); - }); - }); -}); diff --git a/lib/parse/__tests__/parsePageFromString.js b/lib/parse/__tests__/parsePageFromString.js deleted file mode 100644 index 2911fa3..0000000 --- a/lib/parse/__tests__/parsePageFromString.js +++ /dev/null @@ -1,37 +0,0 @@ -var parsePageFromString = require('../parsePageFromString'); -var Page = require('../../models/page'); - -describe('parsePageFromString', function() { - var page = new Page(); - - it('should parse YAML frontmatter', function() { - var CONTENT = '---\nhello: true\nworld: "cool"\n---\n# Hello World\n'; - var newPage = parsePageFromString(page, CONTENT); - - expect(newPage.getDir()).toBe('ltr'); - expect(newPage.getContent()).toBe('# Hello World\n'); - - var attrs = newPage.getAttributes(); - expect(attrs.size).toBe(2); - expect(attrs.get('hello')).toBe(true); - expect(attrs.get('world')).toBe('cool'); - }); - - it('should parse text direction (english)', function() { - var CONTENT = 'Hello World'; - var newPage = parsePageFromString(page, CONTENT); - - expect(newPage.getDir()).toBe('ltr'); - expect(newPage.getContent()).toBe('Hello World'); - expect(newPage.getAttributes().size).toBe(0); - }); - - it('should parse text direction (arab)', function() { - var CONTENT = 'Ù…Ø±ØØ¨Ø§ بالعالم'; - var newPage = parsePageFromString(page, CONTENT); - - expect(newPage.getDir()).toBe('rtl'); - expect(newPage.getContent()).toBe('Ù…Ø±ØØ¨Ø§ بالعالم'); - expect(newPage.getAttributes().size).toBe(0); - }); -}); diff --git a/lib/parse/__tests__/parseReadme.js b/lib/parse/__tests__/parseReadme.js deleted file mode 100644 index 4270ea3..0000000 --- a/lib/parse/__tests__/parseReadme.js +++ /dev/null @@ -1,36 +0,0 @@ -var Promise = require('../../utils/promise'); -var Book = require('../../models/book'); -var createMockFS = require('../../fs/mock'); - -describe('parseReadme', function() { - var parseReadme = require('../parseReadme'); - - it('should parse summary if exists', function() { - var fs = createMockFS({ - 'README.md': '# Hello\n\nAnd here is the description.' - }); - var book = Book.createForFS(fs); - - return parseReadme(book) - .then(function(resultBook) { - var readme = resultBook.getReadme(); - var file = readme.getFile(); - - expect(file.exists()).toBeTruthy(); - expect(readme.getTitle()).toBe('Hello'); - expect(readme.getDescription()).toBe('And here is the description.'); - }); - }); - - it('should fail if doesn\'t exist', function() { - var fs = createMockFS({}); - var book = Book.createForFS(fs); - - return parseReadme(book) - .then(function(resultBook) { - throw new Error('It should have fail'); - }, function() { - return Promise(); - }); - }); -}); diff --git a/lib/parse/__tests__/parseSummary.js b/lib/parse/__tests__/parseSummary.js deleted file mode 100644 index 55a445e..0000000 --- a/lib/parse/__tests__/parseSummary.js +++ /dev/null @@ -1,34 +0,0 @@ -var Book = require('../../models/book'); -var createMockFS = require('../../fs/mock'); - -describe('parseSummary', function() { - var parseSummary = require('../parseSummary'); - - it('should parse summary if exists', function() { - var fs = createMockFS({ - 'SUMMARY.md': '# Summary\n\n* [Hello](hello.md)' - }); - var book = Book.createForFS(fs); - - return parseSummary(book) - .then(function(resultBook) { - var summary = resultBook.getSummary(); - var file = summary.getFile(); - - expect(file.exists()).toBeTruthy(); - }); - }); - - it('should not fail if doesn\'t exist', function() { - var fs = createMockFS({}); - var book = Book.createForFS(fs); - - return parseSummary(book) - .then(function(resultBook) { - var summary = resultBook.getSummary(); - var file = summary.getFile(); - - expect(file.exists()).toBeFalsy(); - }); - }); -}); diff --git a/lib/parse/findParsableFile.js b/lib/parse/findParsableFile.js deleted file mode 100644 index 51e2dd0..0000000 --- a/lib/parse/findParsableFile.js +++ /dev/null @@ -1,36 +0,0 @@ -var path = require('path'); - -var Promise = require('../utils/promise'); -var parsers = require('../parsers'); - -/** - Find a file parsable (Markdown or AsciiDoc) in a book - - @param {Book} book - @param {String} filename - @return {Promise<File | Undefined>} -*/ -function findParsableFile(book, filename) { - var fs = book.getContentFS(); - var ext = path.extname(filename); - var basename = path.basename(filename, ext); - var basedir = path.dirname(filename); - - // Ordered list of extensions to test - var exts = parsers.extensions; - - return Promise.some(exts, function(ext) { - var filepath = basename + ext; - - return fs.findFile(basedir, filepath) - .then(function(found) { - if (!found || book.isContentFileIgnored(found)) { - return undefined; - } - - return fs.statFile(found); - }); - }); -} - -module.exports = findParsableFile; diff --git a/lib/parse/index.js b/lib/parse/index.js deleted file mode 100644 index 1f73946..0000000 --- a/lib/parse/index.js +++ /dev/null @@ -1,15 +0,0 @@ - -module.exports = { - parseBook: require('./parseBook'), - parseSummary: require('./parseSummary'), - parseGlossary: require('./parseGlossary'), - parseReadme: require('./parseReadme'), - parseConfig: require('./parseConfig'), - parsePagesList: require('./parsePagesList'), - parseIgnore: require('./parseIgnore'), - listAssets: require('./listAssets'), - parseLanguages: require('./parseLanguages'), - parsePage: require('./parsePage'), - parsePageFromString: require('./parsePageFromString'), - lookupStructureFile: require('./lookupStructureFile') -}; diff --git a/lib/parse/listAssets.js b/lib/parse/listAssets.js deleted file mode 100644 index d83d8fd..0000000 --- a/lib/parse/listAssets.js +++ /dev/null @@ -1,43 +0,0 @@ -var timing = require('../utils/timing'); - -/** - List all assets in a book - Assets are file not ignored and not a page - - @param {Book} book - @param {List<String>} pages - @param -*/ -function listAssets(book, pages) { - var fs = book.getContentFS(); - - var summary = book.getSummary(); - var summaryFile = summary.getFile().getPath(); - - var glossary = book.getGlossary(); - var glossaryFile = glossary.getFile().getPath(); - - var langs = book.getLanguages(); - var langsFile = langs.getFile().getPath(); - - var config = book.getConfig(); - var configFile = config.getFile().getPath(); - - function filterFile(file) { - return !( - file === summaryFile || - file === glossaryFile || - file === langsFile || - file === configFile || - book.isContentFileIgnored(file) || - pages.has(file) - ); - } - - return timing.measure( - 'parse.listAssets', - fs.listAllFiles('.', filterFile) - ); -} - -module.exports = listAssets; diff --git a/lib/parse/lookupStructureFile.js b/lib/parse/lookupStructureFile.js deleted file mode 100644 index 36b37f8..0000000 --- a/lib/parse/lookupStructureFile.js +++ /dev/null @@ -1,20 +0,0 @@ -var findParsableFile = require('./findParsableFile'); - -/** - Lookup a structure file (ex: SUMMARY.md, GLOSSARY.md) in a book. Uses - book's config to find it. - - @param {Book} book - @param {String} type: one of ["glossary", "readme", "summary", "langs"] - @return {Promise<File | Undefined>} The path of the file found, relative - to the book content root. -*/ -function lookupStructureFile(book, type) { - var config = book.getConfig(); - - var fileToSearch = config.getValue(['structure', type]); - - return findParsableFile(book, fileToSearch); -} - -module.exports = lookupStructureFile; diff --git a/lib/parse/parseBook.js b/lib/parse/parseBook.js deleted file mode 100644 index a92f39e..0000000 --- a/lib/parse/parseBook.js +++ /dev/null @@ -1,77 +0,0 @@ -var Promise = require('../utils/promise'); -var timing = require('../utils/timing'); -var Book = require('../models/book'); - -var parseIgnore = require('./parseIgnore'); -var parseConfig = require('./parseConfig'); -var parseGlossary = require('./parseGlossary'); -var parseSummary = require('./parseSummary'); -var parseReadme = require('./parseReadme'); -var parseLanguages = require('./parseLanguages'); - -/** - Parse content of a book - - @param {Book} book - @return {Promise<Book>} -*/ -function parseBookContent(book) { - return Promise(book) - .then(parseReadme) - .then(parseSummary) - .then(parseGlossary); -} - -/** - Parse a multilingual book - - @param {Book} book - @return {Promise<Book>} -*/ -function parseMultilingualBook(book) { - var languages = book.getLanguages(); - var langList = languages.getList(); - - return Promise.reduce(langList, function(currentBook, lang) { - var langID = lang.getID(); - var child = Book.createFromParent(currentBook, langID); - var ignore = currentBook.getIgnore(); - - return Promise(child) - .then(parseConfig) - .then(parseBookContent) - .then(function(result) { - // Ignore content of this book when generating parent book - ignore = ignore.add(langID + '/**'); - currentBook = currentBook.set('ignore', ignore); - - return currentBook.addLanguageBook(langID, result); - }); - }, book); -} - - -/** - Parse a whole book from a filesystem - - @param {Book} book - @return {Promise<Book>} -*/ -function parseBook(book) { - return timing.measure( - 'parse.book', - Promise(book) - .then(parseIgnore) - .then(parseConfig) - .then(parseLanguages) - .then(function(resultBook) { - if (resultBook.isMultilingual()) { - return parseMultilingualBook(resultBook); - } else { - return parseBookContent(resultBook); - } - }) - ); -} - -module.exports = parseBook; diff --git a/lib/parse/parseConfig.js b/lib/parse/parseConfig.js deleted file mode 100644 index a411af8..0000000 --- a/lib/parse/parseConfig.js +++ /dev/null @@ -1,55 +0,0 @@ -var Promise = require('../utils/promise'); - -var validateConfig = require('./validateConfig'); -var CONFIG_FILES = require('../constants/configFiles'); - -/** - Parse configuration from "book.json" or "book.js" - - @param {Book} book - @return {Promise<Book>} -*/ -function parseConfig(book) { - var fs = book.getFS(); - var config = book.getConfig(); - - return Promise.some(CONFIG_FILES, function(filename) { - // Is this file ignored? - if (book.isFileIgnored(filename)) { - return; - } - - // Try loading it - return fs.loadAsObject(filename) - .then(function(cfg) { - return fs.statFile(filename) - .then(function(file) { - return { - file: file, - values: cfg - }; - }); - }) - .fail(function(err) { - if (err.code != 'MODULE_NOT_FOUND') throw(err); - else return Promise(false); - }); - }) - - .then(function(result) { - var values = result? result.values : {}; - values = validateConfig(values); - - // Set the file - if (result.file) { - config = config.setFile(result.file); - } - - // Merge with old values - config = config.mergeValues(values); - - return book.setConfig(config); - }); -} - -module.exports = parseConfig; diff --git a/lib/parse/parseGlossary.js b/lib/parse/parseGlossary.js deleted file mode 100644 index a96e5fc..0000000 --- a/lib/parse/parseGlossary.js +++ /dev/null @@ -1,26 +0,0 @@ -var parseStructureFile = require('./parseStructureFile'); -var Glossary = require('../models/glossary'); - -/** - Parse glossary - - @param {Book} book - @return {Promise<Book>} -*/ -function parseGlossary(book) { - var logger = book.getLogger(); - - return parseStructureFile(book, 'glossary') - .spread(function(file, entries) { - if (!file) { - return book; - } - - logger.debug.ln('glossary index file found at', file.getPath()); - - var glossary = Glossary.createFromEntries(file, entries); - return book.set('glossary', glossary); - }); -} - -module.exports = parseGlossary; diff --git a/lib/parse/parseIgnore.js b/lib/parse/parseIgnore.js deleted file mode 100644 index 84d8c33..0000000 --- a/lib/parse/parseIgnore.js +++ /dev/null @@ -1,51 +0,0 @@ -var Promise = require('../utils/promise'); -var IGNORE_FILES = require('../constants/ignoreFiles'); - -var DEFAULT_IGNORES = [ - // Skip Git stuff - '.git/', - - // Skip OS X meta data - '.DS_Store', - - // Skip stuff installed by plugins - 'node_modules', - - // Skip book outputs - '_book', - - // Ignore files in the templates folder - '_layouts' -]; - -/** - Parse ignore files - - @param {Book} - @return {Book} -*/ -function parseIgnore(book) { - if (book.isLanguageBook()) { - return Promise.reject(new Error('Ignore files could be parsed for language books')); - } - - var fs = book.getFS(); - var ignore = book.getIgnore(); - - ignore = ignore.add(DEFAULT_IGNORES); - - return Promise.serie(IGNORE_FILES, function(filename) { - return fs.readAsString(filename) - .then(function(content) { - ignore = ignore.add(content.toString().split(/\r?\n/)); - }, function(err) { - return Promise(); - }); - }) - - .then(function() { - return book.setIgnore(ignore); - }); -} - -module.exports = parseIgnore; diff --git a/lib/parse/parseLanguages.js b/lib/parse/parseLanguages.js deleted file mode 100644 index 346f3a3..0000000 --- a/lib/parse/parseLanguages.js +++ /dev/null @@ -1,28 +0,0 @@ -var parseStructureFile = require('./parseStructureFile'); -var Languages = require('../models/languages'); - -/** - Parse languages list from book - - @param {Book} book - @return {Promise<Book>} -*/ -function parseLanguages(book) { - var logger = book.getLogger(); - - return parseStructureFile(book, 'langs') - .spread(function(file, result) { - if (!file) { - return book; - } - - var languages = Languages.createFromList(file, result); - - logger.debug.ln('languages index file found at', file.getPath()); - logger.info.ln('parsing multilingual book, with', languages.getList().size, 'languages'); - - return book.set('languages', languages); - }); -} - -module.exports = parseLanguages; diff --git a/lib/parse/parsePage.js b/lib/parse/parsePage.js deleted file mode 100644 index fdc56a3..0000000 --- a/lib/parse/parsePage.js +++ /dev/null @@ -1,21 +0,0 @@ -var parsePageFromString = require('./parsePageFromString'); - -/** - * Parse a page, read its content and parse the YAMl header - * - * @param {Book} book - * @param {Page} page - * @return {Promise<Page>} - */ -function parsePage(book, page) { - var fs = book.getContentFS(); - var file = page.getFile(); - - return fs.readAsString(file.getPath()) - .then(function(content) { - return parsePageFromString(page, content); - }); -} - - -module.exports = parsePage; diff --git a/lib/parse/parsePageFromString.js b/lib/parse/parsePageFromString.js deleted file mode 100644 index e64664b..0000000 --- a/lib/parse/parsePageFromString.js +++ /dev/null @@ -1,23 +0,0 @@ -var Immutable = require('immutable'); -var fm = require('front-matter'); -var direction = require('direction'); - -/** - * Parse a page, its content and the YAMl header - * - * @param {Page} page - * @return {Page} - */ -function parsePageFromString(page, content) { - // Parse page YAML - var parsed = fm(content); - - return page.merge({ - content: parsed.body, - attributes: Immutable.fromJS(parsed.attributes), - dir: direction(parsed.body) - }); -} - - -module.exports = parsePageFromString; diff --git a/lib/parse/parsePagesList.js b/lib/parse/parsePagesList.js deleted file mode 100644 index fa15a9d..0000000 --- a/lib/parse/parsePagesList.js +++ /dev/null @@ -1,97 +0,0 @@ -var Immutable = require('immutable'); - -var timing = require('../utils/timing'); -var Page = require('../models/page'); -var walkSummary = require('./walkSummary'); -var parsePage = require('./parsePage'); - - -/** - Parse a page from a path - - @param {Book} book - @param {String} filePath - @return {Page?} -*/ -function parseFilePage(book, filePath) { - var fs = book.getContentFS(); - - return fs.statFile(filePath) - .then( - function(file) { - var page = Page.createForFile(file); - return parsePage(book, page); - }, - function(err) { - // file doesn't exist - return null; - } - ) - .fail(function(err) { - var logger = book.getLogger(); - logger.error.ln('error while parsing page "' + filePath + '":'); - throw err; - }); -} - - -/** - Parse all pages from a book as an OrderedMap - - @param {Book} book - @return {Promise<OrderedMap<Page>>} -*/ -function parsePagesList(book) { - var summary = book.getSummary(); - var glossary = book.getGlossary(); - var map = Immutable.OrderedMap(); - - // Parse pages from summary - return timing.measure( - 'parse.listPages', - walkSummary(summary, function(article) { - if (!article.isPage()) return; - - var filepath = article.getPath(); - - // Is the page ignored? - if (book.isContentFileIgnored(filepath)) return; - - return parseFilePage(book, filepath) - .then(function(page) { - // file doesn't exist - if (!page) { - return; - } - - map = map.set(filepath, page); - }); - }) - ) - - // Parse glossary - .then(function() { - var file = glossary.getFile(); - - if (!file.exists()) { - return; - } - - return parseFilePage(book, file.getPath()) - .then(function(page) { - // file doesn't exist - if (!page) { - return; - } - - map = map.set(file.getPath(), page); - }); - }) - - .then(function() { - return map; - }); -} - - -module.exports = parsePagesList; diff --git a/lib/parse/parseReadme.js b/lib/parse/parseReadme.js deleted file mode 100644 index a2ede77..0000000 --- a/lib/parse/parseReadme.js +++ /dev/null @@ -1,28 +0,0 @@ -var parseStructureFile = require('./parseStructureFile'); -var Readme = require('../models/readme'); - -var error = require('../utils/error'); - -/** - Parse readme from book - - @param {Book} book - @return {Promise<Book>} -*/ -function parseReadme(book) { - var logger = book.getLogger(); - - return parseStructureFile(book, 'readme') - .spread(function(file, result) { - if (!file) { - throw new error.FileNotFoundError({ filename: 'README' }); - } - - logger.debug.ln('readme found at', file.getPath()); - - var readme = Readme.create(file, result); - return book.set('readme', readme); - }); -} - -module.exports = parseReadme; diff --git a/lib/parse/parseStructureFile.js b/lib/parse/parseStructureFile.js deleted file mode 100644 index 718f731..0000000 --- a/lib/parse/parseStructureFile.js +++ /dev/null @@ -1,67 +0,0 @@ -var Promise = require('../utils/promise'); -var error = require('../utils/error'); -var lookupStructureFile = require('./lookupStructureFile'); - -/** - Parse a ParsableFile using a specific method - - @param {FS} fs - @param {ParsableFile} file - @param {String} type - @return {Promise<Array<String, List|Map>>} -*/ -function parseFile(fs, file, type) { - var filepath = file.getPath(); - var parser = file.getParser(); - - if (!parser) { - return Promise.reject( - error.FileNotParsableError({ - filename: filepath - }) - ); - } - - return fs.readAsString(filepath) - .then(function(content) { - if (type === 'readme') { - return parser.parseReadme(content); - } else if (type === 'glossary') { - return parser.parseGlossary(content); - } else if (type === 'summary') { - return parser.parseSummary(content); - } else if (type === 'langs') { - return parser.parseLanguages(content); - } else { - throw new Error('Parsing invalid type "' + type + '"'); - } - }) - .then(function(result) { - return [ - file, - result - ]; - }); -} - - -/** - Parse a structure file (ex: SUMMARY.md, GLOSSARY.md). - It uses the configuration to find the specified file. - - @param {Book} book - @param {String} type: one of ["glossary", "readme", "summary"] - @return {Promise<List|Map>} -*/ -function parseStructureFile(book, type) { - var fs = book.getContentFS(); - - return lookupStructureFile(book, type) - .then(function(file) { - if (!file) return [undefined, undefined]; - - return parseFile(fs, file, type); - }); -} - -module.exports = parseStructureFile; diff --git a/lib/parse/parseSummary.js b/lib/parse/parseSummary.js deleted file mode 100644 index 2c1e3b3..0000000 --- a/lib/parse/parseSummary.js +++ /dev/null @@ -1,44 +0,0 @@ -var parseStructureFile = require('./parseStructureFile'); -var Summary = require('../models/summary'); -var SummaryModifier = require('../modifiers').Summary; - -/** - Parse summary in a book, the summary can only be parsed - if the readme as be detected before. - - @param {Book} book - @return {Promise<Book>} -*/ -function parseSummary(book) { - var readme = book.getReadme(); - var logger = book.getLogger(); - var readmeFile = readme.getFile(); - - return parseStructureFile(book, 'summary') - .spread(function(file, result) { - var summary; - - if (!file) { - logger.warn.ln('no summary file in this book'); - summary = Summary(); - } else { - logger.debug.ln('summary file found at', file.getPath()); - summary = Summary.createFromParts(file, result.parts); - } - - // Insert readme as first entry if not in SUMMARY.md - var readmeArticle = summary.getByPath(readmeFile.getPath()); - - if (readmeFile.exists() && !readmeArticle) { - summary = SummaryModifier.unshiftArticle(summary, { - title: 'Introduction', - ref: readmeFile.getPath() - }); - } - - // Set new summary - return book.setSummary(summary); - }); -} - -module.exports = parseSummary; diff --git a/lib/parse/validateConfig.js b/lib/parse/validateConfig.js deleted file mode 100644 index 21294ac..0000000 --- a/lib/parse/validateConfig.js +++ /dev/null @@ -1,31 +0,0 @@ -var jsonschema = require('jsonschema'); -var jsonSchemaDefaults = require('json-schema-defaults'); - -var schema = require('../constants/configSchema'); -var error = require('../utils/error'); -var mergeDefaults = require('../utils/mergeDefaults'); - -/** - Validate a book.json content - And return a mix with the default value - - @param {Object} bookJson - @return {Object} -*/ -function validateConfig(bookJson) { - var v = new jsonschema.Validator(); - var result = v.validate(bookJson, schema, { - propertyName: 'config' - }); - - // Throw error - if (result.errors.length > 0) { - throw new error.ConfigurationError(new Error(result.errors[0].stack)); - } - - // Insert default values - var defaults = jsonSchemaDefaults(schema); - return mergeDefaults(bookJson, defaults); -} - -module.exports = validateConfig; diff --git a/lib/parse/walkSummary.js b/lib/parse/walkSummary.js deleted file mode 100644 index 0117752..0000000 --- a/lib/parse/walkSummary.js +++ /dev/null @@ -1,34 +0,0 @@ -var Promise = require('../utils/promise'); - -/** - Walk over a list of articles - - @param {List<Article>} articles - @param {Function(article)} - @return {Promise} -*/ -function walkArticles(articles, fn) { - return Promise.forEach(articles, function(article) { - return Promise(fn(article)) - .then(function() { - return walkArticles(article.getArticles(), fn); - }); - }); -} - -/** - Walk over summary and execute "fn" on each article - - @param {Summary} summary - @param {Function(article)} - @return {Promise} -*/ -function walkSummary(summary, fn) { - var parts = summary.getParts(); - - return Promise.forEach(parts, function(part) { - return walkArticles(part.getArticles(), fn); - }); -} - -module.exports = walkSummary; diff --git a/lib/parsers.js b/lib/parsers.js deleted file mode 100644 index 70e44f4..0000000 --- a/lib/parsers.js +++ /dev/null @@ -1,63 +0,0 @@ -var path = require('path'); -var Immutable = require('immutable'); - -var markdownParser = require('gitbook-markdown'); -var asciidocParser = require('gitbook-asciidoc'); - -var EXTENSIONS_MARKDOWN = require('./constants/extsMarkdown'); -var EXTENSIONS_ASCIIDOC = require('./constants/extsAsciidoc'); -var Parser = require('./models/parser'); - -// This list is ordered by priority of parsers to use -var parsers = Immutable.List([ - Parser.create('markdown', EXTENSIONS_MARKDOWN, markdownParser), - Parser.create('asciidoc', EXTENSIONS_ASCIIDOC, asciidocParser) -]); - -/** - * Return a specific parser by its name - * - * @param {String} name - * @return {Parser|undefined} - */ -function getParser(name) { - return parsers.find(function(parser) { - return parser.getName() === name; - }); -} - -/** - * Return a specific parser according to an extension - * - * @param {String} ext - * @return {Parser|undefined} - */ -function getParserByExt(ext) { - return parsers.find(function(parser) { - return parser.matchExtension(ext); - }); -} - -/** - * Return parser for a file - * - * @param {String} ext - * @return {Parser|undefined} - */ -function getParserForFile(filename) { - return getParserByExt(path.extname(filename)); -} - -// List all parsable extensions -var extensions = parsers - .map(function(parser) { - return parser.getExtensions(); - }) - .flatten(); - -module.exports = { - extensions: extensions, - get: getParser, - getByExt: getParserByExt, - getForFile: getParserForFile -}; diff --git a/lib/plugins/__tests__/findForBook.js b/lib/plugins/__tests__/findForBook.js deleted file mode 100644 index d8af2e9..0000000 --- a/lib/plugins/__tests__/findForBook.js +++ /dev/null @@ -1,19 +0,0 @@ -var path = require('path'); - -var Book = require('../../models/book'); -var createNodeFS = require('../../fs/node'); -var findForBook = require('../findForBook'); - -describe('findForBook', function() { - var fs = createNodeFS( - path.resolve(__dirname, '../../..') - ); - var book = Book.createForFS(fs); - - it('should list default plugins', function() { - return findForBook(book) - .then(function(plugins) { - expect(plugins.has('fontsettings')).toBeTruthy(); - }); - }); -}); diff --git a/lib/plugins/__tests__/findInstalled.js b/lib/plugins/__tests__/findInstalled.js deleted file mode 100644 index 9377190..0000000 --- a/lib/plugins/__tests__/findInstalled.js +++ /dev/null @@ -1,25 +0,0 @@ -var path = require('path'); -var Immutable = require('immutable'); - -describe('findInstalled', function() { - var findInstalled = require('../findInstalled'); - - it('must list default plugins for gitbook directory', function() { - // Read gitbook-plugins from package.json - var pkg = require(path.resolve(__dirname, '../../../package.json')); - var gitbookPlugins = Immutable.Seq(pkg.dependencies) - .filter(function(v, k) { - return k.indexOf('gitbook-plugin') === 0; - }) - .cacheResult(); - - return findInstalled(path.resolve(__dirname, '../../../')) - .then(function(plugins) { - expect(plugins.size >= gitbookPlugins.size).toBeTruthy(); - - expect(plugins.has('fontsettings')).toBe(true); - expect(plugins.has('search')).toBe(true); - }); - }); - -}); diff --git a/lib/plugins/__tests__/installPlugin.js b/lib/plugins/__tests__/installPlugin.js deleted file mode 100644 index 0c1a346..0000000 --- a/lib/plugins/__tests__/installPlugin.js +++ /dev/null @@ -1,29 +0,0 @@ -var path = require('path'); - -var PluginDependency = require('../../models/pluginDependency'); -var Book = require('../../models/book'); -var NodeFS = require('../../fs/node'); -var installPlugin = require('../installPlugin'); - -var Parse = require('../../parse'); - -describe('installPlugin', function() { - var book; - - this.timeout(30000); - - before(function() { - var fs = NodeFS(path.resolve(__dirname, '../../../')); - var baseBook = Book.createForFS(fs); - - return Parse.parseConfig(baseBook) - .then(function(_book) { - book = _book; - }); - }); - - it('must install a plugin from NPM', function() { - var dep = PluginDependency.createFromString('ga'); - return installPlugin(book, dep); - }); -}); diff --git a/lib/plugins/__tests__/installPlugins.js b/lib/plugins/__tests__/installPlugins.js deleted file mode 100644 index 1a66f90..0000000 --- a/lib/plugins/__tests__/installPlugins.js +++ /dev/null @@ -1,30 +0,0 @@ -var path = require('path'); - -var Book = require('../../models/book'); -var NodeFS = require('../../fs/node'); -var installPlugins = require('../installPlugins'); - -var Parse = require('../../parse'); - -describe('installPlugins', function() { - var book; - - this.timeout(30000); - - before(function() { - var fs = NodeFS(path.resolve(__dirname, '../../../')); - var baseBook = Book.createForFS(fs); - - return Parse.parseConfig(baseBook) - .then(function(_book) { - book = _book; - }); - }); - - it('must install all plugins from NPM', function() { - return installPlugins(book) - .then(function(n) { - expect(n).toBe(2); - }); - }); -}); diff --git a/lib/plugins/__tests__/listDependencies.js b/lib/plugins/__tests__/listDependencies.js deleted file mode 100644 index 940faba..0000000 --- a/lib/plugins/__tests__/listDependencies.js +++ /dev/null @@ -1,38 +0,0 @@ -var PluginDependency = require('../../models/pluginDependency'); -var listDependencies = require('../listDependencies'); -var toNames = require('../toNames'); - -describe('listDependencies', function() { - it('must list default', function() { - var deps = PluginDependency.listFromString('ga,great'); - var plugins = listDependencies(deps); - var names = toNames(plugins); - - expect(names).toEqual([ - 'ga', 'great', - 'highlight', 'search', 'lunr', 'sharing', 'fontsettings', - 'theme-default' ]); - }); - - it('must list from array with -', function() { - var deps = PluginDependency.listFromString('ga,-great'); - var plugins = listDependencies(deps); - var names = toNames(plugins); - - expect(names).toEqual([ - 'ga', - 'highlight', 'search', 'lunr', 'sharing', 'fontsettings', - 'theme-default' ]); - }); - - it('must remove default plugins using -', function() { - var deps = PluginDependency.listFromString('ga,-search'); - var plugins = listDependencies(deps); - var names = toNames(plugins); - - expect(names).toEqual([ - 'ga', - 'highlight', 'lunr', 'sharing', 'fontsettings', - 'theme-default' ]); - }); -}); diff --git a/lib/plugins/__tests__/locateRootFolder.js b/lib/plugins/__tests__/locateRootFolder.js deleted file mode 100644 index bb414a3..0000000 --- a/lib/plugins/__tests__/locateRootFolder.js +++ /dev/null @@ -1,10 +0,0 @@ -var path = require('path'); -var locateRootFolder = require('../locateRootFolder'); - -describe('locateRootFolder', function() { - it('should correctly resolve the node_modules for gitbook', function() { - expect(locateRootFolder()).toBe( - path.resolve(__dirname, '../../../') - ); - }); -}); diff --git a/lib/plugins/__tests__/resolveVersion.js b/lib/plugins/__tests__/resolveVersion.js deleted file mode 100644 index 1877c9e..0000000 --- a/lib/plugins/__tests__/resolveVersion.js +++ /dev/null @@ -1,22 +0,0 @@ -var PluginDependency = require('../../models/pluginDependency'); -var resolveVersion = require('../resolveVersion'); - -describe('resolveVersion', function() { - it('must skip resolving and return non-semver versions', function() { - var plugin = PluginDependency.createFromString('ga@git+ssh://samy@github.com/GitbookIO/plugin-ga.git'); - - return resolveVersion(plugin) - .then(function(version) { - expect(version).toBe('git+ssh://samy@github.com/GitbookIO/plugin-ga.git'); - }); - }); - - it('must resolve a normal plugin dependency', function() { - var plugin = PluginDependency.createFromString('ga@>0.9.0 < 1.0.1'); - - return resolveVersion(plugin) - .then(function(version) { - expect(version).toBe('1.0.0'); - }); - }); -}); diff --git a/lib/plugins/__tests__/sortDependencies.js b/lib/plugins/__tests__/sortDependencies.js deleted file mode 100644 index 87df477..0000000 --- a/lib/plugins/__tests__/sortDependencies.js +++ /dev/null @@ -1,42 +0,0 @@ -var PluginDependency = require('../../models/pluginDependency'); -var sortDependencies = require('../sortDependencies'); -var toNames = require('../toNames'); - -describe('sortDependencies', function() { - it('must load themes after plugins', function() { - var allPlugins = PluginDependency.listFromArray([ - 'hello', - 'theme-test', - 'world' - ]); - - var sorted = sortDependencies(allPlugins); - var names = toNames(sorted); - - expect(names).toEqual([ - 'hello', - 'world', - 'theme-test' - ]); - }); - - it('must keep order of themes', function() { - var allPlugins = PluginDependency.listFromArray([ - 'theme-test', - 'theme-test1', - 'hello', - 'theme-test2', - 'world' - ]); - var sorted = sortDependencies(allPlugins); - var names = toNames(sorted); - - expect(names).toEqual([ - 'hello', - 'world', - 'theme-test', - 'theme-test1', - 'theme-test2' - ]); - }); -});
\ No newline at end of file diff --git a/lib/plugins/__tests__/validatePlugin.js b/lib/plugins/__tests__/validatePlugin.js deleted file mode 100644 index 635423c..0000000 --- a/lib/plugins/__tests__/validatePlugin.js +++ /dev/null @@ -1,16 +0,0 @@ -var Promise = require('../../utils/promise'); -var Plugin = require('../../models/plugin'); -var validatePlugin = require('../validatePlugin'); - -describe('validatePlugin', function() { - it('must not validate a not loaded plugin', function() { - var plugin = Plugin.createFromString('test'); - - return validatePlugin(plugin) - .then(function() { - throw new Error('Should not be validate'); - }, function(err) { - return Promise(); - }); - }); -}); diff --git a/lib/plugins/findForBook.js b/lib/plugins/findForBook.js deleted file mode 100644 index be2ad9f..0000000 --- a/lib/plugins/findForBook.js +++ /dev/null @@ -1,34 +0,0 @@ -var Immutable = require('immutable'); - -var Promise = require('../utils/promise'); -var timing = require('../utils/timing'); -var findInstalled = require('./findInstalled'); -var locateRootFolder = require('./locateRootFolder'); - -/** - * List all plugins installed in a book - * - * @param {Book} - * @return {Promise<OrderedMap<String:Plugin>>} - */ -function findForBook(book) { - return timing.measure( - 'plugins.findForBook', - - Promise.all([ - findInstalled(locateRootFolder()), - findInstalled(book.getRoot()) - ]) - - // Merge all plugins - .then(function(results) { - return Immutable.List(results) - .reduce(function(out, result) { - return out.merge(result); - }, Immutable.OrderedMap()); - }) - ); -} - - -module.exports = findForBook; diff --git a/lib/plugins/findInstalled.js b/lib/plugins/findInstalled.js deleted file mode 100644 index 06cc6c4..0000000 --- a/lib/plugins/findInstalled.js +++ /dev/null @@ -1,91 +0,0 @@ -var readInstalled = require('read-installed'); -var Immutable = require('immutable'); -var path = require('path'); - -var Promise = require('../utils/promise'); -var fs = require('../utils/fs'); -var Plugin = require('../models/plugin'); -var PREFIX = require('../constants/pluginPrefix'); - -/** - * Validate if a package name is a GitBook plugin - * - * @return {Boolean} - */ -function validateId(name) { - return name && name.indexOf(PREFIX) === 0; -} - - -/** - * List all packages installed inside a folder - * - * @param {String} folder - * @return {OrderedMap<String:Plugin>} - */ -function findInstalled(folder) { - var options = { - dev: false, - log: function() {}, - depth: 4 - }; - var results = Immutable.OrderedMap(); - - function onPackage(pkg, parent) { - if (!pkg.name) return; - - var name = pkg.name; - var version = pkg.version; - var pkgPath = pkg.realPath; - var depth = pkg.depth; - var dependencies = pkg.dependencies; - - var pluginName = name.slice(PREFIX.length); - - if (!validateId(name)){ - if (parent) return; - } else { - results = results.set(pluginName, Plugin({ - name: pluginName, - version: version, - path: pkgPath, - depth: depth, - parent: parent - })); - } - - Immutable.Map(dependencies).forEach(function(dep) { - onPackage(dep, pluginName); - }); - } - - // Search for gitbook-plugins in node_modules folder - var node_modules = path.join(folder, 'node_modules'); - - // List all folders in node_modules - return fs.readdir(node_modules) - .fail(function() { - return Promise([]); - }) - .then(function(modules) { - return Promise.serie(modules, function(module) { - // Not a gitbook-plugin - if (!validateId(module)) { - return Promise(); - } - - // Read gitbook-plugin package details - var module_folder = path.join(node_modules, module); - return Promise.nfcall(readInstalled, module_folder, options) - .then(function(data) { - onPackage(data); - }); - }); - }) - .then(function() { - // Return installed plugins - return results; - }); -} - -module.exports = findInstalled; diff --git a/lib/plugins/index.js b/lib/plugins/index.js deleted file mode 100644 index 607a7f1..0000000 --- a/lib/plugins/index.js +++ /dev/null @@ -1,10 +0,0 @@ - -module.exports = { - loadForBook: require('./loadForBook'), - validateConfig: require('./validateConfig'), - installPlugins: require('./installPlugins'), - listResources: require('./listResources'), - listBlocks: require('./listBlocks'), - listFilters: require('./listFilters') -}; - diff --git a/lib/plugins/installPlugin.js b/lib/plugins/installPlugin.js deleted file mode 100644 index 37852df..0000000 --- a/lib/plugins/installPlugin.js +++ /dev/null @@ -1,47 +0,0 @@ -var npmi = require('npmi'); - -var Promise = require('../utils/promise'); -var resolveVersion = require('./resolveVersion'); - -/** - Install a plugin for a book - - @param {Book} - @param {PluginDependency} - @return {Promise} -*/ -function installPlugin(book, plugin) { - var logger = book.getLogger(); - - var installFolder = book.getRoot(); - var name = plugin.getName(); - var requirement = plugin.getVersion(); - - logger.info.ln(''); - logger.info.ln('installing plugin "' + name + '"'); - - // Find a version to install - return resolveVersion(plugin) - .then(function(version) { - if (!version) { - throw new Error('Found no satisfactory version for plugin "' + name + '" with requirement "' + requirement + '"'); - } - - logger.info.ln('install plugin "' + name +'" (' + requirement + ') from NPM with version', version); - return Promise.nfcall(npmi, { - 'name': plugin.getNpmID(), - 'version': version, - 'path': installFolder, - 'npmLoad': { - 'loglevel': 'silent', - 'loaded': true, - 'prefix': installFolder - } - }); - }) - .then(function() { - logger.info.ok('plugin "' + name + '" installed with success'); - }); -} - -module.exports = installPlugin; diff --git a/lib/plugins/installPlugins.js b/lib/plugins/installPlugins.js deleted file mode 100644 index 307c41e..0000000 --- a/lib/plugins/installPlugins.js +++ /dev/null @@ -1,48 +0,0 @@ -var npmi = require('npmi'); - -var DEFAULT_PLUGINS = require('../constants/defaultPlugins'); -var Promise = require('../utils/promise'); -var installPlugin = require('./installPlugin'); - -/** - Install plugin requirements for a book - - @param {Book} - @return {Promise<Number>} -*/ -function installPlugins(book) { - var logger = book.getLogger(); - var config = book.getConfig(); - var plugins = config.getPluginDependencies(); - - // Remove default plugins - // (only if version is same as installed) - plugins = plugins.filterNot(function(plugin) { - var dependency = DEFAULT_PLUGINS.find(function(dep) { - return dep.getName() === plugin.getName(); - }); - - return ( - // Disabled plugin - !plugin.isEnabled() || - - // Or default one installed in GitBook itself - (dependency && - plugin.getVersion() === dependency.getVersion()) - ); - }); - - if (plugins.size == 0) { - logger.info.ln('nothing to install!'); - return Promise(); - } - - logger.info.ln('installing', plugins.size, 'plugins using npm@' + npmi.NPM_VERSION); - - return Promise.forEach(plugins, function(plugin) { - return installPlugin(book, plugin); - }) - .thenResolve(plugins.size); -} - -module.exports = installPlugins; diff --git a/lib/plugins/listBlocks.js b/lib/plugins/listBlocks.js deleted file mode 100644 index 3ac28af..0000000 --- a/lib/plugins/listBlocks.js +++ /dev/null @@ -1,18 +0,0 @@ -var Immutable = require('immutable'); - -/** - List blocks from a list of plugins - - @param {OrderedMap<String:Plugin>} - @return {Map<String:TemplateBlock>} -*/ -function listBlocks(plugins) { - return plugins - .reverse() - .reduce(function(result, plugin) { - var blocks = plugin.getBlocks(); - return result.merge(blocks); - }, Immutable.Map()); -} - -module.exports = listBlocks; diff --git a/lib/plugins/listDependencies.js b/lib/plugins/listDependencies.js deleted file mode 100644 index d52eaa9..0000000 --- a/lib/plugins/listDependencies.js +++ /dev/null @@ -1,33 +0,0 @@ -var DEFAULT_PLUGINS = require('../constants/defaultPlugins'); -var sortDependencies = require('./sortDependencies'); - -/** - * List all dependencies for a book, including default plugins. - * It returns a concat with default plugins and remove disabled ones. - * - * @param {List<PluginDependency>} deps - * @return {List<PluginDependency>} - */ -function listDependencies(deps) { - // Extract list of plugins to disable (starting with -) - var toRemove = deps - .filter(function(plugin) { - return !plugin.isEnabled(); - }) - .map(function(plugin) { - return plugin.getName(); - }); - - // Concat with default plugins - deps = deps.concat(DEFAULT_PLUGINS); - - // Remove plugins - deps = deps.filterNot(function(plugin) { - return toRemove.includes(plugin.getName()); - }); - - // Sort - return sortDependencies(deps); -} - -module.exports = listDependencies; diff --git a/lib/plugins/listDepsForBook.js b/lib/plugins/listDepsForBook.js deleted file mode 100644 index 196e3aa..0000000 --- a/lib/plugins/listDepsForBook.js +++ /dev/null @@ -1,18 +0,0 @@ -var listDependencies = require('./listDependencies'); - -/** - * List all plugin requirements for a book. - * It can be different from the final list of plugins, - * since plugins can have their own dependencies - * - * @param {Book} - * @return {List<PluginDependency>} - */ -function listDepsForBook(book) { - var config = book.getConfig(); - var plugins = config.getPluginDependencies(); - - return listDependencies(plugins); -} - -module.exports = listDepsForBook; diff --git a/lib/plugins/listFilters.js b/lib/plugins/listFilters.js deleted file mode 100644 index 4d8a471..0000000 --- a/lib/plugins/listFilters.js +++ /dev/null @@ -1,17 +0,0 @@ -var Immutable = require('immutable'); - -/** - List filters from a list of plugins - - @param {OrderedMap<String:Plugin>} - @return {Map<String:Function>} -*/ -function listFilters(plugins) { - return plugins - .reverse() - .reduce(function(result, plugin) { - return result.merge(plugin.getFilters()); - }, Immutable.Map()); -} - -module.exports = listFilters; diff --git a/lib/plugins/listResources.js b/lib/plugins/listResources.js deleted file mode 100644 index fe31b5a..0000000 --- a/lib/plugins/listResources.js +++ /dev/null @@ -1,45 +0,0 @@ -var Immutable = require('immutable'); -var path = require('path'); - -var LocationUtils = require('../utils/location'); -var PLUGIN_RESOURCES = require('../constants/pluginResources'); - -/** - List all resources from a list of plugins - - @param {OrderedMap<String:Plugin>} - @param {String} type - @return {Map<String:List<{url, path}>} -*/ -function listResources(plugins, resources) { - return plugins.reduce(function(result, plugin) { - var npmId = plugin.getNpmID(); - var pluginResources = resources.get(plugin.getName()); - - PLUGIN_RESOURCES.forEach(function(resourceType) { - var assets = pluginResources.get(resourceType); - if (!assets) return; - - var list = result.get(resourceType) || Immutable.List(); - - assets = assets.map(function(assetFile) { - if (LocationUtils.isExternal(assetFile)) { - return { - url: assetFile - }; - } else { - return { - path: LocationUtils.normalize(path.join(npmId, assetFile)) - }; - } - }); - - list = list.concat(assets); - result = result.set(resourceType, list); - }); - - return result; - }, Immutable.Map()); -} - -module.exports = listResources; diff --git a/lib/plugins/loadForBook.js b/lib/plugins/loadForBook.js deleted file mode 100644 index 757677e..0000000 --- a/lib/plugins/loadForBook.js +++ /dev/null @@ -1,73 +0,0 @@ -var Immutable = require('immutable'); - -var Promise = require('../utils/promise'); -var listDepsForBook = require('./listDepsForBook'); -var findForBook = require('./findForBook'); -var loadPlugin = require('./loadPlugin'); - - -/** - * Load all plugins in a book - * - * @param {Book} - * @return {Promise<Map<String:Plugin>} - */ -function loadForBook(book) { - var logger = book.getLogger(); - - // List the dependencies - var requirements = listDepsForBook(book); - - // List all plugins installed in the book - return findForBook(book) - .then(function(installedMap) { - var missing = []; - var plugins = requirements.reduce(function(result, dep) { - var name = dep.getName(); - var installed = installedMap.get(name); - - if (installed) { - var deps = installedMap - .filter(function(plugin) { - return plugin.getParent() === name; - }) - .toArray(); - - result = result.concat(deps); - result.push(installed); - } else { - missing.push(name); - } - - return result; - }, []); - - // Convert plugins list to a map - plugins = Immutable.List(plugins) - .map(function(plugin) { - return [ - plugin.getName(), - plugin - ]; - }); - plugins = Immutable.OrderedMap(plugins); - - // Log state - logger.info.ln(installedMap.size + ' plugins are installed'); - if (requirements.size != installedMap.size) { - logger.info.ln(requirements.size + ' explicitly listed'); - } - - // Verify that all plugins are present - if (missing.length > 0) { - throw new Error('Couldn\'t locate plugins "' + missing.join(', ') + '", Run \'gitbook install\' to install plugins from registry.'); - } - - return Promise.map(plugins, function(plugin) { - return loadPlugin(book, plugin); - }); - }); -} - - -module.exports = loadForBook; diff --git a/lib/plugins/loadPlugin.js b/lib/plugins/loadPlugin.js deleted file mode 100644 index 9ed83a1..0000000 --- a/lib/plugins/loadPlugin.js +++ /dev/null @@ -1,89 +0,0 @@ -var path = require('path'); -var resolve = require('resolve'); -var Immutable = require('immutable'); - -var Promise = require('../utils/promise'); -var error = require('../utils/error'); -var timing = require('../utils/timing'); - -var validatePlugin = require('./validatePlugin'); - -// Return true if an error is a "module not found" -// Wait on https://github.com/substack/node-resolve/pull/81 to be merged -function isModuleNotFound(err) { - return err.code == 'MODULE_NOT_FOUND' || err.message.indexOf('Cannot find module') >= 0; -} - -/** - Load a plugin in a book - - @param {Book} book - @param {Plugin} plugin - @param {String} pkgPath (optional) - @return {Promise<Plugin>} -*/ -function loadPlugin(book, plugin) { - var logger = book.getLogger(); - - var name = plugin.getName(); - var pkgPath = plugin.getPath(); - - // Try loading plugins from different location - var p = Promise() - .then(function() { - var packageContent; - var packageMain; - var content; - - // Locate plugin and load package.json - try { - var res = resolve.sync('./package.json', { basedir: pkgPath }); - - pkgPath = path.dirname(res); - packageContent = require(res); - } catch (err) { - if (!isModuleNotFound(err)) throw err; - - packageContent = undefined; - content = undefined; - - return; - } - - // Locate the main package - try { - var indexJs = path.normalize(packageContent.main || 'index.js'); - packageMain = resolve.sync('./' + indexJs, { basedir: pkgPath }); - } catch (err) { - if (!isModuleNotFound(err)) throw err; - packageMain = undefined; - } - - // Load plugin JS content - if (packageMain) { - try { - content = require(packageMain); - } catch(err) { - throw new error.PluginError(err, { - plugin: name - }); - } - } - - // Update plugin - return plugin.merge({ - 'package': Immutable.fromJS(packageContent), - 'content': Immutable.fromJS(content || {}) - }); - }) - - .then(validatePlugin); - - p = timing.measure('plugin.load', p); - - logger.info('loading plugin "' + name + '"... '); - return logger.info.promise(p); -} - - -module.exports = loadPlugin; diff --git a/lib/plugins/locateRootFolder.js b/lib/plugins/locateRootFolder.js deleted file mode 100644 index 1139510..0000000 --- a/lib/plugins/locateRootFolder.js +++ /dev/null @@ -1,22 +0,0 @@ -var path = require('path'); -var resolve = require('resolve'); - -var DEFAULT_PLUGINS = require('../constants/defaultPlugins'); - -/** - * Resolve the root folder containing for node_modules - * since gitbook can be used as a library and dependency can be flattened. - * - * @return {String} folderPath - */ -function locateRootFolder() { - var firstDefaultPlugin = DEFAULT_PLUGINS.first(); - var pluginPath = resolve.sync(firstDefaultPlugin.getNpmID() + '/package.json', { - basedir: __dirname - }); - var nodeModules = path.resolve(pluginPath, '../../..'); - - return nodeModules; -} - -module.exports = locateRootFolder; diff --git a/lib/plugins/resolveVersion.js b/lib/plugins/resolveVersion.js deleted file mode 100644 index 61aef8d..0000000 --- a/lib/plugins/resolveVersion.js +++ /dev/null @@ -1,71 +0,0 @@ -var npm = require('npm'); -var semver = require('semver'); -var Immutable = require('immutable'); - -var Promise = require('../utils/promise'); -var Plugin = require('../models/plugin'); -var gitbook = require('../gitbook'); - -var npmIsReady; - -/** - Initialize and prepare NPM - - @return {Promise} -*/ -function initNPM() { - if (npmIsReady) return npmIsReady; - - npmIsReady = Promise.nfcall(npm.load, { - silent: true, - loglevel: 'silent' - }); - - return npmIsReady; -} - -/** - Resolve a plugin dependency to a version - - @param {PluginDependency} plugin - @return {Promise<String>} -*/ -function resolveVersion(plugin) { - var npmId = Plugin.nameToNpmID(plugin.getName()); - var requiredVersion = plugin.getVersion(); - - if (plugin.isGitDependency()) { - return Promise.resolve(requiredVersion); - } - - return initNPM() - .then(function() { - return Promise.nfcall(npm.commands.view, [npmId + '@' + requiredVersion, 'engines'], true); - }) - .then(function(versions) { - versions = Immutable.Map(versions).entrySeq(); - - var result = versions - .map(function(entry) { - return { - version: entry[0], - gitbook: (entry[1].engines || {}).gitbook - }; - }) - .filter(function(v) { - return v.gitbook && gitbook.satisfies(v.gitbook); - }) - .sort(function(v1, v2) { - return semver.lt(v1.version, v2.version)? 1 : -1; - }) - .get(0); - - if (!result) { - return undefined; - } else { - return result.version; - } - }); -} - -module.exports = resolveVersion; diff --git a/lib/plugins/sortDependencies.js b/lib/plugins/sortDependencies.js deleted file mode 100644 index 7f10095..0000000 --- a/lib/plugins/sortDependencies.js +++ /dev/null @@ -1,34 +0,0 @@ -var Immutable = require('immutable'); - -var THEME_PREFIX = require('../constants/themePrefix'); - -var TYPE_PLUGIN = 'plugin'; -var TYPE_THEME = 'theme'; - - -/** - * Returns the type of a plugin given its name - * @param {Plugin} plugin - * @return {String} - */ -function pluginType(plugin) { - var name = plugin.getName(); - return (name && name.indexOf(THEME_PREFIX) === 0) ? TYPE_THEME : TYPE_PLUGIN; -} - - -/** - * Sort the list of dependencies to match list in book.json - * The themes should always be loaded after the plugins - * - * @param {List<PluginDependency>} deps - * @return {List<PluginDependency>} - */ -function sortDependencies(plugins) { - var byTypes = plugins.groupBy(pluginType); - - return byTypes.get(TYPE_PLUGIN, Immutable.List()) - .concat(byTypes.get(TYPE_THEME, Immutable.List())); -} - -module.exports = sortDependencies;
\ No newline at end of file diff --git a/lib/plugins/toNames.js b/lib/plugins/toNames.js deleted file mode 100644 index ad0dd8f..0000000 --- a/lib/plugins/toNames.js +++ /dev/null @@ -1,16 +0,0 @@ - -/** - * Return list of plugin names. This method is nly used in unit tests. - * - * @param {OrderedMap<String:Plugin} plugins - * @return {Array<String>} - */ -function toNames(plugins) { - return plugins - .map(function(plugin) { - return plugin.getName(); - }) - .toArray(); -} - -module.exports = toNames; diff --git a/lib/plugins/validateConfig.js b/lib/plugins/validateConfig.js deleted file mode 100644 index fab1fef..0000000 --- a/lib/plugins/validateConfig.js +++ /dev/null @@ -1,71 +0,0 @@ -var Immutable = require('immutable'); -var jsonschema = require('jsonschema'); -var jsonSchemaDefaults = require('json-schema-defaults'); - -var Promise = require('../utils/promise'); -var error = require('../utils/error'); -var mergeDefaults = require('../utils/mergeDefaults'); - -/** - Validate one plugin for a book and update book's confiration - - @param {Book} - @param {Plugin} - @return {Book} -*/ -function validatePluginConfig(book, plugin) { - var config = book.getConfig(); - var packageInfos = plugin.getPackage(); - - var configKey = [ - 'pluginsConfig', - plugin.getName() - ].join('.'); - - var pluginConfig = config.getValue(configKey, {}).toJS(); - - var schema = (packageInfos.get('gitbook') || Immutable.Map()).toJS(); - if (!schema) return book; - - // Normalize schema - schema.id = '/' + configKey; - schema.type = 'object'; - - // Validate and throw if invalid - var v = new jsonschema.Validator(); - var result = v.validate(pluginConfig, schema, { - propertyName: configKey - }); - - // Throw error - if (result.errors.length > 0) { - throw new error.ConfigurationError(new Error(result.errors[0].stack)); - } - - // Insert default values - var defaults = jsonSchemaDefaults(schema); - pluginConfig = mergeDefaults(pluginConfig, defaults); - - - // Update configuration - config = config.setValue(configKey, pluginConfig); - - // Return new book - return book.set('config', config); -} - -/** - Validate a book configuration for plugins and - returns an update configuration with default values. - - @param {Book} - @param {OrderedMap<String:Plugin>} - @return {Promise<Book>} -*/ -function validateConfig(book, plugins) { - return Promise.reduce(plugins, function(newBook, plugin) { - return validatePluginConfig(newBook, plugin); - }, book); -} - -module.exports = validateConfig; diff --git a/lib/plugins/validatePlugin.js b/lib/plugins/validatePlugin.js deleted file mode 100644 index 4baa911..0000000 --- a/lib/plugins/validatePlugin.js +++ /dev/null @@ -1,34 +0,0 @@ -var gitbook = require('../gitbook'); - -var Promise = require('../utils/promise'); - -/** - Validate a plugin - - @param {Plugin} - @return {Promise<Plugin>} -*/ -function validatePlugin(plugin) { - var packageInfos = plugin.getPackage(); - - var isValid = ( - plugin.isLoaded() && - packageInfos && - packageInfos.get('name') && - packageInfos.get('engines') && - packageInfos.get('engines').get('gitbook') - ); - - if (!isValid) { - return Promise.reject(new Error('Error loading plugin "' + plugin.getName() + '" at "' + plugin.getPath() + '"')); - } - - var engine = packageInfos.get('engines').get('gitbook'); - if (!gitbook.satisfies(engine)) { - return Promise.reject(new Error('GitBook doesn\'t satisfy the requirements of this plugin: ' + engine)); - } - - return Promise(plugin); -} - -module.exports = validatePlugin; diff --git a/lib/templating/__tests__/conrefsLoader.js b/lib/templating/__tests__/conrefsLoader.js deleted file mode 100644 index 196b513..0000000 --- a/lib/templating/__tests__/conrefsLoader.js +++ /dev/null @@ -1,98 +0,0 @@ -var path = require('path'); - -var TemplateEngine = require('../../models/templateEngine'); -var renderTemplate = require('../render'); -var ConrefsLoader = require('../conrefsLoader'); - -describe('ConrefsLoader', function() { - var dirName = __dirname + '/'; - var fileName = path.join(dirName, 'test.md'); - - describe('Git', function() { - var engine = TemplateEngine({ - loader: new ConrefsLoader(dirName) - }); - - it('should include content from git', function() { - return renderTemplate(engine, fileName, '{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}') - .then(function(out) { - expect(out.getContent()).toBe('Hello from git'); - }); - }); - - it('should handle deep inclusion (1)', function() { - return renderTemplate(engine, fileName, '{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.md" %}') - .then(function(out) { - expect(out.getContent()).toBe('First Hello. Hello from git'); - }); - }); - - it('should handle deep inclusion (2)', function() { - return renderTemplate(engine, fileName, '{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test3.md" %}') - .then(function(out) { - expect(out.getContent()).toBe('First Hello. Hello from git'); - }); - }); - }); - - describe('Local', function() { - var engine = TemplateEngine({ - loader: new ConrefsLoader(dirName) - }); - - describe('Relative', function() { - it('should resolve basic relative filepath', function() { - return renderTemplate(engine, fileName, '{% include "include.md" %}') - .then(function(out) { - expect(out.getContent()).toBe('Hello World'); - }); - }); - - it('should resolve basic parent filepath', function() { - return renderTemplate(engine, path.join(dirName, 'hello/test.md'), '{% include "../include.md" %}') - .then(function(out) { - expect(out.getContent()).toBe('Hello World'); - }); - }); - }); - - describe('Absolute', function() { - it('should resolve absolute filepath', function() { - return renderTemplate(engine, fileName, '{% include "/include.md" %}') - .then(function(out) { - expect(out.getContent()).toBe('Hello World'); - }); - }); - - it('should resolve absolute filepath when in a directory', function() { - return renderTemplate(engine, path.join(dirName, 'hello/test.md'), '{% include "/include.md" %}') - .then(function(out) { - expect(out.getContent()).toBe('Hello World'); - }); - }); - }); - }); - - describe('transform', function() { - function transform(filePath, source) { - expect(filePath).toBeA('string'); - expect(source).toBeA('string'); - - expect(filePath).toBe(path.resolve(__dirname, 'include.md')); - expect(source).toBe('Hello World'); - - return 'test-' + source + '-endtest'; - } - var engine = TemplateEngine({ - loader: new ConrefsLoader(dirName, transform) - }); - - it('should transform included content', function() { - return renderTemplate(engine, fileName, '{% include "include.md" %}') - .then(function(out) { - expect(out.getContent()).toBe('test-Hello World-endtest'); - }); - }); - }); -}); - diff --git a/lib/templating/__tests__/include.md b/lib/templating/__tests__/include.md deleted file mode 100644 index 5e1c309..0000000 --- a/lib/templating/__tests__/include.md +++ /dev/null @@ -1 +0,0 @@ -Hello World
\ No newline at end of file diff --git a/lib/templating/__tests__/postRender.js b/lib/templating/__tests__/postRender.js deleted file mode 100644 index 131e29b..0000000 --- a/lib/templating/__tests__/postRender.js +++ /dev/null @@ -1,51 +0,0 @@ -var TemplateEngine = require('../../models/templateEngine'); -var TemplateBlock = require('../../models/templateBlock'); - -var renderTemplate = require('../render'); -var postRender = require('../postRender'); - -describe('postRender', function() { - var testPost; - var engine = TemplateEngine.create({ - blocks: [ - TemplateBlock.create('lower', function(blk) { - return blk.body.toLowerCase(); - }), - TemplateBlock.create('prefix', function(blk) { - return { - body: '_' + blk.body + '_', - post: function() { - testPost = true; - } - }; - }) - ] - }); - - it('should correctly replace block', function() { - return renderTemplate(engine, 'README.md', 'Hello {% lower %}Samy{% endlower %}') - .then(function(output) { - expect(output.getContent()).toMatch(/Hello \{\{\-([\S]+)\-\}\}/); - expect(output.getBlocks().size).toBe(1); - - return postRender(engine, output); - }) - .then(function(result) { - expect(result).toBe('Hello samy'); - }); - }); - - it('should correctly replace blocks', function() { - return renderTemplate(engine, 'README.md', 'Hello {% lower %}Samy{% endlower %}{% prefix %}Pesse{% endprefix %}') - .then(function(output) { - expect(output.getContent()).toMatch(/Hello \{\{\-([\S]+)\-\}\}\{\{\-([\S]+)\-\}\}/); - expect(output.getBlocks().size).toBe(2); - return postRender(engine, output); - }) - .then(function(result) { - expect(result).toBe('Hello samy_Pesse_'); - expect(testPost).toBe(true); - }); - }); - -}); diff --git a/lib/templating/__tests__/replaceShortcuts.js b/lib/templating/__tests__/replaceShortcuts.js deleted file mode 100644 index 216a1c8..0000000 --- a/lib/templating/__tests__/replaceShortcuts.js +++ /dev/null @@ -1,27 +0,0 @@ -var Immutable = require('immutable'); - -var TemplateBlock = require('../../models/templateBlock'); -var replaceShortcuts = require('../replaceShortcuts'); - -describe('replaceShortcuts', function() { - var blocks = Immutable.List([ - TemplateBlock.create('math', { - shortcuts: { - start: '$$', - end: '$$', - parsers: ['markdown'] - } - }) - ]); - - it('should correctly replace inline matches by block', function() { - var content = replaceShortcuts(blocks, 'test.md', 'Hello $$a = b$$'); - expect(content).toBe('Hello {% math %}a = b{% endmath %}'); - }); - - it('should correctly replace block matches', function() { - var content = replaceShortcuts(blocks, 'test.md', 'Hello\n$$\na = b\n$$\n'); - expect(content).toBe('Hello\n{% math %}\na = b\n{% endmath %}\n'); - }); -}); - diff --git a/lib/templating/conrefsLoader.js b/lib/templating/conrefsLoader.js deleted file mode 100644 index b3cdb3f..0000000 --- a/lib/templating/conrefsLoader.js +++ /dev/null @@ -1,93 +0,0 @@ -var path = require('path'); -var nunjucks = require('nunjucks'); - -var fs = require('../utils/fs'); -var Git = require('../utils/git'); -var LocationUtils = require('../utils/location'); -var PathUtils = require('../utils/path'); - - -/** - * Template loader resolving both: - * - relative url ("./test.md") - * - absolute url ("/test.md") - * - git url ("") - * - * @param {String} rootFolder - * @param {Function(filePath, source)} transformFn (optional) - * @param {Logger} logger (optional) - */ -var ConrefsLoader = nunjucks.Loader.extend({ - async: true, - - init: function(rootFolder, transformFn, logger) { - this.rootFolder = rootFolder; - this.transformFn = transformFn; - this.logger = logger; - this.git = new Git(); - }, - - getSource: function(sourceURL, callback) { - var that = this; - - this.git.resolve(sourceURL) - .then(function(filepath) { - // Is local file - if (!filepath) { - filepath = path.resolve(sourceURL); - } else { - if (that.logger) that.logger.debug.ln('resolve from git', sourceURL, 'to', filepath); - } - - // Read file from absolute path - return fs.readFile(filepath) - .then(function(source) { - source = source.toString('utf8'); - - if (that.transformFn) { - return that.transformFn(filepath, source); - } - - return source; - }) - .then(function(source) { - return { - src: source, - path: filepath - }; - }); - }) - .nodeify(callback); - }, - - resolve: function(from, to) { - // If origin is in the book, we enforce result file to be in the book - if (PathUtils.isInRoot(this.rootFolder, from)) { - - // Path of current template in the rootFolder (not absolute to fs) - var fromRelative = path.relative(this.rootFolder, from); - - // Resolve "to" to a filepath relative to rootFolder - var href = LocationUtils.toAbsolute(to, path.dirname(fromRelative), ''); - - // Return absolute path - return PathUtils.resolveInRoot(this.rootFolder, href); - } - - // If origin is in a git repository, we resolve file in the git repository - var gitRoot = this.git.resolveRoot(from); - if (gitRoot) { - return PathUtils.resolveInRoot(gitRoot, to); - } - - // If origin is not in the book (include from a git content ref) - return path.resolve(path.dirname(from), to); - }, - - // Handle all files as relative, so that nunjucks pass responsability to 'resolve' - isRelative: function(filename) { - return LocationUtils.isRelative(filename); - } -}); - -module.exports = ConrefsLoader; diff --git a/lib/templating/index.js b/lib/templating/index.js deleted file mode 100644 index bd74aca..0000000 --- a/lib/templating/index.js +++ /dev/null @@ -1,10 +0,0 @@ - -module.exports = { - render: require('./render'), - renderFile: require('./renderFile'), - postRender: require('./postRender'), - replaceShortcuts: require('./replaceShortcuts'), - - ConrefsLoader: require('./conrefsLoader'), - ThemesLoader: require('./themesLoader') -}; diff --git a/lib/templating/listShortcuts.js b/lib/templating/listShortcuts.js deleted file mode 100644 index 8d0a64a..0000000 --- a/lib/templating/listShortcuts.js +++ /dev/null @@ -1,31 +0,0 @@ -var Immutable = require('immutable'); -var parsers = require('../parsers'); - -/** - * Return a list of all shortcuts that can apply - * to a file for a TemplatEngine - * - * @param {List<TemplateBlock>} engine - * @param {String} filePath - * @return {List<TemplateShortcut>} - */ -function listShortcuts(blocks, filePath) { - var parser = parsers.getForFile(filePath); - - if (!parser) { - return Immutable.List(); - } - - return blocks - .map(function(block) { - return block.getShortcuts(); - }) - .filter(function(shortcuts) { - return ( - shortcuts && - shortcuts.acceptParser(parser.getName()) - ); - }); -} - -module.exports = listShortcuts; diff --git a/lib/templating/postRender.js b/lib/templating/postRender.js deleted file mode 100644 index f464f86..0000000 --- a/lib/templating/postRender.js +++ /dev/null @@ -1,53 +0,0 @@ -var Promise = require('../utils/promise'); - - -/** - * Replace position markers of blocks by body after processing - * This is done to avoid that markdown/asciidoc processer parse the block content - * - * @param {String} content - * @return {Object} {blocks: Set, content: String} - */ -function replaceBlocks(content, blocks) { - var newContent = content.replace(/\{\{\-\%([\s\S]+?)\%\-\}\}/g, function(match, key) { - var replacedWith = match; - - var block = blocks.get(key); - if (block) { - replacedWith = replaceBlocks(block.get('body'), blocks); - } - - return replacedWith; - }); - - return newContent; -} - -/** - * Post render a template: - * - Execute "post" for blocks - * - Replace block content - * - * @param {TemplateEngine} engine - * @param {TemplateOutput} content - * @return {Promise<String>} - */ -function postRender(engine, output) { - var content = output.getContent(); - var blocks = output.getBlocks(); - - var result = replaceBlocks(content, blocks); - - return Promise.forEach(blocks, function(block) { - var post = block.get('post'); - - if (!post) { - return; - } - - return post(); - }) - .thenResolve(result); -} - -module.exports = postRender; diff --git a/lib/templating/render.js b/lib/templating/render.js deleted file mode 100644 index 1a8b0cd..0000000 --- a/lib/templating/render.js +++ /dev/null @@ -1,44 +0,0 @@ -var Promise = require('../utils/promise'); -var timing = require('../utils/timing'); -var TemplateOutput = require('../models/templateOutput'); -var replaceShortcuts = require('./replaceShortcuts'); - -/** - * Render a template - * - * @param {TemplateEngine} engine - * @param {String} filePath: absolute path for the loader - * @param {String} content - * @param {Object} context (optional) - * @return {Promise<TemplateOutput>} - */ -function renderTemplate(engine, filePath, content, context) { - context = context || {}; - - // Mutable objects to contains all blocks requiring post-processing - var blocks = {}; - - // Create nunjucks environment - var env = engine.toNunjucks(blocks); - - // Replace shortcuts from plugin's blocks - content = replaceShortcuts(engine.getBlocks(), filePath, content); - - return timing.measure( - 'template.render', - - Promise.nfcall( - env.renderString.bind(env), - content, - context, - { - path: filePath - } - ) - .then(function(content) { - return TemplateOutput.create(content, blocks); - }) - ); -} - -module.exports = renderTemplate; diff --git a/lib/templating/renderFile.js b/lib/templating/renderFile.js deleted file mode 100644 index 8672e8b..0000000 --- a/lib/templating/renderFile.js +++ /dev/null @@ -1,41 +0,0 @@ -var Promise = require('../utils/promise'); -var error = require('../utils/error'); -var render = require('./render'); - -/** - * Render a template - * - * @param {TemplateEngine} engine - * @param {String} filePath - * @param {Object} context - * @return {Promise<TemplateOutput>} - */ -function renderTemplateFile(engine, filePath, context) { - var loader = engine.getLoader(); - - // Resolve the filePath - var resolvedFilePath = loader.resolve(null, filePath); - - return Promise() - .then(function() { - if (!loader.async) { - return loader.getSource(resolvedFilePath); - } - - var deferred = Promise.defer(); - loader.getSource(resolvedFilePath, deferred.makeNodeResolver()); - return deferred.promise; - }) - .then(function(result) { - if (!result) { - throw error.TemplateError(new Error('Not found'), { - filename: filePath - }); - } - - return render(engine, result.path, result.src, context); - }); - -} - -module.exports = renderTemplateFile; diff --git a/lib/templating/replaceShortcuts.js b/lib/templating/replaceShortcuts.js deleted file mode 100644 index 1cfdbf0..0000000 --- a/lib/templating/replaceShortcuts.js +++ /dev/null @@ -1,39 +0,0 @@ -var escapeStringRegexp = require('escape-string-regexp'); -var listShortcuts = require('./listShortcuts'); - -/** - * Apply a shortcut of block to a template - * @param {String} content - * @param {Shortcut} shortcut - * @return {String} - */ -function applyShortcut(content, shortcut) { - var start = shortcut.getStart(); - var end = shortcut.getEnd(); - - var tagStart = shortcut.getStartTag(); - var tagEnd = shortcut.getEndTag(); - - var regex = new RegExp( - escapeStringRegexp(start) + '([\\s\\S]*?[^\\$])' + escapeStringRegexp(end), - 'g' - ); - return content.replace(regex, function(all, match) { - return '{% ' + tagStart + ' %}' + match + '{% ' + tagEnd + ' %}'; - }); -} - -/** - * Replace shortcuts from blocks in a string - * - * @param {List<TemplateBlock>} engine - * @param {String} filePath - * @param {String} content - * @return {String} - */ -function replaceShortcuts(blocks, filePath, content) { - var shortcuts = listShortcuts(blocks, filePath); - return shortcuts.reduce(applyShortcut, content); -} - -module.exports = replaceShortcuts; diff --git a/lib/templating/themesLoader.js b/lib/templating/themesLoader.js deleted file mode 100644 index bae4c12..0000000 --- a/lib/templating/themesLoader.js +++ /dev/null @@ -1,115 +0,0 @@ -var Immutable = require('immutable'); -var nunjucks = require('nunjucks'); -var fs = require('fs'); -var path = require('path'); - -var PathUtils = require('../utils/path'); - - -var ThemesLoader = nunjucks.Loader.extend({ - init: function(searchPaths) { - this.searchPaths = Immutable.List(searchPaths) - .map(path.normalize); - }, - - /** - * Read source of a resolved filepath - * @param {String} - * @return {Object} - */ - getSource: function(fullpath) { - if (!fullpath) return null; - - fullpath = this.resolve(null, fullpath); - var templateName = this.getTemplateName(fullpath); - - if(!fullpath) { - return null; - } - - var src = fs.readFileSync(fullpath, 'utf-8'); - - src = '{% do %}var template = template || {}; template.stack = template.stack || []; template.stack.push(template.self); template.self = ' + JSON.stringify(templateName) + '{% enddo %}\n' + - src + - '\n{% do %}template.self = template.stack.pop();{% enddo %}'; - - return { - src: src, - path: fullpath, - noCache: true - }; - }, - - /** - * Nunjucks calls "isRelative" to determine when to call "resolve". - * We handle absolute paths ourselves in ".resolve" so we always return true - */ - isRelative: function() { - return true; - }, - - /** - * Get original search path containing a template - * @param {String} filepath - * @return {String} searchPath - */ - getSearchPath: function(filepath) { - return this.searchPaths - .sortBy(function(s) { - return -s.length; - }) - .find(function(basePath) { - return (filepath && filepath.indexOf(basePath) === 0); - }); - }, - - /** - * Get template name from a filepath - * @param {String} filepath - * @return {String} name - */ - getTemplateName: function(filepath) { - var originalSearchPath = this.getSearchPath(filepath); - return originalSearchPath? path.relative(originalSearchPath, filepath) : null; - }, - - /** - * Resolve a template from a current template - * @param {String|null} from - * @param {String} to - * @return {String|null} - */ - resolve: function(from, to) { - var searchPaths = this.searchPaths; - - // Relative template like "./test.html" - if (PathUtils.isPureRelative(to) && from) { - return path.resolve(path.dirname(from), to); - } - - // Determine in which search folder we currently are - var originalSearchPath = this.getSearchPath(from); - var originalFilename = this.getTemplateName(from); - - // If we are including same file from a different search path - // Slice the search paths to avoid including from previous ones - if (originalFilename == to) { - var currentIndex = searchPaths.indexOf(originalSearchPath); - searchPaths = searchPaths.slice(currentIndex + 1); - } - - // Absolute template to resolve in root folder - var resultFolder = searchPaths.find(function(basePath) { - var p = path.resolve(basePath, to); - - return ( - p.indexOf(basePath) === 0 - && fs.existsSync(p) - ); - }); - if (!resultFolder) return null; - return path.resolve(resultFolder, to); - } -}); - -module.exports = ThemesLoader; diff --git a/lib/utils/__tests__/git.js b/lib/utils/__tests__/git.js deleted file mode 100644 index abc1ea1..0000000 --- a/lib/utils/__tests__/git.js +++ /dev/null @@ -1,57 +0,0 @@ -var path = require('path'); -var os = require('os'); - -var Git = require('../git'); - -describe('Git', function() { - - describe('URL parsing', function() { - - it('should correctly validate git urls', function() { - // HTTPS - expect(Git.isUrl('git+https://github.com/Hello/world.git')).toBeTruthy(); - - // SSH - expect(Git.isUrl('git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1')).toBeTruthy(); - - // Non valid - expect(Git.isUrl('https://github.com/Hello/world.git')).toBeFalsy(); - expect(Git.isUrl('README.md')).toBeFalsy(); - }); - - it('should parse HTTPS urls', function() { - var parts = Git.parseUrl('git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md'); - - expect(parts.host).toBe('https://gist.github.com/69ea4542e4c8967d2fa7.git'); - expect(parts.ref).toBe(null); - expect(parts.filepath).toBe('test.md'); - }); - - it('should parse HTTPS urls with a reference', function() { - var parts = Git.parseUrl('git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md#1.0.0'); - - expect(parts.host).toBe('https://gist.github.com/69ea4542e4c8967d2fa7.git'); - expect(parts.ref).toBe('1.0.0'); - expect(parts.filepath).toBe('test.md'); - }); - - it('should parse SSH urls', function() { - var parts = Git.parseUrl('git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1'); - - expect(parts.host).toBe('git@github.com:GitbookIO/gitbook.git'); - expect(parts.ref).toBe('e1594cde2c32e4ff48f6c4eff3d3d461743d74e1'); - expect(parts.filepath).toBe('directory/README.md'); - }); - }); - - describe('Cloning and resolving', function() { - it('should clone an HTTPS url', function() { - var git = new Git(path.join(os.tmpdir(), 'test-git-'+Date.now())); - return git.resolve('git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md') - .then(function(filename) { - expect(path.extname(filename)).toBe('.md'); - }); - }); - }); - -}); diff --git a/lib/utils/__tests__/location.js b/lib/utils/__tests__/location.js deleted file mode 100644 index 822338e..0000000 --- a/lib/utils/__tests__/location.js +++ /dev/null @@ -1,100 +0,0 @@ -var LocationUtils = require('../location'); - -describe('LocationUtils', function() { - it('should correctly test external location', function() { - expect(LocationUtils.isExternal('http://google.fr')).toBe(true); - expect(LocationUtils.isExternal('https://google.fr')).toBe(true); - expect(LocationUtils.isExternal('test.md')).toBe(false); - expect(LocationUtils.isExternal('folder/test.md')).toBe(false); - expect(LocationUtils.isExternal('/folder/test.md')).toBe(false); - expect(LocationUtils.isExternal('data:image/png')).toBe(false); - }); - - it('should correctly test data:uri location', function() { - expect(LocationUtils.isDataURI('data:image/png')).toBe(true); - expect(LocationUtils.isDataURI('http://google.fr')).toBe(false); - expect(LocationUtils.isDataURI('https://google.fr')).toBe(false); - expect(LocationUtils.isDataURI('test.md')).toBe(false); - expect(LocationUtils.isDataURI('data.md')).toBe(false); - }); - - it('should correctly detect anchor location', function() { - expect(LocationUtils.isAnchor('#test')).toBe(true); - expect(LocationUtils.isAnchor(' #test')).toBe(true); - expect(LocationUtils.isAnchor('https://google.fr#test')).toBe(false); - expect(LocationUtils.isAnchor('test.md#test')).toBe(false); - }); - - describe('.relative', function() { - it('should resolve to a relative path (same folder)', function() { - expect(LocationUtils.relative('links/', 'links/test.md')).toBe('test.md'); - }); - - it('should resolve to a relative path (parent folder)', function() { - expect(LocationUtils.relative('links/', 'test.md')).toBe('../test.md'); - }); - - it('should resolve to a relative path (child folder)', function() { - expect(LocationUtils.relative('links/', 'links/hello/test.md')).toBe('hello/test.md'); - }); - }); - - describe('.flatten', function() { - it('should remove leading slash', function() { - expect(LocationUtils.flatten('/test.md')).toBe('test.md'); - expect(LocationUtils.flatten('/hello/cool.md')).toBe('hello/cool.md'); - }); - - it('should remove leading slashes', function() { - expect(LocationUtils.flatten('///test.md')).toBe('test.md'); - }); - - it('should not break paths', function() { - expect(LocationUtils.flatten('hello/cool.md')).toBe('hello/cool.md'); - }); - }); - - describe('.toAbsolute', function() { - it('should correctly transform as absolute', function() { - expect(LocationUtils.toAbsolute('http://google.fr')).toBe('http://google.fr'); - expect(LocationUtils.toAbsolute('test.md', './', './')).toBe('test.md'); - expect(LocationUtils.toAbsolute('folder/test.md', './', './')).toBe('folder/test.md'); - }); - - it('should correctly handle windows path', function() { - expect(LocationUtils.toAbsolute('folder\\test.md', './', './')).toBe('folder/test.md'); - }); - - it('should correctly handle absolute path', function() { - expect(LocationUtils.toAbsolute('/test.md', './', './')).toBe('test.md'); - expect(LocationUtils.toAbsolute('/test.md', 'test', 'test')).toBe('../test.md'); - expect(LocationUtils.toAbsolute('/sub/test.md', 'test', 'test')).toBe('../sub/test.md'); - expect(LocationUtils.toAbsolute('/test.png', 'folder', '')).toBe('test.png'); - }); - - it('should correctly handle absolute path (windows)', function() { - expect(LocationUtils.toAbsolute('\\test.png', 'folder', '')).toBe('test.png'); - }); - - it('should resolve path starting by "/" in root directory', function() { - expect( - LocationUtils.toAbsolute('/test/hello.md', './', './') - ).toBe('test/hello.md'); - }); - - it('should resolve path starting by "/" in child directory', function() { - expect( - LocationUtils.toAbsolute('/test/hello.md', './hello', './') - ).toBe('test/hello.md'); - }); - - it('should resolve path starting by "/" in child directory, with same output directory', function() { - expect( - LocationUtils.toAbsolute('/test/hello.md', './hello', './hello') - ).toBe('../test/hello.md'); - }); - }); - -}); - - diff --git a/lib/utils/__tests__/path.js b/lib/utils/__tests__/path.js deleted file mode 100644 index 22bb016..0000000 --- a/lib/utils/__tests__/path.js +++ /dev/null @@ -1,17 +0,0 @@ -var path = require('path'); - -describe('Paths', function() { - var PathUtils = require('..//path'); - - describe('setExtension', function() { - it('should correctly change extension of filename', function() { - expect(PathUtils.setExtension('test.md', '.html')).toBe('test.html'); - expect(PathUtils.setExtension('test.md', '.json')).toBe('test.json'); - }); - - it('should correctly change extension of path', function() { - expect(PathUtils.setExtension('hello/test.md', '.html')).toBe(path.normalize('hello/test.html')); - expect(PathUtils.setExtension('hello/test.md', '.json')).toBe(path.normalize('hello/test.json')); - }); - }); -}); diff --git a/lib/utils/command.js b/lib/utils/command.js deleted file mode 100644 index 90a556e..0000000 --- a/lib/utils/command.js +++ /dev/null @@ -1,118 +0,0 @@ -var is = require('is'); -var childProcess = require('child_process'); -var spawn = require('spawn-cmd').spawn; -var Promise = require('./promise'); - -/** - Execute a command - - @param {String} command - @param {Object} options - @return {Promise} -*/ -function exec(command, options) { - var d = Promise.defer(); - - var child = childProcess.exec(command, options, function(err, stdout, stderr) { - if (!err) { - return d.resolve(); - } - - err.message = stdout.toString('utf8') + stderr.toString('utf8'); - d.reject(err); - }); - - child.stdout.on('data', function (data) { - d.notify(data); - }); - - child.stderr.on('data', function (data) { - d.notify(data); - }); - - return d.promise; -} - -/** - Spawn an executable - - @param {String} command - @param {Array} args - @param {Object} options - @return {Promise} -*/ -function spawnCmd(command, args, options) { - var d = Promise.defer(); - var child = spawn(command, args, options); - - child.on('error', function(error) { - return d.reject(error); - }); - - child.stdout.on('data', function (data) { - d.notify(data); - }); - - child.stderr.on('data', function (data) { - d.notify(data); - }); - - child.on('close', function(code) { - if (code === 0) { - d.resolve(); - } else { - d.reject(new Error('Error with command "'+command+'"')); - } - }); - - return d.promise; -} - -/** - Transform an option object to a command line string - - @param {String|number} value - @param {String} -*/ -function escapeShellArg(value) { - if (is.number(value)) { - return value; - } - - value = String(value); - value = value.replace(/"/g, '\\"'); - - return '"' + value + '"'; -} - -/** - Transform a map of options into a command line arguments string - - @param {Object} options - @return {String} -*/ -function optionsToShellArgs(options) { - var result = []; - - for (var key in options) { - var value = options[key]; - - if (value === null || value === undefined || value === false) { - continue; - } - - if (is.bool(value)) { - result.push(key); - } else { - result.push(key + '=' + escapeShellArg(value)); - } - } - - return result.join(' '); -} - -module.exports = { - exec: exec, - spawn: spawnCmd, - optionsToShellArgs: optionsToShellArgs -}; diff --git a/lib/utils/error.js b/lib/utils/error.js deleted file mode 100644 index 7686779..0000000 --- a/lib/utils/error.js +++ /dev/null @@ -1,99 +0,0 @@ -var is = require('is'); - -var TypedError = require('error/typed'); -var WrappedError = require('error/wrapped'); - - -// Enforce as an Error object, and cleanup message -function enforce(err) { - if (is.string(err)) err = new Error(err); - err.message = err.message.replace(/^Error: /, ''); - - return err; -} - -// Random error wrappers during parsing/generation -var ParsingError = WrappedError({ - message: 'Parsing Error: {origMessage}', - type: 'parse' -}); -var OutputError = WrappedError({ - message: 'Output Error: {origMessage}', - type: 'generate' -}); - -// A file does not exists -var FileNotFoundError = TypedError({ - type: 'file.not-found', - message: 'No "{filename}" file (or is ignored)', - filename: null -}); - -// A file cannot be parsed -var FileNotParsableError = TypedError({ - type: 'file.not-parsable', - message: '"{filename}" file cannot be parsed', - filename: null -}); - -// A file is outside the scope -var FileOutOfScopeError = TypedError({ - type: 'file.out-of-scope', - message: '"{filename}" not in "{root}"', - filename: null, - root: null, - code: 'EACCESS' -}); - -// A file is outside the scope -var RequireInstallError = TypedError({ - type: 'install.required', - message: '"{cmd}" is not installed.\n{install}', - cmd: null, - code: 'ENOENT', - install: '' -}); - -// Error for nunjucks templates -var TemplateError = WrappedError({ - message: 'Error compiling template "{filename}": {origMessage}', - type: 'template', - filename: null -}); - -// Error for nunjucks templates -var PluginError = WrappedError({ - message: 'Error with plugin "{plugin}": {origMessage}', - type: 'plugin', - plugin: null -}); - -// Error with the book's configuration -var ConfigurationError = WrappedError({ - message: 'Error with book\'s configuration: {origMessage}', - type: 'configuration' -}); - -// Error during ebook generation -var EbookError = WrappedError({ - message: 'Error during ebook generation: {origMessage}\n{stdout}', - type: 'ebook', - stdout: '' -}); - -module.exports = { - enforce: enforce, - - ParsingError: ParsingError, - OutputError: OutputError, - RequireInstallError: RequireInstallError, - - FileNotParsableError: FileNotParsableError, - FileNotFoundError: FileNotFoundError, - FileOutOfScopeError: FileOutOfScopeError, - - TemplateError: TemplateError, - PluginError: PluginError, - ConfigurationError: ConfigurationError, - EbookError: EbookError -}; diff --git a/lib/utils/fs.js b/lib/utils/fs.js deleted file mode 100644 index 35839a3..0000000 --- a/lib/utils/fs.js +++ /dev/null @@ -1,170 +0,0 @@ -var fs = require('graceful-fs'); -var mkdirp = require('mkdirp'); -var destroy = require('destroy'); -var rmdir = require('rmdir'); -var tmp = require('tmp'); -var request = require('request'); -var path = require('path'); -var cp = require('cp'); -var cpr = require('cpr'); - -var Promise = require('./promise'); - -// Write a stream to a file -function writeStream(filename, st) { - var d = Promise.defer(); - - var wstream = fs.createWriteStream(filename); - var cleanup = function() { - destroy(wstream); - wstream.removeAllListeners(); - }; - - wstream.on('finish', function () { - cleanup(); - d.resolve(); - }); - wstream.on('error', function (err) { - cleanup(); - d.reject(err); - }); - - st.on('error', function(err) { - cleanup(); - d.reject(err); - }); - - st.pipe(wstream); - - return d.promise; -} - -// Return a promise resolved with a boolean -function fileExists(filename) { - var d = Promise.defer(); - - fs.exists(filename, function(exists) { - d.resolve(exists); - }); - - return d.promise; -} - -// Generate temporary file -function genTmpFile(opts) { - return Promise.nfcall(tmp.file, opts) - .get(0); -} - -// Generate temporary dir -function genTmpDir(opts) { - return Promise.nfcall(tmp.dir, opts) - .get(0); -} - -// Download an image -function download(uri, dest) { - return writeStream(dest, request(uri)); -} - -// Find a filename available in a folder -function uniqueFilename(base, filename) { - var ext = path.extname(filename); - filename = path.resolve(base, filename); - filename = path.join(path.dirname(filename), path.basename(filename, ext)); - - var _filename = filename+ext; - - var i = 0; - while (fs.existsSync(filename)) { - _filename = filename + '_' + i + ext; - i = i + 1; - } - - return Promise(path.relative(base, _filename)); -} - -// Create all required folder to create a file -function ensureFile(filename) { - var base = path.dirname(filename); - return Promise.nfcall(mkdirp, base); -} - -// Remove a folder -function rmDir(base) { - return Promise.nfcall(rmdir, base, { - fs: fs - }); -} - -/** - Assert a file, if it doesn't exist, call "generator" - - @param {String} filePath - @param {Function} generator - @return {Promise} -*/ -function assertFile(filePath, generator) { - return fileExists(filePath) - .then(function(exists) { - if (exists) return; - - return generator(); - }); -} - -/** - Pick a file, returns the absolute path if exists, undefined otherwise - - @param {String} rootFolder - @param {String} fileName - @return {String} -*/ -function pickFile(rootFolder, fileName) { - var result = path.join(rootFolder, fileName); - if (fs.existsSync(result)) { - return result; - } - - return undefined; -} - -/** - Ensure that a directory exists and is empty - - @param {String} folder - @return {Promise} -*/ -function ensureFolder(rootFolder) { - return rmDir(rootFolder) - .fail(function() { - return Promise(); - }) - .then(function() { - return Promise.nfcall(mkdirp, rootFolder); - }); -} - -module.exports = { - exists: fileExists, - existsSync: fs.existsSync, - mkdirp: Promise.nfbind(mkdirp), - readFile: Promise.nfbind(fs.readFile), - writeFile: Promise.nfbind(fs.writeFile), - assertFile: assertFile, - pickFile: pickFile, - stat: Promise.nfbind(fs.stat), - statSync: fs.statSync, - readdir: Promise.nfbind(fs.readdir), - writeStream: writeStream, - readStream: fs.createReadStream, - copy: Promise.nfbind(cp), - copyDir: Promise.nfbind(cpr), - tmpFile: genTmpFile, - tmpDir: genTmpDir, - download: download, - uniqueFilename: uniqueFilename, - ensureFile: ensureFile, - ensureFolder: ensureFolder, - rmDir: rmDir -}; diff --git a/lib/utils/genKey.js b/lib/utils/genKey.js deleted file mode 100644 index 0650011..0000000 --- a/lib/utils/genKey.js +++ /dev/null @@ -1,13 +0,0 @@ -var lastKey = 0; - -/* - Generate a random key - @return {String} -*/ -function generateKey() { - lastKey += 1; - var str = lastKey.toString(16); - return '00000'.slice(str.length) + str; -} - -module.exports = generateKey; diff --git a/lib/utils/git.js b/lib/utils/git.js deleted file mode 100644 index 6884b83..0000000 --- a/lib/utils/git.js +++ /dev/null @@ -1,133 +0,0 @@ -var is = require('is'); -var path = require('path'); -var crc = require('crc'); -var URI = require('urijs'); - -var pathUtil = require('./path'); -var Promise = require('./promise'); -var command = require('./command'); -var fs = require('./fs'); - -var GIT_PREFIX = 'git+'; - -function Git() { - this.tmpDir; - this.cloned = {}; -} - -// Return an unique ID for a combinaison host/ref -Git.prototype.repoID = function(host, ref) { - return crc.crc32(host+'#'+(ref || '')).toString(16); -}; - -// Allocate a temporary folder for cloning repos in it -Git.prototype.allocateDir = function() { - var that = this; - - if (this.tmpDir) return Promise(); - - return fs.tmpDir() - .then(function(dir) { - that.tmpDir = dir; - }); -}; - -// Clone a git repository if non existant -Git.prototype.clone = function(host, ref) { - var that = this; - - return this.allocateDir() - - // Return or clone the git repo - .then(function() { - // Unique ID for repo/ref combinaison - var repoId = that.repoID(host, ref); - - // Absolute path to the folder - var repoPath = path.join(that.tmpDir, repoId); - - if (that.cloned[repoId]) return repoPath; - - // Clone repo - return command.exec('git clone '+host+' '+repoPath) - - // Checkout reference if specified - .then(function() { - that.cloned[repoId] = true; - - if (!ref) return; - return command.exec('git checkout '+ref, { cwd: repoPath }); - }) - .thenResolve(repoPath); - }); -}; - -// Get file from a git repo -Git.prototype.resolve = function(giturl) { - // Path to a file in a git repo? - if (!Git.isUrl(giturl)) { - if (this.resolveRoot(giturl)) return Promise(giturl); - return Promise(null); - } - if (is.string(giturl)) giturl = Git.parseUrl(giturl); - if (!giturl) return Promise(null); - - // Clone or get from cache - return this.clone(giturl.host, giturl.ref) - .then(function(repo) { - return path.resolve(repo, giturl.filepath); - }); -}; - -// Return root of git repo from a filepath -Git.prototype.resolveRoot = function(filepath) { - var relativeToGit, repoId; - - // No git repo cloned, or file is not in a git repository - if (!this.tmpDir || !pathUtil.isInRoot(this.tmpDir, filepath)) return null; - - // Extract first directory (is the repo id) - relativeToGit = path.relative(this.tmpDir, filepath); - repoId = relativeToGit.split(path.sep)[0]; - if (!repoId) { - return; - } - - // Return an absolute file - return path.resolve(this.tmpDir, repoId); -}; - -// Check if an url is a git dependency url -Git.isUrl = function(giturl) { - return (giturl.indexOf(GIT_PREFIX) === 0); -}; - -// Parse and extract infos -Git.parseUrl = function(giturl) { - var ref, uri, fileParts, filepath; - - if (!Git.isUrl(giturl)) return null; - giturl = giturl.slice(GIT_PREFIX.length); - - uri = new URI(giturl); - ref = uri.fragment() || null; - uri.fragment(null); - - // Extract file inside the repo (after the .git) - fileParts = uri.path().split('.git'); - filepath = fileParts.length > 1? fileParts.slice(1).join('.git') : ''; - if (filepath[0] == '/') { - filepath = filepath.slice(1); - } - - // Recreate pathname without the real filename - uri.path(fileParts[0] + '.git'); - - return { - host: uri.toString(), - ref: ref, - filepath: filepath - }; -}; - -module.exports = Git; diff --git a/lib/utils/images.js b/lib/utils/images.js deleted file mode 100644 index 6d4b927..0000000 --- a/lib/utils/images.js +++ /dev/null @@ -1,60 +0,0 @@ -var Promise = require('./promise'); -var command = require('./command'); -var fs = require('./fs'); -var error = require('./error'); - -// Convert a svg file to a pmg -function convertSVGToPNG(source, dest, options) { - if (!fs.existsSync(source)) return Promise.reject(new error.FileNotFoundError({ filename: source })); - - return command.spawn('svgexport', [source, dest]) - .fail(function(err) { - if (err.code == 'ENOENT') { - err = error.RequireInstallError({ - cmd: 'svgexport', - install: 'Install it using: "npm install svgexport -g"' - }); - } - throw err; - }) - .then(function() { - if (fs.existsSync(dest)) return; - - throw new Error('Error converting '+source+' into '+dest); - }); -} - -// Convert a svg buffer to a png file -function convertSVGBufferToPNG(buf, dest) { - // Create a temporary SVG file to convert - return fs.tmpFile({ - postfix: '.svg' - }) - .then(function(tmpSvg) { - return fs.writeFile(tmpSvg, buf) - .then(function() { - return convertSVGToPNG(tmpSvg, dest); - }); - }); -} - -// Converts a inline data: to png file -function convertInlinePNG(source, dest) { - if (!/^data\:image\/png/.test(source)) return Promise.reject(new Error('Source is not a PNG data-uri')); - - var base64data = source.split('data:image/png;base64,')[1]; - var buf = new Buffer(base64data, 'base64'); - - return fs.writeFile(dest, buf) - .then(function() { - if (fs.existsSync(dest)) return; - - throw new Error('Error converting '+source+' into '+dest); - }); -} - -module.exports = { - convertSVGToPNG: convertSVGToPNG, - convertSVGBufferToPNG: convertSVGBufferToPNG, - convertInlinePNG: convertInlinePNG -};
\ No newline at end of file diff --git a/lib/utils/location.js b/lib/utils/location.js deleted file mode 100644 index 00d8004..0000000 --- a/lib/utils/location.js +++ /dev/null @@ -1,139 +0,0 @@ -var url = require('url'); -var path = require('path'); - -// Is the url an external url -function isExternal(href) { - try { - return Boolean(url.parse(href).protocol) && !isDataURI(href); - } catch(err) { - return false; - } -} - -// Is the url an iniline data-uri -function isDataURI(href) { - try { - return Boolean(url.parse(href).protocol) && (url.parse(href).protocol === 'data:'); - } catch(err) { - return false; - } -} - -// Inverse of isExternal -function isRelative(href) { - return !isExternal(href); -} - -// Return true if the link is an achor -function isAnchor(href) { - try { - var parsed = url.parse(href); - return !!(!parsed.protocol && !parsed.path && parsed.hash); - } catch(err) { - return false; - } -} - -// Normalize a path to be a link -function normalize(s) { - return path.normalize(s).replace(/\\/g, '/'); -} - -/** - * Flatten a path, it removes the leading "/" - * - * @param {String} href - * @return {String} - */ -function flatten(href) { - href = normalize(href); - if (href[0] == '/') { - href = normalize(href.slice(1)); - } - - return href; -} - -/** - * Convert a relative path to absolute - * - * @param {String} href - * @param {String} dir: directory parent of the file currently in rendering process - * @param {String} outdir: directory parent from the html output - * @return {String} - */ -function toAbsolute(_href, dir, outdir) { - if (isExternal(_href) || isDataURI(_href)) { - return _href; - } - - outdir = outdir == undefined? dir : outdir; - - _href = normalize(_href); - dir = normalize(dir); - outdir = normalize(outdir); - - // Path "_href" inside the base folder - var hrefInRoot = normalize(path.join(dir, _href)); - if (_href[0] == '/') { - hrefInRoot = normalize(_href.slice(1)); - } - - // Make it relative to output - _href = path.relative(outdir, hrefInRoot); - - // Normalize windows paths - _href = normalize(_href); - - return _href; -} - -/** - * Convert an absolute path to a relative path for a specific folder (dir) - * ('test/', 'hello.md') -> '../hello.md' - * - * @param {String} dir: current directory - * @param {String} file: absolute path of file - * @return {String} - */ -function relative(dir, file) { - var isDirectory = file.slice(-1) === '/'; - return normalize(path.relative(dir, file)) + (isDirectory? '/': ''); -} - -/** - * Convert an absolute path to a relative path for a specific folder (dir) - * ('test/test.md', 'hello.md') -> '../hello.md' - * - * @param {String} baseFile: current file - * @param {String} file: absolute path of file - * @return {String} - */ -function relativeForFile(baseFile, file) { - return relative(path.dirname(baseFile), file); -} - -/** - * Compare two paths, return true if they are identical - * ('README.md', './README.md') -> true - * - * @param {String} p1: first path - * @param {String} p2: second path - * @return {Boolean} - */ -function areIdenticalPaths(p1, p2) { - return normalize(p1) === normalize(p2); -} - -module.exports = { - areIdenticalPaths: areIdenticalPaths, - isDataURI: isDataURI, - isExternal: isExternal, - isRelative: isRelative, - isAnchor: isAnchor, - normalize: normalize, - toAbsolute: toAbsolute, - relative: relative, - relativeForFile: relativeForFile, - flatten: flatten -}; diff --git a/lib/utils/logger.js b/lib/utils/logger.js deleted file mode 100644 index 6fac92b..0000000 --- a/lib/utils/logger.js +++ /dev/null @@ -1,172 +0,0 @@ -var is = require('is'); -var util = require('util'); -var color = require('bash-color'); -var Immutable = require('immutable'); - -var LEVELS = Immutable.Map({ - DEBUG: 0, - INFO: 1, - WARN: 2, - ERROR: 3, - DISABLED: 10 -}); - -var COLORS = Immutable.Map({ - DEBUG: color.purple, - INFO: color.cyan, - WARN: color.yellow, - ERROR: color.red -}); - -function Logger(write, logLevel) { - if (!(this instanceof Logger)) return new Logger(write, logLevel); - - this._write = write || function(msg) { - if(process.stdout) { - process.stdout.write(msg); - } - }; - this.lastChar = '\n'; - - this.setLevel(logLevel || 'info'); - - // Create easy-to-use method like "logger.debug.ln('....')" - LEVELS.forEach(function(level, levelKey) { - if (levelKey === 'DISABLED') { - return; - } - levelKey = levelKey.toLowerCase(); - - this[levelKey] = this.log.bind(this, level); - this[levelKey].ln = this.logLn.bind(this, level); - this[levelKey].ok = this.ok.bind(this, level); - this[levelKey].fail = this.fail.bind(this, level); - this[levelKey].promise = this.promise.bind(this, level); - }, this); -} - -/** - Change minimum level - - @param {String} logLevel -*/ -Logger.prototype.setLevel = function(logLevel) { - if (is.string(logLevel)) { - logLevel = logLevel.toUpperCase(); - logLevel = LEVELS.get(logLevel); - } - - this.logLevel = logLevel; -}; - -/** - Return minimum logging level - - @return {Number} -*/ -Logger.prototype.getLevel = function(logLevel) { - return this.logLevel; -}; - -/** - Print a simple string - - @param {String} -*/ -Logger.prototype.write = function(msg) { - msg = msg.toString(); - this.lastChar = msg[msg.length - 1]; - return this._write(msg); -}; - -/** - Format a string using the first argument as a printf-like format. -*/ -Logger.prototype.format = function() { - return util.format.apply(util, arguments); -}; - -/** - Print a line - - @param {String} -*/ -Logger.prototype.writeLn = function(msg) { - return this.write((msg || '')+'\n'); -}; - -/** - Log/Print a message if level is allowed - - @param {Number} level -*/ -Logger.prototype.log = function(level) { - if (level < this.logLevel) return; - - var levelKey = LEVELS.findKey(function(v) { - return v === level; - }); - var args = Array.prototype.slice.apply(arguments, [1]); - var msg = this.format.apply(this, args); - - if (this.lastChar == '\n') { - msg = COLORS.get(levelKey)(levelKey.toLowerCase()+':')+' '+msg; - } - - return this.write(msg); -}; - -/** - Log/Print a line if level is allowed -*/ -Logger.prototype.logLn = function() { - if (this.lastChar != '\n') this.write('\n'); - - var args = Array.prototype.slice.apply(arguments); - args.push('\n'); - return this.log.apply(this, args); -}; - -/** - Log a confirmation [OK] -*/ -Logger.prototype.ok = function(level) { - var args = Array.prototype.slice.apply(arguments, [1]); - var msg = this.format.apply(this, args); - if (arguments.length > 1) { - this.logLn(level, color.green('>> ') + msg.trim().replace(/\n/g, color.green('\n>> '))); - } else { - this.log(level, color.green('OK'), '\n'); - } -}; - -/** - Log a "FAIL" -*/ -Logger.prototype.fail = function(level) { - return this.log(level, color.red('ERROR') + '\n'); -}; - -/** - Log state of a promise - - @param {Number} level - @param {Promise} - @return {Promise} -*/ -Logger.prototype.promise = function(level, p) { - var that = this; - - return p. - then(function(st) { - that.ok(level); - return st; - }, function(err) { - that.fail(level); - throw err; - }); -}; - -Logger.LEVELS = LEVELS; - -module.exports = Logger; diff --git a/lib/utils/mergeDefaults.js b/lib/utils/mergeDefaults.js deleted file mode 100644 index 47a374b..0000000 --- a/lib/utils/mergeDefaults.js +++ /dev/null @@ -1,16 +0,0 @@ -var Immutable = require('immutable'); - -/** - * Merge - * @param {Object|Map} obj - * @param {Object|Map} src - * @return {Object} - */ -function mergeDefaults(obj, src) { - var objValue = Immutable.fromJS(obj); - var srcValue = Immutable.fromJS(src); - - return srcValue.mergeDeep(objValue).toJS(); -} - -module.exports = mergeDefaults; diff --git a/lib/utils/path.js b/lib/utils/path.js deleted file mode 100644 index 26b6005..0000000 --- a/lib/utils/path.js +++ /dev/null @@ -1,74 +0,0 @@ -var path = require('path'); -var error = require('./error'); - -// Normalize a filename -function normalizePath(filename) { - return path.normalize(filename); -} - -// Return true if file path is inside a folder -function isInRoot(root, filename) { - root = path.normalize(root); - filename = path.normalize(filename); - - if (root === '.') { - return true; - } - if (root[root.length - 1] != path.sep) { - root = root + path.sep; - } - - return (filename.substr(0, root.length) === root); -} - -// Resolve paths in a specific folder -// Throw error if file is outside this folder -function resolveInRoot(root) { - var input, result; - var args = Array.prototype.slice.call(arguments, 1); - - input = args - .reduce(function(current, p) { - // Handle path relative to book root ("/README.md") - if (p[0] == '/' || p[0] == '\\') return p.slice(1); - - return current? path.join(current, p) : path.normalize(p); - }, ''); - - result = path.resolve(root, input); - - if (!isInRoot(root, result)) { - throw new error.FileOutOfScopeError({ - filename: result, - root: root - }); - } - - return result; -} - -// Chnage extension of a file -function setExtension(filename, ext) { - return path.join( - path.dirname(filename), - path.basename(filename, path.extname(filename)) + ext - ); -} - -/* - Return true if a filename is relative. - - @param {String} - @return {Boolean} -*/ -function isPureRelative(filename) { - return (filename.indexOf('./') === 0 || filename.indexOf('../') === 0); -} - -module.exports = { - isInRoot: isInRoot, - resolveInRoot: resolveInRoot, - normalize: normalizePath, - setExtension: setExtension, - isPureRelative: isPureRelative -}; diff --git a/lib/utils/promise.js b/lib/utils/promise.js deleted file mode 100644 index b5cca4b..0000000 --- a/lib/utils/promise.js +++ /dev/null @@ -1,148 +0,0 @@ -var Q = require('q'); -var Immutable = require('immutable'); - -// Debugging for long stack traces -if (process.env.DEBUG || process.env.CI) { - Q.longStackSupport = true; -} - -/** - * Reduce an array to a promise - * - * @param {Array|List} arr - * @param {Function(value, element, index)} - * @return {Promise<Mixed>} - */ -function reduce(arr, iter, base) { - arr = Immutable.Iterable.isIterable(arr)? arr : Immutable.List(arr); - - return arr.reduce(function(prev, elem, key) { - return prev - .then(function(val) { - return iter(val, elem, key); - }); - }, Q(base)); -} - -/** - * Iterate over an array using an async iter - * - * @param {Array|List} arr - * @param {Function(value, element, index)} - * @return {Promise} - */ -function forEach(arr, iter) { - return reduce(arr, function(val, el, key) { - return iter(el, key); - }); -} - -/** - * Transform an array - * - * @param {Array|List} arr - * @param {Function(value, element, index)} - * @return {Promise} - */ -function serie(arr, iter, base) { - return reduce(arr, function(before, item, key) { - return Q(iter(item, key)) - .then(function(r) { - before.push(r); - return before; - }); - }, []); -} - -/** - * Iter over an array and return first result (not null) - * - * @param {Array|List} arr - * @param {Function(element, index)} - * @return {Promise<Mixed>} - */ -function some(arr, iter) { - arr = Immutable.List(arr); - - return arr.reduce(function(prev, elem, i) { - return prev.then(function(val) { - if (val) return val; - - return iter(elem, i); - }); - }, Q()); -} - -/** - * Map an array using an async (promised) iterator - * - * @param {Array|List} arr - * @param {Function(element, index)} - * @return {Promise<List>} - */ -function mapAsList(arr, iter) { - return reduce(arr, function(prev, entry, i) { - return Q(iter(entry, i)) - .then(function(out) { - prev.push(out); - return prev; - }); - }, []); -} - -/** - * Map an array or map - * - * @param {Array|List|Map|OrderedMap} arr - * @param {Function(element, key)} - * @return {Promise<List|Map|OrderedMap>} - */ -function map(arr, iter) { - if (Immutable.Map.isMap(arr)) { - var type = 'Map'; - if (Immutable.OrderedMap.isOrderedMap(arr)) { - type = 'OrderedMap'; - } - - return mapAsList(arr, function(value, key) { - return Q(iter(value, key)) - .then(function(result) { - return [key, result]; - }); - }) - .then(function(result) { - return Immutable[type](result); - }); - } else { - return mapAsList(arr, iter) - .then(function(result) { - return Immutable.List(result); - }); - } -} - - -/** - * Wrap a function in a promise - * - * @param {Function} func - * @return {Funciton} - */ -function wrap(func) { - return function() { - var args = Array.prototype.slice.call(arguments, 0); - - return Q() - .then(function() { - return func.apply(null, args); - }); - }; -} - -module.exports = Q; -module.exports.forEach = forEach; -module.exports.reduce = reduce; -module.exports.map = map; -module.exports.serie = serie; -module.exports.some = some; -module.exports.wrapfn = wrap; diff --git a/lib/utils/reducedObject.js b/lib/utils/reducedObject.js deleted file mode 100644 index 7bcfd5b..0000000 --- a/lib/utils/reducedObject.js +++ /dev/null @@ -1,33 +0,0 @@ -var Immutable = require('immutable'); - -/** - * Reduce the difference between a map and its default version - * @param {Map} defaultVersion - * @param {Map} currentVersion - * @return {Map} The properties of currentVersion that differs from defaultVersion - */ -function reducedObject(defaultVersion, currentVersion) { - if(defaultVersion === undefined) { - return currentVersion; - } - - return currentVersion.reduce(function(result, value, key) { - var defaultValue = defaultVersion.get(key); - - if (Immutable.Map.isMap(value)) { - var diffs = reducedObject(defaultValue, value); - - if (diffs.size > 0) { - return result.set(key, diffs); - } - } - - if (Immutable.is(defaultValue, value)) { - return result; - } - - return result.set(key, value); - }, Immutable.Map()); -} - -module.exports = reducedObject; diff --git a/lib/utils/timing.js b/lib/utils/timing.js deleted file mode 100644 index e6b0323..0000000 --- a/lib/utils/timing.js +++ /dev/null @@ -1,97 +0,0 @@ -var Immutable = require('immutable'); -var is = require('is'); - -var timers = {}; -var startDate = Date.now(); - -/** - Mesure an operation - - @parqm {String} type - @param {Promise} p - @return {Promise} -*/ -function measure(type, p) { - timers[type] = timers[type] || { - type: type, - count: 0, - total: 0, - min: undefined, - max: 0 - }; - - var start = Date.now(); - - return p - .fin(function() { - var end = Date.now(); - var duration = (end - start); - - timers[type].count ++; - timers[type].total += duration; - - if (is.undefined(timers[type].min)) { - timers[type].min = duration; - } else { - timers[type].min = Math.min(timers[type].min, duration); - } - - timers[type].max = Math.max(timers[type].max, duration); - }); -} - -/** - Return a milliseconds number as a second string - - @param {Number} ms - @return {String} -*/ -function time(ms) { - if (ms < 1000) { - return (ms.toFixed(0)) + 'ms'; - } - - return (ms/1000).toFixed(2) + 's'; -} - -/** - Dump all timers to a logger - - @param {Logger} logger -*/ -function dump(logger) { - var prefix = ' > '; - var measured = 0; - var totalDuration = Date.now() - startDate; - - // Enable debug logging - var logLevel = logger.getLevel(); - logger.setLevel('debug'); - - Immutable.Map(timers) - .valueSeq() - .sortBy(function(timer) { - measured += timer.total; - return timer.total; - }) - .forEach(function(timer) { - var percent = (timer.total * 100) / totalDuration; - - - logger.debug.ln((percent.toFixed(1)) + '% of time spent in "' + timer.type + '" (' + timer.count + ' times) :'); - logger.debug.ln(prefix + 'Total: ' + time(timer.total)+ ' | Average: ' + time(timer.total / timer.count)); - logger.debug.ln(prefix + 'Min: ' + time(timer.min) + ' | Max: ' + time(timer.max)); - logger.debug.ln('---------------------------'); - }); - - - logger.debug.ln(time(totalDuration - measured) + ' spent in non-mesured sections'); - - // Rollback to previous level - logger.setLevel(logLevel); -} - -module.exports = { - measure: measure, - dump: dump -}; |