Skip to content

Commit dc98de3

Browse files
committed
fix: Change default of onlyWithURL, remove graceful-fs, modernise sources
* Trying to load source maps for files without `sourceMappingURL` is not something usual and should not be the default behaviour. * Windows support in Node.js has improved and graceful-fs should not be needed any more. * Use modern JavaScript language syntax. Node.js 15 is required anyway, at least because of the need for `replaceAll`. BREAKING CHANGE: The package `graceful-fs` is not used any more. It should not affect your project, but better to test it. Source maps are loaded only for files with `sourceMappingURL` by default. (The default value of the option `onlyWithURL` has changed to `true`.) If your JavaScript files lack `sourceMappingURL`, but include external source maps, set the options `onlyWithURL` to `false`.
1 parent 490c85f commit dc98de3

11 files changed

Lines changed: 200 additions & 74 deletions

.denolint.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"rules": {
3+
"tags": ["recommended"]
4+
}
5+
}

.editorconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# editorconfig.org
2+
root = true
3+
4+
[*]
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = true

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ Initial release of [this fork] of the [original project] with the following impr
1919

2020
* Introduce unit tests for the existing functionality
2121

22-
**BREAKING CHANGE**: The minimum supported version of Node.js is 15.
22+
**BREAKING CHANGE**: Use `@prantlf/karma-sourcemap-loader` instead of `karma-sourcemap-loader` as the package name to depend on. Other changes should not affect typical projects:
23+
* The minimum supported version of Node.js is 15. The current LTS version is already 16.
24+
* The package `graceful-fs` is not used any more. Recent versions of Node.js should be reliable enough on Windows.
25+
* Source maps are loaded only for files with `sourceMappingURL` by default. (The default value of the option `onlyWithURL` has changed to `true`.) If your JavaScript files lack `sourceMappingURL`, but include external source maps, set the options `onlyWithURL` to `false`.
2326

2427
[this fork]: http://github.com/prantlf/karma-sourcemap-loader
2528
[original project]: https://github.com/demerzel3/karma-sourcemap-loader

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This is a fork of the [original project] with the following improvements:
1313
* Allow changing `sourceRoot` in source maps
1414
* Allow adapting the source map files alone, if served separately by the Karma web server
1515
* Allow failing the test run in case of invalid and missing source maps
16+
* Allow loading the source maps for files without `sourceMapingURL`
1617
* Fix handling of raw (URI-encoded) source maps - trim the leading `,` before parsing the content
1718
* Introduce unit tests for the existing functionality
1819

@@ -144,7 +145,7 @@ module.exports = function(config) {
144145
};
145146
```
146147

147-
The code below shows a sample configuration of the preprocessor with source map loading only for files with the `sourceMappingURL` set. The default behaviour is trying to load source maps for all JavaScript files, also those without the `sourceMappingURL` set.
148+
The code below shows a sample configuration of the preprocessor with source map loading also for files without the `sourceMappingURL`. The default behaviour tries loading source maps only for JavaScript files with the `sourceMappingURL` set.
148149

149150
```js
150151
// karma.conf.js
@@ -155,7 +156,7 @@ module.exports = function(config) {
155156
'**/*.js': ['sourcemap']
156157
},
157158
sourceMapLoader: {
158-
onlyWithURL: true
159+
onlyWithURL: false
159160
}
160161
});
161162
};

index.js

Lines changed: 63 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
1-
var fs = require('graceful-fs');
2-
var path = require('path');
1+
const { access, readFile } = require('fs');
2+
const { dirname, resolve } = require('path');
33

4-
var sourcemapUrlRegeExp = /^\/\/#\s*sourceMappingURL=/;
4+
const sourcemapUrlRegeExp = /^\/\/#\s*sourceMappingURL=/;
5+
const charsetRegex = /^;charset=([^;]+);/;
56

6-
var createSourceMapLocatorPreprocessor = function(args, logger, config) {
7+
const createSourceMapPreprocessor = (_args, logger, config) => {
78
/* c8 ignore next */
8-
var options = config && config.sourceMapLoader || {};
9-
var remapPrefixes = options.remapPrefixes;
10-
var remapSource = options.remapSource;
11-
var useSourceRoot = options.useSourceRoot;
12-
var onlyWithURL = options.onlyWithURL;
13-
var strict = options.strict;
14-
var needsUpdate = remapPrefixes || remapSource || useSourceRoot;
15-
16-
var log = logger.create('preprocessor.sourcemap');
17-
var charsetRegex = /^;charset=([^;]+);/;
18-
19-
return function(content, file, done) {
9+
const options = config && config.sourceMapLoader || {};
10+
const { remapPrefixes, remapSource, useSourceRoot, onlyWithURL, strict } = options;
11+
const needsUpdate = remapPrefixes || remapSource || useSourceRoot;
12+
13+
const log = logger.create('preprocessor.sourcemap');
14+
15+
return (content, file, done) => {
16+
const { path, originalPath } = file;
17+
18+
// Updates source paths according to the configuration
2019
function remapSources(sources) {
21-
var all = sources.length;
22-
var remapped = 0;
23-
var remappedPrefixes = {};
24-
var i, source, remappedSource;
20+
const all = sources.length;
21+
let remapped = 0;
22+
const remappedPrefixes = {};
23+
let i, source, remappedSource;
2524

2625
// Replaces source path prefixes using a key:value map
2726
function handlePrefixes() {
28-
var sourcePrefix, targetPrefix, target;
29-
for (sourcePrefix in remapPrefixes) {
30-
targetPrefix = remapPrefixes[sourcePrefix];
27+
for (const sourcePrefix in remapPrefixes) {
28+
const targetPrefix = remapPrefixes[sourcePrefix];
3129
if (source.startsWith(sourcePrefix)) {
32-
target = targetPrefix + source.substring(sourcePrefix.length);
30+
const target = targetPrefix + source.substring(sourcePrefix.length);
3331
sources[i] = target;
3432
++remapped;
3533
// Log only one remapping as an example for each prefix to prevent
@@ -41,11 +39,12 @@ var createSourceMapLocatorPreprocessor = function(args, logger, config) {
4139
return true;
4240
}
4341
}
42+
/* c8 ignore next */
4443
}
4544

4645
// Replaces source paths using a custom function
4746
function handleMapper() {
48-
var target = remapSource(source);
47+
const target = remapSource(source);
4948
// Remapping is considered happenned only if the handler returns
5049
// a non-empty path different from the existing one
5150
if (target && target !== source) {
@@ -86,11 +85,11 @@ var createSourceMapLocatorPreprocessor = function(args, logger, config) {
8685
} catch (err) {
8786
/* c8 ignore next 5 */
8887
if (strict) {
89-
done(new Error('malformed source map for', file.originalPath + '\nError: ' + err.message));
88+
done(new Error(`malformed source map for ${originalPath}\nError: ${err.message}`));
9089
// Returning `false` will make the caller abort immediately
9190
return false;
9291
}
93-
log.warn('malformed source map for', file.originalPath);
92+
log.warn('malformed source map for', originalPath);
9493
log.warn('Error:', err.message);
9594
}
9695
}
@@ -111,8 +110,9 @@ var createSourceMapLocatorPreprocessor = function(args, logger, config) {
111110
}
112111
}
113112

114-
function sourceMapData(data){
115-
var sourceMap = parseMap(data);
113+
// Updates the source map content according to the configuration
114+
function sourceMapData(data) {
115+
const sourceMap = parseMap(data);
116116
if (sourceMap) {
117117
// Perform the remapping only if there is a configuration for it
118118
if (needsUpdate) {
@@ -126,81 +126,79 @@ var createSourceMapLocatorPreprocessor = function(args, logger, config) {
126126
done(content);
127127
}
128128

129-
function inlineMap(inlineData){
130-
131-
var charset = 'utf-8';
132-
129+
// Processes the inline source map
130+
function inlineMap(inlineData) {
131+
let charset = 'utf-8';
133132
if (charsetRegex.test(inlineData)) {
134-
var matches = inlineData.match(charsetRegex);
135-
136-
if (matches.length === 2) {
133+
const matches = inlineData.match(charsetRegex);
134+
if (matches) {
137135
charset = matches[1];
138136
inlineData = inlineData.slice(matches[0].length -1);
139137
}
140138
}
141139

142140
if (/^;base64,/.test(inlineData)) {
143141
// base64-encoded JSON string
144-
log.debug('base64-encoded source map for', file.originalPath);
145-
var buffer = Buffer.from(inlineData.slice(';base64,'.length), 'base64');
142+
log.debug('base64-encoded source map for', originalPath);
143+
const buffer = Buffer.from(inlineData.slice(8), 'base64');
146144
sourceMapData(buffer.toString(charset));
147145
} else if (inlineData.startsWith(',')) {
148146
// straight-up URL-encoded JSON string
149-
log.debug('raw inline source map for', file.originalPath);
147+
log.debug('raw inline source map for', originalPath);
150148
sourceMapData(decodeURIComponent(inlineData.slice(1)));
151149
} else {
152150
/* c8 ignore next 2 */
153151
if (strict) {
154-
done(new Error('invalid source map in ' + file.originalPath));
152+
done(new Error(`invalid source map in ${originalPath}`));
155153
} else {
156-
log.warn('invalid source map in', file.originalPath);
154+
log.warn('invalid source map in', originalPath);
157155
done(content)
158156
}
159157
}
160158
}
161159

160+
// Processes the external source map
162161
function fileMap(mapPath, optional) {
163-
fs.exists(mapPath, function(exists) {
164-
if (!exists) {
162+
access(mapPath, err => {
163+
if (err) {
165164
if (!optional) {
166165
/* c8 ignore next 3 */
167166
if (strict) {
168-
done(new Error('missing external source map for ' + file.originalPath));
167+
done(new Error(`missing external source map for ${originalPath}`));
169168
return;
170169
} else {
171-
log.warn('missing external source map for', file.originalPath);
170+
log.warn('missing external source map for', originalPath);
172171
}
173172
}
174173
done(content);
175174
return;
176175
}
177176

178-
fs.readFile(mapPath, function(err, data) {
177+
readFile(mapPath, (err, data) => {
179178
/* c8 ignore next 10 */
180179
if (err) {
181180
if (strict) {
182-
done(new Error('reading external source map failed for ' + file.originalPath + '\n' + err));
181+
done(new Error(`reading external source map failed for ${originalPath}\n${err}`));
183182
} else {
184-
log.warn('reading external source map failed for', file.originalPath);
183+
log.warn('reading external source map failed for', originalPath);
185184
log.warn(err);
186185
done(content);
187186
}
188187
return;
189188
}
190189

191-
log.debug('external source map exists for', file.originalPath);
190+
log.debug('external source map exists for', originalPath);
192191
sourceMapData(data);
193192
});
194193
});
195194
}
196195

197196
// Remap source paths in a directly served source map
198197
function convertMap() {
199-
var sourceMap;
200198
// Perform the remapping only if there is a configuration for it
201199
if (needsUpdate) {
202-
log.debug('processing source map', file.originalPath);
203-
sourceMap = parseMap(content);
200+
log.debug('processing source map', originalPath);
201+
const sourceMap = parseMap(content);
204202
if (sourceMap) {
205203
updateSourceMap(sourceMap);
206204
content = JSON.stringify(sourceMap);
@@ -212,39 +210,37 @@ var createSourceMapLocatorPreprocessor = function(args, logger, config) {
212210
done(content);
213211
}
214212

215-
if (file.path.endsWith('.map')) {
216-
return convertMap();
217-
}
213+
// If the preprocessed file is not JavaScript, handle it as an external
214+
// source map and have Karma serve the updated content
215+
if (path.endsWith('.map')) return convertMap();
218216

219-
var lines = content.split(/\n/);
220-
var lastLine = lines.pop();
221-
while (/^\s*$/.test(lastLine)) {
217+
const lines = content.split(/\n/);
218+
let lastLine;
219+
do {
222220
lastLine = lines.pop();
223-
}
224-
225-
var mapUrl;
221+
} while (/^\s*$/.test(lastLine));
226222

223+
let mapUrl;
227224
if (sourcemapUrlRegeExp.test(lastLine)) {
228225
mapUrl = lastLine.replace(sourcemapUrlRegeExp, '');
229226
}
230227

231228
if (!mapUrl) {
232-
if (onlyWithURL) {
229+
if (onlyWithURL !== false) {
233230
done(content);
234231
} else {
235-
fileMap(file.path + ".map", true);
232+
fileMap(`${path}.map`, true);
236233
}
237234
} else if (/^data:application\/json/.test(mapUrl)) {
238235
inlineMap(mapUrl.slice('data:application/json'.length));
239236
} else {
240-
fileMap(path.resolve(path.dirname(file.path), mapUrl), false);
237+
fileMap(resolve(dirname(path), mapUrl), false);
241238
}
242239
};
243240
};
244241

245-
createSourceMapLocatorPreprocessor.$inject = ['args', 'logger', 'config'];
242+
createSourceMapPreprocessor.$inject = ['args', 'logger', 'config'];
246243

247-
// PUBLISH DI MODULE
248244
module.exports = {
249-
'preprocessor:sourcemap': ['factory', createSourceMapLocatorPreprocessor]
245+
'preprocessor:sourcemap': ['factory', createSourceMapPreprocessor]
250246
};

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"@semantic-release/changelog": "6.0.2",
6464
"@semantic-release/git": "10.0.1",
6565
"c8": "7.12.0",
66+
"denolint": "2.0.5",
6667
"jasmine-core": "4.5.0",
6768
"karma": "6.4.1",
6869
"karma-brief-reporter": "0.2.2",

0 commit comments

Comments
 (0)