Conversation
Switches the npm package from CommonJS to ESM-only output. - tsconfig.json: module/moduleResolution NodeNext, target ES2022 - package.json: adds "type": "module" and "exports" map; drops "main" - CI Generate: adds esModuleInterop=true and importSuffix=.js so ts-proto emits relative imports with .js extensions for NodeNext Breaking change for CommonJS consumers (require() is blocked by the exports map); a major version bump should accompany this before publishing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR migrates the @blueyerobotics/protocol-definitions npm package build output from CommonJS to ESM-only, aligning the TypeScript compiler and ts-proto generation with Node’s ESM/NodeNext requirements.
Changes:
- Update TypeScript compilation to
module/moduleResolution: NodeNextand raisetargettoES2022. - Convert the npm package to ESM-only via
"type": "module"and anexportsmap (dropping the previousmainentrypoint). - Adjust CI generation to emit
.js-suffixed relative imports and enableesModuleInteropin ts-proto output.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tsconfig.json | Switches TS compiler settings to NodeNext ESM output and ES2022 target. |
| package.json | Declares ESM-only package semantics and defines an exports map for the entrypoint/types. |
| .github/workflows/ci-typescript.yaml | Updates ts-proto generation options to be compatible with NodeNext ESM resolution. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Bump to 4.0.0: the output format change is breaking, and the `3.2.0-<sha>` prerelease scheme means a `^3.2.0-<sha>` range would otherwise let `npm update` move consumers onto the ESM build silently. - Use a `default` export condition instead of `import`, so `require()` still resolves on Node 22.12+ via `require(esm)` instead of failing with ERR_PACKAGE_PATH_NOT_EXPORTED. - Restore deep imports via a `./dist/*` subpath export, preserving the specifiers that worked before the `exports` map was introduced. - Move the protoc invocation into a `generate` npm script so CI and the docs cannot drift; CLAUDE.md documented flags that no longer compiled under NodeNext. - Add a CI step that packs the tarball and verifies both the ESM import and the CJS require against the real `exports` map. - Document the module format in README.npm.md and ignore `dist/`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"ESM only" reads as "you cannot require this", which is not true here — require() resolves on Node 22.12+ via require(esm). Lead with what both loaders actually do instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Match the README wording — "ESM-only" implies require() is unavailable, which it is not. Also record why the exports map uses a `default` condition, so it does not get "corrected" back to `import` later. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The published README (README.npm.md, copied over README.md at publish time) never mentioned that this implements protocol v3, so npm consumers had no signal beyond inferring it from the leading version digit. That inference was never reliable — the NuGet package sits at 5.4.0 for the same protocol v3 — so state it explicitly and note that the package version is a separate axis. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 6 changed files in this pull request and generated no new comments.
Suppressed comments (1)
package.json:16
- This wildcard maps
@blueyerobotics/protocol-definitions/dist/telemetryliterally to./dist/telemetry; unlike the old extensionless CommonJS path resolution, package export targets do not search for.js, so that previously valid deep import now fails. If preserving deep-import compatibility is intended, add an extensionless compatibility mapping (while retaining the.jsform), or document this additional breaking change instead of claiming the old specifiers remain supported.
"./dist/*": "./dist/*"
Resolve the package.json version conflict as 3.4.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`exports` targets never get an extension appended, so `"./dist/*": "./dist/*"` broke specifiers like `…/dist/mission_planning` that resolved before the exports map existed — BlueyeCloud imports this way in ~18 files. Map `./dist/*` to `./dist/*.js`, with a `./dist/*.js` entry taking precedence for specifiers that already carry the extension. Also export `./package.json`, which the exports map otherwise blocks. The CI smoke test now covers both deep-import forms and the package.json subpath. Drop the protocol-version section from the npm README; it existed to explain a 4.0.0 bump that is no longer happening. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The workflow only ran on pushes to master, so generation, compilation and the entrypoint smoke test were first exercised after merge. Run them on pull requests too, and limit the npm publish and the blueye-ts bump dispatch to pushes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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.
Summary
Publishes
@blueyerobotics/protocol-definitionsas native ES modules. Existing import specifiers keep working, andrequire()still works on current Node.tsconfig.json:module/moduleResolutionset toNodeNext,targetraised toES2022.package.json: adds"type": "module"and anexportsmap, dropsmain, bumps the version to 3.4.0.generatenpm script withesModuleInterop=trueandimportSuffix=.js(NodeNext requires explicit.jsextensions). CI andCLAUDE.mdnow share the script, so the flags can't drift apart.importandrequire(), both deep-import forms, and thepackage.jsonsubpath against the realexportsmap. The TypeScript workflow now also runs on pull requests, with the npm publish and the blueye-ts dispatch limited to pushes to master, so these checks run before merge instead of after.Why
This is the last step of the 2026-04-30 ESM migration, after BluEye-Robotics/jszmq#2 and BluEye-Robotics/blueye-ts#7. While this package is CommonJS, ESM tooling has to guess its named exports, and Vite's dependency optimizer guesses wrong for
export *: it kept onlydefault, soimport { blueye } from "@blueyerobotics/blueye-ts"failed in the browser. blueye-ts worked around it with an explicit re-export (dffbc78d). Publishing ESM removes that whole class of interop bug for any future consumer. As a side effect, a one-message esbuild bundle drops by about 12% (583.7 KB → 515.8 KB).Compatibility
import { blueye } from "@blueyerobotics/protocol-definitions".require(): the.entry uses adefaultcondition rather thanimport, sorequire()resolves on Node 22.12+ / 20.19+ throughrequire(esm). The generated code has no top-level await. On older Node, or under Jest in CommonJS mode,require()fails.exportstargets never get an extension appended, so./dist/*maps to./dist/*.js. That keeps extensionless specifiers like…/dist/mission_planningworking, which BlueyeCloud uses in about 18 files../dist/*.jstakes precedence for specifiers that already end in.js.package.json: exported explicitly, since theexportsmap would otherwise block it.Shipping only ESM, rather than dual-publishing, also avoids the dual package hazard.
Dependents
3.2.0-d6c9260a, and its extensionless deep imports resolve under the new map. No changes needed to upgrade.Why 3.4.0 and not 4.0.0
CI publishes every master push as a prerelease
X.Y.Z-<sha>, and semver ranges never match prereleases of a differentX.Y.Z, so^3.3.0-<sha>doesn't match3.4.0-<sha>. A minor bump therefore already stopsnpm updatefrom moving anyone onto the ESM build unannounced, and both internal consumers pin exact versions anyway. The only breaks arerequire()on Node older than 22.12 / 20.19 and CommonJS-mode Jest, neither of which applies to any known consumer, so a major version isn't needed.🤖 Generated with Claude Code