Usage

October 13, 2021 · View on GitHub

There are a number of ways you can use tsm in your project:

  1. As a node CLI replacement
  2. As a CommonJS --require hook
  3. As an ESM --loader
  4. As a shell shebang interpreter

CLI

Examples

# run a file
$ tsm server.tsx

# run a file w/ Node flags
# NOTE: flags are forwarded to `node` directly
$ tsm main.ts --trace-warnings

# run a file w/ ENV variables
# NOTE: any ENV is forwarded to `node` directly
$ NO_COLOR=1 PORT=8080 tsm main.ts

# use npx/pnpx with tsm
$ pnpx tsm server.tsx
$ npx tsm server.tsx

Require Hook

The --require hook has existed for a very long time and many tools throughout the ecosystem support/integrate with this feature. However, not every tool – so please consult with your tools' documentation to see if they support a -r/--require flag.

Background: Essentially, the --require/-r hook subjects any require()d file to additional and/or custom transformation(s). Even though this works on a per-extension basis, it can be quite costly (performance-wise) and there is discouraged; however, for tools like tsm, it's still valuable.

A configuration file is still auto-loaded (if exists) when using --require tsm or -r tsm.

Examples

# node with require hook(s)
$ node --require tsm server.tsx
$ node -r dotenv/register -r tsm server.tsx

# external tool with require hook support
$ uvu -r tsm packages tests
$ uvu --require tsm

Loader Hook

The --loader hook is ESM's version of the --require hook. A loader is only applied to file(s) loaded through import or import() – anything loaded through require is ignored by the loader.

Important: ESM loaders are experimental and will be redesigned. tsm will conform to new design(s) as the feature stabilizes.

You may use --loader tsm or --experimental-loader tsm anywhere that supports ESM loaders. At time of writing, this seems to be limited to node itself.

A configuration file is still auto-loaded (if exists) when using --loader tsm.

Examples

# run node with tsm loader
$ node --loader tsm server.tsx
$ node --experimental-loader tsm main.ts

Shell / Shebang

If you have tsm installed globally on your system, you may write shell scripts with tsm as the interpreter. Here's an example:

// file: example.ts
#!/usr/bin/env tsm
import { sum } from './math';
import { greet } from './hello';

let [who] = process.argv.slice(2);

greet(who || 'myself');

let answer = sum(11, 31);
console.log('the answer is:', answer);

// file: math.ts
export const sum = (a: number, b: number) => a + b;

// file: hello.ts
export function greet(name: string) {
  console.log(`Hello, ${name}~!`);
}

Important: These are three separate TypeScript files.

Here, the main example.ts file imports/references functionality defined in the two other files. Additionally, the first line within example.ts contains a shebang (#!) followed by /usr/bin/env tsm, which tells the shell to use the tsm binary within the user's environment to process this file. Effectively, this means that the shebang is a shortcut for running this in your terminal:

$ tsm example.ts

However, by including the shebang, you are embedding the instructions for how this file should be executed. This also allows you to include additional CLI flags within the shebang, meaning you don't have to redefine or remember them later on. For example, you can forward the --trace-warnings argument through tsm, which will always be there whenever the example.ts script executes.

--#!/usr/bin/env tsm
++#!/usr/bin/env tsm --trace-warnings

Now, in order to actually execute the example.ts script directly, you have to modify its permissions and mark it as executable:

$ chmod +x example.ts

At this point, you can run the file directly in your terminal:

$ ./example.ts
#  ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
#    at emitExperimentalWarning (node:internal/util:227:11)
#    at initializeLoader (node:internal/process/esm_loader:54:3)
#    at Object.loadESM (node:internal/process/esm_loader:67:11)
#    at runMainESM (node:internal/modules/run_main:46:31)
#    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:5)
#    at node:internal/main/run_main_module:17:47
# Hello, myself~!
# the answer is: 42

Note: The large block of ExperimentalWarning text is from the --trace-warnings argument. This flag is forwarded to node, which prints this output natively.