This is an explainer for the module system configuration of the SES shim
package through some properties of package.json
.
For Node.js, this means that any .js
file will be interpreted as if it were
.mjs
, meaning a JavaScript module / ESM, as opposed to .cjs
which is
explicitly CommonJS.
Notably, the node -r esm
emulation does not recognize this distinction
and translates all .js
files down from ESM to CommonJS.
A patch to esm
that is in the Agoric SDK repository allows .js
files in dependencies to break through to the underlying Node.js ESM.
SES provides its own translation from its own ESM sources to CommonJS, emitted
by yarn build
, specifically scripts/bundle.js
.
This is spiritually similar to Rollup because it produces a CommonJS module
from an JavaScript module and its transitive imports, but uses a translation
that was designed to preserve the security properties of SES and participate
fully in SES audits.
The build step generates a file that does not use any module system.
It provides its API by altering the intrinsics and adding functions to
globalThis
. It has no dependencies and consists entirely of sources from the
ses
package. So, a single file is suitable for use as a script, a CommonJS
module, or an ESM (JavaScript module). However, some tools are sensitive
to the file extension, so it copies the same content to dist/ses.umd.js
and
dist/ses.cjs
.
The main
property has been supported by the npm ecosystem since the
earliest versions, so every version of Node.js and every tool will look
here if nothing else in package.json
overrides it.
Some tools like WebPack, Parcel and the ESM emulation provided by node -r esm
use this instead of main
if it is present.
This is a dead-end design that Node.js did not adopt when it implemented
JavaScript modules, but is necssary for these older tools to
indicate that the index.js
source is ESM.
Otherwise, they would use main
which is not a valid ESM.
This could have been ./dist/ses.umd.js
, but the generated file contains
non-ASCII characters (we use zero-width-joiner to avoid collisions with other
names in scope, then censor the use of zero-width-joiner in source).
Most tools tolerate this, but WebPack does not.
The Unpkg CDN uses this property to direct usage of SES to a precompiled
module in "Universal Module Definition" format.
Because the SES shim bundle has no dependencies and uses globalThis
to
vend out its API instead of using any particular module system,
a single SES bundle serves as a CommonJS module and a suitable source for
a <script>
tag.
TypeScript uses this to transport type definitions. These include type declarations (which are scoped like module exports) an also definitions of global variables. A pragma can be used in any JavaScript file to import the global type definitions from SES into environments that assume SES has been initialized and do not directly import the shim.
/// <reference types="ses"/>
Node.js introduced imports
and exports
properties to package.json
when it added support for JavaScript modules.
Any package that provides exports
can enforce which modules inside its
package are externally visible, can create aliases, and can declare predicates
for which alias to use depending on the environment, like import
for systems
supporting JavaScript modules, require
for CommonJS, browser
for scripts.
This could have been a single value and had any extension to support every
usage pattern through versions of Node.js. Before supporting exports
, Node.js
would simply have used main
. And, node -r esm
just uses module
. After
supporting exports
, ignores the extension, relying on the import
or
require
to redirect if necessary for the importers's needs.
However, @web/dev-server
gets confused by the extension.
The variations differ only in file name extension.
Node.js and other tools will use this file when importing ses
as an ESM.
(JavaScript module).
Node.js and other tools will use this file when importing ses
as an CommonJS module.
The most recent SES only provides one API, but a previous version
exported a separate ses/lockdown
layer.
For ease of migration, we provide this alias, but the distinction
is deprecated.
The value is the same as for "."
above.
Tools like Svelte need to access the package.json
of every package in an
application.
Node.js versions with JavaScript module support require this "module" to be
made expressly public.