diff --git a/package.json b/package.json index 34da534..e294035 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "enb": ">= 0.8.22" }, "devDependencies": { + "chai": "3.2.0", + "chai-as-promised": "5.1.0", "enb": ">= 0.8.22", "istanbul": "0.3.14", "jscs": "1.13.1", @@ -36,7 +38,7 @@ "mocha": "2.2.4", "mock-enb": "0.0.2", "mock-fs": "3.0.0", - "must": "0.12.0" + "rimraf": "2.4.2" }, "scripts": { "test": "npm run lint && npm run unit", @@ -44,5 +46,10 @@ "unit": "mocha -R spec", "cover": "istanbul cover _mocha", "coveralls": "npm i coveralls && npm run cover -- --report lcovonly && cat ./coverage/lcov.info | coveralls" + }, + "dependencies": { + "browserify": "10.2.6", + "vow": "0.4.10", + "vow-node": "0.3.0" } } diff --git a/techs/node-js.js b/techs/node-js.js index 4c0a236..cbdb532 100644 --- a/techs/node-js.js +++ b/techs/node-js.js @@ -16,12 +16,16 @@ * nodeConfig.addTech(require('enb-diverse-js/techs/node-js')); * ``` */ -var EOL = require('os').EOL; +var EOL = require('os').EOL, + browserify = require('browserify'), + promisify = require('vow-node').promisify; + module.exports = require('enb/lib/build-flow').create() .name('node-js') .target('target', '?.node.js') .useFileList(['vanilla.js', 'node.js']) .defineOption('devMode', true) + .defineOption('bundled', false) .builder(function (sourceFiles) { var node = this.node, dropRequireCacheFunc = [ @@ -44,6 +48,24 @@ module.exports = require('enb/lib/build-flow').create() ].join(EOL), devMode = this._devMode || ''; + if (this._bundled) { + var files = sourceFiles.map(function (file) { + return node.relativePath(file.fullname); + }), + browserifyOptions = { + basedir: node.getDir(), + builtins: [], + bundleExternal: false + }, + renderer = browserify(files, browserifyOptions), + bundle = promisify(renderer.bundle.bind(renderer)); + + return bundle() + .then(function (buf) { + return buf.toString('utf-8'); + }); + } + return [ devMode && dropRequireCacheFunc, sourceFiles.map(function (file) { diff --git a/test/mocha.opts b/test/mocha.opts index b2e017d..8589224 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,4 +1,3 @@ test/**/*.test.js --ui bdd --reporter dot ---require must diff --git a/test/techs/browser-js.test.js b/test/techs/browser-js.test.js index d158d86..4824571 100644 --- a/test/techs/browser-js.test.js +++ b/test/techs/browser-js.test.js @@ -1,3 +1,7 @@ +require('chai') + .use(require('chai-as-promised')) + .should(); + var EOL = require('os').EOL, mock = require('mock-fs'), FileList = require('enb/lib/file-list'), @@ -26,7 +30,7 @@ describe('browser-js', function () { return build(blocks) .then(function (content) { - content[0].must.be(reference); + content[0].should.be.equal(reference); }); }); }); @@ -45,14 +49,14 @@ describe('browser-js', function () { it('code must executed in isolation', function () { return build(blocks, { iife: true }, true) .then(function () { - globals[0].must.be(2); + globals[0].should.be.equal(2); }); }); it('code must be executed in the same scoupe', function () { return build(blocks, null, true) .then(function () { - globals[0].must.be(1); + globals[0].should.be.equal(1); }); }); }); diff --git a/test/techs/node-js.test.js b/test/techs/node-js.test.js index 19b8a44..64ef512 100644 --- a/test/techs/node-js.test.js +++ b/test/techs/node-js.test.js @@ -1,10 +1,15 @@ +require('chai') + .use(require('chai-as-promised')) + .should(); + var fs = require('fs'), path = require('path'), mock = require('mock-fs'), FileList = require('enb/lib/file-list'), dropRequireCache = require('enb/lib/fs/drop-require-cache'), MockNode = require('mock-enb/lib/mock-node'), - NodeJsTech = require('../../techs/node-js'); + NodeJsTech = require('../../techs/node-js'), + rimraf = require('rimraf'); describe('node-js', function () { var globals; @@ -18,30 +23,43 @@ describe('node-js', function () { }); describe('join files', function () { - var blocks = { - 'block.vanilla.js': 'global.REQUIRED_TECHS.push("vanilla-js");', - 'block.node.js': 'global.REQUIRED_TECHS.push("node-js");' - }; + var scheme = { + blocks: { + 'block.vanilla.js': 'global.REQUIRED_TECHS.push("vanilla-js");', + 'block.node.js': 'global.REQUIRED_TECHS.push("node-js");' + } + }, + targetPath = path.resolve('./bundle/bundle.node.js'); + + afterEach(function () { + dropRequireCache(require, targetPath); + }); it('must join all files', function () { - return build(blocks) + return build(scheme) .then(function () { - globals.must.include('vanilla-js'); - globals.must.include('node-js'); + require(targetPath); + + globals.should.include('vanilla-js'); + globals.should.include('node-js'); }); }); it('must join `vanilla.js` file after `node.js` file', function () { - return build(blocks) + return build(scheme) .then(function () { - globals.indexOf('node-js').must.be.above(globals.indexOf('vanilla-js')); + require(targetPath); + + globals.indexOf('node-js').should.be.above(globals.indexOf('vanilla-js')); }); }); }); describe('devMode', function () { - var blocks = { - 'block.node.js': 'global.REQUIRED_TECHS.push("node-js");' + var scheme = { + blocks: { + 'block.node.js': 'global.REQUIRED_TECHS.push("node-js");' + } }, blockPath = path.resolve('./blocks/block.node.js'), targetPath = path.resolve('./bundle/bundle.node.js'); @@ -52,37 +70,142 @@ describe('node-js', function () { }); it('must drop require cache by default', function () { - return build(blocks) + return build(scheme) .then(function () { + require(targetPath); fs.writeFileSync(blockPath, 'global.REQUIRED_TECHS.push("fake-node-js");'); - dropRequireCache(require, targetPath); require(targetPath); - globals.must.include('fake-node-js'); + globals.should.include('fake-node-js'); }); }); it('must not drop require cache in production mode', function () { - return build(blocks, { devMode: false }) + return build(scheme, { devMode: false }) .then(function () { + require(targetPath); fs.writeFileSync(blockPath, 'global.REQUIRED_TECHS.push("fake-node-js");'); + dropRequireCache(require, targetPath); + require(targetPath); + + globals.should.include('node-js'); + globals.should.have.length(1); + }); + }); + }); + + describe('bundled', function () { + var targetPath = path.resolve('./bundle/bundle.node.js'); + + afterEach(function () { + global.__fake = ''; + dropRequireCache(require, targetPath); + }); + it('must run code from bundle in isolation of blocks', function () { + var scheme = { + blocks: { + 'block.vanilla.js': 'global.REQUIRED_TECHS.push("vanilla-js");', + 'block.node.js': 'global.REQUIRED_TECHS.push("node-js");' + } + }; + return build(scheme, { bundled: true }) + .then(function () { + rimraf.sync('./blocks/'); + require(targetPath); + + globals.should.include('node-js'); + globals.should.include('vanilla-js'); + }); + }); + + it('must use CommonJS module', function () { + var scheme = { + blocks: { + 'block.node.js': 'global.__fake = require("fake");' + }, + // jscs:disable + node_modules: { + fake: { + 'index.js': 'module.exports = "fake";' + } + } + // jscs:enable + }; + + return build(scheme, { bundled: true }) + .then(function () { + dropRequireCache(require, targetPath); + require(targetPath); + + global.__fake.should.be.equal('fake'); + }) + .then(function () { + dropRequireCache(require, require.resolve('fake')); + }) + .fail(function (err) { + dropRequireCache(require, require.resolve('fake')); + throw err; + }); + }); + + it('must not browserify base node modules', function () { + global.__type = 'fake'; + var scheme = { + blocks: { + 'block.node.js': 'global.__type = require("os").type();' + } + }; + + return build(scheme, { bundled: true }) + .then(function () { dropRequireCache(require, targetPath); require(targetPath); - globals.must.include('node-js'); - globals.must.have.length(1); + global.__type.should.not.equal('fake'); + global.__type.should.not.equal('Browser'); + }); + }); + + it('must not reveal node_modules', function () { + var scheme = { + blocks: { + 'block.node.js': 'require("fake");' + }, + // jscs:disable + node_modules: { + fake: { + 'index.js': 'module.exports = "fake";' + } + } + // jscs:enable + }, + pathToModule = require.resolve('fake'); + + return build(scheme, { bundled: true }) + .then(function () { + dropRequireCache(require, targetPath); + rimraf.sync('./node_modules'); + + (function () { + require(targetPath); + }).should.throw(/fake/); + }) + .then(function () { + dropRequireCache(require, pathToModule); + }) + .fail(function (err) { + dropRequireCache(require, pathToModule); + throw err; }); }); }); }); -function build(blocks, options) { - mock({ - blocks: blocks, - bundle: {} - }); +function build(scheme, options) { + scheme.bundle = {}; + mock(scheme); var bundle = new MockNode('bundle'), fileList = new FileList(); @@ -91,5 +214,5 @@ function build(blocks, options) { bundle.provideTechData('?.files', fileList); - return bundle.runTechAndRequire(NodeJsTech, options); + return bundle.runTech(NodeJsTech, options); }