DEV Community

Alain
Alain

Posted on • Edited on

Creating, Publishing to NPM, and Using Reason Libraries and Tools

TLDR

  1. The original instructions are here.
  2. The foo repo is here.
  3. The bar repo is here.

Creating Our First Library

First thing to do is install pesy.

npm install -g pesy
Enter fullscreen mode Exit fullscreen mode

Easiest way to get started with distributing you library is to publish the source to NPM. Let's take a look at an example.

In the original instructions, the project name foo is used. We change foo in the examples below to some other project name as foo is already in use on npm and you will not be able to run npm publish on that name.

For this demonstration, I used idkjs-foo.
Consider a base package foo that you created and distributed on NPM. And let's assume, bar is the package that consumes foo.

$ mkdir idkjs-foo
$ cd idkjs-foo
$ pesy
# edit code
$ esy pesy # if build config has changes
$ esy build
$ npm publish
Enter fullscreen mode Exit fullscreen mode

Publishing to npm is just a matter of running npm publish. Lets take closer look at the commands.

$ mkdir idkjs-foo
$ cd idkjs-foo
$ pesy
Enter fullscreen mode Exit fullscreen mode

As you'd know by now, these commands, bootstrap, install dependencies and build the entire project.

At this point, our project looks like this

│
├── library
│
├── executable
│
├── test
│
└── testExe (test runner)

Enter fullscreen mode Exit fullscreen mode

With the buildDirs section of the package.json looking like the following

  "buildDirs": {
    "test": {
      "require": [
        "idkjs-foo/library",
        "rely.lib"
      ],
      "flags": [
        "-linkall",
        "-g"
      ]
    },
    "testExe": {
      "require": [
        "idkjs-foo/test"
      ],
      "bin": {
        "RunIdkjsFooTests.exe": "RunIdkjsFooTests.re"
      }
    },
    "library": {},
    "executable": {
      "require": [
        "idkjs-foo/library"
      ],
      "bin": {
        "IdkjsFooApp.exe": "IdkjsFooApp.re"
      }
    }
  },
Enter fullscreen mode Exit fullscreen mode

Given that pesy tries to unify packages and libraries, for a config mentioned above it has made library available under the namespace Library. So anytime foo is added as a dependency, a module Library becomes available in the codebase.

Since Library is to generic to be useful, let's rename it to FooLib (i.e. make rename the package as foolib)

$ mv library foolib
Enter fullscreen mode Exit fullscreen mode

And update the config

  "buildDirs": {
    "test": {
      "require": [
-        "idkjs-foo/library"
+        "idkjs-foo/foolib"
        "rely.lib"
      ],
      "flags": [
        "-linkall",
        "-g"
      ]
    },
    "testExe": {
      "require": [
        "idkjs-foo/test"
      ],
      "bin": {
        "RunFooTests.exe": "RunFooTests.re"
      }
    },
-    "library": {},
+    "foolib": {},
    "executable": {
      "require": [
-        "idkjs-foo/library"
+        "idkjs-foo/foolib"
      ],
      "bin": {
        "IdkjsFooApp.exe": "IdkjsFooApp.re"
      }
    }
  },
Enter fullscreen mode Exit fullscreen mode

Since config has changed, we run esy pesy and esy build to build the project.

$ esy pesy
$ esy build
Enter fullscreen mode Exit fullscreen mode

Let's edit the Utils.re too

let foo = () => print_endline("Hello from foo");
Enter fullscreen mode Exit fullscreen mode

Let call this from IdkjsFooApp.re.

let () = Foolib.Util.foo();
Enter fullscreen mode Exit fullscreen mode

Run esy build and you should get an error because Foolib.Util.foo() is not available in Utils.rei which controls which functions from Utils.re are exposed from the Utils.re module. It's worth noting here how silly stupid awesome the errors you get are when using reason-native. If you are used to using reason with bucklescript you already know that the compilers error messages take a minute to get used to but are amazing. They just aren't formated so clearly. Here, right in the middle of the noise is the clearly formatted, color coded error with the type signatures that don't match. You can almost copy/paste the signature of your function into the the .rei. Here, the error tells us that the Foolib.Util.foo function is not available to IdkjsFooApp.re. Let's do that.

Alt Text

Expose foo in Utils.rei by adding the following to it:

let foo: unit => unit;
Enter fullscreen mode Exit fullscreen mode

Build and publish!

$ esy build
$ npm publish
Enter fullscreen mode Exit fullscreen mode

That's it. We just created and published our first library to NPM. Next we will use that library in a completely different project called, wait for it, wait for it, Bar!

Consuming Our Foo Library from NPM

Let quickly create a new project, bar and add idkjs-foo.

$ mkdir bar
$ cd bar
$ pesy
$ esy add idkjs-foo
Enter fullscreen mode Exit fullscreen mode

We can now require idkjs-foo (sort of like we did in Javascript)

  "buildDirs": {
    "test": {
      "require": [
        "bar/library",
        "rely.lib"
      ],
      "flags": [
        "-linkall",
        "-g"
      ]
    },
    "testExe": {
      "require": [
        "bar/test"
      ],
      "bin": {
        "RunBarTests.exe": "RunBarTests.re"
      }
    },
    "library": {
      "require": [
+        "idkjs-foo/foolib"
      ]
    },
    "executable": {
      "require": [
        "bar/library"
      ],
      "bin": {
        "BarApp.exe": "BarApp.re"
      }
    }
  },
Enter fullscreen mode Exit fullscreen mode

And then edit Utils.re

let foo = () => {
  Foolib.Util.foo();
  print_endline("This is from bar");
};
Enter fullscreen mode Exit fullscreen mode

And then edit Utils.rei to expose the foo function like we did above.

let foo: unit => unit;
Enter fullscreen mode Exit fullscreen mode

Call foo from BarApp.re:

Console.log("Running Foo function");
let () = Library.Util.foo();
Enter fullscreen mode Exit fullscreen mode
$ esy pesy
$ esy build
$ esy x BarApp.exe
Running Test Program:
Hello, World!
Running Foo function
Hello from foo
This is from bar
Enter fullscreen mode Exit fullscreen mode

Publishing and consuming Reason native packages from NPM is easy. We just need to keep in mind that the namespace exposed from our library depends on the name of the folder!

Thanks to @jordwalke who's readem.md this is basically a direct copy of except where its updated and with a few additional comments.

Thanks to @_anmonteiro, Et7f3, GriefSeeds, lessp, @ulrikstrid,@cem2ran and @jordwalke for the impromptu master class they held in the reasonml #native-development channel for no damned reason at all except maybe to share the knowledge. I hope this post is proper reciprocation of their generosity. The master class is a good read. Its starts here on the #native-development channel.

Top comments (0)