Publishing ES6 code on NPM


I've covered a couple of client libraries in VBA library for Ephemeral exchange and Apps Script library for Ephemeral exchange, and this article will talk a little about the Node library, but more specifically, a general discussion on how to publish JavaScript code written in ES6, when most browsers don't yet support it.

Where to get it

You can get it on NPM, https://www.npmjs.com/package/effex-api-client, and install it like this
npm install effex-api-client --save

The code is on github, and where you'll also find detailed documentation on the API, which I won't bother repeating in this post.

NPM


If you develop on Node, which is becoming pretty much standard  nowadays, you'll typically install all the dependencies you need using npm. There are almost 500,000 packages hosted availabled from NPM, and there are about 2m downloads every week. If you then go ahead and create an app intended to run on the browser, you'll probably use something like webpack, which bundles up all the modules you are using in Node, transpiling where required, and creates a single JavaScript file which can be understood by most browsers. The Ephemeral exchange console is built that way and although it is written using ES6 syntax and has many dependencies such as React , axios, D3, Recharts and of course  effex-api-client, none of these are referenced while it is running in a browser. This is because webpack builds a file with all of this stuff built in.

If you are planning to publish something on NPM, then you have to publish the transpiled version of your code - not the original source code. Let's take a look at how to do that. 

Build versus source

Your source code is translated using a transpiler (in my case Babel), which downgrades the syntax you are writing in to a JavaScript that can be understood universally, so you start with a directory structure that reflects that as below. 


The files under src, are the ones I work on, and the files under dist are the ones that get built and are published to npm. node_modules contain all the node dependencies I'm using - and there are many - to  provide the functionality I need, but mainly to do the code transpilation.  Here's a snapshot of just a few.


Package.json

All this is defined with a file called package.json, where you set up a list of things you'll need. There are two kinds of dependencies
  • devDependencies - needed while you are deleveloping things - such as transpilers and test runners
  • dependencies - needed to run your package. If you are simply creating a webpack file, to run in a browser, then you have no dependencies and webpack bundles the whole thing into the file it needs. If you are planning to allow your code to be run in node, then it will contain the things you need in normal production mode. 
The dependencies of my my package.json looks like this. I need axios to run in production, and the other stuff is only needed for testing and transpilation. The list of node_modules is of course a lot bigger than this - because of these themselves have dependencies which they pulled in automatically.
  "dependencies": {
    "axios": "^0.15.3"
  },

  "devDependencies": {
    "babel-cli": "^6.24.0",
    "babel-plugin-transform-object-rest-spread": "^6.23.0",
    "babel-preset-es2015": "^6.24.0",
    "chai": "^3.5.0",
    "mocha": "^3.2.0"
  },

To install you can either update the package.json manually the just run npm install, or you can use npm
...for transpilation
npm install babel-cli babel-plugin-transform-object-rest-spread babel-preset-es2015 --save-dev

...for testing
npm install chai mocha --save-dev

...for production
npm install axios --save

Transpiling

One of these days, this won't be necessary, but for now we have to downgrade the JavaScript as written. This is the business of running your source code through the stuff we've just installed, and populating the dist directory, which we'll publish. Package.json also controls this, under the script key. 
  "scripts": {
    "build": "babel src --out-dir dist",
    "prepublish": "npm run build",
    "test": "mocha"
  },

when you enter
npm run <key>

<key> is used to reference this script property, and it will run whatever is there -
npm run build will execute babel to transpile everything in the src directory to the dist directory
npm run test will run mocha to run my test suite (we'll get to later)
and prepublish is automatically run when you use npm publish, to make sure that what you  publish is the latest build.

You also need to tell babel what to do while transpiling. There are a few ways to do this, but I prefer to also put this in the package.json. This is saying that standard es2015 syntax should be converted to regular JavaScript, along with an experimental feature (spread operator).
  "babel": {
    "plugins": [
      "babel-plugin-transform-object-rest-spread"
    ],
    "presets": [
      "babel-preset-es2015"
    ],
    "ignore": []
  }

.npmignore

When you do publish, you probably don't want everything to go up to npm. You only want the directory with your built code in it - namely dist, along with your package.json, README etc. You can create a .npmignore file to instruct npm to ignore certain things. My .npmignore simply looks like this
src
test

Versions and main entry point

You also have to give npm a version number and how to initiate the module. Here's my completed package.json including that stuff.
{
  "name": "effex-api-client",
  "version": "1.0.3",
  "description": "JavaScript client for Ephemeral Exchange",
  "main": "./dist/index.js",
  "scripts": {
    "build": "babel src --out-dir dist",
    "prepublish": "npm run build",
    "test": "mocha"
  },
  "author": "Bruce Mcpherson <bruce@mcpher.com> (http://ramblings.mcpher.com)",
  "license": "MIT",
  "keywords": [
    "api",
    "ephemeral exchange",
    "cache",
    "effex"
  ],
  "dependencies": {
    "axios": "^0.15.3"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/brucemcpherson/effex-api-client"
  },
  "devDependencies": {
    "babel-cli": "^6.24.0",
    "babel-plugin-transform-object-rest-spread": "^6.23.0",
    "babel-preset-es2015": "^6.24.0",
    "chai": "^3.5.0",
    "mocha": "^3.2.0"
  },
  "babel": {
    "plugins": [
      "babel-plugin-transform-object-rest-spread"
    ],
    "presets": [
      "babel-preset-es2015"
    ],
    "ignore": []
  }
}

Publishing

Now we're ready to publish!

npm publish 

and if all goes well








For more like this, see Google Apps Scripts snippets. Why not join our forum, follow the blog or follow me on twitter to ensure you get updates when they are available.
Comments