Problem with UltimateExpress bindings

I am trying to update existing bindings for express and move it to ultimate-express support
(GitHub - dimdenGD/ultimate-express: The Ultimate Express. Fastest http server with full Express compatibility, based on µWebSockets.).
This is my fork: GitHub - the-man-with-a-golden-mind/rescript-ultimate-express: Experimental bindings to ultimate-express

It looks fine. However, there is one issue with importing the module.
The example (example/App.res) is compiling to:

import * as UltimateExpress from "ultimate-express";
import UltimateExpress$1 from "ultimate-express";

var app = UltimateExpress$1();
var router = UltimateExpress.Router();

Importing with * does not work here, so I get an error message: UltimateExpress.Router is not a function.

Any clues on how I can solve it? :slight_smile:

The compiler adds a $ and a number to an import when another import with the same name already exists. Is it really a problem?

I’ve to edit the post. Formatting has consumed part of the code. The issue is in “*”.

It looks like ultimate-express uses a default export, rather than exporting everything by name. One way to fix the bindings would be to use @send decorators on the default value.

If you put something like this in UltimateExpress.res:

type ultimateExpressObject
@module("ultimate-express")
external ultimateExpressObject: ultimateExpressObject = "default"
...
module Router = {
  type t
  @send external rawMake: ultimateExpressObject => t = "Router"
  let make = () => rawMake(ultimateExpressObject)
  ...

and then only export make in UltimateExpress.resi:

...
module Router = {
  type t
  let make: unit => t
  ...

you’ll get the same rescript semantics while fixing the js output. (After doing likewise for the other properties, of course)

1 Like

(If you’re curious as to how I worked this out, this JS code tells the whole story. :wink: )

import * as UE from 'ultimate-express';
console.log(UE)
2 Likes

Why have you “wrapped” make with rawMake?

It seems that it needs to be called as a method on the default return of UltimateExpress. By wrapping it, you don’t have to pass around that value, but you could omit it if you wanted to.

Ough. So, now I have to do it for every function. Is there a simpler way? :smiley:

Could write a shim in JS to do the remapping? Something like

import UltimateExpress from "ultimate-express";
export const Router = UltimateExpress.Router;
export const json = UltimateExpress.json
....

Then swap out your existing module decorators to point to the shim. Still some writing, but maybe a little less ceremony.

I might have misunderstood, but couldn’t you do this instead:

module Router = {
  type t
  @module("ultimate-express") @scope("default")
  external make: unit => t = "Router"
}

When used, it generates:

import * as UltimateExpress from "ultimate-express";

let router = UltimateExpress.default.Router();

Which should work as expected.

3 Likes

Something in the back of my head told me I was forgetting something. Dur.

3 Likes