From a4cb059fe971b1d3e694210562a7f4ae8c86f9c2 Mon Sep 17 00:00:00 2001 From: eric thul Date: Tue, 24 Mar 2015 22:29:42 -0400 Subject: [PATCH] Adding argument for verbose output The new `--verbose` argument may be passed to `gulp` in order to log the output from `psc-make`. By default the `pscMake` task only log output if an error occurs. This resolves #17 and resolves #7 --- .gitignore | 1 + README.md | 5 ++ index.js | 249 +++++++++++++++++++++++++++------------------------ options.js | 39 ++++++++ package.json | 24 +++-- test.js | 185 +++++++++++++++++++------------------- 6 files changed, 283 insertions(+), 220 deletions(-) create mode 100644 options.js diff --git a/.gitignore b/.gitignore index eadce5e..c29c77b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.log .psci node_modules +output diff --git a/README.md b/README.md index 34d52ef..6c2d343 100644 --- a/README.md +++ b/README.md @@ -91,3 +91,8 @@ Invokes the `pscDocs` command. Generates a `.psci` file in the current directory. Each source file is added with the `:m` command. + +## Command line arguments + +The `--verbose` argument will display the output during the `psc-make` +command. For example `gulp --verbose`. diff --git a/index.js b/index.js index c7b9e22..405b0ce 100644 --- a/index.js +++ b/index.js @@ -1,66 +1,57 @@ 'use strict'; -var gutil = require('gulp-util') - , through = require('through2') - , lodash = require('lodash') - , cp = require('child_process') - , fs = require('fs') - , path = require('path') - , which = require('which') - , PLUGIN = 'gulp-purescript' - , DOTPSCI = '.psci' - , LOADM = ':m' - , CWD = process.cwd() - , OPTIONS = { - psc: { - cmd: 'psc', - flags: { - noPrelude: '--no-prelude', - noOpts: '--no-opts', - noMagicDo: '--no-magic-do', - noTco: '--no-tco', - main: '--main', - verboseErrors: '--verbose-errors' - } - , single: {browserNamespace: '--browser-namespace', externs: '--externs', main: '--main', output: '--output'} - , multi: {modules: '--module', codegen: '--codegen'} - }, - pscMake: { - cmd: 'psc-make', - flags: { - noPrelude: '--no-prelude', - noOpts: '--no-opts', - noMagicDo: '--no-magic-do', - noTco: '--no-tco', - verboseErrors: '--verbose-errors' - } - , single: {browserNamespace: '--browser-namespace', output: '--output'} - , multi: {} - }, - pscDocs: { - cmd: 'psc-docs', - flags: { - hierarchy: '--hierarchy-images' - } - , single: {} - , multi: {} - } - } -; +var fs = require('fs'); + +var path = require('path'); + +var child_process = require('child_process'); + +var gutil = require('gulp-util'); + +var through2 = require('through2'); + +var lodash = require('lodash'); + +var which = require('which'); + +var minimist = require('minimist'); + +var logalot = require('logalot'); + +var multipipe = require('multipipe'); + +var options = require('./options'); + +var pluginName = 'gulp-purescript'; -function run(cmd, args, k) { +var psciFilename = '.psci'; + +var psciLoadCommand = ':m'; + +var argv = minimist(process.argv.slice(2)); + +var isVerbose = argv.verbose; + +var cwd = process.cwd(); + +var PluginError = gutil.PluginError; + +var File = gutil.File; + +function runCommand(cmd, args, k) { var err = [ 'Failed to find ' + gutil.colors.magenta(cmd), 'in your path.' , 'Please ensure that ' + gutil.colors.magenta(cmd) - , 'is available on your system.' ].join(' ') - , that = this - ; + , 'is available on your system.' ].join(' '); + + var that = this; + which(cmd, function(e){ - if (e) that.emit('error', new gutil.PluginError(PLUGIN, err)); - else k(cp.spawn(cmd, args)); + if (e) that.emit('error', new PluginError(pluginName, err)); + else k(child_process.spawn(cmd, args)); }); } -function options(o, opts) { +function mkOptions(o, opts) { return Object.keys(opts || {}).reduce(function(b, a){ if (a in o.flags && opts[a] === true) return b.concat([o.flags[a]]); else if (a in o.single && typeof opts[a] === 'string') return b.concat([o.single[a] + '=' + opts[a]]); @@ -76,111 +67,131 @@ function options(o, opts) { }, []); } -function acc(f) { - var files = []; - return through.obj(function(file, env, cb){ - if (file.isNull()) { - this.push(file); - return cb(); - } - if (file.isStream()) { - this.emit('error', new gutil.PluginError(PLUGIN, 'Streaming not supported')); - return cb(); +function collectPaths() { + var paths = []; + + function transform(chunk, encoding, callback) { + if (chunk.isNull()) callback(null, chunk); + else if (chunk.isStream()) callback(new PluginError(pluginName, 'Streaming not supported')); + else { + paths.push(chunk.path); + callback(); } - files.push(file.path); - cb(); - }, function(cb){f.apply(this, [files, cb]);}); + } + + function flush(callback){ + this.push(paths); + callback(); + }; + + return through2.obj(transform, flush); } function psc(opts) { - var output = opts && opts.output ? opts.output : 'psc.js' - , opts$prime = lodash.omit(opts || {}, 'output') - ; + var output = opts && opts.output ? opts.output : 'psc.js'; + + var opts$prime = lodash.omit(opts || {}, 'output'); + // The `output` given there will be passed to gulp, not `psc` command. // If it was passed to `psc` command, the file will be created and gulp // won't receive any input stream from this function. - return acc(function(files, cb){ - var args = files.concat(options(OPTIONS.psc, opts$prime)) - , buffero = new Buffer(0) - , buffere = new Buffer(0) - , that = this - ; - run.apply(this, [OPTIONS.psc.cmd, args, function(cmd){ + function transform(chunk, encoding, callback) { + var args = chunk.concat(mkOptions(options.psc, opts$prime)); + + var buffero = new Buffer(0); + + var buffere = new Buffer(0); + + runCommand.apply(this, [options.psc.cmd, args, function(cmd){ cmd.stdout.on('data', function(stdout){buffero = Buffer.concat([buffero, new Buffer(stdout)]);}); + cmd.stderr.on('data', function(stderr){buffere = Buffer.concat([buffere, new Buffer(stderr)]);}); + cmd.on('close', function(code){ - if (!!code) that.emit('error', new gutil.PluginError(PLUGIN, buffere.toString())); + if (code !== 0) callback(new PluginError(pluginName, buffere.toString())); else { - that.push(new gutil.File({ + callback(null, new File({ path: output, contents: buffero })); } - cb(); }); }]); - }); + } + + return multipipe(collectPaths(), through2.obj(transform)); } function pscMake(opts) { - return acc(function(files, cb){ - var args = options(OPTIONS.pscMake, opts).concat(files) - , that = this - ; - run.apply(this, [OPTIONS.pscMake.cmd, args, function(cmd){ - cmd.stdout.on('data', function(stdout){ - gutil.log('Stdout from \'' + gutil.colors.cyan(OPTIONS.pscMake.cmd) + '\'\n' + gutil.colors.magenta(stdout)); - }); - cmd.stderr.on('data', function(stderr){ - gutil.log('Stderr from \'' + gutil.colors.cyan(OPTIONS.pscMake.cmd) + '\'\n' + gutil.colors.magenta(stderr)); - }); + function transform(chunk, encoding, callback) { + var args = mkOptions(options.pscMake, opts).concat(chunk); + + var buffero = new Buffer(0); + + var buffere = new Buffer(0); + + runCommand.apply(this, [options.pscMake.cmd, args, function(cmd){ + cmd.stdout.on('data', function(stdout){buffero = Buffer.concat([buffero, new Buffer(stdout)]);}); + + cmd.stderr.on('data', function(stderr){buffere = Buffer.concat([buffere, new Buffer(stderr)]);}); + cmd.on('close', function(code){ - if (!!code) that.emit('error', new gutil.PluginError(PLUGIN, OPTIONS.pscMake.cmd + ' has failed')); - cb(); + var message = + function() { return [ gutil.colors.cyan(options.pscMake.cmd) + , buffero.toString() + , buffere.toString() ].join('\n') }; + + if (code !== 0) callback(new PluginError(pluginName, message())); + else { + if (isVerbose) logalot.info(message()); + callback(); + } }); }]); - }); + }; + + return multipipe(collectPaths(), through2.obj(transform)); } function pscDocs(opts) { - return acc(function(files, cb){ - var args = options(OPTIONS.pscDocs, opts).concat(files) - , buffero = new Buffer(0) - , buffere = new Buffer(0) - , that = this - ; - run.apply(this, [OPTIONS.pscDocs.cmd, args, function(cmd){ + function transform(chunk, encoding, callback) { + var args = mkOptions(options.pscDocs, opts).concat(chunk); + + var buffero = new Buffer(0); + + var buffere = new Buffer(0); + + runCommand.apply(this, [options.pscDocs.cmd, args, function(cmd){ cmd.stdout.on('data', function(stdout){buffero = Buffer.concat([buffero, new Buffer(stdout)]);}); + cmd.stderr.on('data', function(stderr){buffere = Buffer.concat([buffere, new Buffer(stderr)]);}); + cmd.on('close', function(code){ - if (!!code) that.emit('error', new gutil.PluginError(PLUGIN, buffere.toString())); + if (code !== 0) callback(new PluginError(pluginName, buffere.toString())); else { - that.push(new gutil.File({ + callback(null, new File({ path: '.', contents: buffero })); } - cb(); }); }]); - }); + } + + return multipipe(collectPaths(), through2.obj(transform)); } function dotPsci(opts) { - var stream = through.obj(function(file, env, cb){ - if (file.isNull()) { - this.push(file); - return cb(); + function transform(chunk, encoding, callback) { + if (chunk.isNull()) callback(null, chunk); + else if (chunk.isStream()) callback(new PluginError(pluginName, 'Streaming not supported')); + else { + var buffer = new Buffer(psciLoadCommand + ' ' + path.relative(cwd, chunk.path) + '\n'); + callback(null, buffer); } - if (file.isStream()) { - this.emit('error', new gutil.PluginError(PLUGIN, 'Streaming not supported')); - return cb(); - } - this.push(new Buffer(LOADM + ' ' + path.relative(CWD, file.path) + '\n')); - cb(); - }); - stream.pipe(fs.createWriteStream(DOTPSCI)); - return stream; + } + + return multipipe(through2.obj(transform), fs.createWriteStream(psciFilename)); } module.exports = { diff --git a/options.js b/options.js new file mode 100644 index 0000000..0a6fa67 --- /dev/null +++ b/options.js @@ -0,0 +1,39 @@ +'use strict'; + +var options = { + psc: { + cmd: 'psc', + flags: { + noPrelude: '--no-prelude', + noOpts: '--no-opts', + noMagicDo: '--no-magic-do', + noTco: '--no-tco', + main: '--main', + verboseErrors: '--verbose-errors' + } + , single: {browserNamespace: '--browser-namespace', externs: '--externs', main: '--main', output: '--output'} + , multi: {modules: '--module', codegen: '--codegen'} + }, + pscMake: { + cmd: 'psc-make', + flags: { + noPrelude: '--no-prelude', + noOpts: '--no-opts', + noMagicDo: '--no-magic-do', + noTco: '--no-tco', + verboseErrors: '--verbose-errors' + } + , single: {browserNamespace: '--browser-namespace', output: '--output'} + , multi: {} + }, + pscDocs: { + cmd: 'psc-docs', + flags: { + hierarchy: '--hierarchy-images' + } + , single: {} + , multi: {} + } +}; + +module.exports = options; diff --git a/package.json b/package.json index 6ad4cf6..704f164 100644 --- a/package.json +++ b/package.json @@ -8,27 +8,33 @@ "name": "Eric", "email": "thul.eric@gmail.com" }, - "engineStrict": true, "engines": { - "node": ">=0.10" + "node": ">=0.10.0" }, "files": [ - "index.js" + "index.js", + "options.js" ], "scripts": { - "test": "mocha" + "test": "node test.js | tap-spec" }, "keywords": [ "gulpplugin", "purescript" ], "dependencies": { - "gulp-util": "^2.2.14", - "lodash": "^2.4.1", - "through2": "^0.4.1", - "which": "^1.0.5" + "gulp-util": "^3.0.4", + "lodash": "^3.5.0", + "logalot": "^2.1.0", + "minimist": "^1.1.1", + "multipipe": "^0.1.2", + "through2": "^0.6.3", + "which": "^1.0.9" }, "devDependencies": { - "mocha": "*" + "gulp": "^3.8.11", + "rewire": "^2.3.1", + "tap-spec": "^2.2.2", + "tape": "^3.5.0" } } diff --git a/test.js b/test.js index e6467a2..3826a89 100644 --- a/test.js +++ b/test.js @@ -1,112 +1,113 @@ 'use strict'; -var gutil = require('gulp-util') - , assert = require('assert') - , fs = require('fs') - , purescript = require('./') -; - -it('should compile purescript', function(cb){ - var stream = purescript.psc({noPrelude: true}) - , fixture = 'Fixture1.purs' - ; - - stream.on('data', function(file){ - assert(/Fixture/.test(file.contents.toString())); - assert.equal('psc.js', file.path); - cb(); - }); +var fs = require('fs'); - fs.readFile(fixture, function(e, buffer){ - if (e) cb(assert(false)); - else { - stream.write(new gutil.File({ - cwd: __dirname, - base: __dirname, - path: __dirname + '/' + fixture, - contents: buffer, - stat: {mtime: new Date()} - })); - stream.end(); - } - }); +var test = require('tape'); + +var gulp = require('gulp'); + +var through2 = require('through2'); + +var rewire = require('rewire'); + +var purescript = require('./'); + +test('psc - basic', function(t){ + t.plan(2); + + var stream = purescript.psc({noPrelude: true}); + + var fixture = 'Fixture1.purs'; + + gulp.src(fixture).pipe(stream). + pipe(through2.obj(function(chunk, encoding, callback){ + t.ok(/Fixture/.test(chunk.contents.toString()), 'should have a compiled result'); + t.equal('psc.js', chunk.path); + callback(); + })); }); -it('should compile purescript to specified output, without creating file', function(cb){ - var fixture = 'Fixture1.purs' - , output = 'output.js' - , stream = purescript.psc({noPrelude: true, output: output}) - ; +test('psc - output option', function(t){ + t.plan(2); - stream.on('data', function(file){ - assert(!fs.existsSync(__dirname + "/" + output)); - assert.equal(output, file.path); - cb(); - }); + var fixture = 'Fixture1.purs'; - fs.readFile(fixture, function(e, buffer){ - if (e) cb(assert(false)); - else { - stream.write(new gutil.File({ - cwd: __dirname, - base: __dirname, - path: __dirname + '/' + fixture, - contents: buffer, - stat: {mtime: new Date()} - })); - stream.end(); - } - }); + var output = 'output.js'; + + var stream = purescript.psc({noPrelude: true, output: output}); + + gulp.src(fixture).pipe(stream). + pipe(through2.obj(function(chunk, encoding, callback){ + t.ok(!fs.existsSync(__dirname + '/' + output), 'output file should not exist'); + t.equal(output, chunk.path); + callback(); + })); }); -it('should fail to compile with an error message', function(cb){ - var stream = purescript.psc({noPrelude: true}) - , fixture = 'Fixture2.purs' - ; +test('psc - failure', function(t){ + t.plan(2); + + var stream = purescript.psc({noPrelude: true}); + + var fixture = 'Fixture2.purs'; - stream.on('error', function(e){ - assert("Error" === e.name); - assert(/expecting "where"/.test(e.message)); - cb(); + gulp.src(fixture).pipe(stream). + on('error', function(e){ + t.ok(/"where"/.test(e.message), 'should have a failure message'); + t.equal('Error', e.name); }); +}); + + +test('psci - basic', function(t){ + t.plan(1); + + var stream = purescript.dotPsci(); + + var fixture = 'Fixture1.purs'; + + var output = ':m ' + fixture + '\n'; + + gulp.src(fixture).pipe(stream). + pipe(through2.obj(function(chunk, encoding, callback){ + t.equal(output, chunk.toString()); + callback(); + })); +}); + +test('psc-make - basic', function(t){ + t.plan(1); - fs.readFile(fixture, function(e, buffer){ - if (e) cb(assert(false)); - else { - stream.write(new gutil.File({ - cwd: __dirname, - base: __dirname, - path: __dirname + '/' + fixture, - contents: buffer, - stat: {mtime: new Date()} - })); - stream.end(); + var purescript = rewire('./'); + + var mock = { + success: function(){ + t.fail('Should not get a log message'); } + }; + + purescript.__set__('logalot', mock); + + var stream = purescript.pscMake({noPrelude: true}); + + var fixture = 'Fixture1.purs'; + + gulp.src(fixture).pipe(stream). + on('finish', function(){ + t.pass('should output a compiled result'); }); }); -it('should write a .psci file', function(cb){ - var stream = purescript.dotPsci() - , fixture = 'Fixture1.purs' - , output = ':m ' + fixture + '\n' - ; +test('psc-make - error', function(t){ + t.plan(2); - stream.on('data', function(line){ - assert.equal(output, line.toString()); - cb(); - }); + var stream = purescript.pscMake({noPrelude: true}); - fs.readFile(fixture, function(e, buffer){ - if (e) cb(assert(false)); - else { - stream.write(new gutil.File({ - cwd: __dirname, - base: __dirname, - path: __dirname + '/' + fixture, - contents: buffer, - stat: {mtime: new Date()} - })); - stream.end(); - } + var fixture = 'Fixture2.purs'; + + gulp.src(fixture).pipe(stream). + on('error', function(e){ + t.ok(/"where"/.test(e.message), 'should have a failure message'); + t.equal('Error', e.name); }); });