Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into import-assertion-support
Browse files Browse the repository at this point in the history
  • Loading branch information
cspotcode committed Jan 21, 2022
2 parents 56c5cb2 + a817289 commit 84efc82
Show file tree
Hide file tree
Showing 19 changed files with 252 additions and 153 deletions.
6 changes: 6 additions & 0 deletions ava.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ export default {
failWithoutAssertions: false,
environmentVariables: {
ts_node_install_lock: `id-${Math.floor(Math.random() * 10e9)}`,
// Force jest expect() errors to generate colorized strings, makes output more readable.
// Delete the env var within ava processes via `require` option below.
// This avoids passing it to spawned processes under test, which would negatively affect
// their behavior.
FORCE_COLOR: '3',
},
require: ['./src/test/remove-env-var-force-color.js'],
timeout: '300s',
};
2 changes: 1 addition & 1 deletion src/bin-cwd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

import { main } from './bin';

main(undefined, { '--cwd-mode': true });
main(undefined, { '--cwdMode': true });
2 changes: 1 addition & 1 deletion src/bin-script-deprecated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ console.warn(
'Please use ts-node-script instead'
);

main(undefined, { '--script-mode': true });
main(undefined, { '--scriptMode': true });
2 changes: 1 addition & 1 deletion src/bin-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

import { main } from './bin';

main(undefined, { '--script-mode': true });
main(undefined, { '--scriptMode': true });
2 changes: 1 addition & 1 deletion src/bin-transpile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

import { main } from './bin';

main(undefined, { '--transpile-only': true });
main(undefined, { '--transpileOnly': true });
181 changes: 104 additions & 77 deletions src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
STDIN_NAME,
REPL_FILENAME,
} from './repl';
import { VERSION, TSError, register } from './index';
import { VERSION, TSError, register, versionGteLt } from './index';
import type { TSInternal } from './ts-compiler-types';
import { addBuiltinLibsToObject } from '../dist-raw/node-cjs-helpers';

Expand All @@ -28,6 +28,20 @@ export function main(
argv: string[] = process.argv.slice(2),
entrypointArgs: Record<string, any> = {}
) {
// HACK: technically, this function is not marked @internal so it's possible
// that libraries in the wild are doing `require('ts-node/dist/bin').main({'--transpile-only': true})`
// We can mark this function @internal in next major release.
// For now, rewrite args to avoid a breaking change.
entrypointArgs = { ...entrypointArgs };
for (const key of Object.keys(entrypointArgs)) {
entrypointArgs[
key.replace(
/([a-z])-([a-z])/g,
(_$0, $1, $2: string) => `${$1}${$2.toUpperCase()}`
)
] = entrypointArgs[key];
}

const args = {
...entrypointArgs,
...arg(
Expand All @@ -40,33 +54,33 @@ export function main(

// CLI options.
'--help': Boolean,
'--cwd-mode': Boolean,
'--script-mode': Boolean,
'--cwdMode': Boolean,
'--scriptMode': Boolean,
'--version': arg.COUNT,
'--show-config': Boolean,
'--showConfig': Boolean,

// Project options.
'--cwd': String,
'--files': Boolean,
'--compiler': String,
'--compiler-options': parse,
'--compilerOptions': parse,
'--project': String,
'--ignore-diagnostics': [String],
'--ignoreDiagnostics': [String],
'--ignore': [String],
'--transpile-only': Boolean,
'--transpileOnly': Boolean,
'--transpiler': String,
'--swc': Boolean,
'--type-check': Boolean,
'--compiler-host': Boolean,
'--typeCheck': Boolean,
'--compilerHost': Boolean,
'--pretty': Boolean,
'--skip-project': Boolean,
'--skip-ignore': Boolean,
'--prefer-ts-exts': Boolean,
'--log-error': Boolean,
'--skipProject': Boolean,
'--skipIgnore': Boolean,
'--preferTsExts': Boolean,
'--logError': Boolean,
'--emit': Boolean,
'--scope': Boolean,
'--scope-dir': String,
'--no-experimental-repl-await': Boolean,
'--scopeDir': String,
'--noExperimentalReplAwait': Boolean,

// Aliases.
'-e': '--eval',
Expand All @@ -76,16 +90,30 @@ export function main(
'-h': '--help',
'-s': '--script-mode',
'-v': '--version',
'-T': '--transpile-only',
'-H': '--compiler-host',
'-T': '--transpileOnly',
'-H': '--compilerHost',
'-I': '--ignore',
'-P': '--project',
'-C': '--compiler',
'-D': '--ignore-diagnostics',
'-O': '--compiler-options',
'-D': '--ignoreDiagnostics',
'-O': '--compilerOptions',
'--dir': '--cwd',
'--showConfig': '--show-config',
'--scopeDir': '--scope-dir',

// Support both tsc-style camelCase and node-style hypen-case for *all* flags
'--cwd-mode': '--cwdMode',
'--script-mode': '--scriptMode',
'--show-config': '--showConfig',
'--compiler-options': '--compilerOptions',
'--ignore-diagnostics': '--ignoreDiagnostics',
'--transpile-only': '--transpileOnly',
'--type-check': '--typeCheck',
'--compiler-host': '--compilerHost',
'--skip-project': '--skipProject',
'--skip-ignore': '--skipIgnore',
'--prefer-ts-exts': '--preferTsExts',
'--log-error': '--logError',
'--scope-dir': '--scopeDir',
'--no-experimental-repl-await': '--noExperimentalReplAwait',
},
{
argv,
Expand All @@ -100,74 +128,74 @@ export function main(
const {
'--cwd': cwdArg,
'--help': help = false,
'--script-mode': scriptMode,
'--cwd-mode': cwdMode,
'--scriptMode': scriptMode,
'--cwdMode': cwdMode,
'--version': version = 0,
'--show-config': showConfig,
'--showConfig': showConfig,
'--require': argsRequire = [],
'--eval': code = undefined,
'--print': print = false,
'--interactive': interactive = false,
'--files': files,
'--compiler': compiler,
'--compiler-options': compilerOptions,
'--compilerOptions': compilerOptions,
'--project': project,
'--ignore-diagnostics': ignoreDiagnostics,
'--ignoreDiagnostics': ignoreDiagnostics,
'--ignore': ignore,
'--transpile-only': transpileOnly,
'--type-check': typeCheck,
'--transpileOnly': transpileOnly,
'--typeCheck': typeCheck,
'--transpiler': transpiler,
'--swc': swc,
'--compiler-host': compilerHost,
'--compilerHost': compilerHost,
'--pretty': pretty,
'--skip-project': skipProject,
'--skip-ignore': skipIgnore,
'--prefer-ts-exts': preferTsExts,
'--log-error': logError,
'--skipProject': skipProject,
'--skipIgnore': skipIgnore,
'--preferTsExts': preferTsExts,
'--logError': logError,
'--emit': emit,
'--scope': scope = undefined,
'--scope-dir': scopeDir = undefined,
'--no-experimental-repl-await': noExperimentalReplAwait,
'--scopeDir': scopeDir = undefined,
'--noExperimentalReplAwait': noExperimentalReplAwait,
} = args;

if (help) {
console.log(`
Usage: ts-node [options] [ -e script | script.ts ] [arguments]
Options:
-e, --eval [code] Evaluate code
-p, --print Print result of \`--eval\`
-r, --require [path] Require a node module before execution
-i, --interactive Opens the REPL even if stdin does not appear to be a terminal
-h, --help Print CLI usage
-v, --version Print module version information
--cwd-mode Use current directory instead of <script.ts> for config resolution
--show-config Print resolved configuration and exit
-T, --transpile-only Use TypeScript's faster \`transpileModule\` or a third-party transpiler
--swc Use the swc transpiler
-H, --compiler-host Use TypeScript's compiler host API
-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
--transpiler [name] Specify a third-party, non-typechecking transpiler
-D, --ignore-diagnostics [code] Ignore TypeScript warnings by diagnostic code
-O, --compiler-options [opts] JSON object to merge with compiler options
--cwd Behave as if invoked within this working directory.
--files Load \`files\`, \`include\` and \`exclude\` from \`tsconfig.json\` on startup
--pretty Use pretty diagnostic formatter (usually enabled by default)
--skip-project Skip reading \`tsconfig.json\`
--skip-ignore Skip \`--ignore\` checks
--emit Emit output files into \`.ts-node\` directory
--scope Scope compiler to files within \`scopeDir\`. Anything outside this directory is ignored.
--scope-dir Directory for \`--scope\`
--prefer-ts-exts Prefer importing TypeScript files over JavaScript files
--log-error Logs TypeScript errors to stderr instead of throwing exceptions
--no-experimental-repl-await Disable top-level await in REPL. Equivalent to node's --no-experimental-repl-await
`);
Usage: ts-node [options] [ -e script | script.ts ] [arguments]
Options:
-e, --eval [code] Evaluate code
-p, --print Print result of \`--eval\`
-r, --require [path] Require a node module before execution
-i, --interactive Opens the REPL even if stdin does not appear to be a terminal
-h, --help Print CLI usage
-v, --version Print module version information
--cwdMode Use current directory instead of <script.ts> for config resolution
--showConfig Print resolved configuration and exit
-T, --transpileOnly Use TypeScript's faster \`transpileModule\` or a third-party transpiler
--swc Use the swc transpiler
-H, --compilerHost Use TypeScript's compiler host API
-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
--transpiler [name] Specify a third-party, non-typechecking transpiler
-D, --ignoreDiagnostics [code] Ignore TypeScript warnings by diagnostic code
-O, --compilerOptions [opts] JSON object to merge with compiler options
--cwd Behave as if invoked within this working directory.
--files Load \`files\`, \`include\` and \`exclude\` from \`tsconfig.json\` on startup
--pretty Use pretty diagnostic formatter (usually enabled by default)
--skipProject Skip reading \`tsconfig.json\`
--skipIgnore Skip \`--ignore\` checks
--emit Emit output files into \`.ts-node\` directory
--scope Scope compiler to files within \`scopeDir\`. Anything outside this directory is ignored.
--scopeDir Directory for \`--scope\`
--preferTsExts Prefer importing TypeScript files over JavaScript files
--logError Logs TypeScript errors to stderr instead of throwing exceptions
--noExperimentalReplAwait Disable top-level await in REPL. Equivalent to node's --no-experimental-repl-await
`);

process.exit(0);
}
Expand Down Expand Up @@ -427,12 +455,11 @@ let guaranteedNonexistentDirectorySuffix = 0;
* https://stackoverflow.com/questions/59865584/how-to-invalidate-cached-require-resolve-results
*/
function requireResolveNonCached(absoluteModuleSpecifier: string) {
// node 10 and 11 fallback: The trick below triggers a node 10 & 11 bug
// On those node versions, pollute the require cache instead. This is a deliberate
// ts-node limitation that will *rarely* manifest, and will not matter once node 10
// is end-of-life'd on 2021-04-30
const isSupportedNodeVersion =
parseInt(process.versions.node.split('.')[0], 10) >= 12;
// node <= 12.1.x fallback: The trick below triggers a node bug on old versions.
// On these old versions, pollute the require cache instead. This is a deliberate
// ts-node limitation that will *rarely* manifest, and will not matter once node 12
// is end-of-life'd on 2022-04-30
const isSupportedNodeVersion = versionGteLt(process.versions.node, '12.2.0');
if (!isSupportedNodeVersion) return require.resolve(absoluteModuleSpecifier);

const { dir, base } = parsePath(absoluteModuleSpecifier);
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,8 @@ export interface Service {
installSourceMapSupport(): void;
/** @internal */
enableExperimentalEsmLoaderInterop(): void;
/** @internal */
transpileOnly: boolean;
}

/**
Expand Down Expand Up @@ -1330,6 +1332,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
addDiagnosticFilter,
installSourceMapSupport,
enableExperimentalEsmLoaderInterop,
transpileOnly,
};
}

Expand Down
43 changes: 32 additions & 11 deletions src/repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,16 +368,20 @@ export function createRepl(options: CreateReplOptions = {}) {
// those starting with _
// those containing /
// those that already exist as globals
// Intentionally suppress type errors in case @types/node does not declare any of them.
state.input += `// @ts-ignore\n${builtinModules
.filter(
(name) =>
!name.startsWith('_') &&
!name.includes('/') &&
!['console', 'module', 'process'].includes(name)
)
.map((name) => `declare import ${name} = require('${name}')`)
.join(';')}\n`;
// Intentionally suppress type errors in case @types/node does not declare any of them, and because
// `declare import` is technically invalid syntax.
// Avoid this when in transpileOnly, because third-party transpilers may not handle `declare import`.
if (!service?.transpileOnly) {
state.input += `// @ts-ignore\n${builtinModules
.filter(
(name) =>
!name.startsWith('_') &&
!name.includes('/') &&
!['console', 'module', 'process'].includes(name)
)
.map((name) => `declare import ${name} = require('${name}')`)
.join(';')}\n`;
}
}

reset();
Expand Down Expand Up @@ -480,6 +484,8 @@ export function createEvalAwarePartialHost(
return { readFile, fileExists };
}

const sourcemapCommentRe = /\/\/# ?sourceMappingURL=\S+[\s\r\n]*$/;

type AppendCompileAndEvalInputResult =
| { containsTopLevelAwait: true; valuePromise: Promise<any> }
| { containsTopLevelAwait: false; value: any };
Expand Down Expand Up @@ -525,8 +531,23 @@ function appendCompileAndEvalInput(options: {

output = adjustUseStrict(output);

// Note: REPL does not respect sourcemaps!
// To properly do that, we'd need to prefix the code we eval -- which comes
// from `diffLines` -- with newlines so that it's at the proper line numbers.
// Then we'd need to ensure each bit of eval-ed code, if there are multiples,
// has the sourcemap appended to it.
// We might also need to integrate with our sourcemap hooks' cache; I'm not sure.
const outputWithoutSourcemapComment = output.replace(sourcemapCommentRe, '');
const oldOutputWithoutSourcemapComment = state.output.replace(
sourcemapCommentRe,
''
);

// Use `diff` to check for new JavaScript to execute.
const changes = diffLines(state.output, output);
const changes = diffLines(
oldOutputWithoutSourcemapComment,
outputWithoutSourcemapComment
);

if (isCompletion) {
undo();
Expand Down
2 changes: 1 addition & 1 deletion src/test/exec-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ChildProcess, ExecException, ExecOptions } from 'child_process';
import { exec as childProcessExec } from 'child_process';
import * as expect from 'expect';
import { expect } from './testlib';

export type ExecReturn = Promise<ExecResult> & { child: ChildProcess };
export interface ExecResult {
Expand Down
1 change: 0 additions & 1 deletion src/test/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import type * as tsNodeTypes from '../index';
import type _createRequire from 'create-require';
import { has, once } from 'lodash';
import semver = require('semver');
import * as expect from 'expect';
const createRequire: typeof _createRequire = require('create-require');
export { tsNodeTypes };

Expand Down
1 change: 1 addition & 0 deletions src/test/remove-env-var-force-color.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
delete process.env.FORCE_COLOR;
Loading

0 comments on commit 84efc82

Please sign in to comment.