diff options
author | Samy Pessé <samypesse@gmail.com> | 2015-01-24 18:04:03 +0100 |
---|---|---|
committer | Samy Pessé <samypesse@gmail.com> | 2015-01-24 18:04:03 +0100 |
commit | 796dd4c13ac55e8b6232cbf38d49d9cf8c6676d2 (patch) | |
tree | ebbb0853571aec61384188402f673b9834f32a88 /lib | |
parent | e02f29e76392660b31be87063649abd4adb22826 (diff) | |
download | gitbook-796dd4c13ac55e8b6232cbf38d49d9cf8c6676d2.zip gitbook-796dd4c13ac55e8b6232cbf38d49d9cf8c6676d2.tar.gz gitbook-796dd4c13ac55e8b6232cbf38d49d9cf8c6676d2.tar.bz2 |
Add base loader to resolve git dependencies
Diffstat (limited to 'lib')
-rw-r--r-- | lib/template.js | 37 | ||||
-rw-r--r-- | lib/utils/fs.js | 7 | ||||
-rw-r--r-- | lib/utils/git.js | 110 |
3 files changed, 152 insertions, 2 deletions
diff --git a/lib/template.js b/lib/template.js index 315a373..76bb9ab 100644 --- a/lib/template.js +++ b/lib/template.js @@ -1,15 +1,50 @@ var _ = require("lodash"); var Q = require("q"); +var path = require("path"); var nunjucks = require("nunjucks"); +var git = require("./utils/git"); +var fs = require("./utils/fs"); var pkg = require("../package.json"); + +// The loader should handle relative and git url +var BookLoader = nunjucks.Loader.extend({ + async: true, + + init: function(book) { + this.book = book; + }, + + getSource: function(fileurl, callback) { + var that = this; + + git.resolveFile(fileurl) + .then(function(filepath) { + // Is local file + if (!filepath) filepath = path.resolve(that.book.root, fileurl); + else that.book.logDebug("resolve from git", fileurl, "to", filepath) + + // Read file from absolute path + return fs.readFile(filepath) + .then(function(source) { + return { + src: source.toString(), + path: filepath + } + }); + }) + .nodeify(callback); + } +}); + + var TemplateEngine = function(book) { this.book = book; // Nunjucks env this.env = new nunjucks.Environment( - new nunjucks.FileSystemLoader(book.root), + new BookLoader(book), { // Escaping is done after by the markdown parser autoescape: false diff --git a/lib/utils/fs.js b/lib/utils/fs.js index 95fa73d..51577c6 100644 --- a/lib/utils/fs.js +++ b/lib/utils/fs.js @@ -1,5 +1,6 @@ var Q = require("q"); -var fs = require('graceful-fs'); +var tmp = require("tmp"); +var fs = require("graceful-fs"); var fsExtra = require("fs-extra"); var Ignore = require("fstream-ignore"); @@ -62,6 +63,10 @@ var getFiles = function(path) { }; module.exports = { + tmp: { + file: Q.denodeify(tmp.file.bind(tmp)), + dir: Q.denodeify(tmp.dir.bind(tmp)) + }, list: getFiles, stat: Q.denodeify(fs.stat), readdir: Q.denodeify(fs.readdir), diff --git a/lib/utils/git.js b/lib/utils/git.js new file mode 100644 index 0000000..8b515ec --- /dev/null +++ b/lib/utils/git.js @@ -0,0 +1,110 @@ +var Q = require("q"); +var _ = require("lodash"); +var url = require("url"); +var tmp = require("tmp"); +var path = require("path"); +var crc = require("crc"); +var exec = Q.denodeify(require("child_process").exec); + +var fs = require("./fs"); + +var GIT_PREFIX = "git+"; +var GIT_TMP = null; + + +// Check if an url is a git dependency url +function checkGitUrl(giturl) { + return (giturl.indexOf(GIT_PREFIX) === 0); +} + +// Validates a SHA in hexadecimal +function validateSha(str) { + return (/[0-9a-f]{40}/).test(str); +} + +// Parse and extract infos +function parseGitUrl(giturl) { + if (!checkGitUrl(giturl)) return null; + giturl = giturl.slice(GIT_PREFIX.length); + + var parts = url.parse(giturl); + + var ref = parts.hash; + + // Extract file inside the repo (after the .git) + var fileParts = parts.pathname.split(".git"); + var filepath = fileParts.length > 1? fileParts.slice(1).join(".git") : ""; + if (filepath[0] == "/") filepath = filepath.slice(1); + + // Rebuild + parts.pathname = _.first(fileParts)+".git"; + var githost = url.format(parts); + + return { + host: githost, + ref: ref, + filepath: filepath + }; +} + +// Clone a git repo from a specific ref +function cloneGitRepo(host, ref) { + var isBranch = false; + + ref = ref || "master"; + if (!validateSha(ref)) isBranch = true; + + return Q() + + // Create temporary folder to store git repos + .then(function() { + if (GIT_TMP) return; + return fs.tmp.dir().get(0) + .then(function(_tmp) { + GIT_TMP = _tmp; + }); + }) + + // Return or clone the git repo + .then(function() { + // Unique ID for repo/ref combinaison + var repoId = crc.crc32(host+"#"+ref).toString(16); + + // Absolute path to the folder + var repoPath = path.resolve(GIT_TMP, repoId); + + return fs.exists(repoPath) + .then(function(doExists) { + if (doExists) return; + + // Clone repo + return exec("git clone "+host+" "+repoPath) + .then(function() { + return exec("git checkout "+ref, { cwd: repoPath }); + }) + }) + .thenResolve(repoPath); + }); +} + + +// Get file from a git repo +function resolveFileFromGit(giturl) { + if (_.isString(giturl)) giturl = parseGitUrl(giturl); + if (!giturl) return Q(null); + + // Clone or get from cache + return cloneGitRepo(giturl.host, giturl.ref) + .then(function(repo) { + + // Resolve relative path + return path.resolve(repo, giturl.filepath); + }); +}; + + +module.exports = { + checkUrl: checkGitUrl, + parseUrl: parseGitUrl, + resolveFile: resolveFileFromGit +}; |