Modules

ESM export

Many modules use conditionally loaded functions inside a synchronous context. Only require supports loading packages or files (cjs) without using an async/await chain all the way to the top-level (e.g. squared-express). NodeJS/NPM packages themselves rarely use top-level await and can load ESM packages synchronously with require as of these versions:

  • 24+

  • 22.12

  • 20.19

E-mc will detect default exports in these versions:

  • 14+ [1]

  • 13.10

  • 12.16

There is not much difference between a “.cjs” and a “.mjs” file except TypeScript 6/7 no longer supports CommonJS.

ESM
import type * as terser from "terser";

import util from "@e-mc/document/util";

export default async function transform(context: typeof terser, value: string, options: terser.MinifyOptions) {
    options.plugins = util.loadPlugins(options.plugins);
    return (await context.minify(value, options)).code;
}
CJS
import type * as terser from "terser";

import util = require("@e-mc/document/util");

export = async function transform(context: typeof terser, value: string, options: terser.MinifyOptions) {
    options.plugins = util.loadPlugins(options.plugins);
    return (await context.minify(value, options)).code;
}

Note

E-mc is shipped only as CJS modules until 1.0.0. The source repository module system did change from “commonjs” to “preserve” in E-mc 0.14. [2]

Private members

Symbols for semi-privacy were replaced with JavaScript private properties [3] in E-mc 0.12.

0.11.0
const kConfig = Symbol('config');

class Host {
    private readonly [kConfig]: Readonly<HostInitConfig>;

    constructor(config: HostInitConfig) {
        if (config.username) {
            HOST_USERNAME.set(this, config.username);
        }
        this[kConfig] = Object.freeze({ ...config });
    }
}
0.12.0
class Host {
    readonly #config: Readonly<HostInitConfig>;

    constructor(config: HostInitConfig) {
        if (config.username) {
            const cipher = HOST.CIPHER;
            this.#username = cipher ? decryptUTF8(cipher.algorithm, cipher.key, cipher.iv, config.username) || '' : config.username;
        }
        this.#config = Object.freeze({ ...config });
    }
}

The username property in particular requires pre-encryption [4] using the encryptUTF8 method exported from the Types package.

ESM imports

The default convention for all packages was removed in E-mc 0.9. It is auto-generated by the TSC compiler and is not necessary for out-of-source tree usage.

0.8.0
import Module from "@e-mc/module"; // OK
const Module = require("@e-mc/module"); // OK

let Module;
import("@e-mc/module").then(res => Module = res.default); // OK

Module.default.isPath("/path/file"); // OK
Module.isPath("/path/file"); // OK
0.9.0
import Module from "@e-mc/module"; // OK
const Module = require("@e-mc/module"); // OK

let Module;
import("@e-mc/module").then(res => Module = res.default); // OK

Module.default.isPath("/path/file"); // FAIL
Module.isPath("/path/file"); // OK

Besides static methods there is an additional utility library within these modules: