Skip to content

Commit

Permalink
Un-deprecate scope and scopedir; add to CLI, tsconfig.json, and env v…
Browse files Browse the repository at this point in the history
…ars (#1367)

* re-add scope and scopeDir to CLI; add env vars

* fix tests and bug found by tests

* Fix typo
  • Loading branch information
cspotcode authored Jun 6, 2021
1 parent 7cac7df commit 17b3a55
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 70 deletions.
55 changes: 32 additions & 23 deletions src/bin.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const a: string = 'value';
1 change: 1 addition & 0 deletions tests/scope/c/config/scopedir/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const a: string = 'value';
6 changes: 6 additions & 0 deletions tests/scope/c/config/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"ts-node": {
"scope": true,
"scopeDir": "./scopedir"
}
}
26 changes: 26 additions & 0 deletions tests/scope/c/index.js
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const a: string = 'value';
2 changes: 2 additions & 0 deletions website/docs/options.md
Original file line number Diff line number Diff line change
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

0 comments on commit 17b3a55

Please sign in to comment.