Permalink
Browse files

add changes 👪

  • Loading branch information...
azat-co committed Jul 7, 2018
1 parent 462f78b commit f53055241455f55e11886f80ee51add1debaf6bc
Showing with 41 additions and 39 deletions.
  1. +41 −39 chapter12/chapter12.md
@@ -2,19 +2,19 @@ Chapter 12
----------
# Modularizing Your Code and Publishing Node.js Modules to npm

One of the key factors that attributed to the rapid growth of the Node.js module ecosystem is its open-source nature and robust packaging systems (with registry). As of April 2013, JavaScript and Node.js had already surpassed any other language/platform in number of packages contributed per year ([source](http://caines.ca/blog/2013/04/13/the-node-dot-js-community-is-quietly-changing-the-face-of-open-source/)):
Two of the key factors that attributed to the rapid growth of the Node.js module ecosystem are its open-source nature and robust packaging systems (with registry). As of mid 2014, JavaScript and Node.js had surpassed any other language/platform in number of packages contributed per year ([source](http://caines.ca/blog/2013/04/13/the-node-dot-js-community-is-quietly-changing-the-face-of-open-source/)):

- Python: 1351 packages per year (29,720 packages in 22 years)
- Ruby: 3022 packages per year (54,385 packages in 18 years)
- Node.js: *6742 packages per year* (26,966 packages in 4 years)
- *Node.js*: *6742 packages per year* (26,966 packages in 4 years)
- *Python*: 1351 packages per year (29,720 packages in 22 years)
- *Ruby*: 3022 packages per year (54,385 packages in 18 years)

This year’s (2018) numbers are even higher with npm having over 620,000 packages. That's more than half a million! As you can see from the chart take from <http://modulecounts.com>, Node's npm surpassed other platforms' package repositories in absolute numbers. Maven Central (Java) and Packagist (PHP) try to catch up but fail miserably. npm and Node are the top dogs.
Recent numbers are even higher with npm having over 620,000 packages. That's more than half a million! As you can see from the chart taken from <http://modulecounts.com> (Figure 12-1), Node's npm surpassed other platforms' package repositories in absolute numbers. Maven Central (Java) and Packagist (PHP) try to catch up but fail miserably. npm and Node are the top dogs.

![Node's npm is dominating by the number of modules since mid 2014](media/modulecounts.png)
***Figure 12-1.** Node's npm is dominating by the number of modules since mid 2014*
***Figure 12-1.** Node's npm is dominating by the number of modules since mid 2014.*
Other factors that attribute to the Node.js popularity include:
Other factors that contribute to the Node.js's popularity include:
- Ability to share code between front-end/browser and server-side (with projects such as [browserify](http://browserify.org/) and [ender.js](https://github.com/ender-js/Ender))
- Philosophy of small (in terms of lines of code and functionality) functional modules vs. large, standard/core packages (i.e., granularity)
@@ -31,7 +31,7 @@ With this in mind, many Node.js enthusiasts find it rewarding to contribute to t
Recommended Folder Structure
============================
Here is an example of a good, structured npm module in which you have documentation, project manifest, starting file and a folder for dependencies:
Here is an example of a good, structured npm module in which you have documentation, project manifest, starting file, and a folder for dependencies:
```
webapp
@@ -42,7 +42,7 @@ webapp
README.md
```
The `index.js` file does the initialization whereas `lib/webapp.js` has all the principal logic.
The `index.js` file does the initialization, whereas `lib/webapp.js` has all the principal logic.
If you’re building a command-line tool, add the `bin` folder:
@@ -69,24 +69,24 @@ Also, for the CLI module, add the following to `package.json`:
The `webapp-cli.js` file starts with the line `#!/usr/bin/env node`, but then has normal Node.js code.
It’s a good idea to add unit tests to your external module, which increases confidence and the likelihood of other people using it. Some programmers go as far as not using a module that doesn’t have any tests! The added benefit is that tests serve as a poor man’s examples and
It’s a good idea to add unit tests to your external module, because it increases confidence and the likelihood of other people using it. Some programmers go as far as not using a module that doesn’t have any tests! The added benefit is that tests serve as a poor man’s examples and
documentation.
TravisCI, which we covered in previous chapters, allows free testing for open-source projects. Its badges, which turn from red to green, depending on the status of tests (failing or passing, became the de facto standard of quality and are often seen on the README pages of the most popular Node.js projects.
TravisCI, which we covered in previous chapters, allows free testing for open-source projects. Its badges, which turn from red to green, depending on the status of tests (failing or passing), became the de facto standard of quality and are often seen on the README pages of the most popular Node.js projects.
Modularizing Patterns
=================
Modularizing is the best practice because you can keep your application flexible and update different part independently of each other. It's totally fine to have bunch of module with only a single function in each one of them. In fact, a lot of module on npm are just that.
Modularizing is the best practice because you can keep your application flexible and update different parts independently of each other. It's totally fine to have bunch of modules with only a single function in each one of them. In fact, a lot of module on npm are just that—a single function.
There are a few common patterns for writing external (meant for use by other users, not just within your app) modules:
There are a few common patterns for writing external modules (meant for use by other users, not just within your app):
- `module.exports` as a function pattern (recommended)
- `module.exports` as a class pattern (not recommended)
- `module.exports` as an object pattern
- `exports.NAME` pattern; which could be an object or a function
- `exports.NAME` pattern, which could be an object or a function
Here is an example of the the `module.exports` as a function pattern:
Here is an example of the `module.exports` as a function pattern:
```js
let _privateAttribute = 'A'
@@ -100,7 +100,7 @@ module.exports = function (options) {
}
```
And here is an example of an equivalent with a function declaration but this time we used named function which we exported via the global `module.exports`:
And here is an example of an equivalent with a function declaration, but this time we used named function that we exported via the global `module.exports`:
```js
module.exports = webapp
@@ -111,7 +111,7 @@ function webapp (options) {
}
```
**Tip** For info about named function expressions vs. function declarations, visit the comprehensive resource [Named function expressions demystified](http://kangax.github.io/nfe/#named-expr).
**Tip** For info about named function expressions vs. function declarations, refer to Chapter 1.
The file in which we include the module looks like this:
@@ -150,7 +150,7 @@ const wa = new Webapp()
The example of this `module.exports` as a class pattern is the OAuth module ([source code](https://github.com/ciaranj/node-oauth/blob/master/lib/oauth.js#L9)).
The `module.exports` as an object pattern similar to the first pattern (functional), only without the constructor. It may be useful for defining constants, locales, and other settings:
The `module.exports` as an object pattern is similar to the first pattern (functional), only without the constructor. It may be useful for defining constants, locales, and other settings:
```js
module.exports = {
@@ -172,7 +172,7 @@ const http = require('http')
http.globalAgent.maxSockets = webapp.sockets
```
**Note** The `require` method can read JSON files directly. The main difference is that JSON standard has mandatory double quotes (`"`) for wrapping property names.
**Note** The `require` method can read JSON files directly. The main difference is that the JSON standard has the mandatory double quotes (`"`) for wrapping property names.
The `exports.NAME` pattern is just a shortcut for `module.exports.NAME` when there’s no need for one constructor method. For example, we can have multiple routes defined this way:
@@ -222,11 +222,11 @@ Another mandatory part of an npm module is its `package.json` file. The easiest
}
```
The most important fields are `name` and `version`. The others are optional and self-explanatory, by name. The full list of supported keys is located at [the npm web site](https://www.npmjs.org/doc/json.html).
The most important fields are `name` and `version`. The others are optional and self-explanatory, by name. The full list of supported keys is located at [the npm web site](https://www.npmjs.org/doc/json.html): <https://www.npmjs.org/doc/json.html>.
**Warning** `package.json` must have double quotes around values and property names, unlike native JavaScript object literals.
Important feature which will benefit all projects (and more so large projects) is npm scripts. See that `scripts` property in the `package.json` file? Inside of it developers can define any commands which act as aliases. The left part is the alias and the right part (after the `:` colon) is the actual command:
npm scripts is an important feature that benefits all projects and more so large one. See that `scripts` property in the `package.json` file? Inside of it developers can define any commands, which act as aliases. The left part is the alias, and the right part (after the `:` colon) is the actual command:
```json
"scripts": {
@@ -238,7 +238,7 @@ Important feature which will benefit all projects (and more so large projects) i
}
```
To run the command, you use `npm run NAME`, e.g., `npm run build` or `npm run deploy`. The two names are special. The don't need `run`. They are `test` and `start`. That is to execute test or start, simply use `npm test` and `npm start`.
To run the command, you use `npm run NAME`, e.g., `npm run build` or `npm run deploy`. The two names are special. The don't need `run`. They are `test` and `start`. That is to execute `test` or `start`, simply use `npm test` and `npm start`.
It's possible to call other npm scripts from the right side (the values):
@@ -250,7 +250,7 @@ It's possible to call other npm scripts from the right side (the values):
}
```
Lastly, there are post and pre hook for each npm script. They are defined as pre and post prefixes to the names. For example, if I always want to build after the installation, I can set up `postinstall`:
Lastly, there are `post` and `pre` hooks for each npm script. They are defined as pre and post prefixes to the names. For example, if I always want to build after the installation, I can set up `postinstall`:
```json
"scripts": {
@@ -259,48 +259,50 @@ Lastly, there are post and pre hook for each npm script. They are defined as pre
}
```
npm scripts are very powerful. Some Node developers are even abandoning their build tools such as Grunt or Gulp or Webpack and implement their build pipelines with npm scripts and some low-level Node code. I sort of agree with them. Having to learn and depend on myriads of Grunt, Gulp or Webpack plugins is no fun. For more use cases of npm scripts, start at this page: <https://docs.npmjs.com/misc/scripts>.
npm scripts are very powerful. Some Node developers are even abandoning their build tools, such as Grunt or Gulp or Webpack, and implementing their build pipelines with npm scripts and some low-level Node code. I sort of agree with them. Having to learn and depend on myriads of Grunt, Gulp, or Webpack plugins is no fun. For more use cases of npm scripts, start at this page: <https://docs.npmjs.com/misc/scripts>.
It’s worth noting that `package.json` and npm do not limit their use. In other words, you are encouraged to add custom fields and devise new conventions for their cases.
Publishing to npm
=================
To publish to npm, we must have an account there. So first you need to proceed to the website npmjs.org and register there. Once you do that, you will have an account which mean you will have a username and password. The next step is to sign in on the command line. We do this by executing the following:
To publish to npm, you must have an account there. So first, you need to proceed to the website npmjs.org and register there. Once you do that, you will have an account, and you will have a username and password. The next step is to sign in on the command line. Do this by executing the following:
```
$ npm adduser
```
You just need to sign in with the npm CLI once. After you do it you are read to publish as many time as you wish. To publish a new module or an update to an already published module, simply execute from the project folder:
You just need to sign in with the npm CLI once. After you do it, you are read to publish as many times as you wish. To publish a new module or an update to an already published module, simply execute the following command from the module/package project folder:
```
$ npm publish
```
Some useful npm commands are as follows:
- `$ npm tag NAME@VERSION TAG`: tag a version
- `$ npm version SEMVERSION`: increment a version to the value of `SEMVERSION` ([semver](http://semver.org/)) and update `package.json`
- `$ npm version patch`: increment the last number in a version (e.g., 0.0.1 to 0.0.2) and update `package.json`
- `$ npm version minor`: increment a middle version number (e.g., 0.0.1 to 0.1.0 or 0.0.1 to 1.0.0) and update `package.json`
- `$ npm unpublish PACKAGE_NAME`: unpublish package from npm (take optional version with `@`)
- `$ npm owner ls PACKAGE_NAME`: list owners of this package
- `$ npm owner add USER PACKAGE_NAME`: add an owner
- `$ npm owner rm USER PACKAGE_NAME`: remove an owner
- `$ npm tag NAME@VERSION TAG`: Tag a version
- `$ npm version SEMVERSION`: Increment a version to the value of `SEMVERSION` ([semver](http://semver.org/)) and update `package.json`
- `$ npm version patch`: Increment the last number in a version (e.g., 0.0.1 to 0.0.2) and update `package.json`
- `$ npm version minor`: Increment a middle version number (e.g., 0.0.1 to 0.1.0 or 0.0.1 to 1.0.0) and update `package.json`
- `$ npm unpublish PACKAGE_NAME`: Unpublish package from npm (take optional version with `@`)
- `$ npm owner ls PACKAGE_NAME`: List owners of this package
- `$ npm owner add USER PACKAGE_NAME`: Add an owner
- `$ npm owner rm USER PACKAGE_NAME`: Remove an owner
Not-Locking Versions
================
The rule of thumb is that when we publish external modules, we don’t lock dependencies’ versions. However, when we deploy apps, we lock versions in `package.json`. You can ready more on why and how lock version in projects which are applications (i.e., not npm modules) in chapter 10. Then why not lock versions in the modules?
The rule of thumb is that when we publish external modules, we don’t lock dependencies’ versions. However, when we deploy apps, we lock versions in `package.json`. You can read more on why and how lock versions in projects that are applications (i.e., not npm modules) in Chapter 10.
The answer is that open source is often a part-time gig and an after thought for most people. It's not like you'll make millions and can spend 40 hours per week on your FOSS npm module. Let's say there's a security vulnerability or a something is outdated in a dependency of your npm module. Most likely, you'll be patching your app which is your main full-time daily job and not patching this poor little npm module. That's why it's a good idea no NOT lock the version but let it use a caret symbol `^` which means the patches will be updated in dependencies.
Then why not lock versions in the modules? The answer is that open source is often a part-time gig and an afterthought for most people. It's not like you'll make millions and can spend 40 hours per week on your FOSS npm module.
Yes. If someone depends on your npm module, they might get a bug when it pulls a newer dependency but the trade off worth it. Your module will have latest dependencies automatically without requiring your attention (the next time someone installs your module).
Let's say there's a security vulnerability or something is outdated in a dependency of your npm module. Most likely, you'll be patching your app that is your main full-time daily job, and not patching this poor little npm module. That's why it's a good idea to NOT lock the version but let it use a caret symbol `^`, which means the patches will be updated in dependencies.
That's the main reason why almost all popular npm modules such as Express, Webpack, and React do have `^` in package.json ([source](https://github.com/expressjs/express/blob/master/package.json), [source](https://github.com/webpack/webpack/blob/master/package.json) and [source](https://github.com/facebook/react/blob/master/package.json)).
Yes. If someone depends on your npm module, they may get a bug when it pulls a newer dependency, but the tradeoff is worth it. Your module will have the latest dependencies automatically, without requiring your attention (the next time someone installs your module).
That's the main reason why almost all popular npm modules such as Express, Webpack, and React do have `^` in package.json (<https://github.com/expressjs/express/blob/master/package.json>, <https://github.com/webpack/webpack/blob/master/package.json> and <https://github.com/facebook/react/blob/master/package.json>).
Summary
=======
Open-source factors have contributed to the success and widespread use of the Node.js platform. It’s relatively easy to publish a module and make a name for yourself (unlike other mature platforms with solid cores). We looked at the recommended patterns and structures, and explored a few commands to get started with publishing modules to npm.
Open source factors have contributed to the success and widespread use of the Node.js platform. It’s relatively easy to publish a module and make a name for yourself (unlike other mature platforms with solid cores). We looked at the recommended patterns and structures, and explored a few commands to get started with publishing modules to npm.

0 comments on commit f530552

Please sign in to comment.