Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove assertions when debug:true as well #2

Merged
merged 9 commits into from
Sep 25, 2015
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ sudo: false
node_js:
- "0.10"
- "0.12"
- "iojs"
- "stable"
script:
- "npm test"
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
unassertify
================================

[Browserify](http://browserify.org/) transform to remove assertions on production build. Encourages Design by Contract (DbC).
[Browserify](http://browserify.org/) transform to remove assertions from code. Encourages Design by Contract (DbC).

[![Build Status][travis-image]][travis-url]
[![NPM version][npm-image]][npm-url]
Expand Down Expand Up @@ -43,7 +43,7 @@ var glob = require('glob'),

gulp.task('production_build', function() {
var files = glob.sync('./src/*.js');
var b = browserify({entries: files, debug: false});
var b = browserify({entries: files});
b.transform('unassertify');
return b.bundle()
.pipe(source('bundle.js'))
Expand Down
65 changes: 56 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* unassertify
* Browserify transform to remove assertions on production build
* Browserify transform to remove assertions from code to encourage Design by Contract (DbC)
*
* https://github.com/twada/unassertify
*
Expand All @@ -13,22 +13,65 @@
var through = require('through');
var esprima = require('esprima');
var escodegen = require('escodegen');
var convert = require('convert-source-map');
var transfer = require('multi-stage-sourcemap').transfer;
var unassert = require('unassert');

function isDebugMode (options) {
return (options && options._flags && options._flags.debug);
function mergeSourceMap (incomingSourceMap, outgoingSourceMap) {
if (typeof outgoingSourceMap === 'string' || outgoingSourceMap instanceof String) {
outgoingSourceMap = JSON.parse(outgoingSourceMap);
}
if (!incomingSourceMap) {
return outgoingSourceMap;
}
return JSON.parse(transfer({fromSourceMap: outgoingSourceMap, toSourceMap: incomingSourceMap}));
}

function handleIncomingSourceMap (originalCode) {
var commented = convert.fromSource(originalCode);
if (commented) {
return commented.toObject();
}
return null;
}

function applyUnassertWithSourceMap (code, filepath, options) {
var ast = esprima.parse(code, { sourceType: 'module', tolerant: true, loc: true });
var inMap = handleIncomingSourceMap(code);
var instrumented = escodegen.generate(unassert(ast), {
sourceMap: filepath,
sourceContent: code,
sourceMapWithCode: true
});
var outMap = convert.fromJSON(instrumented.map.toString());
if (inMap) {
var mergedRawMap = mergeSourceMap(inMap, outMap.toObject());
var reMap = convert.fromObject(mergedRawMap);
if (inMap.sources) {
reMap.setProperty('sources', inMap.sources);
}
if (inMap.sourceRoot) {
reMap.setProperty('sourceRoot', inMap.sourceRoot);
}
if (inMap.sourcesContent) {
reMap.setProperty('sourcesContent', inMap.sourcesContent);
}
return instrumented.code + '\n' + reMap.toComment() + '\n';
} else {
return instrumented.code + '\n' + outMap.toComment() + '\n';
}
}

function applyUnassert (code, options) {
function applyUnassertWithoutSourceMap (code, filepath, options) {
var ast = esprima.parse(code, { sourceType: 'module' });
return escodegen.generate(unassert(ast));
}

module.exports = function unassertify (filepath, options) {
if (isDebugMode(options)) {
return through();
}
function shouldProduceSourceMap (options) {
return (options && options._flags && options._flags.debug);
}

module.exports = function unassertify (filepath, options) {
var data = '',
stream = through(write, end);

Expand All @@ -37,7 +80,11 @@ module.exports = function unassertify (filepath, options) {
}

function end() {
stream.queue(applyUnassert(data, options));
if (shouldProduceSourceMap(options)) {
stream.queue(applyUnassertWithSourceMap(data, filepath, options));
} else {
stream.queue(applyUnassertWithoutSourceMap(data, filepath, options));
}
stream.queue(null);
}

Expand Down
16 changes: 11 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "unassertify",
"description": "Browserify transform to remove assertions on production build",
"description": "Browserify transform to remove assertions from code to encourage Design by Contract (DbC)",
"version": "1.0.2",
"author": {
"name": "Takuto Wada",
Expand All @@ -9,15 +9,21 @@
},
"bugs": "https://github.com/twada/unassertify/issues",
"dependencies": {
"convert-source-map": "^1.1.1",
"escodegen": "^1.6.1",
"esprima": "^2.2.0",
"multi-stage-sourcemap": "^0.2.1",
"through": "^2.3.7",
"unassert": "^1.1.0"
"unassert": "^1.2.0"
},
"devDependencies": {
"browserify": "^11.0.1",
"browserify": "^11.2.0",
"coffeeify": "^1.1.0",
"espower-loader": "^1.0.0",
"event-stream": "^3.3.1",
"mocha": "^2.2.5"
"intelli-espower-loader": "^1.0.0",
"mocha": "^2.3.3",
"power-assert": "^1.0.1"
},
"files": [
"README.md",
Expand All @@ -40,6 +46,6 @@
"url": "http://github.com/twada/unassertify.git"
},
"scripts": {
"test": "mocha"
"test": "mocha --require intelli-espower-loader"
}
}
8 changes: 8 additions & 0 deletions test/fixtures/coffee/fixture.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
assert = require('assert')

add = (a, b) ->
console.assert typeof a == 'number'
assert !isNaN(a)
assert.equal typeof b, 'number'
assert.ok !isNaN(b)
a + b
5 changes: 5 additions & 0 deletions test/fixtures/func/expected-with-sourcemap.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 68 additions & 4 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ var unassertify = require('..');
var fs = require('fs');
var path = require('path');
var Stream = require('stream');
var assert = require('assert');
var assert = require('power-assert');
var browserify = require('browserify');
var coffeeify = require('coffeeify');
var es = require('event-stream');
var convert = require('convert-source-map');


describe('unassertify', function () {
Expand All @@ -22,10 +24,72 @@ describe('unassertify', function () {
done();
}));
});
it('produces sourcemap when debug: true', function (done) {
var b = browserify({debug: true});
var testFilepath = path.normalize(path.join(__dirname, 'fixtures', 'func', 'fixture.js'));
b.add(testFilepath);
b.transform(unassertify);
b.bundle().pipe(es.wait(function(err, data) {
assert(!err);
var code = data.toString('utf-8');
// console.log(code);
assert(! /assert/.test(code));
var inlineMap = convert.fromSource(code);
assert(inlineMap);
var sourceMap = inlineMap.toObject();
assert(sourceMap);
// console.log(JSON.stringify(sourceMap, null, 2));
assert(sourceMap.sources.some(function (fpath) { return fpath === testFilepath; }));
done();
}));
});
});


describe('with preceding transform', function () {
it('just remove assertions and dependencies when debug: false', function (done) {
var b = browserify();
b.add(path.normalize(path.join(__dirname, 'fixtures', 'coffee', 'fixture.coffee')));
b.transform(coffeeify);
b.transform(unassertify);
b.bundle().pipe(es.wait(function(err, data) {
assert(!err);
var code = data.toString('utf-8');
// console.log(code);
var inlineMap = convert.fromSource(code);
assert(!inlineMap);
assert(! /require\('assert'\)/.test(code));
done();
}));
});
it('adjust sourcemap if debug: true', function (done) {
var b = browserify({debug: true});
var testFilepath = path.normalize(path.join(__dirname, 'fixtures', 'coffee', 'fixture.coffee'));
b.add(testFilepath);
b.transform(coffeeify);
b.transform(unassertify);
b.bundle().pipe(es.wait(function(err, data) {
assert(!err);
var code = data.toString('utf-8');
// console.log(code);
assert(! /require\('assert'\)/.test(code));
var inlineMap = convert.fromSource(code);
assert(inlineMap);
var sourceMap = inlineMap.toObject();
assert(sourceMap);
// console.log(JSON.stringify(sourceMap, null, 2));
assert(sourceMap.sources.some(function (fpath) { return fpath === testFilepath; }));
var originalCode = fs.readFileSync(testFilepath, 'utf-8');
assert(sourceMap.sourcesContent.some(function (eachCode) {
return eachCode === originalCode;
}));
done();
}));
});
});


describe('do nothing if debug: true', function() {
describe('adjust sourcemap if debug: true', function() {
var stream = unassertify(
'/absolute/path/to/test/fixtures/func/fixture.js',
{
Expand All @@ -47,7 +111,7 @@ describe('do nothing if debug: true', function() {
output += buf;
});
stream.on('end', function() {
var expected = fs.readFileSync('test/fixtures/func/fixture.js', 'utf8');
var expected = fs.readFileSync('test/fixtures/func/expected-with-sourcemap.js', 'utf8');
assert.equal(output, expected);
done();
});
Expand All @@ -57,7 +121,7 @@ describe('do nothing if debug: true', function() {
});


describe('remove assertions if debug: false', function() {
describe('just remove assertions if debug: false', function() {
var stream = unassertify(
'/absolute/path/to/test/fixtures/func/fixture.js',
{
Expand Down