forked from ben-ng/sourcemap-validator
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
153 lines (128 loc) · 4.75 KB
/
index.js
File metadata and controls
153 lines (128 loc) · 4.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
var validate
, validateMapping
, toAscii
, assert = require('assert')
, SMConsumer = require('source-map').SourceMapConsumer
, each = require('lodash.foreach')
, template = require('lodash.template')
, jsesc = require('jsesc')
, fancyArrow = String.fromCharCode(parseInt(2192,16));
// Lifted from UglifyJS
toAscii = function (str, identifier) {
return str.replace(/[\u0080-\uffff]/g, function(ch) {
var code = ch.charCodeAt(0).toString(16);
if (code.length <= 2 && !identifier) {
while (code.length < 2) code = "0" + code;
return "\\x" + code;
} else {
while (code.length < 4) code = "0" + code;
return "\\u" + code;
}
}).replace(/\x0B/g, "\\x0B");
};
// Performs simple validation of a mapping
validateMapping = function (mapping) {
var prettyMapping = JSON.stringify(mapping, null, 2);
assert.ok(mapping.generatedColumn!=null, 'missing generated column, mapping: ' + prettyMapping);
assert.ok(mapping.generatedLine!=null, 'missing generated line, mapping: ' + prettyMapping);
assert.ok(mapping.generatedColumn >= 0, 'generated column must be greater or equal to zero, mapping: ' + prettyMapping);
assert.ok(mapping.generatedLine >= 0, 'generated line must be greater or equal to zero: ' + prettyMapping);
assert.ok(mapping.originalColumn!=null, 'missing original column, mapping: ' + prettyMapping);
assert.ok(mapping.originalLine!=null, 'missing original line, mapping: ' + prettyMapping);
assert.ok(mapping.originalColumn >= 0, 'original column must be greater or equal to zero, mapping: ' + prettyMapping);
assert.ok(mapping.originalLine >= 0, 'original line must be greater or equal to zero, mapping: ' + prettyMapping);
assert.notEqual(mapping.source, null, 'source is missing');
};
// Validates an entire sourcemap
validate = function (min, map, srcs) {
var consumer
, mappingCount = 0
, splitSrcs = {};
srcs = srcs || {};
// If no map was given, try to extract it from min
if(map == null) {
try {
var re = /\s*\/\/(?:@|#) sourceMappingURL=data:application\/json;base64,(\S*)$/m
, map = min.match(re);
map = (new Buffer(map[1], 'base64')).toString();
min = min.replace(re, '');
}
catch (e) {
throw new Error('No map argument provided, and no inline sourcemap found');
}
}
try {
consumer = new SMConsumer(map);
}
catch (e) {
throw new Error('The map is not valid JSON');
}
each(consumer.sources, function (src) {
var content = consumer.sourceContentFor(src);
if(content)
srcs[src] = content;
});
each(srcs, function (src, file) {
return splitSrcs[file] = src.split('\n'); // Split sources by line
});
consumer.eachMapping(function (mapping) {
mappingCount++;
validateMapping(mapping);
// These validations can't be performed with just the mapping
var originalLine
, errMsg
, mapRef
, expected
, actuals = []
, success = false;
if(mapping.name) {
if(!splitSrcs[mapping.source]) {
throw new Error(mapping.source + ' not found in ' + Object.keys(splitSrcs).join(', '));
}
originalLine = splitSrcs[mapping.source][mapping.originalLine - 1];
expected = [
mapping.name
, '\'' + jsesc(mapping.name) + '\''
, '\'' + toAscii(mapping.name) + '\''
, '"' + jsesc(mapping.name, {quotes: 'double'}) + '"'
, '"' + toAscii(mapping.name) + '"'
];
// An exact match
for(var i=0, ii=expected.length; i<ii; i++) {
// It's possible to go out of bounds on some stupid cases
try {
var actual = originalLine.split('').splice(mapping.originalColumn, expected[i].length).join('');
}
catch (e) {
return;
}
actuals.push(actual);
if(expected[i] === actual) {
success = true;
break;
}
};
if(!success) {
mapRef = template('<%=generatedLine%>:<%=generatedColumn%>'
+ fancyArrow
+ '<%=originalLine%>:<%=originalColumn%> "<%=name%>" in <%=source%>')(mapping);
errMsg = template(''
+ 'Warning: mismatched names\n'
+ 'Expected: <%=expected%>\n'
+ 'Got: <%=actual%>\n'
+ 'Original Line: <%=original%>\n'
+ 'Mapping: <%=mapRef%>'
, {
expected: expected.join(' || ')
, actual: actuals.join(' || ')
, original: originalLine
, mapRef: mapRef
});
throw new Error(errMsg);
}
}
});
assert.ok(JSON.parse(map).sources && JSON.parse(map).sources.length, 'There were no sources in the file');
assert.ok(mappingCount > 0, 'There were no mappings in the file');
};
module.exports = validate;