How to Ship man pages with your Node Programs
Tags: npm, javascript, documentation, dx
Published: 2021-08-14 12:07:38

---
title: How to Ship man pages with your Node Programs
published: true
description: Make sure to cover all the places your user might look for help, by shipping your JS CLI with man pages.
tags: npm, javascript, documentation, dx
canonical_url: https://www.hughrawlinson.me/posts/2021/07/09/how-to-ship-man-pages-with-your-node-programs
---

_This post was first published on [my website](https://www.hughrawlinson.me/posts/2021/07/09/how-to-ship-man-pages-with-your-node-programs)._

The Javascript ecosystem uses the npm package registry to distribute libraries - and also binaries. If you've ever done `npm install -g yarn`, you've used this feature. The yarn package exposes a binary (a node js script) that executes on your system as a program, often invoked in the command line. Your npm installation then installs that package in a central global directory, and links each binary exposed by the package to a central directory that's in your shell. Then you can invoke the binary on your command line.

```
hugh@hugh-XPS-13-9343 ~> yarn --version
1.22.10
```

Pretty neat right?

In unix systems, the [man utility] is a common way to look up how to use a given command.

```
hugh@hugh-XPS-13-9343 ~> man yarn
No manual entry for yarn
```

But not all packages provide man pages. This isn't terrible - in the case of yarn, there is a whole 'help' subcommand to look up information about how to use yarn's cli - plus [a documentation website][yarn cli documentation]. But, if like me, you think the developer experience is improved by meeting your developer where _they_ expect you to be, you might like to distribute at least a nearly empty man page pointing devs in the right direction. If there's some chance of some portion of your users reaching for `man {your binary}`, I would suggest that it's worth weighing the effort of providing at least _a_ man page with the benefit that those users would get from having docs (or a pointer to docs) where they expect.

Isn't that effort huge? Aren't man pages in a weird format for fancy native developers? How would I even install them from an npm package? In this post I'll attempt to convince you that making a basic man page is not a huge lift, and might be worth the your work for the developer experience benefit.

Let's start off with a creating a basic empty package for demonstration purposes, and installing some dependencies that will help us create our man pages.

I've created [an example repo] for you to refer to if you like.

```
$ mkdir my-package && cd my-package
$ npm init --yes
$ npm install --save marked<1 marked-man
```

Now we find ourselves with an empty package with two dependencies - marked, and marked-man. marked is a peer dependency of marked-man, the package that will take our markdown document and convert it to roff, the format used by man-pages. If you'd like to cut down dependencies and write in roff directly, go ahead! But I figure most javascript developers will be more familiar with Markdown.

Right now, there's a [bug in marked-man] causing it not to support versions of it's peerDependency marked greater than 1.0.0. At the moment, I suggest installing a version of marked below 1, and keeping an eye on the bug.

Next, let's write a simple document. Store the following in `README.md`.

```
# my-package(1) -- demo package

## Synopsis

my-package is a demonstration package that does nothing

## See also

example.com
```

There's a git more going on here than simple markdown syntax. Let's walk through it. On the first line, we have a heading containing our package name, followed immediately by a number in brackets. This number refers to the man 'section number' for your page. In our case, we're using section number 1 to show that our documentation concerns "Executable programs or shell commands" - but you can specify library calls, special files, games, etc. Check out the table in the `man man` document. The package name(section number) is followed by a ` -- ` spacer and a short description of your command.

Later on in the document, we can see sections headed by h2s. These are man "section names". You can have custom sections, but conventional section names include NAME (which is automatically generated for you), SYNOPSIS, CONFIGURATION, DESCRIPTION, FILES, NOTES, BUGS, AUTHORS, SEE ALSO, and more that are shown in the `man man` document.

Time to use marked-man to generate the man file. Man files are stored in the `roff` format, and we can use marked-man as follows to generate our roff file from our README.md.

```
$ ./node_modules/.bin/marked-man README.md
```

You'll see the following output:

```
.TH "MY\-PACKAGE" "1" "June 2021" "" ""
.SH "NAME"
\fBmy-package\fR \- demo package
.SH Synopsis
.P
my\-package is a demonstration package that does nothing
.SH See Also
.P
example\.com

```

I'm certainly glad I don't have to manually write in that format! Let's store this in a directory, and add an npm script so that we don't have to type out the full command every time. Add the following entry to your 'scripts' object in your package.json.

```
    "generate-man-page": "mkdir -p man && marked-man README.md > ./man/my-package.1"
```

Note that the roff output is stored in the `man` directory in a file called `my-package.1`. For your man file, you should follow the same naming convention: `{packageName}.{section number}`.

How do we make the man utility aware of the document when we install the package? We add an entry pointing at the file in our package.json as follows:

```
  "man": ["./man/my-package.1"]
```

We can test this out by running "npm install --global .", which will install the package in the current directory onto the system globally. Then, run `man my-package` to see the man page in action.

```
MY-PACKAGE(1)                                                                        MY-PACKAGE(1)

NAME
       my-package - demo package

Synopsis
       my-package is a demonstration package that does nothing

See Also
       example.com

                                          June 2021                                  MY-PACKAGE(1)

```

There we have it! Man pages for your node packages. If you'd like to have multiple man pages for your package (say your command is configurable by a dotfile that you'd like to document, for example), you can write multiple markdown documents, modify your npm script to generate them all, and add them to the list of exports on your package.json `man` object.

You may notice in the npm documentation that there's a `directories.man` configuration in the package.json spec, which is documented as exporting _all_ the man pages to the system. I wasn't able to get that to work. If you are, please [let me know]!

Thanks for reading. I hope that I've covered the procedure for generating man pages from markdown documents such that it's clear to you - and I hope you agree that it's a relatively low amount of effort to add a touch of delight to your package's developer experience. If you have any feedback, please get in touch [on twitter] or [on mastodon]. I'd love to hear from you!

[man utility]: https://en.wikipedia.org/wiki/Man_page
[yarn cli documentation]: https://yarnpkg.com/en/docs/cli
[bug in marked-man]: https://github.com/kapouer/marked-man/issues/28
[on mastodon]: https://mastodon.technology/@hughrawlinson
[on twitter]: https://twitter.com/hughrawlinson/
[let me know]: https://twitter.com/hughrawlinson/
[an example repo]: https://github.com/hughrawlinson/npm-man-page-example