Skip to content

feat: esm#4509

Open
florian-lefebvre wants to merge 16 commits intolovell:0.35from
florian-lefebvre:feat/build-step
Open

feat: esm#4509
florian-lefebvre wants to merge 16 commits intolovell:0.35from
florian-lefebvre:feat/build-step

Conversation

@florian-lefebvre
Copy link
Copy Markdown

@florian-lefebvre florian-lefebvre commented Mar 18, 2026

Closes #2981

I kept this PR as minimal as I could. It introduces esbuild to be able to compile ESM to CJS to support both:

  • I had to migrate lib to ESM because esbuild doesn't support transforming require() into import
  • I had to write a little esbuild plugin to transform TLAs into require()

Things left to do where I'll need some help:

  • I added a build script for esm/cjs but I'm not sure how it relates to install/build.js
  • I'm not sure how tests are currently setup so unsure how to properly test esm/cjs
  • I left some require() calls in libvips.js because they only seem consumed by binding.gyp

Comment thread lib/sharp.js
}

if (sharp) {
module.exports = sharp;
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Conditional exports are not supported by ESM but it doesn't matter here because importing the module will throw as needed

@lovell
Copy link
Copy Markdown
Owner

lovell commented Mar 20, 2026

This is great, thank you for doing the hard work Florian, and at less of an impact in terms of changed code than I had imagined, merci donc d'avoir fait simple.

For the v0.35.x releases I'd like to maintain compatibility with versions of Node.js where ESM isn't fully available and then I suspect the v0.36.x releases (or maybe v1.0.x if I'm feeling lucky after 13 years) is the place for this to land and wish CommonJS bon voyage.

As an aside, I've been invited to (and accepted) the Astro event in London in a few weeks and spotted your name on the list so I might be able to thank you in person for this.

@florian-lefebvre
Copy link
Copy Markdown
Author

Glad I can help :) Yeah I saw you on the list for the event, looking forward to meeting you! I'll give a talk on fonts

Btw I was thinking about this PR during the weekend and I don't know if the packages under npm can be updated to ESM yet. Maybe worth waiting for 0.36 or 1.0 because altho it's just matter of importing the .node file, not sure if that'd work for a CJS project

@florian-lefebvre
Copy link
Copy Markdown
Author

@lovell can you approve the workflow to see if the tests pass?

@florian-lefebvre
Copy link
Copy Markdown
Author

florian-lefebvre commented Apr 2, 2026

To keep this PR as minimal as possible, I switched from ESM first + CJS compat to CJS first + ESM compat, which required a few more changes. Workflow should run better next time you approve it!

@florian-lefebvre
Copy link
Copy Markdown
Author

Build and tests are now passing locally!

Comment thread package.json
],
"scripts": {
"build": "node install/build.js",
"build:dist": "node scripts/build.mjs",
Copy link
Copy Markdown
Author

@florian-lefebvre florian-lefebvre Apr 5, 2026

Choose a reason for hiding this comment

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

I had to add another script for lib to dist compilation because if I understood correctly, sharp can be built from source by doing npm explore sharp -- npm run build. If the compilation happened in this script, it would fail since mjs files in lib are not shipped to NPM anymore

@florian-lefebvre
Copy link
Copy Markdown
Author

I swear this should work now

@florian-lefebvre florian-lefebvre marked this pull request as ready for review April 6, 2026 12:14
@styfle
Copy link
Copy Markdown
Contributor

styfle commented Apr 7, 2026

Can we remove CJS completely from the published npm package now that you can require(esm) in every LTS version of Node.js (20,22, 24) today?

https://joyeecheung.github.io/blog/2025/12/30/require-esm-in-node-js-from-experiment-to-stability/

Node.js 18 has been EOL for a year now:

https://nodejs.org/en/about/previous-releases#looking-for-the-latest-release-of-a-version-branch

@florian-lefebvre
Copy link
Copy Markdown
Author

florian-lefebvre commented Apr 8, 2026

My first thought was also to move to ESM-only but we can't unfortunately (yet). The problem is CJS cannot require ESM with TLAs, which is currently used to load the native binaries.

This PR aims to be as small/compatible as possible. Once this is released, I'll make another PR for another release (maybe 1.0?) to move to ESM-only.

To be clear, that means that in the current state it could not be used by CJS at all. Maybe that's fine, otherwise it will require a change in the public API, e.g.:

-import sharp from 'sharp'
+import loadSharp from 'sharp'
+const sharp = await loadSharp() // native binaries loading

await sharp(buffer).resize()

I think 1.0 would be a good fit for ESM-only + API change + Node engine requirement change

@styfle
Copy link
Copy Markdown
Contributor

styfle commented Apr 8, 2026

Once this is released, I'll make another PR for another release (maybe 1.0?) to move to ESM-only.

Whats the purpose of the intermediary step though?

Its making sharp (thats downloaded 53M times per week) larger and sounds like it doesn't solve the original issue (making sharp work in non-CJS environments).

@florian-lefebvre
Copy link
Copy Markdown
Author

florian-lefebvre commented Apr 8, 2026

Supporting both at the same time seemed to be Lovell's preferred solution on the issue so that's why. I don't have strong opinions on whether that's not worth doing. At the very least it's useful as research

sounds like it doesn't solve the original issue (making sharp work in non-CJS environments).

This PR makes it work perfectly fine in ESM and CJS, but yeah with tradeoff of making it larger

@lovell
Copy link
Copy Markdown
Owner

lovell commented Apr 9, 2026

@styfle @florian-lefebvre Thanks both, a move to ESM-only as part of a major version bump feels like the way to go here.

I still think it's worth providing support for both CJS and ESM in v0.35.0, as Florian has kindly implemented in this PR.

For those who care about installation size (I know you do Steven 😅 ) then the biggest issue with the next release is going to be via an npm bug that erroneously installs transitive dependencies of unmet optional dependencies - see npm/cli#8832 (comment)

@florian-lefebvre florian-lefebvre changed the base branch from main to 0.35 April 13, 2026 12:51
@florian-lefebvre
Copy link
Copy Markdown
Author

Alright I fixed everything so it should work fine against the 0.35 branch 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.

Add support to ES Modules

3 participants