Skip to content

TSX

The easiest way to run TypeScript in Node.js.

Background

Neither Typescript's editor tsc nor esbuild's transform API performs path rewriting operations after transpilation.

The reason why tsc does not perform rewriting paths

  1. From the explanation of paths:

    Note that this feature does not change how import paths are emitted by tsc, so paths should only be used to inform TypeScript that another tool has this mapping and will use it at runtime or when bundling.

    The paths configuration item is only used to inform typescript that other tools will handle path rewriting at runtime or during the build phase. For typescript itself, it mainly guides typescript to perform correct type checking and ensures that the editor does not report type errors.

  2. From Module resolution:

    TypeScript doesn't modify import specifiers during emit: the relationship between an import specifier and a file on disk (if one even exists) is host-defined, and TypeScript is not a host.

    This explains the fundamental reason why typescript does not rewrite import paths:

    The relationship between an import specifier and the actual file is defined by the host environment, and typescript is not the host environment. This is not a defect, but a well-considered design decision. Appropriate build tools should be used to handle path rewriting requirements.

The reason why esbuild's transform API does not perform rewriting paths

From the official documentation:

These options affect esbuild's resolution of import/require paths to files on the file system. You can use it to define package aliases and to rewrite import paths in other ways. Note that using esbuild for import path transformation requires bundling to be enabled, as esbuild's path resolution only happens during bundling. Also note that esbuild also has a native alias feature which you may want to use instead.

esbuild's path resolution feature is only executed during the build phase, and path rewriting is not performed during the transpilation phase.

Existing Solutions

tsc-alias

tsc-alias checks the generated js modules after tsc transpiles ts to js modules, and then replaces alias paths according to the rules in the complier.paths configuration item in tsconfig.json.

Execution command:

bash
npx tsc && npx tsc-alias

Referring to issue #81, it can be seen that tsc-alias is executed in steps and does not support runtime usage.

tsconfig-paths

Supports runtime loading and API loading. typescript by default mimics node.js runtime module resolution strategy. The difference is that typescript also supports using path mapping, allowing specification of arbitrary module paths (not starting with "/" or ".") and mapping them to physical paths in the file system. The typescript compiler can resolve these paths through the paths configuration item in the user-configured tsconfig.json, thus compiling successfully. However, if you try to execute the js files compiled by typescript using node (or ts-node), it will not respect the paths configuration item in tsconfig.json, but will search the node_modules folder up to the file system root.

The tsconfig-paths/register module guides node (or ts-node) to read paths from tsconfig.json or jsconfig.json and convert them to physical file paths of the instance.

bash
ts-node -r tsconfig-paths/register main.ts

However, this requires additional installation of ts-node and tsconfig-paths packages, and manually executing the ts-node -r tsconfig-paths/register main.ts command seems somewhat cumbersome.

tsx solves the above pain points. It doesn't require additional dependencies, supports path rewriting at runtime through rewriting, and also uses esbuild's transform api to quickly transpile ts files, improving execution efficiency.

Contributors

Changelog

Discuss

Released under the CC BY-SA 4.0 License. (dbcbf17)