Skip to content

chore: convert to esm + vitest + oxlint/oxfmt#30

Merged
Marsup merged 17 commits into
hapijs:masterfrom
damusix:esm-conversion
Jul 1, 2026
Merged

chore: convert to esm + vitest + oxlint/oxfmt#30
Marsup merged 17 commits into
hapijs:masterfrom
damusix:esm-conversion

Conversation

@damusix

@damusix damusix commented May 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Convert @hapi/bourne from CJS to ESM and switch test/lint tooling. First conversion of the ecosystem-wide ESM migration; bourne is the leaf-most CJS holdout — no @hapi/* runtime deps, 8 downstream packages depend on it — so it's the natural template for the remaining waves.

Why both at once

@hapi/lab does not support ESM. Each conversion has to switch test and lint tooling alongside the module-system swap; lab + code can't follow the ecosystem forward into ESM. The roadmap (in the workspace docs) absorbs the tooling migration into Phase 1 rather than deferring it to a later TypeScript phase.

Notable choices

  • Drop the internals namespace. CJS-era idiom that ESM module scope makes redundant — const suspectRx = ... directly at module scope; no wrapping object.
  • Adopt Object.hasOwn(node, '__proto__') over the defensive Object.prototype.hasOwnProperty.call(node, '__proto__'). Node 16.9+, well within the new engines.node: >=22 baseline.
  • Adopt catch {} for the intentionally-ignored error in safeParse. ES2019+; clearer than catch (ignoreError).
  • Keep the __proto__ regex pre-scan. The TC39 JSON-source-text-access proposal (Node 24+) does NOT make it redundant — the regex check is structural (does the raw text contain a __proto__ key?), not value-based.
  • Don't tighten the .d.ts return types here. parse and safeParse keep returning any; tightening to unknown is a real type-level break for downstream consumers and belongs in the TypeScript-conversion phase, not the ESM PR.
  • oxlint + oxfmt configured to match the previous @hapi/eslint-plugin style (4-space indent, single quotes, semicolons, no trailing commas).
  • Add tsconfig.json with allowJs/checkJs so source stays JS for now but type contracts in lib/index.d.ts are verified by tsc --noEmit. Sets up the next-phase TS conversion.
  • Version stays at 3.0.0. Per project policy, version bumps ship in a separate release commit.

damusix and others added 16 commits May 28, 2026 09:36
Switch module system to ESM (named exports, drop `internals` namespace),
test framework to vitest + @vitest/coverage-v8, lint/format to oxlint +
oxfmt (configured to match the previous hapi style — 4-space indent,
single quotes, semicolons, no trailing commas).

Add tsconfig.json with allowJs/checkJs so source stays JS while the
existing lib/index.d.ts type contracts are verified. Rewrite test/index.ts
to vitest's expectTypeOf. Add typescript devDep + npm typecheck script.

Adopt Node 22+ idioms:
- Object.hasOwn(node, '__proto__') over Object.prototype.hasOwnProperty.call
- catch {} for the intentionally-ignored error in safeParse

Lab does not support ESM, so the test/lint tooling migration has to
happen alongside the module-system swap — this is the per-package
pattern for the ESM phase of the ecosystem-wide migration.

Refs the ESM roadmap in the workspace docs (not in this repo).
Switch from `ci-module.yml@master` to `@min-node-22-hapi-21` so bourne's
CI runs against the new fixed matrix (node 22 + 24 + lts/* + latest)
instead of the broken inputs-conditional matrix on master (which falls
through to testing Node 12/14/16).

The `min-node-version` input is removed in the new workflow, so the
caller's `with:` block goes away. Once that workflow merges to master,
flip back to `@master`.
Replace local .oxlintrc.json/.oxfmtrc.json with oxlint.config.ts/oxfmt.config.ts
extending the shared @hapi/oxc-plugin configs. Format the tree under the shared
oxfmt config and bump oxlint/oxfmt to the versions the plugin requires.
Include the human-written docs in the published tarball so consumers and
LLMs inspecting the package reach usage guidance, not just the built code.
Align with the JS-ESM archetype convention adopted across the Wave 1-2
leaves (src/ + explicit .mjs extension).

- lib/ -> src/; index.js -> index.mjs; index.d.ts -> index.d.mts
  (NodeNext resolves a .mjs import's types from .d.mts, not .d.ts)
- repoint exports/types, vitest coverage include, tests, benchmarks
@Marsup Marsup self-assigned this Jul 1, 2026
@Marsup Marsup added the breaking changes Change that can breaking existing code label Jul 1, 2026
@Marsup Marsup added this to the 4.0.0 milestone Jul 1, 2026
@Marsup
Marsup merged commit 1579494 into hapijs:master Jul 1, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking changes Change that can breaking existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants