diff --git a/README.md b/README.md index 17e3468..5d0605a 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,19 @@ Use your own starting point when setting up a new app, e.g. CSS, JS, manifests a $ npm install -g create-react-app $ npm install -g craftool -## Create a new app +## Create a new app from remote ZIP $ craft MyApp https://github.com/stoyan/fail/archive/master.zip $ cd MyApp $ npm install . # sets up create-react-app + +## Create a new app from local ZIP file + + $ craft MyApp ../template.zip + $ cd MyApp + $ npm install . # sets up create-react-app -This creates an app called `MyApp` using a zip template from github +This creates an app called `MyApp` using a zip template. ## Get serious diff --git a/index.js b/index.js index cc188c0..4daca32 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,7 @@ if (process.argv.length < 4) { console.log('Please provide an app name and a ZIP with the template'); console.log('e.g.'); - console.log('$ craft MyApp https://github.com/stoyan/fail/archive/master.zip'); + console.log('$ craft MyApp https://github.com/stoyan/fail/archive/master.zip'); process.exit(1); } @@ -12,9 +12,10 @@ const path = require('path'); const request = require('request'); const url = require('url'); const unzip = require('extract-zip'); +const resolve = require('resolve-file'); const stat = fs.statSync; - + const zip = process.argv[3]; const app = process.argv[2]; @@ -23,16 +24,20 @@ const replacebles = [ '.css', '.js', '.json', -].reduce((res, el) => {res[el] = 1; return res;}, {}); +].reduce((res, el) => {res[el] = 1;return res;}, {}); let tempDir; let tempUnzipDir; +let localZIPPath; const appDir = path.resolve(process.cwd(), app); log('Validating...'); const uri = url.parse(zip); if (!uri.host || !uri.path || !uri.protocol) { - fail(zip, 'is not a valid URL'); + localZIPPath = resolve(uri.href); + if (!localZIPPath) { + fail(zip, 'is not a valid URL or file'); + } } try { @@ -46,38 +51,51 @@ fs.mkdirSync(appDir); tempDir = fs.mkdtempSync(appDir); tempUnzipDir = fs.mkdtempSync(tempDir); -log('Downloading template...'); -const localZip = path.resolve(tempDir, 'template.zip'); -const file = fs.createWriteStream(localZip); - -request - .get(zip) - .on('error', err => fail(err)) - .pipe(file); +if (localZIPPath) { + log('Local file detected (' + localZIPPath + ')...'); + unzipFile(localZIPPath, tempUnzipDir)(); +} else { + log('Downloading template...'); + const localZip = path.resolve(tempDir, 'template.zip'); + const file = fs.createWriteStream(localZip); + + request + .get(uri.href) + .on('error', err => fail(err)) + .pipe(file); + + file.on('finish', () => { + file.close(unzipFile(localZip, tempUnzipDir)); + }); +} -file.on('finish', () => { - file.close(() => { +function unzipFile(zipFile, targetPath) { + return () => { let packageDir; log('Unzipping...'); - unzip(localZip, { - dir: tempUnzipDir, + unzip( + zipFile, + { + dir: targetPath, onEntry: entry => { if (entry.fileName.endsWith('package.json')) { - packageDir = path.resolve(tempUnzipDir, path.dirname(entry.fileName)); + packageDir = path.resolve(targetPath, path.dirname(entry.fileName)); } }, - }, err => { + }, + err => { if (err) { - fail('Error unzipping, giving up', localZip); + fail('Error unzipping, giving up', zipFile); } if (!packageDir) { fail('package.json missing from the template, giving up'); } - log('Configuring app...') + log('Configuring app...'); createApp(packageDir, appDir); - }); - }); -}); + } + ); + }; +} function createApp(source, dest) { fs.copy(source, dest, (err) => { @@ -86,7 +104,7 @@ function createApp(source, dest) { } const packageJson = path.resolve(dest, 'package.json'); const oldPackage = require(packageJson); - + // replace all app names in all files replaceFiles(dest, oldPackage.name, app); @@ -98,21 +116,21 @@ function createApp(source, dest) { logError(err); } }); - + // write readme fs.writeFile( path.resolve(dest, 'README.md'), `# ${app}\n\nHello`, () => {} ); - + done(); }); } function rmTemp() { if (tempDir) { - fs.removeSync(tempDir); + fs.removeSync(tempDir); } if (tempUnzipDir) { fs.removeSync(tempUnzipDir); @@ -127,17 +145,17 @@ function replaceFiles(dir, seek, replaceWith) { if (f === 'package.json' || f.startsWith('README')) { return; // special plan for these } - + const file = path.resolve(dir, f); const stats = stat(file); - + if (stats.isDirectory()) { return replaceFiles(file, seek, replaceWith); } if (!replacebles[path.extname(f)]) { return; // images and such - } - + } + fs.readFile(file, 'utf-8', (err, contents) => { if (err) { logError(err); @@ -157,13 +175,13 @@ function fail(...msg) { logError(msg.join(' ')); rmTemp(); if (appDir) { - fs.removeSync(appDir); + fs.removeSync(appDir); } process.exit(1); } function log(...msg) { - console.log('\x1B[90m'+ msg.join(' ') +'\x1B[39m'); // thanks echomd + console.log('\x1B[90m' + msg.join(' ') + '\x1B[39m'); // thanks echomd } function logError(...msg) { @@ -176,7 +194,7 @@ function done() { console.log(' cd ' + app); console.log(' npm install .'); rmTemp(); - + const postcraft = path.resolve(appDir, 'postcraft.txt'); fs.readFile(postcraft, 'utf-8', (err, contents) => { if (err) {/* whatever */} @@ -185,4 +203,4 @@ function done() { fs.remove(postcraft); }); -} \ No newline at end of file +} diff --git a/package.json b/package.json index 5eff26a..122bf4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "craftool", - "version": "1.2.3", + "version": "1.3.0", "description": "Create React App From Template", "main": "index.js", "author": "", @@ -9,9 +9,10 @@ "craft": "./index.js" }, "dependencies": { + "extract-zip": "^1.6.0", "fs-extra": "^1.0.0", "request": "^2.79.0", - "extract-zip": "^1.6.0" + "resolve-file": "^0.3.0" }, "repository": { "type": "git",