Compare commits

...

25 Commits

Author SHA1 Message Date
Aaron O'Mullan 3426428142 Bump version to 0.5.5 2014-06-18 00:25:23 -07:00
Aaron O'Mullan 6c5c8e6eb7 Fix LANGS.md parsing
And cleanup Summary parsing
2014-06-17 16:18:08 -07:00
Aaron O'Mullan 3aefcaea73 Bump version to 0.5.4 2014-06-17 10:56:45 -07:00
Aaron O'Mullan 04c025774d Improve robustness of link utility functions
Fixes #321, fixes #323
2014-06-17 10:56:13 -07:00
Aaron O'Mullan 80aac7f581 Support --verbose flag, fixes #325 2014-06-17 10:46:56 -07:00
Samy Pessé 266f7ca73d Add infos about debugging 2014-06-17 18:45:44 +02:00
Aaron O'Mullan d6eb8e4c60 Support URLs in "github" config
Fixes GitbookIO/gitbook.io#90
2014-06-16 18:52:36 -07:00
Aaron O'Mullan c87edb155a Better string slicing in parse.git.githubID() 2014-06-16 17:48:18 -07:00
Samy Pessé ef0468ad5d Fix #310: improve display of long title in header 2014-06-16 14:03:28 +02:00
Samy Pessé f03153eb0c Remove page break between articles in ebook
Add title for exercise solutions sections
2014-06-16 13:55:07 +02:00
Samy Pessé 3d8b6e550a Merge pull request #319 from GitbookIO/enchancement/custom_intro
Enchancement/custom intro
2014-06-16 13:02:38 +02:00
Aaron O'Mullan 4c3dbca5ca Normalize intro node in Summary parsing 2014-06-16 00:26:11 -07:00
Aaron O'Mullan a258feea37 Remove special case in navigation
For intro node
2014-06-16 00:25:59 -07:00
Aaron O'Mullan 3a3332d0b9 Adapt summary tests to new summary parsing
We now insert default README.md (introduction) node in parsing the
SUMMARY.md logic
2014-06-16 00:25:33 -07:00
Aaron O'Mullan 4259283f32 Refactor Summary rendering
Introduction is no longer an edge case
2014-06-16 00:09:48 -07:00
Aaron O'Mullan 243d977cd6 Add test for level regarding #318 2014-06-15 23:36:40 -07:00
Aaron O'Mullan 94bbbecedf Support custom intro node, fixes #318 2014-06-15 23:30:43 -07:00
Aaron O'Mullan f49eab42a9 Fix test for #318 2014-06-15 23:29:24 -07:00
Aaron O'Mullan ce6b2bc47d Add extra navigation tests for #318 2014-06-15 23:19:28 -07:00
Aaron O'Mullan 3245d7e430 Drop Travis tests for Node 0.8 2014-06-15 22:49:15 -07:00
Aaron O'Mullan 6ee17c32ae Bump version to 0.5.3 2014-06-15 22:39:18 -07:00
Aaron O'Mullan bf7ffe6d4c Skip extra files and folders in copy phase of generation 2014-06-15 22:37:15 -07:00
Aaron O'Mullan ec7bc6b347 Add link to gitbook.io in readme 2014-06-15 22:36:51 -07:00
Aaron O'Mullan b425db0979 Allow external links in summary, fixes #300 2014-06-08 12:31:52 -07:00
Samy Pessé 30c6d1c9de Remove useless log 2014-06-08 02:21:05 +02:00
22 changed files with 186 additions and 63 deletions
-1
View File
@@ -2,4 +2,3 @@ language: node_js
node_js:
- "0.11"
- "0.10"
- "0.8"
+10 -1
View File
@@ -3,7 +3,7 @@ GitBook
[![Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook)
GitBook is a command line tool (and Node.js library) for building beautiful books and exercises using GitHub/Git and Markdown. You can see an example: [Learn Javascript](https://www.gitbook.io/book/GitBookIO/javascript). An [editor](https://github.com/GitbookIO/editor) is available for Windows, Mac and Linux. You can follow [@GitBookIO](https://twitter.com/GitBookIO) on Twitter. Complete documentation is available at [help.gitbook.io](http://help.gitbook.io/).
GitBook is a command line tool (and Node.js library) for building beautiful books and exercises using GitHub/Git and Markdown. Here is an example: [Learn Javascript](https://www.gitbook.io/book/GitBookIO/javascript). You can publish book easily online using [gitbook.io](https://www.gitbook.io) and an [editor](https://github.com/GitbookIO/editor) is available for Windows, Mac and Linux. You can follow [@GitBookIO](https://twitter.com/GitBookIO) on Twitter. Complete documentation is available at [help.gitbook.io](http://help.gitbook.io/).
![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png)
@@ -253,3 +253,12 @@ Plugins can used to extend your book's functionality. Read [GitbookIO/plugin](ht
* [Markdown within HTML](https://github.com/mrpotes/gitbook-plugin-nestedmd): Process markdown within HTML blocks - allows custom layout options for individual pages
* [Bootstrap JavaScript plugins](https://github.com/mrpotes/gitbook-plugin-bootstrapjs): Use the [Bootstrap JavaScript plugins](http://getbootstrap.com/javascript) in your online GitBook
* [Piwik Open Analytics](https://github.com/emmanuel-keller/gitbook-plugin-piwik): Piwik Open Analytics tracking for your book
#### Debugging
You can use the environment variable `DEBUG=true` to get better error messages (with stack trace). For example:
```
$ export DEBUG=true
$ gitbook build ./
```
+13 -3
View File
@@ -10,9 +10,10 @@ var generators = require("../lib/generate").generators;
var buildCommand = function(command) {
return command
.option('-v, --verbose', 'Activate verbose mode, useful for debugging errors')
.option('-o, --output <directory>', 'Path to output directory, defaults to ./_book')
.option('-f, --format <name>', 'Change generation format, defaults to site, availables are: '+_.keys(generators).join(", "))
.option('--config <config file>', 'Configuration file to use, defaults to book.js or book.json')
.option('--config <config file>', 'Configuration file to use, defaults to book.js or book.json');
};
@@ -26,6 +27,11 @@ var makeBuildFunc = function(converter) {
dir = dir || process.cwd();
outputDir = options.output;
// Set debugging
if(options.verbose) {
process.env.DEBUG = "true";
}
console.log('Starting build ...');
return converter(
_.extend({}, options || {}, {
@@ -38,8 +44,12 @@ var makeBuildFunc = function(converter) {
.then(function(output) {
console.log("Successfully built!");
return output;
}, utils.logError)
.fail(function() {
})
.fail(function(err) {
// Log error
utils.logError(err);
// Exit process with failure code
process.exit(-1);
});
};
+12 -1
View File
@@ -16,9 +16,20 @@ var getFiles = function(path) {
// Add extra rules to ignore common folders
ig.addIgnoreRules([
// Skip Git stuff
'.git/',
'.gitignore',
'.DS_Store'
// Skip OS X meta data
'.DS_Store',
// Skip stuff installed by plugins
'node_modules',
// Skip book outputs
'*.pdf',
'*.epub',
'*.mobi',
], '__custom_stuff');
// Push each file to our list
-1
View File
@@ -62,7 +62,6 @@ BaseGenerator.prototype.copyCover = function() {
]);
})
.fail(function(err) {
console.log(err);
return Q();
});
};
+9 -2
View File
@@ -224,8 +224,15 @@ var generateBook = function(options) {
options.github = null;
return null;
} else if(options.github) {
// Git already specified in options
return options.github;
// Git already specified in options
options.github = (
// Support URLs in "github" entry of book.json
parse.git.githubID(options.github) ||
// Fallback
options.github
);
return;
}
// Try auto detecting
+6 -1
View File
@@ -2,6 +2,7 @@ var path = require("path");
var swig = require('swig');
var hljs = require('highlight.js');
var links = require('../utils/').links;
var pkg = require('../../package.json');
swig.setDefaults({
@@ -24,7 +25,6 @@ swig.setFilter('mdLink', function(link) {
return link;
});
// Swig filter: highlight coloration
swig.setFilter('code', function(code, lang) {
try {
@@ -44,4 +44,9 @@ swig.setFilter('pathJoin', function(base, _path) {
return path.join(base, _path);
});
// Is a link an absolute link
swig.setFilter('isExternalLink', function(link) {
return links.isExternal(link);
});
module.exports = swig;
+1 -1
View File
@@ -28,7 +28,7 @@ function gitURL(path) {
// Parse a git URL to a github ID : username/reponame
function githubID(_url) {
// Remove .git if it's in _url
var sliceEnd = _url.slice(-4) === '.git' ? -4 : _url.length;
var sliceEnd = _url.slice(-4) === '.git' ? -4 : undefined;
// Detect HTTPS repos
var parsed = url.parse(_url);
+8 -6
View File
@@ -1,13 +1,14 @@
var _ = require("lodash")
var parseSummary = require("./summary");
var _ = require("lodash");
var parseEntries = require("./summary").entries;
var parseLangs = function(content) {
var summary = parseSummary(content);
var entries = parseEntries(content);
return {
list: _.chain(summary.chapters)
list: _.chain(entries)
.filter(function(entry) {
return entry.path != null;
return Boolean(entry.path);
})
.map(function(entry) {
return {
@@ -17,7 +18,8 @@ var parseLangs = function(content) {
};
})
.value()
}
};
};
module.exports = parseLangs;
+2 -8
View File
@@ -26,15 +26,9 @@ function navigation(summary, files) {
// Support single files as well as list
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
// Special README nav
var README_NAV = {
path: 'README.md',
title: 'Introduction',
level: '0',
};
// List of all navNodes
var navNodes = [README_NAV].concat(flattenChapters(summary.chapters));
// Flatten chapters, then add in default README node if ndeeded etc ...
var navNodes = flattenChapters(summary.chapters);
var prevNodes = [null].concat(navNodes.slice(0, -1));
var nextNodes = navNodes.slice(1).concat([null]);
+33 -8
View File
@@ -87,6 +87,9 @@ function parseTitle(src, nums) {
}
function parseChapter(nodes, nums) {
// Convert single number to an array
nums = _.isArray(nums) ? nums : [nums];
return _.extend(parseTitle(_.first(nodes).text, nums), {
articles: _.map(listSplit(filterList(nodes), 'list_item_start', 'list_item_end'), function(nodes, i) {
return parseChapter(nodes, nums.concat(i + 1));
@@ -94,24 +97,46 @@ function parseChapter(nodes, nums) {
});
}
function parseSummary(src) {
function defaultChapterList(chapterList) {
var first = _.first(chapterList);
var chapter = parseChapter(first, [0]);
// Already have README node, we're good to go
if(chapter.path === 'README.md') {
return chapterList;
}
return [
[ { type: 'text', text: '[Introduction](README.md)' } ]
].concat(chapterList);
}
function listGroups(src) {
var nodes = marked.lexer(src);
// Get out list of chapters
var chapterList = filterList(nodes);
// Get out groups of lists
return listSplit(
filterList(nodes),
'list_item_start', 'list_item_end'
);
}
function parseSummary(src) {
// Split out chapter sections
var chapters = _.chain(listSplit(chapterList, 'list_item_start', 'list_item_end'))
.map(function(nodes, i) {
return parseChapter(nodes, [i + 1]);
})
.value();
var chapters = defaultChapterList(listGroups(src))
.map(parseChapter);
return {
chapters: chapters
};
}
function parseEntries (src) {
return listGroups(src).map(parseChapter);
}
// Exports
module.exports = parseSummary;
module.exports.entries = parseEntries;
+17 -3
View File
@@ -1,11 +1,24 @@
var url = require('url');
var path = require('path');
// Is the link an external link
var isExternal = function(href) {
try {
return Boolean(url.parse(href).protocol);
} catch(err) { }
return false;
};
// Return true if the link is relative
var isRelative = function(href) {
var parsed = url.parse(href);
try {
var parsed = url.parse(href);
return !parsed.protocol && parsed.path && parsed.path[0] != '/';
return !parsed.protocol && parsed.path && parsed.path[0] != '/';
} catch(err) {}
return true;
};
// Relative to absolute path
@@ -41,6 +54,7 @@ var join = function() {
module.exports = {
isRelative: isRelative,
isExternal: isExternal,
toAbsolute: toAbsolute,
join: join
};
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "gitbook",
"version": "0.5.2",
"version": "0.5.5",
"homepage": "http://www.gitbook.io/",
"description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js",
+13
View File
@@ -0,0 +1,13 @@
# Summary
* [Custom name for Introduction](README.md)
* [Chapter 1](chapter-1/README.md)
* [Article 1](chapter-1/ARTICLE1.md)
* [Article 2](chapter-1/ARTICLE2.md)
* [article 1.2.1](chapter-1/ARTICLE-1-2-1.md)
* [article 1.2.2](chapter-1/ARTICLE-1-2-2.md)
* [Chapter 2](chapter-2/README.md)
* [Chapter 3](chapter-3/README.md)
* [Chapter 4](chapter-4/README.md)
* Unfinished article
* Unfinished Chapter
+17
View File
@@ -7,7 +7,9 @@ var navigation = require('../').parse.navigation;
var CONTENT = fs.readFileSync(path.join(__dirname, './fixtures/SUMMARY.md'), 'utf8');
var ALT_CONTENT = fs.readFileSync(path.join(__dirname, './fixtures/ALTERNATIVE_SUMMARY.md'), 'utf8');
var LEXED = summary(CONTENT);
var ALT_LEXED = summary(ALT_CONTENT);
describe('Summary navigation', function() {
@@ -76,6 +78,21 @@ describe('Summary navigation', function() {
assert.equal(nav['chapter-3/README.md'].level, '3');
});
it('should have a default README node', function() {
var nav = navigation(LEXED);
assert.equal(nav['README.md'].level, '0');
assert.equal(nav['README.md'].title, 'Introduction');
});
it('Should allow README node to be customized', function() {
var nav = navigation(ALT_LEXED);
assert(nav['README.md']);
assert.equal(nav['README.md'].level, '0');
assert.notEqual(nav['README.md'].title, 'Introduction');
});
it('should not accept null paths', function() {
var nav = navigation(LEXED);
+17 -13
View File
@@ -12,13 +12,13 @@ var LEXED = summary(CONTENT);
describe('Summary parsing', function () {
it('should detect chapters', function() {
assert.equal(LEXED.chapters.length, 5);
assert.equal(LEXED.chapters.length, 6);
});
it('should support articles', function() {
assert.equal(LEXED.chapters[0].articles.length, 2);
assert.equal(LEXED.chapters[1].articles.length, 0);
assert.equal(LEXED.chapters[1].articles.length, 2);
assert.equal(LEXED.chapters[2].articles.length, 0);
assert.equal(LEXED.chapters[3].articles.length, 0);
});
it('should detect paths and titles', function() {
@@ -26,30 +26,34 @@ describe('Summary parsing', function () {
assert(LEXED.chapters[1].path);
assert(LEXED.chapters[2].path);
assert(LEXED.chapters[3].path);
assert.equal(LEXED.chapters[4].path, null);
assert(LEXED.chapters[4].path);
assert.equal(LEXED.chapters[5].path, null);
assert(LEXED.chapters[0].title);
assert(LEXED.chapters[1].title);
assert(LEXED.chapters[2].title);
assert(LEXED.chapters[3].title);
assert(LEXED.chapters[4].title);
assert(LEXED.chapters[5].title);
});
it('should normalize paths from .md to .html', function() {
assert.equal(LEXED.chapters[0].path,'chapter-1/README.md');
assert.equal(LEXED.chapters[1].path,'chapter-2/README.md');
assert.equal(LEXED.chapters[2].path,'chapter-3/README.md');
assert.equal(LEXED.chapters[0].path,'README.md');
assert.equal(LEXED.chapters[1].path,'chapter-1/README.md');
assert.equal(LEXED.chapters[2].path,'chapter-2/README.md');
assert.equal(LEXED.chapters[3].path,'chapter-3/README.md');
});
it('should detect levels correctly', function() {
var c = LEXED.chapters;
assert.equal(c[0].level, '1');
assert.equal(c[1].level, '2');
assert.equal(c[2].level, '3');
assert.equal(c[0].level, '0');
assert.equal(c[1].level, '1');
assert.equal(c[2].level, '2');
assert.equal(c[3].level, '3');
assert.equal(c[0].articles[0].level, '1.1');
assert.equal(c[0].articles[1].level, '1.2');
assert.equal(c[0].articles[1].articles[0].level, '1.2.1');
assert.equal(c[1].articles[0].level, '1.1');
assert.equal(c[1].articles[1].level, '1.2');
assert.equal(c[1].articles[1].articles[0].level, '1.2.1');
});
});
+1 -1
View File
@@ -1 +1 @@
.link-inherit{color:inherit}.link-inherit:hover,.link-inherit:focus{color:inherit}.hljs-comment,.hljs-title{color:#8e908c}.hljs-variable,.hljs-attribute,.hljs-tag,.hljs-regexp,.ruby .hljs-constant,.xml .hljs-tag .hljs-title,.xml .hljs-pi,.xml .hljs-doctype,.html .hljs-doctype,.css .hljs-id,.css .hljs-class,.css .hljs-pseudo{color:#c82829}.hljs-number,.hljs-preprocessor,.hljs-pragma,.hljs-built_in,.hljs-literal,.hljs-params,.hljs-constant{color:#f5871f}.ruby .hljs-class .hljs-title,.css .hljs-rules .hljs-attribute{color:#eab700}.hljs-string,.hljs-value,.hljs-inheritance,.hljs-header,.ruby .hljs-symbol,.xml .hljs-cdata{color:#718c00}.css .hljs-hexcolor{color:#3e999f}.hljs-function,.python .hljs-decorator,.python .hljs-title,.ruby .hljs-function .hljs-title,.ruby .hljs-title .hljs-keyword,.perl .hljs-sub,.javascript .hljs-title,.coffeescript .hljs-title{color:#4271ae}.hljs-keyword,.javascript .hljs-function{color:#8959a8}.hljs{display:block;background:white;color:#4d4d4c;padding:.5em}.coffeescript .javascript,.javascript .xml,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:.5}.book-chapter{display:none}article{page-break-after:always}pre,blockquote{border:1px solid #999;page-break-inside:avoid;background:#f1f1f1;padding:8px}img{max-width:100%!important;page-break-inside:avoid;margin:0 auto}.exercise,.quiz{margin:1cm 0;padding:.4cm;page-break-inside:avoid;border:3px solid #ddd}.exercise .exercise-header,.quiz .exercise-header{margin-bottom:.4cm;padding-bottom:.2cm;border-bottom:1px solid #ddd}.exercise .question,.quiz .question{margin-top:.4cm}
.link-inherit{color:inherit}.link-inherit:hover,.link-inherit:focus{color:inherit}.hljs-comment,.hljs-title{color:#8e908c}.hljs-variable,.hljs-attribute,.hljs-tag,.hljs-regexp,.ruby .hljs-constant,.xml .hljs-tag .hljs-title,.xml .hljs-pi,.xml .hljs-doctype,.html .hljs-doctype,.css .hljs-id,.css .hljs-class,.css .hljs-pseudo{color:#c82829}.hljs-number,.hljs-preprocessor,.hljs-pragma,.hljs-built_in,.hljs-literal,.hljs-params,.hljs-constant{color:#f5871f}.ruby .hljs-class .hljs-title,.css .hljs-rules .hljs-attribute{color:#eab700}.hljs-string,.hljs-value,.hljs-inheritance,.hljs-header,.ruby .hljs-symbol,.xml .hljs-cdata{color:#718c00}.css .hljs-hexcolor{color:#3e999f}.hljs-function,.python .hljs-decorator,.python .hljs-title,.ruby .hljs-function .hljs-title,.ruby .hljs-title .hljs-keyword,.perl .hljs-sub,.javascript .hljs-title,.coffeescript .hljs-title{color:#4271ae}.hljs-keyword,.javascript .hljs-function{color:#8959a8}.hljs{display:block;background:white;color:#4d4d4c;padding:.5em}.coffeescript .javascript,.javascript .xml,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:.5}.book-chapter{display:none}pre,blockquote{border:1px solid #999;page-break-inside:avoid;background:#f1f1f1;padding:8px}img{max-width:100%!important;page-break-inside:avoid;margin:0 auto}.exercise,.quiz{margin:1cm 0;padding:.4cm;page-break-inside:avoid;border:3px solid #ddd}.exercise .exercise-header,.quiz .exercise-header{margin-bottom:.4cm;padding-bottom:.2cm;border-bottom:1px solid #ddd}.exercise .question,.quiz .question{margin-top:.4cm}
File diff suppressed because one or more lines are too long
-3
View File
@@ -7,9 +7,6 @@
display: none;
}
article {
page-break-after: always;
}
pre, blockquote {
border: 1px solid #999;
+5 -1
View File
@@ -44,12 +44,16 @@
padding-right: 200px;
.transition(margin-left 0.5s ease);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
a, a:hover {
color: inherit;
text-decoration: none;
}
@media (max-width: 800px) {
@media (max-width: 1000px) {
display: none;
}
+19 -7
View File
@@ -1,10 +1,25 @@
{% macro articles(_articles) %}
{% for item in _articles %}
<li class="chapter {% if item._path == _input %}active{% endif %}" data-level="{{ item.level }}" {% if item.path %}data-path="{{ item.path|mdLink }}"{% endif %}>
{% set externalLink = item.path|isExternalLink %}
<li class="chapter {% if item._path == _input %}active{% endif %}" data-level="{{ item.level }}" {% if item.path && !externalLink %}data-path="{{ item.path|mdLink }}"{% endif %}>
{% if item.path %}
<a href="{{ basePath }}/{{ item.path|mdLink }}">
<i class="fa fa-check"></i> <b>{{ item.level }}.</b> {{ item.title }}
</a>
{% if !externalLink %}
<a href="{{ basePath }}/{{ item.path|mdLink }}">
<i class="fa fa-check"></i>
{% if item.level !== "0" %}
<b>{{ item.level }}.</b>
{% endif %}
{{ item.title }}
</a>
{% else %}
<a target="_blank" href="{{ item.path }}">
<i class="fa fa-check"></i>
{% if item.level !== "0" %}
<b>{{ item.level }}.</b>
{% endif %}
{{ item.title }}
</a>
{% endif %}
{% else %}
<span><b>{{ item.level }}.</b> {{ item.title }}</span>
{% endif %}
@@ -48,9 +63,6 @@
<li class="divider"></li>
{% endif %}
<li data-level="0" data-path="index.html">
<a href="{{ basePath }}/"><i class="fa fa-check"></i> Introduction</a>
</li>
{{ articles(summary.chapters) }}
{% if options.links.gitbook !== false %}
+1
View File
@@ -52,6 +52,7 @@
{% set exercise = 1 %}
<article>
<h1 class="book-chapter book-chapter-1">Exercise Solutions</h1>
<h1>Exercise Solutions</h1>
{% for item in progress.chapters %}
{% if pages[item.path] %}