Skip to content

Commit

Permalink
Merge pull request #7 from enb-make/fix/inner.source.map
Browse files Browse the repository at this point in the history
Correctly handle inner sourcemap on writeFileContent
  • Loading branch information
blond committed Aug 20, 2015
2 parents ba8ad27 + b13eda7 commit aefb48d
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 76 deletions.
45 changes: 26 additions & 19 deletions lib/file.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
var os = require('os');
var path = require('path');
var url = require('url');
var mozilla = require('source-map');
var SourceMapGenerator = mozilla.SourceMapGenerator;
var utils = require('./utils');
var getSourceMap = utils.getSourceMap;
var removeBuiltInSourceMap = utils.removeBuiltInSourceMap;

/**
* @param {String} filename
Expand All @@ -20,9 +20,8 @@ function File(filename, opts) {

this._content = [''];
if (opts.sourceMap) {
this._map = new SourceMapGenerator({file: filename});
this._map = new SourceMapGenerator({file: path.basename(filename)});
}
this._filename = filename;
this._opts = opts;
}

Expand All @@ -46,19 +45,34 @@ File.prototype = {
this._content = this._content.concat(lines);
},

writeFileFragment: function (filename, content, lineNumber, column) {
var lines = getLines(content);
var subSourceMap;
if (this._map) {
subSourceMap = getSourceMap(lines);
/**
* @param {String} relPath relative from file being added to processed file
* @param {String} content
* @param {Number} lineNumber
* @param {Number} column
*/
writeFileFragment: function (relPath, content, lineNumber, column) {
var data = utils.splitContentAndSourceMap(content);

if (this._map && data.sourceMap) {
var lineOffset = this._content.length - 1;
var middleDir = path.dirname(relPath) + '/';

data.sourceMap.eachMapping(function (mapping) {
this._map.addMapping({
source: url.resolve(middleDir, mapping.source), // make source relative to processed file
original: {line: mapping.originalLine, column: mapping.originalColumn},
generated: {line: lineOffset + mapping.generatedLine, column: mapping.generatedColumn}
});
}.bind(this));
}

lines = removeBuiltInSourceMap(lines);
var lines = getLines(data.content);
var lastLineNum = lines.length - 1;
lines.forEach(function (line, i) {
if (this._map) {
if (this._map && !data.sourceMap) {
this._map.addMapping({
source: filename,
source: relPath,
original: {line: lineNumber + i, column: i === 0 ? column : 0},
generated: {line: this._content.length, column: i === 0 ? this.getCursor().column : 0}
});
Expand All @@ -69,13 +83,6 @@ File.prototype = {
this.writeLine(line);
}
}, this);

if (this._map && subSourceMap) {
this._map.applySourceMap(
subSourceMap,
filename
);
}
},

getCursor: function () {
Expand Down
9 changes: 9 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ module.exports = {
}
},

///
splitContentAndSourceMap: function (content) {
var pieces = split(content);
if (pieces.sourceMap) {
pieces.sourceMap = mkSourceMap(pieces.sourceMap);
}
return pieces;
},

/**
* @param {String} content
* @param {SourceMapGenerator} sourceMap
Expand Down
207 changes: 150 additions & 57 deletions tests/file.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
var os = require('os');
var path = require('path');
var format = require('util').format;
var sinon = require('sinon');
var SourceMapGenerator = require('source-map').SourceMapGenerator;
var File = require('../lib/file');
var SourceLocator = require('../lib/source-locator');
var utils = require('../lib/utils');

describe('File', function () {
Expand Down Expand Up @@ -99,15 +98,6 @@ describe('File', function () {
});
});

describe('writeFileContent()', function () {
it('should add content to the output', function () {
file.writeFileContent('2.js', 'line 1\nline 2');
file.writeFileContent('2.js', 'line 3\nline 4');
hasSourceMap(file.render()).should.equal(true);
stripSourceMap(file.render()).should.equal('line 1\nline 2\nline 3\nline 4\n');
});
});

describe('write()', function () {
it('should add content to the output', function () {
file.write('1');
Expand Down Expand Up @@ -147,31 +137,108 @@ describe('File', function () {
hasSourceMap(file.render()).should.equal(true);
stripSourceMap(file.render()).should.equal('_line 1\nline 2line 3\nline 4\n');

var locator = new SourceLocator('1.js', file.render());
var loc1 = locator.locate(1, 0);
loc1.source.should.equal('1.js');
loc1.line.should.equal(1);
loc1.column.should.equal(0);

var loc2 = locator.locate(1, 1);
loc2.source.should.equal(path.resolve(__dirname + '/../2.js'));
loc2.line.should.equal(1);
loc2.column.should.equal(0);

var loc3 = locator.locate(2, 1);
loc3.source.should.equal(path.resolve(__dirname + '/../2.js'));
loc3.line.should.equal(2);
loc3.column.should.equal(1);

var loc4 = locator.locate(2, 6);
loc4.source.should.equal(path.resolve(__dirname + '/../3.js'));
loc4.line.should.equal(2);
loc4.column.should.equal(3);

var loc5 = locator.locate(3, 1);
loc5.source.should.equal(path.resolve(__dirname + '/../3.js'));
loc5.line.should.equal(3);
loc5.column.should.equal(1);
toReadableString(utils.getSourceMap(file.render())).should.equal(
[
'1, 1 -> 1, 0 2.js',
'2, 0 -> 2, 0 2.js',
'2, 6 -> 2, 3 3.js',
'3, 0 -> 3, 0 3.js'
].join('\n')
);
});
});

describe('writeFileContent()', function () {
it('should add content to the output', function () {
file.writeFileContent('2.js', 'line 1\nline 2');
file.writeFileContent('2.js', 'line 3\nline 4');
hasSourceMap(file.render()).should.equal(true);
stripSourceMap(file.render()).should.equal('line 1\nline 2\nline 3\nline 4\n');
});

describe('with existing source map', function() {
it('should point to source file', function () {
var middleFile = new File('middle-file.js', {sourceMap: true});
middleFile.writeFileContent('source.js', 'line');
var middleContent = middleFile.render();

file.writeFileContent('some-file.js', middleContent);

var pos = utils.getSourceMap(file.render()).originalPositionFor({line: 1, column: 0});
pos.source.should.equal('source.js');
pos.line.should.equal(1);
pos.column.should.equal(0);
});

it('should save relative path to source file', function () {
var middleFile = new File('middle-file.js', {sourceMap: true});
middleFile.writeFileContent('../other/path/source.js', 'line');
var middleContent = middleFile.render();

file.writeFileContent('../some/path/some-file.js', middleContent);

var pos = utils.getSourceMap(file.render()).originalPositionFor({line: 1, column: 0});
pos.source.should.equal('../some/other/path/source.js');
});

it('should save absolute path to source file when passed absolute', function () {
var middleFile = new File('middle-file.js', {sourceMap: true});
middleFile.writeFileContent('../other/path/source.js', 'line');
var middleContent = middleFile.render();

file.writeFileContent('/some/path/some-file.js', middleContent);

var pos = utils.getSourceMap(file.render()).originalPositionFor({line: 1, column: 0});
pos.source.should.equal('/some/other/path/source.js');
});

it('should keep absolute source path', function () {
var middleFile = new File('middle-file.js', {sourceMap: true});
middleFile.writeFileContent('/other/path/source.js', 'line');
var middleContent = middleFile.render();

file.writeFileContent('/some/path/some-file.js', middleContent);

var pos = utils.getSourceMap(file.render()).originalPositionFor({line: 1, column: 0});
pos.source.should.equal('/other/path/source.js');
});

it('should correctly handle column numbers', function () {
var css = [
'.button',
'{',
' vertical-align: middle;',
'}'
].join('\n'),
map = mkMap_(
'middle.css', 'source.css',
[[1, 0], [1, 0]],
[[3, 4], [3, 4]],
[[3, 27], [3, 27]],
[[4, 1], [4, 1]]
),
middleContent = utils.joinContentAndSourceMap(css, map);

file.writeFileContent('some-file.js', middleContent);

var expected = toReadableString(utils.getSourceMap(middleContent));
var actual = toReadableString(utils.getSourceMap(file.render()));
expected.should.equal(actual);
});

///
function mkMap_(generated, source) {
var map = new SourceMapGenerator({file: generated});
var mappings = [].slice.call(arguments, 2);
mappings.forEach(function(m) {
map.addMapping({
source: source,
original: {line: m[1][0], column: m[1][1]},
generated: {line: m[0][0], column: m[0][1]}
});
});
return map;
}
});
});

Expand All @@ -181,32 +248,43 @@ describe('File', function () {
file.writeContent('// Some unmapped content');
file.writeFileContent(
'func1.js',
'// anonymous function here\n' +
'var f1 = function() {\n' +
' return 1;\n' +
'};\n' +
'// end of anonymous function\n'
[
'// anonymous function here',
'var f1 = function() {',
' return 1;',
'};',
'// end of anonymous function',
''
].join('\n')
);
file.writeFileContent(
'func2.js',
'// named function here\n' +
' function f1() {\n' +
' return 1;\n' +
' }\n' +
'// end of named function\n'
[
'// named function here',
' function f1() {',
' return 1;',
' }',
'// end of named function',
''
].join('\n')
);

var locator = new SourceLocator('1.js', file.render());

var comment1Loc = locator.locate(1, 3);
comment1Loc.source.should.equal('1.js');
comment1Loc.line.should.equal(1);
comment1Loc.column.should.equal(3);

var function1Loc = locator.locate(4, 9);
function1Loc.source.should.equal(path.resolve(__dirname + '/../func1.js'));
function1Loc.line.should.equal(2);
function1Loc.column.should.equal(9);
toReadableString(utils.getSourceMap(file.render())).should.equal(
[
'3, 0 -> 1, 0 func1.js',
'4, 0 -> 2, 0 func1.js',
'5, 0 -> 3, 0 func1.js',
'6, 0 -> 4, 0 func1.js',
'7, 0 -> 5, 0 func1.js',
'8, 0 -> 6, 0 func1.js',
'9, 0 -> 1, 0 func2.js',
'10, 0 -> 2, 0 func2.js',
'11, 0 -> 3, 0 func2.js',
'12, 0 -> 4, 0 func2.js',
'13, 0 -> 5, 0 func2.js',
'14, 0 -> 6, 0 func2.js'
].join('\n')
);
});
});
});
Expand Down Expand Up @@ -274,3 +352,18 @@ function stripSourceMap(source) {
lines.pop();
return lines.join(os.EOL) + '\n';
}

///
function toReadableString(consumer) {
var pieces = [];
consumer.eachMapping(function(mapping) {
pieces.push(format('%s, %s -> %s, %s %s',
mapping.generatedLine,
mapping.generatedColumn,
mapping.originalLine,
mapping.originalColumn,
mapping.source
));
});
return pieces.join('\n');
}

0 comments on commit aefb48d

Please sign in to comment.