diff options
author | Samy Pessé <samypesse@gmail.com> | 2015-09-15 11:09:08 +0200 |
---|---|---|
committer | Samy Pessé <samypesse@gmail.com> | 2015-09-15 11:09:08 +0200 |
commit | baf10e9b159b64c30ce650c83eb437675434e3e2 (patch) | |
tree | 583fc00922e6ca52fea87947af0b4d5703aabf7d | |
parent | 463a947df1e5c8c862c555a5b0ae675e356a0d5c (diff) | |
download | gitbook-baf10e9b159b64c30ce650c83eb437675434e3e2.zip gitbook-baf10e9b159b64c30ce650c83eb437675434e3e2.tar.gz gitbook-baf10e9b159b64c30ce650c83eb437675434e3e2.tar.bz2 |
Improve book.resolve to ensure file is in the book
Adapt tests to plugin-highlight
-rw-r--r-- | lib/book.js | 43 | ||||
-rw-r--r-- | lib/template.js | 8 | ||||
-rw-r--r-- | lib/utils/navigation.js | 9 | ||||
-rw-r--r-- | package.json | 4 | ||||
-rw-r--r-- | test/configuration.js | 8 | ||||
-rw-r--r-- | test/ebook.js | 19 | ||||
-rw-r--r-- | test/plugins.js | 7 | ||||
-rw-r--r-- | test/resolve.js | 61 |
8 files changed, 139 insertions, 20 deletions
diff --git a/lib/book.js b/lib/book.js index b306c51..980e505 100644 --- a/lib/book.js +++ b/lib/book.js @@ -630,21 +630,27 @@ Book.prototype.findFile = function(filename) { // Check if a file exists in the book Book.prototype.fileExists = function(filename) { return fs.exists( - path.join(this.root, filename) + this.resolve(filename) ); }; +// Check if a file path is inside the book +Book.prototype.fileIsInBook = function(filename) { + filename = path.normalize(filename); + return (filename.substr(0, this.root.length) === this.root); +}; + // Read a file Book.prototype.readFile = function(filename) { return fs.readFile( - path.join(this.root, filename), + this.resolve(filename), { encoding: "utf8" } ); }; // Return stat for a file Book.prototype.statFile = function(filename) { - return fs.stat(path.join(this.root, filename)); + return fs.stat(this.resolve(filename)); }; // List all files in the book @@ -702,9 +708,34 @@ Book.prototype.isEntryPoint = function(fp) { return fp == this.readmeFile; }; -// Resolve a path in book -Book.prototype.resolve = function(p) { - return path.resolve(this.root, p); +// Alias to book.config.get +Book.prototype.getConfig = function(key, def) { + return this.config.get(key, def); +}; + +// Resolve a path in the book source +// Enforce that the output path in the root folder +Book.prototype.resolve = function() { + var input = _.chain(arguments) + .toArray() + .reduce(function(current, p) { + // Handle path relative to book root ('/README.md') + if (p[0] == '/' || p[0] == '\\') return p.slice(1); + + return path.join(current, p); + }) + .value(); + + + var result = path.resolve(this.root, input); + + if (!this.fileIsInBook(result)) { + err = new Error("EACCESS: '" + result + "' not in '" + this.root + "'"); + err.code = "EACCESS"; + throw err; + } + + return result }; // Normalize a path to .html and convert README -> index diff --git a/lib/template.js b/lib/template.js index 4b2035f..b1bc632 100644 --- a/lib/template.js +++ b/lib/template.js @@ -30,7 +30,7 @@ var BookLoader = nunjucks.Loader.extend({ git.resolveFile(fileurl) .then(function(filepath) { // Is local file - if (!filepath) filepath = that.book.resolve(fileurl); + if (!filepath) filepath = path.resolve(fileurl); else that.book.log.debug.ln("resolve from git", fileurl, "to", filepath) // Read file from absolute path @@ -46,6 +46,12 @@ var BookLoader = nunjucks.Loader.extend({ }, resolve: function(from, to) { + // If origin is in the book, we enforce result file to be in the book + if (this.book.fileIsInBook(from)) { + return this.book.resolve(path.dirname(from), to); + } + + // If origin is not in the book (include from a git content ref) return path.resolve(path.dirname(from), to); } }); diff --git a/lib/utils/navigation.js b/lib/utils/navigation.js index af9330d..d825c2c 100644 --- a/lib/utils/navigation.js +++ b/lib/utils/navigation.js @@ -27,7 +27,7 @@ function navigation(summary, files) { files = _.isArray(files) ? files : (_.isString(files) ? [files] : null); // List of all navNodes - // Flatten chapters, then add in default README node if needed etc ... + // Flatten chapters var navNodes = flattenChapters(summary.chapters); // Mapping of prev/next for a give path @@ -39,8 +39,7 @@ function navigation(summary, files) { if(!current.exists) return null; // Find prev - prev = _.chain(navNodes) - .slice(0, i) + prev = _.chain(navNodes.slice(0, i)) .reverse() .find(function(node) { return node.exists && !node.external; @@ -48,14 +47,12 @@ function navigation(summary, files) { .value(); // Find next - next = _.chain(navNodes) - .slice(i+1) + next = _.chain(navNodes.slice(i+1)) .find(function(node) { return node.exists && !node.external; }) .value(); - return [current.path, { index: i, title: current.title, diff --git a/package.json b/package.json index 4baeec2..fec67d9 100644 --- a/package.json +++ b/package.json @@ -7,13 +7,13 @@ "dependencies": { "q": "1.0.1", "lunr": "0.5.7", - "lodash": "3.5.0", + "lodash": "3.10.1", "graceful-fs": "3.0.5", "resolve": "0.6.3", "fs-extra": "0.16.5", "fstream-ignore": "1.0.2", "gitbook-parsers": "0.8.2", - "gitbook-plugin-highlight": "1.0.0", + "gitbook-plugin-highlight": "1.0.1", "nunjucks": "mozilla/nunjucks#dc89bf91611a2101731c2c06afcf5c32160b4dc9", "nunjucks-autoescape": "1.0.0", "nunjucks-filter": "1.0.0", diff --git a/test/configuration.js b/test/configuration.js index eedec49..2cff26e 100644 --- a/test/configuration.js +++ b/test/configuration.js @@ -29,4 +29,12 @@ describe('Configuration', function () { book.options.title.should.be.equal("js-config"); }); }); + + it('should provide configuration on book.config.get', function() { + return books.parse("basic") + .then(function(book) { + book.config.get('description').should.be.equal("Default description for the book."); + book.getConfig('description').should.be.equal("Default description for the book."); + }); + }); }); diff --git a/test/ebook.js b/test/ebook.js index 9b353d2..033fd04 100644 --- a/test/ebook.js +++ b/test/ebook.js @@ -37,12 +37,23 @@ describe('eBook generator', function () { path.join(book.options.output, "index.html"), { encoding: "utf-8" } ); + + // There are 2 styles (one from plugin-highlight and the new style) PAGE.should.be.html({ "link": { - count: 1, - attributes: { - href: "./styles/print.css" - } + count: 2 + } + }); + + PAGE.should.be.html({ + "link[href='./styles/print.css']": { + count: 1 + } + }); + + PAGE.should.be.html({ + "link[href='gitbook/plugins/gitbook-plugin-highlight/ebook.css']": { + count: 1 } }); }); diff --git a/test/plugins.js b/test/plugins.js index cc9c8e6..6d5b9de 100644 --- a/test/plugins.js +++ b/test/plugins.js @@ -90,7 +90,12 @@ describe('Plugins', function () { it('should extend books plugins', function() { var resources = book.plugins.resources("ebook"); - resources["css"].should.have.lengthOf(1); + + // There is resources from highlight plugin and this plugin + resources["css"].should.have.lengthOf(2); + should.exist(_.find(resources["css"], { + path: './resources/test' + })); }); }); }); diff --git a/test/resolve.js b/test/resolve.js new file mode 100644 index 0000000..b31a10d --- /dev/null +++ b/test/resolve.js @@ -0,0 +1,61 @@ +var fs = require('fs'); +var path = require('path'); + +describe('Resolve Files', function () { + var book; + + before(function() { + return books.parse("basic") + .then(function(_book) { + book = _book; + }); + }); + + describe('book.fileIsInBook', function() { + it('should return true for correct paths', function() { + book.fileIsInBook(path.join(book.root, 'README.md')).should.equal(true); + book.fileIsInBook(path.join(book.root, 'styles/website.css')).should.equal(true); + }); + + it('should return true for root folder', function() { + book.fileIsInBook(path.join(book.root, './')).should.equal(true); + book.fileIsInBook(book.root).should.equal(true); + }); + + it('should return false for files out of scope', function() { + book.fileIsInBook(path.join(book.root, '../')).should.equal(false); + book.fileIsInBook('README.md').should.equal(false); + book.fileIsInBook(path.resolve(book.root, '../README.md')).should.equal(false); + }); + + it('should correctly handle windows paths', function() { + book.fileIsInBook(path.join(book.root, '\\styles\\website.css')).should.equal(true); + }); + }); + + describe('book.resolve', function() { + it('should resolve a file to its absolute path', function() { + book.resolve('README.md').should.equal(path.resolve(book.root, 'README.md')); + book.resolve('website/README.md').should.equal(path.resolve(book.root, 'website/README.md')); + }); + + it('should correctly handle windows paths', function() { + book.resolve('styles\\website.css').should.equal(path.resolve(book.root, 'styles\\website.css')); + }); + + it('should correctly resolve all arguments', function() { + book.resolve('test', 'hello', '..', 'README.md').should.equal(path.resolve(book.root, 'test/README.md')); + }); + + it('should correctly resolve to root folder', function() { + book.resolve('test', '/README.md').should.equal(path.resolve(book.root, 'README.md')); + book.resolve('test', '\\README.md').should.equal(path.resolve(book.root, 'README.md')); + }); + + it('should throw an error for file out of book', function() { + (function() { + return book.resolve('../README.md'); + }).should.throw(); + }); + }); +}); |