Where are `Node._module` and `Node.require`?


#1

This page describes: Node._module and Node.require. Both seem to be gone.

Use case: I’d like to determine if the currently running script is “main”:

let isMain = () =>
  switch (Node.require) {
  | Some(require) => require#main == Node.module_
  | None => Js.Exn.raiseError("Illegal state")
  };

#2

On mobile here, can’t test this, but I think you should be able to write your own bs.node like the link you sent but write your own type annotation. Seems to me that “require” can be typed as a normal JS object with a field “main”
Something like type require = {. “main”: string }.


#3

I’d prefer to use something built-in, but that’s probably what I’ll do if there isn’t anything, yes. Maybe even a raw JS function that returns a boolean.


#4

Maybe I’m confused, but the page you link to make no mention of Node._module or Node.require. It mentions the types Node.node_module and Node.node_require, which according to the API docs are still present, and the use of _module and require with the %bs.node extension point.

So to accomplish what you want, it seems you should do:

let isMain = () =>
  switch ([%node require], [%node _module]) {
  | (Some(require), Some(_module)) => require#main == _module
  | None => Js.Exn.raiseError("Illegal state")
  };

#5

Ah, interesting! So the following bindings are something people have to do themselves; they are not part of a BuckleScript module.

let dirname: option(string) = [%bs.node __dirname];
let filename: option(string) = [%bs.node __filename];
let _module: option(Node.node_module) = [%bs.node _module];
let require: option(Node.node_require) = [%bs.node require];

I only now realize that I misinterpreted the sentence “bs.node exposes support for these”: I thought bs.node was the module Node, not a directive.

Thanks for the code!


#6

Well they’re not really bindings I’d say, just very limited examples of how you’d use %bs.node (which I guess you could call the binidng part). It’s a weirdly specific feature that I think stems from the inability to use ordinary bindings for local variables, since they might be inlined and therefore taken out of the intended scope. I’d like to see a more general solution for that instead.

Also funny how you seem to have assumed these examples were just code copied directly from the source. I think it says something more about the overall quality of the manual, not just this page, when people expect that :joy:

It would be great if you could make an example out of the code you’re working on, once you get it to work, so there’s a real usage example in there.


#7

This is how I finally got it to work:

/**
 * Is the current file being run from the shell?
 * @param _module Call with [%bs.node _module]
 * @see https://bucklescript.github.io/docs/en/nodejs-special-variables.html
 */
let isMain = (_module: option(Node.node_module)) => {
  let require: option(Node.node_require) = [%bs.node require];
  
  switch (require, _module) {
  | (Some(require'), Some(_module')) =>
    switch (Js.undefinedToOption(require'##main)) {
    | Some(main') =>
      main' === _module'
    | None => Js.Exn.raiseError("Illegal state")
    };
  | _ => Js.Exn.raiseError("Illegal state")
  };
};

#8

Submitted here: https://github.com/BuckleScript/bucklescript/issues/2545


#9

Ouch. I’d probably just do if ([%raw "require.main === module"]) { ... } myself :wink:

I think you can cut it down a bit by doing this instead though:

  switch (require, _module) {
  | (Some(require'), Some(_module')) =>
    require'##main === Js.Undefined.return(_module')
  | _ => Js.Exn.raiseError("Illegal state")
  };

Or even

  switch (require) {
  | Some(require') =>
    require'##main === Js.Undefined.fromOption(_module)
  | _ => Js.Exn.raiseError("Illegal state")
  };

Unless it’s important to fail if not all assumptions hold. I’d also probably just return false instead of raising an error if require is undefined, since it still seems to hold that it is not the main module in that case.

And using Js.Option.andThen, this could be my final answer:

let isMain = (_module: option(Node.node_module)) =>
  [%node require] |> Js.Option.andThen([@bs] r => Js.undefinedToOption(r##main)) === _module;

(I’m not saying this last one is a good example, btw, just proving to myself that it can be a one-liner :D)


#10

I had forgotten about the tool functions in Js.Option etc.!

New version:

/**
 * Is the current file being run from the shell?
 * JavaScript: require.main === module
 * @param _module Call with [%bs.node _module]
 * @see https://bucklescript.github.io/docs/en/nodejs-special-variables.html
 */
let isMain = (_module: option(Node.node_module)) => {
  open Js;
  let requireMain' = [%bs.node require] |> Option.getExn
    |> r => r##main |> Undefined.getExn;
  let _module' = _module |> Option.getExn;
  /* Must compare references in this case */
  requireMain' === _module';
};

Your raw version has a lot going for it, too.


#11

It’s ok to use raw sometimes, no pressure :wink: