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
npm
modulePrepare for publishing
https://github.com/typicode/pkg-ok
Prepare source code for release (compile).
Add information about entry point:
"main"
"browser"
"module"
"unpkg"
Add description of included files on publish.
"files"
.gitignore
.npmignore
Check
version
, probably bump it.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?