Node.js loader for compiling TypeScript modules to ESM

Overview

esm-loader

Node.js import hook to instantaneously transform TypeScript to ESM on demand using esbuild.

Features

  • Transforms TypeScript to ESM on demand
  • Supports TS extensions .cjs & .mjs (.cts & .mts)
  • Classic Node.js resolution (extensionless & directory imports)
  • Cached for performance boost
  • Supports Node.js v12.20.0+
  • Handles node: import prefixes

Tip:

esm-loader doesn't hook into require() calls.

Use this with cjs-loader for require() support. Alternatively, use tsx to handle them both automatically.

Install

npm install --save-dev @esbuild-kit/esm-loader

Usage

Pass @esbuild-kit/esm-loader into the --loader flag.

node --loader @esbuild-kit/esm-loader ./file.ts

TypeScript configuration

The following properties are used from tsconfig.json in the working directory:

  • jsxFactory
  • jsxFragmentFactory

Cache

Modules transformations are cached in the system cache directory (TMPDIR). Transforms are cached by content hash so duplicate dependencies are not re-transformed.

Set environment variable ESBK_DISABLE_CACHE to a truthy value to disable the cache:

ESBK_DISABLE_CACHE=1 node --loader @esbuild-kit/esm-loader ./file.ts

FAQ

Can it import JSON modules?

Yes. This loader transpiles JSON modules so it's also compatible with named imports.

Can it import ESM modules over network?

Node.js has built-in support for network imports behind the --experimental-network-imports flag.

You can pass it in with esm-loader:

node --loader @esbuild-kit/esm-loader --experimental-network-imports ./file.ts

Can it resolve files without an extension?

In ESM, import paths must be explicit (must include file name and extension).

For backwards compatibility, this loader adds support for classic Node resolution for extensions: .js, .json, .ts, .tsx, .jsx. Resolving a index file by the directory name works too.

import file from './file' // -> ./file.js
import directory from './directory' // -> ./directory/index.js

Related

Comments
  • Loading WASM is not supported

    Loading WASM is not supported

    Seems like can't handle wasm I guess. Can I use esbuild plugin?

    https://github.com/SettingDust/esm-loader-parse-error There isn't "type": "module" in @minify-html/wasm package.json. So I added

    Error: Parse error @:1:285
        at yt (E:\javascript\esm-loader-parse-error\node_modules\@esbuild-kit\core-utils\dist\index.js:9:7926)
        at oA (E:\javascript\esm-loader-parse-error\node_modules\@esbuild-kit\core-utils\dist\index.js:14:66)
        at dt (E:\javascript\esm-loader-parse-error\node_modules\@esbuild-kit\core-utils\dist\index.js:22:85)
        at H (file:///E:/javascript/esm-loader-parse-error/node_modules/@esbuild-kit/esm-loader/dist/index.js:1:2969)
        at async nextLoad (node:internal/modules/esm/loader:163:22)
        at async ESMLoader.load (node:internal/modules/esm/loader:605:20)
        at async ESMLoader.moduleProvider (node:internal/modules/esm/loader:457:11) {
      idx: 284
    }
    
    

    Full log: https://pastes.dev/t9RjNTub7P

    opened by SettingDust 6
  • Can't resolve directory import

    Can't resolve directory import

    import('./content-extractor') is failed. It's fine after add /index. image

    Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/E:/javascript/article-extractor/src/content-extractor' imported from E:\javascript\article-extractor\src\default-extractors.ts
    
    opened by SettingDust 5
  • Resolve failed when the same name file and directory

    Resolve failed when the same name file and directory

    Reproduction: https://github.com/sxzz/esbuild-esm-loader-issue

    src/
    ├── index.ts
    ├── utils        # this is a directory
    └── utils.ts
    
    node --loader @esbuild-kit/esm-loader src/index.ts
    
    Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/path/esbuild-esm-loader-issue/src/utils' is not supported resolving ES modules imported from /path/esbuild-esm-loader-issue/src/index.ts
        at __node_internal_captureLargerStackTrace (node:internal/errors:465:5)
        at new NodeError (node:internal/errors:372:5)
        at finalizeResolution (node:internal/modules/esm/resolve:433:17)
        at moduleResolve (node:internal/modules/esm/resolve:1009:10)
        at defaultResolve (node:internal/modules/esm/resolve:1218:11)
        at i (file:///path/esbuild-esm-loader-issue/node_modules/.pnpm/@[email protected]/node_modules/@esbuild-kit/esm-loader/dist/index.js:87:17)
        at ESMLoader.resolve (node:internal/modules/esm/loader:580:30)
        at ESMLoader.getModuleJob (node:internal/modules/esm/loader:294:18)
        at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:80:40)
        at link (node:internal/modules/esm/module_job:78:36) {
      code: 'ERR_UNSUPPORTED_DIR_IMPORT',
      url: 'file:///path/esbuild-esm-loader-issue/src/utils'
    }
    

    If rename or remove src/utils, it works.

    IMHO, the problem caused by here. https://github.com/esbuild-kit/esm-loader/blob/0d64195031bb599034be6c2d29427efd4d9d6334/src/loaders.ts#L79-L81

    opened by sxzz 3
  • Support `tsconfig.paths`

    Support `tsconfig.paths`

    This will resolve the error cannot process paths specified at compilerOptions.paths in tsconfig.json

    This patch was created to fix sudden errors from esno::pull#24

    opened by tachibana-shin 3
  • Problems running on GitHub actions across pnpm workspaces

    Problems running on GitHub actions across pnpm workspaces

    I have no clue what's happening 😐 I use this package as a --loader argument to node when running my @avajs tests.

    Everything is working fine when executed locally, GitHub actions throws this at me, where this package seems to have problems resolving local pnpm workspace dependencies?

    image (https://github.com/wilsonjs/wilson2/runs/7886347702?check_suite_focus=true)

    Might be similar to #9, which seems fixed though.

    @esbuild-kit/esm-loader is configured for @avajs tests here: https://github.com/wilsonjs/wilson2/blob/f4bcf26c8279c01198096204ba766b7b416fa11f/packages/utils/ava.config.js#L9

    opened by codepunkt 2
  • Error: Non-relative paths are not allowed when 'baseUrl'

    Error: Non-relative paths are not allowed when 'baseUrl'

    Hello!

    I'm absolutely loving TSX, the dev experience is 💯

    I started getting this error recently, I believe it is since the recent support for paths in tsconfig.

    Error: Non-relative paths are not allowed when 'baseUrl' is not set. Did you forget a leading './'?
        at file:///Users/jameshomer/Projects/Rosewood/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:3:7227
        at Array.map (<anonymous>)
        at file:///Users/jameshomer/Projects/Rosewood/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:3:7105
        at Array.map (<anonymous>)
        at se (file:///Users/jameshomer/Projects/Rosewood/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:3:7002)
        at ce (file:///Users/jameshomer/Projects/Rosewood/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:3:7533)
        at file:///Users/jameshomer/Projects/Rosewood/node_modules/.pnpm/@[email protected]/node_modules/@esbuild-kit/esm-loader/dist/index.js:1:338
        at ModuleJob.run (node:internal/modules/esm/module_job:198:25)
        at async Promise.all (index 0)
        at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
    

    Here is tsonfig.json

    {
      "extends": "./.svelte-kit/tsconfig.json",
      "compilerOptions": {
        "allowJs": true,
        "checkJs": true,
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "resolveJsonModule": true,
        "skipLibCheck": true,
        "sourceMap": true,
        "strict": true
      }
    }
    

    And .svelte-kit/tsconfig.json

    {
    	"compilerOptions": {
    		"baseUrl": "..",
    		"paths": {
    			"$lib": [
    				"src/lib"
    			],
    			"$lib/*": [
    				"src/lib/*"
    			]
    		},
    		"rootDirs": [
    			"..",
    			"./types"
    		],
    		"importsNotUsedAsValues": "error",
    		"isolatedModules": true,
    		"preserveValueImports": true,
    		"lib": [
    			"esnext",
    			"DOM"
    		],
    		"moduleResolution": "node",
    		"module": "esnext",
    		"target": "esnext"
    	},
    	"include": [
    		"../src/**/*.js",
    		"../src/**/*.ts",
    		"../src/**/*.svelte"
    	],
    	"exclude": [
    		"../node_modules/**",
    		"./**"
    	]
    }
    

    I was able to fix the issue by manually adding baseUrl to my root tsconfig.json.

    It seems like perhaps the extends feature is supported for paths but not baseUrl, maybe this is relevant to get-tsconfig?

    Furthermore, after I added baseUrl which fixed this isuue I then had to add .js onto a couple of relative module paths - not a big deal but I thought this loader was supposed to support this automatically. ie. lodash/orderBy -> lodash/orderBy.js.

    Thanks!

    bug 
    opened by homerjam 2
  • Option to disable minification

    Option to disable minification

    I'm using this loader in order to run .tsx files in Mocha, but I noticed when attaching a debugger that the scripts were minified. It'd be nice to be able to configure the esbuild minification option to improve debugging.

    duplicate 
    opened by connorjclark 2
  • Not sure why this doesn't work, but `commander` package doesn't import when using this loader

    Not sure why this doesn't work, but `commander` package doesn't import when using this loader

    So, if I use commander with my TypeScript code and try to run it using tsx - it doesn't work. commander is a very popular npm package.

    Here are steps to reproduce:

    nvm use 16
    npm init -y
    npm add commander
    echo "import { program } from 'commander';" > index.mjs
    npx tsx index.mjs
    

    I get this:

    SyntaxError: The requested module './index.js' does not provide an export named 'default'
        at ModuleJob._instantiate (node:internal/modules/esm/module_job:128:21)
        at async ModuleJob.run (node:internal/modules/esm/module_job:194:5)
        at async Promise.all (index 0)
        at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
        at async loadESM (node:internal/process/esm_loader:88:5)
        at async handleMainPromise (node:internal/modules/run_main:61:12)
    

    However, using just pure node ... works. Setting "type": "module" and using .js extension has same effect.

    The now deprecated https://github.com/antfu/esbuild-node-loader works fine with above steps.

    bug 
    opened by zaripych 2
  • fix: support data urls

    fix: support data urls

    For now, only JavaScript via 'data:text/javascript;base64,... is supported.

    Can add text/typescript or (text/ts? text/tsx?) support later if the use-case arises.

    released 
    opened by privatenumber 1
  • feat: custom tsconfig path via ESBK_TSCONFIG_PATH

    feat: custom tsconfig path via ESBK_TSCONFIG_PATH

    Summary

    This PR adds support for ESBK_TSCONFIG_NAME=tsconfig.custom.json and passes it down to get-tsconfig.

    (This will be used for the upcoming tsx --tsconfig arg)

    released 
    opened by amitdahan 1
  • use with Yarn's PnP mode

    use with Yarn's PnP mode

    Yarn has an alternative modules layout system it calls Plug'n'Play.

    For this to work in ESM mode, it uses a loader. However, node can only load one loader. In this case, first the pnp loader must be called to obtain the module source and then the esbuild loader.

    There's node-loader-core which allows chaining loaders, but I wonder if there would be a more direct way possible?

    There's an official esbuild plugin to make esbuild use pnp. Maybe that could be used?

    opened by wmertens 1
  • Emits messages on the ipc channel

    Emits messages on the ipc channel

    Hello,

    I'm trying to use this loader together with Jest, but are running into some problems. It seems like using this loader causes extra messages to be emitted when spawning a child process using child_process.fork.


    Here is a minimal reproduction:

    package.json

    {
      "type":"module",
      "dependencies": {
        "@esbuild-kit/esm-loader":"^2.5.0"
      }
    }
    

    parent.js

    import child_process from 'node:child_process'
    
    const child = child_process.fork('./child.js')
    
    child.on('message', (msg) => {
      console.log(msg)
    })
    

    child.js

    process.send({ hello: 'world' })
    

    Here is the output when running with just regular Node.js

    $  node ./parent.js 
    { hello: 'world' }
    

    And here is the output with the loader:

    $ node --loader @esbuild-kit/esm-loader ./parent.js
    (node:74148) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
    (Use `node --trace-warnings ...` to show where the warning was created)
    (node:74149) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
    (Use `node --trace-warnings ...` to show where the warning was created)
    { type: 'dependency', path: 'file:///private/tmp/hjdkas/child.js' }
    { hello: 'world' }
    

    Notice the extra { type: 'dependency', path: 'file:///private/tmp/hjdkas/child.js' } line

    opened by LinusU 3
  • Importing a typescript file from a js file using extension fails.

    Importing a typescript file from a js file using extension fails.

    hello.mts:

    const hello = 'hello';
    export default hello;
    

    static.mjs:

    import hello from './hello.mjs';
    console.log(hello);
    

    Execute

    node --loader @esbuild-kit/esm-loader static.mjs
    

    Error

    Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/mshima/git/test/hello.mjs' imported from /Users/mshima/git/test/static.mjs
    

    This is useful for allowJs option.

    opened by mshima 0
  • `ERR_UNSUPPORTED_ESM_URL_SCHEME`  when dyanmic import in windows

    `ERR_UNSUPPORTED_ESM_URL_SCHEME` when dyanmic import in windows

    Description

    In windows, Using dynamic import with absolute path cause ERR_UNSUPPORTED_ESM_URL_SCHEME error

    image

    Reproduction

    1. create a.ts file following
    import path from "path";
    
    const abs_path = path.resolve('./b.ts', );  // absolute path
    import(abs_path)
    
    1. run node --loader @esbuild-kit/esm-loader a.ts

    extra

    tsx has same error.

    opened by tangdaoyuan 2
Releases(v2.5.3)
Owner
esbuild kit
High quality tools enhanced by esbuild
esbuild kit
Babel plugin and helper functions for interoperation between Node.js native ESM and Babel ESM

babel-plugin-node-cjs-interop and node-cjs-interop: fix the default import interoperability issue in Node.js The problem to solve Consider the followi

Masaki Hara 15 Nov 6, 2022
Node.js ESM loader for chaining multiple custom loaders.

ESMultiloader Node.js ESM loader for chaining multiple custom loaders. Fast and lightweight No configuration required, but configurable if needed Usag

jhmaster2000 2 Sep 12, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Command-line toolkit for parsing, compiling, transpiling, optimizing, linking, dataizing, and running EOLANG programs

First, you install npm and Java SE. Then, you install eolang package: $ npm install eolang Then, you write a simple EO program in hello.eo file in th

objectionary 17 Nov 17, 2022
An npm package for demonstration purposes using TypeScript to build for both the ECMAScript Module format (i.e. ESM or ES Module) and CommonJS Module format. It can be used in Node.js and browser applications.

An npm package for demonstration purposes using TypeScript to build for both the ECMAScript Module format (i.e. ESM or ES Module) and CommonJS Module format. It can be used in Node.js and browser applications.

Snyk Labs 57 Dec 28, 2022
CLI utility that parses argv, loads your specified file, and passes the parsed argv into your file's exported function. Supports ESM/TypeScript/etc out of the box.

cleffa CLI tool that: Parses argv into an object (of command-line flags) and an array of positional arguments Loads a function from the specified file

Lily Scott 9 Mar 6, 2022
Universal importer for CommonJS and ESM in Node.js

ModuleImporter by Nicholas C. Zakas If you find this useful, please consider supporting my work with a donation. Description A utility for seamlessly

Human Who Codes 18 Dec 2, 2022
Minimal utility to convert to or from any timezone. Deno/Node/Browser. ESM/CommonJS.

minitz Features Convert dates between any timezone supported by the system. Parses ISO8601 time strings. MIT licensed, use the library any way you wan

Hexagon 14 Oct 10, 2022
This project is based on the Awesome Books app repo, refactored with ES6 and organized with modules. The purpose of this project is to learn functionality organization using JavaScript modules.

Awesome Books with ES6 and modules A basic app project built with HTML, CSS and JS ES6 to keep track of awesome books. Built With HTML/CSS and JS best

Karla Delgado 10 Aug 27, 2022
📦 🍣 Zero-config JS bundler for ESM, CommonJS, and .d.ts outputs

pkgroll Write your code in ESM & TypeScript and bundle it to get ESM, CommonJS, and type declaration outputs with a single command! Features Zero conf

hiroki osame 153 Dec 23, 2022
Recursively publish ESM packages as CommonJS!

Commonify.js For us who are still relying on CommonJS, or using Electron which does not support ESM. ?? See also build-electron I made this tool that

Mikael Finstad 31 Dec 29, 2022
testing rollup dist for cjs/esm

std.module.format version 0.1.3 std.module.format Overview TLDR Avoid Default Exports and Prefer Named Exports Context Summary Decision ECMAScript Mod

sam bacha 5 Dec 11, 2022
📦 🍣 Zero-config JS bundler for ESM, CommonJS, and .d.ts outputs. (Forked from pkgroll)

?? ?? puild (A fork of pkgroll) Write your code in ESM & TypeScript and bundle it to get ESM, CommonJS, and type declaration outputs with a single com

ʀᴀʏ 6 Sep 6, 2022
utility library for promise, support both commonjs and ESM

promising-utils A utility library for promise, supports both commonjs and ESM npm install promising-utils --save yarn add promising-utils wait Used wh

Qiang Li 4 Oct 18, 2022
Three.js boilerplate project configured with typescript, webpack and css/style loader, HTTPS local server, and a sample test codes !!

three.js-boilerplate Welcome, this is a three.js boilerplate project where you can clone it and start to work !!! Installed and Configured Items: Type

pravin poudel 4 Jul 6, 2022
Cumcord loader on Browser (Chrome, Firefox, etc)

CumLoad CumLoad is a Chrome Extension that allows you to load Cumcord and plugins inside your Discord pages. We recommend using the MV2 version becaus

Cumcord Loader Plugins 11 Nov 7, 2022
The Termpura loader core package.

This project is a Work in Progress and currently in development. The API is subject to change without warning. Install npm install @termpura/core Usag

Termpura 2 Oct 15, 2022