Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1381,6 +1381,18 @@ added:

Enable experimental support for the QUIC protocol.

### `--experimental-repl-typescript`

<!-- YAML
added: REPLACEME
-->

> Stability: 1.0 - Early development

Enable experimental support for TypeScript stripping in the REPL. Input that is
valid JavaScript is evaluated as JavaScript before falling back to TypeScript
type stripping.

### `--experimental-sea-config`

<!-- YAML
Expand Down Expand Up @@ -3821,6 +3833,7 @@ one is included in the list below.
* `--experimental-package-map`
* `--experimental-print-required-tla`
* `--experimental-quic`
* `--experimental-repl-typescript`
* `--experimental-require-module`
* `--experimental-shadow-realm`
* `--experimental-specifier-resolution`
Expand Down
7 changes: 7 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,11 @@ this flag allows Node.js to locate and print their locations.
.It Fl -experimental-quic
Enable experimental support for the QUIC protocol.
.
.It Fl -experimental-repl-typescript
Enable experimental support for TypeScript stripping in the REPL. Input that is
valid JavaScript is evaluated as JavaScript before falling back to TypeScript
type stripping.
.
.It Fl -experimental-sea-config
Use this flag to generate a blob that can be injected into the Node.js
binary to produce a single executable application. See the documentation
Expand Down Expand Up @@ -1973,6 +1978,8 @@ one is included in the list below.
.It
\fB--experimental-quic\fR
.It
\fB--experimental-repl-typescript\fR
.It
\fB--experimental-require-module\fR
.It
\fB--experimental-shadow-realm\fR
Expand Down
26 changes: 23 additions & 3 deletions lib/internal/repl/eval.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ const {
transformModuleSyntax,
} = require('internal/repl/transform');
const { getReplInspector, prepareContext } = require('internal/repl/inspector');
const { getOptionValue } = require('internal/options');

let debug = require('internal/util/debuglog').debuglog('repl', (fn) => {
debug = fn;
});

const blankRegExp = /^\s*$/;
const experimentalREPLTypeScript =
getOptionValue('--experimental-repl-typescript');

// Matches the first stack frame that belongs to the REPL
const internalFrameRegExp =
Expand Down Expand Up @@ -119,11 +122,28 @@ function createReplEval(repl) {
installDynamicImport(context);
prepareContext(context);

let code = rawCode;
const valid = isValidSyntax(code);

if (!valid && experimentalREPLTypeScript) {
try {
const { stripTypeScriptModuleTypes } =
require('internal/modules/typescript');
code = stripTypeScriptModuleTypes(code, file);
} catch (err) {
if (err.code !== 'ERR_INVALID_TYPESCRIPT_SYNTAX' &&
err.code !== 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX') {
throw err;
}
// Let the evaluator report the original JavaScript syntax error. It
// also determines whether incomplete input is recoverable.
}
}

// Wrap bare object literals so `{ a: 1 }` is evaluated as an expression
// rather than a block statement.
let code = rawCode;
if (isObjectLiteral(code) && isValidSyntax(code)) {
code = `(${StringPrototypeTrim(rawCode)})\n`;
if (valid && isObjectLiteral(code)) {
code = `(${StringPrototypeTrim(code)})\n`;
}

// Rewrite ES module syntax into runnable script form.
Expand Down
4 changes: 4 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
kAllowedInEnvvar,
false,
OptionNamespaces::kPermissionNamespace);
AddOption("--experimental-repl-typescript",
"experimental TypeScript support in the REPL",
&EnvironmentOptions::experimental_repl_typescript,
kAllowedInEnvvar);
AddOption("--experimental-vm-modules",
"experimental ES Module support in vm module",
&EnvironmentOptions::experimental_vm_modules,
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ class EnvironmentOptions : public Options {
bool allow_wasi = false;
bool allow_ffi = false;
bool allow_worker_threads = false;
bool experimental_repl_typescript = EXPERIMENTALS_DEFAULT_VALUE;
bool experimental_vm_modules = EXPERIMENTALS_DEFAULT_VALUE;
bool async_context_frame = true;
bool expose_internals = false;
Expand Down
25 changes: 25 additions & 0 deletions test/parallel/test-repl-typescript.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Flags: --experimental-repl-typescript

import * as common from '../common/index.mjs';
import assert from 'node:assert';
import { startNewREPLServer } from '../common/repl.js';

common.skipIfInspectorDisabled();

const { run, output } = startNewREPLServer({
terminal: false,
prompt: '> ',
});

await run('let x: number = 3\n');
assert.match(output.accumulator, /undefined\n> /);
output.accumulator = '';

await run('x\n');
assert.match(output.accumulator, /3\n> /);
output.accumulator = '';

// Syntax which is valid in both languages must retain its JavaScript
// semantics instead of being type-stripped.
await run("const foo = () => 'hello world'; foo<Object>(0);\n");
assert.match(output.accumulator, /true\n> /);
Loading