02. Package Development

Creating package

package.json

{
  /* Name of the project */
  "name": "react-component-boilerplate",

  /* Brief description */
  "description": "Boilerplate for React.js components",

  /* Who is the author + optional email + optional site */
  "author": "Juho Vepsäläinen <email goes here> (site goes here)",

  /* Version of the package */
  "version": "0.0.0",

  /* Do not allow publishing, useful for apps or private packages */
  "private": true,
  
  "license": "MIT",

  /* Keywords related to package. */
  /* Fill this well to make the package discoverable. */
  "keywords": ["react", "reactjs", "boilerplate"],
  
  /* Links to homepage, repository, and issue tracker */
  "homepage": "https://<organization/user>.github.io/<project>/",
  "repository": {
    "type": "git",
    "url": "https://github.com/<organization/user>/<project>.git"
  },
  "bugs": {
    "url": "https://github.com/<organization/user>/<project>/issues"
  }

  /* Files to include to npm distribution. */
  /* Relative patterns like "./src" fail! */
  "files": ["lib/", "esm/", "bin/"]
  
  // ------------------------------------------------------------
  /* `npm run <name>` - `npm run` to get the available commands */
  "scripts": {
    /* You don’t need to write node_modules/.bin/catalog, npm will */
    /* automatically call locally-installed package. */
    "start": "catalog start docs",

    /* Namespacing (namespace:task) is a convention used for */
    /* grouping. */
    "test": "jest",
    "test:coverage": "jest --coverage",
    "test:watch": "jest --watch",
    "test:lint": "eslint . --ignore-path .gitignore",

    "gh-pages": "catalog build docs",
    "gh-pages:deploy": "gh-pages -d docs/build",

    "build": "npm run build:esm && npm run build:cjs",
    "build:esm": "babel --delete-dir-on-start -d esm/ src/",
    "build:cjs": "babel --delete-dir-on-start --env-name cjs -d lib/ src/",

    "preversion": "npm run test",
    "prepublishOnly": "npm run build",
    "postpublish": "npm run gh-pages && npm run gh-pages:deploy",

    /* If your library is installed through Git, compile it */
    "postinstall": "node lib/post_install.js"
  }
  
  // ------------------------------------------------------------
  /* Entry point for command line interface. */
  /* Don't set this unless you intend to allow command line usage */
  "bin": "bin/index.js",

  /* Main entry point (defaults to index.js) */
  "main": "lib/",

  /* ESM-based entry point for bundlers. */
  "module": "esm/"
  
  // ------------------------------------------------------------
  /* Dependencies required to use the package. */
  "dependencies": {
    /* ... */
  },

  /* Dependencies needed to develop/compile the package. */
  "devDependencies": {
    /* ... */
  },

  /* Package peer dependencies. The consumer chooses exact versions. */
  "peerDependencies": {
    "lodash": ">= 3.5.0 < 4.0.0",
    "react": ">= 0.14.0 < 17.0.0"
  }
}

Project Structure

Folders:

  • src: (uncompiled)

  • lib: compiled-node

  • dist: compiled-browser

Deployment of JS code

You can only deliver source code for a single platform via an npm package. Property engines of package.json lets you specify exactly what platform that is:

{ "engines" : { "node" : ">=0.10.3 <0.12" } }
{ "engines" : { "npm" : "~1.0.20" } }

However, that doesn’t help you with the following use cases, where you need source code for multiple platforms per package:

  • Browsers: deliver both a native version (e.g. in ES5 via CJS) and a “bleeding edge” version (e.g. latest ECMAScript version via an ES module), to be transpiled by Babel.

  • Node.js: deliver the same module for several versions of Node.js.

  • Browsers: allow new libraries to age gracefully – transpile only as long your target platforms don’t support the features, yet. We want the same convenience that babel-preset-env affords us.

There are two dimensions at play here:

  • On one hand, there is a distinction between code that is to be transpiled and “native” code.

  • On the other hand, native code may have to run on platforms with different capabilities.

Three properties in package.json currently let you deliver multiple versions of the same code:

  • main: points to a CommonJS module (or UMD module) with JavaScript as modern as Node.js can currently handle.

  • browser: swaps out some of the main code so that it works in browsers.

  • module: the same code as main, but in ES modules.

  • es2015: untranspiled ES6 code. Introduced by Angular.

  • unpkg: entry point if your code is loaded from unpkg.

If you publish your lib on npm it will be available over unpkg. It's a CDN so people can pull your scripts into their scripts without installing them over npm https://unpkg.com/package@version/file. Most of the time, it is a good practice to define the minified version of your lib there.

Then Babel introduced preset-env, which lets you adapt the code you generate to the platforms it will run on. Now, the best approach would be:

  • App code: transpiled via Babel and preset-env:

    • Input: JavaScript with stage 4 features or older, in ES modules.

    • Output: whatever JavaScript runs best on the target platforms.

  • Dependencies: must be delivered as both...

    • Transpiled code that runs natively on Node.js (meaning CommonJS modules, at the moment).

    • Untranspiled code that is transpiled along with your own code.

The benefits of this new approach are:

  • ES modules enable tree-shaking.

  • The output is upgraded automatically as the target platforms evolve (thanks to preset-env).

Developing a JavaScript package

Books

Articles

Publish npm module

Prepare for publishing

https://github.com/typicode/pkg-ok

  1. Prepare source code for release (compile).

  2. Add information about entry point:

  3. "main"

  4. "browser"

  5. "module"

  6. "unpkg"

  7. Add description of included files on publish.

  8. "files"

  9. .gitignore

  10. .npmignore

  11. Check version, probably bump it.

  12. Publish package, probably with a release tag.

Scoped packages

Scoped packages are private by default. However, public scoped modules are free and don’t require a paid subscription. To publish a public scoped module, set the access option when publishing it. This option will remain set for all subsequent publishes.

JS Dev Tools

Editors

Package size

Code coverage

Documentation

Last updated