summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/backbone/summary.js17
-rw-r--r--lib/book.js11
-rw-r--r--lib/cli/index.js12
-rw-r--r--lib/init.js68
-rw-r--r--test/all.js3
-rw-r--r--test/assertions.js19
-rw-r--r--test/init.js57
7 files changed, 179 insertions, 8 deletions
diff --git a/lib/backbone/summary.js b/lib/backbone/summary.js
index 4ae3453..a79b1e9 100644
--- a/lib/backbone/summary.js
+++ b/lib/backbone/summary.js
@@ -6,7 +6,6 @@ var location = require('../utils/location');
var error = require('../utils/error');
var BackboneFile = require('./file');
-
/*
An article represent an entry in the Summary.
It's defined by a title, a reference, and children articles,
@@ -37,8 +36,9 @@ function TOCArticle(def, parent) {
var parts = url.parse(this.ref);
if (!this.isExternal()) {
- this.path = parts.pathname;
- this.anchor = parts.hash;
+ var parts = this.ref.split('#');
+ this.path = (parts.length > 1? parts.slice(0, -1).join('#') : this.ref);
+ this.anchor = (parts.length > 1? '#' + _.last(parts) : null);
}
}
@@ -264,6 +264,17 @@ Summary.prototype.find = function(filter) {
return result;
};
+// Flatten the list of articles
+Summary.prototype.flatten = function() {
+ var result = [];
+
+ this.walk(function(article) {
+ result.push(article);
+ });
+
+ return result;
+};
+
// Return the first TOCArticle for a specific page (or path)
Summary.prototype.getArticle = function(page) {
if (!_.isString(page)) page = page.path;
diff --git a/lib/book.js b/lib/book.js
index a678181..09bd59f 100644
--- a/lib/book.js
+++ b/lib/book.js
@@ -13,6 +13,7 @@ var error = require('./utils/error');
var Promise = require('./utils/promise');
var Logger = require('./utils/logger');
var parsers = require('./parsers');
+var initBook = require('./init');
/*
@@ -103,10 +104,10 @@ function Book(opts) {
}
});
+ _.bindAll(this);
+
// Loop for template filters/blocks
error.deprecateField(this, 'book', this, '"book" property is deprecated, use "this" directly instead');
-
- _.bindAll(this);
}
// Return templating context for the book
@@ -368,5 +369,11 @@ Book.setup = function(fs, root, opts) {
});
};
+// Initialize a book
+Book.init = function(fs, root, opts) {
+ return Book.setup(fs, root, opts)
+ .then(initBook);
+};
+
module.exports = Book;
diff --git a/lib/cli/index.js b/lib/cli/index.js
index 4d3d364..f1aca5e 100644
--- a/lib/cli/index.js
+++ b/lib/cli/index.js
@@ -7,6 +7,7 @@ var tinylr = require('tiny-lr');
var Promise = require('../utils/promise');
var PluginsManager = require('../plugins');
var Book = require('../book');
+var initBook = require('../init');
var helper = require('./helper');
var Server = require('./server');
@@ -14,6 +15,17 @@ var watch = require('./watch');
module.exports = {
commands: [
+ {
+ name: 'init [book]',
+ description: 'setup and create files for chapters',
+ options: [
+ helper.options.log
+ ],
+ exec: function(args) {
+ var input = path.resolve(args[0] || process.cwd());
+ return Book.init(helper.nodeFS, input);
+ }
+ },
{
name: 'parse [book]',
diff --git a/lib/init.js b/lib/init.js
new file mode 100644
index 0000000..ea75a77
--- /dev/null
+++ b/lib/init.js
@@ -0,0 +1,68 @@
+var _ = require('lodash');
+var path = require('path');
+
+var fs = require('./utils/fs');
+var Promise = require('./utils/promise');
+
+// Initialize folder structure for a book
+// Read SUMMARY to created the right chapter
+function initBook(book) {
+ var extensionToUse = '.md';
+
+ book.log.info.ln('init book at', book.root);
+ return fs.mkdirp(book.root)
+ .then(function() {
+ return book.config.load();
+ })
+ .then(function() {
+ book.log.info.ln('detect structure from SUMMARY (if it exists)');
+ return book.summary.load();
+ })
+ .then(function() {
+ var summary = book.summary.path || 'SUMMARY.md';
+ var articles = book.summary.flatten();
+
+ // Use extension of summary
+ extensionToUse = path.extname(summary);
+
+ // Readme doesn't have a path
+ if (!articles[0].path) {
+ articles[0].path = 'README' + extensionToUse;
+ }
+
+ // Summary doesn't exists? create one
+ if (!book.summary.path) {
+ articles.push({
+ title: 'Summary',
+ path: 'SUMMARY'+extensionToUse
+ });
+ }
+
+ // Create files that don't exist
+ return Promise.serie(articles, function(article) {
+ if (!article.path) return;
+
+ var absolutePath = book.resolve(article.path);
+
+ return fs.exists(absolutePath)
+ .then(function(exists) {
+ if(exists) {
+ book.log.info.ln('found', article.path);
+ return;
+ } else {
+ book.log.info.ln('create', article.path);
+ }
+
+ return fs.mkdirp(path.dirname(absolutePath))
+ .then(function() {
+ return fs.writeFile(absolutePath, '# '+article.title+'\n\n');
+ });
+ });
+ });
+ })
+ .then(function() {
+ book.log.info.ln('initialization is finished');
+ });
+}
+
+module.exports = initBook;
diff --git a/test/all.js b/test/all.js
index 9c82c91..67b1661 100644
--- a/test/all.js
+++ b/test/all.js
@@ -25,3 +25,6 @@ require('./assets-inliner');
require('./output-json');
require('./output-website');
require('./output-ebook');
+
+// Misc
+require('./init');
diff --git a/test/assertions.js b/test/assertions.js
index 56da249..f96fdc1 100644
--- a/test/assertions.js
+++ b/test/assertions.js
@@ -1,18 +1,31 @@
var fs = require('fs');
+var path = require('path');
var _ = require('lodash');
var cheerio = require('cheerio');
var should = require('should');
// Assertions to test if an Output has generated a file
should.Assertion.add('file', function(file, description) {
+ var rootFolder;
+ if (_.isFunction(this.obj.root)) {
+ rootFolder = this.obj.root();
+ } else {
+ rootFolder = this.obj;
+ }
+
this.params = {
- actual: this.obj.root(),
+ actual: rootFolder,
operator: 'have file ' + file,
message: description
};
- this.obj.should.have.property('resolve').which.is.a.Function;
- this.assert(fs.existsSync(this.obj.resolve(file)));
+ if (_.isFunction(this.obj.resolve)) {
+ file = this.obj.resolve(file);
+ } else {
+ file = path.resolve(rootFolder, file);
+ }
+
+ this.assert(fs.existsSync(file));
});
should.Assertion.add('html', function(rules, description) {
diff --git a/test/init.js b/test/init.js
new file mode 100644
index 0000000..e1e8a4a
--- /dev/null
+++ b/test/init.js
@@ -0,0 +1,57 @@
+var Book = require('../lib/book');
+var mock = require('./mock');
+
+describe('Init', function() {
+
+ it('should create file according to summary', function() {
+ return mock.setupFS({
+ 'SUMMARY.md': '# Summary\n\n'
+ + '* [Hello](hello.md)\n'
+ + '* [Hello 2](hello 2.md)\n'
+ })
+ .then(function(rootFolder) {
+ return Book.init(mock.fs, rootFolder, {
+ log: function() {}
+ })
+ .then(function() {
+ rootFolder.should.have.file('SUMMARY.md');
+ rootFolder.should.have.file('README.md');
+ rootFolder.should.have.file('hello.md');
+ rootFolder.should.have.file('hello 2.md');
+ });
+ })
+ });
+
+ it('should create file subfolder', function() {
+ return mock.setupFS({
+ 'SUMMARY.md': '# Summary\n\n'
+ + '* [Hello](test/hello.md)\n'
+ + '* [Hello 2](test/test2/world.md)\n'
+ })
+ .then(function(rootFolder) {
+ return Book.init(mock.fs, rootFolder, {
+ log: function() {}
+ })
+ .then(function() {
+ rootFolder.should.have.file('README.md');
+ rootFolder.should.have.file('SUMMARY.md');
+ rootFolder.should.have.file('test/hello.md');
+ rootFolder.should.have.file('test/test2/world.md');
+ });
+ })
+ });
+
+ it('should create SUMMARY if non-existant', function() {
+ return mock.setupFS({})
+ .then(function(rootFolder) {
+ return Book.init(mock.fs, rootFolder, {
+ log: function() {}
+ })
+ .then(function() {
+ rootFolder.should.have.file('SUMMARY.md');
+ rootFolder.should.have.file('README.md');
+ });
+ })
+ });
+
+});