Skip to content

Commit 5bc8cce

Browse files
committed
added SourceNode.fromStringWithSourceMap.
1 parent 68ed4f4 commit 5bc8cce

File tree

3 files changed

+151
-0
lines changed

3 files changed

+151
-0
lines changed

lib/source-map/source-node.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,102 @@ define(function (require, exports, module) {
3232
if (aChunks != null) this.add(aChunks);
3333
}
3434

35+
/**
36+
* Creates a SourceNode from generated code and a SourceMapConsumer.
37+
*
38+
* @param aGeneratedCode The generated code
39+
* @param aSourceMapConsumer The SourceMap for the generated code
40+
*/
41+
SourceNode.fromStringWithSourceMap =
42+
function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) {
43+
// The SourceNode we want to fill with the generated code
44+
// and the SourceMap
45+
var node = new SourceNode();
46+
47+
// The generated code
48+
// Processed fragments are removed from this array.
49+
var remainingLines = aGeneratedCode.split('\n');
50+
51+
// We need to remember the position of "remainingLines"
52+
var lastGeneratedLine = 1, lastGeneratedColumn = 0;
53+
54+
// The generate SourceNodes we need a code range.
55+
// To extract it current and last mapping is used.
56+
// Here we store the last mapping.
57+
var lastMapping = null;
58+
59+
aSourceMapConsumer.eachMapping(function (mapping) {
60+
if(lastMapping === null) {
61+
// We add the generated code until the first mapping
62+
// to the SourceNode without any mapping.
63+
// Each line is added as separate string.
64+
while(lastGeneratedLine < mapping.generatedLine) {
65+
node.add(remainingLines.shift() + "\n");
66+
lastGeneratedLine++;
67+
}
68+
if(lastGeneratedColumn < mapping.generatedColumn) {
69+
var nextLine = remainingLines[0];
70+
node.add(nextLine.substr(0, mapping.generatedColumn));
71+
remainingLines[0] = nextLine.substr(mapping.generatedColumn);
72+
lastGeneratedColumn = mapping.generatedColumn;
73+
}
74+
} else {
75+
// We add the code from "lastMapping" to "mapping":
76+
// First check if there is a new line in between.
77+
if(lastGeneratedLine < mapping.generatedLine) {
78+
// Associate the remaining code in this line with "lastMapping"
79+
addMappingWithCode(lastMapping, remainingLines.shift() + "\n");
80+
lastGeneratedLine++;
81+
lastGeneratedColumn = 0;
82+
// More lines are not be mapped to any mapping
83+
while(lastGeneratedLine < mapping.generatedLine) {
84+
node.add(remainingLines.shift() + "\n");
85+
lastGeneratedLine++;
86+
}
87+
// When we reached the correct line, we add code until we
88+
// reach the correct column too.
89+
// This code is not mapped.
90+
if(lastGeneratedColumn < mapping.generatedColumn) {
91+
var nextLine = remainingLines[0];
92+
node.add(nextLine.substr(0, mapping.generatedColumn));
93+
remainingLines[0] = nextLine.substr(mapping.generatedColumn);
94+
lastGeneratedColumn = mapping.generatedColumn;
95+
}
96+
} else {
97+
// There is no new line in between.
98+
// Associate the code between "lastGeneratedColumn" and
99+
// "mapping.generatedColumn" with "lastMapping"
100+
var nextLine = remainingLines[0];
101+
var code = nextLine.substr(0, mapping.generatedColumn -
102+
lastGeneratedColumn);
103+
remainingLines[0] = nextLine.substr(mapping.generatedColumn -
104+
lastGeneratedColumn);
105+
lastGeneratedColumn = mapping.generatedColumn;
106+
addMappingWithCode(lastMapping, code);
107+
}
108+
}
109+
lastMapping = mapping;
110+
}, this);
111+
// We have processed all mappings.
112+
// Associate the remaining code in the current line with "lastMapping"
113+
// and add the remaining lines without any mapping
114+
addMappingWithCode(lastMapping, remainingLines.shift());
115+
remainingLines.forEach(function(line) {
116+
node.add("\n");
117+
node.add(line);
118+
});
119+
120+
return node;
121+
122+
function addMappingWithCode(mapping, code) {
123+
if(mapping.source === undefined) {
124+
node.add(code);
125+
} else {
126+
node.add(new SourceNode(mapping.originalLine, mapping.originalColumn, mapping.source, code, mapping.name));
127+
}
128+
}
129+
};
130+
35131
/**
36132
* Add a chunk of generated JS to this source node.
37133
*

test/source-map/test-source-node.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,57 @@ define(function (require, exports, module) {
188188
assert.equal(actual.column, null);
189189
};
190190

191+
exports['test .fromStringWithSourceMap()'] = function (assert, util) {
192+
var node = SourceNode.fromStringWithSourceMap(
193+
util.testGeneratedCode,
194+
new SourceMapConsumer(util.testMap));
195+
196+
var result = node.toStringWithSourceMap({
197+
file: 'min.js'
198+
});
199+
var map = result.map;
200+
var code = result.code;
201+
202+
assert.equal(code, util.testGeneratedCode);
203+
assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
204+
map = map.toJSON();
205+
assert.equal(map.version, util.testMap.version);
206+
assert.equal(map.file, util.testMap.file);
207+
assert.equal(map.mappings, util.testMap.mappings);
208+
};
209+
210+
exports['test .fromStringWithSourceMap() complex version'] = function (assert, util) {
211+
var input = new SourceNode(null, null, null, [
212+
"(function() {\n",
213+
" var Test = {};\n",
214+
" ", new SourceNode(1, 0, "a.js", "Test.A = { value: 1234 };\n"),
215+
" ", new SourceNode(2, 0, "a.js", "Test.A.x = 'xyz';"), "\n",
216+
"}());\n",
217+
"/* Generated Source */"]);
218+
input = input.toStringWithSourceMap({
219+
file: 'foo.js'
220+
});
221+
222+
var node = SourceNode.fromStringWithSourceMap(
223+
input.code,
224+
new SourceMapConsumer(input.map.toString()));
225+
226+
var result = node.toStringWithSourceMap({
227+
file: 'foo.js'
228+
});
229+
var map = result.map;
230+
var code = result.code;
231+
232+
assert.equal(code, input.code);
233+
assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
234+
map = map.toJSON();
235+
var inputMap = input.map.toJSON();
236+
assert.equal(map.version, inputMap.version);
237+
assert.equal(map.file, inputMap.file);
238+
assert.equal(map.mappings, inputMap.mappings);
239+
assert.deepEqual(map.sources, inputMap.sources);
240+
assert.equal(map.sourceRoot, inputMap.sourceRoot);
241+
assert.deepEqual(map.names, inputMap.names);
242+
};
243+
191244
});

test/source-map/util.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ define(function (require, exports, module) {
2828
//
2929
// ONE.foo=function(a){return baz(a);};
3030
// TWO.inc=function(a){return a+1;};
31+
exports.testGeneratedCode = " ONE.foo=function(a){return baz(a);};\n"+
32+
" TWO.inc=function(a){return a+1;};";
3133
exports.testMap = {
3234
version: 3,
3335
file: 'min.js',

0 commit comments

Comments
 (0)