diff --git a/lib/cmd.js b/lib/cmd.js index 2f09eb5dd..6267f373c 100644 --- a/lib/cmd.js +++ b/lib/cmd.js @@ -45,6 +45,7 @@ class Cmd { .description(__('New Application')) .option('--simple', __('create a barebones project meant only for contract development')) .option('--locale [locale]', __('language to use (default: en)')) + .option('--template [url]', __('download template')) .action(function (name, options) { i18n.setOrDetectLocale(options.locale); if (name === undefined) { @@ -63,7 +64,7 @@ class Cmd { if (options.simple) { embark.generateTemplate('simple', './', inputvalue); } else { - embark.generateTemplate('boilerplate', './', inputvalue); + embark.generateTemplate('boilerplate', './', inputvalue, options.template); } } }); @@ -71,7 +72,7 @@ class Cmd { if (options.simple) { embark.generateTemplate('simple', './', name); } else { - embark.generateTemplate('boilerplate', './', name); + embark.generateTemplate('boilerplate', './', name, options.template); } } }); diff --git a/lib/cmds/template_generator.js b/lib/cmds/template_generator.js index 8597fcc02..43d6b1ec8 100644 --- a/lib/cmds/template_generator.js +++ b/lib/cmds/template_generator.js @@ -6,22 +6,37 @@ class TemplateGenerator { this.templateName = templateName; } + downloadAndGenerate(uri, destinationFolder, name) { + const self = this; + let {url, filePath} = this.getExternalProject(uri); + let tmpFilePath = fs.tmpDir(filePath); + console.log(__('Installing Template from ' + uri + '....').green); + + fs.mkdirpSync(utils.dirname(tmpFilePath)); + utils.downloadFile(url, tmpFilePath, () => { + let fspath = utils.joinPath(destinationFolder, name); + + utils.extractZip(tmpFilePath, fspath, { + map: file => { + let fixed_path = file.path.split('/'); + fixed_path.shift(); // remove first directory + file.path = utils.joinPath(...fixed_path); + return file; + } + }).then(() => { + self.installTemplate(fspath, name, true); + }); + }); + } + generate(destinationFolder, name) { - let templatePath = fs.embarkPath(utils.joinPath('templates', this.templateName)); console.log(__('Initializing Embark Template....').green); + + let templatePath = fs.embarkPath(utils.joinPath('templates', this.templateName)); let fspath = utils.joinPath(destinationFolder, name); - fs.copySync(templatePath, fspath); - utils.cd(fspath); - utils.sed('package.json', '%APP_NAME%', name); - if (name === 'embark_demo') { - console.log(__('Installing packages...').green); - utils.runCmd('npm install'); - } - - console.log(__('Init complete').green); - console.log('\n' + __('App ready at ').green + fspath); + this.installTemplate(fspath, name, (name === 'embark_demo')); if (name === 'embark_demo') { console.log('-------------------'.yellow); @@ -31,6 +46,47 @@ class TemplateGenerator { console.log(__('For more info go to http://embark.status.im').green); } } + + installTemplate(templatePath, name, installPackages) { + utils.cd(templatePath); + utils.sed('package.json', '%APP_NAME%', name); + + if (installPackages) { + console.log(__('Installing packages...').green); + utils.runCmd('npm install'); + } + + console.log(__('Init complete').green); + console.log('\n' + __('App ready at ').green + templatePath); + } + + getExternalProject(uri) { + let match = uri.match( + /\.[a-z]+\/([-a-zA-Z0-9@:%_+.~#?&\/=]+)/ + ); + + let url, folder; + + if (uri.startsWith('http')) { + url = uri + "/archive/master.zip"; + folder = match[1]; + } else if (uri.startsWith('github')) { + url = "https://" + uri + "/archive/master.zip"; + folder = match[1]; + } else if (uri.split('/').length === 2) { + url = "https://github.com/" + uri + "/archive/master.zip"; + folder = uri; + } else if (uri.indexOf('/') === -1) { + url = "https://github.com/embark-framework/embark-" + uri + "-template/archive/master.zip"; + folder = "embark-framework/embark-" + uri + "-template"; + } + + return { + url, + filePath: utils.joinPath(".embark/templates/", folder, "archive.zip") + }; + } + } module.exports = TemplateGenerator; diff --git a/lib/core/fs.js b/lib/core/fs.js index 6dbc12285..4e2e21792 100644 --- a/lib/core/fs.js +++ b/lib/core/fs.js @@ -86,6 +86,11 @@ function createWriteStream() { return fs.createWriteStream.apply(fs.createWriteStream, arguments); } +function tmpDir() { + let os = require('os'); + return utils.joinPath(os.tmpdir(), ...arguments); +} + module.exports = { mkdirpSync, mkdirp, @@ -105,5 +110,6 @@ module.exports = { removeSync, embarkPath, dappPath, - createWriteStream + createWriteStream, + tmpDir }; diff --git a/lib/index.js b/lib/index.js index f2dd51002..032287e13 100644 --- a/lib/index.js +++ b/lib/index.js @@ -53,10 +53,14 @@ class Embark { simulator.run(options); } - generateTemplate(templateName, destinationFolder, name) { + generateTemplate(templateName, destinationFolder, name, url) { this.context = [constants.contexts.templateGeneration]; let TemplateGenerator = require('./cmds/template_generator.js'); let templateGenerator = new TemplateGenerator(templateName); + + if (url) { + return templateGenerator.downloadAndGenerate(url, destinationFolder, name); + } templateGenerator.generate(destinationFolder, name); } diff --git a/lib/utils/utils.js b/lib/utils/utils.js index 172423d3f..62036d19c 100644 --- a/lib/utils/utils.js +++ b/lib/utils/utils.js @@ -6,6 +6,11 @@ function joinPath() { return path.join.apply(path.join, arguments); } +function dirname() { + const path = require('path'); + return path.dirname.apply(path.dirname, arguments); +} + function filesMatchingPattern(files) { const globule = require('globule'); return globule.find(files, {nonull: true}); @@ -168,6 +173,14 @@ function extractTar(filename, packageDirectory, cb) { ); } +function extractZip(filename, packageDirectory, cb) { + const decompress = require('decompress'); + + decompress(filename, packageDirectory).then((_files) => { + cb(); + }); +} + function proposeAlternative(word, _dictionary, _exceptions) { const propose = require('propose'); let exceptions = _exceptions || []; @@ -298,29 +311,31 @@ function buildUrlFromConfig (configObj){ } module.exports = { - joinPath: joinPath, - filesMatchingPattern: filesMatchingPattern, - fileMatchesPattern: fileMatchesPattern, - recursiveMerge: recursiveMerge, - checkIsAvailable: checkIsAvailable, - httpGet: httpGet, - httpsGet: httpsGet, - httpGetJson: httpGetJson, - httpsGetJson: httpsGetJson, - hexToNumber: hexToNumber, + joinPath, + dirname, + filesMatchingPattern, + fileMatchesPattern, + recursiveMerge, + checkIsAvailable, + httpGet, + httpsGet, + httpGetJson, + httpsGetJson, + hexToNumber, pingEndpoint, - decodeParams: decodeParams, - runCmd: runCmd, + decodeParams, + runCmd, cd: cd, sed: sed, exit: exit, - downloadFile: downloadFile, - extractTar: extractTar, - proposeAlternative: proposeAlternative, + downloadFile, + extractTar, + extractZip, + proposeAlternative, pwd: pwd, getExternalContractUrl, - toChecksumAddress: toChecksumAddress, - sha3: sha3, + toChecksumAddress, + sha3, normalizeInput, buildUrl, buildUrlFromConfig diff --git a/package.json b/package.json index ed38bbe2c..2534ce7e3 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "colors": "^1.1.2", "commander": "^2.15.1", "css-loader": "^0.28.11", + "decompress": "^4.2.0", "deep-equal": "^1.0.1", "ejs": "^2.5.8", "embark-web3-provider-engine": "14.0.7", diff --git a/test/template.js b/test/template.js new file mode 100644 index 000000000..3e5e252d1 --- /dev/null +++ b/test/template.js @@ -0,0 +1,55 @@ +/*globals describe, it*/ +const assert = require('assert'); +const TemplateGenerator = require('../lib/cmds/template_generator'); + +describe('TemplateGenerator', function () { + describe('getExternalProject', function () { + let templateGenerator; + + before(() => { + templateGenerator = new TemplateGenerator(); + }); + + describe('with github link', function () { + + it('return correct zip filename for https link', function () { + let result = templateGenerator.getExternalProject("https://github.com/embark-framework/embark"); + assert.strictEqual(result.url, "https://github.com/embark-framework/embark/archive/master.zip"); + assert.strictEqual(result.filePath, ".embark/templates/embark-framework/embark/archive.zip"); + }); + + it('return correct zip filename for http link', function () { + let result = templateGenerator.getExternalProject("http://github.com/embark-framework/embark"); + assert.strictEqual(result.url, "http://github.com/embark-framework/embark/archive/master.zip"); + assert.strictEqual(result.filePath, ".embark/templates/embark-framework/embark/archive.zip"); + }); + + it('return correct zip filename without protocol specified ', function () { + let result = templateGenerator.getExternalProject("github.com/embark-framework/embark"); + assert.strictEqual(result.url, "https://github.com/embark-framework/embark/archive/master.zip"); + assert.strictEqual(result.filePath, ".embark/templates/embark-framework/embark/archive.zip"); + }); + + it('return correct zip filename without protocol specified ', function () { + let result = templateGenerator.getExternalProject("github.com/embark-framework/embark"); + assert.strictEqual(result.url, "https://github.com/embark-framework/embark/archive/master.zip"); + assert.strictEqual(result.filePath, ".embark/templates/embark-framework/embark/archive.zip"); + }); + + it('return correct zip filename with just username/repo specified', function () { + let result = templateGenerator.getExternalProject("embark-framework/embark"); + assert.strictEqual(result.url, "https://github.com/embark-framework/embark/archive/master.zip"); + assert.strictEqual(result.filePath, ".embark/templates/embark-framework/embark/archive.zip"); + }); + + it('return correct zip filename with just embark template specified', function () { + let result = templateGenerator.getExternalProject("react"); + assert.strictEqual(result.url, "https://github.com/embark-framework/embark-react-template/archive/master.zip"); + assert.strictEqual(result.filePath, ".embark/templates/embark-framework/embark-react-template/archive.zip"); + }); + + }); + + }); +}); +