
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
importx-tsup
Advanced tools
(With CommonJS, forked) Unified tool for importing TypeScript modules at runtime
Unified tool for importing TypeScript modules at runtime.
It's a common need for tools to support importing TypeScript modules at runtime. For example, to support configure files written in TypeScript.
There are so many ways to do that, each with its own trade-offs and limitations. This library aims to provide a simple, unified API for importing TypeScript modules, providing an easy-to-use API, and making it easy to switch between different loaders.
By default, it also provides a smart "auto" mode that decides the best loader based on the environment, trying to ease out their limitations and provide the best experience.
The goal is for this library to swallow the complexity of the underlying implementations, where you can just focus on the feature set you need. This library will keep up-to-date with the latest loaders and the runtime environment.
npm i importx
const mod = await import('importx').then(x => x.import('./path/to/module.ts', import.meta.url))
You can turn the second argument of import into an object to provide options:
const mod = await import('importx').then(x => x.import('./path/to/module.ts', {
parentURL: import.meta.url, // *required
// The following options are their default values
cache: null, // false, if you want to always get a new module
listDependencies: false, // true, if you need to get the list of dependencies
loader: 'auto', // most of the time, you don't need to change this as they will be chosen automatically
}))
When loader is not provided in the options explicitly, it will read from IMPORTX_LOADER environment variable, and then fall back to the auto.
autoAutomatically choose the best loader based on the environment (if the below graph doesn't render, click here to view it on GitHub).
graph TD
A((Auto)) --> Cache
Cache --> |false| RuntimeTsx
Cache --> |true / null| IsTS
IsTS --> |Yes| SupportTs
IsTS --> |No| Native1
SupportTs --> |No| RuntimeTsx
SupportTs --> |Yes| Native2
RuntimeTsx --> |Yes| Tsx
RuntimeTsx --> |No| ListDeps
ListDeps --> |Yes| BundleRequire
ListDeps --> |No| Jiti
IsTS{{"Is importing a TypeScript file?"}}
SupportTs{{"Supports native TypeScript?"}}
Cache[["Cache enabled?"]]
Native1(["native import()"])
Native2(["native import()"])
RuntimeTsx{{"Is current runtime supports tsx?"}}
ListDeps[["Need to list dependencies?"]]
Tsx([tsx loader])
Jiti([jiti loader])
BundleRequire([bundle-require loader])
classDef auto fill:#0f82,stroke:#0f83,stroke-width:2px;
classDef question fill:#f9f2,stroke:#f9f3,stroke-width:2px;
classDef cache fill:#eb527120,stroke:#eb527133,stroke-width:2px;
classDef native fill:#8882,stroke:#8883,stroke-width:2px;
classDef ts fill:#09f2,stroke:#09f3,stroke-width:2px;
classDef tsx fill:#0fe2,stroke:#0fe3,stroke-width:2px;
classDef jiti fill:#ffde2220,stroke:#ffde2230,stroke-width:2px;
classDef bundle fill:#5f32,stroke:#5f33,stroke-width:2px;
class A auto;
class RuntimeTsx question;
class Cache,ListDeps cache;
class Native1,Native2,Native3 native;
class IsTS,SupportTs ts;
class Tsx tsx;
class Jiti jiti;
class BundleRequire bundle;
linkStyle default stroke:#8888
nativeUse the native import() to import the module. According to the ESM spec, importing the same module multiple times will return the same module instance.
tsxUse tsx's tsImport API to import the module. Under the hood, it registers Node.js loader API and uses esbuild to transpile TypeScript to JavaScript.
^18.18.0, ^20.6.0 or above. Does not work on other runtime yet.jitiUse jiti to import the module. It uses a bundled Babel parser to transpile modules. It runs in CJS mode and has its own cache and module runner.
bundle-requireUse bundle-require to import the module. It uses esbuild to bundle the entry module, saves it to a temporary file, and then imports it.
node_modules).By definition, ESM modules are always cached by the runtime, which means you will get the same module instance when importing the same module multiple times. In some scenarios, like a dev server watching for config file changes, the cache may not be desired as you want to get the new module with the latest code on your disk.
importx allows you to specify if you want to have the module cache or not, by providing the cache option:)
const mod = await import('importx')
.then(x => x.import('./path/to/module.ts', {
cache: false, // <-- this
parentURL: import.meta.url,
}))
Setting cache: null (default) means you don't care about the cache (if you only import the module once).
Note that some loaders always have a cache, and some loaders always have no cache. With the auto loader, we will choose the best loader based on your need. Otherwise, an unsupported combination will throw an error. For example:
// This will throw an error because `bundle-require` does not support cache.
const mod = await import('importx')
.then(x => x.import('./path/to/module.ts', {
cache: true,
loader: 'bundle-require',
parentURL: import.meta.url,
// ignoreImportxWarning: true // unless you have this
}))
You can get the extra module information by passing the module instance to getModuleInfo:
await import('importx')
.then(async (x) => {
const mod = await x.import('./path/to/module.ts', import.meta.url)
const info = x.getModuleInfo(mod)
console.log(
info.loader, // the final loader used
info.timestampInit, // timestamp when the module is initialized
info.timestampLoad, // timestamp when the module is imported
info.dependencies, // list of dependencies (available only in `tsx` and `bundle-require` loader),
(info.timestampLoad - info.timestampInit) // time taken to load the module (in ms)
)
})
In cases like loading a config file for a dev server, where you need to watch for changes in the config file and reload the server, you may want to know the module's dependencies to watch for changes in them as well.
tsx and bundle-require loaders support listing the dependencies of the module. You can get the list of dependencies by getting the module info. To ensure you use always have the dependencies list in auto mode, you can set the listDependencies option to true:
const mod = await import('importx')
.then(x => x.import('./path/to/module.ts', {
listDependencies: true,
parentURL: import.meta.url,
}))
Since v0.4, importx supports fallback loaders when previous loaders fail to load the module. By default ['jiti'] will be used as it's the most compatible loader. You can customize the fallback loaders by setting the fallbackLoaders option:
const mod = await import('importx')
.then(x => x.import('./path/to/module.ts', {
fallbackLoaders: ['jiti', 'tsx'],
parentURL: import.meta.url,
}))
You can also disable fallback loaders by setting it to false:
const mod = await import('importx')
.then(x => x.import('./path/to/module.ts', {
fallbackLoaders: false,
parentURL: import.meta.url,
}))
Importing a TypeScript module with importx:
Generated with version
v0.4.4at 2024-09-27T00:27:46.691Z
| native | tsx | jiti | bundle-require | |
|---|---|---|---|---|
| node | Import: ❌ Cache: ❌ No cache: ❌ Deps: ❌ CTS Import: ❌ ESM/CJS Mixed: ❌ Const Enum: ❌ Import ESM Dep: ❌ | Import: ✅ Cache: ✅ No cache: ✅ Deps: ✅ CTS Import: ✅ ESM/CJS Mixed: ✅ Const Enum: ✅ Import ESM Dep: ✅ | Import: ✅ Cache: ✅ No cache: ✅ Deps: ✅ CTS Import: ✅ ESM/CJS Mixed: ✅ Const Enum: ✅ Import ESM Dep: ✅ | Import: ✅ Cache: ❌ No cache: ✅ Deps: ✅ CTS Import: ❌ ESM/CJS Mixed: ✅ Const Enum: ✅ Import ESM Dep: ✅ |
| tsx | Import: ✅ Cache: ✅ No cache: ❌ Deps: ❌ CTS Import: ✅ ESM/CJS Mixed: ✅ Const Enum: ✅ Import ESM Dep: ✅ | Import: ✅ Cache: ✅ No cache: ✅ Deps: ✅ CTS Import: ✅ ESM/CJS Mixed: ✅ Const Enum: ✅ Import ESM Dep: ✅ | Import: ✅ Cache: ✅ No cache: ✅ Deps: ✅ CTS Import: ✅ ESM/CJS Mixed: ✅ Const Enum: ✅ Import ESM Dep: ✅ | Import: ✅ Cache: ❌ No cache: ✅ Deps: ✅ CTS Import: ❌ ESM/CJS Mixed: ✅ Const Enum: ✅ Import ESM Dep: ✅ |
| deno | Import: ✅ Cache: ✅ No cache: ❌ Deps: ❌ CTS Import: ❌ ESM/CJS Mixed: ❌ Const Enum: ❌ Import ESM Dep: ✅ | Import: ❌ Cache: ❌ No cache: ❌ Deps: ❌ CTS Import: ❌ ESM/CJS Mixed: ❌ Const Enum: ❌ Import ESM Dep: ❌ | Import: ❌ Cache: ❌ No cache: ❌ Deps: ❌ CTS Import: ❌ ESM/CJS Mixed: ❌ Const Enum: ❌ Import ESM Dep: ❌ | Import: ✅ Cache: ❌ No cache: ✅ Deps: ✅ CTS Import: ❌ ESM/CJS Mixed: ✅ Const Enum: ✅ Import ESM Dep: ✅ |
| bun | Import: ✅ Cache: ✅ No cache: ❌ Deps: ❌ CTS Import: ✅ ESM/CJS Mixed: ✅ Const Enum: ✅ Import ESM Dep: ✅ | Import: ❌ Cache: ❌ No cache: ❌ Deps: ❌ CTS Import: ❌ ESM/CJS Mixed: ❌ Const Enum: ❌ Import ESM Dep: ❌ | Import: ✅ Cache: ❌ No cache: ❌ Deps: ✅ CTS Import: ✅ ESM/CJS Mixed: ✅ Const Enum: ✅ Import ESM Dep: ✅ | Import: ✅ Cache: ❌ No cache: ✅ Deps: ✅ CTS Import: ❌ ESM/CJS Mixed: ✅ Const Enum: ✅ Import ESM Dep: ✅ |
| native | tsx | jiti | bundle-require | |
|---|---|---|---|---|
Cache: true | ✅ | ✅ | ✅ | ❌ |
Cache: false | ❌ | ✅ | ✅ | ✅ |
| List dependencies | ❌ | ✅ | ✅ | ✅ |
| Runtimes other than Node.js | ✅ | ❌ | ✅ | ✅ |
| Native ESM Import | ✅ | ✅ | ✅ | ✅ |
| Top-level await | ✅ | ✅ | ✅ | ✅ |
| Runtime module type* | ESM | ESM | CJS | ESM/CJS |
*This indicates what's the module type for each loader to evaluate the modules. For
CJS, it means the loader transpiles the module to CJS and executes it in CJS mode, which may have some limitations like top-level await.
MIT License © 2024-PRESENT Anthony Fu
FAQs
(With CommonJS, forked) Unified tool for importing TypeScript modules at runtime
The npm package importx-tsup receives a total of 276 weekly downloads. As such, importx-tsup popularity was classified as not popular.
We found that importx-tsup demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.