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: v1.7.1
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: v1.7.2
Choose a head ref
  • 2 commits
  • 4 files changed
  • 1 contributor

Commits on Dec 9, 2016

  1. Copy the full SHA
    e760502 View commit details
  2. v1.7.2

    blakeembrey committed Dec 9, 2016
    Copy the full SHA
    55be95c View commit details
Showing with 87 additions and 47 deletions.
  1. +2 −1 package.json
  2. +83 −45 src/_bin.ts
  3. +1 −1 src/index.spec.ts
  4. +1 −0 typings.json
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ts-node",
"version": "1.7.1",
"version": "1.7.2",
"preferGlobal": true,
"description": "TypeScript execution environment and REPL for node",
"main": "dist/index.js",
@@ -61,6 +61,7 @@
"dependencies": {
"arrify": "^1.0.0",
"chalk": "^1.1.1",
"diff": "^3.1.0",
"make-error": "^1.1.1",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
128 changes: 83 additions & 45 deletions src/_bin.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import arrify = require('arrify')
import Module = require('module')
import minimist = require('minimist')
import chalk = require('chalk')
import { diffLines } from 'diff'
import { createScript } from 'vm'
import { register, VERSION, getFile, fileExists, TSError, parse } from './index'

@@ -159,17 +160,18 @@ const service = register({
fileExists: isEval ? fileExistsEval : fileExists
})

// Increment the `eval` id to keep track of execution.
let evalId = 0

// Note: TypeScript files must always end with `.ts`.
const EVAL_PATHS: { [path: string]: string } = {}

// Require specified modules before start-up.
for (const id of arrify(argv.require)) {
Module._load(id)
}

/**
* Eval helpers.
*/
const EVAL_FILENAME = `[eval].ts`
const EVAL_PATH = join(cwd, EVAL_FILENAME)
const EVAL_INSTANCE = { input: '', output: '', version: 0, lines: 0 }

// Execute the main contents (either eval, script or piped).
if (isEvalScript) {
evalAndExit(code as string, isPrinted)
@@ -196,13 +198,11 @@ if (isEvalScript) {
* Evaluate a script.
*/
function evalAndExit (code: string, isPrinted: boolean) {
const filename = getEvalFileName(evalId)

const module = new Module(filename)
module.filename = filename
const module = new Module(EVAL_FILENAME)
module.filename = EVAL_FILENAME
module.paths = Module._nodeModulePaths(cwd)

;(global as any).__filename = filename
;(global as any).__filename = EVAL_FILENAME
;(global as any).__dirname = cwd
;(global as any).exports = module.exports
;(global as any).module = module
@@ -241,18 +241,36 @@ function print (error: TSError) {
* Evaluate the code snippet.
*/
function _eval (input: string, context: any) {
const lines = EVAL_INSTANCE.lines
const isCompletion = !/\n$/.test(input)
const path = join(cwd, getEvalFileName(evalId++))
const { code, lineOffset } = getEvalContent(input)
const filename = basename(path)
const undo = appendEval(input)
let output: string

const output = service().compile(code, path, lineOffset)
try {
output = service().compile(EVAL_INSTANCE.input, EVAL_PATH, -lines)
} catch (err) {
undo()

const script = createScript(output, supportsScriptOptions ? { filename, lineOffset } : filename)
const result = script.runInNewContext(context)
throw err
}

if (!isCompletion) {
EVAL_PATHS[path] = code
// Use `diff` to check for new JavaScript to execute.
const changes = diffLines(EVAL_INSTANCE.output, output)

if (isCompletion) {
undo()
} else {
EVAL_INSTANCE.output = output
}

let result: any

for (const change of changes) {
if (change.added) {
const script = createScript(change.value, EVAL_FILENAME)

result = script.runInNewContext(context)
}
}

return result
@@ -270,6 +288,10 @@ function startRepl () {
useGlobal: false
})

const undo = appendEval('')

repl.on('reset', () => undo())

repl.defineCommand('type', {
help: 'Check the type of a TypeScript identifier',
action: function (identifier: string) {
@@ -278,16 +300,10 @@ function startRepl () {
return
}

const path = join(cwd, getEvalFileName(evalId++))
const { code, lineOffset } = getEvalContent(identifier)

// Cache the file for language services lookup.
EVAL_PATHS[path] = code
const undo = appendEval(identifier)
const { name, comment } = service().getTypeInfo(EVAL_PATH, EVAL_INSTANCE.input.length)

const { name, comment } = service().getTypeInfo(path, code.length)

// Delete the file from the cache after used for lookup.
delete EVAL_PATHS[path]
undo()

repl.outputStream.write(`${chalk.bold(name)}\n${comment ? `${comment}\n` : ''}`)
repl.displayPrompt()
@@ -327,36 +343,58 @@ function replEval (code: string, context: any, filename: string, callback: (err?
}

/**
* Get the file text, checking for eval first.
* Append to the eval instance and return an undo function.
*/
function getFileEval (path: string) {
return EVAL_PATHS.hasOwnProperty(path) ? EVAL_PATHS[path] : getFile(path)
function appendEval (input: string) {
const undoInput = EVAL_INSTANCE.input
const undoVersion = EVAL_INSTANCE.version
const undoOutput = EVAL_INSTANCE.output
const undoLines = EVAL_INSTANCE.lines

// Handle ASI issues with TypeScript re-evaluation.
if (undoInput.charAt(undoInput.length - 1) === '\n' && /^\s*[\[\(\`]/.test(input) && !/;\s*$/.test(undoInput)) {
EVAL_INSTANCE.input = `${EVAL_INSTANCE.input.slice(0, -1)};\n`
}

EVAL_INSTANCE.input += input
EVAL_INSTANCE.lines += lineCount(input)
EVAL_INSTANCE.version++

return function () {
EVAL_INSTANCE.input = undoInput
EVAL_INSTANCE.output = undoOutput
EVAL_INSTANCE.version = undoVersion
EVAL_INSTANCE.lines = undoLines
}
}

/**
* Get whether the file exists.
* Count the number of lines.
*/
function fileExistsEval (path: string) {
return EVAL_PATHS.hasOwnProperty(path) || fileExists(path)
function lineCount (value: string) {
let count = 0

for (const char of value) {
if (char === '\n') {
count++
}
}

return count
}

/**
* Create an file for evaluation.
* Get the file text, checking for eval first.
*/
function getEvalContent (input: string) {
const refs = Object.keys(EVAL_PATHS).map(x => `/// <reference path="${x}" />\n`)

return {
lineOffset: -refs.length,
code: refs.join('') + input
}
function getFileEval (path: string) {
return path === EVAL_PATH ? EVAL_INSTANCE.input : getFile(path)
}

/**
* Retrieve the eval filename.
* Get whether the file exists.
*/
function getEvalFileName (index: number) {
return `[eval ${index}].ts`
function fileExistsEval (path: string) {
return path === EVAL_PATH || fileExists(path)
}

const RECOVERY_CODES: number[] = [
2 changes: 1 addition & 1 deletion src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -79,7 +79,7 @@ describe('ts-node', function () {
exec(`${BIN_EXEC} -e "import * as m from './tests/module';console.log(m.example(123))"`, function (err) {
expect(err.message).to.match(new RegExp(
// Node 0.10 can not override the `lineOffset` option.
'\\[eval [01]\\]\\.ts \\(1,59\\): Argument of type \'(?:number|123)\' ' +
'\\[eval\\]\\.ts \\(1,59\\): Argument of type \'(?:number|123)\' ' +
'is not assignable to parameter of type \'string\'\\. \\(2345\\)'
))

1 change: 1 addition & 0 deletions typings.json
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
"dependencies": {
"arrify": "registry:npm/arrify#1.0.0+20160723033700",
"chalk": "registry:npm/chalk#1.0.0+20160211003958",
"diff": "registry:npm/diff#2.0.0+20160723033700",
"make-error": "registry:npm/make-error#1.0.0+20160211003958",
"minimist": "registry:npm/minimist#1.0.0+20160229232932",
"mkdirp": "registry:npm/mkdirp#0.5.0+20160222053049",