What is this for?

Properly converting NodeJS require statements to EcmaScript import statments (using the Tree Sitter parser NOT REGEX, like most the other tools out there). This includes auto-adding ./ in front of local paths, adding .js when file extentions are missing, defaulting to /index.js when importing a directory, etc.

Works for JavaScript.

Note: does not handle exports.

How do I use it?

There's a CLI, a backend interface, and a client-side interface.

CLI

Install

# isntall deno
curl -fsSL https://deno.land/install.sh | sh
# install to-esm
deno install -Afg https://deno.land/x/to_esm/main/to-esm.js

Usage:

# non-destructive, creates file1.esm.js, look for "CHECKME" comments
to-esm ./file1.js

# destructive
to-esm --inplace ./file1.js
to-esm -i ./file1.js

# recursive
to-esm --recursive .
to-esm -r .

# choose the rename extension (file1.fixed.js)
to-esm --add-ext .fixed -- ./file1.js

NOTE: there will never be a perfect conversion. Check the output for CHECKME and FIXME comments for the cases where there were problems.

Backend API

import { toEsm } from "https://deno.land/x/to_esm/main/impure_api.js"

// only does one file at a time
const newFileContent = await toEsm({
    path: "./file1.js", 
    customConverter: (importPathString, path) => {
        // convert all npm stuff to esm.sh
        if (importPathString.startsWith("npm:")) {
            return JSON.stringify("https://esm.sh/${importPathString.slice(4)}")
        }
        if (importPathString === "b") {
            return JSON.stringify("https://deno.land/x/a@1.2.3/mod.js")+" // CHECKME "
        } 
    },
    // if you want to hack an edge case for your own project
    handleUnhandlable: (requireArgs, statementText, statement) => {
        // if the require args are "b" or 'b'
        if (requireArgs.match(/("|')b(\1)/)) {
            return `import { a } from "https://deno.land/x/a@1.2.3/mod.js" // CHECKME`
        }
    }
})

Frontend API

Note: the frontend API can't do as much as the backend API because it can't scan the file system to make intelligent guesses.

import { convertImports } from "https://deno.land/x/to_esm/main/pure_api.js"

const newFileContent = await convertImports({
    fileContent: `require("something")`,
    path,
    defaultExtension=".js",
})