0
# ESM Integration
1
2
Native ECMAScript module support with loader hooks for seamless TypeScript execution in ESM contexts and modern Node.js applications.
3
4
## Capabilities
5
6
### ESM Hooks Creation
7
8
Creates ESM loader hooks for TypeScript module resolution and compilation.
9
10
```typescript { .api }
11
/**
12
* Create ESM loader hooks from an existing ts-node service
13
* @param tsNodeService - Service instance for TypeScript compilation
14
* @returns ESM hooks object compatible with Node.js loader API
15
*/
16
function createEsmHooks(tsNodeService: Service): NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2;
17
18
/**
19
* Register ts-node and create ESM hooks in one step
20
* @param opts - Configuration options for registration
21
* @returns ESM hooks object
22
*/
23
function registerAndCreateEsmHooks(opts?: RegisterOptions): NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2;
24
```
25
26
**Usage Examples:**
27
28
```typescript
29
import { createEsmHooks, create } from "ts-node";
30
31
// Create service and hooks separately
32
const service = create({
33
esm: true,
34
compilerOptions: {
35
target: "es2020",
36
module: "esnext",
37
}
38
});
39
40
const hooks = createEsmHooks(service);
41
42
// Or create both in one step
43
const hooks = registerAndCreateEsmHooks({
44
esm: true,
45
compilerOptions: {
46
target: "es2020",
47
module: "esnext",
48
}
49
});
50
```
51
52
### Node.js Loader Hooks API
53
54
ESM loader hooks compatible with Node.js loader API versions 1 and 2.
55
56
```typescript { .api }
57
/**
58
* Node.js Loader Hooks API Version 1
59
*/
60
interface NodeLoaderHooksAPI1 {
61
/**
62
* Resolve module specifier to URL
63
* @param specifier - Module specifier to resolve
64
* @param context - Resolution context
65
* @param defaultResolve - Default Node.js resolve function
66
* @returns Promise resolving to module URL and format
67
*/
68
resolve(
69
specifier: string,
70
context: { parentURL?: string; conditions?: string[] },
71
defaultResolve: Function
72
): Promise<{ url: string; format?: NodeLoaderHooksFormat }>;
73
74
/**
75
* Get format of resolved module
76
* @param url - Module URL
77
* @param context - Format context
78
* @param defaultGetFormat - Default Node.js getFormat function
79
* @returns Promise resolving to module format
80
*/
81
getFormat(
82
url: string,
83
context: object,
84
defaultGetFormat: Function
85
): Promise<{ format: NodeLoaderHooksFormat }>;
86
87
/**
88
* Transform source code before execution
89
* @param source - Source code to transform
90
* @param context - Transform context including URL and format
91
* @param defaultTransformSource - Default transform function
92
* @returns Promise resolving to transformed source
93
*/
94
transformSource(
95
source: string | Buffer,
96
context: { url: string; format: NodeLoaderHooksFormat },
97
defaultTransformSource: Function
98
): Promise<{ source: string | Buffer }>;
99
}
100
101
/**
102
* Node.js Loader Hooks API Version 2 (Node.js 16.12.0+)
103
*/
104
interface NodeLoaderHooksAPI2 {
105
/**
106
* Resolve module specifier to URL
107
* @param specifier - Module specifier to resolve
108
* @param context - Resolution context
109
* @param next - Next resolver in chain
110
* @returns Promise resolving to module URL and format
111
*/
112
resolve(
113
specifier: string,
114
context: {
115
conditions: string[];
116
importAssertions?: object;
117
parentURL?: string;
118
},
119
next: Function
120
): Promise<{
121
format?: NodeLoaderHooksFormat;
122
shortCircuit?: boolean;
123
url: string;
124
}>;
125
126
/**
127
* Load and transform module source
128
* @param url - Module URL to load
129
* @param context - Load context
130
* @param next - Next loader in chain
131
* @returns Promise resolving to loaded module
132
*/
133
load(
134
url: string,
135
context: {
136
conditions: string[];
137
format?: NodeLoaderHooksFormat;
138
importAssertions?: object;
139
},
140
next: Function
141
): Promise<{
142
format: NodeLoaderHooksFormat;
143
shortCircuit?: boolean;
144
source?: string | Buffer;
145
}>;
146
}
147
148
/**
149
* Supported ESM module formats
150
*/
151
type NodeLoaderHooksFormat =
152
| 'builtin'
153
| 'commonjs'
154
| 'json'
155
| 'module'
156
| 'wasm';
157
```
158
159
### API Version Filtering
160
161
Utility for filtering hooks based on Node.js version compatibility.
162
163
```typescript { .api }
164
/**
165
* Filter ESM hooks by Node.js API version
166
* @param hooks - Complete hooks object
167
* @param apiVersion - Target API version (1 or 2)
168
* @returns Hooks object compatible with specified API version
169
*/
170
function filterHooksByAPIVersion(
171
hooks: NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2,
172
apiVersion: 1 | 2
173
): NodeLoaderHooksAPI1 | NodeLoaderHooksAPI2;
174
```
175
176
**Usage Examples:**
177
178
```typescript
179
import { createEsmHooks, filterHooksByAPIVersion } from "ts-node";
180
181
const service = create({ esm: true });
182
const allHooks = createEsmHooks(service);
183
184
// Filter for Node.js API version 2 (recommended)
185
const v2Hooks = filterHooksByAPIVersion(allHooks, 2);
186
187
// Filter for Node.js API version 1 (legacy)
188
const v1Hooks = filterHooksByAPIVersion(allHooks, 1);
189
```
190
191
## ESM Entry Points
192
193
### Direct ESM Loader Usage
194
195
Pre-configured ESM loaders for command-line usage.
196
197
```typescript { .api }
198
// Available as /esm.mjs entry point
199
export const resolve: NodeLoaderHooksAPI2['resolve'];
200
export const load: NodeLoaderHooksAPI2['load'];
201
202
// Available as /esm/transpile-only.mjs entry point
203
// Same hooks with transpileOnly: true preset
204
export const resolve: NodeLoaderHooksAPI2['resolve'];
205
export const load: NodeLoaderHooksAPI2['load'];
206
```
207
208
**Usage Examples:**
209
210
```bash
211
# Use ts-node ESM loader
212
node --loader ts-node/esm script.ts
213
214
# Use transpile-only ESM loader for faster startup
215
node --loader ts-node/esm/transpile-only script.ts
216
217
# With experimental specifier resolution
218
node --loader ts-node/esm --experimental-specifier-resolution=node script.ts
219
```
220
221
### Child Process ESM Loader
222
223
Isolated ESM loader for child processes.
224
225
```typescript { .api }
226
// Available as /child-loader.mjs entry point
227
// ESM loader hooks that run in child processes for better isolation
228
export const resolve: NodeLoaderHooksAPI2['resolve'];
229
export const load: NodeLoaderHooksAPI2['load'];
230
```
231
232
**Usage Examples:**
233
234
```bash
235
# Use child process loader for isolation
236
node --loader ts-node/child-loader.mjs script.ts
237
```
238
239
## Configuration for ESM
240
241
### ESM-Specific Options
242
243
Configuration options specifically for ESM module handling.
244
245
```typescript { .api }
246
interface ESMCreateOptions extends CreateOptions {
247
/**
248
* Enable native ESM support
249
* @default false
250
*/
251
esm?: boolean;
252
253
/**
254
* Experimental specifier resolution mode
255
* @default undefined
256
*/
257
experimentalSpecifierResolution?: 'node' | 'explicit';
258
259
/**
260
* Allow .ts extensions in import specifiers
261
* @default false
262
*/
263
experimentalTsImportSpecifiers?: boolean;
264
265
/**
266
* Module type overrides for specific file patterns
267
*/
268
moduleTypes?: ModuleTypes;
269
}
270
```
271
272
**Usage Examples:**
273
274
```typescript
275
import { register } from "ts-node";
276
277
// Full ESM configuration
278
register({
279
esm: true,
280
experimentalSpecifierResolution: "node",
281
experimentalTsImportSpecifiers: true,
282
compilerOptions: {
283
target: "es2020",
284
module: "esnext",
285
moduleResolution: "node",
286
allowSyntheticDefaultImports: true,
287
esModuleInterop: true,
288
},
289
moduleTypes: {
290
"**/*.test.ts": "cjs", // Tests as CommonJS
291
"src/**/*.ts": "esm", // Source as ESM
292
}
293
});
294
```
295
296
## Advanced ESM Usage
297
298
### Mixed Module Systems
299
300
```typescript
301
import { register } from "ts-node";
302
303
// Configure for mixed CJS/ESM project
304
register({
305
esm: true,
306
compilerOptions: {
307
target: "es2020",
308
module: "esnext",
309
moduleResolution: "node",
310
},
311
moduleTypes: {
312
// Legacy components as CommonJS
313
"src/legacy/**/*.ts": "cjs",
314
// New components as ESM
315
"src/modern/**/*.ts": "esm",
316
// Tests follow package.json type
317
"**/*.test.ts": "package",
318
}
319
});
320
```
321
322
### Dynamic Import Support
323
324
```typescript
325
// TypeScript file with dynamic imports
326
export async function loadModule(moduleName: string) {
327
const module = await import(moduleName);
328
return module.default || module;
329
}
330
331
// Works with ts-node ESM loader
332
const result = await loadModule('./my-typescript-module.ts');
333
```
334
335
### ESM with Top-level Await
336
337
```typescript
338
import { register } from "ts-node";
339
340
// Configure for top-level await in ESM
341
register({
342
esm: true,
343
experimentalReplAwait: true,
344
compilerOptions: {
345
target: "es2022", // Required for top-level await
346
module: "esnext",
347
}
348
});
349
```
350
351
```typescript
352
// my-esm-script.ts - Can use top-level await
353
const response = await fetch('https://api.example.com/data');
354
const data = await response.json();
355
console.log(data);
356
```
357
358
### Package.json Configuration
359
360
```json
361
{
362
"type": "module",
363
"scripts": {
364
"dev": "node --loader ts-node/esm src/index.ts",
365
"dev:fast": "node --loader ts-node/esm/transpile-only src/index.ts"
366
},
367
"ts-node": {
368
"esm": true,
369
"experimentalSpecifierResolution": "node",
370
"compilerOptions": {
371
"target": "es2020",
372
"module": "esnext"
373
}
374
}
375
}
376
```