Skip to content

Commit 2b315b8

Browse files
committed
Test that paths in mapped stack frames match expected format: either file:/ or native path
1 parent 5b79f38 commit 2b315b8

File tree

2 files changed

+57
-50
lines changed

2 files changed

+57
-50
lines changed

source-map-support.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,6 @@ function createPrepareStackTrace(hookState) {
608608

609609
// Generate position and snippet of original source with pointer
610610
function getErrorSource(error) {
611-
// TODO this is not robust enough
612611
var match = /\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(error.stack);
613612
if (match) {
614613
var source = match[1];

test.js

Lines changed: 57 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ var child_process = require('child_process');
1212
var assert = require('assert');
1313
var fs = require('fs');
1414
var util = require('util');
15+
var path = require('path');
16+
const { pathToFileURL } = require('url');
1517
var bufferFrom = Buffer.from;
1618
const semver = require('semver');
1719

@@ -45,6 +47,17 @@ function stackFrameAtTest(sourceMapSupportInstalled = true) {
4547
function stackFrameAtTrace(fileRe) {
4648
return extension === 'mjs' ? `${fileRe}` : `Object\\.<anonymous> \\(${fileRe}\\)`;
4749
}
50+
/**
51+
* Describe how the source path in a stack frame is expected to start.
52+
* If generated module is ESM, node uses file:// URL, so we expect mapped original path to also be file:// to match
53+
* On windows, when not doing file:// URIs, expect windows-style paths
54+
*/
55+
function stackFramePathStartsWith() {
56+
if(extension === 'mjs') return 'file:/';
57+
// Escape backslashes since we are returning regexp syntax
58+
return path.parse(process.cwd()).root.replace(/\\/g, '\\\\');
59+
// this re \((?:.*[/\\])?
60+
}
4861
/**
4962
* Tests were initially written as CJS with require() calls.
5063
* We can support require() calls in MJS tests, too, as long as we create a require() function.
@@ -177,7 +190,6 @@ function rewriteExpectation(expected, generatedFilenameIn, generatedFilenameOut)
177190
async function compareStackTrace(sourceMap, source, expected) {
178191
const header = srcPrefix();
179192
// Check once with a separate source map
180-
// fs.writeFileSync(`.generated-${id}-separate.${extension}.map`, JSON.stringify({...JSON.parse(sourceMap.toString()), file: `.generated-${id}-separate.${extension}`}));
181193
fs.writeFileSync(`.generated-${id}-separate.${extension}.map`, sourceMap.toString());
182194
fs.writeFileSync(`.generated-${id}-separate.${extension}`, `${header}${namedExportDeclaration()} = function() {` +
183195
source.join('\n') + `};//@ sourceMappingURL=.generated-${id}-separate.${extension}.map`);
@@ -235,15 +247,15 @@ async function normalThrow() {
235247
'throw new Error("test");'
236248
], [
237249
'Error: test',
238-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$`
250+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$`
239251
]);
240252
}
241253
async function normalThrowWithoutSourceMapSupportInstalled() {
242254
await compareStackTrace(sourceMapConstructors.createMultiLineSourceMap(), [
243255
'throw new Error("test");'
244256
], [
245257
'Error: test',
246-
re`^ at ${stackFrameAtTest(false)} \((?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$`
258+
re`^ at ${stackFrameAtTest(false)} \(${stackFramePathStartsWith()}(?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$`
247259
]);
248260
}
249261
}
@@ -335,7 +347,7 @@ it('fs.readFileSync failure', async function() {
335347
'}'
336348
], [
337349
'Error: test',
338-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line7\.js:1007:107\)$`
350+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line7\.js:1007:107\)$`
339351
]);
340352
});
341353

@@ -348,8 +360,8 @@ it('throw inside function', async function() {
348360
'foo();'
349361
], [
350362
'Error: test',
351-
/^ at foo \((?:.*[/\\])?line2\.js:1002:102\)$/,
352-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line4\.js:1004:104\)$`
363+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line2\.js:1002:102\)$`,
364+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line4\.js:1004:104\)$`
353365
]);
354366
});
355367

@@ -364,9 +376,9 @@ it('throw inside function inside function', async function() {
364376
'foo();'
365377
], [
366378
'Error: test',
367-
/^ at bar \((?:.*[/\\])?line3\.js:1003:103\)$/,
368-
/^ at foo \((?:.*[/\\])?line5\.js:1005:105\)$/,
369-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line7\.js:1007:107\)$`
379+
re`^ at bar \(${stackFramePathStartsWith()}(?:.*[/\\])?line3\.js:1003:103\)$`,
380+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line5\.js:1005:105\)$`,
381+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line7\.js:1007:107\)$`
370382
]);
371383
});
372384

@@ -377,9 +389,9 @@ it('eval', async function() {
377389
'Error: test',
378390

379391
// TODO
380-
/^ at eval \(eval at (<anonymous>|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/,
392+
re`^ at eval \(eval at (<anonymous>|exports\.test|test) \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)`,
381393

382-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$`
394+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$`
383395
]);
384396
});
385397

@@ -388,9 +400,9 @@ it('eval inside eval', async function() {
388400
'eval("eval(\'throw new Error(\\"test\\")\')");'
389401
], [
390402
'Error: test',
391-
/^ at eval \(eval at (<anonymous>|exports\.test|test) \(eval at (<anonymous>|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/,
392-
/^ at eval \(eval at (<anonymous>|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/,
393-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$`
403+
re`^ at eval \(eval at (<anonymous>|exports\.test|test) \(eval at (<anonymous>|exports\.test|test) \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)`,
404+
re`^ at eval \(eval at (<anonymous>|exports\.test|test) \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)`,
405+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$`
394406
]);
395407
});
396408

@@ -402,9 +414,9 @@ it('eval inside function', async function() {
402414
'foo();'
403415
], [
404416
'Error: test',
405-
/^ at eval \(eval at foo \((?:.*[/\\])?line2\.js:1002:102\)/,
406-
/^ at foo \((?:.*[/\\])?line2\.js:1002:102\)/,
407-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line4\.js:1004:104\)$`
417+
re`^ at eval \(eval at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line2\.js:1002:102\)`,
418+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line2\.js:1002:102\)`,
419+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line4\.js:1004:104\)$`
408420
]);
409421
});
410422

@@ -414,7 +426,7 @@ it('eval with sourceURL', async function() {
414426
], [
415427
'Error: test',
416428
/^ at eval \(sourceURL\.js:1:7\)$/,
417-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$`
429+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$`
418430
]);
419431
});
420432

@@ -424,8 +436,8 @@ it('eval with sourceURL inside eval', async function() {
424436
], [
425437
'Error: test',
426438
/^ at eval \(sourceURL\.js:1:7\)$/,
427-
/^ at eval \(eval at (<anonymous>|exports.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/,
428-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$`
439+
re`^ at eval \(eval at (<anonymous>|exports.test|test) \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)`,
440+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$`
429441
]);
430442
});
431443

@@ -452,7 +464,7 @@ it('throw with empty source map', async function() {
452464
'throw new Error("test");'
453465
], [
454466
'Error: test',
455-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?\.generated-${id}.${extension}:1:123\)$`
467+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?\.generated-${id}.${extension}:1:123\)$`
456468
]);
457469
});
458470

@@ -467,7 +479,7 @@ it('throw in Timeout with empty source map', function(done) {
467479
' throw new Error("this is the error")',
468480
/^ \^$/,
469481
'Error: this is the error',
470-
re`^ at ((null)|(Timeout))\._onTimeout \((?:.*[/\\])?.generated-${id}\.${extension}:3:11\)$`
482+
re`^ at ((null)|(Timeout))\._onTimeout \(${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}\.${extension}:3:11\)$`
471483
]);
472484
});
473485

@@ -476,7 +488,7 @@ it('throw with source map with gap', async function() {
476488
'throw new Error("test");'
477489
], [
478490
'Error: test',
479-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$`
491+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$`
480492
]);
481493
});
482494

@@ -485,7 +497,7 @@ it('sourcesContent with data URL', async function() {
485497
'throw new Error("test");'
486498
], [
487499
'Error: test',
488-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?original-${id}\.js:1001:5\)$`
500+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?original-${id}\.js:1001:5\)$`
489501
]);
490502
});
491503

@@ -495,7 +507,7 @@ it('finds the last sourceMappingURL', async function() {
495507
'throw new Error("test");'
496508
], [
497509
'Error: test',
498-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?original-${id}\.js:1002:5\)$`
510+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?original-${id}\.js:1002:5\)$`
499511
]);
500512
});
501513

@@ -519,8 +531,8 @@ it('maps original name from source', async function() {
519531
'foo();'
520532
], [
521533
'Error: test',
522-
re`^ at myOriginalName \((?:.*[/\\])?\.original-${id}.js:1000:11\)$`,
523-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?\.original-${id}.js:1002:2\)$`
534+
re`^ at myOriginalName \(${stackFramePathStartsWith()}(?:.*[/\\])?\.original-${id}.js:1000:11\)$`,
535+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?\.original-${id}.js:1002:2\)$`
524536
]);
525537
});
526538

@@ -536,7 +548,7 @@ it('default options', function(done) {
536548
'this is the original code',
537549
'^',
538550
'Error: this is the error',
539-
re`^ at foo \((?:.*[/\\])?\.original-${id}\.js:1:1\)$`
551+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?\.original-${id}\.js:1:1\)$`
540552
]);
541553
});
542554

@@ -551,7 +563,7 @@ it('handleUncaughtExceptions is true', function(done) {
551563
'this is the original code',
552564
'^',
553565
'Error: this is the error',
554-
re`^ at foo \((?:.*[/\\])?\.original-${id}\.js:1:1\)$`
566+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?\.original-${id}\.js:1:1\)$`
555567
]);
556568
});
557569

@@ -568,7 +580,7 @@ it('handleUncaughtExceptions is false', function(done) {
568580
' ^',
569581

570582
'Error: this is the error',
571-
re`^ at foo \((?:.*[/\\])?.original-${id}\.js:1:1\)$`
583+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?.original-${id}\.js:1:1\)$`
572584
]);
573585
});
574586

@@ -583,7 +595,7 @@ it('default options with empty source map', function(done) {
583595
'function foo() { throw new Error("this is the error"); }',
584596
' ^',
585597
'Error: this is the error',
586-
re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:2:24\)$`
598+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:2:24\)$`
587599
]);
588600
});
589601

@@ -598,7 +610,7 @@ it('default options with source map with gap', function(done) {
598610
'function foo() { throw new Error("this is the error"); }',
599611
' ^',
600612
'Error: this is the error',
601-
re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:2:24\)$`
613+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:2:24\)$`
602614
]);
603615
});
604616

@@ -629,7 +641,7 @@ it('sourcesContent', function(done) {
629641
' line 2',
630642
' ^',
631643
'Error: this is the error',
632-
re`^ at foo \((?:.*[/\\])?original-${id}\.js:1002:5\)$`
644+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?original-${id}\.js:1002:5\)$`
633645
]);
634646
});
635647

@@ -652,14 +664,18 @@ it('missing source maps should also be cached', function(done) {
652664
'process.nextTick(function() { console.log(count); });',
653665
], [
654666
'Error: this is the error',
655-
re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:4:15\)$`,
667+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:4:15\)$`,
656668
'Error: this is the error',
657-
re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:4:15\)$`,
669+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:4:15\)$`,
658670
'1', // The retrieval should only be attempted once
659671
]);
660672
});
661673

662674
it('should consult all retrieve source map providers', function(done) {
675+
// TODO are we supposed to be resolving this URL to absolute? Or should we test that non-absolute is supported?
676+
// Test in vanilla source-map-support
677+
let originalPath = path.resolve(`.original-${id}.js`);
678+
if(extension === 'mjs') originalPath = pathToFileURL(originalPath).toString();
663679
compareStdout(done, createSingleLineSourceMap(), [
664680
'',
665681
'var count = 0;',
@@ -676,7 +692,7 @@ it('should consult all retrieve source map providers', function(done) {
676692
' retrieveSourceMap: function(name) {',
677693
` if (/\\.generated-${id}\\.${extension}$/.test(name)) {`,
678694
' count++;',
679-
' return ' + JSON.stringify({url: `.original-${id}.js`, map: createMultiLineSourceMapWithSourcesContent().toJSON()}) + ';',
695+
' return ' + JSON.stringify({url: originalPath, map: createMultiLineSourceMapWithSourcesContent().toJSON()}) + ';',
680696
' }',
681697
' }',
682698
'});',
@@ -685,9 +701,9 @@ it('should consult all retrieve source map providers', function(done) {
685701
'process.nextTick(function() { console.log(count); });',
686702
], [
687703
'Error: this is the error',
688-
re`^ at foo \((?:.*[/\\])?original-${id}\.js:1004:5\)$`,
704+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?original-${id}\.js:1004:5\)$`,
689705
'Error: this is the error',
690-
re`^ at foo \((?:.*[/\\])?original-${id}\.js:1004:5\)$`,
706+
re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?original-${id}\.js:1004:5\)$`,
691707
'1', // The retrieval should only be attempted once
692708
]);
693709
});
@@ -728,13 +744,6 @@ it('should allow for runtime inline source maps', function(done) {
728744
'0', // The retrieval should only be attempted once
729745
]);
730746
});
731-
// }
732-
733-
// TODO should this suite also be inside the matrix?
734-
// describe('Other', function() {
735-
// Wrapped in a suite to preserve test execution order
736-
// const {createEmptySourceMap, createSingleLineSourceMap, createMultiLineSourceMap} = sourceMapCreators();
737-
// const extension = 'cjs';
738747

739748
/* The following test duplicates some of the code in
740749
* `compareStackTrace` but appends a charset to the
@@ -745,7 +754,7 @@ it('finds source maps with charset specified', async function() {
745754
var source = [ 'throw new Error("test");' ];
746755
var expected = [
747756
'Error: test',
748-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$`
757+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$`
749758
];
750759

751760
fs.writeFileSync(`.generated-${id}.${extension}`, `${namedExportDeclaration()} = function() {` +
@@ -767,7 +776,7 @@ it('allows code/comments after sourceMappingURL', async function() {
767776
var source = [ 'throw new Error("test");' ];
768777
var expected = [
769778
'Error: test',
770-
re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$`
779+
re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$`
771780
];
772781

773782
fs.writeFileSync(`.generated-${id}.${extension}`, `${namedExportDeclaration()} = function() {` +
@@ -846,10 +855,9 @@ it('supports multiple instances', function(done) {
846855
'this is some other original code',
847856
'^',
848857
'Error: this is the error',
849-
re`^ at test \((?:.*[/\\])?.original2-${id}\.js:1:1\)$`
858+
re`^ at test \(${stackFramePathStartsWith()}(?:.*[/\\])?.original2-${id}\.js:1:1\)$`
850859
]);
851860
});
852-
// });
853861
}
854862

855863
describe('redirects require() of "source-map-support" to this module', function() {

0 commit comments

Comments
 (0)