Skip to content

Commit 5eafbd7

Browse files
committed
feat: reduce source map size by merging white spaces and mutations into existing tokens
1 parent e16391d commit 5eafbd7

5 files changed

Lines changed: 155 additions & 163 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const result = m.transform();
4343
version: 3,
4444
sources: [ 'optional-file-name.js' ],
4545
names: [],
46-
mappings: 'AAAA;AAAA,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAG,CAAC,CACpB,mBAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC',
46+
mappings: 'AAAA;AAAA,IAAI,EAAE,EAAE,OAAO,CAAC,QAAG,CAAC,CACpB,mBAAO,CAAC,IAAI,EAAE,CAAC',
4747
file: 'optional-file-name.js',
4848
sourcesContent: [ 'var a = require("a");\nexports.foo = a;\n' ]
4949
}

index.js

Lines changed: 68 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ var SourceNode = require('source-map').SourceNode;
44
module.exports = function(code, filePath) {
55
// the file name to be used in sourcemap sources and file fields
66
filePath = (filePath || 'file.js').replace(/\\/g, '/');
7-
var tokens = tokenize(code);
87
var mutations = [];
98

109
function checkIndex(idx) {
@@ -16,13 +15,32 @@ module.exports = function(code, filePath) {
1615
}
1716
}
1817

18+
function checkOverlap(start, end, str) {
19+
var i = 0, ii = mutations.length, mutation;
20+
for (; i < ii; i++) {
21+
mutation = mutations[i];
22+
// don't check insertion against insertion
23+
if (mutation.start === mutation.end && start === end) continue;
24+
if (mutation.start < end && mutation.end > start) {
25+
throw new Error('Conflict! new mutation: start=' + mutation.start +
26+
' end=' + mutation.end + ' str=' + mutation.value + ' ' +
27+
'with existing mutation: start=' + start +
28+
' end=' + end + ' str=' + str);
29+
30+
}
31+
}
32+
}
33+
1934
function replace(start, end, str) {
2035
var existing;
2136
checkIndex(start);
2237
checkIndex(end);
38+
checkOverlap(start, end, str);
39+
2340
if (end < start) {
2441
throw new Error('end-index: ' + end + ' cannot be smaller than start-index: ' + start);
2542
}
43+
2644
if (start === end) {
2745
// allow multiple insertion to same location
2846
existing = mutations.find(function(m) {
@@ -42,32 +60,35 @@ module.exports = function(code, filePath) {
4260
modifyCode.prepend = function(str) {
4361
return replace(0, 0, str);
4462
};
63+
4564
modifyCode.append = function(str) {
4665
return replace(code.length, code.length, str);
4766
};
67+
4868
modifyCode.insert = function(start, str) {
4969
return replace(start, start, str);
5070
};
71+
5172
modifyCode.replace = function(start, end, str) {
5273
return replace(start, end, str);
5374
};
75+
5476
modifyCode.delete = function(start, end) {
5577
return replace(start, end, '');
5678
};
79+
5780
modifyCode.transform = function() {
5881
var i = 0, ti = 0, ii = mutations.length, newTokens = [];
59-
var mutation, newValue, offset, offset2, merged;
60-
61-
function advance() { if (ti < tokens.length) ti++; }
62-
function token() { return tokens[ti]; }
63-
function prev() { return tokens[ti - 1]; }
82+
var mutation, offset, offset2, merged, isInsertion;
83+
var tokens = tokenize(code);
6484

6585
mutations.sort(function(a, b) {return a.start - b.start;});
6686

6787
for (; i < ii; i++) {
6888
mutation = mutations[i];
89+
isInsertion = mutation.start === mutation.end;
6990

70-
if (token() && token().start > mutation.start) {
91+
if (tokens[ti] && tokens[ti].start > mutation.start) {
7192
if (newTokens.length && newTokens[newTokens.length - 1].start <= mutation.start) {
7293
throw new Error('does not allow mutating same token again. Token affected: ' +
7394
JSON.stringify(newTokens[newTokens.length - 1].value));
@@ -76,79 +97,66 @@ module.exports = function(code, filePath) {
7697
}
7798
} else {
7899
// move to current affected token
79-
while (token() && token().end <= mutation.start) {
80-
newTokens.push(token());
81-
advance();
100+
while (tokens[ti] && (isInsertion ?
101+
tokens[ti].end < mutation.start :
102+
tokens[ti].end <= mutation.start // push replacement to next token
103+
)) {
104+
newTokens.push(tokens[ti]);
105+
ti++;
82106
}
83107
}
84108

85-
if (mutation.start === mutation.end) {
109+
if (isInsertion) {
86110
// an insertion
87-
if (!token()) {
88-
// append
89-
newTokens.push({
90-
value: mutation.value,
91-
start: mutation.start,
92-
end: mutation.end,
93-
line: prev() ? prev().endLine : 1,
94-
column: prev() ? prev().endColumn : 0
95-
});
96-
} else if (token().start === mutation.start) {
97-
// prepend or insert
98-
newTokens.push({
99-
value: mutation.value,
100-
start: mutation.start,
101-
end: mutation.end,
102-
line: token().line,
103-
column: token().column
104-
});
105-
newTokens.push(token());
106-
advance();
111+
if (!tokens[ti]) {
112+
// this can only happen when
113+
// 1. empty code where tokens size is zero.
114+
// 2. append in the end
115+
if (mutation.start === 0) {
116+
newTokens.push({
117+
value: mutation.value,
118+
start: 0,
119+
end: 0,
120+
line: 1,
121+
column: 0
122+
});
123+
} else if (newTokens.length && newTokens[newTokens.length - 1].end === mutation.start) {
124+
newTokens[newTokens.length - 1].value += mutation.value;
125+
} else {
126+
panic(mutation);
127+
}
107128
} else {
108129
// insertion in this token
109-
offset = mutation.start - token().start;
110-
newValue = token().value.slice(0, offset) + mutation.value + token().value.slice(offset);
111-
newTokens.push({
112-
value: newValue,
113-
start: token().start,
114-
end: token().end,
115-
line: token().line,
116-
column: token().column
117-
});
118-
advance();
130+
offset = mutation.start - tokens[ti].start;
131+
tokens[ti].value = tokens[ti].value.slice(0, offset) + mutation.value + tokens[ti].value.slice(offset);
132+
newTokens.push(tokens[ti]);
133+
ti++;
119134
}
120135
} else {
121136
// a replacement
122-
if (!token()) panic(mutation);
137+
if (!tokens[ti]) panic(mutation);
123138

124139
// merge tokens if replacement affects multiple tokens
125-
merged = {
126-
value: token().value,
127-
start: token().start,
128-
end: token().end,
129-
line: token().line,
130-
column: token().column
131-
};
132-
140+
merged = tokens[ti];
133141
while (merged.end < mutation.end) {
134-
advance();
135-
if (!token()) panic(mutation);
136-
merged.value = merged.value + token().value;
137-
merged.end = token().end;
142+
ti++;
143+
if (!tokens[ti]) panic(mutation);
144+
merged.value = merged.value + tokens[ti].value;
145+
merged.end = tokens[ti].end;
138146
}
139147

140148
offset = mutation.start - merged.start;
141149
offset2 = mutation.end - merged.start;
142150
merged.value = merged.value.slice(0, offset) + mutation.value + merged.value.slice(offset2);
143151
newTokens.push(merged);
144-
advance();
152+
ti++;
145153
}
146154
}
147155

148156
// the rest unaffected tokens
149-
while (token()) {
150-
newTokens.push(token());
151-
advance();
157+
while (tokens[ti]) {
158+
newTokens.push(tokens[ti]);
159+
ti++;
152160
}
153161

154162
var node = new SourceNode(null, null, null, newTokens.map(function(t) {
@@ -168,5 +176,6 @@ module.exports = function(code, filePath) {
168176
};
169177

170178
function panic(mutation) {
171-
throw new Error('Panic! mutation: start=' + mutation.start + ' end=' + mutation.end + ' str=' + mutation.value);
179+
throw new Error('Panic! mutation: start=' + mutation.start +
180+
' end=' + mutation.end + ' str=' + mutation.value);
172181
}

0 commit comments

Comments
 (0)