fix(cjs): make require() a synchronous wrapper around the ESM build - #80
Merged
Merged
Conversation
The CommonJS entry went through a generated loader.cjs that did
`module.exports = (async () => await import("../index.mjs"))()`, so
require("@cldmv/jsonv") - and every require("@cldmv/jsonv/<year>") -
returned a Promise of the ESM namespace instead of the API.
- scripts/build-cjs.mjs: every generated .cjs file is now a thin wrapper,
`module.exports = require("<esm file>")`, loaded through Node's
synchronous require(esm). require() returns the same exports as import.
The async loader.cjs is no longer generated, dist/cjs is rebuilt from
scratch, and dist/years/year-resolver.mjs now gets a CJS wrapper too
(the "./*" export already pointed require at one that did not exist).
- Where Node.js has no require(esm) (before 20.19 / 22.12) the wrappers
throw ERR_REQUIRE_ESM with a message pointing to import() instead of a
bare loader error. engines is unchanged.
- tests/cjs: node:test checks against the built dist/, run by `npm test`
and `npm run coverage` after Vitest: require() of the entry and of a
year module returns the same exports as import, and the version check
fires when require(esm) is off.
- docs: note that require() is synchronous and its Node.js requirement.
Existing `await require("@cldmv/jsonv")` code keeps working, since
awaiting a non-promise returns it unchanged; only code that chained
.then() on the require() result changes.
Shinrai
approved these changes
Oct 3, 2026
cldmv-bot Bot
added a commit
that referenced
this pull request
Oct 5, 2026
# @cldmv/jsonv v1.1.4 Changelog
**Release Date**: October 2026
**Release Type**: Patch
**Branch**: `release/1.1.4`
---
## Overview
Version 1.1.4 fixes the CommonJS entry points. Until now `require("@cldmv/jsonv")`, and every `require("@cldmv/jsonv/<year>")`, returned a Promise of the ESM module rather than the API, so CommonJS code had to `await` it. `require()` now returns the same exports as `import`, synchronously, loaded through Node's `require(esm)`.
Existing `await require("@cldmv/jsonv")` code keeps working unchanged. Two cases do change and are covered in the upgrade notes below: code that chained `.then()` on the `require()` result, and CommonJS code on Node.js versions without `require(esm)`. The ESM API is untouched. The dev toolchain is also refreshed (`@cldmv/fix-headers` 2.2.0, `@cldmv/configs` 1.2.4, `@cldmv/vitest-runner` 1.5.1, `typescript-eslint` 8.71.0); none of it ships in the package.
---
## 🐛 Bug Fixes
### `require()` is a synchronous wrapper around the ESM build ([#80](#80))
The CommonJS build went through a generated `loader.cjs` that did `module.exports = (async () => await import("../index.mjs"))()`, so `require()` handed back a Promise. Every generated `.cjs` file is now a thin wrapper, `module.exports = require("<esm file>")`, so `require()` returns the same module namespace object as `import`:
```js
const { parse } = require("@cldmv/jsonv");
parse("{ a: 1 }"); // { a: 1 }
const jsonv2021 = require("@cldmv/jsonv/2021");
```
The rest of the CommonJS build was tidied along the way:
- The async `loader.cjs` is no longer generated, and `dist/cjs` is rebuilt from scratch on every build so a removed wrapper can't linger.
- `@cldmv/jsonv/year-resolver` now has a CommonJS wrapper. The `"./*"` export already pointed `require` at `dist/cjs/years/year-resolver.cjs`, but that file was never built, so `require("@cldmv/jsonv/year-resolver")` failed outright. `@cldmv/jsonv/loader` and the year modules are wrapped the same way.
- On Node.js without `require(esm)` (before 20.19.0, or 22.0–22.11), the wrappers throw an `ERR_REQUIRE_ESM` error whose message names the required Node.js versions and points to `import()`, instead of failing with a bare loader error.
---
## 🔧 CI & tooling
- New `tests/cjs/entry.test.cjs` (`node:test`), run by `npm test` and `npm run coverage` after Vitest through a new `test:cjs` script. It checks against the built `dist/` that `require()` of the entry and of a year module returns the same exports as `import`, and that the Node.js version check fires when `require(esm)` is unavailable.
---
## 📚 Documentation
- **NEW:** [docs/changelog/v1/v1.1.4.md](./v1.1.4.md) — this changelog.
- [docs/versioning-and-exports.md](https://github.com/CLDMV/jsonv/blob/master/docs/versioning-and-exports.md) — notes that `require()` is synchronous and states its Node.js requirement.
- README — restructured to the CLDMV README layout (badges, What's New, Installation with Node.js requirements, Documentation index, Links), with the changelog history backfilled for every earlier release under `docs/changelog/v1/`.
---
## 🔧 Dependencies
All development-only:
- `@cldmv/vitest-runner` 1.4.3 → 1.5.1 ([#77](#77))
- `typescript-eslint` 8.70.1 → 8.71.0 ([#77](#77))
- `@cldmv/fix-headers` `^2.1.1` → `^2.2.0`, resolved to 2.2.0. The range was first raised to `^2.1.4` ([#83](#83)) and then to `^2.2.0` ([#85](#85)). Version 2.1.4 no longer writes a JavaScript comment into JSON or Markdown files, processes every repeated `--input`, and never walks dependency folders such as `node_modules`. Version 2.2.0 changes `@Last modified by` only when a file's content was edited, so header-only rewrites keep the recorded editor.
- `@cldmv/configs` `^1.2.0` → `^1.2.4`, resolved to 1.2.4. The shared fix-headers config now sets `forceAuthorUpdate` and `forceLastModifiedAuthorUpdate` to false ([#85](#85)).
- No file headers were restamped: each fix-headers bump changed only `package.json` and the lockfile.
---
## Upgrade notes
- **`await require(...)` keeps working.** Awaiting a non-Promise returns it unchanged, so CommonJS code written against the old Promise-returning entry needs no change.
- **`.then()` on the `require()` result no longer works.** `require("@cldmv/jsonv").then((jsonv) => ...)` now throws `TypeError: ... .then is not a function`, because the result is the module itself. Use the result directly (`const jsonv = require("@cldmv/jsonv")`), or `await` it.
- **CommonJS needs Node.js ^20.19.0 or >=22.12.0.** On older Node.js, where `await require("@cldmv/jsonv")` used to resolve through the async loader, `require()` now throws `ERR_REQUIRE_ESM` with a message pointing to `import()`. Load the package with `await import("@cldmv/jsonv")` there. The `engines` field (`>=18.0.0`) and ESM `import` support are unchanged.
<details>
<summary>👥 Contributors</summary>
- @Shinrai
</details>
---
<!-- coverage-start -->

| Metric | Coverage |
|--------|----------|
| Statements | 98.7% |
| Branches | 97.3% |
| Functions | 100.0% |
| Lines | 98.8% |
*Avg: **98.7%** · `640792e` · Node lts/**
<!-- coverage-end -->
<!-- co-authors -->
Co-authored-by: Shinrai <Shinrai@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🚀 What's Changed
💥 Breaking Changes
No breaking changes
✨ Features
No new features
🐛 Bug Fixes
📦 Dependencies
No dependency updates
🔧 Other Changes
No other changes
👥 Contributors