@@ -14,7 +14,7 @@ packageJson = require '../../package.json'
14
14
# The current CoffeeScript version number.
15
15
exports .VERSION = packageJson .version
16
16
17
- exports .FILE_EXTENSIONS = [' .coffee' , ' .litcoffee' , ' .coffee.md' ]
17
+ exports .FILE_EXTENSIONS = FILE_EXTENSIONS = [' .coffee' , ' .litcoffee' , ' .coffee.md' ]
18
18
19
19
# Expose helpers for testing.
20
20
exports .helpers = helpers
@@ -266,15 +266,35 @@ formatSourcePosition = (frame, getSourceMapping) ->
266
266
else
267
267
fileLocation
268
268
269
- getSourceMap = (filename ) ->
270
- if sourceMaps[filename]?
271
- sourceMaps[filename][sourceMaps[filename].length - 1 ]
269
+ getSourceMap = (filename , line , column ) ->
270
+ # Skip files that we didn’t compile, like Node system files that appear in
271
+ # the stack trace, as they never have source maps.
272
+ return null unless filename is ' <anonymous>' or filename .slice (filename .lastIndexOf (' .' )) in FILE_EXTENSIONS
273
+
274
+ if filename isnt ' <anonymous>' and sourceMaps[filename]?
275
+ return sourceMaps[filename][sourceMaps[filename].length - 1 ]
276
+ # CoffeeScript compiled in a browser or via `CoffeeScript.compile` or `.run`
277
+ # may get compiled with `options.filename` that’s missing, which becomes
278
+ # `<anonymous>`; but the runtime might request the stack trace with the
279
+ # filename of the script file. See if we have a source map cached under
280
+ # `<anonymous>` that matches the error.
272
281
else if sourceMaps[' <anonymous>' ]?
273
- # CoffeeScript compiled in a browser may get compiled with `options.filename`
274
- # of `<anonymous>`, but the browser may request the stack trace with the
275
- # filename of the script file.
276
- sourceMaps[' <anonymous>' ][sourceMaps[' <anonymous>' ].length - 1 ]
277
- else if sources[filename]?
282
+ # Work backwards from the most recent anonymous source maps, until we find
283
+ # one that works. This isn’t foolproof; there is a chance that multiple
284
+ # source maps will have line/column pairs that match. But we have no other
285
+ # way to match them. `frame.getFunction().toString()` doesn’t always work,
286
+ # and it’s not foolproof either.
287
+ for map in sourceMaps[' <anonymous>' ] by - 1
288
+ sourceLocation = map .sourceLocation [line - 1 , column - 1 ]
289
+ return map if sourceLocation? [0 ]? and sourceLocation[1 ]?
290
+
291
+ # If all else fails, recompile this source to get a source map. We need the
292
+ # previous section (for `<anonymous>`) despite this option, because after it
293
+ # gets compiled we will still need to look it up from
294
+ # `sourceMaps['<anonymous>']` in order to find and return it. That’s why we
295
+ # start searching from the end in the previous block, because most of the
296
+ # time the source map we want is the last one.
297
+ if sources[filename]?
278
298
answer = compile sources[filename][sources[filename].length - 1 ],
279
299
filename : filename
280
300
sourceMap : yes
@@ -289,7 +309,7 @@ getSourceMap = (filename) ->
289
309
# positions.
290
310
Error .prepareStackTrace = (err , stack ) ->
291
311
getSourceMapping = (filename , line , column ) ->
292
- sourceMap = getSourceMap filename
312
+ sourceMap = getSourceMap filename, line, column
293
313
answer = sourceMap .sourceLocation [line - 1 , column - 1 ] if sourceMap?
294
314
if answer? then [answer[0 ] + 1 , answer[1 ] + 1 ] else null
295
315
0 commit comments