@@ -7,7 +7,7 @@ import Module = require('module')
7
7
import arg = require ( 'arg' )
8
8
import { diffLines } from 'diff'
9
9
import { Script } from 'vm'
10
- import { readFileSync , statSync } from 'fs'
10
+ import { readFileSync , statSync , realpathSync } from 'fs'
11
11
import { homedir } from 'os'
12
12
import { VERSION , TSError , parse , Register , register } from './index'
13
13
@@ -154,6 +154,7 @@ export function main (argv: string[]) {
154
154
}
155
155
156
156
const cwd = dir || process . cwd ( )
157
+ /** Unresolved. May point to a symlink, not realpath. May be missing file extension */
157
158
const scriptPath = args . _ . length ? resolve ( cwd , args . _ [ 0 ] ) : undefined
158
159
const state = new EvalState ( scriptPath || join ( cwd , EVAL_FILENAME ) )
159
160
@@ -251,7 +252,28 @@ function getCwd (dir?: string, scriptMode?: boolean, scriptPath?: string) {
251
252
throw new TypeError ( 'Script mode cannot be combined with `--dir`' )
252
253
}
253
254
254
- return dirname ( scriptPath )
255
+ // Use node's own resolution behavior to ensure we follow symlinks.
256
+ // scriptPath may omit file extension or point to a directory with or without package.json.
257
+ // This happens before we are registered, so we tell node's resolver to consider ts, tsx, and jsx files.
258
+ // In extremely rare cases, is is technically possible to resolve the wrong directory,
259
+ // because we do not yet know preferTsExts, jsx, nor allowJs.
260
+ // See also, justification why this will not happen in real-world situations:
261
+ // https://github.com/TypeStrong/ts-node/pull/1009#issuecomment-613017081
262
+ const exts = [ '.js' , '.jsx' , '.ts' , '.tsx' ]
263
+ const extsTemporarilyInstalled : string [ ] = [ ]
264
+ for ( const ext of exts ) {
265
+ if ( ! hasOwnProperty ( require . extensions , ext ) ) { // tslint:disable-line
266
+ extsTemporarilyInstalled . push ( ext )
267
+ require . extensions [ ext ] = function ( ) { } // tslint:disable-line
268
+ }
269
+ }
270
+ try {
271
+ return dirname ( require . resolve ( scriptPath ) )
272
+ } finally {
273
+ for ( const ext of extsTemporarilyInstalled ) {
274
+ delete require . extensions [ ext ] // tslint:disable-line
275
+ }
276
+ }
255
277
}
256
278
257
279
return dir
@@ -481,6 +503,11 @@ function isRecoverable (error: TSError) {
481
503
return error . diagnosticCodes . every ( code => RECOVERY_CODES . has ( code ) )
482
504
}
483
505
506
+ /** Safe `hasOwnProperty` */
507
+ function hasOwnProperty ( object : any , property : string ) : boolean {
508
+ return Object . prototype . hasOwnProperty . call ( object , property )
509
+ }
510
+
484
511
if ( require . main === module ) {
485
512
main ( process . argv . slice ( 2 ) )
486
513
}
0 commit comments