Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: TypeStrong/ts-node
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.3.0
Choose a base ref
...
head repository: TypeStrong/ts-node
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v4.0.0
Choose a head ref

Commits on Aug 9, 2017

  1. Copy the full SHA
    1385d47 View commit details

Commits on Aug 28, 2017

  1. Verified

    This commit was signed with the committer’s verified signature.
    addaleax Anna Henningsen
    Copy the full SHA
    1f4123b View commit details

Commits on Sep 4, 2017

  1. Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    BridgeAR Ruben Bridgewater
    Copy the full SHA
    2ee9575 View commit details

Commits on Sep 11, 2017

  1. Migrate to @types packages (#413)

    devoto13 authored and blakeembrey committed Sep 11, 2017

    Unverified

    The email in this signature doesn’t match the committer email.
    Copy the full SHA
    d80999d View commit details
  2. Include .jsx files with allowJs (#423)

    rharriso authored and blakeembrey committed Sep 11, 2017

    Verified

    This commit was signed with the committer’s verified signature.
    targos Michaël Zasso
    Copy the full SHA
    5e12d33 View commit details

Commits on Sep 12, 2017

  1. Copy the full SHA
    46f6361 View commit details
  2. Copy the full SHA
    daa8c01 View commit details

Commits on Sep 21, 2017

  1. Support custom transformers (#429)

    kriszyp authored and blakeembrey committed Sep 21, 2017
    Copy the full SHA
    1313726 View commit details

Commits on Oct 11, 2017

  1. Copy the full SHA
    c01709c View commit details
  2. Copy the full SHA
    a62b3b6 View commit details
  3. Copy the full SHA
    6dd8867 View commit details

Commits on Oct 25, 2017

  1. Update chalk imports (#451)

    rpd10 authored and blakeembrey committed Oct 25, 2017
    Copy the full SHA
    3718bf1 View commit details

Commits on Oct 27, 2017

  1. Copy the full SHA
    88e9a9d View commit details

Commits on Oct 30, 2017

  1. Copy the full SHA
    1d630f9 View commit details

Commits on Nov 16, 2017

  1. Copy the full SHA
    d02f8f1 View commit details
  2. Copy the full SHA
    ce1eceb View commit details
  3. Copy the full SHA
    e8bdbed View commit details
  4. Copy the full SHA
    e7591c7 View commit details

Commits on Nov 18, 2017

  1. Copy the full SHA
    c57ded2 View commit details

Commits on Nov 29, 2017

  1. Copy the full SHA
    d4a1c94 View commit details

Commits on Dec 10, 2017

  1. Copy the full SHA
    85c139a View commit details
  2. Forward signals correctly (#419)

    stelcheck authored and blakeembrey committed Dec 10, 2017
    Copy the full SHA
    be7895c View commit details
  3. 4.0.0

    blakeembrey committed Dec 10, 2017
    Copy the full SHA
    94cba9c View commit details
Showing with 281 additions and 175 deletions.
  1. +0 −1 .gitignore
  2. +7 −7 .travis.yml
  3. +12 −21 README.md
  4. +24 −13 package.json
  5. +0 −1 register.js
  6. +1 −0 register/index.js
  7. +3 −0 register/type-check.js
  8. +29 −30 src/_bin.ts
  9. +29 −15 src/bin.ts
  10. +94 −31 src/index.spec.ts
  11. +60 −49 src/index.ts
  12. +7 −0 tests/allow-js/with-jsx.jsx
  13. +12 −0 tests/signals.ts
  14. +1 −4 tests/tsconfig.json
  15. +2 −3 tsconfig.json
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -4,4 +4,3 @@ coverage/
.DS_Store
npm-debug.log
dist/
typings/
14 changes: 7 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -15,14 +15,14 @@ script:
- npm run test-cov

env:
- NODE=6 TYPESCRIPT=typescript@1.6
- NODE=6 TYPESCRIPT=typescript@1.7
- NODE=6 TYPESCRIPT=typescript@1.8
- NODE=6 TYPESCRIPT=typescript@2.0
- NODE=6 TYPESCRIPT=typescript@2.1
- NODE=8 TYPESCRIPT=typescript@1.6
- NODE=8 TYPESCRIPT=typescript@1.7
- NODE=8 TYPESCRIPT=typescript@1.8
- NODE=8 TYPESCRIPT=typescript@2.0
- NODE=8 TYPESCRIPT=typescript@2.1
- NODE=4 TYPESCRIPT=typescript@latest
- NODE=6 TYPESCRIPT=typescript@latest
- NODE=6 TYPESCRIPT=typescript@next
- NODE=8 TYPESCRIPT=typescript@latest
- NODE=8 TYPESCRIPT=typescript@next

node_js:
- stable
33 changes: 12 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -46,12 +46,18 @@ echo "console.log('Hello, world!')" | ts-node

![TypeScript REPL](https://github.com/TypeStrong/ts-node/raw/master/screenshot.png)

### Programmatic

You can require `ts-node` and register the loader for future requires by using `require('ts-node').register({ /* options */ })`. You can also use the shortcut files `node -r ts-node/register` or `node -r ts-node/register/type-check` depending on your preferences.

### Mocha

```sh
mocha --compilers ts:ts-node/register,tsx:ts-node/register [...args]
mocha --require ts-node/register --watch-extensions ts,tsx "test/**/*.{ts,tsx}" [...args]
```

**Note:** `--watch-extensions` is only used in `--watch` mode.

### Tape

```sh
@@ -73,7 +79,7 @@ gulp

## Loading `tsconfig.json`

**Typescript Node** uses `tsconfig.json` automatically, use `-n` to skip loading `tsconfig.json`.
**Typescript Node** uses `tsconfig.json` automatically, use `--no-project` to skip loading `tsconfig.json`.

**NOTE**: You can use `ts-node` together with [tsconfig-paths](https://www.npmjs.com/package/tsconfig-paths) to load modules according to the `paths` section in `tsconfig.json`.

@@ -87,31 +93,16 @@ You can set options by passing them in before the script.
ts-node --compiler ntypescript --project src --ignoreWarnings 2304 hello-world.ts
```

* **--project, -P** Path to load TypeScript configuration from (JSON file, a directory containing `tsconfig.json`, or `false` to disable) (also `process.env.TS_NODE_PROJECT`)
* **--project, -P** Path to load TypeScript configuration from (JSON file, a directory containing `tsconfig.json`, or `--no-project`/`false` to disable) (also `process.env.TS_NODE_PROJECT`)
* **--compiler, -C** Use a custom, require-able TypeScript compiler compatible with `typescript@>=1.5.0-alpha` (also `process.env.TS_NODE_COMPILER`)
* **--ignore** Specify an array of regular expression strings for `ts-node` to skip compiling as TypeScript (defaults to `/node_modules/`, `false` to disable) (also `process.env.TS_NODE_IGNORE`)
* **--ignore** Specify an array of regular expression strings for `ts-node` to skip compiling as TypeScript (defaults to `/node_modules/`, `--no-ignore`/`false` to disable) (also `process.env.TS_NODE_IGNORE`)
* **--ignoreWarnings, -I** Set an array of TypeScript diagnostic codes to ignore (also `process.env.TS_NODE_IGNORE_WARNINGS`)
* **--disableWarnings, -D** Ignore all TypeScript errors (also `process.env.TS_NODE_DISABLE_WARNINGS`)
* **--compilerOptions, -O** Set compiler options using JSON (E.g. `--compilerOptions '{"target":"es6"}'`) (also `process.env.TS_NODE_COMPILER_OPTIONS`)
* **--fast, -F** Use TypeScript's `transpileModule` mode (no type checking, but faster compilation) (also `process.env.TS_NODE_FAST`)
* **--type-check** Use TypeScript with type checking (also `process.env.TS_NODE_TYPE_CHECK`)
* **--no-cache** Skip hitting the compiled JavaScript cache (also `process.env.TS_NODE_CACHE`)
* **--cache-directory** Configure the TypeScript cache directory (also `process.env.TS_NODE_CACHE_DIRECTORY`)

## Programmatic Usage

```js
require('ts-node').register({ /* options */ })

// Or using the shortcut file.
require('ts-node/register')
```

This will register the TypeScript compiler for "on the fly" compilation support of `.ts` and `.tsx` files during the run
of the script. From here you can use `require` to bring in modules from TypeScript files:

```js
var someModule = require('path_to_a_typescript_file');
```
Additionally, the `transformers` option may be provided when programmatically registering `ts-node` to specify custom TypeScript transformers.

## License

37 changes: 24 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
{
"name": "ts-node",
"version": "3.3.0",
"version": "4.0.0",
"description": "TypeScript execution environment and REPL for node",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"ts-node": "dist/bin.js",
"_ts-node": "dist/_bin.js"
},
"files": [
"dist/",
"typings.js",
"register.js",
"LICENSE"
],
"scripts": {
"lint": "tslint \"src/**/*.ts\"",
"lint": "tslint \"src/**/*.ts\" --project tsconfig.json --type-check",
"clean": "rimraf dist",
"tsc": "tsc",
"build": "npm run clean && npm run tsc",
"test-spec": "mocha dist/**/*.spec.js -R spec --bail",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- \"dist/**/*.spec.js\" -R spec --bail",
"test": "npm run build && npm run lint && npm run test-cov",
"prepublish": "typings install && npm run build"
"prepublish": "npm run build"
},
"engines": {
"node": ">=4.2.0"
@@ -49,29 +49,40 @@
},
"homepage": "https://github.com/TypeStrong/ts-node",
"devDependencies": {
"@types/react": "^15.0.38",
"@types/chai": "^4.0.4",
"@types/mocha": "^2.2.42",
"@types/proxyquire": "^1.3.28",
"@types/react": "^16.0.2",
"@types/semver": "^5.3.34",
"chai": "^4.0.1",
"istanbul": "^0.4.0",
"mocha": "^3.0.0",
"mocha": "^4.0.0",
"ntypescript": "^1.201507091536.1",
"proxyquire": "^1.7.2",
"react": "^15.6.1",
"react": "^16.0.0",
"rimraf": "^2.5.4",
"semver": "^5.1.0",
"tslint": "^5.0.0",
"tslint-config-standard": "^6.0.1",
"typescript": "^2.4.1",
"typings": "^2.0.0"
"tslint-config-standard": "^7.0.0",
"typescript": "^2.4.1"
},
"dependencies": {
"@types/arrify": "^1.0.1",
"@types/diff": "^3.2.1",
"@types/minimist": "^1.2.0",
"@types/mkdirp": "^0.5.0",
"@types/node": "^8.0.27",
"@types/source-map-support": "^0.4.0",
"@types/v8flags": "types/npm-v8flags#de224ae1cd5fd7dbb4e7158a6cc7a29e5315930d",
"@types/yn": "types/npm-yn#ca75f6c82940fae6a06fb41d2d37a6aa9b4ea8e9",
"arrify": "^1.0.0",
"chalk": "^2.0.0",
"chalk": "^2.3.0",
"diff": "^3.1.0",
"make-error": "^1.1.1",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"source-map-support": "^0.4.0",
"tsconfig": "^6.0.0",
"source-map-support": "^0.5.0",
"tsconfig": "^7.0.0",
"v8flags": "^3.0.0",
"yn": "^2.0.0"
}
1 change: 0 additions & 1 deletion register.js

This file was deleted.

1 change: 1 addition & 0 deletions register/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require('../').register()
3 changes: 3 additions & 0 deletions register/type-check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require('../').register({
typeCheck: true
})
59 changes: 29 additions & 30 deletions src/_bin.ts
Original file line number Diff line number Diff line change
@@ -4,15 +4,15 @@ import { inspect } from 'util'
import arrify = require('arrify')
import Module = require('module')
import minimist = require('minimist')
import chalk = require('chalk')
import chalk from 'chalk'
import { diffLines } from 'diff'
import { Script } from 'vm'
import { register, VERSION, getFile, fileExists, TSError, parse, printError } from './index'

interface Argv {
eval?: string
print?: string
fast?: boolean
typeCheck?: boolean
cache?: boolean
cacheDirectory?: string
version?: boolean
@@ -22,26 +22,24 @@ interface Argv {
require?: string | string[]
ignore?: boolean | string | string[]
ignoreWarnings?: string | string[]
disableWarnings?: boolean
compilerOptions?: any
_: string[]
}

const strings = ['eval', 'print', 'compiler', 'project', 'ignoreWarnings', 'require', 'cacheDirectory', 'ignore']
const booleans = ['help', 'fast', 'version', 'disableWarnings', 'cache']
const booleans = ['help', 'typeCheck', 'version', 'cache']

const aliases: { [key: string]: string[] } = {
help: ['h'],
fast: ['F'],
version: ['v'],
eval: ['e'],
print: ['p'],
project: ['P'],
compiler: ['C'],
require: ['r'],
typeCheck: ['type-check'],
cacheDirectory: ['cache-directory'],
ignoreWarnings: ['I', 'ignore-warnings'],
disableWarnings: ['D', 'disable-warnings'],
compilerOptions: ['O', 'compiler-options']
}

@@ -102,8 +100,7 @@ const argv = minimist<Argv>(process.argv.slice(2, stop), {
alias: aliases,
default: {
cache: null,
fast: null,
disableWarnings: null
typeCheck: null
}
})

@@ -118,7 +115,6 @@ Options:
-r, --require [path] Require a node module for execution
-C, --compiler [name] Specify a custom TypeScript compiler
-I, --ignoreWarnings [code] Ignore TypeScript warnings by diagnostic code
-D, --disableWarnings Ignore every TypeScript warning
-P, --project [path] Path to TypeScript project (or \`false\`)
-O, --compilerOptions [opts] JSON object to merge with compiler options
-F, --fast Run TypeScript compilation in transpile mode
@@ -131,21 +127,20 @@ Options:
}

const cwd = process.cwd()
const code = argv.eval == null ? argv.print : argv.eval
const code = argv.eval === undefined ? argv.print : argv.eval
const isEvalScript = typeof argv.eval === 'string' || !!argv.print // Minimist struggles with empty strings.
const isEval = isEvalScript || stop === process.argv.length
const isPrinted = argv.print != null
const isPrinted = argv.print !== undefined

// Register the TypeScript compiler instance.
const service = register({
fast: argv.fast,
typeCheck: argv.typeCheck,
cache: argv.cache,
cacheDirectory: argv.cacheDirectory,
compiler: argv.compiler,
project: argv.project,
ignore: argv.ignore,
ignoreWarnings: argv.ignoreWarnings,
disableWarnings: argv.disableWarnings,
compilerOptions: parse(argv.compilerOptions),
getFile: isEval ? getFileEval : getFile,
fileExists: isEval ? fileExistsEval : fileExists
@@ -198,7 +193,7 @@ if (isEvalScript) {
function evalAndExit (code: string, isPrinted: boolean) {
const module = new Module(EVAL_FILENAME)
module.filename = EVAL_FILENAME
module.paths = Module._nodeModulePaths(cwd)
module.paths = (Module as any)._nodeModulePaths(cwd)

;(global as any).__filename = EVAL_FILENAME
;(global as any).__dirname = cwd
@@ -222,8 +217,6 @@ function evalAndExit (code: string, isPrinted: boolean) {
if (isPrinted) {
console.log(typeof result === 'string' ? result : inspect(result))
}

process.exit(0)
}

/**
@@ -252,17 +245,18 @@ function _eval (input: string, context: any) {
EVAL_INSTANCE.output = output
}

let result: any

for (const change of changes) {
if (change.added) {
const script = new Script(change.value, EVAL_FILENAME)
return changes.reduce((result, change) => {
return change.added ? exec(change.value, EVAL_FILENAME, context) : result
}, undefined)
}

result = script.runInNewContext(context)
}
}
/**
* Execute some code.
*/
function exec (code: string, filename: string, context: any) {
const script = new Script(code, { filename: filename })

return result
return script.runInNewContext(context)
}

/**
@@ -277,12 +271,17 @@ function startRepl () {
useGlobal: false
})

// Hard fix for TypeScript forcing `Object.defineProperty(exports, ...)`.
appendEval('exports = module.exports\n')

// Bookmark the point where we should reset the REPL state.
const reset = appendEval('')
const resetEval = appendEval('')

function reset () {
resetEval()

// Hard fix for TypeScript forcing `Object.defineProperty(exports, ...)`.
exec('exports = module.exports', EVAL_FILENAME, (repl as any).context)
}

reset()
repl.on('reset', reset)

repl.defineCommand('type', {
@@ -322,7 +321,7 @@ function replEval (code: string, context: any, _filename: string, callback: (err
} catch (error) {
if (error instanceof TSError) {
// Support recoverable compilations using >= node 6.
if (typeof Recoverable === 'function' && isRecoverable(error)) {
if (Recoverable && isRecoverable(error)) {
err = new Recoverable(error)
} else {
err = printError(error)
44 changes: 29 additions & 15 deletions src/bin.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import { join } from 'path'
import v8flags = require('v8flags')

const argv = process.argv.slice(2)
const signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGWINCH']

v8flags(function (err, v8flags) {
if (err) {
@@ -18,6 +19,7 @@ v8flags(function (err, v8flags) {

const knownFlags = v8flags.concat([
'debug',
'inspect',
'--debug',
'--debug-brk',
'--inspect',
@@ -29,18 +31,16 @@ v8flags(function (err, v8flags) {
'--trace-deprecation',
'--allow-natives-syntax',
'--perf-basic-prof',
'--preserve-symlinks'
'--preserve-symlinks',
'--expose-gc',
'--expose-http2'
])

for (let i = 0; i < argv.length; i++) {
const arg = argv[i]
const flag = arg.split('=', 1)[0]

if (flag === '-d') {
nodeArgs.push('--debug')
} else if (flag === '-gc') {
nodeArgs.push('--expose-gc')
} else if (knownFlags.indexOf(flag) > -1) {
if (knownFlags.indexOf(flag) > -1) {
nodeArgs.push(arg)
} else if (/^-/.test(flag)) {
scriptArgs.push(arg)
@@ -54,16 +54,30 @@ v8flags(function (err, v8flags) {
const proc = spawn(
process.execPath,
nodeArgs.concat(join(__dirname, '_bin.js'), scriptArgs),
{ stdio: 'inherit' }
{
// We need to run in detached mode so to avoid
// automatic propagation of signals to the child process.
// This is necessary because by default, keyboard interrupts
// are propagated to the process tree, but `kill` is not.
//
// See: https://nodejs.org/api/child_process.html#child_process_options_detached
detached: true,
stdio: 'inherit'
}
)

proc.on('exit', function (code: number, signal: string) {
process.on('exit', function () {
if (signal) {
process.kill(process.pid, signal)
} else {
process.exit(code)
}
})
// Ignore signals, and instead forward them to the child process.
signals.forEach(signal => process.on(signal, () => proc.kill(signal)))

// On spawned close, exit this process with the same code.
proc.on('close', (code: number, signal: string) => {
if (signal) {
process.kill(process.pid, signal)
} else {
process.exit(code)
}
})

// If this process exits, kill the child first.
process.on('exit', () => proc.kill())
})
125 changes: 94 additions & 31 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from 'chai'
import { exec } from 'child_process'
import { exec, spawn } from 'child_process'
import { join } from 'path'
import semver = require('semver')
import ts = require('typescript')
@@ -20,6 +20,43 @@ describe('ts-node', function () {
})

describe('cli', function () {
this.slow(1000)

it('should forward signals to the child process', function (done) {
this.slow(5000)

// We use `spawn` instead because apparently TravisCI
// does not let subprocesses be killed when ran under `sh`
//
// See: https://github.com/travis-ci/travis-ci/issues/704#issuecomment-328278149
const proc = spawn('node', [
EXEC_PATH,
'--project',
testDir,
'tests/signals'
], {
shell: '/bin/bash'
})

let stdout = ''
proc.stdout.on('data', (data) => stdout += data.toString())

let stderr = ''
proc.stderr.on('data', (data) => stderr += data.toString())

proc.on('exit', function (code) {
expect(stderr).to.equal('')
expect(stdout).to.equal('exited fine')
expect(code).to.equal(0)

return done()
})

// Leave enough time for node to fully start
// the process, then send a signal
setTimeout(() => proc.kill('SIGINT'), 2000)
})

it('should execute cli', function (done) {
exec(`${BIN_EXEC} tests/hello-world`, function (err, stdout) {
expect(err).to.equal(null)
@@ -29,6 +66,17 @@ describe('ts-node', function () {
})
})

it('should register via cli', function (done) {
exec(`node -r ../register hello-world.ts`, {
cwd: testDir
}, function (err, stdout) {
expect(err).to.equal(null)
expect(stdout).to.equal('Hello, world!\n')

return done()
})
})

it('should execute cli with absolute path', function (done) {
exec(`${BIN_EXEC} "${join(testDir, 'hello-world')}"`, function (err, stdout) {
expect(err).to.equal(null)
@@ -63,6 +111,22 @@ describe('ts-node', function () {
}
)
})

it('should include jsx when `allow-js` true', function (done) {
exec(
[
BIN_EXEC,
'-O "{\\\"allowJs\\\":true}"',
'-p "import { Foo2 } from \'./tests/allow-js/with-jsx\'; Foo2.sayHi()"'
].join(' '),
function (err, stdout) {
expect(err).to.equal(null)
expect(stdout).to.equal('hello world\n')

return done()
}
)
})
}

it('should eval code', function (done) {
@@ -78,7 +142,11 @@ describe('ts-node', function () {
})

it('should throw errors', function (done) {
exec(`${BIN_EXEC} -e "import * as m from './tests/module';console.log(m.example(123))"`, function (err) {
exec(`${BIN_EXEC} --type-check -e "import * as m from './tests/module';console.log(m.example(123))"`, function (err) {
if (err === null) {
return done('Command was expected to fail, but it succeeded.')
}

expect(err.message).to.match(new RegExp(
// Node 0.10 can not override the `lineOffset` option.
'\\[eval\\]\\.ts \\(1,59\\): Argument of type \'(?:number|123)\' ' +
@@ -91,8 +159,12 @@ describe('ts-node', function () {

it('should be able to ignore errors', function (done) {
exec(
`${BIN_EXEC} --ignoreWarnings 2345 -e "import * as m from './tests/module';console.log(m.example(123))"`,
`${BIN_EXEC} --type-check --ignoreWarnings 2345 -e "import * as m from './tests/module';console.log(m.example(123))"`,
function (err) {
if (err === null) {
return done('Command was expected to fail, but it succeeded.')
}

expect(err.message).to.match(
/TypeError: (?:(?:undefined|foo\.toUpperCase) is not a function|.*has no method \'toUpperCase\')/
)
@@ -102,28 +174,12 @@ describe('ts-node', function () {
)
})

it('should be able to disable warnings from environment', function (done) {
exec(
`${BIN_EXEC} tests/compiler-error`,
{
env: {
PATH: process.env.PATH,
HOME: process.env.HOME,
TS_NODE_DISABLE_WARNINGS: true
}
},
function (err) {
expect(err.message).to.match(
/TypeError: (?:(?:undefined|str\.toUpperCase) is not a function|.*has no method \'toUpperCase\')/
)

return done()
it('should work with source maps', function (done) {
exec(`${BIN_EXEC} --type-check tests/throw`, function (err) {
if (err === null) {
return done('Command was expected to fail, but it succeeded.')
}
)
})

it('should work with source maps', function (done) {
exec(`${BIN_EXEC} tests/throw`, function (err) {
expect(err.message).to.contain([
`${join(__dirname, '../tests/throw.ts')}:3`,
' bar () { throw new Error(\'this is a demo\') }',
@@ -135,21 +191,28 @@ describe('ts-node', function () {
})
})

it('eval should work with source maps', function (done) {
exec(`${BIN_EXEC} -p "import './tests/throw'"`, function (err) {
it.skip('eval should work with source maps', function (done) {
exec(`${BIN_EXEC} --type-check -p "import './tests/throw'"`, function (err) {
if (err === null) {
return done('Command was expected to fail, but it succeeded.')
}

expect(err.message).to.contain([
`${join(__dirname, '../tests/throw.ts')}:3`,
' bar () { throw new Error(\'this is a demo\') }',
' ^',
'Error: this is a demo'
' ^'
].join('\n'))

return done()
})
})

it('should ignore all warnings', function (done) {
exec(`${BIN_EXEC} -D -p "x"`, function (err) {
it('should use transpile mode by default', function (done) {
exec(`${BIN_EXEC} -p "x"`, function (err) {
if (err === null) {
return done('Command was expected to fail, but it succeeded.')
}

expect(err.message).to.contain('ReferenceError: x is not defined')

return done()
@@ -263,10 +326,10 @@ describe('ts-node', function () {
let compiled: string

before(function () {
require.extensions['.tsx'] = (m, fileName) => {
require.extensions['.tsx'] = (m: any, fileName) => {
const _compile = m._compile

m._compile = (code, fileName) => {
m._compile = (code: string, fileName: string) => {
compiled = code
return _compile.call(this, code, fileName)
}
109 changes: 60 additions & 49 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ import { relative, basename, extname, resolve, dirname, join } from 'path'
import { writeFileSync, readFileSync, statSync } from 'fs'
import { EOL, tmpdir, homedir } from 'os'
import sourceMapSupport = require('source-map-support')
import chalk = require('chalk')
import chalk from 'chalk'
import mkdirp = require('mkdirp')
import crypto = require('crypto')
import yn = require('yn')
@@ -55,25 +55,25 @@ export const VERSION = pkg.version
* Registration options.
*/
export interface Options {
fast?: boolean | null
cache?: boolean | null
typeCheck?: boolean
cache?: boolean
cacheDirectory?: string
compiler?: string
project?: boolean | string
ignore?: boolean | string | string[]
ignoreWarnings?: number | string | Array<number | string>
disableWarnings?: boolean | null
getFile?: (path: string) => string
fileExists?: (path: string) => boolean
compilerOptions?: any
transformers?: TS.CustomTransformers
}

/**
* Track the project information.
*/
interface Cache {
contents: { [path: string]: string }
versions: { [path: string]: number }
versions: { [path: string]: number | undefined }
outputs: { [path: string]: string }
}

@@ -93,13 +93,12 @@ const DEFAULTS = {
fileExists,
cache: yn(process.env['TS_NODE_CACHE'], { default: true }),
cacheDirectory: process.env['TS_NODE_CACHE_DIRECTORY'],
disableWarnings: yn(process.env['TS_NODE_DISABLE_WARNINGS']),
compiler: process.env['TS_NODE_COMPILER'],
compilerOptions: parse(process.env['TS_NODE_COMPILER_OPTIONS']),
project: process.env['TS_NODE_PROJECT'],
ignore: split(process.env['TS_NODE_IGNORE']),
ignoreWarnings: split(process.env['TS_NODE_IGNORE_WARNINGS']),
fast: yn(process.env['TS_NODE_FAST'])
typeCheck: yn(process.env['TS_NODE_TYPE_CHECK'])
}

/**
@@ -123,6 +122,24 @@ export function normalizeSlashes (value: string): string {
return value.replace(/\\/g, '/')
}

/**
* TypeScript diagnostics error.
*/
export class TSError extends BaseError {

name = 'TSError'

constructor (public diagnostics: TSDiagnostic[]) {
super(
`⨯ Unable to compile TypeScript\n${diagnostics.map(x => x.message).join('\n')}`
)
}

}

/**
* Return type for registering `ts-node`.
*/
export interface Register {
cwd: string
extensions: string[]
@@ -150,12 +167,11 @@ export function register (options: Options = {}): Register {
const ignoreWarnings = arrify(
options.ignoreWarnings || DEFAULTS.ignoreWarnings || []
).concat(emptyFileListWarnings).map(Number)
const disableWarnings = !!(options.disableWarnings == null ? DEFAULTS.disableWarnings : options.disableWarnings)
const getFile = options.getFile || DEFAULTS.getFile
const fileExists = options.fileExists || DEFAULTS.fileExists
const shouldCache = !!(options.cache == null ? DEFAULTS.cache : options.cache)
const fast = !!(options.fast == null ? DEFAULTS.fast : options.fast)
const project = options.project || DEFAULTS.project
const shouldCache = !!(options.cache === undefined ? DEFAULTS.cache : options.cache)
const typeCheck = !!(options.typeCheck === undefined ? DEFAULTS.typeCheck : options.typeCheck)
const project = options.project === undefined ? DEFAULTS.project : options.project
const cacheDirectory = options.cacheDirectory || DEFAULTS.cacheDirectory || getTmpDir()
const compilerOptions = Object.assign({}, DEFAULTS.compilerOptions, options.compilerOptions)
const originalJsHandler = require.extensions['.js']
@@ -187,12 +203,12 @@ export function register (options: Options = {}): Register {
const cwd = process.cwd()
const ts: typeof TS = require(compiler)
const config = readConfig(compilerOptions, project, cwd, ts)
const configDiagnostics = filterDiagnostics(config.errors, ignoreWarnings, disableWarnings)
const configDiagnostics = filterDiagnostics(config.errors, ignoreWarnings)
const extensions = ['.ts', '.tsx']

const cachedir = join(
resolve(cwd, cacheDirectory),
getCompilerDigest({ version: ts.version, fast, ignoreWarnings, disableWarnings, config, compiler })
getCompilerDigest({ version: ts.version, typeCheck, ignoreWarnings, config, compiler })
)

// Render the configuration errors and exit the script.
@@ -203,20 +219,21 @@ export function register (options: Options = {}): Register {
// Enable `allowJs` when flag is set.
if (config.options.allowJs) {
extensions.push('.js')
extensions.push('.jsx')
}

// Add all files into the file hash.
for (const fileName of config.fileNames) {
if (/\.d\.ts$/.test(fileName)) {
cache.versions[fileName] = 1
}
cache.versions[fileName] = 1
}

/**
* Get the extension for a transpiled file.
*/
function getExtension (fileName: string) {
if (config.options.jsx === ts.JsxEmit.Preserve && extname(fileName) === '.tsx') {
const ext = extname(fileName)

if (config.options.jsx === ts.JsxEmit.Preserve && (ext === '.tsx' || ext === '.jsx')) {
return '.jsx'
}

@@ -230,11 +247,12 @@ export function register (options: Options = {}): Register {
const result = ts.transpileModule(code, {
fileName,
compilerOptions: config.options,
reportDiagnostics: true
reportDiagnostics: true,
transformers: options.transformers
})

const diagnosticList = result.diagnostics ?
filterDiagnostics(result.diagnostics, ignoreWarnings, disableWarnings) :
filterDiagnostics(result.diagnostics, ignoreWarnings) :
[]

if (diagnosticList.length) {
@@ -254,21 +272,32 @@ export function register (options: Options = {}): Register {
)

let getTypeInfo = function (_code: string, _fileName: string, _position: number): TypeInfo {
throw new TypeError(`No type information available under "--fast" mode`)
throw new TypeError(`Type information is unavailable without "--type-check"`)
}

// Use full language services when the fast option is disabled.
if (!fast) {
if (typeCheck) {
// Set the file contents into cache.
const setCache = function (code: string, fileName: string) {
cache.contents[fileName] = code
cache.versions[fileName] = (cache.versions[fileName] + 1) || 1
if (cache.contents[fileName] !== code) {
cache.contents[fileName] = code
cache.versions[fileName] = (cache.versions[fileName] || 0) + 1
}
}

// Create the compiler host for type checking.
const serviceHost = {
getScriptFileNames: () => Object.keys(cache.versions),
getScriptVersion: (fileName: string) => String(cache.versions[fileName]),
getScriptVersion: (fileName: string) => {
const version = cache.versions[fileName]

// We need to return `undefined` and not a string here because TypeScript will use
// `getScriptVersion` and compare against their own version - which can be `undefined`.
// If we don't return `undefined` it results in `undefined === "undefined"` and run
// `createProgram` again (which is very slow). Using a `string` assertion here to avoid
// TypeScript errors from the function signature (expects `(x: string) => string`).
return version === undefined ? undefined as any as string : String(version)
},
getScriptSnapshot (fileName: string) {
if (!cache.contents[fileName]) {
if (!fileExists(fileName)) {
@@ -288,7 +317,8 @@ export function register (options: Options = {}): Register {
getNewLine: () => EOL,
getCurrentDirectory: () => cwd,
getCompilationSettings: () => config.options,
getDefaultLibFileName: () => ts.getDefaultLibFilePath(config.options)
getDefaultLibFileName: () => ts.getDefaultLibFilePath(config.options),
getCustomTransformers: () => options.transformers
}

const service = ts.createLanguageService(serviceHost)
@@ -301,7 +331,7 @@ export function register (options: Options = {}): Register {
.concat(service.getSyntacticDiagnostics(fileName))
.concat(service.getSemanticDiagnostics(fileName))

const diagnosticList = filterDiagnostics(diagnostics, ignoreWarnings, disableWarnings)
const diagnosticList = filterDiagnostics(diagnostics, ignoreWarnings)

if (diagnosticList.length) {
throw new TSError(formatDiagnostics(diagnosticList, cwd, ts, lineOffset))
@@ -379,14 +409,14 @@ function registerExtension (
) {
const old = require.extensions[ext] || originalHandler

require.extensions[ext] = function (m, filename) {
require.extensions[ext] = function (m: any, filename) {
if (shouldIgnore(filename, ignore)) {
return old(m, filename)
}

const _compile = m._compile

m._compile = function (code, fileName) {
m._compile = function (code: string, fileName: string) {
debug('module._compile', fileName)

return _compile.call(this, register.compile(code, fileName), fileName)
@@ -442,7 +472,7 @@ function readConfig (compilerOptions: any, project: string | boolean | undefined
}

if (typeof ts.parseJsonConfigFileContent === 'function') {
return fixConfig(ts.parseJsonConfigFileContent(result.config, ts.sys, basePath, undefined, configPath as string), ts)
return fixConfig(ts.parseJsonConfigFileContent(result.config, ts.sys, basePath, undefined, configPath), ts)
}

throw new TypeError('Could not find a compatible `parseConfigFile` function')
@@ -576,11 +606,7 @@ export function getFile (fileName: string): string {
/**
* Filter diagnostics.
*/
function filterDiagnostics (diagnostics: TS.Diagnostic[], ignore: number[], disable: boolean) {
if (disable) {
return []
}

function filterDiagnostics (diagnostics: TS.Diagnostic[], ignore: number[]) {
return diagnostics.filter(x => ignore.indexOf(x.code) === -1)
}

@@ -627,21 +653,6 @@ export function formatDiagnostic (
return { message: `${messageText} (${code})`, code }
}

/**
* TypeScript diagnostics error.
*/
export class TSError extends BaseError {

name = 'TSError'

constructor (public diagnostics: TSDiagnostic[]) {
super(
`⨯ Unable to compile TypeScript\n${diagnostics.map(x => x.message).join('\n')}`
)
}

}

/**
* Stringify the `TSError` instance.
*/
7 changes: 7 additions & 0 deletions tests/allow-js/with-jsx.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class Foo2 {
render () {
return <div />
}
}

Foo2.sayHi = () => 'hello world'
12 changes: 12 additions & 0 deletions tests/signals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
process.on('SIGINT', () => {
process.stdout.write('exited')
setTimeout(() => {
process.stdout.write(' fine')

// Needed to make sure what we wrote has time
// to be written
process.nextTick(() => process.exit())
}, 500)
})

setInterval(() => console.log('should not be reached'), 3000)
5 changes: 1 addition & 4 deletions tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
{
"compilerOptions": {
"jsx": "react"
},
"files": [
"../typings/globals/node/index.d.ts"
]
}
}
5 changes: 2 additions & 3 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -13,10 +13,9 @@
"moduleResolution": "node",
"sourceMap": true,
"inlineSources": true,
"types": []
"types": ["node", "mocha"]
},
"include": [
"src/**/*",
"typings/**/*"
"src/**/*"
]
}