Skip to content

Commit 17b3a55

Browse files
authored
Un-deprecate scope and scopedir; add to CLI, tsconfig.json, and env vars (#1367)
* re-add scope and scopeDir to CLI; add env vars * fix tests and bug found by tests * Fix typo
1 parent 7cac7df commit 17b3a55

File tree

10 files changed

+135
-70
lines changed

10 files changed

+135
-70
lines changed

src/bin.ts

+32-23
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ export function main(
5151
'--prefer-ts-exts': Boolean,
5252
'--log-error': Boolean,
5353
'--emit': Boolean,
54+
'--scope': Boolean,
55+
'--scope-dir': String,
5456

5557
// Aliases.
5658
'-e': '--eval',
@@ -69,6 +71,7 @@ export function main(
6971
'-O': '--compiler-options',
7072
'--dir': '--cwd',
7173
'--showConfig': '--show-config',
74+
'--scopeDir': '--scope-dir',
7275
},
7376
{
7477
argv,
@@ -107,6 +110,8 @@ export function main(
107110
'--prefer-ts-exts': preferTsExts,
108111
'--log-error': logError,
109112
'--emit': emit,
113+
'--scope': scope = undefined,
114+
'--scope-dir': scopeDir = undefined,
110115
} = args;
111116

112117
if (help) {
@@ -115,32 +120,34 @@ export function main(
115120
116121
Options:
117122
118-
-e, --eval [code] Evaluate code
119-
-p, --print Print result of \`--eval\`
120-
-r, --require [path] Require a node module before execution
121-
-i, --interactive Opens the REPL even if stdin does not appear to be a terminal
122-
123-
-h, --help Print CLI usage
124-
-v, --version Print module version information
125-
--cwd-mode Use current directory instead of <script.ts> for config resolution
126-
--show-config Print resolved configuration and exit
127-
128-
-T, --transpile-only Use TypeScript's faster \`transpileModule\` or a third-party transpiler
129-
-H, --compiler-host Use TypeScript's compiler host API
130-
-I, --ignore [pattern] Override the path patterns to skip compilation
131-
-P, --project [path] Path to TypeScript JSON project file
132-
-C, --compiler [name] Specify a custom TypeScript compiler
133-
--transpiler [name] Specify a third-party, non-typechecking transpiler
123+
-e, --eval [code] Evaluate code
124+
-p, --print Print result of \`--eval\`
125+
-r, --require [path] Require a node module before execution
126+
-i, --interactive Opens the REPL even if stdin does not appear to be a terminal
127+
128+
-h, --help Print CLI usage
129+
-v, --version Print module version information
130+
--cwd-mode Use current directory instead of <script.ts> for config resolution
131+
--show-config Print resolved configuration and exit
132+
133+
-T, --transpile-only Use TypeScript's faster \`transpileModule\` or a third-party transpiler
134+
-H, --compiler-host Use TypeScript's compiler host API
135+
-I, --ignore [pattern] Override the path patterns to skip compilation
136+
-P, --project [path] Path to TypeScript JSON project file
137+
-C, --compiler [name] Specify a custom TypeScript compiler
138+
--transpiler [name] Specify a third-party, non-typechecking transpiler
134139
-D, --ignore-diagnostics [code] Ignore TypeScript warnings by diagnostic code
135140
-O, --compiler-options [opts] JSON object to merge with compiler options
136141
137-
--cwd Behave as if invoked within this working directory.
138-
--files Load \`files\`, \`include\` and \`exclude\` from \`tsconfig.json\` on startup
139-
--pretty Use pretty diagnostic formatter (usually enabled by default)
140-
--skip-project Skip reading \`tsconfig.json\`
141-
--skip-ignore Skip \`--ignore\` checks
142-
--prefer-ts-exts Prefer importing TypeScript files over JavaScript files
143-
--log-error Logs TypeScript errors to stderr instead of throwing exceptions
142+
--cwd Behave as if invoked within this working directory.
143+
--files Load \`files\`, \`include\` and \`exclude\` from \`tsconfig.json\` on startup
144+
--pretty Use pretty diagnostic formatter (usually enabled by default)
145+
--skip-project Skip reading \`tsconfig.json\`
146+
--skip-ignore Skip \`--ignore\` checks
147+
--scope Scope compiler to files within \`scopeDir\`. Anything outside this directory is ignored.
148+
--scope-dir Directory for \`--scope\`
149+
--prefer-ts-exts Prefer importing TypeScript files over JavaScript files
150+
--log-error Logs TypeScript errors to stderr instead of throwing exceptions
144151
`);
145152

146153
process.exit(0);
@@ -183,6 +190,8 @@ export function main(
183190
readFile: code !== undefined ? evalAwarePartialHost.readFile : undefined,
184191
fileExists:
185192
code !== undefined ? evalAwarePartialHost.fileExists : undefined,
193+
scope,
194+
scopeDir,
186195
});
187196

188197
// Bind REPL service to ts-node compiler service (chicken-and-egg problem)

src/configuration.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export function readConfig(
107107
// Fix ts-node options that come from tsconfig.json
108108
const tsNodeOptionsFromTsconfig: TsConfigOptions = Object.assign(
109109
{},
110-
filterRecognizedTsConfigTsNodeOptions(config['ts-node'])
110+
filterRecognizedTsConfigTsNodeOptions(config['ts-node']).recognized
111111
);
112112

113113
// Remove resolution of "files".
@@ -160,6 +160,8 @@ export function readConfig(
160160
)
161161
);
162162

163+
// Some options are relative to the config file, so must be converted to absolute paths here
164+
163165
if (tsNodeOptionsFromTsconfig.require) {
164166
// Modules are found relative to the tsconfig file, not the `dir` option
165167
const tsconfigRelativeRequire = createRequire(configFilePath!);
@@ -169,6 +171,12 @@ export function readConfig(
169171
}
170172
);
171173
}
174+
if (tsNodeOptionsFromTsconfig.scopeDir) {
175+
tsNodeOptionsFromTsconfig.scopeDir = resolve(
176+
basePath,
177+
tsNodeOptionsFromTsconfig.scopeDir
178+
);
179+
}
172180

173181
return { configFilePath, config: fixedConfig, tsNodeOptionsFromTsconfig };
174182
}
@@ -179,8 +187,8 @@ export function readConfig(
179187
*/
180188
function filterRecognizedTsConfigTsNodeOptions(
181189
jsonObject: any
182-
): TsConfigOptions {
183-
if (jsonObject == null) return jsonObject;
190+
): { recognized: TsConfigOptions; unrecognized: any } {
191+
if (jsonObject == null) return { recognized: jsonObject, unrecognized: {} };
184192
const {
185193
compiler,
186194
compilerHost,
@@ -197,6 +205,9 @@ function filterRecognizedTsConfigTsNodeOptions(
197205
transpileOnly,
198206
typeCheck,
199207
transpiler,
208+
scope,
209+
scopeDir,
210+
...unrecognized
200211
} = jsonObject as TsConfigOptions;
201212
const filteredTsConfigOptions = {
202213
compiler,
@@ -214,9 +225,11 @@ function filterRecognizedTsConfigTsNodeOptions(
214225
transpileOnly,
215226
typeCheck,
216227
transpiler,
228+
scope,
229+
scopeDir,
217230
};
218231
// Use the typechecker to make sure this implementation has the correct set of properties
219232
const catchExtraneousProps: keyof TsConfigOptions = (null as any) as keyof typeof filteredTsConfigOptions;
220233
const catchMissingProps: keyof typeof filteredTsConfigOptions = (null as any) as keyof TsConfigOptions;
221-
return filteredTsConfigOptions;
234+
return { recognized: filteredTsConfigOptions, unrecognized };
222235
}

src/index.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ export interface ProcessEnv {
7373
/** @deprecated */
7474
TS_NODE_DIR?: string;
7575
TS_NODE_EMIT?: string;
76-
/** @deprecated */
7776
TS_NODE_SCOPE?: string;
77+
TS_NODE_SCOPE_DIR?: string;
7878
TS_NODE_FILES?: string;
7979
TS_NODE_PRETTY?: string;
8080
TS_NODE_COMPILER?: string;
@@ -296,8 +296,6 @@ export interface TsConfigOptions
296296
| 'dir'
297297
| 'cwd'
298298
| 'projectSearchDir'
299-
| 'scope'
300-
| 'scopeDir'
301299
| 'experimentalEsmLoader'
302300
> {}
303301

@@ -318,6 +316,7 @@ export const DEFAULTS: RegisterOptions = {
318316
cwd: env.TS_NODE_CWD ?? env.TS_NODE_DIR,
319317
emit: yn(env.TS_NODE_EMIT),
320318
scope: yn(env.TS_NODE_SCOPE),
319+
scopeDir: env.TS_NODE_SCOPE_DIR,
321320
files: yn(env.TS_NODE_FILES),
322321
pretty: yn(env.TS_NODE_PRETTY),
323322
compiler: env.TS_NODE_COMPILER,
@@ -1292,7 +1291,8 @@ function registerExtension(
12921291
m._compile = function (code: string, fileName: string) {
12931292
debug('module._compile', fileName);
12941293

1295-
return _compile.call(this, service.compile(code, fileName), fileName);
1294+
const result = service.compile(code, fileName);
1295+
return _compile.call(this, result, fileName);
12961296
};
12971297

12981298
return old(m, filename);

src/test/index.spec.ts

+45-39
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,14 @@ test.suite('ts-node', (test) => {
923923
expect(stderr).to.contain('Error: --show-config requires');
924924
});
925925
}
926+
927+
test('should support compiler scope specified via tsconfig.json', async (t) => {
928+
const { err, stderr, stdout } = await exec(
929+
`${cmd} --project ./scope/c/config/tsconfig.json ./scope/c/index.js`
930+
);
931+
expect(err).to.equal(null);
932+
expect(stdout).to.equal(`value\nFailures: 0\n`);
933+
});
926934
});
927935

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

980-
if (semver.gte(ts.version, '2.7.0')) {
981-
test('should support compiler scopes', ({
982-
context: { registered, moduleTestPath },
983-
}) => {
984-
const calls: string[] = [];
988+
test('should support compiler scopes', ({
989+
context: { registered, moduleTestPath },
990+
}) => {
991+
const calls: string[] = [];
985992

986-
registered.enabled(false);
993+
registered.enabled(false);
987994

988-
const compilers = [
989-
register({
990-
projectSearchDir: join(TEST_DIR, 'scope/a'),
991-
scopeDir: join(TEST_DIR, 'scope/a'),
992-
scope: true,
993-
}),
994-
register({
995-
projectSearchDir: join(TEST_DIR, 'scope/a'),
996-
scopeDir: join(TEST_DIR, 'scope/b'),
997-
scope: true,
998-
}),
999-
];
995+
const compilers = [
996+
register({
997+
projectSearchDir: join(TEST_DIR, 'scope/a'),
998+
scopeDir: join(TEST_DIR, 'scope/a'),
999+
scope: true,
1000+
}),
1001+
register({
1002+
projectSearchDir: join(TEST_DIR, 'scope/a'),
1003+
scopeDir: join(TEST_DIR, 'scope/b'),
1004+
scope: true,
1005+
}),
1006+
];
10001007

1001-
compilers.forEach((c) => {
1002-
const old = c.compile;
1003-
c.compile = (code, fileName, lineOffset) => {
1004-
calls.push(fileName);
1008+
compilers.forEach((c) => {
1009+
const old = c.compile;
1010+
c.compile = (code, fileName, lineOffset) => {
1011+
calls.push(fileName);
10051012

1006-
return old(code, fileName, lineOffset);
1007-
};
1008-
});
1013+
return old(code, fileName, lineOffset);
1014+
};
1015+
});
10091016

1010-
try {
1011-
expect(require('../../tests/scope/a').ext).to.equal('.ts');
1012-
expect(require('../../tests/scope/b').ext).to.equal('.ts');
1013-
} finally {
1014-
compilers.forEach((c) => c.enabled(false));
1015-
}
1017+
try {
1018+
expect(require('../../tests/scope/a').ext).to.equal('.ts');
1019+
expect(require('../../tests/scope/b').ext).to.equal('.ts');
1020+
} finally {
1021+
compilers.forEach((c) => c.enabled(false));
1022+
}
10161023

1017-
expect(calls).to.deep.equal([
1018-
join(TEST_DIR, 'scope/a/index.ts'),
1019-
join(TEST_DIR, 'scope/b/index.ts'),
1020-
]);
1024+
expect(calls).to.deep.equal([
1025+
join(TEST_DIR, 'scope/a/index.ts'),
1026+
join(TEST_DIR, 'scope/b/index.ts'),
1027+
]);
10211028

1022-
delete require.cache[moduleTestPath];
1029+
delete require.cache[moduleTestPath];
10231030

1024-
expect(() => require(moduleTestPath)).to.throw();
1025-
});
1026-
}
1031+
expect(() => require(moduleTestPath)).to.throw();
1032+
});
10271033

10281034
test('should compile through js and ts', () => {
10291035
const m = require('../../tests/complex');

tests/scope/c/config/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const a: string = 'value';
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const a: string = 'value';

tests/scope/c/config/tsconfig.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"ts-node": {
3+
"scope": true,
4+
"scopeDir": "./scopedir"
5+
}
6+
}

tests/scope/c/index.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
let failures = 0;
2+
try {
3+
// This should fail with an error because it is outside scopedir
4+
require('./scopedir/index');
5+
failures++;
6+
} catch (e) {
7+
// good
8+
}
9+
10+
try {
11+
// This should fail with an error because it is outside scopedir
12+
require('./config/index');
13+
failures++;
14+
} catch (e) {
15+
// good
16+
}
17+
18+
try {
19+
// this should succeed
20+
console.log(require('./config/scopedir/index').a);
21+
} catch (e) {
22+
// bad
23+
failures++;
24+
}
25+
26+
console.log(`Failures: ${failures}`);

tests/scope/c/scopedir/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const a: string = 'value';

website/docs/options.md

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ _Environment variables, where available, are in `ALL_CAPS`_
4949
- `-r, --require [path]` Require a node module before execution
5050
- `--cwd` Behave as if invoked in this working directory <br/>*Default:* `process.cwd()`<br/>*Environment:* `TS_NODE_CWD`
5151
- `--emit` Emit output files into `.ts-node` directory <br/>*Default:* `false` <br/>*Environment:* `TS_NODE_EMIT`
52+
- `--scope` Scope compiler to files within `scopeDir`. Anything outside this directory is ignored. <br/>*Default: `false` <br/>*Environment:* `TS_NODE_SCOPE`
53+
- `--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`
5254
- `TS_NODE_HISTORY` Path to history file for REPL <br/>*Default:* `~/.ts_node_repl_history`<br/>
5355

5456
## API

0 commit comments

Comments
 (0)