Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
88 changes: 53 additions & 35 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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];

Expand All @@ -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 {
Expand All @@ -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) => {
Expand All @@ -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);

Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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) {
Expand All @@ -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 */}
Expand All @@ -185,4 +203,4 @@ function done() {
fs.remove(postcraft);
});

}
}
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "craftool",
"version": "1.2.3",
"version": "1.3.0",
"description": "Create React App From Template",
"main": "index.js",
"author": "",
Expand All @@ -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",
Expand Down