diff --git a/README.md b/README.md index c6218f3..5b30442 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,12 @@ - Does not spawn another process to transpile TypeScript. - Does not spawn another Node process to run your script. - Zero config: no config file, no command line arguments, no environment variables, no nothing. -- Does not even need a `tsconfig.json` (though you may need one for *authoring* your scripts -- see below). +- Does not even need a `tsconfig.json` (though you may need one for *authoring* your scripts -- keep reading below). +- Light: only 220 kilobytes installed! +- Zero dependency! ### Non-features -- Not for running full TypeScript projects. +- Not for running full-blown TypeScript projects. - No REPL support. @@ -22,7 +24,7 @@ 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 (using [Sucrase](https://github.com/alangpierce/sucrase) under the hood) without you having to run the TypeScript compliler beforehand. +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 (using a bundled [Sucrase](https://github.com/alangpierce/sucrase)'s parser under the hood) without you having to run the TypeScript compliler beforehand. ## Installation and usage @@ -38,7 +40,12 @@ For everyday use, you may want to install `ts-run` globally: npm install -g @septh/ts-run ``` -and have it always available in your CLI. +and have it always available in your CLI: + +```sh +ts-run path/to/some/script.ts +``` + #### Local install Or you may install it locally in a project: @@ -65,15 +72,24 @@ npx ts-run ./scripts/do-something.ts ``` > #### Note: -> `ts-run` is not a wrapper around Node, it *is* Node with a (tiny) preload script that transpiles TypeScript to JavaScript. Therefore, all Node command-line options and flags are available: +> `ts-run` is not a wrapper around Node, it *is* Node with a (tiny) preload script that transpiles TypeScript to JavaScript. Therefore, all Node command-line options and flags are available, e.g.: > > ```sh -> ts-run --no-warnings ./some-script.ts +> ts-run --no-warnings some-script.ts > ``` ## TypeScript to JavaScript considerations +### import specifiers +Use the `.ts`, `.mts` or `.cts` extensions when importing modules. They are mandatory in ESM modules and highly recommended in CJS modules. + +```ts +import { something } from './utilities.ts' +``` + +Contrary to the TypeScript compiler or other tools like `ts-node`, `tsx` or `tsimp`, `ts-run` will not try and find a corresponding `.ts` file if you use a `.js` specifier. + ### ESM vs CommonJS `ts-run`'s sole role is to transpile TypeScript code to JavaScript code, no more, no less. It does not try to convert scripts from CommonJS to ESM or vice-versa, it does not try to optimize or minify your code and it does not downlevel nor polyfill JavaScript. @@ -89,20 +105,29 @@ Conversely, CommonJS features (i.e., things like `__dirname` or `require()`) do > If you are not familiar with CommonJS and ESM modules and when NodeJS expects one format or the other, Node's documentation has [a comprehensive guide about modules](https://nodejs.org/docs/latest-v20.x/api/esm.html). +### Import assignments +`ts-run` relies on Sucrase to transpile TypeScript to JavaScript. Unfortunately, Sucrase translates so called *"import assignements"* to simple `require()` calls, which will make your script crash if it runs in an ESM context. -### import specifiers -Use the `.ts`, `.mts` or `.cts` extensions when importing modules. They are mandatory in ESM modules and highly recommended in CJS modules. +To prevent crashes, instead of: ```ts -import { something } from './utilities.ts' +// ❌ This won't work in ESM mode +import cjsModule = require('./module.cts') ``` -Contrary to the TypeScript compiler, `ts-run` will not try and find a corresponding `.ts` file if you use a `.js` specifier. +use: -## Authoring scripts +```ts +// ✅ This works is ESM mode +import { createRequire } from 'node:module' +const cjsModule = createRequire(import.meta.url)('./module.cts') +``` + + +## Authoring your scripts For the reasons stated above, `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: +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: ```jsonc { @@ -110,14 +135,14 @@ The same is not true however for the TypeScript Language Server that your Intell // This tells the TypeScript language server that this directory contains Node scripts. "module": "Node16", - // Scripts must import .ts files -- ts-run does not map .js to .ts + // - Scripts must import .ts files as ts-run does not map .js to .ts + // - `noEmit` is required when using `allowImportingTsExtensions` "allowImportingTsExtensions": true, - - // `noEmit: true` is required when using `allowImportingTsExtensions: true` "noEmit": true, - // Scripts are compiled in isolation, this imposes a few restrictions + // 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: @@ -126,10 +151,57 @@ The same is not true however for the TypeScript Language Server that your Intell } } ``` -For reference, you can find such a `tsconfig.json` file in the [test folder](./test/tsconfig.json). +For reference, you can find such a `tsconfig.json` file in the [test folder](./test/tsconfig.json) of this repository. -## Debugging scripts with VS Code +## Using with a test-runner +I have only tested with [ava](https://github.com/avajs/ava) and [Node itself](https://nodejs.org/api/test.html) and it works very well in both cases. I can see no reason why it wouldn't work with another test-runner. + +### With node:test +This very repo is using Node as its test-runner of choice -- see the `scripts` section in `package.json`: + +```json + "scripts": { + "test": "ts-run test/check-node-21.ts && node --import=@septh/ts-run/register --test test/**/*.test.{ts,mts,cts}" } +``` + +The only caveat here is that Node started to support glob patterns as arguments to the `--test` option only since version 21, hence the little script that checks the version of Node before running the tests. This is a limitation of Node, not `ts-run`. + +On the other hand, this works with older versions of Node supported by `ts-run`: +```json + "scripts": { + "test": "node --import=@septh/ts-run/register --test my-test-script.ts" +} +``` + +### With ava +Add the following entry to your `package.json`: + +```json + "ava": { + "extensions": { + "ts": "module", + "mts": "module", + "cts": "commonjs" + }, + "nodeArguments": [ + "--import=@septh/ts-run/register" + ] + } +``` + +Here's an example: https://github.com/Septh/rollup-plugin-node-externals + +### With other test-runners +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: +```sh +NODE_OPTIONS="--import=@septh/ts-run/register" my-test-runner +``` + + +## Debugging scripts with VS Code Because `ts-run` supports sourcemaps, you can set breakpoints in your script code, inspect variables, etc. Either run `ts-run` in the VS Code Javascript Debug Terminal or use the following `launch.json` configuration (replace `` with the actual path to your script):