You Don't Know ESM
https://beyondcodebootcamp.github.io/presos/javascript-modules/
Video: https://youtu.be/Wt46wuoVZT8
2025: The year of ESM
You can publish a package as a module without breaking downstream dependencies.
Syntax v. Implementation
import "...";
await main();
import Widget from "widget";
https://caniuse.com/import-maps (Mar, 2023)
let Widget = require("widget").default;
https://nodejs.org/en/blog/release/v22.12.0 (Dec, 2024)
Who am I? 🔍
AJ ONeal
Father of 2.
Father of 3.
Deep Learner.
Credentials 🪪
Vermont-Area Linux
Utah JS
Utah Node.js
Utah Rust
Utah Zig
Utah Colo
(big XMission fan)
The Root Company
🐹 Go
(🛜 Network 🔐 Security)
🏢 Colo
(🐧 Linux/POSIX 📦 Proxmox 💽 TrueNAS)
Goal
Try JavaScript
maybe for the first time
maybe create the first modern JS framework
"JavaScript" Modules
"JavaScript"
TC39, WHATWG, W3C, JS Foundation
A Real, Interpreted Language
- By a Browser
- By node.js, etc
- And, most importantly, by both
Whatever you can copy and paste and have it work equally well whether into a browser console or a node REPL.
Framework1Script
import Logo from "./logo.png";
import "./styles.css";
import Greeter from "./greeter";
function HelloWorld() {
return (
<div className="flex justify-center items-center h-screen">
<img src={Logo} alt="Logo" className="logo" />
<h1 className="text-4xl font-bold text-blue-600">
<Greeter name="World" />
</h1>
</div>
);
}
Framework2Script
import config from "./config.json";
function greet(name: string = "World"): string {
const greeting: string = config.defaultGreeting || "Hello";
const message: string = `${greeting}, ${name}!`;
return message;
}
JavaScript (with Types)
import Config from "./config.js";
/**
* @param {String} [name]
* @returns String
*/
function greet(name = "World") {
let greeting = Config.defaultGreeting || "Hello";
let message = `${greeting}, ${name}!`;
return message;
}
NOT JavaScript, NOT ESM
import "./foo";
import "./style.css";
import "./data.json";
ESM is for JavaScript
Module
- use code from another file
- use code from another package (namespaced)
- as JavaScript
1. Local Files
customer.js
var User = {};
User.create = function () {};
employee.js
:
var User = {};
User.create = function () {};
main.js
import Customer from "./customer.js";
import Employee from "./employee.js";
// do stuff ...
TL;DW: export
let User = {};
// ...
export default User;
export let create = User.create;
export let get = User.get;
TL;DW: import
// default
import Employee from "./employee.js";
// explicit
import * as Employee from "./employee.js";
TL;DW: require
// default (counterintuitive)
let Employee = require("./employee.js").default;
// explicit
let Employee = require("./employee.js");
https://nodejs.org/en/blog/release/v22.12.0 (Dec, 2024)
2. Published Packages
Syntax
main.js
import Auth from "@example/auth";
import Keypairs from "keypairs";
// do stuff ...
TC39 ECMAScript Modules: https://tc39.es/ecma262/#sec-modules
Syntax import

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
Syntax export

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
Package Paths & Namespaces
- Browsers: index.html
- Runtimes: package.json
- Tooling: jsconfig.json
(runtime entry points)
Semantics
index.html
<form method="dialog" onsubmit="FooUI.submit(window.event);">...</form>
<script type="importmap">
{
"imports": {
"foo": "./foo.js",
"foo/": "./lib/",
"bar": "./node_modules/bar/index.js"
}
}
</script>
<script type="module">
import FooUI from "foo/ui.js";
window.FooUI = FooUI;
</script>
WHATWG: https://html.spec.whatwg.org/multipage/webappapis.html#import-map
package.json
{
"name": "foo",
"type": "module",
"imports": {
"foo": "./foo.js",
"foo/": "./lib/",
"baz": "./vendor/baz.js"
},
"exports": {
".": "./foo.js",
"./*": "./lib/*"
}
}
https://nodejs.org/api/packages.html#package-entry-points
package.json (bonus)
__dirname
, __filename
import Path from "node:path";
let modulePath = import.meta.url.slice("file://".length);
let moduleDir = Path.dirname(modulePath);
jsconfig.json
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"moduleResolution": "nodenext",
"paths": {
"foo/*": ["./lib/*"],
"foo": ["./foo.js"],
"baz": ["./vendor/baz.js"]
}
}
https://www.typescriptlang.org/tsconfig/#paths
https://www.typescriptlang.org/docs/handbook/modules/reference.html#paths
FUD
The rumors...
- top-level await
- stuttering trees shake HARDER
- async module loading
- ALL-THE-CONFERENCE-SLIDES!
Top-Level Await
# ⚠️ not for published packages
await fetch();
Foobar.init = async function () {
await fetch();
};
Tree Shake Stuttering
// ❌ stuttering doesn't shake harder
import { createFoo, getFoo } from "./stuttering-foo.js";
# ✅ easy-to-search
grep -r -F 'Foo.'
// default
import Employee from "./employee.js";
// explicit
let Employee = require("./employee.js");
This is the way.
2026: The Year of JavaScript?
EOF
Q&A
AJ ONeal - The Root Company
aj@therootcompany.com
🐹 Go (🛜 Network 🔐 Security)
🏢 Colo (🐧 Linux/POSIX 📦 Proxmox 💽 TrueNAS)