1 | var tsNode = require('ts-node')
|
2 |
|
3 | var fs = require('fs')
|
4 | var path = require('path')
|
5 | var os = require('os')
|
6 | var mkdirp = require('mkdirp')
|
7 | var rimraf = require('rimraf')
|
8 | var { resolveSync } = require('tsconfig')
|
9 |
|
10 | var getCompiledPath = require('./get-compiled-path')
|
11 | var tmpDir = '.ts-node'
|
12 |
|
13 | const fixPath = (p) => p.replace(/\\/g, '/').replace(/\$/g, '$$$$')
|
14 |
|
15 | var sourceMapSupportPath = require.resolve('source-map-support')
|
16 |
|
17 | var extensions = ['.ts', '.tsx']
|
18 | var empty = function () {}
|
19 | var cwd = process.cwd()
|
20 | var compilationInstanceStamp = Math.random().toString().slice(2)
|
21 |
|
22 | var originalJsHandler = require.extensions['.js']
|
23 |
|
24 | var extHandlers = {}
|
25 |
|
26 | function hasOwnProperty (object, property) {
|
27 | return Object.prototype.hasOwnProperty.call(object, property)
|
28 | }
|
29 |
|
30 | function getCwd(dir, scriptMode, scriptPath) {
|
31 | if (scriptMode) {
|
32 | if (!scriptPath) {
|
33 | throw new TypeError('Script mode must be used with a script name, e.g. `ts-node-dev -s <script.ts>`')
|
34 | }
|
35 |
|
36 | if (dir) {
|
37 | throw new TypeError('Script mode cannot be combined with `--dir`')
|
38 | }
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 | const exts = ['.js', '.jsx', '.ts', '.tsx']
|
48 | const extsTemporarilyInstalled = []
|
49 | for (const ext of exts) {
|
50 | if (!hasOwnProperty(require.extensions, ext)) {
|
51 | extsTemporarilyInstalled.push(ext)
|
52 | require.extensions[ext] = function() {}
|
53 | }
|
54 | }
|
55 | try {
|
56 | return path.dirname(require.resolve(scriptPath))
|
57 | } finally {
|
58 | for (const ext of extsTemporarilyInstalled) {
|
59 | delete require.extensions[ext]
|
60 | }
|
61 | }
|
62 | }
|
63 |
|
64 | return dir
|
65 | }
|
66 |
|
67 |
|
68 | var compiler = {
|
69 | allowJs: false,
|
70 | tsConfigPath: '',
|
71 | _errorCompileTimeout: 0,
|
72 | getCompilationId: function () {
|
73 | return compilationInstanceStamp
|
74 | },
|
75 | createCompiledDir: function () {
|
76 | var compiledDir = compiler.getCompiledDir()
|
77 | if (!fs.existsSync(compiledDir)) {
|
78 | mkdirp.sync(compiler.getCompiledDir())
|
79 | }
|
80 | },
|
81 | getCompiledDir: function () {
|
82 | return path.join(tmpDir, 'compiled').replace(/\\/g, '/')
|
83 | },
|
84 | getCompileReqFilePath: function () {
|
85 | return path.join(
|
86 | compiler.getCompiledDir(),
|
87 | compiler.getCompilationId() + '.req'
|
88 | )
|
89 | },
|
90 | getCompilerReadyFilePath: function () {
|
91 | return path
|
92 | .join(os.tmpdir(), 'ts-node-dev-ready-' + compilationInstanceStamp)
|
93 | .replace(/\\/g, '/')
|
94 | },
|
95 | getChildHookPath: function () {
|
96 | return path
|
97 | .join(os.tmpdir(), 'ts-node-dev-hook-' + compilationInstanceStamp + '.js')
|
98 | .replace(/\\/g, '/')
|
99 | },
|
100 | writeReadyFile: function () {
|
101 | var fileData = fs.writeFileSync(compiler.getCompilerReadyFilePath(), '')
|
102 | },
|
103 | writeChildHookFile: function (options) {
|
104 | var fileData = fs.readFileSync(
|
105 | path.join(__dirname, 'child-require-hook.js'),
|
106 | 'utf-8'
|
107 | )
|
108 | var compileTimeout = parseInt(options['compile-timeout'])
|
109 | if (compileTimeout) {
|
110 | fileData = fileData.replace('10000', compileTimeout.toString())
|
111 | }
|
112 | if (compiler.allowJs) {
|
113 | fileData = fileData.replace('allowJs = false', 'allowJs = true')
|
114 | }
|
115 | if (options['prefer-ts'] || options['prefer-ts-exts']) {
|
116 | fileData = fileData.replace('preferTs = false', 'preferTs = true')
|
117 | }
|
118 | if (options['exec-check']) {
|
119 | fileData = fileData.replace('execCheck = false', 'execCheck = true')
|
120 | }
|
121 |
|
122 | if (options['exit-child']) {
|
123 | fileData = fileData.replace('exitChild = false', 'exitChild = true')
|
124 | }
|
125 | if (options['ignore'] !== undefined) {
|
126 | var ignore = options['ignore']
|
127 | var ignoreVal =
|
128 | !ignore || ignore === 'false'
|
129 | ? 'false'
|
130 | : '[' +
|
131 | (Array.isArray(ignore) ? ignore : ignore.split(/, /))
|
132 | .map((ignore) => 'new RegExp("' + ignore + '")')
|
133 | .join(', ') +
|
134 | ']'
|
135 | fileData = fileData.replace(
|
136 | 'var ignore = [/node_modules/]',
|
137 | 'var ignore = ' + ignoreVal
|
138 | )
|
139 | }
|
140 | fileData = fileData.replace(
|
141 | 'var compilationId',
|
142 | 'var compilationId = "' + compiler.getCompilationId() + '"'
|
143 | )
|
144 | fileData = fileData.replace(
|
145 | 'var compiledDir',
|
146 | 'var compiledDir = "' + compiler.getCompiledDir() + '"'
|
147 | )
|
148 | fileData = fileData.replace(
|
149 | './get-compiled-path',
|
150 | fixPath(path.join(__dirname, 'get-compiled-path'))
|
151 | )
|
152 | fileData = fileData.replace(
|
153 | 'var readyFile',
|
154 | 'var readyFile = "' + compiler.getCompilerReadyFilePath() + '"'
|
155 | )
|
156 | fileData = fileData.replace(
|
157 | 'var sourceMapSupportPath',
|
158 | 'var sourceMapSupportPath = "' + fixPath(sourceMapSupportPath) + '"'
|
159 | )
|
160 | fileData = fileData.replace(
|
161 | 'var libPath',
|
162 | 'var libPath = "' + __dirname.replace(/\\/g, '\\\\') + '"'
|
163 | )
|
164 | fileData = fileData.replace(/__dirname/, '"' + fixPath(__dirname) + '"')
|
165 | fs.writeFileSync(compiler.getChildHookPath(), fileData)
|
166 | },
|
167 | clearErrorCompile: () => {
|
168 | clearTimeout(compiler._errorCompileTimeout)
|
169 | },
|
170 | init: function (options) {
|
171 | compiler.options = options
|
172 | var project = options['project']
|
173 | compiler.log = options.log
|
174 | compiler.tsConfigPath =
|
175 | resolveSync(cwd, typeof project === 'string' ? project : undefined) || ''
|
176 |
|
177 |
|
178 | require.extensions['.ts'] = empty
|
179 | require.extensions['.tsx'] = empty
|
180 | tmpDir = options['cache-directory']
|
181 | ? path.resolve(options['cache-directory'])
|
182 | : fs.mkdtempSync(path.join(os.tmpdir(), '.ts-node'))
|
183 |
|
184 | compiler.registerTsNode()
|
185 |
|
186 |
|
187 | rimraf.sync(compiler.getCompiledDir())
|
188 | compiler.createCompiledDir()
|
189 |
|
190 | var allowJsEnabled = require.extensions['.js'] !== originalJsHandler
|
191 | if (allowJsEnabled) {
|
192 | compiler.allowJs = true
|
193 |
|
194 | extensions.push('.js')
|
195 | }
|
196 |
|
197 | compiler.writeChildHookFile(options)
|
198 | },
|
199 | registerTsNode: function () {
|
200 | var options = compiler.options
|
201 | extensions.forEach(function (ext) {
|
202 | require.extensions[ext] = originalJsHandler
|
203 | })
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 | var compilerOptionsArg =
|
223 | options['compilerOptions'] || options['compiler-options']
|
224 | var compilerOptions
|
225 | if (compilerOptionsArg) {
|
226 | try {
|
227 | compilerOptions = JSON.parse(compilerOptionsArg)
|
228 | } catch (e) {
|
229 | console.log(
|
230 | 'Could not parse compilerOptions',
|
231 | options['compilerOptions']
|
232 | )
|
233 | console.log(e)
|
234 | }
|
235 | }
|
236 | let ignore = options['ignore'] || process.env['TS_NODE_IGNORE']
|
237 | if (ignore && typeof ignore === 'string') {
|
238 | ignore = [ignore]
|
239 | }
|
240 |
|
241 | const scriptPath = options._.length ? path.resolve(cwd, options._[0]) : undefined
|
242 |
|
243 | var DEFAULTS = tsNode.DEFAULTS
|
244 |
|
245 | try {
|
246 | compiler.service = tsNode.register({
|
247 |
|
248 | dir: getCwd(options['dir'], options['script-mode'], scriptPath),
|
249 | scope: options['dir'] || DEFAULTS.scope,
|
250 | emit: options['emit'] || DEFAULTS.emit,
|
251 | files: options['files'] || DEFAULTS.files,
|
252 | pretty: options['pretty'] || DEFAULTS.pretty,
|
253 | transpileOnly: options['transpile-only'] || DEFAULTS.transpileOnly,
|
254 | ignore: options['ignore']
|
255 | ? tsNode.split(options['ignore']) || options['ignore']
|
256 | : DEFAULTS.ignore,
|
257 | preferTsExts:
|
258 | options['prefer-ts-exts'] ||
|
259 | options['prefer-ts'] ||
|
260 | DEFAULTS.preferTsExts,
|
261 | logError: options['log-error'] || DEFAULTS.logError,
|
262 | project: options['project'],
|
263 | skipProject: options['skip-project'],
|
264 | skipIgnore: options['skip-ignore'],
|
265 | compiler: options['compiler'] || DEFAULTS.compiler,
|
266 | compilerHost: options['compiler-host'] || DEFAULTS.compilerHost,
|
267 | ignoreDiagnostics: options['ignore-diagnostics'],
|
268 | compilerOptions: tsNode.parse(options['compiler-options']),
|
269 | })
|
270 | } catch (e) {
|
271 | console.log(e)
|
272 | return
|
273 | }
|
274 | extensions.forEach(function (ext) {
|
275 | extHandlers[ext] = require.extensions[ext]
|
276 | require.extensions[ext] = originalJsHandler
|
277 | })
|
278 | },
|
279 | compileChanged: function (fileName) {
|
280 | var ext = path.extname(fileName)
|
281 | if (extensions.indexOf(ext) < 0) return
|
282 | try {
|
283 | var code = fs.readFileSync(fileName, 'utf-8')
|
284 | compiler.compile({
|
285 | code: code,
|
286 | compile: fileName,
|
287 | compiledPath: getCompiledPath(
|
288 | code,
|
289 | fileName,
|
290 | compiler.getCompiledDir()
|
291 | ),
|
292 | })
|
293 | } catch (e) {
|
294 | console.error(e)
|
295 | }
|
296 | },
|
297 | compile: function (params) {
|
298 | var fileName = params.compile
|
299 | var code = fs.readFileSync(fileName, 'utf-8')
|
300 | var compiledPath = params.compiledPath
|
301 | function writeCompiled(code, filename) {
|
302 | fs.writeFileSync(compiledPath, code)
|
303 | fs.writeFileSync(compiledPath + '.done', '')
|
304 | }
|
305 | if (fs.existsSync(compiledPath)) {
|
306 | return
|
307 | }
|
308 | var starTime = new Date().getTime()
|
309 | var m = {
|
310 | _compile: writeCompiled,
|
311 | }
|
312 | const _compile = () => {
|
313 | var ext = path.extname(fileName)
|
314 | var extHandler = extHandlers[ext] || require.extensions[ext]
|
315 | extHandler(m, fileName)
|
316 | m._compile(code, fileName)
|
317 | compiler.log.debug(
|
318 | fileName,
|
319 | 'compiled in',
|
320 | new Date().getTime() - starTime,
|
321 | 'ms'
|
322 | )
|
323 | }
|
324 | try {
|
325 | _compile()
|
326 | } catch (e) {
|
327 | console.log('Compilation error in', fileName)
|
328 | const errorCode =
|
329 | 'throw ' + 'new Error(' + JSON.stringify(e.message) + ')' + ';'
|
330 | writeCompiled(errorCode)
|
331 |
|
332 |
|
333 |
|
334 | setTimeout(() => {
|
335 | compiler.registerTsNode()
|
336 | })
|
337 |
|
338 | if (!compiler.options['error-recompile']) {
|
339 | return
|
340 | }
|
341 | const timeoutMs =
|
342 | parseInt(process.env.TS_NODE_DEV_ERROR_RECOMPILE_TIMEOUT) || 5000
|
343 | const errorHandler = () => {
|
344 | clearTimeout(compiler._errorCompileTimeout)
|
345 | compiler._errorCompileTimeout = setTimeout(() => {
|
346 | try {
|
347 | _compile()
|
348 | compiler.restart(fileName)
|
349 | } catch (e) {
|
350 | compiler.registerTsNode()
|
351 | errorHandler()
|
352 | }
|
353 | }, timeoutMs)
|
354 | }
|
355 |
|
356 | errorHandler()
|
357 | }
|
358 | },
|
359 | }
|
360 |
|
361 | module.exports = compiler
|