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

Was this helpful?