jsmin-sourcemap
Version:
JSMin with sourcemaps!
260 lines (218 loc) • 7.2 kB
JavaScript
// Load in dependencies
var assert = require('assert'),
fs = require('fs'),
sourcemap = require('source-map'),
charProps = require('char-props'),
jsmin = require('../'),
SourceMapConsumer = sourcemap.SourceMapConsumer,
testFilesDir = __dirname + '/test_files';
function minifySingle() {
before(function () {
// Read in the src file
var params = this.params,
src = params.src,
singleSrc = fs.readFileSync(testFilesDir + '/' + src, 'utf8');
// Save to the code namespace
this.input = [{'src': src, 'code': singleSrc}];
this.result = jsmin({'code': singleSrc, 'src': src, 'dest': params.dest});
});
}
function minifyMulti() {
before(function () {
// Localize the src
var params = this.params,
src = params.src;
// Read in the src files
var srcFiles = src.map(function (filepath) {
var fileSrc = fs.readFileSync(testFilesDir + '/' + filepath, 'utf8'),
retObj = {
'code': fileSrc,
'src': filepath
};
return retObj;
});
// Save relevant info
this.input = srcFiles;
this.result = jsmin({'input': srcFiles, 'dest': params.dest});
});
}
function assertMatchesC() {
it('matches its C-minified counterpart', function () {
var params = this.params,
expectedCode = fs.readFileSync(__dirname + '/expected_files/' + params.dest, 'utf8');
assert.strictEqual(
this.result.code,
expectedCode,
'Minified ' + JSON.stringify(params.src) + ' does not match ' + params.dest
);
});
}
function mapAgainstSource() {
before(function () {
// Iterate over the input
var srcPropMap = {};
this.input.forEach(function (item) {
srcPropMap[item.src] = charProps(item.code);
});
// Generate a consumer and charProps lookups
var result = this.result;
this.srcConsumer = new SourceMapConsumer(result.sourcemap);
this.actualProps = charProps(result.code);
this.srcPropMap = srcPropMap;
});
}
function assertAllPositionsMatch() {
it('matches at all positions', function () {
// Iterate over each of the characters
var breaks = this.expectedBreaks || [],
result = this.result,
actualCode = result.code,
actualProps = this.actualProps,
i = 0,
len = actualCode.length;
for (; i < len; i++) {
// Look up the position of our index
var actualPosition = {
'line': actualProps.lineAt(i) + 1,
'column': actualProps.columnAt(i)
};
// Grab the position of the item and its fil
var srcPosition = this.srcConsumer.originalPositionFor(actualPosition),
srcFile = srcPosition.source;
// If we have a source file and we are not at a break spot
// TODO: Actually figure out `breaks`
var atBreakSpot = breaks.indexOf(i) > -1;
if (srcFile && !atBreakSpot) {
// Grab the srcProps for it
var srcProps = this.srcPropMap[srcFile];
// Lookup the character at the src positions
var srcLine = srcPosition.line - 1,
srcCol = srcPosition.column,
srcChar = srcProps.charAt({
'line': srcLine,
'column': srcCol
});
// Assert that the actual and expected characters are equal
var actualChar = actualCode.charAt(i);
// DEV: Nuking character comparison for break location so we can find it via error message
// assert.strictEqual(actualChar, srcChar, 'The sourcemapped character at index ' + i + ' does not match its original character at line ' + srcLine + ', column ' + srcCol + '.');
assert(actualChar === srcChar, 'The sourcemapped character at index ' + i + ' does not match its original character at line ' + srcLine + ', column ' + srcCol + '.');
}
}
});
}
function isDebuggable() {
if (process.env.TEST_DEBUG) {
before(function () {
try { fs.mkdirSync(__dirname + '/actual_files'); } catch (e) {}
fs.writeFileSync(__dirname + '/actual_files/debug.min.js', this.result.code, 'utf8');
fs.writeFileSync(__dirname + '/actual_files/debug.min.map', this.result.sourcemap, 'utf8');
});
}
}
describe('A single file', function () {
before(function jQueryPaths () {
this.params = {'src': '1.js', 'dest': 'single.js'};
});
describe('minified and sourcemapped (single)', function () {
minifySingle();
isDebuggable();
assertMatchesC();
describe('mapped against its source', function () {
mapAgainstSource();
assertAllPositionsMatch();
});
});
});
describe('jQuery', function () {
before(function jQueryPaths () {
this.params = {'src': 'jquery.js', 'dest': 'jquery.min.js'};
});
describe('minified and sourcemapped (single)', function () {
minifySingle();
isDebuggable();
assertMatchesC();
});
});
describe('Multiple files', function () {
before(function multiPaths () {
this.params = {'src': ['1.js', '2.js', '3.js'], 'dest': 'multi.js'};
this.expectedBreaks = [52, 70];
});
describe('minified and sourcemapped (multi)', function () {
minifyMulti();
isDebuggable();
assertMatchesC();
describe('mapped against its source', function () {
mapAgainstSource();
assertAllPositionsMatch();
});
});
});
describe('Multiple nested files', function () {
before(function () {
this.params = {
'src': [
'nested.js',
'nested/controllers/controller1.js',
'nested/controllers/controller2.js',
'nested/models/model1.js'
],
'dest': 'nested.min.js'
};
this.expectedBreaks = [1, 43, 88, 100];
});
describe('minified and sourcemapped (multi)', function () {
minifyMulti();
isDebuggable();
assertMatchesC();
describe('mapped against its source', function () {
mapAgainstSource();
assertAllPositionsMatch();
});
});
});
describe('Multiple files containing "use strict"', function () {
before(function () {
this.params = {
'src': [
'strict1.js',
'strict2.js'
],
'dest': 'strict.min.js'
};
this.expectedBreaks = [36];
});
describe('minified and sourcemapped (multi)', function () {
minifyMulti();
isDebuggable();
// DEV: This is not the actual C output. The C one doesn't contain line breaks which can break concatenated scripts.
assertMatchesC();
describe('mapped against its source', function () {
mapAgainstSource();
assertAllPositionsMatch();
});
});
});
describe('Multiple files containing "use strict" 2', function () {
before(function () {
this.params = {
'src': [
'strict3.js',
'strict4.js'
],
'dest': 'strict2.min.js'
};
this.expectedBreaks = [135];
});
describe('minified and sourcemapped (multi)', function () {
minifyMulti();
isDebuggable();
// DEV: This is not the actual C output. The C one doesn't contain line breaks which can break concatenated scripts.
assertMatchesC();
describe('mapped against its source', function () {
mapAgainstSource();
assertAllPositionsMatch();
});
});
});