UNPKG

4.22 kBtext/coffeescriptView Raw
1# Node.js-specific support: module loading, sourceMapping errors
2
3fs = require 'fs'
4path = require 'path'
5Module = require 'module'
6CoffeeScript = require './module'
7{SourceMapConsumer} = require 'source-map'
8
9# NodeJS / V8 have no support for transforming positions in stack traces using
10# sourceMap, so we must monkey-patch Error to display CoffeeScript source
11# positions.
12
13# Ideally, this would happen in a way that is scalable to multiple compile-to-
14# JS languages trying to do the same thing in the same NodeJS process. We can
15# implement it as if there were an API, and then patch in support for that
16# API. The following maybe should be in its own npm module that multiple
17# compilers can include.
18
19patched = false
20patchStackTrace = ->
21 return if patched
22 patched = true
23
24 # Map of filenames -> functions that return a sourceMap string.
25 Module._sourceMaps = {}
26
27 # (Assigning to a property of the Module object in the normal module cache is
28 # unsuitable, because node deletes those objects from the cache if an
29 # exception is thrown in the module body.)
30
31 Error.prepareStackTrace = (err, stack) ->
32 sourceFiles = {}
33
34 getSourceMapping = (filename, line, column) ->
35 mapString = Module._sourceMaps[filename]?()
36 if mapString
37 sourceMap = sourceFiles[filename] ?= new SourceMapConsumer mapString
38 sourceMap.originalPositionFor {line, column}
39
40 frames = for frame in stack
41 break if frame.getFunction() is exports.runMain
42 " at #{formatSourcePosition frame, getSourceMapping}"
43
44 "#{err.name}: #{err.message ? ''}\n#{frames.join '\n'}\n"
45
46# Based on http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js
47# Modified to handle sourceMap
48formatSourcePosition = (frame, getSourceMapping) ->
49 fileName = undefined
50 fileLocation = ''
51
52 if frame.isNative()
53 fileLocation = "native"
54 else
55 if frame.isEval()
56 fileName = frame.getScriptNameOrSourceURL()
57 fileLocation = "#{frame.getEvalOrigin()}, " unless fileName
58 else
59 fileName = frame.getFileName()
60
61 fileName or= "<anonymous>"
62
63 line = frame.getLineNumber()
64 column = frame.getColumnNumber()
65
66 # Check for a sourceMap position
67 source = getSourceMapping fileName, line, column
68 fileLocation =
69 if source
70 "#{fileName}:#{source.line}:#{source.column}, <js>:#{line}:#{column}"
71 else
72 "#{fileName}:#{line}:#{column}"
73
74 functionName = frame.getFunctionName()
75 isConstructor = frame.isConstructor()
76 isMethodCall = not (frame.isToplevel() or isConstructor)
77
78 if isMethodCall
79 methodName = frame.getMethodName()
80 typeName = frame.getTypeName()
81
82 if functionName
83 tp = as = ''
84 if typeName and functionName.indexOf typeName
85 tp = "#{typeName}."
86 if methodName and functionName.indexOf(".#{methodName}") isnt functionName.length - methodName.length - 1
87 as = " [as #{methodName}]"
88
89 "#{tp}#{functionName}#{as} (#{fileLocation})"
90 else
91 "#{typeName}.#{methodName or '<anonymous>'} (#{fileLocation})"
92 else if isConstructor
93 "new #{functionName or '<anonymous>'} (#{fileLocation})"
94 else if functionName
95 "#{functionName} (#{fileLocation})"
96 else
97 fileLocation
98
99# Run JavaScript as a main program - resetting process.argv and module lookup paths
100exports.runMain = (csSource, jsSource, jsAst, filename) ->
101 mainModule = new Module '.'
102 mainModule.filename = process.argv[1] = filename
103
104 # Set it as the main module -- this is used for require.main
105 process.mainModule = mainModule
106
107 # Add the module to the cache
108 Module._cache[mainModule.filename] = mainModule
109
110 # Assign paths for node_modules loading
111 mainModule.paths = Module._nodeModulePaths path.dirname filename
112
113 runModule mainModule, jsSource, jsAst, filename
114
115runModule = (module, jsSource, jsAst, filename) ->
116 do patchStackTrace
117
118 Module._sourceMaps[filename] = -> "#{CoffeeScript.sourceMap jsAst, filename}"
119
120 module._compile jsSource, filename
121
122require.extensions['.coffee'] = (module, filename) ->
123 input = fs.readFileSync filename, 'utf8'
124 csAst = CoffeeScript.parse input, raw: yes
125 jsAst = CoffeeScript.compile csAst
126 js = CoffeeScript.js jsAst
127
128 runModule module, js, jsAst, filename