Uncommonjs
A minimum viable shim for module.exports
Install / Use
/learn @chocolateboy/UncommonjsREADME
UnCommonJS
<!-- TOC -->- NAME
- FEATURES
- INSTALLATION
- USAGE
- DESCRIPTION
- TYPES
- GLOBALS
- EXPORTS
- CAVEATS
- DEVELOPMENT
- COMPATIBILITY
- SEE ALSO
- VERSION
- AUTHOR
- COPYRIGHT AND LICENSE
NAME
UnCommonJS - a minimum viable shim for module.exports
FEATURES
module.exportsexports- pluggable
require - supports live exports (ESM emulation)
- tiny (< 700 B minified + gzipped)
- no dependencies
- fully typed (TypeScript)
- CDN builds - [jsDelivr][], [unpkg][]
INSTALLATION
$ npm install @chocolateboy/uncommonjs
USAGE
// ==UserScript==
// @name My Userscript
// @description A userscript which uses some CommonJS modules
// @include https://www.example.com/*
// @require https://unpkg.com/@chocolateboy/uncommonjs@3.2.1
// @require https://cdn.jsdelivr.net/npm/crypto-hash@1.3.0
// @require https://cdn.jsdelivr.net/npm/tiny-once@1.0.0
// ==/UserScript==
console.log(module.exported) // { once: ..., sha1: ..., sha256: ..., ... }
console.log(exports === module.exports) // true
const { once, sha256: encrypt } = exports
// ...
DESCRIPTION
UnCommonJS is a tiny library which exposes a module.exports global (and
exports alias) which behaves like the CommonJS built-in. It can be used to
gather exports in environments which don't otherwise support CommonJS.
Names are deduplicated, so that e.g. if multiple modules export the same name
or assign multiple values to module.exports, each export is given a distinct
name.
This shim is useful in very constrained environments in which it's not possible (usually for political or policy reasons) to use transpilers or bundlers to integrate third-party modules.
Why?
Userscripts
I mainly use it to work around NPM modules that don't have UMD builds when I
want to use one in a [userscript][userscripts] (and as a way to import
dependencies that are available as UMD bundles without polluting window).
For example, let's say I want to use the following modules, which are available on NPM but don't have UMD builds:
Since both of these modules are simple, small, and standalone — i.e. they don't
use require — I can use UnCommonJS to expose module.exports and exports
globals which they can attach their exports to. I can then pull these exported
values (functions in this case) into a userscript simply by extracting them
from the module.exports/exports object:
// ==UserScript==
// @name My Userscript
// @require https://unpkg.com/@chocolateboy/uncommonjs@3.2.1
// @require https://cdn.jsdelivr.net/npm/crypto-hash@1.3.0
// @require https://cdn.jsdelivr.net/npm/tiny-once@1.0.0
// ==/UserScript==
const { once, sha256: encrypt } = module.exports
// ...
ESM-only environments
It can also be used to add support for (dependency-free) NPM modules to environments which only support ESM such as [Deno][] and [QuickJS][].
$ deno
Deno
exit using ctrl+d or close()
> import 'https://unpkg.com/@chocolateboy/uncommonjs@3.2.1'
> import 'https://unpkg.com/micro-down@1.6.2'
> exports.parse('Hi, **this** _is_ [Markdown](#markdown)!')
'<p>Hi, <strong>this</strong> <em>is</em> <a href="#markdown" >Markdown</a>!</p>'
Why not?
This is a hack to get CommonJS modules working in constrained environments such as userscripts when no other option is available. It shouldn't be used in situations or environments where sane solutions are available.
TYPES
The following types are referenced in the descriptions below.
<details>type Exports = Record<PropertyKey, any>
type Require = (id: string) => any
type Module = {
get exports (): Exports;
set exports (value: any);
readonly exported: Exports;
require: Require;
}
type Environment = {
module: Module;
exports: Exports;
require: Require;
}
type Options = {
require?: Require;
}
</details>
GLOBALS
When the shim is loaded, module, exports and
require are defined as global variables if they're not defined
already. Unless noted, they should have the same behavior as the corresponding
values in Node.js and other CommonJS environments.
import '@chocolateboy/uncommonjs/polyfill'
module.exports = 42
console.log(module.exported) // { "default": 42 }
The API can be imported without being automatically registered via the module's main file, e.g.:
import cjs from '@chocolateboy/uncommonjs'
const env = cjs() // { module: ..., exports: ..., require: ... }
Object.assign(globalThis, env)
exports
An alias for module.exports.
module
An object which contains the following properties:
<!-- TOC:display:exported -->module.exported
module.exports (and its exports alias) is
implemented as a thin wrapper (an ES6 Proxy) around the actual exports which
transparently handles name deduplication.
Most of the time this distinction doesn't matter, but it can crop up when
logging/debugging — e.g. when dumping the exported values with console.log —
since some environments display the Proxy's internals, rather than its target.
This can make it hard to see what's actually available. The module.exported
property solves this by exposing a (read-only) view of the underlying object.
console.log(module.exports)
// Proxy { <target>: {…}, <handler>: {…} }
console.log(module.exported)
// Object { once: ..., sha1: ..., sha256: ..., ... }
<!-- TOC:display:exports -->
module.exports
An object (dictionary) of exported values which can be assigned to by name, e.g.:
module.exports.foo = function foo () { ... }
module.exports.bar = function () { ... }
exports is an alias for module.exports, so named assignments to exports
are identical to named assignments to module.exports.
The first time a named export is assigned, it is given the specified name.
Subsequent assignments to the same name with the same value are ignored. If
different values are assigned to the same name, they are assigned unique names
by appending numeric suffixes, e.g.: foo, foo_1, foo_2 etc.
In addition to named exports, default exports can be assigned directly to
module.exports. Note: unlike named exports, which can be assigned to
exports, default exports only work by assignment to module.exports.
If a named function is assigned to module.exports, it is equivalent to a
named export, e.g.:
module.exports = function foo () { }
is equivalent to:
module.exports.foo = function foo () { }
If the assigned value is an anonymous function or a non-function, it is
assigned the name default. As with named exports, default assignments with
the same value are ignored and default assignments with different values are
assigned distinct names by appending a numeric suffix, e.g. default,
default_1, default_2 etc.
If a plain object is assigned to module.exports, its properties are assigned
by name (the object's own, enumerable string keys) in addition to the default
export, e.g.:
const props = { foo, bar }
module.exports = props
is equivalent to:
module.exports.foo = foo
module.exports.bar = bar
module.exports.default = props
<!-- TOC:display:require -->
module.require
An alias for the require export. Can be assigned a new require
implementation which is used whenever the exported require function is
called.
const mods = {
'is-even': (value => value % 2 === 0),
'is-odd': (value => value % 2 === 1),
'is-thirteen': (value => value === 13),
}
module.require = id => {
return mods[id] || throw new Error(...)
}
require
A function which takes a module ID (string) and returns the value exported by the module.
The default implementation is a stub which raises an exception which includes
the name of the required module. It can be overridden by assigning to
module.require.
EXPORTS
default
Type: (options?: Options) => Environment
import cjs from '@chocolateboy/uncommonjs'
const mods = {
'is-even': (value => value % 2 === 0),
'is-odd': (value => value % 2 === 1),
}
const myRequire = id => mods[id] || throw new Error(...)
const env = cjs({ require: myRequire })
Object.assign(globalThis, env)
A function which generates a new CommonJS environment, i.e. an object
containing CommonJS-compatible module, exports and
require properties.
Takes an optional options object supporting the following options:
<!-- TOC:ignore -->require
Type: Require
A require implementation which is delegated to by the exported
require function. If not supplied, it defaults to a function
which raises an exception with the supplied module ID.
CAVEATS
- By default,
requireis defined b
Related Skills
node-connect
330.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
81.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
330.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
81.3kCommit, push, and open a PR
