CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ts-node

TypeScript execution environment and REPL for Node.js with source map support

Pending
Overview
Eval results
Files

esm.mddocs/

ESM Integration

Native ECMAScript module support with loader hooks for seamless TypeScript execution in ESM contexts and modern Node.js applications.

Capabilities

ESM Hooks Creation

Creates ESM loader hooks for TypeScript module resolution and compilation.

/**
 * Create ESM loader hooks from an existing ts-node service
 * @param tsNodeService - Service instance for TypeScript compilation
 * @returns ESM hooks object compatible with Node.js loader API
 */
function createEsmHooks(tsNodeService: Service): NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2;

/**
 * Register ts-node and create ESM hooks in one step
 * @param opts - Configuration options for registration
 * @returns ESM hooks object
 */
function registerAndCreateEsmHooks(opts?: RegisterOptions): NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2;

Usage Examples:

import { createEsmHooks, create } from "ts-node";

// Create service and hooks separately
const service = create({
  esm: true,
  compilerOptions: {
    target: "es2020",
    module: "esnext",
  }
});

const hooks = createEsmHooks(service);

// Or create both in one step
const hooks = registerAndCreateEsmHooks({
  esm: true,
  compilerOptions: {
    target: "es2020",
    module: "esnext",
  }
});

Node.js Loader Hooks API

ESM loader hooks compatible with Node.js loader API versions 1 and 2.

/**
 * Node.js Loader Hooks API Version 1
 */
interface NodeLoaderHooksAPI1 {
  /**
   * Resolve module specifier to URL
   * @param specifier - Module specifier to resolve
   * @param context - Resolution context
   * @param defaultResolve - Default Node.js resolve function
   * @returns Promise resolving to module URL and format
   */
  resolve(
    specifier: string,
    context: { parentURL?: string; conditions?: string[] },
    defaultResolve: Function
  ): Promise<{ url: string; format?: NodeLoaderHooksFormat }>;

  /**
   * Get format of resolved module
   * @param url - Module URL
   * @param context - Format context
   * @param defaultGetFormat - Default Node.js getFormat function
   * @returns Promise resolving to module format
   */
  getFormat(
    url: string,
    context: object,
    defaultGetFormat: Function
  ): Promise<{ format: NodeLoaderHooksFormat }>;

  /**
   * Transform source code before execution
   * @param source - Source code to transform
   * @param context - Transform context including URL and format
   * @param defaultTransformSource - Default transform function
   * @returns Promise resolving to transformed source
   */
  transformSource(
    source: string | Buffer,
    context: { url: string; format: NodeLoaderHooksFormat },
    defaultTransformSource: Function
  ): Promise<{ source: string | Buffer }>;
}

/**
 * Node.js Loader Hooks API Version 2 (Node.js 16.12.0+)
 */
interface NodeLoaderHooksAPI2 {
  /**
   * Resolve module specifier to URL
   * @param specifier - Module specifier to resolve
   * @param context - Resolution context
   * @param next - Next resolver in chain
   * @returns Promise resolving to module URL and format
   */
  resolve(
    specifier: string,
    context: {
      conditions: string[];
      importAssertions?: object;
      parentURL?: string;
    },
    next: Function
  ): Promise<{
    format?: NodeLoaderHooksFormat;
    shortCircuit?: boolean;
    url: string;
  }>;

  /**
   * Load and transform module source
   * @param url - Module URL to load
   * @param context - Load context
   * @param next - Next loader in chain
   * @returns Promise resolving to loaded module
   */
  load(
    url: string,
    context: {
      conditions: string[];
      format?: NodeLoaderHooksFormat;
      importAssertions?: object;
    },
    next: Function
  ): Promise<{
    format: NodeLoaderHooksFormat;
    shortCircuit?: boolean;
    source?: string | Buffer;
  }>;
}

/**
 * Supported ESM module formats
 */
type NodeLoaderHooksFormat = 
  | 'builtin'
  | 'commonjs' 
  | 'json'
  | 'module'
  | 'wasm';

API Version Filtering

Utility for filtering hooks based on Node.js version compatibility.

/**
 * Filter ESM hooks by Node.js API version
 * @param hooks - Complete hooks object
 * @param apiVersion - Target API version (1 or 2)
 * @returns Hooks object compatible with specified API version
 */
function filterHooksByAPIVersion(
  hooks: NodeLoaderHooksAPI1 & NodeLoaderHooksAPI2,
  apiVersion: 1 | 2
): NodeLoaderHooksAPI1 | NodeLoaderHooksAPI2;

Usage Examples:

import { createEsmHooks, filterHooksByAPIVersion } from "ts-node";

const service = create({ esm: true });
const allHooks = createEsmHooks(service);

// Filter for Node.js API version 2 (recommended)
const v2Hooks = filterHooksByAPIVersion(allHooks, 2);

// Filter for Node.js API version 1 (legacy)
const v1Hooks = filterHooksByAPIVersion(allHooks, 1);

ESM Entry Points

Direct ESM Loader Usage

Pre-configured ESM loaders for command-line usage.

// Available as /esm.mjs entry point
export const resolve: NodeLoaderHooksAPI2['resolve'];
export const load: NodeLoaderHooksAPI2['load'];

// Available as /esm/transpile-only.mjs entry point  
// Same hooks with transpileOnly: true preset
export const resolve: NodeLoaderHooksAPI2['resolve'];
export const load: NodeLoaderHooksAPI2['load'];

Usage Examples:

# Use ts-node ESM loader
node --loader ts-node/esm script.ts

# Use transpile-only ESM loader for faster startup
node --loader ts-node/esm/transpile-only script.ts

# With experimental specifier resolution
node --loader ts-node/esm --experimental-specifier-resolution=node script.ts

Child Process ESM Loader

Isolated ESM loader for child processes.

// Available as /child-loader.mjs entry point
// ESM loader hooks that run in child processes for better isolation
export const resolve: NodeLoaderHooksAPI2['resolve'];
export const load: NodeLoaderHooksAPI2['load'];

Usage Examples:

# Use child process loader for isolation
node --loader ts-node/child-loader.mjs script.ts

Configuration for ESM

ESM-Specific Options

Configuration options specifically for ESM module handling.

interface ESMCreateOptions extends CreateOptions {
  /**
   * Enable native ESM support
   * @default false
   */
  esm?: boolean;
  
  /**
   * Experimental specifier resolution mode
   * @default undefined
   */
  experimentalSpecifierResolution?: 'node' | 'explicit';
  
  /**
   * Allow .ts extensions in import specifiers
   * @default false
   */
  experimentalTsImportSpecifiers?: boolean;
  
  /**
   * Module type overrides for specific file patterns
   */
  moduleTypes?: ModuleTypes;
}

Usage Examples:

import { register } from "ts-node";

// Full ESM configuration
register({
  esm: true,
  experimentalSpecifierResolution: "node",
  experimentalTsImportSpecifiers: true,
  compilerOptions: {
    target: "es2020",
    module: "esnext",
    moduleResolution: "node",
    allowSyntheticDefaultImports: true,
    esModuleInterop: true,
  },
  moduleTypes: {
    "**/*.test.ts": "cjs", // Tests as CommonJS
    "src/**/*.ts": "esm",  // Source as ESM
  }
});

Advanced ESM Usage

Mixed Module Systems

import { register } from "ts-node";

// Configure for mixed CJS/ESM project
register({
  esm: true,
  compilerOptions: {
    target: "es2020",
    module: "esnext",
    moduleResolution: "node",
  },
  moduleTypes: {
    // Legacy components as CommonJS
    "src/legacy/**/*.ts": "cjs",
    // New components as ESM
    "src/modern/**/*.ts": "esm",
    // Tests follow package.json type
    "**/*.test.ts": "package",
  }
});

Dynamic Import Support

// TypeScript file with dynamic imports
export async function loadModule(moduleName: string) {
  const module = await import(moduleName);
  return module.default || module;
}

// Works with ts-node ESM loader
const result = await loadModule('./my-typescript-module.ts');

ESM with Top-level Await

import { register } from "ts-node";

// Configure for top-level await in ESM
register({
  esm: true,
  experimentalReplAwait: true,
  compilerOptions: {
    target: "es2022", // Required for top-level await
    module: "esnext",
  }
});
// my-esm-script.ts - Can use top-level await
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);

Package.json Configuration

{
  "type": "module",
  "scripts": {
    "dev": "node --loader ts-node/esm src/index.ts",
    "dev:fast": "node --loader ts-node/esm/transpile-only src/index.ts"
  },
  "ts-node": {
    "esm": true,
    "experimentalSpecifierResolution": "node",
    "compilerOptions": {
      "target": "es2020",
      "module": "esnext"
    }
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-ts-node

docs

cli.md

configuration.md

core-service.md

esm.md

index.md

register.md

repl.md

transpilers.md

tile.json