Modules with '.' delimiters in the filename

Remix uses filenames with ‘.’ to denote client or server components. (e.g. auth.server.res, entry.client.res)

Is there any way to use these modules in rescript?

Currently using raw like this

let authenticator: RemixAuth.Authenticator.t = %raw(`require("~/auth.server.js").authenticator`) 
1 Like

You can create a Server module and a Client module, and then create the Auth moudle inside of it. Then, you can use Server.Auth and Client.Auth

My problem is I cant access modules with ‘.’ in the name.

Auth.Server.authenticator doesn’t work

Not sure how to do this other that using require directly like you did, but to make it more safe you can consider a custom binding instead of %raw:

// auth.res
let x = 1

// auth_.server.res
include Auth

// page.res
module type Auth = module type of Auth
@val external requireAuth: (@as("path/to/auth_.server.js") _, unit) => module(Auth) = "require"
module Auth = unpack(requireAuth())

Js.log(Auth.x)

Also, I looked at whether this can be configured on Remix side, but unfortunately the special treatment of *.server.js is hardcoded there.

You may also consider opening an issue at Remix, to make this behaviour more customisable.

Why should you use . to create a module name?
Just like this:

// Auth__Server.res
let authenticator = //... extern the js library
// Auth__Client.res
let authenticator = // ... extern the js library
// Auth.res
module Server = Auth__Server
module Client = Auth__Client
// Main.res call authenticator in Server and Client
Auth.Server.authenticator()
Auth.Client.authenticator()

The goal is to make the generated JavaScript to look like require(".../something.server.js") or import ... from ".../something.server.js". This lets Remix know that the code shouldn’t be included in the frontend bundle.

BTW, this should not be necessary most of the time, as far as I understand. Only when tree-shaking gets confused and you need to force it to not include the code.

You can create those files in rescript but you can’t reference it.

To work around this, create a file Auth.res and in it a module Server. Then create your file Auth.Server.res that include the module Server from Auth.

This way you can both have the file correctly named for remix and you can reference this module from Rescript.

3 Likes

Interesting, I thought this solution would work, however when I tried including Auth.Server module in auth.server.res

//auth.server.res

include Auth.Server

the compiler errors with

The module or file Auth can't be found.

The module or file Auth can’t be found.

This tends to happen when you try to use module X from a file named X.Something.res.

But in any case, what @tsnobip suggests will create a .server.js file with the right content, but you’ll still need to import this file in the generated JavaScript, which is the tricky part.

oh yeah that’s right, I guess you should call it something different than Auth, maybe just create a file AuthServer.res and auth.server.res will import AuthServer.

If your rescript code has to import this file in the generated JS, then I guess you have to go with @rpominov’s solution which is safer than a pure %raw. But really, JS frameworks should really stop creating semantics out of nowhere. This leads to such poor flexibility and hidden magic.

2 Likes

This is what I did.

The include statement is causing the error

100%, this is very annoying.

2 Likes