Skip to content

Commit

Permalink
Reuse same compiler across environments
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey committed Jul 15, 2015
1 parent 962e4ca commit 32697ff
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 86 deletions.
72 changes: 6 additions & 66 deletions src/bin/ts-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { readFileSync } from 'fs'
import Module = require('module')
import extend = require('xtend')
import { runInThisContext } from 'vm'
import { register, createError, getInlineSourceMap, getDiagnostics } from '../typescript-node'
import { register } from '../typescript-node'

var program = new Command('ts-node')
var pkg = require('../../package.json')
Expand All @@ -32,15 +32,13 @@ const eval = opts.eval
const code: string = eval == null ? print : eval

// Register returns environment options, helps creating a new language service.
const compiler = register(opts)
const compileInline = register(opts)

// Defer creation of eval services.
let files: { [filename: string]: { text: string, version: number } }
let service: LanguageService

if (typeof code === 'string') {
createService()

global.__filename = EVAL_FILENAME
global.__dirname = cwd

Expand Down Expand Up @@ -96,75 +94,17 @@ if (typeof code === 'string') {

Module.runMain()
} else {
createService()
startRepl()
}
}

/**
* Evaluate the code snippet.
*/
function _eval (text: string, filename: string) {
if (!files[filename]) {
files[filename] = { version: 0, text: '' }
}

const file = files[filename]

file.text = text
file.version++

const output = service.getEmitOutput(filename)
const diagnostics = getDiagnostics(service, filename, compiler.options)

if (diagnostics.length) {
throw createError(diagnostics, compiler.ts)
}

const result = output.outputFiles[1].text
const sourceMap = output.outputFiles[0].text

const code = result.replace(
'//# sourceMappingURL=' + filename.replace(/\.ts$/, '.js.map'),
'//# sourceMappingURL=' + getInlineSourceMap(sourceMap, filename, text)
)

return runInThisContext(result, filename)
}

/**
* Create an inline eval service on demand.
*/
function createService () {
const { ts, registry, config } = compiler

// Initialize files object.
files = {}

// Create language services for `eval`.
service = ts.createLanguageService(<LanguageServiceHost> {
getScriptFileNames: () => {
return config.fileNames.concat(Object.keys(files))
},
getScriptVersion: (fileName) => files[fileName] && files[fileName].version.toString(),
getScriptSnapshot (fileName) {
const file = files[fileName]

if (file) {
return ts.ScriptSnapshot.fromString(file.text)
}

try {
return ts.ScriptSnapshot.fromString(readFileSync(fileName, 'utf-8'))
} catch (e) {
return
}
},
getCurrentDirectory: () => cwd,
getScriptIsOpen: () => true,
getCompilationSettings: () => extend(config.options, { sourceMap: true }),
getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options)
}, registry)
function _eval (code: string, filename: string) {
// Adding `;` before the code is jank, but avoids issues with source map
// columns becoming negative.
return runInThisContext(compileInline(filename, ';' + code), filename)
}

/**
Expand Down
48 changes: 28 additions & 20 deletions src/typescript-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export function register (opts?: Options) {

const maps: { [fileName: string]: string } = {}
const files: { [fileName: string]: boolean } = {}
const versions: { [fileName: string]: number } = {}
const snapshots: { [fileName: string]: TS.IScriptSnapshot } = {}

// Enable compiler overrides.
options.compiler = options.compiler || 'typescript'
Expand All @@ -71,8 +73,12 @@ export function register (opts?: Options) {

const serviceHost: TS.LanguageServiceHost = {
getScriptFileNames: () => config.fileNames.concat(Object.keys(files)),
getScriptVersion: (fileName) => 'node',
getScriptVersion: (fileName) => String(versions[fileName] || 'node'),
getScriptSnapshot (fileName): TS.IScriptSnapshot {
if (snapshots[fileName]) {
return snapshots[fileName]
}

try {
return ts.ScriptSnapshot.fromString(readFileSync(fileName, 'utf-8'))
} catch (e) {
Expand All @@ -89,19 +95,14 @@ export function register (opts?: Options) {
const service = ts.createLanguageService(serviceHost, registry)
const hasSourceMap = config.options.sourceMap

const retrieveSourceMap = sourceMapSupport.retrieveSourceMap

// Install source map support and read from cache.
sourceMapSupport.install({
handleUncaughtExceptions: false,
retrieveSourceMap (filename: string) {
var map = maps && maps[filename]
retrieveSourceMap (fileName: string) {
var map = maps && maps[fileName]

if (map) {
return <sourceMapSupport.UrlAndMap> { map, url: null }
}

return retrieveSourceMap(filename)
}
})

Expand All @@ -113,7 +114,10 @@ export function register (opts?: Options) {

// Cache source maps where provided.
if (hasSourceMap) {
maps[output.outputFiles[0].name] = output.outputFiles[0].text
const sourceText = service.getSourceFile(fileName).text
const sourceMapText = output.outputFiles[0].text

maps[fileName] = getSourceMap(sourceMapText, fileName, sourceText)
}

// Log all diagnostics before exiting the program.
Expand All @@ -135,12 +139,18 @@ export function register (opts?: Options) {
require.extensions[extension] = loader
})

return {
registry,
ts,
config,
options
function compileInline (fileName: string, code: string) {
if (!versions[fileName]) {
versions[fileName] = 0
}

versions[fileName]++
snapshots[fileName] = ts.ScriptSnapshot.fromString(code)

return compile(fileName)
}

return compileInline
}

/**
Expand Down Expand Up @@ -174,7 +184,7 @@ export function formatDiagnostic (diagnostic: TS.Diagnostic, ts: typeof TS, cwd:
* Create a "TypeScript" error.
*/
export function createError (diagnostics: TS.Diagnostic[], ts: typeof TS): Error {
const message = ['Unable to compile TypeScript:']
const message = ['Unable to compile TypeScript']
.concat(diagnostics.map((d) => formatDiagnostic(d, ts)))
.join(EOL)

Expand All @@ -184,14 +194,12 @@ export function createError (diagnostics: TS.Diagnostic[], ts: typeof TS): Error
}

/**
* Generate an base 64 inline source map.
* Sanitize the source map content.
*/
export function getInlineSourceMap (map: string, fileName: string, code: string): string {
export function getSourceMap (map: string, fileName: string, code: string): string {
var sourceMap = JSON.parse(map)
sourceMap.file = fileName
sourceMap.sources = [fileName]
sourceMap.sourcesContent = [code]
delete sourceMap.sourceRoot
const inlineSourceMap = new Buffer(JSON.stringify(sourceMap)).toString('base64')
return 'data:application/json;base64,' + inlineSourceMap
return JSON.stringify(sourceMap)
}

0 comments on commit 32697ff

Please sign in to comment.