Comparing version 0.0.4 to 0.1.0
@@ -5,11 +5,16 @@ #!/usr/bin/env node | ||
const path = require('path'); | ||
const debug = require('debug')('bake'); | ||
const which = require('which'); | ||
const { existsSync: exists } = fs; | ||
const { spawn } = require('child_process'); | ||
const { CLI } = require('..'); | ||
const { format } = require('util'); | ||
const { existsSync: exists } = fs; | ||
const { CLI, Bake } = require('..'); | ||
const { verbose, info, warn, error } = require('../src/log'); | ||
const { fail } = CLI; | ||
const assign = Object.assign || require('object-assign'); | ||
const separator = process.platform === 'win32' ? ';' : ':'; | ||
// Init | ||
let separator = process.platform === 'win32' ? ';' : ':'; | ||
let env = assign({}, process.env, { | ||
@@ -23,4 +28,32 @@ PATH: process.env.PATH + separator + path.resolve('./node_modules/.bin') | ||
let cli = new CLI(bakefile, { | ||
let bake = new Bake(bakefile, { | ||
env: env | ||
}); | ||
bake.on(Bake.UNKNOWN_TARGET, (target, targets) => { | ||
var cmd = 'bake-' + target; | ||
which(cmd, (err, filename) => { | ||
if (err) { | ||
fail(err.message); | ||
return bake.help(targets); | ||
} | ||
// var args = bake.argv._.slice(1); | ||
var args = process.argv.slice(3); | ||
info('Go for it', filename, args); | ||
var sh = spawn(filename, args, { | ||
stdio: 'inherit', | ||
env: bake.env | ||
}); | ||
// sh.on('error', error.bind(null)); | ||
sh.on('close', (code) => { | ||
if (code === 0) return; | ||
// fail(new Error(format('%s exited with code %d', cmd, code))); | ||
process.exit(code); | ||
}); | ||
}); | ||
}); |
@@ -5,2 +5,22 @@ # Change Log | ||
<a name="0.1.0"></a> | ||
# [0.1.0](https://github.com/mklabs/bake/compare/v0.0.4...v0.1.0) (2016-05-25) | ||
### Bug Fixes | ||
* improve log output, merge dependencies ([838a08c](https://github.com/mklabs/bake/commit/838a08c)) | ||
### Features | ||
* --help output list of available templates ([b58eb43](https://github.com/mklabs/bake/commit/b58eb43)) | ||
* "global" commands, bake-<target> ([ce6572d](https://github.com/mklabs/bake/commit/ce6572d)) | ||
* Add --skip flag, merge .eslintrc / .babelrc ([091e28c](https://github.com/mklabs/bake/commit/091e28c)) | ||
* add cli template ([3e90cef](https://github.com/mklabs/bake/commit/3e90cef)) | ||
* bake init <template> ([34b788b](https://github.com/mklabs/bake/commit/34b788b)) | ||
* implement template hook for start / install ([5990fa6](https://github.com/mklabs/bake/commit/5990fa6)) | ||
<a name="0.0.4"></a> | ||
@@ -7,0 +27,0 @@ ## [0.0.4](https://github.com/mklabs/bake/compare/v0.0.3...v0.0.4) (2016-05-24) |
@@ -1,3 +0,8 @@ | ||
var bake = module.exports = require('./src/parser'); | ||
var bake = module.exports; | ||
bake.log = require('./src/log'); | ||
bake.parser = require('./src/parser'); | ||
bake.CLI = require('./src/cli'); | ||
bake.Bake = require('./src/bake'); | ||
bake.Template = require('./src/template'); |
278
lib/cli.js
@@ -1,208 +0,172 @@ | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const minimist = require('minimist'); | ||
const chalk = require('chalk'); | ||
const events = require('events'); | ||
const debug = require('debug'); | ||
const log = require('./log'); | ||
const logsymbols = require('log-symbols'); | ||
const fsutil = require('./util'); | ||
const { spawn } = require('child_process'); | ||
const debug = require('debug'); | ||
const padding = 20; | ||
const PADDING = 20; | ||
const exists = fs.existsSync; | ||
// CLI | ||
// | ||
// This class exposes various utilities for parsing options and logging purpose. | ||
// | ||
// - this.argv - minimist result from options.argv or process.argv.slice(2) | ||
// - this.debug - debug module logger, enabled with -d flag | ||
// - this.alias - if defined, is used to parse arguments with minimist | ||
// - this.env - options.env or a clone of process.env | ||
// - this.start - Timestamp marking instance creation, namely used to report | ||
// build time. | ||
// | ||
// And these static methods: | ||
// | ||
// - CLI.fail - to invoke with an error, log the error with npmlog error level | ||
// - CLI.end - Log options.success message with elapsed time as a parameter | ||
// | ||
// Options: | ||
// | ||
// - this.options.namespace - Define the debug namespace (eg. require('debug')(namespace)). | ||
// Default: bake:cli | ||
// - this.options.success - Success message to print with end() | ||
export default class CLI extends events.EventEmitter { | ||
const bake = require('..'); | ||
const log = bake.log; | ||
get example() { return ''; } | ||
export default class CLI { | ||
// Used to parse arguments with minimist | ||
get alias() { | ||
return { | ||
alias: { | ||
h: 'help', | ||
v: 'version', | ||
d: 'debug' | ||
} | ||
h: 'help', | ||
v: 'version', | ||
d: 'debug' | ||
}; | ||
} | ||
constructor(filename, opts = {}) { | ||
this.options = opts; | ||
this.env = this.options.env || {}; | ||
this.argv = minimist(this.options.argv || process.argv.slice(2), this.alias); | ||
// Used to generate the help output | ||
get flags() { | ||
return { | ||
help: 'Show this help output', | ||
version: 'Show package version' | ||
}; | ||
} | ||
if (opts.debug || this.argv.debug) { | ||
debug.enable('bake*'); | ||
} | ||
if (!filename) { | ||
return CLI.fail('Missing %s Makefile / Bakefile', filename); | ||
} | ||
this.debug = debug('bake:cli'); | ||
constructor(opts = {}) { | ||
super(); | ||
this.start = Date.now(); | ||
this.debug('Bake init CLI with %s options', Object.keys(opts).join(' ')); | ||
this.bakefile = filename; | ||
this.init(); | ||
} | ||
this.options = opts; | ||
this.options.namespace = this.options.namespace || 'bake:cli'; | ||
this.options.success = this.options.success || ('Build success in %sms'); | ||
init() { | ||
let argv = this.argv; | ||
if (argv.help && !this.bakefile) { | ||
return CLI.help(); | ||
} | ||
this.options.name = this.options.name || process.argv[1].split('/').slice(-1)[0]; | ||
if (argv.version) { | ||
return console.log(require('../package.json').version); | ||
} | ||
this.argv = this.parse(this.options.argv); | ||
this.args = this.argv._.concat(); | ||
this.env = this.options.env || Object.assign({}, process.env); | ||
if (!this.bakefile) { | ||
log('No Makefile/Bakefile in the current folder'); | ||
return this.generate('Makefile', argv); | ||
if (this.options.debug || this.argv.debug) { | ||
debug.enable(this.options.namespace); | ||
} | ||
this.file = fs.readFileSync(this.bakefile, 'utf8'); | ||
this.result = bake(this.file); | ||
this.debug = debug(this.options.namespace); | ||
} | ||
this.targets = this.result.targets; | ||
this.variables = this.result.variables; | ||
if (argv.help) { | ||
return CLI.help(this.targets); | ||
} | ||
let args = argv._; | ||
if (this.bakefile !== 'Bakefile' && this.bakefile !== 'Makefile') args = args.slice(1); | ||
if (!args[0]) args[0] = 'all'; | ||
// Run! | ||
this.run(args); | ||
parse(argv = process.argv.slice(2), alias = this.alias) { | ||
return minimist(argv, { alias }); | ||
} | ||
run(targets) { | ||
this.debug('Run %s targets', targets.join(' ')); | ||
var argv = targets.concat(); | ||
exec(recipe, opts = { env: this.env, stdio: 'inherit' }) { | ||
return new Promise((r, errback) => { | ||
(function next(name) { | ||
if (!name) return CLI.end(this.start, r); | ||
this.debug('exec:', recipe); | ||
this.silly('env:', opts.env); | ||
spawn('bash', ['-c', recipe], opts) | ||
.on('error', errback) | ||
.on('close', (code) => { | ||
if (code !== 0) { | ||
this.error(recipe); | ||
return errback(new Error('Recipe exited with code %d', code)); | ||
} | ||
this.executeTarget(name) | ||
.then(() => { | ||
next.call(this, argv.shift()); | ||
}) | ||
.catch((err) => { | ||
CLI.fail(argv.debug ? err : err.message); | ||
errback(err); | ||
}); | ||
}).call(this, argv.shift()); | ||
r(); | ||
}); | ||
}); | ||
} | ||
executeTarget(target) { | ||
return new Promise((r, errback) => { | ||
if (!this.targets[target]) { | ||
CLI.help(this.targets); | ||
return errback(new Error('No target matching "' + target + '"')); | ||
} | ||
log.info('Invoking %s target', target); | ||
var name = this.targets[target]; | ||
return this.executeRecipe(name, target) | ||
.then(r) | ||
.catch(errback) | ||
}); | ||
end(cb) { | ||
var time = Date.now() - this.start; | ||
return CLI.end(this.options.success, time, cb); | ||
} | ||
executeRecipe(target, name) { | ||
return new Promise((r, errback) => { | ||
var prerequities = target.prerequities; | ||
help(targets = {}) { | ||
let targetList = ''; | ||
let leftpad = this.options.leftpad || ' '; | ||
if (Object.keys(targets).length) targetList += ' Targets:\n'; | ||
// deps on this recipe, execute rules right away | ||
if (!prerequities.length) return this.executeRules(target) | ||
.then(r) | ||
.catch(errback); | ||
var keys = Object.keys(targets); | ||
targetList += keys.map((t) => { | ||
return leftpad + t + this.pad(t) + 'Run target ' + t; | ||
}).join('\n'); | ||
// found prereq, execute them before executing rules | ||
this.debug('Prerequities "%s" for target %s', prerequities.join(' '), name); | ||
return this.executePrereq(target) | ||
.then(() => { | ||
return this.executeRules(target) | ||
.then(r) | ||
.catch(errback); | ||
}) | ||
.catch(errback); | ||
}); | ||
} | ||
var options = ''; | ||
if (this.flags) { | ||
options += 'Options:\n'; | ||
options += Object.keys(this.flags).map((flag) => { | ||
return leftpad + '--' + flag + this.pad('--' + flag) + this.flags[flag]; | ||
}).join('\n'); | ||
} | ||
executePrereq(target) { | ||
return new Promise((r, errback) => { | ||
var prerequities = target.prerequities; | ||
let opts = { | ||
example: this.example || this.options.example, | ||
name: this.options.name, | ||
commands: targetList, | ||
more: this.more, | ||
options, | ||
}; | ||
// Before executing this recipe, execute any prerequities first | ||
(function nextPrereq(pre) { | ||
if (!pre) return r(); | ||
return CLI.help(opts); | ||
} | ||
this.executeTarget(pre) | ||
.catch(errback) | ||
.then(() => { | ||
nextPrereq.call(this, prerequities.shift()); | ||
}); | ||
}).call(this, prerequities.shift()); | ||
}); | ||
pad(str, padding = PADDING) { | ||
let len = padding - str.length; | ||
return new Array(len <= 1 ? 2 : len).join(' '); | ||
} | ||
executeRules(target) { | ||
var recipe = target.recipe; | ||
return new Promise((r, errback) => { | ||
this.debug('bash:', recipe); | ||
// Help output | ||
// | ||
// Options: | ||
// | ||
// - name - Used in the generated example (ex: $ name --help) | ||
// - example - Used in the generated example instead of the default one | ||
// - options - Used in the generated example instead of the default one | ||
static help(options = {}) { | ||
options.name = options.name || ''; | ||
options.example = options.example || (options.name + ' --help'); | ||
var sh = spawn('bash', ['-c', recipe], { | ||
stdio: 'inherit', | ||
env: this.env | ||
}); | ||
sh.on('error', errback); | ||
sh.on('close', (code) => { | ||
if (code != 0) { | ||
return errback(new Error('%s exited with code %d', target.name, code)); | ||
} | ||
r(); | ||
}); | ||
}); | ||
} | ||
static help(targets = []) { | ||
console.log(` | ||
$ bake <target> [options] | ||
$ ${options.example} | ||
Options: | ||
-h, --help Show this help | ||
-v, --version Show package version | ||
-d, --debug Enable extended output | ||
`); | ||
${options.options}`); | ||
if (Object.keys(targets).length) console.log(' Targets:'); | ||
if (options.commands) console.log('\n', options.commands); | ||
if (options.more) console.log(options.more); | ||
var keys = Object.keys(targets); | ||
var str = keys.map((t) => { | ||
let pad = new Array(padding - t.length).join(' '); | ||
return ' ' + t + pad + 'Run target ' + t; | ||
}); | ||
console.log(str.join('\n')); | ||
console.log(); | ||
} | ||
static fail() { | ||
static fail(e, exit) { | ||
log.error.apply(log, arguments); | ||
process.exit(1); | ||
if (exit) process.exit(isNaN(exit) ? 1 : exit); | ||
} | ||
static end(start, cb) { | ||
var time = Date.now() - start; | ||
log.info(logsymbols.success + ' Build sucess in %sms', time); | ||
static end(message, time, cb) { | ||
log.info(message, time); | ||
cb && cb(); | ||
} | ||
} | ||
CLI.PADDING = PADDING; | ||
Object.assign(CLI.prototype, log); | ||
Object.assign(CLI.prototype, fsutil); |
const chalk = require('chalk'); | ||
const chalk = require('chalk'); | ||
const npmlog = require('npmlog'); | ||
const prefix = 'bake'; | ||
const { format } = require('util'); | ||
const { error, info, success, warning } = require('log-symbols'); | ||
let log = module.exports = npmlog; | ||
@@ -13,1 +16,31 @@ log.heading = prefix; | ||
}); | ||
// Few logsymbols log helper | ||
log.success = (...args) => { | ||
let msg = format.apply(null, args); | ||
let level = chalk.green('info'); | ||
let symbol = success; | ||
console.error(`${log.heading} ${symbol} ${level} ${msg}`); | ||
}; | ||
log.warning = (...args) => { | ||
let msg = format.apply(null, args); | ||
let level = chalk.yellow('warn'); | ||
let symbol = warning; | ||
console.error(`${log.heading} ${symbol} ${level} ${msg}`); | ||
}; | ||
// let _info = log.info; | ||
log.info = (...args) => { | ||
let msg = format.apply(null, args); | ||
let level = chalk.green('info'); | ||
let symbol = info; | ||
console.error(`${log.heading} ${symbol} ${level} ${msg}`); | ||
}; | ||
log.error = (...args) => { | ||
let msg = format.apply(null, args); | ||
let level = chalk.red('ERR '); | ||
let symbol = error; | ||
console.error(`${log.heading} ${symbol} ${level} ${msg}`); | ||
}; |
@@ -14,3 +14,3 @@ | ||
// reference to last parsed target | ||
var last; | ||
var last = {}; | ||
@@ -17,0 +17,0 @@ var tokens = { |
{ | ||
"name": "bake-cli", | ||
"version": "0.0.4", | ||
"version": "0.1.0", | ||
"description": "Make like Task runner", | ||
"bin": { | ||
"bake": "bin/bake.js" | ||
}, | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "bake test" | ||
"test": "mocha -R spec" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "^6.9.0", | ||
"babel-plugin-add-module-exports": "^0.2.1", | ||
"babel-preset-es2015": "^6.9.0", | ||
"bake-cli": "0.0.4", | ||
"eslint": "^2.10.2", | ||
"eslint-config-standard": "^5.3.1", | ||
"eslint-plugin-promise": "^1.1.0", | ||
"eslint-plugin-standard": "^1.3.2", | ||
"mocha": "^2.5.2", | ||
"standard-version": "^2.2.1", | ||
"watchd": "github:mklabs/watchd" | ||
}, | ||
"bin": { | ||
"bake": "bin/bake.js", | ||
"bake-init": "bin/bake-init.js" | ||
}, | ||
"dependencies": { | ||
"chalk": "^1.1.3", | ||
"debug": "^2.2.0", | ||
"jsonlint": "^1.6.2", | ||
"log-symbols": "^1.0.2", | ||
"minimist": "^1.2.0", | ||
"npmlog": "^2.0.3" | ||
"npmlog": "^2.0.3", | ||
"which": "^1.2.9" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "^6.7.5", | ||
"babel-plugin-add-module-exports": "^0.2.1", | ||
"babel-preset-es2015": "^6.6.0", | ||
"standard-version": "^2.2.1", | ||
"watchd": "github:mklabs/watchd" | ||
}, | ||
"repository": { | ||
@@ -27,0 +36,0 @@ "type": "git", |
157
readme.md
@@ -1,5 +0,7 @@ | ||
# Bake | ||
# bake [![Build Status](https://secure.travis-ci.org/mklabs/bake.png)](http://travis-ci.org/mklabs/bake) | ||
> Make like task runner, with npm script goodness | ||
> Make like task runner | ||
npm install bake-cli -g | ||
## The Gist | ||
@@ -53,3 +55,4 @@ | ||
Targets: | ||
foo2 Run target foo2 | ||
all Run target all | ||
build Run target build | ||
foo Run target foo | ||
@@ -60,2 +63,8 @@ prefoo Run target prefoo | ||
$ bake init <template> [options] | ||
default Scaffold an ES6 setup (babel, eslint, ...) | ||
cli Scaffold an ES6 CLI setup (minimist, ...) | ||
## What is Bake ? | ||
@@ -93,6 +102,80 @@ | ||
### Makefiles goodness | ||
## bake init | ||
#### Bash scripting | ||
Basic scaffolding command | ||
Its purpose is to init a project Makefile with sensible defaults for various | ||
development needs. | ||
The default list of templates should be configurable. Adding new ones or | ||
overriding existing ones should be a simple process. | ||
Looking in | ||
- ~/.config/bake/templates | ||
- ~/.bake/templates | ||
Where the templates directories have the following structure: | ||
``` | ||
templates/ | ||
├── es6 | ||
│ ├── .babelrc | ||
│ ├── .eslintrc | ||
│ ├── Makefile | ||
│ ├── package.json | ||
│ └── .travis.yml | ||
└── frontend | ||
├── Makefile | ||
├── package.json | ||
└── webpack.config.js | ||
``` | ||
The subdirectory name is the template name (invoked with bake init <name>). | ||
If no name is defined, it defaults to "default" | ||
- `Makefile` - Is the template Makefile to use | ||
- `package.json` - JSON file to merge with project's package.json (usually to include devDependencies) | ||
- `*.json` - Every JSON files generated is merged with existing files | ||
(`.eslintrc` and `.babelrc` are handled as JSON files) | ||
- Every other top level files is copied to destination, existing files are skipped | ||
The package.json file can have a "bake" field (removed when merged with | ||
package.json), with the following properties: | ||
- "scripts" - Similar to npm scripts, a list of hooks for bake to invoke | ||
- "scripts.start" - Executed when the generation process starts | ||
- "scripts.install" - Executed when the template has been generated | ||
- "description" - Optional description for this template (used on `--help`) | ||
These hooks can be used to further customize the template generation (like | ||
running `npm install` in "scripts.install") | ||
See [the default template](./templates/default) package.json file: | ||
```json | ||
"bake": { | ||
"description": "Scaffold a basic ES6 setup", | ||
"scripts": { | ||
"start": "echo Starting generation of default template", | ||
"prestart": "echo prestart", | ||
"poststart": "echo poststart", | ||
"install": "npm install --loglevel http --cache-min Infinity", | ||
"preinstall": "echo Installing dependencies ...", | ||
"postinstall": "npm ls --depth 1" | ||
} | ||
} | ||
``` | ||
**Note** `--cache-min Infinity` is used to bypass the HTTP network checks to | ||
the registry for already installed packages. | ||
## Makefile | ||
Here is a quick description of Makefiles syntax, with bake differences highlighted. | ||
### Bash scripting | ||
```make | ||
@@ -126,3 +209,3 @@ help: | ||
#### Make like variables | ||
### Make like variables | ||
@@ -142,3 +225,3 @@ ```make | ||
#### Task dependencies | ||
### Task dependencies | ||
@@ -172,3 +255,3 @@ Use prerequities to specify tasks that depends on other tasks. | ||
#### path | ||
### path | ||
@@ -189,1 +272,59 @@ If you depend on modules that define executable scripts, like test suites, | ||
is exported into the `node_modules/.bin` directory on `npm install`. | ||
## Tests | ||
npm test | ||
Outputs help. | ||
```js | ||
cli() | ||
.use('bake -h') | ||
.expect('bake <target...> [options]') | ||
.expect('Options:') | ||
.expect(0) | ||
.end(done); | ||
``` | ||
bake foo. | ||
```js | ||
cli() | ||
.use('bake foo') | ||
.expect('prefoo\nblahblah\nfoo') | ||
.expect(0) | ||
.end(done); | ||
``` | ||
bake all. | ||
```js | ||
cli() | ||
.use('bake') | ||
.expect('prefoo\nblahblah\nfoo\nfoo2\nblahblah\nfoobar') | ||
.expect(0) | ||
.end(done); | ||
``` | ||
bake maoow - Outputs help on UNKNOWN_TARGET. | ||
```js | ||
cli() | ||
.use('bake maoow') | ||
.expect('bake <target...> [options]') | ||
.expect('Options:') | ||
.expect(0) | ||
.end(done); | ||
``` | ||
bake init. | ||
```js | ||
cli({ cwd: join(__dirname, 'examples') }) | ||
.use('bake init --skip') | ||
.expect('Running default template') | ||
.expect(/Makefile\s+already exists, skipping/) | ||
.expect(/Build success in \d+ms/) | ||
.expect(0) | ||
.end(done); | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
40405
34
799
324
7
11
10
3
+ Addedjsonlint@^1.6.2
+ Addedwhich@^1.2.9
+ AddedJSV@4.0.2(transitive)
+ Addedansi-styles@1.0.0(transitive)
+ Addedchalk@0.4.0(transitive)
+ Addedhas-color@0.1.7(transitive)
+ Addedisexe@2.0.0(transitive)
+ Addedjsonlint@1.6.3(transitive)
+ Addednomnom@1.8.1(transitive)
+ Addedstrip-ansi@0.1.1(transitive)
+ Addedunderscore@1.6.0(transitive)
+ Addedwhich@1.3.1(transitive)