Skip to content

Commit

Permalink
Add --build flag, improve TS module error (#896)
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey committed Oct 14, 2019
1 parent cb4b748 commit e0a83b5
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 45 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ _Environment variable denoted in parentheses._
* `--skip-project` Skip project config resolution and loading (`TS_NODE_SKIP_PROJECT`, default: `false`)
* `--skip-ignore` Skip ignore checks (`TS_NODE_SKIP_IGNORE`, default: `false`)
* `--log-error` Logs errors of types instead of exit the process (`TS_NODE_LOG_ERROR`, default: `false`)
* `--build` Emit output files into `.ts-node` directory (`TS_NODE_BUILD`, default: `false`)
* `--prefer-ts-exts` Re-order file extensions so that TypeScript imports are preferred (`TS_NODE_PREFER_TS_EXTS`, default: `false`)

### Programmatic Only Options
Expand Down
8 changes: 6 additions & 2 deletions src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ const args = arg({
'--skip-ignore': Boolean,
'--prefer-ts-exts': Boolean,
'--log-error': Boolean,
'--build': Boolean,

// Aliases.
'-e': '--eval',
'-p': '--print',
'-r': '--require',
'-h': '--help',
'-v': '--version',
'-B': '--build',
'-T': '--transpile-only',
'-I': '--ignore',
'-P': '--project',
Expand All @@ -66,7 +68,8 @@ const {
'--skip-project': skipProject = DEFAULTS.skipProject,
'--skip-ignore': skipIgnore = DEFAULTS.skipIgnore,
'--prefer-ts-exts': preferTsExts = DEFAULTS.preferTsExts,
'--log-error': logError = DEFAULTS.logError
'--log-error': logError = DEFAULTS.logError,
'--build': build = DEFAULTS.build
} = args

if (help) {
Expand Down Expand Up @@ -118,16 +121,17 @@ const EVAL_INSTANCE = { input: '', output: '', version: 0, lines: 0 }

// Register the TypeScript compiler instance.
const service = register({
build,
files,
pretty,
typeCheck,
transpileOnly,
ignore,
project,
skipIgnore,
preferTsExts,
logError,
skipProject,
skipIgnore,
compiler,
ignoreDiagnostics,
compilerOptions,
Expand Down
14 changes: 12 additions & 2 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { register, VERSION } from './index'

const TEST_DIR = join(__dirname, '../tests')
const EXEC_PATH = join(__dirname, '../dist/bin')
const PROJECT = join(TEST_DIR, semver.gte(ts.version, '2.5.0') ? 'tsconfig.json5' : 'tsconfig.json')
const PROJECT = join(TEST_DIR, 'tsconfig.json')
const BIN_EXEC = `node "${EXEC_PATH}" --project "${PROJECT}"`

const SOURCE_MAP_REGEXP = /\/\/# sourceMappingURL=data:application\/json;charset=utf\-8;base64,[\w\+]+=*$/
Expand Down Expand Up @@ -165,7 +165,7 @@ describe('ts-node', function () {
})
})

it.skip('eval should work with source maps', function (done) {
it('eval should work with source maps', function (done) {
exec(`${BIN_EXEC} -pe "import './tests/throw'"`, function (err) {
if (err === null) {
return done('Command was expected to fail, but it succeeded.')
Expand Down Expand Up @@ -305,6 +305,16 @@ describe('ts-node', function () {
return done()
})
})

it('should give ts error for invalid node_modules', function (done) {
exec(`${BIN_EXEC} --skip-ignore tests/from-node-modules`, function (err, stdout) {
if (err === null) return done('Expected an error')

expect(err.message).to.contain('Unable to compile file from external library')

return done()
})
})
})

describe('register', function () {
Expand Down
59 changes: 33 additions & 26 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const VERSION = require('../package.json').version
* Registration options.
*/
export interface Options {
build?: boolean | null
pretty?: boolean | null
typeCheck?: boolean | null
transpileOnly?: boolean | null
Expand All @@ -64,8 +65,8 @@ export interface Options {
compiler?: string
ignore?: string[]
project?: string
skipIgnore?: boolean | null
skipProject?: boolean | null
skipIgnore?: boolean | null
preferTsExts?: boolean | null
compilerOptions?: object
ignoreDiagnostics?: Array<number | string>
Expand Down Expand Up @@ -95,19 +96,20 @@ export interface TypeInfo {
* Default register options.
*/
export const DEFAULTS: Options = {
files: yn(process.env['TS_NODE_FILES']),
pretty: yn(process.env['TS_NODE_PRETTY']),
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']),
preferTsExts: yn(process.env['TS_NODE_PREFER_TS_EXTS']),
ignoreDiagnostics: split(process.env['TS_NODE_IGNORE_DIAGNOSTICS']),
typeCheck: yn(process.env['TS_NODE_TYPE_CHECK']),
transpileOnly: yn(process.env['TS_NODE_TRANSPILE_ONLY']),
logError: yn(process.env['TS_NODE_LOG_ERROR'])
files: yn(process.env.TS_NODE_FILES),
pretty: yn(process.env.TS_NODE_PRETTY),
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,
skipProject: yn(process.env.TS_NODE_SKIP_PROJECT),
skipIgnore: yn(process.env.TS_NODE_SKIP_IGNORE),
preferTsExts: yn(process.env.TS_NODE_PREFER_TS_EXTS),
ignoreDiagnostics: split(process.env.TS_NODE_IGNORE_DIAGNOSTICS),
typeCheck: yn(process.env.TS_NODE_TYPE_CHECK),
transpileOnly: yn(process.env.TS_NODE_TRANSPILE_ONLY),
logError: yn(process.env.TS_NODE_LOG_ERROR),
build: yn(process.env.TS_NODE_BUILD)
}

/**
Expand Down Expand Up @@ -191,7 +193,7 @@ function cachedLookup <T> (fn: (arg: string) => T): (arg: string) => T {
* Register TypeScript compiler.
*/
export function register (opts: Options = {}): Register {
const options = Object.assign({}, DEFAULTS, opts)
const options = { ...DEFAULTS, ...opts }
const originalJsHandler = require.extensions['.js'] // tslint:disable-line

const ignoreDiagnostics = [
Expand All @@ -201,9 +203,7 @@ export function register (opts: Options = {}): Register {
...(options.ignoreDiagnostics || [])
].map(Number)

const ignore = options.skipIgnore ? [] : (
options.ignore || ['/node_modules/']
).map(str => new RegExp(str))
const ignore = options.skipIgnore ? [] : (options.ignore || ['/node_modules/']).map(str => new RegExp(str))

// Require the TypeScript compiler and configuration.
const cwd = process.cwd()
Expand Down Expand Up @@ -369,31 +369,37 @@ export function register (opts: Options = {}): Register {
const sourceFile = builderProgram.getSourceFile(fileName)
if (!sourceFile) throw new TypeError(`Unable to read file: ${fileName}`)

const diagnostics = ts.getPreEmitDiagnostics(builderProgram.getProgram(), sourceFile)
const program = builderProgram.getProgram()
const diagnostics = ts.getPreEmitDiagnostics(program, sourceFile)
const diagnosticList = filterDiagnostics(diagnostics, ignoreDiagnostics)

if (diagnosticList.length) reportTSError(diagnosticList)

const result = builderProgram.emit(sourceFile, (path, file) => {
const result = builderProgram.emit(sourceFile, (path, file, writeByteOrderMark) => {
if (path.endsWith('.map')) {
output[1] = file
} else {
output[0] = file
}

if (options.build) sys.writeFile(path, file, writeByteOrderMark)
}, undefined, undefined, getCustomTransformers())

if (result.emitSkipped) {
throw new TypeError(`${relative(cwd, fileName)}: Emit skipped`)
}

// Throw an error when requiring `.d.ts` files.
// Throw an error when requiring files that cannot be compiled.
if (output[0] === '') {
if (program.isSourceFileFromExternalLibrary(sourceFile)) {
throw new TypeError(`Unable to compile file from external library: ${relative(cwd, fileName)}`)
}

throw new TypeError(
'Unable to require `.d.ts` file.\n' +
`Unable to require file: ${relative(cwd, fileName)}\n` +
'This is usually the result of a faulty configuration or import. ' +
'Make sure there is a `.js`, `.json` or another executable extension and ' +
'loader (attached before `ts-node`) available alongside ' +
`\`${basename(fileName)}\`.`
'Make sure there is a `.js`, `.json` or other executable extension with ' +
'loader attached before `ts-node` available.'
)
}

Expand All @@ -420,7 +426,8 @@ export function register (opts: Options = {}): Register {
}
}

if (config.options.incremental) {
// Write `.tsbuildinfo` when `--build` is enabled.
if (options.build && config.options.incremental) {
process.on('exit', () => {
// Emits `.tsbuildinfo` to filesystem.
(builderProgram.getProgram() as any).emitBuildInfo()
Expand Down
1 change: 1 addition & 0 deletions tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!node_modules/
4 changes: 2 additions & 2 deletions tests/emit-compiled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ extensions.forEach(ext => {
const old = require.extensions[ext]

require.extensions[ext] = (m, path) => {
const _compile = m._compile
const _compile = (m as any)._compile

m._compile = (code, path) => {
;(m as any)._compile = (code, path) => {
console.error(code)
return _compile.call(this, code, path)
}
Expand Down
1 change: 1 addition & 0 deletions tests/from-node-modules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import 'test'
3 changes: 3 additions & 0 deletions tests/node_modules/test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions tests/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
"compilerOptions": {
"jsx": "react",
"noEmit": true,
// Global type definitions.
"typeRoots": [
"./typings",
"../node_modules/@types"
]
"../node_modules/@types",
],
}
}
11 changes: 0 additions & 11 deletions tests/tsconfig.json5

This file was deleted.

0 comments on commit e0a83b5

Please sign in to comment.