Cursed JS is Cursed. Need Help Rescript-ifying this Code

Here is the JS code:

const { pipe, abort } = renderToPipeableStream(
      <RemixServer context={remixContext} url={request.url} />,
      {
        "onAllReady": () => {
          const body = new PassThrough()

          pipe(body)
        },
      }
    )

pipe() is destructured as a return value, but it is also used in the parameters. Is this possible to write in rescript?

For something like this you always have %raw() as an escape hatch for “just js things”, if you need to use it a lot I’d maybe abstract it away

let outer = renderToPipeableStream(
  None, 
  {
    "onAllReady": () => {
      let body = PassThrough.make()
      %raw(`outer`)->pipe(body)
    }
  }
)

Rescript Playground

1 Like

Yea, I ended up using raw, I was more curious how someone would attempt this without raw

Actually, on further inspection this doesn’t work.

Only destructured values can be used within the function parameters in JS.

Using outer.pipe here will error with outer is undefined

I’m not sure, but since the return of renderToPipeableStream returns a stream, which is an eventEmitter…
Maybe this also emits an event allReady?
If it does, you could just manually reigster an eventHandler to the stream listening for that specific event, after you constructed the stream.

Edit:
Ok, so I looked at react-server and it seems to me, they don’t emit any events. They just pass down the event handler (given in the options object) and call it: https://github.com/facebook/react/blob/8e2bde6f2751aa6335f3cef488c05c3ea08e074a/packages/react-server/src/ReactFizzServer.js#L1655

I tried to find a way in the playground. The bindings are porbably not correct at all, but I just wanted to see if it was possible in general: it seems you would need to use rescript’s ref to have a mutable value:
https://rescript-lang.org/try?version=v10.1.2&code=C4TwDgpgBA9mwGcoF4oG8YDsCCAbXAYgJaZEIAWEAJgFxQCupwKAfA0wL4BQXokUCYACcIAQwC2XAAKYIAdyhTxMKvVwQAFACJBIiVoCUXCAA9gEIZlG4o40QGsIABVEIEAFXJCY9AObk6RiJmZDZdMXEUKC0XN09vP3ItaQQITCpjMwsrGzAiSDoNcIkAGgFhCINWdmCorTzIZOllVXVtPQBjYABaVKEANwtDTPNLaygRdIt3GCd8sQAjdQBlCok6OERq4sjULUmqadn50SWIVb1xJvVmHaiRADMNADksCCM7mmQuKAm0w6EMzmkFOKzW4g0aB+v1gOHwxFIFGohSqoWhMPKlwAdB0sOZMIh0TDuiwAEIQXDALEAeXgRCwWLsYCK4O24JJDU0dkcsQ8Xh8-g0BmFRN+JPJlJpdIZvggwAA6sFyAARCAPURqYBCkrQjgGEnLGDiCBcIA

type opts = {onAllFinished: unit => unit}

type stream
@new @module("stream")
external makePassThrough: unit => stream = "PassThrough"
@send
external pipe: (stream, stream) => unit = "pipe"

@module("react-server")
external renderToPipeableStream: opts => stream = "renderToPipeableStream"

let stream = ref(None)
stream :=
  renderToPipeableStream({
    onAllFinished: () =>
      stream.contents
      ->Belt.Option.map(stream => stream->pipe(makePassThrough()))
      ->Belt.Option.getWithDefault(),
  })->Some

The compiled js isn’t really beautiful but looks like what you are trying to do?

// Generated by ReScript, PLEASE EDIT WITH CARE

import * as Stream from "stream";
import * as Belt_Option from "./stdlib/belt_Option.js";
import * as Caml_option from "./stdlib/caml_option.js";
import * as ReactServer from "react-server";

var stream = {
  contents: undefined
};

stream.contents = Caml_option.some(ReactServer.renderToPipeableStream({
          onAllFinished: (function (param) {
              Belt_Option.getWithDefault(Belt_Option.map(stream.contents, (function (stream) {
                          stream.pipe(new Stream.PassThrough());
                        })), undefined);
            })
        }));

export {
  stream ,
}
/*  Not a pure module */

1 Like