The minimalist TypeScript script runner for NodeJS.
- Just-in-time TypeScript transpilation so fast you won't even notice.
- Generates source maps for accurate stack traces.
- Does not spawn another process to transpile TypeScript.
- Does not spawn another Node process to run your script.
- Strictly follows modern Node semantics for ESM and CommonJS modules.
- Zero config: no config file, no command line arguments, no environment variables, no nothing.
- Does not even need a
tsconfig.json
. - Extra-light: only 220 kilobytes installed!
- Zero dependency!
- Not for running full-blown TypeScript projects.
- No REPL support.
ts-run
is a CLI command that you can use to run TypeScript scripts in NodeJS as if they were written in plain JavaScript. It is a simple as:
ts-run ./some-script.ts
The idea is that you take advantage of your IntelliSense-compatible editor to author your scripts with full type checking on, and ts-run
will transparently run them without you having to run the TypeScript compliler beforehand.
ts-run
requires a modern (as of january 2024) version of NodeJS:
- Node 18 version 18.19.0 or later
- Node 20 version 20.6.0 or later
- Any version >= 21
For everyday use, you may want to install ts-run
globally:
npm install -g @septh/ts-run
so it's always available in your CLI:
ts-run path/to/some/script.ts
Or you may install it locally in a project:
# with npm
npm install --save-dev @septh/ts-run
# with pnpm
pnpm add --save-dev @septh/ts-run
# with yarn
yarn add --dev @septh/ts-run
and then call it from the scripts
section in package.json
:
{
"scripts": {
"get-data": "ts-run ./scripts/download-data.ts",
"release": "ts-run ./scripts/prepare-release.ts"
}
}
or from the command line:
# with npx
npx ts-run ./scripts/do-something.ts
# with node --import
node --import=@septh/ts-run ./scripts/do-something.ts
ts-run
's sole role is to transpile TypeScript code to JavaScript code, no more, no less. It does not try to optimize or minify your code and it does not downlevel nor polyfill JavaScript. Therefore, there are a few things you should keep in mind while authoring your scripts.
ts-run
handles import
and export
declarations as one would expect. In short:
- The
import ... from 'specifier'
syntax is left as is in ES modules and transformed toconst ... = require('specifier')
in CommonJS modules. - The
import namespace = require('specifier')
syntax is valid in ES modules only and is transformed toconst require = createRequire(import.meta.url); const namespace = require('specifier')
, with the createRequire() call being hoisted if used several times. - Dynamics imports are always left untouched, even in CJS modules.
export
s are transformed tomodule.exports
assignments in CommonJS modules.- Type-only
import
s andexport
s, whether explicit (with thetype
keyword) or implicit, are silently removed.
Given the above, you should simply import your .ts
scripts as you would with plain Javascript:
// a.ts
export const something = 'great'
// b.ts
import { something } from './a.ts'
Beginning with 1.2.6, .js
specifiers are also supported:
// b.ts
import { something } from './a.js' // works too
However, using .ts
specifiers is highly recommended as a mean to ensure a smooth transition with Node's own --experimental-strip-types
option.
TypeScript's module resolution specificities are not handled; instead, Node's module resolution algorithm is always used. In other words, ts-run
always acts as if both moduleResolution
and module
were set to Node16
and paths
was empty.
ts-run
uses a customized build of Sucrase under the hood and therefore exhibits the same potential bugs and misbehaviors as Sucrase.
Of particular attention, the following quote from Sucrase's README:
Decorators, private fields, throw expressions, generator arrow functions, and do expressions are all unsupported in browsers and Node (as of this writing), and Sucrase doesn't make an attempt to transpile them.
Apart from this, if ts-run
doesn't seem to work as you'd expect, you should first check if there is a Sucrase issue open for your problem.
As stated earlier, ts-run
does not need (and in fact, does not even look for) a tsconfig.json
file.
The same is not true however for the TypeScript Language Server that your IntelliSense-aware editor relies on. You'll find the following tsconfig.json
useful to get the right warnings and errors reports in your IDE:
{
"compilerOptions": {
// This tells the TypeScript language server that this directory contains Node scripts.
// "Bundler" would be fine, too.
"module": "Node16",
// For scripts that use .ts import specifiers (recommended)
// Please note that `noEmit` is required when using `allowImportingTsExtensions`
"allowImportingTsExtensions": true,
"noEmit": true,
// Scripts are transpiled in isolation; this imposes a few restrictions
// on some TypeScript features like const enums or namespaces.
// (see https://www.typescriptlang.org/tsconfig#isolatedModules)
"isolatedModules": true,
// Of course, add any other type-checking options you deem necessary:
"strict": true
// etc.
}
}
For reference, you can find such a tsconfig.json
file in the test directory of this repository.
I have tested ts-run
with ava and Node itself and it works very well in both cases. I can see no reason why it wouldn't work with another test-runner.
This very repo is using Node as its test-runner of choice. Here's what your scripts
section in package.json
might look like:
"scripts": {
"test": "node --import=@septh/ts-run --test test/**/*.test.{ts,mts,cts}"
}
Note: to pass command line options to Node itself, you need to use the
--import
syntax as shown above.
Add the following entry to your package.json
:
"ava": {
"extensions": {
"ts": "module", // Or "commonjs", depending on what your package.json says
"mts": "module",
"cts": "commonjs"
},
"nodeArguments": [
"--import=@septh/ts-run"
]
}
Here's a real-life example: https://github.com/Septh/rollup-plugin-node-externals
Any test-runner that provides a mean to specify Node arguments (just like ava above) should work happily with ts-run
.
In the worst case, you can always use the NODE_OPTIONS
environment variable:
NODE_OPTIONS="--import=@septh/ts-run" my-test-runner
Because ts-run
generates sourcemaps, you can set breakpoints in your script, inspect variables, etc.
Either run ts-run
in the VS Code Javascript Debug Terminal or use the following launch.json
configuration (replace <path/to/your/script.ts>
with the actual path to your script):
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Run with ts-run",
"request": "launch",
"type": "node",
"runtimeArgs": [
"--import=@septh/ts-run"
],
"program": "${workspaceFolder}/<path/to/your/script.ts>",
"skipFiles": [
"<node_internals>/**",
"**/node_modules/**"
]
}
]
}
MIT.