diff --git a/src/cli.js b/src/cli.js index d953064..7b5dd15 100644 --- a/src/cli.js +++ b/src/cli.js @@ -1,22 +1,42 @@ #!/usr/bin/env node +import chalk from 'chalk'; import microbundle from '.'; import prog from './prog'; +import { stdout, stderr } from './utils'; const run = opts => { microbundle(opts) - .then( output => { - if (output!=null) process.stdout.write(output + '\n'); + .then(output => { + if (output!=null) stdout(output); if (!opts.watch) process.exit(0); }) .catch(err => { - process.stderr.write(String(err.error || err) + '\n'); - if (typeof(err.code) === 'string') { - process.stderr.write('error ' + err.code); - process.exit(1); + process.exitCode = (typeof err.code === 'number' && err.code) || 1; + + const error = err.error || err; + const description = `${error.name ? error.name + ': ' : ''}${error.message || error}`; + const message = error.plugin + ? `(${error.plugin} plugin) ${description}` + : description; + + stderr(chalk.bold.red(message)); + + if (error.loc) { + stderr(); + stderr(`at ${error.loc.file}:${error.loc.line}:${error.loc.column}`); } - else { - process.exit(err.code || 1); + + if (error.frame) { + stderr(); + stderr(chalk.dim(error.frame)); } + else if (err.stack) { + const headlessStack = error.stack.replace(message, ''); + stderr(chalk.dim(headlessStack)); + } + + stderr(); + process.exit(); }); }; diff --git a/src/index.js b/src/index.js index bb0b12f..d1f058c 100644 --- a/src/index.js +++ b/src/index.js @@ -19,7 +19,7 @@ import prettyBytes from 'pretty-bytes'; import shebangPlugin from 'rollup-plugin-preserve-shebang'; import typescript from 'rollup-plugin-typescript2'; import flow from './lib/flow-plugin'; -import { readFile, isDir, isFile } from './utils'; +import { readFile, isDir, isFile, stdout, stderr } from './utils'; import camelCase from 'camelcase'; const removeScope = name => name.replace(/^@.*\//, ''); @@ -39,9 +39,9 @@ export default async function microbundle(options) { options.pkg = JSON.parse(await readFile(resolve(cwd, 'package.json'), 'utf8')); } catch (err) { - process.stderr.write(chalk.yellow(`${chalk.yellow.inverse('WARN')} no package.json found. Assuming a pkg.name of "${basename(options.cwd)}".`) + '\n'); + stderr(chalk.yellow(`${chalk.yellow.inverse('WARN')} no package.json found. Assuming a pkg.name of "${basename(options.cwd)}".`)); let msg = String(err.message || err); - if (!msg.match(/ENOENT/)) console.warn(` ${chalk.red.dim(msg)}`); + if (!msg.match(/ENOENT/)) stderr(` ${chalk.red.dim(msg)}`); options.pkg = {}; hasPackageJson = false; } @@ -49,7 +49,7 @@ export default async function microbundle(options) { if (!options.pkg.name) { options.pkg.name = basename(options.cwd); if (hasPackageJson) { - process.stderr.write(chalk.yellow(`${chalk.yellow.inverse('WARN')} missing package.json "name" field. Assuming "${options.pkg.name}".`) + '\n'); + stderr(chalk.yellow(`${chalk.yellow.inverse('WARN')} missing package.json "name" field. Assuming "${options.pkg.name}".`)); } } @@ -104,7 +104,7 @@ export default async function microbundle(options) { if (options.watch) { const onBuild = options.onBuild; return new Promise((resolve, reject) => { - process.stdout.write(chalk.blue(`Watching source, compiling to ${relative(cwd, dirname(options.output))}:\n`)); + stdout(chalk.blue(`Watching source, compiling to ${relative(cwd, dirname(options.output))}:`)); steps.map(options => { watch(Object.assign({ output: options.outputOptions, @@ -115,7 +115,7 @@ export default async function microbundle(options) { } if (e.code === 'END') { getSizeInfo(options._code, options.outputOptions.file).then(text => { - process.stdout.write(`Wrote ${text.trim()}\n`); + stdout(`Wrote ${text.trim()}`); }); if (typeof onBuild === 'function') { onBuild(e); diff --git a/src/utils.js b/src/utils.js index acbc6f6..72cb7f2 100644 --- a/src/utils.js +++ b/src/utils.js @@ -6,3 +6,5 @@ export const readFile = promisify(fs.readFile); export const stat = promisify(fs.stat); export const isDir = name => stat(name).then( stats => stats.isDirectory() ).catch( () => false ); export const isFile = name => stat(name).then( stats => stats.isFile() ).catch( () => false ); +export const stdout = console.log.bind(console); // eslint-disable-line no-console +export const stderr = console.error.bind(console);