npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

to-esm

v2.27.3

Published

Tool to convert Commonjs files into ESM

Downloads

3,068

Readme

Test workflow nycrc Coverage Version workflow npm version


Description

A tool to convert Commonjs files into ESM files. To-esm can be used for generating hybrid modules. Do all of your coding using CommonJs, then convert your code into ESM at build or packaging times.


Notes

🪛 "to-esm" allows generating ESM code from your existing CommonJs code in the least destructive way possible.

As the generated ESM code will still resemble the original code, you can still keep using cjs without worry. You can generate a new ESM version whenever your system needs a build or set up a watcher.

🚫 CommonJs offers many advantages by its dynamic nature as it seems very back-end oriented. On the other hand, ESM is more designed for a front-end approach and module bundling (which often hides the differences between the two systems) .

V8 is likely developed around the idea that Chrome should not touch the OS file system for evident security matters. Therefore, even though switching to ESM seems ideal, many "hiccups" still exist.

Hybrid modules make things easier. Note that to-esm can help with this.


Installation


npm install to-esm -g

Usage


to-esm <filepath> [--output=<dirpath>] [--html=<filepath>] [--noheader] [--target=< browser|esm|package >] 
[--prefixpath=<dirpath>] [--bundle=<filepath>] [--watch] [--update-all] [--minify] [--no-bundle-minify]

Examples

📋

Generate ESM code

To generate an .mjs(ES module) file from a .js file do:


# 💻 < Command
$> to-esm  example/cjs/input.js

🚫 NOTE: to-esm should run from the project root folder.


Click on the arrow to expand or collapse
📁project                 ⇽ Ran from here
│
└───📁example
│   │
│   └───📁code
│       │   📝 library.js
│       │   📝 demo.js   ⇽
│       │   ...
│

📝 library.js

function hi()
{
    console.log(`I wanted to say hi!`)
}

module.exports = hi;

📝 demo.js

const hi = require("./library.js");
hi();

./demo.js => ./demo.mjs 📝

📁project
│
└───📁example
│   │
│   └───📁code
│       │   📄 library.js
│       │   📄 demo.js
│       │   📝 library.mjs   ⇽
│       │   ...
│
└ 📝 demo.mjs     ⇽

📝 library.js

function hi()
{
    console.log(`I wanted to say hi!`)
}

export default hi;

📝 demo.js

import hi from "./example/code/library.mjs";

hi();

to-esm will convert the entry point inside the working directory. The others will depend on the source location.


📋

Generate code into a dedicated directory

--output < folder >

# 💻 < Command
$> to-esm  example/cjs/input.cjs --output generated/esm

Click on the arrow to expand or collapse
📁project                 ⇽ Ran from here
│
└───📁example
│   │
│   └───📁code
│       │   📝 library.js
│       │   📝 demo.js   ⇽ 🚩
│       │   ...
📁project                 
│
└───📁example
│   │
│   └───📁code
│       │   📝 library.js
│       │   📝 demo.js   
│       │   ...
│
└───📁generated                   ⇽ 🚩
│   └───📁esm
│       └───📁example   
│           └───📁code
│                  📝 library.mjs ⇽ 🚩
│                  📝 demo.mjs    ⇽ 
│                   ...
Checking the conversion has succeeded
node generated/esm/example/code/demo.mjs

📋

Remove automatically generated header

--noheader

# 💻 < Command
$> to-esm  example/cjs/input.cjs --output generated --noheader

⏳ - Without the --noheader option

/**
 * DO NOT EDIT THIS FILE DIRECTLY.
 * This file is generated following the conversion of
 * [./example/code/demo.js]{@link ./example/code/demo.js}
 *
 **/
import hi from "./example/code/library.mjs";

hi();

⌛ With the --noheader option

import hi from "./example/code/library.mjs";

hi();

📋

Generate code for the browser

--target < browser | esm | package >

# Command < 💻
$> to-esm  example/cjs/input.cjs --output generated --target browser

Click on the arrow to expand or collapse

1- When generating code for the browser, **

to-esm** will display a warning when the code uses a native Node library.

📝 demo.js

const path = require("path");                   // See directives below to see how to remove this call
function hi()
{
    console.log(`I wanted to say hi!`)
}

module.exports = hi;
During conversion:
💻 >
to-esm: (1130) ================================================================
to-esm: (1132) Processing: ./example/code/demo.js
to-esm: (1134) ----------------------------------------------------------------
to-esm: (1060) ✔ SUCCESS: Converted [./example/code/demo.js] to [generated-browser\demo.mjs]
to-esm: (1150) 
to-esm: (1130) ================================================================
to-esm: (1132) Processing: ./example/code/library.js
to-esm: (1134) ----------------------------------------------------------------
to-esm: (1017) path is a built-in NodeJs module. ⇽ 🚩
to-esm: (1060) ✔ SUCCESS: Converted [./example/code/library.js] to [generated-browser\example\code\library.mjs]
to-esm: (1150) 

2- To load your files in the HTML code, you only point to the entry file (demo.js).

The browser will automatically load the other files.

img.png

demo.mjs is the entrypoint.

The browser automatically loads all the related files.


Click on the arrow to expand or collapse

When there is a requirement to load libraries from the node_modules folder, to-esm will generate a converted copy of the files to the output directory.

📝 demo.js

const toAnsi = require("to-ansi");
const rgbHex = require("rgb-hex-cjs");
const {COLOR_TABLE, SYSTEM} = require("./some-lib.js");
// ...
📁project  
└─── 📁 original  
     │─── 📝 index.html
     │─── 📝 demo.js
     │─── 📝 some-lib.js
     │
     └─── 📁 generated  
           │─── 📝 demo.mjs             ⬅ 🚩
           │─── 📝 some-lib.mjs         ⬅ 🚩
           │
           └─── 📁 node_modules         ⬅ 🚩
               │
               └───📁 rgb-hex
               │   └── 📝 index.cjs
               │     
               └───📁 to-ansi
                   └── 📝 index.cjs 
                         

The two libraries used will be easily accessible by the system and ease the bundling in production. See the importmap section to have a more modular approach.


📋

Generate importMaps within html files

--html < pattern | html >

# Generates => 📝 ./demo.mjs & update index.html
$> to-esm example/cjs/demo.cjs --html index.html

🚫 NOTE: When this option is used, the target is always "browser". --target.


An import map will allow writing imports like this
import rgbhex from "rgb-hex"
instead of
import rgbhex from "../../../path/to/rgb-hex.mjs"

Click on the arrow to expand or collapse
Before

📝 index.html ↴

<!DOCTYPE html>
<html lang="en">
    <head></head>
    <body>
        <script type="module" src="actual/demo-test.mjs"></script>
    </body>
</html>
After
<!DOCTYPE html>
<html lang="en">
    <head>
        <script type="importmap">
        {
          "imports": {
            "rgb-hex": "./node_modules/rgb-hex/index.cjs"
          }
        }
    
        </script>
    </head>
    <body>
        <script type="module" src="actual/demo-test.mjs"></script>
    </body>
</html>

importmap allows some more elaborated setups where third party caching will be entirely handled by the browser.


📋

Convert files with patterns

You can have multiple files converted in one go. It can be helpful if some files are not connected.


$> to-esm  --input="example/cjs/*.?(c)js" --output=example/esm/


Options to generate code for npm packages --target package

🚫 NOTE: This option is experimental

To generate a non-bundled package code for npm, use this option. It will add an extra prefix ../../ to all relative path to third party modules.


Options to correct relative paths to third party modules

If your code point to a npm module with the option --target browser, the system will convert the import to the module location entrypoint.

For instance:

📝 source.cjs

const toAnsi = require("to-ansi");

Will be converted to this path (or similar):

📝 source.mjs

import toAnsi from "../../node_modules/to-ansi/index.mjs";

The path will be okay at conversion time. However, if the generated file (source.mjs) is required inside a browser, the path may no longer be valid. It will depend on your server configuration.

The option --prefixpath allows correcting the issue by prepending some value to the target path. This way, you can redirect the converted path to point to the correct location on your server.

# 💻 < Command
to-esm source.cjs --output out/ --prefixpath ../somewhere/

📝 source.mjs

import toAnsi from "../somewhere/../../node_modules/to-ansi/index.mjs";

🪛

Options (via command line)

| Options | Description | Expect | default | |---------------------|-------------------------------------------------------------------------------------------------------|--------------------------------|---------| | filepath | File or pattern to convert | file path | | | --bundle | Generate minified bundle for esm environment | file path | | | --bundle-browser | Generate minified bundle for browser environment | file path | | | --bundle-cjs | Generate minified bundle for cjs environment | file path | | | --bundle-esm | Same as above | file path | | | --entrypoint | Explicitely set entry point (otherwise use the first file set in cli) | file path | | | --extension | Set generated files extension | string | .mjs | | --force-lf | Replace CLRF with LF | | | | --html | html files to receive importmaps | glob | | | --keep-external | Do not try to copy files from absolute paths into generated folder | boolean | false | | --keepExisting | Options to skip already converted files | | | | --minify | Options to minify converted files | boolean | | | --nmBrowserImported | Destination folder name for imported third parties when target is "browser" | directory path | | | --no-comments | Options to remove comments from converted files | boolean | false | | --no-bundle-minify | Options to not minify bundled files | boolean | false | | --noHeader | Options to not generate automatic header | boolean | false | | --output | Output directory | directory path | | | --prefixpath | Add a path to paths targeting third party modules | directory path | | | --resolve-absolute | Extra folders to look for when trying to solve absolute imported paths | string | | | --skipEsmResolution | Do not try to resolve third party libraries | boolean | false | | --skipLinks | Don't follow links (It will not convert linked files, but resulting links will need manual updates) | boolean | false | | --target | Setting the targeted environment | esm / browser / package | esm | | --update-all | Automatically update package.json to set entry points | | | | --use-bundle | When updating package.json use bundled/minified code | | | | --watch | Watch mode to automatically apply conversions when changes detected | directory path | | | ~~--subRootDir~~ | ~~Allow to retarget the output sub-directory~~ | ~~true~~ | |

💎

Advanced Options (via config file)

To apply advanced options, create a config file (.to-esm, .to-esm.json or .to-esm.cjs) and it will be automatically loaded. Otherwise, you can create a file with a custom name and explicitly tell the system to load it.

to-esm --config=.my-to-esm-config.cjs

Keys within the config file are case sensitive.

Options to replace strings before and after every conversion

[replaceStart, replaceEnd]

📝 .toesm.cjs ↴


module.exports = {
    replaceStart: [
        {
            search : "const chalk = require(\"chalk\");",
            replace: "// ***"
        },
        {
            search : /const\s+colors\s*=\s*require\(.colors.\);/g,
            replace: "// ***"
        }
    ],
    replaceEnd  : [
        {
            search : `// ***`,
            replace: "// --------- chalk and colors were replaced ----------------"
        }
    ]
}

| Properties | Description | |----------------------|-----------------------------------------------------------------------| | replaceStart | will perform a replacement before doing the conversion to ESM | | replaceEnd | will perform a replacement after doing the conversion to ESM | | replaceStart.search | The regex pattern or string to replace | | replaceStart.replace | The replacement sentence |

Options to use two different modules of the same library.

"replaceModules": ...

Sometimes, you may find libraries where only ESM is available when CJS was available on older versions.

This option allows setting a different version depending on the environment used.

For instance, the module "chalk" uses ESM for its Export on its latest version (5.0.0) and CJS for the older version (4.

1.2).

You can setup toesm to use the appropriate version:

📝 .toesm.cjs ↴

module.exports = {
    replaceModules:
        {
            "rgb-hex":
                {
                    cjs: {
                        name: "rgb-hex-cjs",             // ⬅ 🚩 .cjs files will use 
                                                         // ... = require("rgb-hex-cjs")  
                                                         // to load the module (v3.0.0)
                        version: "@^3.0.0"
                    },
                    esm: {
                        version: "@latest"                  // ⬅ 🚩 .mjs files will use
                                                            // import ... from "rgb-hex"
                                                            // to load the module
                    }
                }
        },
}        

In the .cjs file to convert, you would write:

const rgbhex = require("rgb-hex-cjs");

Which is going to be transformed to:

import rgbhex from "rgb-hex";

| Properties | Description | |-----------------------------------|---------------------------------------------------------| | replaceModules[<moduleName>] | The module we want to use two different versions with | | replaceModules[<moduleName>].cjs | The module version to use with CommonJs files | | replaceModules[<moduleName>].mjs | The module version to use with converted files |

Options to set HTML sources and manipulate importmaps.

"html": ...

module.exports =
    {
        html:
            {
                pattern         : "/index.html",
                importmap       :
                    {
                        "my-project": "../node_modules/my-project/src/esm/add.mjs",
                        "lodash"    : "https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"
                    },
                importmapReplace: [
                    {
                        search : "./node_modules",
                        replace: `/node_modules`,
                    }
                ],
            }
    }

| Properties | Description | |------------------|----------------------------------------------------| | pattern | HTML file pattern where importmap needs updating | | importmap | value to add to HTML files | | importmapReplace | Apply replacements on the importmap list |

The options above will be deployed as below:

<script type="importmap">
        {
          "imports": {
            "my-project": "../node_modules/my-project/src/esm/add.mjs",
            "lodash": "https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"  // ← Example 
          }
        }
    
</script>

Allowing to write this:

import {add} from "my-project"

**NOTE: You can imagine improved caching offered by browsers soon **


💉

Directives

Directives allow more control over the generated code.


Directives to replace code directly from the source.

You can, if you want, also use some to-esm directives within the code. For instance, the code below will not appear when the target is a browser.

/** to-esm-browser: remove **/
const path = require("path");
const fs = require("fs");
const os = require("os");
/** to-esm-browser: end-remove **/

Directives to add code to the source.

It is also possible to add code.

📝 code.cjs ↴

/** to-esm-browser: add
 this.realConsoleLog("LogToFile is not supported in this environment. ")
 **/

In this example, after conversion, the above code will become this:

📝 code.mjs (with target browser) ↴

this.realConsoleLog("LogToFile is not supported in this environment. ")

Directives to ignore code during the parsing so that it won't be converted by mistake.

/** to-esm-all: skip **/
console.log("Skip this");
/** to-esm-all: end-skip **/

Directives to keep the target file as it is.

/** to-esm-all: do-not-overwrite **/

If the .mjs file already exists and contains this directive, it will not be overwritten.


Remove comments from generated code

--no-comments

The example below will remove all comments from the scanned .cjs files.

# 💻 < Command
$> to-esm example/**/*.cjs --output generated --target cjs


💡

Working with both CJS and ESM

You may want to work with both CommonJs and ESM together. So, you benefit from both worlds.

The CommonJs approach is a dynamic one. So, for example, you can do things like:

if (a)
{
    // load module a
    require(a);
}
else
{
    // load module b
    require(b)
}

With ESM and its static approach, loading both modules is necessary.

    // load module a
import "a";
// load module b
import "b";

💡

Some conventions to write code for both CommonJs and ES Modules

Here are a few guides to writing code easily convertible.

Use named exports

For having the best compatibility between the two systems, it is best to use named exports.

Replace structures like:


module.exports = {
    TABLE1  : ...,
    TABLE2  : ...,
    otherKey: ...
}

with:


module.exports.TABLE1 =
...
;
module.exports.TABLE2 =
...
;
module.exports.otherKey =
...
;

Or, if you want to provide a default export too:

// Default export
module.exports = {
    TABLE1, TABLE2, ...
}

// Named export
module.exports.TABLE1 =
...
;
module.exports.TABLE2 =
...
;
module.exports.otherKey =
...
;

Use simple "require"

Rather than using "requires" like below forms (or more complex ones)

🤏 ↴

const Something = require("electron-data-exchanger").myThing;
const anything = require("electron-data-exchanger")(...);

Which may introduce temporary variables (_toesmTemp1)

import _toesmTemp1 from "electron-data-exchanger";

const Something = _toesmTemp1.myThing;

It is best to have them uncomplicated, so the conversion is straightforward.

👍

const MySomething = require("electron-data-exchanger");
const myAnything = require("electron-data-exchanger");

// ... The code that uses what was required

Assignment during declaration

to-esm will not convert the below code as it would require some refactoring that would make the generated code not resemble the original code.

Ambiguous form for to-esm
let myVar = "something";

// ...

myVar = require("my-module")
Expected form:
let myVar = require("my-module");

Use directives to solve issues with environments

Library targeting both Node and the Browser

If it needs to do an import in an ESM environment but not in the Browser, you can use directives to solve the issue.

/** to-esm-browser: add
 let myMod = () => { return "somethings"; };
 **/

/** to-esm-browser: remove **/
let myMod = require("my-mod");
/** to-esm-browser: end-remove **/
The code above will convert to:

Node

import myMod from "my-mod";

Browser

let myMod = () =>
{
    return "somethings";
};

💡

Create a Hybrid Library with to.esm

1- Use the .cjs extension instead of .js

📁project  
└─── 📝 index.cjs                ⬅ 🚩
└─── 📝 package.json
└─── 📁example
│    │
│    └───📁code
│        └───   📝 library.cjs   ⇽ 🚩
│               ...
│
└─── 📁 node_modules
│    └── ...
         

2- Run to-esm against the entry point and update package.json

$> to-esm index.cjs --update-all

🚫 The option --update-all will modify your package.json to make it point to your entrypoint.

// Will point to .index.cjs
require("your-module-name")
 
// Will point to .index.mjs
import("your-module-name") 

Click on the arrow to expand or collapse

📝 ./package.json ↴

{
  "name": "my-project",
  "main": "./index.cjs",
  "scripts": {
    "build": "to-esm index.cjs"
  },
  "devDependencies": {
    "to-esm": "file:.."
  }
}

📝 ./index.cjs ↴

const hi = require("./example/code/library.cjs");
hi();

📝 ././example/code/library.cjs ↴

function hi()
{
    console.log(`I wanted to say hi!`)
}

module.exports = hi;

📝 ./package.json ↴

{
  "name": "my-project",
  "main": "./index.cjs",
  "scripts": {
    "build": "to-esm index.cjs"
  },
  "devDependencies": {
    "to-esm": "file:.."
  },
  "module": "./index.mjs",
  ⬅
  🚩
  "type": "module",
  ⬅
  🚩
  // (Change to commonjs if you don't want to use .cjs extension)
  "exports": {
    ".": {
      "require": "./index.cjs",
      ⬅
      🚩
      "import": "./index.mjs"
      ⬅
      🚩
    }
  }
}

📝 ./index.mjs ↴

/**
 * DO NOT EDIT THIS FILE DIRECTLY.
 * This file is generated following the conversion of
 * [./index.cjs]{@link ./index.cjs}
 *
 **/
import hi from "./example/code/library.mjs";

hi();

📝 ././example/code/library.mjs ↴

/**
 * DO NOT EDIT THIS FILE DIRECTLY.
 * This file is generated following the conversion of
 * [./example/code/library.cjs]{@link ./example/code/library.cjs}
 *
 **/
function hi()
{
    console.log(`I wanted to say hi!`)
}

export default hi;

3- Your code is generated.

To test it in NodeJs.

node index.mjs


💡

Create a Hybrid Library with to.esm supporting the browser

1- Use .cjs extensions instead of .js

📁project  
└─── 📝 index.cjs                ⬅ 🚩
└─── 📝 package.json
└─── 📁example
│    │
│    └───📁code
│        └───   📝 library.cjs   ⇽ 🚩
│               ...
│
└─── 📁 node_modules
│    └── ...
         

2- Run to-esm against en entry point

For the browser
$> to-esm index.cjs --output ./generated --target browser --bundle-browser dist/index.min.js

Click on the arrow to expand or collapse

📝 ./index.cjs ↴

const hi = require("./example/code/library.cjs");
hi();

📝 ././example/code/library.cjs ↴

function hi()
{
    console.log(`I wanted to say hi!`)
}

module.exports = hi;

📝 ./index.mjs ↴

/**
 * DO NOT EDIT THIS FILE DIRECTLY.
 * This file is generated following the conversion of
 * [./index.cjs]{@link ./index.cjs}
 *
 **/
import hi from "./example/code/library.mjs";

hi();

📝 ././example/code/library.mjs ↴

/**
 * DO NOT EDIT THIS FILE DIRECTLY.
 * This file is generated following the conversion of
 * [./example/code/library.cjs]{@link ./example/code/library.cjs}
 *
 **/
function hi()
{
    console.log(`I wanted to say hi!`)
}

export default hi;

📝 ./dist/index.min.js ↴ (Generated because of the --bundle option)

const c = {"95c93": {}};
c["95c93"].default = function ()
{
    console.log("I wanted to say hi!")
};
{
    c.bbc7e = {};
    let b = c["95c93"].default;
    b()
}

3- Your code is generated.

📁project  
└─── 📝 index.cjs                
└─── 📝 package.json
└─── 📁generated                 
│    │
│    └─── 📝 index.mjs          ⬅ 🚩
│    │     
│    └─── 📝 ...
│
└─── 📁 dist         
│    └── index.min.js           ⬅ 🚩
         
Insert the JavaScript browser version
...
<body>
    <script type="module" src="generated/index.mjs"></script>
</body>...
Usage for the ESM version
import "..."

from
"index.mjs" 

💡

Make your CJS module ESM compatible in one go.

Start by creating a .cjs file (or migrate your code) to have this structure.

📁project  
└─── 📝 package.json
└─── 📁 cjs
│    └── 📝 index.cjs
         

Run to-esm against en entry point

$> to-esm cjs/index.cjs --output esm/ --bundle-esm bundle/mycode.min.mjs --bundle-cjs bundle/mycode.min.cjs 
--bundle-browser dist/mycode-browser.min.js --update-all --use-bundle --watch
📁project  
└─── 📝 package.json
└─── 📁 cjs
│    └── 📝 index.cjs                       ⬅ 🚩 Original code
└─── 📁 mjs
│    └── 📝 mycode.mjs                      ⬅ ESM Converted code                     (--output esm/)
└─── 📁 bundle
│    └── 📝 mycode.min.mjs                  ⬅ Bundled and minified code for ESM      (--bundle-esm bundle/mycode.min.mjs)
│    └── 📝 mycode.min.cjs                  ⬅ Bundled and minified code for CommonJs (--bundle-cjs bundle/mycode.min.mjs)
└─── 📁 dist
│    └── 📝 mycode-browser.min.mjs          ⬅ Bundled and minified code for browsers (--bundle-browser bundle/mycode.min.
mjs)   
         
  • The option --update-all will automatically modify your package.json to make your module load the correct file depending on the environment

  • The option --use-bundle will make --update-all configure the module to use minified and bundled versions ("mycode.min.mjs", "mycode.min.cjs") when a "require" or "import" is done against your module (Otherwise, it loads " index. cjs" and "mycode.mjs")

  • The option --watch will automatically regenerate files when a change happens to the original cjs file (. /cjs/index.cjs)

This is it. Your module is fully hybrid, and you can keep working with CommonJs while not worrying about ESM.


Click on the arrow to expand or collapse

📝 ./index.cjs ↴

const hi = require("./example/code/library.cjs");
hi();

📝 ././example/code/library.cjs ↴

function hi()
{
    console.log(`I wanted to say hi!`)
}

module.exports = hi;

📝 ./index.mjs ↴

/**
 * DO NOT EDIT THIS FILE DIRECTLY.
 * This file is generated following the conversion of
 * [./index.cjs]{@link ./index.cjs}
 *
 **/
import hi from "./example/code/library.mjs";

hi();

📝 ././example/code/library.mjs ↴

/**
 * DO NOT EDIT THIS FILE DIRECTLY.
 * This file is generated following the conversion of
 * [./example/code/library.cjs]{@link ./example/code/library.cjs}
 *
 **/
function hi()
{
    console.log(`I wanted to say hi!`)
}

export default hi;

📝 ./dist/index.min.js ↴ (Generated because of the --bundle option)

const c = {"95c93": {}};
c["95c93"].default = function ()
{
    console.log("I wanted to say hi!")
};
{
    c.bbc7e = {};
    let b = c["95c93"].default;
    b()
}

3- Your code is generated.

📁project  
└─── 📝 index.cjs                
└─── 📝 package.json
└─── 📁generated                 
│    │
│    └─── 📝 index.mjs          ⬅ 🚩
│    │     
│    └─── 📝 ...
│
└─── 📁 dist         
│    └── index.min.js           ⬅ 🚩
         
Insert the JavaScript browser version
...
<body>
    <script type="module" src="generated/index.mjs"></script>
</body>...
Usage for the ESM version
import "..."

from
"index.mjs" 


💡

Some thoughts

When you bundle your code, you usually introduce code from other third parties. Therefore, it may create implicit code repetition.

Tree shaking eliminates unnecessary code; however, it is done at "compile" time. It means it can not detect what has been repeated (Even though Tree shaking technics seem to go beyond simple dead code elimination).

Also, most IDEs detect the use of dead code, which also mitigates tree shaking greatness.

For instance, let's say you use two libraries. Lib1.js and Lib2.js.

Lib1 uses lodash and has been minified into lib1.min.js Lib2 also uses lodash and has been minified into lib2.min.js.

When you bundle your code containing lib1.js and lib2.js, you add lodash two times (or some functions of it).

Therefore, there are advantages to not systematically using bundled code.

  • 1- Letting the browser cache all shared libraries is one of them; therefore, having the best caching system > (Chrome or Firefox would surely know the best way to cache files coming from a common ground).

  • 2- Avoiding automated code repetition

  • 3- Less painful and lengthy wait when the codebase becomes enormous.

  • 4- Make Hot Reloading obsolete. Instead, use Cold Reloading (you reload when you save, when not, the bundler has finished its compilation).

  • 5- Working directly on the "original code" rather than an unmaintainable one (even using source maps sometimes > does not solve all the issues).


💊🔥

Why use to-esm?

  • You can keep on working with CommonJs as usual and generates your ESM code when necessary
  • If CommonJs ever becomes obsolete, you can remove your CommonJs code and use the generated one directly
  • The generated ESM code (non-bundled) looks close to the original CommonJs one.

🔥💥

Plus

Benefits:

  • You do not need a source map when working in development mode.
  • You do not need to bundle your code in development.
  • You benefit directly from your browser caching ability (No more bundling of shared libraries)
  • The generated code looks like the original code.
  • ...

Changelog

current:
  • Fix aliases conversion
  • Fix conversion when an exported name does not match a function name
  • Fix options --skipLinks
2.27.0:
  • Allow changing extension of generated files
2.26.5:
  • Fix error 3109 when scripts start with a shell bang symbol
2.26.4:
  • Fix comments disappearance
  • Fix [to-esm add] directive applied too soon
2.26.3:
  • Keep line breaks in package.json on update-all
2.26.2:
  • Add support for some Uglify options on non-bundled generated files --beautify, --braces, --comments, --dead_code, --drop_debugger, --drop_console, --inline, --indent_level, --indent_start, --keep_fargs, --keep_fnames, --keep_quoted, --max_line_len, --preserve_line, --quote_keys, --quote_style, --v8, --expression, --reserved, --ie, --annotations, --module, --nameCache, --semicolons, --sourcemap, --toplevel, --warnings, --webkit, --width, --wrap_iife
  • Add option --no-bundle-minify to not minify generated bundled files
  • Add --minify option to minify non-bundled generated files
  • Add --no-comments option to remove comments from generated code
  • Add --force-lf option to replace CLRF with LF
2.24.1:
  • Remove duplicate export on the same line
  • Add options --skipLinks to avoid following linked files
2.24.0:
  • Add options --skipLinks to avoid following linked files
2.23.1:
  • Fix an undefined source variable error during some parsing
  • Fix undefined options errors
  • Fix async functions exported incorrectly
  • Allow skipping third-party module resolution with the skipEsmResolution option
  • Fix no displayed output
  • Set length for displayed log ids to 4
  • Display help with the PageTerm module
  • Fix --version and --help not displaying correctly
  • Fix cjs minification not generated when conversion is not browser compatible
  • Fix wrong path calculations for third-party browser modules
  • Fix modules detected as installed
  • Fix a broken directive
  • Reduce output directories' depth
  • Update documentation
  • Add option --nmBrowserImported to rename imported node_modules