Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Un-deprecate scope and scopedir; add to CLI, tsconfig.json, and env vars #1367

Merged
merged 3 commits into from Jun 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 32 additions & 23 deletions src/bin.ts
Expand Up @@ -51,6 +51,8 @@ export function main(
'--prefer-ts-exts': Boolean,
'--log-error': Boolean,
'--emit': Boolean,
'--scope': Boolean,
'--scope-dir': String,

// Aliases.
'-e': '--eval',
Expand All @@ -69,6 +71,7 @@ export function main(
'-O': '--compiler-options',
'--dir': '--cwd',
'--showConfig': '--show-config',
'--scopeDir': '--scope-dir',
},
{
argv,
Expand Down Expand Up @@ -107,6 +110,8 @@ export function main(
'--prefer-ts-exts': preferTsExts,
'--log-error': logError,
'--emit': emit,
'--scope': scope = undefined,
'--scope-dir': scopeDir = undefined,
} = args;

if (help) {
Expand All @@ -115,32 +120,34 @@ export function main(

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
-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
-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
-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
--prefer-ts-exts Prefer importing TypeScript files over JavaScript files
--log-error Logs TypeScript errors to stderr instead of throwing exceptions
--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
--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
`);

process.exit(0);
Expand Down Expand Up @@ -183,6 +190,8 @@ export function main(
readFile: code !== undefined ? evalAwarePartialHost.readFile : undefined,
fileExists:
code !== undefined ? evalAwarePartialHost.fileExists : undefined,
scope,
scopeDir,
});

// Bind REPL service to ts-node compiler service (chicken-and-egg problem)
Expand Down
21 changes: 17 additions & 4 deletions src/configuration.ts
Expand Up @@ -107,7 +107,7 @@ export function readConfig(
// Fix ts-node options that come from tsconfig.json
const tsNodeOptionsFromTsconfig: TsConfigOptions = Object.assign(
{},
filterRecognizedTsConfigTsNodeOptions(config['ts-node'])
filterRecognizedTsConfigTsNodeOptions(config['ts-node']).recognized
);

// Remove resolution of "files".
Expand Down Expand Up @@ -160,6 +160,8 @@ export function readConfig(
)
);

// Some options are relative to the config file, so must be converted to absolute paths here

if (tsNodeOptionsFromTsconfig.require) {
// Modules are found relative to the tsconfig file, not the `dir` option
const tsconfigRelativeRequire = createRequire(configFilePath!);
Expand All @@ -169,6 +171,12 @@ export function readConfig(
}
);
}
if (tsNodeOptionsFromTsconfig.scopeDir) {
tsNodeOptionsFromTsconfig.scopeDir = resolve(
basePath,
tsNodeOptionsFromTsconfig.scopeDir
);
}

return { configFilePath, config: fixedConfig, tsNodeOptionsFromTsconfig };
}
Expand All @@ -179,8 +187,8 @@ export function readConfig(
*/
function filterRecognizedTsConfigTsNodeOptions(
jsonObject: any
): TsConfigOptions {
if (jsonObject == null) return jsonObject;
): { recognized: TsConfigOptions; unrecognized: any } {
if (jsonObject == null) return { recognized: jsonObject, unrecognized: {} };
const {
compiler,
compilerHost,
Expand All @@ -197,6 +205,9 @@ function filterRecognizedTsConfigTsNodeOptions(
transpileOnly,
typeCheck,
transpiler,
scope,
scopeDir,
...unrecognized
} = jsonObject as TsConfigOptions;
const filteredTsConfigOptions = {
compiler,
Expand All @@ -214,9 +225,11 @@ function filterRecognizedTsConfigTsNodeOptions(
transpileOnly,
typeCheck,
transpiler,
scope,
scopeDir,
};
// Use the typechecker to make sure this implementation has the correct set of properties
const catchExtraneousProps: keyof TsConfigOptions = (null as any) as keyof typeof filteredTsConfigOptions;
const catchMissingProps: keyof typeof filteredTsConfigOptions = (null as any) as keyof TsConfigOptions;
return filteredTsConfigOptions;
return { recognized: filteredTsConfigOptions, unrecognized };
}
8 changes: 4 additions & 4 deletions src/index.ts
Expand Up @@ -73,8 +73,8 @@ export interface ProcessEnv {
/** @deprecated */
TS_NODE_DIR?: string;
TS_NODE_EMIT?: string;
/** @deprecated */
TS_NODE_SCOPE?: string;
TS_NODE_SCOPE_DIR?: string;
TS_NODE_FILES?: string;
TS_NODE_PRETTY?: string;
TS_NODE_COMPILER?: string;
Expand Down Expand Up @@ -296,8 +296,6 @@ export interface TsConfigOptions
| 'dir'
| 'cwd'
| 'projectSearchDir'
| 'scope'
| 'scopeDir'
| 'experimentalEsmLoader'
> {}

Expand All @@ -318,6 +316,7 @@ export const DEFAULTS: RegisterOptions = {
cwd: env.TS_NODE_CWD ?? env.TS_NODE_DIR,
emit: yn(env.TS_NODE_EMIT),
scope: yn(env.TS_NODE_SCOPE),
scopeDir: env.TS_NODE_SCOPE_DIR,
files: yn(env.TS_NODE_FILES),
pretty: yn(env.TS_NODE_PRETTY),
compiler: env.TS_NODE_COMPILER,
Expand Down Expand Up @@ -1292,7 +1291,8 @@ function registerExtension(
m._compile = function (code: string, fileName: string) {
debug('module._compile', fileName);

return _compile.call(this, service.compile(code, fileName), fileName);
const result = service.compile(code, fileName);
return _compile.call(this, result, fileName);
};

return old(m, filename);
Expand Down
84 changes: 45 additions & 39 deletions src/test/index.spec.ts
Expand Up @@ -923,6 +923,14 @@ test.suite('ts-node', (test) => {
expect(stderr).to.contain('Error: --show-config requires');
});
}

test('should support compiler scope specified via tsconfig.json', async (t) => {
const { err, stderr, stdout } = await exec(
`${cmd} --project ./scope/c/config/tsconfig.json ./scope/c/index.js`
);
expect(err).to.equal(null);
expect(stdout).to.equal(`value\nFailures: 0\n`);
});
});

test.suite('register', (_test) => {
Expand Down Expand Up @@ -977,53 +985,51 @@ test.suite('ts-node', (test) => {
expect(() => require(moduleTestPath)).to.not.throw();
});

if (semver.gte(ts.version, '2.7.0')) {
test('should support compiler scopes', ({
context: { registered, moduleTestPath },
}) => {
const calls: string[] = [];
test('should support compiler scopes', ({
context: { registered, moduleTestPath },
}) => {
const calls: string[] = [];

registered.enabled(false);
registered.enabled(false);

const compilers = [
register({
projectSearchDir: join(TEST_DIR, 'scope/a'),
scopeDir: join(TEST_DIR, 'scope/a'),
scope: true,
}),
register({
projectSearchDir: join(TEST_DIR, 'scope/a'),
scopeDir: join(TEST_DIR, 'scope/b'),
scope: true,
}),
];
const compilers = [
register({
projectSearchDir: join(TEST_DIR, 'scope/a'),
scopeDir: join(TEST_DIR, 'scope/a'),
scope: true,
}),
register({
projectSearchDir: join(TEST_DIR, 'scope/a'),
scopeDir: join(TEST_DIR, 'scope/b'),
scope: true,
}),
];

compilers.forEach((c) => {
const old = c.compile;
c.compile = (code, fileName, lineOffset) => {
calls.push(fileName);
compilers.forEach((c) => {
const old = c.compile;
c.compile = (code, fileName, lineOffset) => {
calls.push(fileName);

return old(code, fileName, lineOffset);
};
});
return old(code, fileName, lineOffset);
};
});

try {
expect(require('../../tests/scope/a').ext).to.equal('.ts');
expect(require('../../tests/scope/b').ext).to.equal('.ts');
} finally {
compilers.forEach((c) => c.enabled(false));
}
try {
expect(require('../../tests/scope/a').ext).to.equal('.ts');
expect(require('../../tests/scope/b').ext).to.equal('.ts');
} finally {
compilers.forEach((c) => c.enabled(false));
}

expect(calls).to.deep.equal([
join(TEST_DIR, 'scope/a/index.ts'),
join(TEST_DIR, 'scope/b/index.ts'),
]);
expect(calls).to.deep.equal([
join(TEST_DIR, 'scope/a/index.ts'),
join(TEST_DIR, 'scope/b/index.ts'),
]);

delete require.cache[moduleTestPath];
delete require.cache[moduleTestPath];

expect(() => require(moduleTestPath)).to.throw();
});
}
expect(() => require(moduleTestPath)).to.throw();
});

test('should compile through js and ts', () => {
const m = require('../../tests/complex');
Expand Down
1 change: 1 addition & 0 deletions tests/scope/c/config/index.ts
@@ -0,0 +1 @@
export const a: string = 'value';
1 change: 1 addition & 0 deletions tests/scope/c/config/scopedir/index.ts
@@ -0,0 +1 @@
export const a: string = 'value';
6 changes: 6 additions & 0 deletions tests/scope/c/config/tsconfig.json
@@ -0,0 +1,6 @@
{
"ts-node": {
"scope": true,
"scopeDir": "./scopedir"
}
}
26 changes: 26 additions & 0 deletions tests/scope/c/index.js
@@ -0,0 +1,26 @@
let failures = 0;
try {
// This should fail with an error because it is outside scopedir
require('./scopedir/index');
failures++;
} catch (e) {
// good
}

try {
// This should fail with an error because it is outside scopedir
require('./config/index');
failures++;
} catch (e) {
// good
}

try {
// this should succeed
console.log(require('./config/scopedir/index').a);
} catch (e) {
// bad
failures++;
}

console.log(`Failures: ${failures}`);
1 change: 1 addition & 0 deletions tests/scope/c/scopedir/index.ts
@@ -0,0 +1 @@
export const a: string = 'value';
2 changes: 2 additions & 0 deletions website/docs/options.md
Expand Up @@ -49,6 +49,8 @@ _Environment variables, where available, are in `ALL_CAPS`_
- `-r, --require [path]` Require a node module before execution
- `--cwd` Behave as if invoked in this working directory <br/>*Default:* `process.cwd()`<br/>*Environment:* `TS_NODE_CWD`
- `--emit` Emit output files into `.ts-node` directory <br/>*Default:* `false` <br/>*Environment:* `TS_NODE_EMIT`
- `--scope` Scope compiler to files within `scopeDir`. Anything outside this directory is ignored. <br/>*Default: `false` <br/>*Environment:* `TS_NODE_SCOPE`
- `--scopeDir` Directory within which compiler is limited when `scope` is enabled. <br/>*Default:* First of: `tsconfig.json` "rootDir" if specified, directory containing `tsconfig.json`, or cwd if no `tsconfig.json` is loaded.<br/>*Environment:* `TS_NODE_SCOPE_DIR`
- `TS_NODE_HISTORY` Path to history file for REPL <br/>*Default:* `~/.ts_node_repl_history`<br/>

## API
Expand Down