Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update bindings style to be compatible with node/es modules/esbuild #6

Merged
merged 13 commits into from
Jun 6, 2024

Conversation

denis-ok
Copy link
Contributor

@denis-ok denis-ok commented Nov 7, 2023

Description

In this PR we add two modules with bindings: one for common js environment usage and another one for es modules environment usage.

Scope of work:

  • Add interface type for bindings.
  • Add two modules with bindings (Cjs/Esm).
  • Setup dual output (cjs/esm) and dual testing.

Problem:

Generated code with [@bs.module] external momentNow: unit => MomentRe.Moment.t = "moment"; doesn't work in node/es modules mode (and esbuild also emits warnings):
Generated code:

import * as Moment from "moment";

var now = Moment();

console.log(now);

export {
  now ,
}

Node error:

var now = Moment();
          ^

TypeError: Moment is not a function

Esbuild warning:

▲ [WARNING] Calling "Moment" will crash at run-time because it's an import namespace object, not a function [call-import-namespace]

    ../../../_build/default/frontend/all/node_modules/melange-moment/MomentRe.bs.js:164:9:
      164 │   return Moment(timestamp * 1000.0);
          ╵          ~~~~~~

  Consider changing "Moment" to a default import instead:

    ../../../_build/default/frontend/all/node_modules/melange-moment/MomentRe.bs.js:4:7:
      4 │ import * as Moment from "moment";
        │        ~~~~~~~~~~~
        ╵        Moment

Generated code with [@bs.module "moment"] external momentNow: unit => MomentRe.Moment.t = "default"; works fine.
Generated code:

import Moment from "moment";

var now = Moment();

console.log(now);

export {
  now ,
}

@denis-ok denis-ok changed the title Update bindings style to be compatible with esbuild + esm output Update bindings style to be compatible with esm/esbuild Nov 8, 2023
@denis-ok denis-ok changed the title Update bindings style to be compatible with esm/esbuild Update bindings style to be compatible with node/es modules/esbuild Nov 8, 2023

let diffWithPrecision = MomentReShared.diffWithPrecision;

[@mel.module "moment"] [@mel.scope "default"]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is also required to work with esm: [@mel.scope "default"].

@denis-ok denis-ok marked this pull request as ready for review November 8, 2023 08:58
@denis-ok denis-ok marked this pull request as draft November 8, 2023 09:00
@denis-ok denis-ok marked this pull request as ready for review November 8, 2023 11:04
@jchavarri
Copy link
Member

Thanks for the PR @denis-ok ! I have a few questions:

Generated code with [@bs.module] external momentNow: unit => MomentRe.Moment.t = "moment"; doesn't work in node/es modules mode (and esbuild also emits warnings):

How did it happen that we never ran into this problem before? We've been using ES6 and Commonjs for years. Is it because esbuild is more restrictive than webpack somehow on the way default exports are handled?

  • Add two modules with bindings (Cjs/Esm).

If this is really an issue across the board, I think it should be solved in Melange, at least for the mid-long term. Could you help me find a reproduction that we can use to submit to the repo upstream? cc @anmonteiro

@anmonteiro
Copy link

anmonteiro commented Jan 12, 2024

Trying to summarize the issues you're seeing:

CommonJS

In order to bind to the module-exported function, we need to write:

type moment

external momentNow : unit -> moment = "moment" [@@mel.module]

let () = Js.log (momentNow ())

ES6

However, in ES6, one needs to write:

type moment

external momentNow : unit -> moment = "default" [@@mel.module "moment"]

let () = Js.log (momentNow ())

And your issue seems to be that there's no way to reconcile the two?

@jchavarri
Copy link
Member

@anmonteiro I think your comment summarizes the issue, afaiu.

@jchavarri
Copy link
Member

I took this branch for a spin and noticed the module names were changing, so that consumers would have to rename MomentRe to either MomentReCjs or MomentReMjs.

In fe6e23b, I added a small change so that the module exposed is MomentRe in both cases. I think this should make migration a bit easier, as the whole cjs / mjs can be left in dune files, while the code itself remains equal.

@jchavarri
Copy link
Member

In c718fd1, i left the mjs lib public name as melange-moment.

With these two changes, consumers like melange-react-dates don't even need to release new versions to support these changes.

@jchavarri jchavarri merged commit 07d2fee into ahrefs:main Jun 6, 2024
jchavarri added a commit to jchavarri/opam-repository that referenced this pull request Jun 6, 2024
CHANGES:

- Fix tests to work with melange v3: [ahrefs/melange-moment#7](ahrefs/melange-moment#7).
- Add support for ES6, place CommonJS support under `melange-moment.cjs`: [ahrefs/melange-moment#6](ahrefs/melange-moment#6).
avsm pushed a commit to avsm/opam-repository that referenced this pull request Sep 5, 2024
CHANGES:

- Fix tests to work with melange v3: [ahrefs/melange-moment#7](ahrefs/melange-moment#7).
- Add support for ES6, place CommonJS support under `melange-moment.cjs`: [ahrefs/melange-moment#6](ahrefs/melange-moment#6).
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.

3 participants