fix(ts-node): patch error

This commit is contained in:
Philipp Kunz 2018-07-01 17:30:05 +02:00
parent dcf5c6ab22
commit cd549b8aaf
8 changed files with 1188 additions and 36 deletions

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2018 Lossless GmbH (hello@lossless.com)
Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

6
dist/index.js vendored
View File

@ -1,12 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tsNode = require("ts-node");
const path = require("path");
const tsNode = require("./tsnode");
tsNode.register({
compilerOptions: {
lib: ['es2016', 'es2017']
},
ignore: ['^(.(.*\.d\.ts))*$'],
skipIgnore: true,
cacheDirectory: path.join(__dirname, '../tscache')
});
if (process.env.CLI_CALL_TSRUN) {
@ -17,4 +17,4 @@ if (process.env.CLI_CALL_TSRUN) {
const pathToLoad = path.join(process.cwd(), pathToTsFile);
Promise.resolve().then(() => require(pathToLoad));
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLGtDQUFrQztBQUNsQyw2QkFBNkI7QUFFN0IsTUFBTSxDQUFDLFFBQVEsQ0FBQztJQUNkLGVBQWUsRUFBRTtRQUNmLEdBQUcsRUFBRSxDQUFFLFFBQVEsRUFBRSxRQUFRLENBQUU7S0FDNUI7SUFDRCxNQUFNLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQztJQUM3QixjQUFjLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsWUFBWSxDQUFDO0NBQ25ELENBQUMsQ0FBQztBQUVILElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLEVBQUU7SUFDOUIseUJBQXlCO0lBQ3pCLHFDQUFxQztJQUNyQyxzQ0FBc0M7SUFDdEMsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUVwQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxZQUFZLENBQUMsQ0FBQztJQUMxRCxxQ0FBTyxVQUFVLEdBQUU7Q0FDcEIifQ==
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLDZCQUE2QjtBQUM3QixtQ0FBbUM7QUFFbkMsTUFBTSxDQUFDLFFBQVEsQ0FBQztJQUNkLGVBQWUsRUFBRTtRQUNmLEdBQUcsRUFBRSxDQUFFLFFBQVEsRUFBRSxRQUFRLENBQUU7S0FDNUI7SUFDRCxVQUFVLEVBQUUsSUFBSTtJQUNoQixjQUFjLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsWUFBWSxDQUFDO0NBQ25ELENBQUMsQ0FBQztBQUVILElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLEVBQUU7SUFDOUIseUJBQXlCO0lBQ3pCLHFDQUFxQztJQUNyQyxzQ0FBc0M7SUFDdEMsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUVwQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxZQUFZLENBQUMsQ0FBQztJQUMxRCxxQ0FBTyxVQUFVLEdBQUU7Q0FDcEIifQ==

99
dist/tsnode.d.ts vendored Normal file
View File

@ -0,0 +1,99 @@
import { BaseError } from 'make-error';
import * as _ts from 'typescript';
/**
* @internal
*/
export declare const INSPECT_CUSTOM: string | symbol;
/**
* Common TypeScript interfaces between versions.
*/
export interface TSCommon {
version: typeof _ts.version;
sys: typeof _ts.sys;
ScriptSnapshot: typeof _ts.ScriptSnapshot;
displayPartsToString: typeof _ts.displayPartsToString;
createLanguageService: typeof _ts.createLanguageService;
getDefaultLibFilePath: typeof _ts.getDefaultLibFilePath;
getPreEmitDiagnostics: typeof _ts.getPreEmitDiagnostics;
flattenDiagnosticMessageText: typeof _ts.flattenDiagnosticMessageText;
transpileModule: typeof _ts.transpileModule;
ModuleKind: typeof _ts.ModuleKind;
ScriptTarget: typeof _ts.ScriptTarget;
findConfigFile: typeof _ts.findConfigFile;
readConfigFile: typeof _ts.readConfigFile;
parseJsonConfigFileContent: typeof _ts.parseJsonConfigFileContent;
formatDiagnostics: typeof _ts.formatDiagnostics;
formatDiagnosticsWithColorAndContext: typeof _ts.formatDiagnosticsWithColorAndContext;
}
/**
* Export the current version.
*/
export declare const VERSION: any;
/**
* Registration options.
*/
export interface Options {
pretty?: boolean | null;
typeCheck?: boolean | null;
transpileOnly?: boolean | null;
files?: boolean | null;
cache?: boolean | null;
cacheDirectory?: string;
compiler?: string;
ignore?: string | string[];
project?: string;
skipIgnore?: boolean | null;
skipProject?: boolean | null;
compilerOptions?: object;
ignoreDiagnostics?: number | string | Array<number | string>;
readFile?: (path: string) => string | undefined;
fileExists?: (path: string) => boolean;
transformers?: _ts.CustomTransformers;
}
/**
* Information retrieved from type info check.
*/
export interface TypeInfo {
name: string;
comment: string;
}
/**
* Default register options.
*/
export declare const DEFAULTS: Options;
/**
* Split a string array of values.
*/
export declare function split(value: string | undefined): string[];
/**
* Parse a string as JSON.
*/
export declare function parse(value: string | undefined): object | undefined;
/**
* Replace backslashes with forward slashes.
*/
export declare function normalizeSlashes(value: string): string;
/**
* TypeScript diagnostics error.
*/
export declare class TSError extends BaseError {
diagnosticText: string;
diagnosticCodes: number[];
name: string;
constructor(diagnosticText: string, diagnosticCodes: number[]);
}
/**
* Return type for registering `ts-node`.
*/
export interface Register {
cwd: string;
extensions: string[];
cachedir: string;
ts: TSCommon;
compile(code: string, fileName: string, lineOffset?: number): string;
getTypeInfo(code: string, fileName: string, position: number): TypeInfo;
}
/**
* Register TypeScript compiler.
*/
export declare function register(opts?: Options): Register;

440
dist/tsnode.js vendored Normal file

File diff suppressed because one or more lines are too long

32
package-lock.json generated
View File

@ -101,11 +101,6 @@
"object-keys": "^1.0.8"
}
},
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
},
"es-abstract": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
@ -278,9 +273,9 @@
}
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mkdirp": {
"version": "0.5.1",
@ -288,13 +283,6 @@
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
}
}
},
"object-keys": {
@ -467,20 +455,6 @@
"strip-bom": "^2.0.0"
}
},
"ts-node": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-6.1.0.tgz",
"integrity": "sha512-mw11Bq08RZgrU/bzcVw/Ti9wNyefpOanXgWsHg008wyVHjvFhWxNatVVrciOAu8BcWSECoNOSunRzUokKH8Mmw==",
"requires": {
"arrify": "^1.0.0",
"diff": "^3.1.0",
"make-error": "^1.1.1",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"source-map-support": "^0.5.6",
"yn": "^2.0.0"
}
},
"typescript": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.1.tgz",

View File

@ -19,9 +19,13 @@
"@types/node": "^10.3.0"
},
"dependencies": {
"arrify": "^1.0.1",
"make-error": "^1.3.4",
"mkdirp": "^0.5.1",
"smartfile": "^4.2.28",
"ts-node": "^6.1.0",
"typescript": "^2.9.1"
"source-map-support": "^0.5.6",
"typescript": "^2.9.1",
"yn": "^2.0.0"
},
"private": false
}

View File

@ -1,11 +1,11 @@
import * as tsNode from 'ts-node';
import * as path from 'path';
import * as tsNode from './tsnode';
tsNode.register({
compilerOptions: {
lib: [ 'es2016', 'es2017' ]
},
ignore: ['^(.(.*\.d\.ts))*$'],
skipIgnore: true,
cacheDirectory: path.join(__dirname, '../tscache')
});

613
ts/tsnode.ts Normal file
View File

@ -0,0 +1,613 @@
import { relative, basename, extname, resolve, dirname, join } from 'path'
import { readFileSync, writeFileSync } from 'fs'
import { EOL, tmpdir, homedir } from 'os'
import sourceMapSupport = require('source-map-support')
import mkdirp = require('mkdirp')
import crypto = require('crypto')
import yn = require('yn')
import arrify = require('arrify')
import bufferFrom = require('buffer-from')
import { BaseError } from 'make-error'
import * as util from 'util'
import * as _ts from 'typescript'
/**
* @internal
*/
export const INSPECT_CUSTOM = util.inspect.custom || 'inspect'
/**
* Debugging `ts-node`.
*/
const shouldDebug = yn(process.env.TS_NODE_DEBUG)
const debug = shouldDebug ? console.log.bind(console, 'ts-node') : () => undefined
const debugFn = shouldDebug ?
<T, U> (key: string, fn: (arg: T) => U) => {
return (x: T) => {
debug(key, x)
return fn(x)
}
} :
<T, U> (_: string, fn: (arg: T) => U) => fn
/**
* Common TypeScript interfaces between versions.
*/
export interface TSCommon {
version: typeof _ts.version
sys: typeof _ts.sys
ScriptSnapshot: typeof _ts.ScriptSnapshot
displayPartsToString: typeof _ts.displayPartsToString
createLanguageService: typeof _ts.createLanguageService
getDefaultLibFilePath: typeof _ts.getDefaultLibFilePath
getPreEmitDiagnostics: typeof _ts.getPreEmitDiagnostics
flattenDiagnosticMessageText: typeof _ts.flattenDiagnosticMessageText
transpileModule: typeof _ts.transpileModule
ModuleKind: typeof _ts.ModuleKind
ScriptTarget: typeof _ts.ScriptTarget
findConfigFile: typeof _ts.findConfigFile
readConfigFile: typeof _ts.readConfigFile
parseJsonConfigFileContent: typeof _ts.parseJsonConfigFileContent
formatDiagnostics: typeof _ts.formatDiagnostics
formatDiagnosticsWithColorAndContext: typeof _ts.formatDiagnosticsWithColorAndContext
}
/**
* Export the current version.
*/
export const VERSION = require('../package.json').version
/**
* Registration options.
*/
export interface Options {
pretty?: boolean | null
typeCheck?: boolean | null
transpileOnly?: boolean | null
files?: boolean | null
cache?: boolean | null
cacheDirectory?: string
compiler?: string
ignore?: string | string[]
project?: string
skipIgnore?: boolean | null
skipProject?: boolean | null
compilerOptions?: object
ignoreDiagnostics?: number | string | Array<number | string>
readFile?: (path: string) => string | undefined
fileExists?: (path: string) => boolean
transformers?: _ts.CustomTransformers
}
/**
* Track the project information.
*/
interface MemoryCache {
contents: { [path: string]: string | undefined }
versions: { [path: string]: number | undefined }
outputs: { [path: string]: string }
}
/**
* Information retrieved from type info check.
*/
export interface TypeInfo {
name: string
comment: string
}
/**
* Default register options.
*/
export const DEFAULTS: Options = {
files: yn(process.env['TS_NODE_FILES']),
cache: yn(process.env['TS_NODE_CACHE'], { default: true }),
pretty: yn(process.env['TS_NODE_PRETTY']),
cacheDirectory: process.env['TS_NODE_CACHE_DIRECTORY'],
compiler: process.env['TS_NODE_COMPILER'],
compilerOptions: parse(process.env['TS_NODE_COMPILER_OPTIONS']),
ignore: split(process.env['TS_NODE_IGNORE']),
project: process.env['TS_NODE_PROJECT'],
skipIgnore: yn(process.env['TS_NODE_SKIP_IGNORE']),
skipProject: yn(process.env['TS_NODE_SKIP_PROJECT']),
ignoreDiagnostics: split(process.env['TS_NODE_IGNORE_DIAGNOSTICS']),
typeCheck: yn(process.env['TS_NODE_TYPE_CHECK']),
transpileOnly: yn(process.env['TS_NODE_TRANSPILE_ONLY'])
}
/**
* Default TypeScript compiler options required by `ts-node`.
*/
const DEFAULT_COMPILER_OPTIONS = {
sourceMap: true,
inlineSourceMap: false,
inlineSources: true,
declaration: false,
noEmit: false,
outDir: '$$ts-node$$'
}
/**
* Split a string array of values.
*/
export function split (value: string | undefined) {
return typeof value === 'string' ? value.split(/ *, */g) : undefined
}
/**
* Parse a string as JSON.
*/
export function parse (value: string | undefined): object | undefined {
return typeof value === 'string' ? JSON.parse(value) : undefined
}
/**
* Replace backslashes with forward slashes.
*/
export function normalizeSlashes (value: string): string {
return value.replace(/\\/g, '/')
}
/**
* TypeScript diagnostics error.
*/
export class TSError extends BaseError {
name = 'TSError'
constructor (public diagnosticText: string, public diagnosticCodes: number[]) {
super(` Unable to compile TypeScript:\n${diagnosticText}`)
}
/**
* @internal
*/
[INSPECT_CUSTOM] () {
return this.diagnosticText
}
}
/**
* Return type for registering `ts-node`.
*/
export interface Register {
cwd: string
extensions: string[]
cachedir: string
ts: TSCommon
compile (code: string, fileName: string, lineOffset?: number): string
getTypeInfo (code: string, fileName: string, position: number): TypeInfo
}
/**
* Return a default temp directory based on home directory of user.
*/
function getTmpDir (): string {
const hash = crypto.createHash('sha256').update(homedir(), 'utf8').digest('hex')
return join(tmpdir(), `ts-node-${hash}`)
}
/**
* Register TypeScript compiler.
*/
export function register (opts: Options = {}): Register {
const options = Object.assign({}, DEFAULTS, opts)
const cacheDirectory = options.cacheDirectory || getTmpDir()
const originalJsHandler = require.extensions['.js']
const ignoreDiagnostics = arrify(options.ignoreDiagnostics).concat([
6059, // "'rootDir' is expected to contain all source files."
18002, // "The 'files' list in config file is empty."
18003 // "No inputs were found in config file."
]).map(Number)
const memoryCache: MemoryCache = {
contents: Object.create(null),
versions: Object.create(null),
outputs: Object.create(null)
}
const ignore = options.skipIgnore ? [] : arrify(
options.ignore || '/node_modules/'
).map(str => new RegExp(str))
// Install source map support and read from memory cache.
sourceMapSupport.install({
environment: 'node',
retrieveFile (path: string) {
return memoryCache.outputs[path]
}
})
// Require the TypeScript compiler and configuration.
const cwd = process.cwd()
const { compilerOptions, project, skipProject } = options
const compiler = options.compiler || 'typescript'
const typeCheck = options.typeCheck === true || options.transpileOnly !== true
const ts: typeof _ts = require(compiler)
const transformers = options.transformers || undefined
const readFile = options.readFile || ts.sys.readFile
const fileExists = options.fileExists || ts.sys.fileExists
const config = readConfig(cwd, ts, fileExists, readFile, compilerOptions, project, skipProject)
const configDiagnosticList = filterDiagnostics(config.errors, ignoreDiagnostics)
const extensions = ['.ts', '.tsx']
const fileNames = options.files ? config.fileNames : []
const cachedir = join(
resolve(cwd, cacheDirectory),
getCompilerDigest({
version: ts.version,
options: config.options,
fileNames,
typeCheck,
ignoreDiagnostics,
compiler
})
)
const diagnosticHost: _ts.FormatDiagnosticsHost = {
getNewLine: () => EOL,
getCurrentDirectory: () => cwd,
getCanonicalFileName: (path) => path
}
const formatDiagnostics = options.pretty
? ts.formatDiagnosticsWithColorAndContext
: ts.formatDiagnostics
function createTSError (diagnostics: ReadonlyArray<_ts.Diagnostic>) {
const diagnosticText = formatDiagnostics(diagnostics, diagnosticHost)
const diagnosticCodes = diagnostics.map(x => x.code)
return new TSError(diagnosticText, diagnosticCodes)
}
// Render the configuration errors and exit the script.
if (configDiagnosticList.length) throw createTSError(configDiagnosticList)
// Enable `allowJs` when flag is set.
if (config.options.allowJs) {
extensions.push('.js')
extensions.push('.jsx')
}
// Initialize files from TypeScript into project.
for (const path of fileNames) memoryCache.versions[path] = 1
/**
* Get the extension for a transpiled file.
*/
const getExtension = config.options.jsx === ts.JsxEmit.Preserve ?
((path: string) => /\.[tj]sx$/.test(path) ? '.jsx' : '.js') :
((_: string) => '.js')
/**
* Create the basic required function using transpile mode.
*/
let getOutput = function (code: string, fileName: string, lineOffset = 0): SourceOutput {
const result = ts.transpileModule(code, {
fileName,
transformers,
compilerOptions: config.options,
reportDiagnostics: true
})
const diagnosticList = result.diagnostics ?
filterDiagnostics(result.diagnostics, ignoreDiagnostics) :
[]
if (diagnosticList.length) throw createTSError(diagnosticList)
return [result.outputText, result.sourceMapText as string]
}
let getTypeInfo = function (_code: string, _fileName: string, _position: number): TypeInfo {
throw new TypeError(`Type information is unavailable without "--type-check"`)
}
// Use full language services when the fast option is disabled.
if (typeCheck) {
// Set the file contents into cache.
const updateMemoryCache = function (code: string, fileName: string) {
if (memoryCache.contents[fileName] !== code) {
memoryCache.contents[fileName] = code
memoryCache.versions[fileName] = (memoryCache.versions[fileName] || 0) + 1
}
}
// Create the compiler host for type checking.
const serviceHost = {
getScriptFileNames: () => Object.keys(memoryCache.versions),
getScriptVersion: (fileName: string) => {
const version = memoryCache.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) {
// Read contents into TypeScript memory cache.
if (!Object.prototype.hasOwnProperty.call(memoryCache.contents, fileName)) {
memoryCache.contents[fileName] = readFile(fileName)
}
const contents = memoryCache.contents[fileName]
if (contents === undefined) return
return ts.ScriptSnapshot.fromString(contents)
},
fileExists: debugFn('fileExists', fileExists),
readFile: debugFn('readFile', readFile),
readDirectory: debugFn('readDirectory', ts.sys.readDirectory),
getDirectories: debugFn('getDirectories', ts.sys.getDirectories),
directoryExists: debugFn('directoryExists', ts.sys.directoryExists),
getNewLine: () => EOL,
getCurrentDirectory: () => cwd,
getCompilationSettings: () => config.options,
getDefaultLibFileName: () => ts.getDefaultLibFilePath(config.options),
getCustomTransformers: () => transformers
}
const service = ts.createLanguageService(serviceHost)
getOutput = function (code: string, fileName: string, lineOffset: number = 0) {
// Must set memory cache before attempting to read file.
updateMemoryCache(code, fileName)
const output = service.getEmitOutput(fileName)
// Get the relevant diagnostics - this is 3x faster than `getPreEmitDiagnostics`.
const diagnostics = service.getCompilerOptionsDiagnostics()
.concat(service.getSyntacticDiagnostics(fileName))
.concat(service.getSemanticDiagnostics(fileName))
const diagnosticList = filterDiagnostics(diagnostics, ignoreDiagnostics)
if (diagnosticList.length) throw createTSError(diagnosticList)
if (output.emitSkipped) {
throw new TypeError(`${relative(cwd, fileName)}: Emit skipped`)
}
// Throw an error when requiring `.d.ts` files.
if (output.outputFiles.length === 0) {
return ['', ''];
}
return [output.outputFiles[1].text, output.outputFiles[0].text]
}
getTypeInfo = function (code: string, fileName: string, position: number) {
updateMemoryCache(code, fileName)
const info = service.getQuickInfoAtPosition(fileName, position)
const name = ts.displayPartsToString(info ? info.displayParts : [])
const comment = ts.displayPartsToString(info ? info.documentation : [])
return { name, comment }
}
}
const compile = readThrough(cachedir, options.cache === true, memoryCache, getOutput, getExtension)
const register: Register = { cwd, compile, getTypeInfo, extensions, cachedir, ts }
// Register the extensions.
extensions.forEach(extension => {
registerExtension(extension, ignore, register, originalJsHandler)
})
return register
}
/**
* Check if the filename should be ignored.
*/
function shouldIgnore (filename: string, ignore: RegExp[]) {
const relname = normalizeSlashes(filename)
return ignore.some(x => x.test(relname))
}
/**
* Register the extension for node.
*/
function registerExtension (
ext: string,
ignore: RegExp[],
register: Register,
originalHandler: (m: NodeModule, filename: string) => any
) {
const old = require.extensions[ext] || originalHandler
require.extensions[ext] = function (m: any, filename) {
if (shouldIgnore(filename, ignore)) {
return old(m, filename)
}
const _compile = m._compile
m._compile = function (code: string, fileName: string) {
debug('module._compile', fileName)
return _compile.call(this, register.compile(code, fileName), fileName)
}
return old(m, filename)
}
}
/**
* Do post-processing on config options to support `ts-node`.
*/
function fixConfig (ts: TSCommon, config: _ts.ParsedCommandLine) {
// Delete options that *should not* be passed through.
delete config.options.out
delete config.options.outFile
delete config.options.declarationDir
delete config.options.declarationMap
delete config.options.emitDeclarationOnly
// Target ES5 output by default (instead of ES3).
if (config.options.target === undefined) {
config.options.target = ts.ScriptTarget.ES5
}
// Target CommonJS modules by default (instead of magically switching to ES6 when the target is ES6).
if (config.options.module === undefined) {
config.options.module = ts.ModuleKind.CommonJS
}
return config
}
/**
* Load TypeScript configuration.
*/
function readConfig (
cwd: string,
ts: TSCommon,
fileExists: (path: string) => boolean,
readFile: (path: string) => string | undefined,
compilerOptions?: object,
project?: string | null,
noProject?: boolean | null
): _ts.ParsedCommandLine {
let config = { compilerOptions: {} }
let basePath = normalizeSlashes(cwd)
let configFileName: string | undefined = undefined
// Read project configuration when available.
if (!noProject) {
configFileName = project
? normalizeSlashes(resolve(cwd, project))
: ts.findConfigFile(normalizeSlashes(cwd), fileExists)
if (configFileName) {
const result = ts.readConfigFile(configFileName, readFile)
// Return diagnostics.
if (result.error) {
return { errors: [result.error], fileNames: [], options: {} }
}
config = result.config
basePath = normalizeSlashes(dirname(configFileName))
}
}
// Override default configuration options `ts-node` requires.
config.compilerOptions = Object.assign({}, config.compilerOptions, compilerOptions, DEFAULT_COMPILER_OPTIONS)
return fixConfig(ts, ts.parseJsonConfigFileContent(config, ts.sys, basePath, undefined, configFileName))
}
/**
* Internal source output.
*/
type SourceOutput = [string, string]
/**
* Wrap the function with caching.
*/
function readThrough (
cachedir: string,
shouldCache: boolean,
memoryCache: MemoryCache,
compile: (code: string, fileName: string, lineOffset?: number) => SourceOutput,
getExtension: (fileName: string) => string
) {
if (shouldCache === false) {
return function (code: string, fileName: string, lineOffset?: number) {
debug('readThrough', fileName)
const [value, sourceMap] = compile(code, fileName, lineOffset)
const output = updateOutput(value, fileName, sourceMap, getExtension)
memoryCache.outputs[fileName] = output
return output
}
}
// Make sure the cache directory exists before continuing.
mkdirp.sync(cachedir)
return function (code: string, fileName: string, lineOffset?: number) {
debug('readThrough', fileName)
const cachePath = join(cachedir, getCacheName(code, fileName))
const extension = getExtension(fileName)
const outputPath = `${cachePath}${extension}`
try {
const output = readFileSync(outputPath, 'utf8')
if (isValidCacheContent(output)) {
memoryCache.outputs[fileName] = output
return output
}
} catch (err) {/* Ignore. */}
const [value, sourceMap] = compile(code, fileName, lineOffset)
const output = updateOutput(value, fileName, sourceMap, getExtension)
memoryCache.outputs[fileName] = output
writeFileSync(outputPath, output)
return output
}
}
/**
* Update the output remapping the source map.
*/
function updateOutput (outputText: string, fileName: string, sourceMap: string, getExtension: (fileName: string) => string) {
const base64Map = bufferFrom(updateSourceMap(sourceMap, fileName), 'utf8').toString('base64')
const sourceMapContent = `data:application/json;charset=utf-8;base64,${base64Map}`
const sourceMapLength = `${basename(fileName)}.map`.length + (getExtension(fileName).length - extname(fileName).length)
return outputText.slice(0, -sourceMapLength) + sourceMapContent
}
/**
* Update the source map contents for improved output.
*/
function updateSourceMap (sourceMapText: string, fileName: string) {
const sourceMap = JSON.parse(sourceMapText)
sourceMap.file = fileName
sourceMap.sources = [fileName]
delete sourceMap.sourceRoot
return JSON.stringify(sourceMap)
}
/**
* Get the file name for the cache entry.
*/
function getCacheName (sourceCode: string, fileName: string) {
return crypto.createHash('sha256')
.update(extname(fileName), 'utf8')
.update('\x00', 'utf8')
.update(sourceCode, 'utf8')
.digest('hex')
}
/**
* Ensure the given cached content is valid by sniffing for a base64 encoded '}'
* at the end of the content, which should exist if there is a valid sourceMap present.
*/
function isValidCacheContent (contents: string) {
return /(?:9|0=|Q==)$/.test(contents.slice(-3))
}
/**
* Create a hash of the current configuration.
*/
function getCompilerDigest (obj: object) {
return crypto.createHash('sha256').update(JSON.stringify(obj), 'utf8').digest('hex')
}
/**
* Filter diagnostics.
*/
function filterDiagnostics (diagnostics: _ts.Diagnostic[], ignore: number[]) {
return diagnostics.filter(x => ignore.indexOf(x.code) === -1)
}