DEV Community

Alain
Alain

Posted on • Updated 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

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

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

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)

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"
      }
    }
  },

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

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"
      }
    }
  },

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

$ esy pesy
$ esy build

Let's edit the Utils.re too

let foo = () => print_endline("Hello from foo");

Let call this from IdkjsFooApp.re.

let () = Foolib.Util.foo();

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;

Build and publish!

$ esy build
$ npm publish

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

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"
      }
    }
  },

And then edit Utils.re

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

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

let foo: unit => unit;

Call foo from BarApp.re:

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

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.

Oldest comments (0)