COMMUNITY

I'm working on creating an extern for CORS via Express JS

Hey :). As part of building an open source stack for everybody to witness the wonder that is Haxe, I’m working at a Node JS Express server (complete with watchers, debug via VS Code, source maps integrated in the error reports in the console and more).

For the past few hours I’ve been tinkering at getting CORS to work with Express. I’ve been using @haxiomic 's dts2hx to generate the externs for both Express and CORS. Express worked fine, but I got stuck with CORS.

I think dts2hx doesn’t completely process the cors package. The .ts files have a namespace declaration called “e”. dts2hx does extract the members in the namespace, but only at a “root level” so to say. It also misses a critical function needed to wire up cors to Express.

The function is at the end of the node_modules\@types\cors\index.d.ts file:

//the rest of the file...

declare function e(
    options?: e.CorsOptions | e.CorsOptionsDelegate
): express.RequestHandler;
export = e;

And this is missing in the Haxe extern. So…

I’ve been working at declaring an extern for this e. I haven’t wrote a single extern before, but I do understand the principles of it. So, I did /some/ work.

But now I need a pointer regarding how would one create an extern for a JS function that is located outside of a class.

This is the class I’ve added to the cors dts2hx-generated package:

And this is how I’m trying to use it, of course, trying to hook it in the Express middleware by using app.use()

Of course, this can’t work anyway because I created the class as HowToMapThis, because I don’t know how to reach that e function :slight_smile:

Oh, and if you want to actually run the project and work directly on it, see the readme.md in the repo root :slight_smile:

dts2hx should be converting that function, I’ve opened a bug to track

The cors definitions aren’t really clear; the function e isn’t actually called e in the source code, it’s just the object you get when you import the cors module. In a sense the function is the module

So to call this function in javascript you might do

let cors = require('cors');
let requestHandler = cors({ /* options */ }); // this is the function 'e'

In haxe, modules must be types or classes so a js function-as-a-module must be wrapped in a class, and use can use the @:selfCall metadata to mark a function as calling the module

// Cors.hx
@:jsRequire('cors') extern class Cors {
    @:selfCall
    static function call(?options: EitherType<CorsOptions, CorsOptionsDelegate>): express.RequestHandler;
}

// usage:
import Cors;
var requestHandler = Cors.call(options);
// or
app.use(Cors.call(options));

This is the same pattern used in the express library, which is why in haxe you do Express.call() rather than express() as you might in js.

Module-level functions are in nightly haxe builds so in the future we might be able to change it so it becomes

// Cors.hx
extern function cors(?options: EitherType<CorsOptions, CorsOptionsDelegate>): express.RequestHandler;

// usage:
import Cors;
var requestHandler = cors(options);

(But this currently doesn’t work!)

2 Likes

@haxiomic ! :slight_smile: damn am I happy you replied. Even happier that my question wasn’t completely stupid. I actually looked at the adaptation of Express and saw that @:selfCall, but didn’t know how to implement it. And yes, I noticed the stuff with .call() and I also had a stab at doing that but it didn’t work :slight_smile:

One question: how do I define the external JS Cors class? In your proposal of Cors.hx you refer to extern class Cors. I suppose I should wrap your JS code in a Cors JavaScript class (and modify cors in node_modules)? I’m going to experiment a bit (& look in the 3 different Express.hx and the Express TS), but in case you’re still around, I’m leaving this here :).

Currently hacked this into its weird existence: :smiley:

export default class Corsifier
{
    corsWrapper(options)
    {
        let cors = require('cors');
        return requestHandler = cors(options); 
    }
}

Just defining Cors.hx as this

@:jsRequire("cors") extern class Cors {
	@:selfCall
	static function call(?options:ts.AnyOf2<cors.CorsOptions, cors.CorsOptionsDelegate>):express.RequestHandler<express_serve_static_core.ParamsDictionary, Dynamic, Dynamic, qs.ParsedQs>;
}

Is all you need to do – later in your haxe code if you write

app.use(Cors.call(options))

It will work because the cors module is imported via @:jsRequire, I just tested with

class Main {

	static function main() {
		Cors.call();
	}

}

And I got this js output

// Generated by Haxe 4.1.3
(function ($global) { "use strict";
var Cors = require("cors");
var Main = function() { };
Main.main = function() {
	Cors();
};
Main.main();
})({});

Which is correct

I’ve found the issue in dts2hx, so I’ll probably have a new version up before tomorrow :slight_smile:

Interesting solution :). You answered exactly as I was celebrating that my hack worked :smiley: . I don’t exactly understand how your solution works just because the Cors module is imported in that way. Because in JS there is no literal named Cors from what I can see. How is the mapping done?

Anyway, in the meantime I also got my hack to work :smiley: . Although maybe it works for reasons that are not exactly what I think. Here’s my code:

Cors.hx

package cors;

import cors.CorsOptions;
import cors.CorsOptionsDelegate;
import express.RequestHandler;
import express_serve_static_core.ParamsDictionary;

@:jsRequire("cors") @:jsRequire("express") extern class Corsifier
{
	@:selfCall
	static function call(?options: ts.AnyOf2<CorsOptions, CorsOptionsDelegate>): RequestHandler<ParamsDictionary, Dynamic, Dynamic, qs.ParsedQs>;
}

Corsifier.js created in node_modules/cors

export default class Corsifier
{
    corsWrapper(options)
    {
        let cors = require('cors');
        return cors(options); 
    }
}

What’s the second issue? :slight_smile:

Is your goal to make dts2hx be able to generate the cors extern? If not, then perhaps I can publish this extern somewhere until a permanent solution is found.

This should just work with dts2hx, so it’s currently broken in that regard

The class I posted works because of @:jsRequire and @:selfCall.

  • @:jsRequire maps references to the Cors class to require('cors'). So if you did in haxe Cors.someField, it compiles into the equivalent of require('cors').someField
  • @:selfCall maps a class field to a function call on the class – so if you have @:selfCall on a static function in Cors called someFunction, then in haxe Cors.someFunction() is rewritten into Cors(). Putting the two together you get Cors.call() translated to require('cors')(). In practise a haxe generates a reference for us so we’re not calling require('cors') all the time, so it actually generates:
var Cors = require("cors");
Cors();
1 Like

This has been fixed in dts2hx 14.0 :slight_smile: – Cors.hx is now generated as it should be

Let me know if you have any troubles after updating @Kyliathy

1 Like

Beautiful work @haxiomic :smiley:

I’m taking a short break now after some intense days. I’ll stay with my own-crafted Cors.hx. I’m happy to have done that with your help so I’ll keep it on of sentimental reasons :smiley:

Also, in the meantime, I had to deal with writing an extern for a small JS library. So yes, it unfortunately had no d.ts files. I tried generating them using some tools I found online but they couldn’t do much, and then dts2hx could do even less. So I started getting the hang of writing externs and happy for this. Your comments helped me a lot to understand how Haxe interacts with JS code.

1 Like

I am guessing you are already on your way with handwriting externs…

In the past, I have written a little bit about that: https://github.com/MatthijsKamstra/hxexterns/blob/master/howto.md

Not sure if it’s helpful (it already 17 months old)

2 Likes

Very useful! Thank you! :slight_smile: