diff --git a/lib/modules/file/write.js b/lib/modules/file/write.js index f9a3c89e..c48e9ece 100644 --- a/lib/modules/file/write.js +++ b/lib/modules/file/write.js @@ -1,46 +1,56 @@ "use strict"; -var _write, - __slice = [].slice; - -_write = function(config, options, next) { - var done, fileUtils, hasFiles, i, _ref; - fileUtils = require('../../util/file'); - hasFiles = ((_ref = options.files) != null ? _ref.length : void 0) > 0; - if (!hasFiles) { - return next(); - } - i = 0; - done = function() { - if (++i === options.files.length) { - return next(); - } - }; - return options.files.forEach(function(file) { - if ((file.outputFileText !== "" && !file.outputFileText) || !file.outputFileName) { - return done(); - } - if (file.outputFileText === "") { - config.log.warn("Compile of file [[ " + file.inputFileName + " ]] resulted in empty output."); - } - if (config.log.isDebug()) { - config.log.debug("Writing file [[ " + file.outputFileName + " ]]"); - } - return fileUtils.writeFile(file.outputFileName, file.outputFileText, function(err) { - if (err != null) { - config.log.error("Failed to write new file [[ " + file.outputFileName + " ]], Error: " + err, { - exitIfBuild: true - }); - } else { - config.log.success("Wrote file [[ " + file.outputFileName + " ]]", options); + +var _write = function( config, options, next ) { + if ( options.files && options.files.length ) { + + var processed = 0; + var done = function() { + if ( ++processed === options.files.length ) { + next(); } - return done(); - }); - }); -}; + }; + + var fileUtils = require( "../../util/file" ); + for ( var i = 0; i < options.files.length; i++ ) { + var file = options.files[i]; -exports.registration = function(config, register) { - var e; - e = config.extensions; - register(['add', 'update', 'remove', 'buildExtension'], 'write', _write, __slice.call(e.template).concat(__slice.call(e.css))); - return register(['add', 'update', 'buildFile'], 'write', _write, __slice.call(e.javascript).concat(__slice.call(e.copy), __slice.call(e.misc))); + // If the outputText is null/undef or there is no name set then all done + if ( ( file.outputFileText !== "" && !file.outputFileText ) || !file.outputFileName ) { + return done(); + } + + // if the output text is empty, let user know + if ( file.outputFileText === "" ) { + config.log.warn( "File [[ " + file.inputFileName + " ]] is empty." ); + } + + fileUtils.writeFile( file.outputFileName, file.outputFileText, function( err ) { + if ( err ) { + config.log.error( "Failed to write new file [[ " + file.outputFileName + " ]], Error: " + err, {exitIfBuild:true}); + } else { + config.log.success( "Wrote file [[ " + file.outputFileName + " ]]", options ); + } + done(); + }); + } + } else { + next(); + } }; + +exports.registration = function( config, register ) { + var e = config.extensions; + register( + ["add", "update", "remove", "buildExtension"], + "write", + _write, + [].concat.apply( e.template, e.css ) + ); + + register( + ["add", "update", "buildFile"], + "write", + _write, + [].concat.apply( e.javascript, e.copy, e.misc ) + ); +}; \ No newline at end of file diff --git a/package.json b/package.json index 0418a0fe..bb4c512f 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "mimosa-jshint": "2.0.0", "mimosa-live-reload": "1.3.0", "mimosa-minify-css": "1.4.0", + "mimosa-minify-html": "^0.3.0", "mimosa-minify-js": "2.0.0", "mimosa-require": "2.3.3", "mimosa-server": "1.6.1", @@ -83,7 +84,13 @@ "engines": { "node": ">=0.10" }, + "devDependencies": { + "mocha": "1.9.0", + "chai": "1.5.0", + "sinon": "1.12.2" + }, "scripts": { + "test": "node_modules/.bin/mocha", "prepublish": "node build.js" } } diff --git a/src/modules/file/write.js b/src/modules/file/write.js new file mode 100644 index 00000000..c48e9ece --- /dev/null +++ b/src/modules/file/write.js @@ -0,0 +1,56 @@ +"use strict"; + +var _write = function( config, options, next ) { + if ( options.files && options.files.length ) { + + var processed = 0; + var done = function() { + if ( ++processed === options.files.length ) { + next(); + } + }; + + var fileUtils = require( "../../util/file" ); + for ( var i = 0; i < options.files.length; i++ ) { + var file = options.files[i]; + + // If the outputText is null/undef or there is no name set then all done + if ( ( file.outputFileText !== "" && !file.outputFileText ) || !file.outputFileName ) { + return done(); + } + + // if the output text is empty, let user know + if ( file.outputFileText === "" ) { + config.log.warn( "File [[ " + file.inputFileName + " ]] is empty." ); + } + + fileUtils.writeFile( file.outputFileName, file.outputFileText, function( err ) { + if ( err ) { + config.log.error( "Failed to write new file [[ " + file.outputFileName + " ]], Error: " + err, {exitIfBuild:true}); + } else { + config.log.success( "Wrote file [[ " + file.outputFileName + " ]]", options ); + } + done(); + }); + } + } else { + next(); + } +}; + +exports.registration = function( config, register ) { + var e = config.extensions; + register( + ["add", "update", "remove", "buildExtension"], + "write", + _write, + [].concat.apply( e.template, e.css ) + ); + + register( + ["add", "update", "buildFile"], + "write", + _write, + [].concat.apply( e.javascript, e.copy, e.misc ) + ); +}; \ No newline at end of file diff --git a/test/common.js b/test/common.js new file mode 100644 index 00000000..22ff0c6b --- /dev/null +++ b/test/common.js @@ -0,0 +1 @@ +global.expect = require('chai').expect; \ No newline at end of file diff --git a/test/tmp/03f9d3fixture_outtest1.js b/test/tmp/03f9d3fixture_outtest1.js new file mode 100644 index 00000000..a77c0b37 --- /dev/null +++ b/test/tmp/03f9d3fixture_outtest1.js @@ -0,0 +1 @@ +c973a97e3048dd9a27eaaf9cecdfa24ec592c5bb0c2aa68d3aa14c6038c2de2565b6be247e195a8b3c6041d956e6dea91e8fb8afcd4077d1f34c91658c2619df \ No newline at end of file diff --git a/test/tmp/14a714fixture_outtest1.js b/test/tmp/14a714fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/18fdf0fixture_outtest1.js b/test/tmp/18fdf0fixture_outtest1.js new file mode 100644 index 00000000..111e0fa2 --- /dev/null +++ b/test/tmp/18fdf0fixture_outtest1.js @@ -0,0 +1 @@ +dffc9f8fd158dcc31045a72821a45bbc698803e7004587a4626f7212382af6fd216ec404ca3d468a933234610fdc710bcd85252ddb37af0f7f35d549be4a4670 \ No newline at end of file diff --git a/test/tmp/21e9a8fixture_outtest1.js b/test/tmp/21e9a8fixture_outtest1.js new file mode 100644 index 00000000..146ee040 --- /dev/null +++ b/test/tmp/21e9a8fixture_outtest1.js @@ -0,0 +1 @@ +76ab01bba7c334b1a42a18d42f55a5d36f4c7f96ed34a6e720ff56bee2876080989a3f699b14f212c494604a1ce11a4cf308c5f491e319f3abd0a257c835433c \ No newline at end of file diff --git a/test/tmp/21fda6fixture_outtest1.js b/test/tmp/21fda6fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/23516dfixture_outtest1.js b/test/tmp/23516dfixture_outtest1.js new file mode 100644 index 00000000..a4bb7771 --- /dev/null +++ b/test/tmp/23516dfixture_outtest1.js @@ -0,0 +1 @@ +4a87c53fe522e33d08febe3eb2b67c68a28ee4addefe6a29940521e3a840466c919adb199c3285d5d1a8d02f94aea1f7e7eb02e42b6d487cc69e67168bbbc10c \ No newline at end of file diff --git a/test/tmp/276715fixture_outtest1.js b/test/tmp/276715fixture_outtest1.js new file mode 100644 index 00000000..59d2faaa --- /dev/null +++ b/test/tmp/276715fixture_outtest1.js @@ -0,0 +1 @@ +2dfb35edd87d926910ff6b07a785bda8c4134e91173f5e9d9732ab3484e70d920e22af5d0d6d2ef1c084f523ca53bc41461b476b5ff1d0f4c906d6ad1afd3c86 \ No newline at end of file diff --git a/test/tmp/2b5b53fixture_outtest1.js b/test/tmp/2b5b53fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/377352fixture_outtest1.js b/test/tmp/377352fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/38797dfixture_outtest1.js b/test/tmp/38797dfixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/4a6722fixture_outtest1.js b/test/tmp/4a6722fixture_outtest1.js new file mode 100644 index 00000000..84feb55e --- /dev/null +++ b/test/tmp/4a6722fixture_outtest1.js @@ -0,0 +1 @@ +e2f27ca1b583872a42f9e07ae6e232d3fe524f5b4fd2f975ccfb6df910f0c1b1fe7338741e1c5e937a38157305f633cce8567537972c0307d20cbf846354c15d \ No newline at end of file diff --git a/test/tmp/549311fixture_outtest1.js b/test/tmp/549311fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/5823cafixture_outtest1.js b/test/tmp/5823cafixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/5d477bfixture_outtest1.js b/test/tmp/5d477bfixture_outtest1.js new file mode 100644 index 00000000..69a18029 --- /dev/null +++ b/test/tmp/5d477bfixture_outtest1.js @@ -0,0 +1 @@ +ef63636e5d157c2d09740e916900a7d5010b7cfae699bf0bb19d2598565cee7fa34849a90adf2be612902fc29b53c28decfd9cc1d3ad0498aeb4420c1ebbdd77 \ No newline at end of file diff --git a/test/tmp/64c62efixture_outtest1.js b/test/tmp/64c62efixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/67bb35fixture_outtest1.js b/test/tmp/67bb35fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/6a6585fixture_outtest1.js b/test/tmp/6a6585fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/736c10fixture_outtest1.js b/test/tmp/736c10fixture_outtest1.js new file mode 100644 index 00000000..b209e828 --- /dev/null +++ b/test/tmp/736c10fixture_outtest1.js @@ -0,0 +1 @@ +0aa28e863ba5065ed161bd3b126f3dc1c0d114546e4189abe7afbd1f19bbce9259c46a5e5433aa7a02f9ae87923a1cd210ef8d54c9ae009fb61cb23e435bbda6 \ No newline at end of file diff --git a/test/tmp/90c7e5fixture_outtest1.js b/test/tmp/90c7e5fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/9cc23efixture_outtest1.js b/test/tmp/9cc23efixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/a23815fixture_outtest1.js b/test/tmp/a23815fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/b8544cfixture_outtest1.js b/test/tmp/b8544cfixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/b97f25fixture_outtest1.js b/test/tmp/b97f25fixture_outtest1.js new file mode 100644 index 00000000..96265f54 --- /dev/null +++ b/test/tmp/b97f25fixture_outtest1.js @@ -0,0 +1 @@ +f83e4d97d1d81fc8cd705463012e04ae9e1600664cf1f35540883744ed1960a2a90edb5dc75e2098fa5d05ca591dd32c7a65e572d37a60ff8b4d011f039fa576 \ No newline at end of file diff --git a/test/tmp/c288fffixture_outtest1.js b/test/tmp/c288fffixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/cdfba9fixture_outtest1.js b/test/tmp/cdfba9fixture_outtest1.js new file mode 100644 index 00000000..831ff655 --- /dev/null +++ b/test/tmp/cdfba9fixture_outtest1.js @@ -0,0 +1 @@ +b1a1b38a1fda475704b7e519d59c747e046b67e034f0bfad69d85f2ec16db2fb9c694ba5ab37cf563ce8c29b6c88ba48c01a9a2153b72c01771aa5df8e7d5061 \ No newline at end of file diff --git a/test/tmp/d93a06fixture_outtest1.js b/test/tmp/d93a06fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/def22ffixture_outtest1.js b/test/tmp/def22ffixture_outtest1.js new file mode 100644 index 00000000..0c194105 --- /dev/null +++ b/test/tmp/def22ffixture_outtest1.js @@ -0,0 +1 @@ +6c10c9535dad7e91690b679be38de0710ce92eb7b1d536c8d09aabe6d917842f7c07e0e15c04993e00b9c931b4b3c5b87a8de2b2c6cbcd8fb7e7e453aa9c8dac \ No newline at end of file diff --git a/test/tmp/e77262fixture_outtest1.js b/test/tmp/e77262fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/fc3d0efixture_outtest1.js b/test/tmp/fc3d0efixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/tmp/fd7059fixture_outtest1.js b/test/tmp/fd7059fixture_outtest1.js new file mode 100644 index 00000000..e69de29b diff --git a/test/unit-test.js b/test/unit-test.js new file mode 100644 index 00000000..cbc30c5e --- /dev/null +++ b/test/unit-test.js @@ -0,0 +1 @@ +require("./units/modules/file/write-test") \ No newline at end of file diff --git a/test/units/modules/file/write-test.js b/test/units/modules/file/write-test.js new file mode 100644 index 00000000..7ee6fe37 --- /dev/null +++ b/test/units/modules/file/write-test.js @@ -0,0 +1,242 @@ +var fs = require( "fs" ) + , path = require( "path" ) + , sinon = require( "sinon" ) + , utils = require( path.join(process.cwd(), "test", "utils") ) + , writeModule = require( path.join(process.cwd(), "lib", "modules", "file", "write") ) + , fileUtils = require( path.join(process.cwd(), "lib", "util", "file" ) ) + , fakeMimosaConfig = utils.fakeMimosaConfig(); + ; + +describe( "Mimosa file writing workflow module", function(){ + + var workflows, step, writeFunction, extensions; + + before( function( done ) { + writeModule.registration( fakeMimosaConfig, function( _workflows, _step , _writeFunction, _extensions ) { + workflows = _workflows; + step = _step; + writeFunction = _writeFunction; + extensions = _extensions; + done(); + }); + }); + + it( "will call register function properly", function() { + expect( workflows ).to.be.instanceof( Array ); + expect( step ).to.be.a( "string" ); + expect( writeFunction ).to.be.instanceof( Function ) + expect( extensions ).to.be.instanceof( Array ); + }); + + describe( "when invoked with no files", function() { + + var spy; + + before(function() { + spy = sinon.spy(); + }); + + afterEach(function() { + spy.reset(); + }); + + it( "will invoke the lifecycle callback when no files array", function(){ + writeFunction( fakeMimosaConfig, { files: null }, spy ); + expect( spy.calledOnce ).to.be.true; + }); + + it( "will invoke the lifecycle callback when empty files array", function(){ + writeFunction( fakeMimosaConfig, { files: [] }, spy ); + expect( spy.calledOnce ).to.be.true; + }); + + it( "will not attempt to write any files", function() { + var writeFileSpy = sinon.spy( fileUtils, "writeFile" ); + writeFunction( fakeMimosaConfig, { files: [] }, function(){} ); + expect( writeFileSpy.called ).to.be.false; + fileUtils.writeFile.restore(); + }); + + }); + + describe( "when invoked with one file", function() { + var callbackSpy; + + before(function() { + callbackSpy = sinon.spy(); + }); + + afterEach(function() { + callbackSpy.reset(); + }); + + it( "with blank output will warn the user, write the file and execute the lifecycle callback", function() { + var warnStub = sinon.stub( fakeMimosaConfig.log, "warn" ); + var writeFileStub = sinon.stub( fileUtils, "writeFile" ); + var options = { files: [utils.fileFixture()] }; + options.files[0].outputFileText = ""; + writeFunction( fakeMimosaConfig, options, callbackSpy ); + expect( warnStub.calledOnce ).to.be.true; + var calledWithCorrect = warnStub.calledWith( "File [[ " + options.files[0].inputFileName + " ]] is empty." ); + expect( calledWithCorrect ).to.be.true; + fileUtils.writeFile.restore(); + fakeMimosaConfig.log.warn.restore(); + }); + + it( "lacking an output name will not write file and will call lifecycle callback", function() { + var writeFileSpy = sinon.spy( fileUtils, "writeFile" ); + var options = { files: [utils.fileFixture()] }; + options.files[0].outputFileName = null; + writeFunction( fakeMimosaConfig, options, callbackSpy ); + expect( callbackSpy.calledOnce ).to.be.true; + expect( writeFileSpy.called ).to.be.false; + fileUtils.writeFile.restore(); + }); + + it( "will attempt to write one file, log success, and lifecycle callback", function(){ + var successStub = sinon.stub( fakeMimosaConfig.log, "success" ); + var options = { files: [utils.fileFixture()] }; + var writeFileStub = sinon.stub( + fileUtils, + "writeFile", + function( outputFileName, outputFileText, writeCallback ) { + expect( outputFileName ).to.equal( options.files[0].outputFileName ); + expect( outputFileText ).to.equal( options.files[0].outputFileText ); + writeCallback(); + } + ); + + writeFunction( fakeMimosaConfig, options, callbackSpy ); + + // will attempt to write file once + expect( writeFileStub.calledOnce ).to.be.true; + + // will call lifecycle callback once + expect( callbackSpy.calledOnce ).to.be.true; + + // will log success for write of file + expect( successStub.calledOnce ).to.be.true; + var calledWithCorrect = successStub.calledWith( "Wrote file [[ " + options.files[0].outputFileName + " ]]" ); + expect( calledWithCorrect ).to.be.true; + + fileUtils.writeFile.restore(); + fakeMimosaConfig.log.success.restore(); + }); + + it( "and an error occurs during write, will log error and call lifecycle callback", function(){ + var errorText = "blah blah blah"; + var errorStub = sinon.stub( fakeMimosaConfig.log, "error" ); + var options = { files: [utils.fileFixture()] }; + var writeFileStub = sinon.stub( + fileUtils, + "writeFile", + function( outputFileName, outputFileText, writeCallback ) { + expect( outputFileName ).to.equal( options.files[0].outputFileName ); + expect( outputFileText ).to.equal( options.files[0].outputFileText ); + writeCallback(errorText); + } + ); + + writeFunction( fakeMimosaConfig, options, callbackSpy ); + + // will attempt to write file once + expect( writeFileStub.calledOnce ).to.be.true; + + // will call lifecycle callback once + expect( callbackSpy.calledOnce ).to.be.true; + + // will log error with message for failed write of file + expect( errorStub.calledOnce ).to.be.true; + var calledWithCorrect = errorStub.calledWith( "Failed to write new file [[ " + options.files[0].outputFileName + " ]], Error: " + errorText ); + expect( calledWithCorrect ).to.be.true; + + fileUtils.writeFile.restore(); + fakeMimosaConfig.log.error.restore(); + }); + }); + + describe( "when invoked with two files", function() { + var callbackSpy; + + before(function() { + callbackSpy = sinon.spy(); + }); + + afterEach(function() { + callbackSpy.reset(); + }); + + it( "will attempt to write one file, log success, and lifecycle callback", function(){ + var successStub = sinon.stub( fakeMimosaConfig.log, "success" ); + var options = { files: [utils.fileFixture(), utils.fileFixture()] }; + var i = 0; + var writeFileStub = sinon.stub( + fileUtils, + "writeFile", + function( outputFileName, outputFileText, writeCallback ) { + expect( outputFileName ).to.equal( options.files[i].outputFileName ); + expect( outputFileText ).to.equal( options.files[i++].outputFileText ); + writeCallback(); + } + ); + + writeFunction( fakeMimosaConfig, options, callbackSpy ); + + // will attempt to write file once + expect( writeFileStub.calledTwice ).to.be.true; + + // will call lifecycle callback once + expect( callbackSpy.calledOnce ).to.be.true; + + // will log success for write of file + expect( successStub.calledTwice ).to.be.true; + var successCall1 = successStub.getCall(0); + var successCall2 = successStub.getCall(1); + + var calledWithCorrect = successCall1.calledWith( "Wrote file [[ " + options.files[0].outputFileName + " ]]" ); + expect( calledWithCorrect ).to.be.true; + + calledWithCorrect = successCall2.calledWith( "Wrote file [[ " + options.files[1].outputFileName + " ]]" ); + expect( calledWithCorrect ).to.be.true; + + fileUtils.writeFile.restore(); + fakeMimosaConfig.log.success.restore(); + }); + + it( "and an error occurs during write, will log error and call lifecycle callback", function(){ + var errorText = "blah blah blah"; + var errorStub = sinon.stub( fakeMimosaConfig.log, "error" ); + var options = { files: [utils.fileFixture(), utils.fileFixture()] }; + var i = 0; + var writeFileStub = sinon.stub( + fileUtils, + "writeFile", + function( outputFileName, outputFileText, writeCallback ) { + expect( outputFileName ).to.equal( options.files[i].outputFileName ); + expect( outputFileText ).to.equal( options.files[i].outputFileText ); + if (i++ === 1) { + writeCallback( errorText ); + } else { + writeCallback(); + } + } + ); + + writeFunction( fakeMimosaConfig, options, callbackSpy ); + + // will attempt to write file once + expect( writeFileStub.calledTwice ).to.be.true; + + // will call lifecycle callback once + expect( callbackSpy.calledOnce ).to.be.true; + + // will log error with message for failed write of file + expect( errorStub.calledOnce ).to.be.true; + var calledWithCorrect = errorStub.calledWith( "Failed to write new file [[ " + options.files[1].outputFileName + " ]], Error: " + errorText ); + expect( calledWithCorrect ).to.be.true; + + fileUtils.writeFile.restore(); + fakeMimosaConfig.log.error.restore(); + }); + }); +}); \ No newline at end of file diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 00000000..aca17d3b --- /dev/null +++ b/test/utils.js @@ -0,0 +1,42 @@ +var crypto = require( "crypto" ) + , path = require( "path" ) + , _ = require( 'lodash' ) + , fakeMimosaConfigObj = { + extensions: { + javascript:["js", "coffee"], + css:["less"], + template:["hog", "hogan"], + copy:["html", "htm"], + misc:["foo"] + }, + log: { + success: function(msg, opts){}, + warn: function(msg, opts){}, + error: function(msg, opts){} + } + } + ; + +var randomString = function( num ) { + return crypto.randomBytes(num || 64).toString('hex'); +}; + +var fileFixture = function() { + var fixture = { + inputFileName: path.join( __dirname, "tmp", randomString(3) + ".js" ), + inputFileText: randomString(), + outputFileName: path.join( __dirname, "tmp", randomString(3) + "fixture_outtest1.js" ), + outputFileText: randomString() + }; + + return fixture; +}; + +var fakeMimosaConfig = function() { + return _.cloneDeep(fakeMimosaConfigObj); +}; + +module.exports = { + fileFixture: fileFixture, + fakeMimosaConfig: fakeMimosaConfig +}; \ No newline at end of file