Skip to content
This repository was archived by the owner on Oct 31, 2024. It is now read-only.

Commit 475deee

Browse files
committed
2.2.0
2.2.0 - 2017-04-16 ------------------------------------------------------------ * CLI now downloads & extracts source from Github instead of bundling with the kit * Adds ESLint and `npm run lint` step * Removes redundant `klaw` and `through2` packages
2 parents d365f2b + 531efd5 commit 475deee

32 files changed

+1198
-8965
lines changed

.eslintrc.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module.exports = {
2+
extends: [
3+
'eslint:recommended',
4+
'plugin:import/errors',
5+
'plugin:import/warnings',
6+
],
7+
parserOptions: {
8+
ecmaVersion: 2017,
9+
},
10+
env: {
11+
node: true,
12+
},
13+
rules: {
14+
'no-console': 0,
15+
'import/no-unresolved': [2, {commonjs: true}],
16+
},
17+
};

CHANGELOG

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
2.2.0 - 2017-04-16
2+
------------------------------------------------------------
3+
* CLI now downloads & extracts source from Github instead of bundling with the kit
4+
* Adds ESLint and `npm run lint` step
5+
* Removes redundant `klaw` and `through2` packages
6+
17
2.1.2 - 2017-04-15
28
------------------------------------------------------------
39
* Merges #24 - Adds `browserlist` for global Webpack build targeting

cli/index.js

Lines changed: 127 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,30 @@ Authored by Lee Benson <lee@leebenson.com>
99
// Node
1010
const os = require('os');
1111
const path = require('path');
12-
const fse = require('fs-extra');
13-
const spawn = require('cross-spawn');
1412

1513
// Third-party
1614
const chalk = require('chalk');
1715
const yargs = require('yargs');
1816
const updateNotifier = require('update-notifier');
1917
const inquirer = require('inquirer');
20-
const through2 = require('through2');
21-
const klaw = require('klaw');
22-
const ejs = require('ejs');
2318
const exists = require('command-exists').sync;
2419
const spdx = require('spdx');
20+
const fse = require('fs-extra');
21+
const spawn = require('cross-spawn');
22+
const temp = require('temp').track();
23+
const yauzl = require('yauzl');
24+
const request = require('request');
25+
const mkdirp = require('mkdirp');
2526

2627
// Local
2728
const banner = require('./banner.js');
2829
const usage = require('./usage.js');
29-
const package = require('../package.json');
30+
const pkg = require('../package.json');
3031

3132
// ----------------------
3233

3334
// Check for ReactQL updates automatically
34-
updateNotifier({ pkg: package, updateCheckInterval: 0 }).notify();
35+
updateNotifier({ pkg, updateCheckInterval: 0 }).notify();
3536

3637
/*
3738
Helper functions
@@ -44,12 +45,13 @@ function emoji(ifSupported, ifNot='\b') {
4445

4546
// Show error message. We'll use this if yarn/npm throws back a non-zero
4647
// code, to display the problem back to the console
47-
function showError(msg) {
48+
function fatalError(msg) {
4849
console.error(`
4950
${chalk.bold.bgRed('ERROR')}${chalk.bgRed(' -- See the output below:')}
5051
5152
${msg}
5253
`.trim());
54+
process.exit();
5355
}
5456

5557
// Finished instructions. Show the user how to use the starter kit
@@ -79,11 +81,6 @@ ${separator}
7981
`.trim();
8082
}
8183

82-
// Paths used during copying the starter kit
83-
const paths = {
84-
files: path.resolve(__dirname, '../starter/files'),
85-
};
86-
8784
// ASCII chars to show possible 'spinner' positions. We'll prefix this to
8885
// the installation message during yarn/npm packaging
8986
const spinner = [
@@ -235,116 +232,131 @@ const args = yargs
235232
// Modify path to be absolute
236233
args.path = path.resolve(process.cwd(), args.path);
237234

238-
// Copy the starter kit files over to the new path
239-
fse.copySync(paths.files, args.path);
240-
241-
// Edit `package.json` with project-specific information
242-
const packageJsonFile = path.resolve(args.path, 'package.json');
243-
const packageJson = require(packageJsonFile);
244-
245-
fse.writeJsonSync(packageJsonFile, Object.assign(packageJson, {
246-
name: args.name,
247-
description: args.desc,
248-
license: args.license,
249-
}));
250-
251-
/*
252-
Find template files, execute the EJS, and create a new file
253-
with the rendered content.
254-
*/
255-
256-
// We'll use `klaw` to walk through the starter kit path, and create
257-
// a stream of `fs.Stats` objects representing each file
258-
klaw(args.path)
259-
260-
// Create a function that will handle file objects, determine whether
261-
// we have templates, and compile to EJS. We're using `through2` to
262-
// take care of handling the stream
263-
.pipe(through2.obj(function(item, enc, next) {
264-
265-
// We're only interested in `.reactql` template files
266-
if (/\.reactql$/.test(item.path)) {
267-
268-
// Get the file content
269-
const content = fse.readFileSync(item.path, {
270-
encoding: 'utf8',
271-
});
272-
273-
// Compile the template through EJS, passing `args`
274-
const compiled = ejs.render(content, { args }).trim();
275-
276-
// Write non-blank content out using the original file
277-
// name, minus the .reactql extension
278-
if (compiled) {
279-
fse.writeFileSync(
280-
item.path.replace(/\.reactql$/, ''),
281-
compiled,
282-
{
283-
encoding: 'utf8',
235+
// Create a tmp file stream to save the file locally
236+
const file = temp.createWriteStream();
237+
238+
// Show the separator to make it clear we've moved on to the
239+
// next step
240+
console.log(separator);
241+
242+
console.log('Downloading source code from Github...');
243+
244+
// Download the .zip containing the kit's source code
245+
request
246+
.get('https://github.com/reactql/kit/archive/master.zip')
247+
.pipe(
248+
file.on('finish', () => {
249+
console.log('Extracting archive...');
250+
yauzl.open(file.path, { lazyEntries: true }, (e, zip) => {
251+
if (e) fatalError("Couldn't read zip file");
252+
253+
// Read the zip entry
254+
zip.readEntry();
255+
256+
// Process zip files
257+
zip.on('entry', entry => {
258+
// Remove leading folder that Github uses
259+
const fileName = entry.fileName
260+
.split('/')
261+
.slice(1)
262+
.join('/');
263+
264+
// Proceed only if we have a file name
265+
if (fileName) {
266+
267+
// Resolve the full file name, including the path
268+
const fullName = path.resolve(args.path, fileName);
269+
270+
// If it's a folder (based on original filename), create it
271+
if (/\/$/.test(fileName)) {
272+
mkdirp(fullName, e => {
273+
if (e) fatalError(`Couldn't create folder ${fullName}`);
274+
zip.readEntry();
275+
});
276+
} else {
277+
// Otherwise, it's a regular file -- write it
278+
zip.openReadStream(entry, (e, readStream) => {
279+
if (e) fatalError(`Couldn't create ZIP read stream`);
280+
readStream
281+
.pipe(fse.createWriteStream(fullName))
282+
.on('finish', () => zip.readEntry());
283+
});
284+
}
285+
} else {
286+
// Blank filename - move on to the next one
287+
zip.readEntry();
288+
}
289+
})
290+
.on('end', () => {
291+
// Edit `package.json` with project-specific information
292+
console.log('Writing package.json...');
293+
294+
const pkgJsonFile = path.resolve(args.path, 'package.json');
295+
const pkgJson = require(pkgJsonFile);
296+
297+
fse.writeJsonSync(pkgJsonFile, Object.assign(pkgJson, {
298+
name: args.name,
299+
description: args.desc,
300+
license: args.license,
301+
}));
302+
303+
// Install pakckage dependencies using yarn if we have
304+
// it, otherwise using NPM
305+
let installer;
306+
307+
// Prefer yarn (it's faster). If it doesn't exist, fall back to
308+
// npm which every user should have. Inform the user that yarn is
309+
// the preferred option!
310+
if (exists('yarn')) {
311+
installer = ['yarn', []];
312+
console.log('Installing via Yarn...\n');
313+
314+
} else {
315+
installer = ['npm', ['i']];
316+
console.log(`Yarn not found; falling back to NPM. Tip: For faster future builds, install ${chalk.underline('https://yarnpkg.com')}\n`);
284317
}
285-
);
286-
}
287-
288-
// Delete the template
289-
fse.unlinkSync(item.path);
290-
}
291-
// Callback to say we've completed walking the tree
292-
next();
293-
}))
294-
// When finished, all of our templates have been run
295-
.on('finish', () => {
296-
// Install the `package.json` dependencies using yarn if we have
297-
// it, otherwise using NPM
298-
let installer;
299-
300-
// Show the separator to make it clear we've moved on to the
301-
// next step
302-
console.log(separator);
303-
304-
// Prefer yarn (it's faster). If it doesn't exist, fall back to
305-
// npm which every user should have. Inform the user that yarn is
306-
// the preferred option!
307-
if (exists('yarn')) {
308-
installer = ['yarn', []];
309-
console.log('Installing via Yarn...\n');
310-
311-
} else {
312-
installer = ['npm', ['i']];
313-
console.log(`Yarn not found; falling back to NPM. Tip: For faster future builds, install ${chalk.underline('https://yarnpkg.com')}\n`);
314-
}
315-
316-
// Create a bottom bar to display the installation spinner at the bottom
317-
// of the console.
318-
const ui = new inquirer.ui.BottomBar({ bottomBar: spinner[0] });
319-
320-
// Temporary var to track the position of the 'spinner'
321-
let i = 0;
322-
323-
// Update the spinner every 300ms, to reflect the installation activity
324-
const update = setInterval(function () {
325-
ui.updateBottomBar(`\n${spinner[++i % 4]} Installing modules -- Please wait...`);
326-
}, 300);
327-
328-
// Execute yarn/npm as a child process, pipe output to stdout
329-
spawn(...installer, {cwd: args.path, stdio: 'pipe'})
330-
.stdout.pipe(ui.log)
331-
// When finished, stop the spinner, update with usage instructons and exit
332-
.on('close', function () {
333-
clearInterval(update);
334-
ui.updateBottomBar('');
335-
console.log(finished(args.path));
336-
process.exit();
337-
});
318+
319+
// Create a bottom bar to display the installation spinner at the bottom
320+
// of the console.
321+
const ui = new inquirer.ui.BottomBar({ bottomBar: spinner[0] });
322+
323+
// Temporary var to track the position of the 'spinner'
324+
let i = 0;
325+
326+
// Update the spinner every 300ms, to reflect the installation activity
327+
const update = setInterval(function () {
328+
ui.updateBottomBar(`\n${spinner[++i % 4]} Installing modules -- Please wait...`);
329+
}, 300);
330+
331+
// Execute yarn/npm as a child process, pipe output to stdout
332+
spawn(...installer, {cwd: args.path, stdio: 'pipe'})
333+
.stdout.pipe(ui.log)
334+
.on('error', () => fatalError("Couldn't install packages"))
335+
// When finished, stop the spinner, update with usage instructons and exit
336+
.on('close', function () {
337+
clearInterval(update);
338+
ui.updateBottomBar('');
339+
console.log(finished(args.path));
340+
process.exit();
341+
});
342+
});
343+
})
344+
})
345+
)
346+
.on('error', () => {
347+
console.error("Couldn't download source code from Github");
348+
process.exit();
338349
});
339-
});
350+
351+
});
340352
},
341353
})
342354
.command({
343355
command: 'version',
344356
aliases: ['v'],
345357
desc: 'Show ReactQL version',
346358
handler() {
347-
console.log(package.version);
359+
console.log(pkg.version);
348360
},
349361
})
350362
.option('name', {
@@ -361,7 +373,7 @@ const args = yargs
361373
})
362374
.option('license', {
363375
alias: 'l',
364-
describe: 'License for package.json',
376+
describe: 'License for pkg.json',
365377
})
366378
.help()
367379
.argv;

package.json

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
22
"name": "reactql",
3-
"version": "2.1.2",
3+
"version": "2.2.0",
44
"description": "Universal React+GraphQL starter kit: React, Apollo, Webpack 2, React Router 4, PostCSS, SSR",
55
"main": "cli/index.js",
66
"scripts": {
77
"test": "echo \"Error: no test specified\" && exit 1",
8+
"lint": "eslint .",
89
"postinstall": "node cli/postinstall"
910
},
1011
"repository": {
@@ -38,13 +39,18 @@
3839
"chalk": "^1.1.3",
3940
"command-exists": "^1.2.2",
4041
"cross-spawn": "^5.1.0",
41-
"ejs": "^2.5.6",
4242
"fs-extra": "^2.1.2",
4343
"inquirer": "^3.0.6",
44-
"klaw": "^1.3.1",
44+
"mkdirp": "^0.5.1",
45+
"request": "^2.81.0",
4546
"spdx": "^0.5.1",
46-
"through2": "^2.0.3",
47+
"temp": "^0.8.3",
4748
"update-notifier": "^2.1.0",
48-
"yargs": "^7.0.2"
49+
"yargs": "^7.0.2",
50+
"yauzl": "^2.7.0"
51+
},
52+
"devDependencies": {
53+
"eslint": "^3.19.0",
54+
"eslint-plugin-import": "^2.2.0"
4955
}
5056
}

starter/files/.babelrc

Lines changed: 0 additions & 9 deletions
This file was deleted.

starter/files/.eslintignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)