chore: convert to esm + vitest + oxlint/oxfmt#30
Merged
Conversation
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
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.
Summary
Convert
@hapi/bournefrom 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/labdoes 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
internalsnamespace. CJS-era idiom that ESM module scope makes redundant —const suspectRx = ...directly at module scope; no wrapping object.Object.hasOwn(node, '__proto__')over the defensiveObject.prototype.hasOwnProperty.call(node, '__proto__'). Node 16.9+, well within the newengines.node: >=22baseline.catch {}for the intentionally-ignored error insafeParse. ES2019+; clearer thancatch (ignoreError).__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..d.tsreturn types here.parseandsafeParsekeep returningany; tightening tounknownis a real type-level break for downstream consumers and belongs in the TypeScript-conversion phase, not the ESM PR.@hapi/eslint-pluginstyle (4-space indent, single quotes, semicolons, no trailing commas).tsconfig.jsonwithallowJs/checkJsso source stays JS for now but type contracts inlib/index.d.tsare verified bytsc --noEmit. Sets up the next-phase TS conversion.