index.js
5.31 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
154
155
'use strict';
var path = require('path');
var convert = require('convert-source-map');
var memoize = require('lodash.memoize');
var createGenerator = require('inline-source-map');
var pathIsAbsolute = require('./lib/path-is-absolute');
var mappingsFromMap = require('./lib/mappings-from-map');
var protocolRx = /^[a-z]+:\/\//;
/**
* Rebases a relative path in 'sourceFile' to be relative
* to the path where 'sourceFile' is located.
*
* This is necessary before adding relative paths to the
* new combined map to ensure all paths are relative to their
* original source.
*
* The 'sourceRoot' from the original source map is joined
* as well to ensure the complete path.
*
* Resulting paths that are absolute are passed along directly.
*
* @param sourceFile {String} path to the original source file that references a map
* @param relativeRoot {String} sourceRoot in sourceFile's map to combine with relativePath
* @param relativePath {String} source path from sourceFile's map
*/
var rebaseRelativePath = memoize(function(sourceFile, relativeRoot, relativePath) {
if (!relativePath) {
return relativePath;
}
// join relative path to root (e.g. 'src/' + 'file.js')
var relativeRootedPath = relativeRoot ? path.join(relativeRoot, relativePath) : relativePath;
relativeRootedPath = relativeRootedPath.replace(/\\/g, '/');
sourceFile = sourceFile.replace(/\\/g, '/');
if (sourceFile === relativeRootedPath || // same path,
pathIsAbsolute(relativeRootedPath) || // absolute path, nor
protocolRx.test(relativeRootedPath)) { // absolute protocol need rebasing
return relativeRootedPath;
}
// make relative to source file
return path.join(path.dirname(sourceFile), relativeRootedPath).replace(/\\/g, '/');
}, function(a, b, c) {
return a + '::' + b + '::' + c;
});
function resolveMap(source) {
var gen = convert.fromSource(source);
return gen ? gen.toObject() : null;
}
function hasInlinedSource(existingMap) {
return existingMap.sourcesContent && !!existingMap.sourcesContent[0];
}
function Combiner(file, sourceRoot) {
// since we include the original code in the map sourceRoot actually not needed
this.generator = createGenerator({ file: file || 'generated.js', sourceRoot: sourceRoot });
}
Combiner.prototype._addGeneratedMap = function (sourceFile, source, offset) {
this.generator.addGeneratedMappings(sourceFile, source, offset);
this.generator.addSourceContent(sourceFile, source);
return this;
};
Combiner.prototype._addExistingMap = function (sourceFile, source, existingMap, offset) {
var mappings = mappingsFromMap(existingMap);
// add all of the sources from the map
for (var i = 0, len = existingMap.sources.length; i < len; i++) {
if (!existingMap.sourcesContent) continue;
this.generator.addSourceContent(
rebaseRelativePath(sourceFile, existingMap.sourceRoot, existingMap.sources[i]),
existingMap.sourcesContent[i]);
}
// add the mappings, preserving the original mapping 'source'
mappings.forEach(function(mapping) {
// Add the mappings one at a time because 'inline-source-map' doesn't handle
// mapping source filenames. The mapping.source already takes sourceRoot into account
// per the SMConsumer.eachMapping function, so pass null for the root here.
this.generator.addMappings(
rebaseRelativePath(sourceFile, null, mapping.source), [mapping], offset);
}, this);
return this;
};
/**
* Adds map to underlying source map.
* If source contains a source map comment that has the source of the original file inlined it will offset these
* mappings and include them.
* If no source map comment is found or it has no source inlined, mappings for the file will be generated and included
*
* @name addMap
* @function
* @param opts {Object} { sourceFile: {String}, source: {String} }
* @param offset {Object} { line: {Number}, column: {Number} }
*/
Combiner.prototype.addFile = function (opts, offset) {
offset = offset || {};
if (!offset.hasOwnProperty('line')) offset.line = 0;
if (!offset.hasOwnProperty('column')) offset.column = 0;
var existingMap = resolveMap(opts.source);
return existingMap && hasInlinedSource(existingMap)
? this._addExistingMap(opts.sourceFile, opts.source, existingMap, offset)
: this._addGeneratedMap(opts.sourceFile, opts.source, offset);
};
/**
* @name base64
* @function
* @return {String} base64 encoded combined source map
*/
Combiner.prototype.base64 = function () {
return this.generator.base64Encode();
};
/**
* @name comment
* @function
* @return {String} base64 encoded sourceMappingUrl comment of the combined source map
*/
Combiner.prototype.comment = function () {
return this.generator.inlineMappingUrl();
};
/**
* @name create
* @function
* @param file {String} optional name of the generated file
* @param sourceRoot {String} optional sourceRoot of the map to be generated
* @return {Object} Combiner instance to which source maps can be added and later combined
*/
exports.create = function (file, sourceRoot) { return new Combiner(file, sourceRoot); };
/**
* @name removeComments
* @function
* @param src
* @return {String} src with all sourceMappingUrl comments removed
*/
exports.removeComments = function (src) {
if (!src.replace) return src;
return src.replace(convert.commentRegex, '').replace(convert.mapFileCommentRegex, '');
};