Skip to content

chore: add ESM type: module to package.json#109319

Merged
JoshuaKGoldberg merged 1 commit intomasterfrom
package-json-type-module
Mar 2, 2026
Merged

chore: add ESM type: module to package.json#109319
JoshuaKGoldberg merged 1 commit intomasterfrom
package-json-type-module

Conversation

@JoshuaKGoldberg
Copy link
Member

@JoshuaKGoldberg JoshuaKGoldberg commented Feb 25, 2026

Switches the default module system for internal config/script files in this repo from CJS to ESM by specifying "type": "module" in the package.json file. In order to support that:

  • .mjs files are renamed to .js, since .js is now ESM instead of CJS
  • .js files using module.exports/require (CJS) are now using export/import (ESM)
  • Include globs in config files are updated to omit .mjs, as that extension is no longer necessary
Quick Node.js CJS/ESM primer

If you're new to the hullabaloo that is Node.js module loading: welcome! There are effectively two main ways that Node.js files are able to export & import things:

  • CommonJS ("CJS"): an old Node.js-specific syntax that's always been available in Node.js
    • Exporting: module.exports.key = value;
    • Importing: const { key } = require("./file");
  • ECMAScript Modules ("ESM"): a new JavaScript standard that's only become widely supported in the last few years
    • Exporting: export const key = value;
    • Importing: import { key } from "./file.js";

Any one file is able to use only one syntax. For legacy reasons Node.js defaults to interpreting .js files as CJS. Node.js can be told to change that (switch to ESM) in two ways:

  • Broad: in a package.json, setting "type": "module switches the default to ESM
  • Per-file: using an explicit .cjs or .mjs extension for a file switches the file to CJS or ESM, respectively

ESM is generally/mostly considered to be a better syntax for a bunch of technical points. Because it's more explicit & less dynamic, it works better for a lot of tools.

Fixes https://linear.app/getsentry/issue/ENG-6928/add-esm-type-module-to-packagejson

@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Feb 25, 2026
tsconfig.json Outdated
"include": [
"*.config.mjs",
"*.config.js",
"*.config.ts",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Question] Could we just go with *.config.* or *.config.*s? Was the having two includes with explicit extensions intentional?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷 *.config.* seems fine, just less explicit

Copy link
Member

@natemoo-re natemoo-re left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm impressed with how little this seems to break so far! Looking good.

tsconfig.json Outdated
"include": [
"*.config.mjs",
"*.config.js",
"*.config.ts",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷 *.config.* seems fine, just less explicit

@JoshuaKGoldberg
Copy link
Member Author

JoshuaKGoldberg commented Feb 25, 2026

Test failures will be resolved by #109326.

@JoshuaKGoldberg JoshuaKGoldberg force-pushed the package-json-type-module branch from 6c3cfcf to f47bd88 Compare February 26, 2026 17:23
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Feb 26, 2026
@github-actions
Copy link
Contributor

🚨 Warning: This pull request contains Frontend and Backend changes!

It's discouraged to make changes to Sentry's Frontend and Backend in a single pull request. The Frontend and Backend are not atomically deployed. If the changes are interdependent of each other, they must be separated into two pull requests and be made forward or backwards compatible, such that the Backend or Frontend can be safely deployed independently.

Have questions? Please ask in the #discuss-dev-infra channel.

Copy link
Member

@scttcper scttcper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

surprised jest doesn't explode

Copy link
Member

@natemoo-re natemoo-re left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have done this much sooner! Nice work 🎉

@@ -1,4 +1,4 @@
'use strict';
import {run} from 'jest';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESM import hoisting defeats env var initialization order

Low Severity

The static import {run} from 'jest' on line 1 is hoisted and evaluated before the process.env assignments on lines 5–7. In the original CJS version, require('jest') was called at the end of the file (after all env setup), matching the comment "Do this as the first thing so that any code reading it knows the right env." Now the jest module (and its transitive dependencies) loads before NODE_ENV, TZ, etc. are set, and the unhandledRejection handler is also registered after the import. A dynamic await import('jest') or moving the env setup to a separate preloaded module would preserve the original execution order.

Fix in Cursor Fix in Web

Copy link
Member Author

@JoshuaKGoldberg JoshuaKGoldberg Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this matters? Jest always runs in test mode, and PUBLIC_URL & TZ are Sentry - only constructs that I know of.

Copy link
Member

@evanpurkhiser evanpurkhiser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is dope

joshuarli added a commit that referenced this pull request Feb 27, 2026
Replace hand-rolled findJsonFiles with Node's built-in
fs.promises.glob, and convert CJS require/module.exports to ESM
import/export to align with the repo-wide ESM migration in #109319.

Co-Authored-By: Claude <noreply@anthropic.com>
@JoshuaKGoldberg JoshuaKGoldberg force-pushed the package-json-type-module branch from 0883b84 to e35bc84 Compare March 2, 2026 13:20
Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

@JoshuaKGoldberg JoshuaKGoldberg force-pushed the package-json-type-module branch from b24be8f to bb573d2 Compare March 2, 2026 14:19
@JoshuaKGoldberg JoshuaKGoldberg merged commit 6b2b1db into master Mar 2, 2026
78 checks passed
@JoshuaKGoldberg JoshuaKGoldberg deleted the package-json-type-module branch March 2, 2026 17:02
JoshuaKGoldberg added a commit that referenced this pull request Mar 2, 2026
Directly changes files from `.mjs` to ~`.mts`. I would have preferred
the cleaner `.ts` but that will require adding `"type": "module"` to
`package.json`, which is more work (which would be a great followup!).~
`.ts`, building on #109319's switching the package to be ESM by default.
Imports are now TypeScript-style extension-less ones.

Now that the plugin's files are typed, this applies a few types
cleanups:

* Uses [typescript-eslint's
`RuleCreator`](https://typescript-eslint.io/developers/custom-rules#rulecreator)
to create rules, which gives nicer typings than the built-in ESLint
`Rule`
* Updates node types from [`estree`](https://github.com/estree/estree)
(only vanilla JS types) to
[`@typescript-eslint/estree`](https://typescript-eslint.io/packages/typescript-estree)
(full TS node types)
* Converts JSDoc types to full TypeScript syntax

This PR should have no changes to the lint rules' behavior. The only
runtime code changes are for deleting unused parameters.
@linear
Copy link

linear bot commented Mar 2, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants