Skip to content

ESM-only builds#3064

Draft
mfedderly wants to merge 17 commits into
masterfrom
mf/esm-only
Draft

ESM-only builds#3064
mfedderly wants to merge 17 commits into
masterfrom
mf/esm-only

Conversation

@mfedderly
Copy link
Copy Markdown
Collaborator

@mfedderly mfedderly commented May 11, 2026

Migrate to esm-only packaging, which allows us to drop tsup.

BREAKING:

  • This breaks node@18 compatibility, as it has no way to require() an ESM module (not even with a flag).

Todo:

  • Add test coverage demonstrating various combinations of node/typescript/bundler consumers
  • Add arethetypeswrong coverage
  • Set package.json engines to node >= 22
  • Specify 'evergreen' JS target in docs
  • Github settings: set branch protection on master to only require 22, 24, 26 passing
  • Probably needs to be re-staged as many separate smaller PRs
  • Adding "allowSyntheticDefaultImports": false to the tsconfig requires removing tape (at least)

NodeJS previous releases
NodeJS end of life includes details on outstanding CVEs

@mfedderly mfedderly added this to the v8 milestone May 11, 2026
@mfedderly mfedderly marked this pull request as draft May 11, 2026 13:04
mfedderly added 4 commits May 11, 2026 10:40
…rtex

Upgrading sweepline-intersections in line-intersect started to cause failures in boolean-crosses.
It looks like rowanwins/sweepline-intersections@e7a48be added endpoint intersection detection.
When trying to grapple with this back in boolean-crosses, I noticed that the booleanEqual first parameter was a geometry but the second was a feature which was causing it to return false even when the [1, 1] point was used in the coordinate arrays of both.

After fixing the doLineStringsCross method, it fixed the test that had been marked as false, but now the true test is failing. From spot-checking against PostGIS I think we're actually supposed to return false if two linestrings only cross at an end point.

postgres=# SELECT ST_Crosses(
postgres(#     'LINESTRING(-2 1, 4 1)'::geometry,
postgres(#     'LINESTRING(1 1, 1 2, 1 3, 1 4)'::geometry
postgres(# );
 st_crosses
------------
 f
(1 row)

postgres=# SELECT ST_Crosses(
    'LINESTRING(-2 2, 1 1)'::geometry,
    'LINESTRING(1 1, 1 2, 1 3, 1 4)'::geometry
);
 st_crosses
------------
 f
(1 row)
Move to module: node18 (and an implied moduleResolution node16 per the TypeScript docs for maximum compatibility as it is the most strict.
https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-resolution-for-libraries

This also sets our lib and target to es2022, while removing tslib. The TypeScript output becomes a passthrough and we're still guaranteed node 22 support
This should hopefully catch typing errors locally instead of shipping them and breaking users
Comment thread package.json
Comment on lines -8 to -10
"lint:escheck-cjs": "es-check es8 packages/*/dist/cjs/index.cjs packages/turf/turf.min.js",
"lint:escheck-esm": "es-check --module es8 packages/*/dist/esm/index.js",
"lint:escheck-web": "es-check es5 packages/turf/turf.min.js",
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

escheck-cjs is no longer necessary because we're dropping cjs builds in this PR.

escheck-web is only necessary if you don't trust the Rollup step to output valid syntax. It has caught some things in the past where newer rollups ship catch{} syntax which isn't es5, but we're also going to move this way forward to es2022 or similar with Turf@8.

escheck-esm is no longer necessary and we are instead protected by tsconfig.shared.json's target of es2022 (in this PR). TypeScript will prevent us from using syntax that isn't supported at this version (which aligns with the tsconfig suggestions for node 22.

Comment thread tsconfig.shared.json
"module": "node16",
"outDir": "${configDir}/dist",
"target": "es2022",
"module": "node18",
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Just to call this out another time (it was in the commit message), node18 is actually a relatively strict setting and we would expect node >= 22 and all bundlers to be supported by the output here.

Comment thread tsconfig.shared.json
"target": "es2017",
"module": "node16",
"outDir": "${configDir}/dist",
"target": "es2022",
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This aligns with node@22 and should limit our syntax to things that will work in all supported versions of node. It should also work in all browsers that are reasonably in use today, but we are also going to document that we intend to ship 'evergreen' js and expect users to transpile in their bundle step if they have specific needs.

Comment thread tsconfig.shared.json
"target": "es2022",
"module": "node18",
"declaration": true,
"esModuleInterop": true,
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I hope that setting this to false makes us more strict and gives our users fewer surprises when we change things. It forces us to make sure our own code works without the interop support.

Comment thread tsconfig.shared.json
"strict": true,
"moduleResolution": "node16",
"importHelpers": true,
"skipLibCheck": true,
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Similar to the above, we intentionally incur the additional strictness by enabling checking of the lib types, so that anyone running full lib checking while consuming Turf will not be surprised by issues our CI missed.

mfedderly added 11 commits May 11, 2026 11:09
…or 18 which should be the only place its required
- Adds 26.x CI everywhere
- Removes 18.x for master, but keeps it for a theoretical support/7.x branch
- Removes support/6.x which hasn't been used in a long time
- Notably this keeps node@20 for advisory reasons, it has about 3x the usage of node 18 and isn't holding back new code adoption (yet)

See NodeJS download counts by version here: https://storage.googleapis.com/access-logs-summaries-nodejs/nodejs.org-access.log.20260513.json
- Explicit skipLibCheck to false
- Disable esModuleInterop
- esModuleInterop disabling also implies a change to allowSyntehicDefaultImports to false, but it is required for at least `tape`, so we have to enable it for now
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant