Skip to content

Commit

Permalink
Replace minimist with type-safe arg (#759)
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey authored Jan 21, 2019
1 parent f148008 commit d9f310e
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 106 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ ts-node
ts-node -e 'console.log("Hello, world!")'

# Execute, and print, code with TypeScript.
ts-node -p '"Hello, world!"'
ts-node -p -e '"Hello, world!"'

# Pipe scripts to execute with TypeScript.
echo "console.log('Hello, world!')" | ts-node
Expand Down Expand Up @@ -123,13 +123,13 @@ Supports `--print`, `--eval` and `--require` from [node.js CLI options](https://

_Environment variable denoted in parentheses._

* `-T, --transpileOnly` Use TypeScript's faster `transpileModule` (`TS_NODE_TRANSPILE_ONLY`, default: `false`)
* `-T, --transpile-only` Use TypeScript's faster `transpileModule` (`TS_NODE_TRANSPILE_ONLY`, default: `false`)
* `--cacheDirectory` Configure the output file cache directory (`TS_NODE_CACHE_DIRECTORY`)
* `-I, --ignore [pattern]` Override the path patterns to skip compilation (`TS_NODE_IGNORE`, default: `/node_modules/`)
* `-P, --project [path]` Path to TypeScript JSON project file (`TS_NODE_PROJECT`)
* `-C, --compiler [name]` Specify a custom TypeScript compiler (`TS_NODE_COMPILER`, default: `typescript`)
* `-D, --ignoreDiagnostics [code]` Ignore TypeScript warnings by diagnostic code (`TS_NODE_IGNORE_DIAGNOSTICS`)
* `-O, --compilerOptions [opts]` JSON object to merge with compiler options (`TS_NODE_COMPILER_OPTIONS`)
* `-D, --ignore-diagnostics [code]` Ignore TypeScript warnings by diagnostic code (`TS_NODE_IGNORE_DIAGNOSTICS`)
* `-O, --compiler-options [opts]` JSON object to merge with compiler options (`TS_NODE_COMPILER_OPTIONS`)
* `--files` Load files from `tsconfig.json` on startup (`TS_NODE_FILES`, default: `false`)
* `--pretty` Use pretty diagnostic formatter (`TS_NODE_PRETTY`, default: `false`)
* `--no-cache` Disable the local TypeScript Node cache (`TS_NODE_CACHE`, default: `true`)
Expand Down
11 changes: 6 additions & 5 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"@types/semver": "^5.3.34",
"@types/source-map-support": "^0.4.0",
"@types/yn": "types/npm-yn#ca75f6c82940fae6a06fb41d2d37a6aa9b4ea8e9",
"arg": "^4.1.0",
"chai": "^4.0.1",
"istanbul": "^0.4.0",
"mocha": "^5.0.1",
Expand All @@ -78,7 +79,6 @@
"arrify": "^1.0.0",
"diff": "^3.1.0",
"make-error": "^1.1.1",
"minimist": "^1.2.0",
"source-map-support": "^0.5.6",
"yn": "^2.0.0"
}
Expand Down
168 changes: 85 additions & 83 deletions src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,79 +3,76 @@
import { join, resolve } from 'path'
import { start, Recoverable } from 'repl'
import { inspect } from 'util'
import arrify = require('arrify')
import Module = require('module')
import minimist = require('minimist')
import arg = require('arg')
import { diffLines } from 'diff'
import { Script } from 'vm'
import { readFileSync, statSync } from 'fs'
import { register, VERSION, DEFAULTS, TSError, parse } from './index'

interface Argv {
const args = arg({
// Node.js-like options.
eval?: string
print?: string
require?: string | string[]
// CLI options.
help?: boolean
version?: boolean
// Register options.
pretty?: boolean
typeCheck?: boolean
transpileOnly?: boolean
files?: boolean
compiler?: string
ignore?: string | string[]
project?: string
skipIgnore?: boolean
skipProject?: boolean
ignoreDiagnostics?: string | string[]
compilerOptions?: string
_: string[]
}
'--eval': String,
'--print': Boolean,
'--require': [String],

const argv = minimist<Argv>(process.argv.slice(2), {
stopEarly: true,
string: ['eval', 'print', 'compiler', 'project', 'ignoreDiagnostics', 'require', 'ignore'],
boolean: ['help', 'transpileOnly', 'typeCheck', 'version', 'files', 'pretty', 'skipProject', 'skipIgnore'],
alias: {
eval: ['e'],
print: ['p'],
require: ['r'],
help: ['h'],
version: ['v'],
typeCheck: ['type-check'],
transpileOnly: ['T', 'transpile-only'],
ignore: ['I'],
project: ['P'],
skipIgnore: ['skip-ignore'],
skipProject: ['skip-project'],
compiler: ['C'],
ignoreDiagnostics: ['D', 'ignore-diagnostics'],
compilerOptions: ['O', 'compiler-options']
},
default: {
files: DEFAULTS.files,
pretty: DEFAULTS.pretty,
typeCheck: DEFAULTS.typeCheck,
transpileOnly: DEFAULTS.transpileOnly,
ignore: DEFAULTS.ignore,
project: DEFAULTS.project,
skipIgnore: DEFAULTS.skipIgnore,
skipProject: DEFAULTS.skipProject,
compiler: DEFAULTS.compiler,
ignoreDiagnostics: DEFAULTS.ignoreDiagnostics
}
// CLI options.
'--files': Boolean,
'--help': Boolean,
'--version': arg.COUNT,

// Project options.
'--compiler': String,
'--compiler-options': parse,
'--project': String,
'--ignore-diagnostics': [String],
'--ignore': [String],
'--transpile-only': Boolean,
'--type-check': Boolean,
'--pretty': Boolean,
'--skip-project': Boolean,
'--skip-ignore': Boolean,

// Aliases.
'-e': '--eval',
'-p': '--print',
'-r': '--require',
'-h': '--help',
'-v': '--version',
'-T': '--transpile-only',
'-I': '--ignore',
'-P': '--project',
'-C': '--compiler',
'-D': '--ignore-diagnostics',
'-O': '--compiler-options'
}, {
stopAtPositional: true
})

if (argv.help) {
const {
'--help': help = false,
'--version': version = 0,
'--files': files = DEFAULTS.files,
'--compiler': compiler = DEFAULTS.compiler,
'--compiler-options': compilerOptions = DEFAULTS.compilerOptions,
'--project': project = DEFAULTS.project,
'--ignore-diagnostics': ignoreDiagnostics = DEFAULTS.ignoreDiagnostics,
'--ignore': ignore = DEFAULTS.ignore,
'--transpile-only': transpileOnly = DEFAULTS.transpileOnly,
'--type-check': typeCheck = DEFAULTS.typeCheck,
'--pretty': pretty = DEFAULTS.pretty,
'--skip-project': skipProject = DEFAULTS.skipProject,
'--skip-ignore': skipIgnore = DEFAULTS.skipIgnore
} = args

if (help) {
console.log(`
Usage: ts-node [options] [ -e script | script.ts ] [arguments]
Options:
-e, --eval [code] Evaluate code
-p, --print [code] Evaluate code and print result
-p, --print Print result of \`--eval\`
-r, --require [path] Require a node module before execution
-h, --help Print CLI usage
Expand All @@ -85,8 +82,8 @@ Options:
-I, --ignore [pattern] Override the path patterns to skip compilation
-P, --project [path] Path to TypeScript JSON project file
-C, --compiler [name] Specify a custom TypeScript compiler
-D, --ignoreDiagnostics [code] Ignore TypeScript warnings by diagnostic code
-O, --compilerOptions [opts] JSON object to merge with compiler options
-D, --ignore-diagnostics [code] Ignore TypeScript warnings by diagnostic code
-O, --compiler-options [opts] JSON object to merge with compiler options
--files Load files from \`tsconfig.json\` on startup
--pretty Use pretty diagnostic formatter
Expand All @@ -97,38 +94,43 @@ Options:
process.exit(0)
}

// Output project information.
if (version === 1) {
console.log(`v${VERSION}`)
process.exit(0)
}

const cwd = process.cwd()
const code = argv.eval === undefined ? argv.print : argv.eval
const isEval = typeof argv.eval === 'string' || !!argv.print // Minimist struggles with empty strings.
const isPrinted = argv.print !== undefined
const code = args['--eval']
const isPrinted = args['--print'] !== undefined

// Register the TypeScript compiler instance.
const service = register({
files: argv.files,
pretty: argv.pretty,
typeCheck: argv.typeCheck,
transpileOnly: argv.transpileOnly,
ignore: argv.ignore,
project: argv.project,
skipIgnore: argv.skipIgnore,
skipProject: argv.skipProject,
compiler: argv.compiler,
ignoreDiagnostics: argv.ignoreDiagnostics,
compilerOptions: parse(argv.compilerOptions) || DEFAULTS.compilerOptions,
readFile: isEval ? readFileEval : undefined,
fileExists: isEval ? fileExistsEval : undefined
files,
pretty,
typeCheck,
transpileOnly,
ignore,
project,
skipIgnore,
skipProject,
compiler,
ignoreDiagnostics,
compilerOptions,
readFile: code ? readFileEval : undefined,
fileExists: code ? fileExistsEval : undefined
})

// Output project information.
if (argv.version) {
if (version >= 2) {
console.log(`ts-node v${VERSION}`)
console.log(`node ${process.version}`)
console.log(`typescript v${service.ts.version}`)
console.log(`compiler v${service.ts.version}`)
process.exit(0)
}

// Require specified modules before start-up.
(Module as any)._preloadModules(arrify(argv.require))
if (args['--require']) (Module as any)._preloadModules(args['--require'])

/**
* Eval helpers.
Expand All @@ -138,16 +140,16 @@ 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 (isEval) {
evalAndExit(code as string, isPrinted)
if (code) {
evalAndExit(code, isPrinted)
} else {
if (argv._.length) {
process.argv = ['node'].concat(resolve(cwd, argv._[0])).concat(argv._.slice(1))
if (args._.length) {
process.argv = ['node'].concat(resolve(cwd, args._[0])).concat(args._.slice(1))
process.execArgv.unshift(__filename)
Module.runMain()
} else {
// Piping of execution _only_ occurs when no other script is specified.
if ((process.stdin as any).isTTY) {
if (process.stdin.isTTY) {
startRepl()
} else {
let code = ''
Expand Down
16 changes: 8 additions & 8 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('ts-node', function () {
})

it('should print scripts', function (done) {
exec(`${BIN_EXEC} -p "import { example } from './tests/complex/index';example()"`, function (err, stdout) {
exec(`${BIN_EXEC} -pe "import { example } from './tests/complex/index';example()"`, function (err, stdout) {
expect(err).to.equal(null)
expect(stdout).to.equal('example\n')

Expand All @@ -67,7 +67,7 @@ describe('ts-node', function () {
[
BIN_EXEC,
'-O "{\\\"allowJs\\\":true}"',
'-p "import { main } from \'./tests/allow-js/run\';main()"'
'-pe "import { main } from \'./tests/allow-js/run\';main()"'
].join(' '),
function (err, stdout) {
expect(err).to.equal(null)
Expand All @@ -83,7 +83,7 @@ describe('ts-node', function () {
[
BIN_EXEC,
'-O "{\\\"allowJs\\\":true}"',
'-p "import { Foo2 } from \'./tests/allow-js/with-jsx\'; Foo2.sayHi()"'
'-pe "import { Foo2 } from \'./tests/allow-js/with-jsx\'; Foo2.sayHi()"'
].join(' '),
function (err, stdout) {
expect(err).to.equal(null)
Expand Down Expand Up @@ -133,7 +133,7 @@ describe('ts-node', function () {

it('should be able to ignore diagnostic', function (done) {
exec(
`${BIN_EXEC} --ignoreDiagnostics 2345 -e "import * as m from './tests/module';console.log(m.example(123))"`,
`${BIN_EXEC} --ignore-diagnostics 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.')
Expand Down Expand Up @@ -166,7 +166,7 @@ describe('ts-node', function () {
})

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

it('should support transpile only mode', function (done) {
exec(`${BIN_EXEC} --transpileOnly -p "x"`, function (err) {
exec(`${BIN_EXEC} --transpile-only -pe "x"`, function (err) {
if (err === null) {
return done('Command was expected to fail, but it succeeded.')
}
Expand Down Expand Up @@ -216,7 +216,7 @@ describe('ts-node', function () {
})

it('should pipe into an eval script', function (done) {
const cp = exec(`${BIN_EXEC} --fast -p 'process.stdin.isTTY'`, function (err, stdout) {
const cp = exec(`${BIN_EXEC} --transpile-only -pe 'process.stdin.isTTY'`, function (err, stdout) {
expect(err).to.equal(null)
expect(stdout).to.equal('undefined\n')

Expand All @@ -227,7 +227,7 @@ describe('ts-node', function () {
})

it('should support require flags', function (done) {
exec(`${BIN_EXEC} -r ./tests/hello-world -p "console.log('success')"`, function (err, stdout) {
exec(`${BIN_EXEC} -r ./tests/hello-world -pe "console.log('success')"`, function (err, stdout) {
expect(err).to.equal(null)
expect(stdout).to.equal('Hello, world!\nsuccess\nundefined\n')

Expand Down
11 changes: 6 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ export interface Options {
transpileOnly?: boolean | null
files?: boolean | null
compiler?: string
ignore?: string | string[]
ignore?: string[]
project?: string
skipIgnore?: boolean | null
skipProject?: boolean | null
compilerOptions?: object
ignoreDiagnostics?: number | string | Array<number | string>
ignoreDiagnostics?: Array<number | string>
readFile?: (path: string) => string | undefined
fileExists?: (path: string) => boolean
transformers?: _ts.CustomTransformers
Expand Down Expand Up @@ -176,11 +176,12 @@ export function register (opts: Options = {}): Register {
const options = Object.assign({}, DEFAULTS, opts)
const originalJsHandler = require.extensions['.js']

const ignoreDiagnostics = arrify(options.ignoreDiagnostics).concat([
const ignoreDiagnostics = [
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)
18003, // "No inputs were found in config file."
...(options.ignoreDiagnostics || [])
].map(Number)

const memoryCache: MemoryCache = {
contents: Object.create(null),
Expand Down

0 comments on commit d9f310e

Please sign in to comment.