summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/utils/command.js19
-rw-r--r--lib/utils/git.js119
-rw-r--r--test.html16
-rw-r--r--test/all.js1
-rw-r--r--test/git.js48
5 files changed, 203 insertions, 0 deletions
diff --git a/lib/utils/command.js b/lib/utils/command.js
new file mode 100644
index 0000000..f395fa1
--- /dev/null
+++ b/lib/utils/command.js
@@ -0,0 +1,19 @@
+var childProcess = require('child_process');
+var Promise = require('./promise');
+
+// On borwser, command execution is not possible
+var isAvailable = childProcess && childProcess.exec;
+
+// Execute a command
+function exec(command, options) {
+ if (!isAvailable) {
+ return Promise.reject(new Error('Command execution is not possible on this platform'));
+ }
+
+ return Promise.nfcall(childProcess.exec, command, options);
+}
+
+module.exports = {
+ isAvailable: isAvailable,
+ exec: exec
+};
diff --git a/lib/utils/git.js b/lib/utils/git.js
new file mode 100644
index 0000000..6e37b14
--- /dev/null
+++ b/lib/utils/git.js
@@ -0,0 +1,119 @@
+var _ = require('lodash');
+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+';
+var GIT_TMP = null;
+
+
+// Check if an url is a git dependency url
+function isGitUrl(giturl) {
+ return (giturl.indexOf(GIT_PREFIX) === 0);
+}
+
+// Parse and extract infos
+function parseGitUrl(giturl) {
+ var ref, uri, fileParts, filepath;
+
+ if (!isGitUrl(giturl)) return null;
+ giturl = giturl.slice(GIT_PREFIX.length);
+
+ uri = new URI(giturl);
+ ref = uri.fragment() || 'master';
+ 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(_.first(fileParts)+'.git');
+
+ return {
+ host: uri.toString(),
+ ref: ref || 'master',
+ filepath: filepath
+ };
+}
+
+// Clone a git repo from a specific ref
+function cloneGitRepo(host, ref) {
+ ref = ref || 'master';
+
+ return Promise()
+
+ // Create temporary folder to store git repos
+ .then(function() {
+ if (GIT_TMP) return;
+ return fs.tmp.dir()
+ .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 command.exec('git clone '+host+' '+repoPath)
+ .then(function() {
+ return command.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 Promise(null);
+
+ // Clone or get from cache
+ return cloneGitRepo(giturl.host, giturl.ref)
+ .then(function(repo) {
+
+ // Resolve relative path
+ return path.resolve(repo, giturl.filepath);
+ });
+}
+
+// Return root of git repo from a filepath
+function resolveGitRoot(filepath) {
+ var relativeToGit, repoId;
+
+ // No git repo cloned, or file is not in a git repository
+ if (!GIT_TMP || !pathUtil.isInRoot(GIT_TMP, filepath)) return null;
+
+ // Extract first directory (is the repo id)
+ relativeToGit = path.relative(GIT_TMP, filepath);
+ repoId = _.first(relativeToGit.split(path.sep));
+ if (!repoId) return;
+
+ // Return an absolute file
+ return path.resolve(GIT_TMP, repoId);
+}
+
+
+module.exports = {
+ isUrl: isGitUrl,
+ parseUrl: parseGitUrl,
+ resolveFile: resolveFileFromGit,
+ resolveRoot: resolveGitRoot
+};
diff --git a/test.html b/test.html
new file mode 100644
index 0000000..3dee7ef
--- /dev/null
+++ b/test.html
@@ -0,0 +1,16 @@
+<!doctype html>
+
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+
+ <title>The HTML5 Herald</title>
+ <meta name="description" content="The HTML5 Herald">
+ <meta name="author" content="SitePoint">
+
+</head>
+
+<body>
+ <script src="test-browser.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/test/all.js b/test/all.js
index 4a5ba83..0f55603 100644
--- a/test/all.js
+++ b/test/all.js
@@ -8,6 +8,7 @@ require('./langs');
require('./parse');
require('./template');
+require('./git');
// Output
require('./output-json');
diff --git a/test/git.js b/test/git.js
new file mode 100644
index 0000000..bad031b
--- /dev/null
+++ b/test/git.js
@@ -0,0 +1,48 @@
+var should = require('should');
+var git = require('../lib/utils/git');
+
+describe('Git', function() {
+
+ describe('URL parsing', function() {
+
+ it('should correctly validate git urls', function() {
+ // HTTPS
+ git.isUrl('git+https://github.com/Hello/world.git').should.be.ok;
+
+ // SSH
+ git.isUrl('git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1').should.be.ok;
+
+ // Non valid
+ git.isUrl('https://github.com/Hello/world.git').should.not.be.ok;
+ git.isUrl('README.md').should.not.be.ok;
+ });
+
+ it('should parse HTTPS urls', function() {
+ var parts = git.parseUrl('git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md');
+
+ should.exist(parts);
+ parts.host.should.be.equal('https://gist.github.com/69ea4542e4c8967d2fa7.git');
+ parts.ref.should.be.equal('master');
+ parts.filepath.should.be.equal('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');
+
+ should.exist(parts);
+ parts.host.should.be.equal('https://gist.github.com/69ea4542e4c8967d2fa7.git');
+ parts.ref.should.be.equal('1.0.0');
+ parts.filepath.should.be.equal('test.md');
+ });
+
+ it('should parse SSH urls', function() {
+ var parts = git.parseUrl('git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1');
+
+ should.exist(parts);
+ parts.host.should.be.equal('git@github.com:GitbookIO/gitbook.git');
+ parts.ref.should.be.equal('e1594cde2c32e4ff48f6c4eff3d3d461743d74e1');
+ parts.filepath.should.be.equal('directory/README.md');
+ });
+ });
+
+});