Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

documentary

Package Overview
Dependencies
Maintainers
1
Versions
118
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

documentary

A library to manage documentation, such as README, usage, man pages and changelog.

  • 1.11.0
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
118
decreased by-45.62%
Maintainers
1
Weekly downloads
 
Created
Source

documentary

npm version

documentary is a command-line tool and a library to manage documentation of Node.js packages. Due to the fact that complex README files are harder to maintain, documentary serves as a pre-processor of documentation.

yarn add -DE documentary

Table Of Contents

Installation & Usage

The doc client is available after installation. It can be used in a doc script of package.json, as follows:

{
  "scripts": {
    "doc": "doc documentary -o README.md"
  }
}

The first argument, documentary is a path to a directory containing source documentation files, or a path to a single file to be processed, e.g., README-source.md.

Therefore, to produce an output README.md, the following command will be used:

yarn doc

When actively working on documentation, it is possible to use the watch mode with -w flag, or -p flag to also automatically push changes to a remote git repository, merging them into a single commit every time.

Features

The processed README.md file will have a generated table of contents, markdown tables and neat titles for API method descriptions, as well as other possible features described in this section.

TOC Generation

Table of contents are useful for navigation the README document. When a %TOC% placeholder is found in the file, it will be replaced with an extracted structure. Titles appearing in comments and code blocks will be skipped.

- [Table Of Contents](#table-of-contents)
- [CLI](#cli)
  * [`-j`, `--jsdoc`: Add JSDoc](#-j---jsdoc-add-jsdoc)
- [API](#api)
- [Copyright](#copyright)
TOC Titles

To be able to include a link to a specific position in the text (i.e., create an "anchor"), documentary supports a TOC Titles feature. Any text written as [Toc Title](t) will generate a relevant position in the table of contents. It will automatically detect the appropriate level and be contained inside the current section.

This feature can be useful when presenting some data in a table in a section, but wanting to include a link to each row in the table of contents so that the structure is immediately visible.

Specific Level: if required, the level can be specified with a number of # symbols, such as [Specific Level](######).

Tables Display

To describe method arguments in a table, but prepare them in a more readable format, documentary will parse the code blocks with table language as a table. The blocks must be in JSON format and contain a single array of arrays which represent rows.

```table
[
  ["arg", "description"],
  ["-f", "Display only free domains"],
  ["-z", "A list of zones to check"],
]
```

Result:

argdescription
-fDisplay only free domains
-zA list of zones to check

Method Title

It is possible to generate neat titles useful for API documentation with documentary. The method signature should be specified as a JSON array, where every member is an argument specified as an array. The first item in the argument array is the argument name, and the second one is type. Type can be either a string, or an object. If it is an object, each value in the object will be an array and first contain the property type, secondly - the default value. To mark a property as optional, the ? symbol can be used at the end. The third item is the short name for the table of contents (so that a complex object can be referenced to its type).

async runSoftware(
  path: string,
  config: {
    View: Container,
    actions: object,
    static?: boolean = true,
    render?: function,
  },
): string

Generated from

```#### async runSoftware => string
[
  ["path", "string"],
  ["config", {
    "View": ["Container"],
    "actions": ["object"],
    "static?": ["boolean", true],
    "render?": ["function"]
  }, "Config"]
]
```
async runSoftware(
  path: string,
): void

Generated from

```#### async runSoftware
[
  ["path", "string"]
]
```
runSoftware(): string

Generated from

```#### runSoftware => string
```

Comments Stripping

Since comments found in <!-- comment --> sections are not visible to users, they will be removed from the output document.

File Splitting

documentary can read a directory and put files together into a single README file. The files will be sorted in alphabetical order, and their content merged into a single stream. The index.md and footer.md are special in this respect, so that the index.md of a directory will always go first, and the footer.md will go last.

Example structure used in this project:

documentary
├── 1-installation-and-usage
│   ├── 1-vs-code.md
│   └── index.md
├── 2-features
│   ├── 1-TOC-generation.md
│   ├── 2-table-display.md
│   ├── 3-method-title.md
│   ├── 4-comment-stripping.md
│   ├── 5-file-splitting.md
│   ├── 6-rules.md
│   ├── 7-examples.md
│   ├── 8-gif.md
│   ├── 9-type.md
│   ├── 91-typedef
│   │   ├── 1-js.md
│   │   ├── 2-readme.md
│   │   ├── 3-imports.md
│   │   ├── 4-schema.md
│   │   ├── 9-migration.md
│   │   └── index.md
│   └── index.md
├── 3-cli.md
├── 4-api
│   ├── 1-toc.md
│   └── index.md
├── footer.md
└── index.md

Replacement Rules

There are some built-in rules for replacements.

RuleDescription
%NPM: package-name%Adds an NPM badge, e.g., [![npm version] (https://badge.fury.io/js/documentary.svg)] (https://npmjs.org/package/documentary)
%TREE directory ...args%Executes the tree command with the given arguments. If tree is not installed, warns and does not replace the match.
%FORK(-lang)? module ...args%Forks the Node.js process to execute the module using child_process.fork. The output is printed in the code block, with optionally given language. For example: %FORK-json example.js -o%

Examples Placement

documentary can be used to embed examples into the documentation. The example file needs to be specified with the following marker:

%EXAMPLE: example/example.js [, ../src => documentary] [, javascript]%

The first argument is the path to the example relative to the working directory of where the command was executed (normally, the project folder). The second optional argument is the replacement for the import statements. The third optional argument is the markdown language to embed the example in and will be determined from the example extension if not specified.

Given the documentation section:

## API Method

This method allows to generate documentation.

%EXAMPLE: example/example.js, ../src => documentary, javascript%

And the example file examples/example.js

import documentary from '../src'
import Catchment from 'catchment'

(async () => {
  await documentary()
})()

The program will produce the following output:

## API Method

This method allows to generate documentation.

```javascript
import documentary from 'documentary'
import Catchment from 'catchment'

(async () => {
  await documentary()
})()
```
Partial Examples

Whenever only a part of an example needs to be shown (but the full code is still needed to be able to run it), documentary allows to use start and end comments to specify which part to print to the documentation. It will also make sure to adjust the indentation appropriately.

import documentary from '../src'
import Catchment from 'catchment'

(async () => {
  /* start example */
  await documentary()
  /* end example */
})()
await documentary()

Gif Detail

The GIF rule will inserts a gif animation inside of a <detail> block. To highlight the summary with background color, <code> should be used instead of back-ticks. TOC title link also work inside the summary.

%GIF doc/doc.gif
Alt: Generating documentation.
Click to View: [<code>yarn doc</code>](t)
%
Click to View: yarn doc
Alt: Generating documentation.

The actual html placed in the README looks like the one below:

<details>
  <summary>Summary of the detail: <code>yarn doc</code></summary>
  <table>
  <tr><td>
    <img alt="Alt: Generating documentation." src="doc/doc.gif" />
  </td></tr>
  </table>
</details>

Type Definition

Often, it is required to document a type of an object, which methods can use. To display the information about type's properties in a table, the TYPE macro can be used. It allows to show all possible properties that an object can contain, show which ones are required, give examples and link them in the table of contents (disabled by default).

Its signature is as follows:

%TYPE addToToc(true|false)
<p name="propertyName" type="propertyType" required>
  <d>Property Description.</d>
  <d>Property Example.</d>
</p>
%

For example,

%TYPE
<p name="text" type="string" required>
  <d>Display text. Required.</d>
  <e>

```js
const q = {
  text: 'What is your name',
}
```
  </e>
</p>
<p name="validation" type="(async) function">
  <d>A function which needs to throw an error if validation does not pass.</d>
  <e>

```js
const q = {
  text: 'What is your name',
  validate(v) {
    if (!v.length) throw new Error('Name is required.')
  },
}
```
  </e>
</p>
%

will display the following table:

PropertyTypeDescriptionExample
text*stringDisplay text. Required.
const q = {
  text: 'What is your name',
}
validation(async) functionA function which needs to throw an error if validation does not pass.
const q = {
  text: 'What is your name',
  validate(v) {
    if (!v.length) throw new Error('Name is required.')
  },
}

When required to use the markdown syntax in tables (such as __, links, etc), an extra space should be left after the d or e tags like so:

%TYPE true
<p name="skipLevelOne" type="boolean">
  <d>

Start the table of contents from level 2, i.e., excluding the `#` title.</d>
</p>
%

Otherwise, the content will not be processed by GitHub. However, it will add an extra margin to the content of the cell as it will be transformed into a paragraph.

Dedicated Example Row

Because examples occupy a lot of space which causes table squeezing on GitHub and scrolling on NPM, documentary allows to dedicate a special row to an example. It can be achieved by adding a row attribute to the e element, like so:

%TYPE
<p name="headers" type="object">
  <d>Incoming headers returned by the server.</d>
  <e row>

```json
{
  "server": "GitHub.com",
  "content-type": "application/json",
  "content-length": "2",
  "connection": "close",
  "status": "200 OK"
}
```
  </e>
</p>
%

In addition, any properties which do not contain examples will not have an example column at all.

PropertyTypeDescriptionExample
bodystring|object|BufferThe return from the server.
headersobjectIncoming headers returned by the server.
{
  "server": "GitHub.com",
  "content-type": "application/json",
  "content-length": "2",
  "connection": "close",
  "status": "200 OK"
}
statusCodenumberThe status code returned by the server.200

Finally, when no examples which are not rows are given, there will be no Example heading.

%TYPE
<p name="data" type="object">
  <d>Optional data to send to the server with the request.</d>
  <e row>

```js
{
  name: 'test',
}
```
  </e>
</p>
<p name="method" type="string">
  <d>What HTTP method to use to send data (only works when <code>data</code> is set).</d>
</p>
%
PropertyTypeDescription
dataobjectOptional data to send to the server with the request.
{
  name: 'test',
}
methodstringWhat HTTP method to use to send data (only works when data is set).

@typedef Organisation

For the purpose of easier maintenance of JSDoc @typedef declarations, documentary allows to keep them in a separate XML file, and then place compiled versions into both source code as well as documentation. By doing this, more flexibility is achieved as types are kept in one place but can be reused for various purposes across multiple files. It is different from TypeScript type declarations as documentary will generate JSDoc comments rather than type definitions which means that a project does not have to be written in TypeScript.

Types are kept in a separate xml file, for example:

<types>
  <import name="ServerResponse" from="http" />
  <type name="SetHeaders"
    type="(res: ServerResponse) => any"
    desc="Function to set custom headers on response." />
  <type name="StaticConfig" desc="Options to setup `koa-static`.">
    <prop string name="root">
      Root directory string.
    </prop>
    <prop number name="maxage" default="0">
      Browser cache max-age in milliseconds.
    </prop>
    <prop boolean name="hidden" default="false">
      Allow transfer of hidden files.
    </prop>
    <prop string name="index" default="index.html">
      Default file name.
    </prop>
    <prop opt type="SetHeaders" name="setHeaders">
      Function to set custom headers on response.
    </prop>
  </type>
</types>

They are then included in both JavaScript source code and markdown documentation.

JS Placement

To include a compiled declaration into a source code, the following line should be placed in the .js file (where the types/static.xml file exists in the project directory from which the doc command will be run):

/* documentary types/static.xml */

For example, an unprocessed JavaScript file can look like this:

/* src/config-static.js */
import Static from 'koa-static'

/**
 * Configure the middleware.
 */
function configure(config) {
  const middleware = Static(config)
  return middleware
}

/* documentary types/static.xml */

export default configure

Please note that the types marker must be placed before export default is done (or just export) as there's currently a bug in VS Code.

The file is then processed with doc src/config-static.js -g command and updated in place, unless - is given as an argument, which will print the output to stdout, or the path to the output file is specified. After the processing is done, the source code will be transformed to include all types specified in the XML file. This routine can be repeated whenever types are updated (unless the spread attribute was set, when the generated JSDoc of a function has to be removed by hand first).

/* src/config-static.js */
import Static from 'koa-static'

/**
 * Configure the middleware.
 */
function configure(config) {
  const middleware = Static(config)
  return middleware
}

/* documentary types/static.xml */
/**
 * @typedef {import('http').ServerResponse} ServerResponse
 *
 * @typedef {(res: ServerResponse) => any} SetHeaders Function to set custom headers on response.
 *
 * @typedef {Object} StaticConfig Options to setup `koa-static`.
 * @prop {string} root Root directory string.
 * @prop {number} [maxage=0] Browser cache max-age in milliseconds. Default `0`.
 * @prop {boolean} [hidden=false] Allow transfer of hidden files. Default `false`.
 * @prop {string} [index="index.html"] Default file name. Default `index.html`.
 * @prop {SetHeaders} [setHeaders] Function to set custom headers on response.
 */

export default configure
Expanded @param

In addition, JSDoc for any method that has an included type as one of its parameters will be updated to its expanded form so that a preview of options is available.

Therefore, a raw function JSDoc of a function written as

/**
 * Configure the middleware.
 * @param {StaticConfig} config Options to setup `koa-static`.
 */
function configure(config) {
  const middleware = Static(config)
  return middleware
}

will be expanded to include the properties of the type:

/**
 * Configure the middleware.
 * @param {StaticConfig} config Options to setup `koa-static`.
 * @param {string} config.root Root directory string.
 * @param {number} [config.maxage=0] Browser cache max-age in milliseconds. Default `0`.
 * @param {boolean} [config.hidden=false] Allow transfer of hidden files. Default `false`.
 * @param {string} [config.index="index.html"] Default file name. Default `index.html`.
 * @param {SetHeaders} [config.setHeaders] Function to set custom headers on response.
 */
function configure(config) {
  const middleware = Static(config)
  return middleware
}

This makes it possible to see the properties of the argument to the configure function fully:

preview of the configure function

And the description of each property will be available when passing an argument to the function:

preview of a property description

Compare that to the preview without JSDoc expansion:

preview of the configure function without expanded params

To prevent the expansion, the noExpand attribute should be added to the type.

Spread @param

Moreover, when the type of the type is just object, it also can be spread into a notation which contains its properties for even better visibility. To do that, the spread attribute must be added to the type definition in the xml file.

Again, a raw function with JSDoc:

/**
 * Configure the middleware.
 * @param {StaticConfig} config Options to setup `koa-static`.
 */
function configure(config) {
  const middleware = Static(config)
  return middleware
}

Can be re-written as spread notation of a type.

/**
 * Configure the middleware.
 * @param {{ root: string, maxage?: number, hidden?: boolean, index?: string, setHeaders?: SetHeaders }} config Options to setup `koa-static`.
 * @param {string} config.root Root directory string.
 * @param {number} [config.maxage=0] Browser cache max-age in milliseconds. Default `0`.
 * @param {boolean} [config.hidden=false] Allow transfer of hidden files. Default `false`.
 * @param {string} [config.index="index.html"] Default file name. Default `index.html`.
 * @param {SetHeaders} [config.setHeaders] Function to set custom headers on response.
 */
function configure(config) {
  const middleware = Static(config)
  return middleware
}

The properties will be visible in the preview:

preview of the configure function with stread params

However, this method has one disadvantage as there will be no descriptions of the properties when trying to use them in a call to function:

spread will not have a description

Therefore, it must be considered what is the best for developers -- to see descriptions of properties when passing a configuration object to a function, but not see all possible properties, or to see the full list of properties, but have no visibility of what they mean.

README placement

To place a type definition as a table into a README file, the TYPEDEF snippet can be used, where the first argument is the path to the xml file containing definitions, and the second one is the name of the type to embed. Moreover, links to the type descriptions will be created in the table of contents using the TOC Titles, but to prevent this, the noToc attribute should be set for a type.

%TYPEDEF path/definitions.xml TypeName%

For example, using previously defined StaticConfig type from types/static.xml file, documentary will process the following markers:

%TYPEDEF types/static.xml ServerResponse%
%TYPEDEF types/static.xml SetHeaders%
%TYPEDEF types/static.xml StaticConfig%

or a single marker to include all types in order in which they appear in the xml file (doing this also allows to reference other types from properties):

%TYPEDEF types/static.xml%

and embed resulting type definitions:

import('http').ServerResponse ServerResponse

(res: ServerResponse) => any SetHeaders: Function to set custom headers on response.

StaticConfig: Options to setup koa-static.

NameTypeDescriptionDefault
root*stringRoot directory string.-
maxagenumberBrowser cache max-age in milliseconds.0
hiddenbooleanAllow transfer of hidden files.false
indexstringDefault file name.index.html
setHeadersSetHeadersFunction to set custom headers on response.-
Importing Types

A special import element can be used to import a type using VS Code's TypeScript engine. An import is just a typedef which looks like /** @typedef {import('package').Type} Type */. This makes it easier to reference the external type later in the file. However, it is not supported in older versions of VS Code.

Original SourceTypes Definition
async function example() {
  process.stdout.write('example\n')
}

/* documentary types/import.xml */

export default example
<types>
  <import name="IncomingMessage" from="http" />
  <import name="ServerResponse" from="http" />
  <import name="StorageEngine" from="koa-multer" />
  <import name="File" from="koa-multer" />
  <type type="(f: File) => void" name="Function"
    desc="A function to save a file." />
</types>
Output
async function example() {
  process.stdout.write('example\n')
}

/* documentary types/import.xml */
/**
 * @typedef {import('http').IncomingMessage} IncomingMessage
 * @typedef {import('http').ServerResponse} ServerResponse
 * @typedef {import('koa-multer').StorageEngine} StorageEngine
 * @typedef {import('koa-multer').File} File
 *
 * @typedef {(f: File) => void} Function A function to save a file.
 */

export default example
XML Schema

The XML file should have the following nodes and attributes:

NodeDescriptionAttributes

types

A single root element.

import

An imported type definition.
  • name: Name of the imported type.
  • from: The module from which the type is imported.

type

A @typedef definition.

  • name: A name of the type.
  • desc: A Description of the type.
  • type: A type of the type, if different from Object.
  • noToc: Do not include link to the type in the table of contents.
  • spread: Spread the type to the { prop: Type, prop2: Type2 } notation when used as a @param.
  • noExpand: Do not expand the type when writing as a @param in JSDoc.

prop

Property of a @typedef definition.

  • name: Name of the property.
  • string: Whether the property is string.
  • number: Whether the property is number.
  • boolean: Whether the property is boolean.
  • opt: Whether the property is optional.
  • default: Default value of the property. When given, the property becomes optional.
Migration

A JavaScript file can be scanned for the presence of @typedef JSDoc comments, which are then extracted to a types.xml file. This can be done with the doc src/index.js -e types/index.xml command. This is primarily a tool to migrate older software to using types.xml files which can be used both for online documentation and editor documentation.

For example, types can be extracted from a JavaScript file which contains JSDoc in form of comments:

async function test() {
  process.stdout.write('ttt')
}

/**
 * @typedef {import('http').IncomingMessage} IncomingMessage
 */

/**
 * @typedef {(m: IncomingMessage)} Test This is test function.
 *
 * @typedef {Object} SessionConfig Description of Session Config.
 * @prop {string} key cookie key.
 * @prop {number|'session'} [maxAge=86400000] maxAge in ms. `session` will result in a cookie that expires when session/browser is closed.
 * @prop {boolean} [overwrite] Can overwrite or not. Default `true`.
 * @prop {boolean} [httpOnly] httpOnly or not or not. Default `true`.
 * @prop {boolean} [renew] Renew session when session is nearly expired, so we can always keep user logged in. Default `false`.
 */


export default test

When a description ends with Default `value`, the default value of a type can also be parsed from there.

<types>
  <import name="IncomingMessage" from="http" />
  <type name="Test" type="(m: IncomingMessage)" desc="This is test function." />
  <type name="SessionConfig" desc="Description of Session Config.">
    <prop string name="key">
      cookie key.
    </prop>
    <prop type="number|'session'" name="maxAge" default="86400000">
      maxAge in ms. `session` will result in a cookie that expires when session/browser is closed.
    </prop>
    <prop boolean name="overwrite" default="true">
      Can overwrite or not.
    </prop>
    <prop boolean name="httpOnly" default="true">
      httpOnly or not or not.
    </prop>
    <prop boolean name="renew" default="false">
      Renew session when session is nearly expired, so we can always keep user logged in.
    </prop>
  </type>
</types>

CLI

The program is used from the CLI (or package.json script).

doc README-source.md [-o README.md] [-tgewp]

The arguments it accepts are:

FlagMeaningDescription
-o pathOutput LocationWhere to save the processed README file. If not specified, the output is written to the stdout.
-tOnly TOCOnly extract and print the table of contents.
-g [path]Generate TypesInsert @typedef JSDoc into JavaScript files. When no path is given, the files are updated in place, and when - is passed, the output is printed to stdout.
-e [path]Extract TypesInsert @typedef JSDoc into JavaScript files. When no path is given, the files are updated in place, and when - is passed, the output is printed to stdout.
-wWatch ModeWatch mode: re-run the program when changes to the source file are detected.
-p "commit message"Automatic PushWatch + push: automatically push changes to a remote git branch by squashing them into a single commit.

When NODE_DEBUG=doc is set, the program will print debug information, e.g.,

DOC 80734: stripping comment
DOC 80734: could not parse the table

API

The programmatic use of the documentary is intended for developers who want to use this software in their projects.

Toc Stream

Toc is a transform stream which can generate a table of contents for incoming markdown data. For every title that the transform sees, it will push the appropriate level of the table of contents.

TocConfig Type

When creating a new Toc instance, it will accept the following configuration object.

PropertyTypeDescriptionExample
skipLevelOnebooleanStart the table of contents from level 2, i.e., excluding the # title.For example, the following code:
# Hello World

## Table Of Contents

## Introduction

will be compiled to

- [Table Of Contents](#table-of-contents)
- [Introduction](#introduction)

when skipLevelOne is not set (by default), and to

- [Hello World](#hello-world)
  * [Table Of Contents](#table-of-contents)
  * [Introduction](#introduction)

when skipLevelOne is set to false.

constructor(
  config?: {
    skipLevelOne?: boolean = true,
  },
): Toc

Create a new instance of a Toc stream.

/* yarn example/toc.js */
import { Toc } from 'documentary'
import Catchment from 'catchment'
import { createReadStream } from 'fs'

(async () => {
  try {
    const md = createReadStream('example/markdown.md')
    const rs = new Toc()
    md.pipe(rs)

    const { promise } = new Catchment({ rs })
    const res = await promise
    console.log(res)
  } catch ({ stack }) {
    console.log(stack)
  }
})()
- [Table Of Contents](#table-of-contents)
- [CLI](#cli)
  * [`-j`, `--jsdoc`: Add JSDoc](#-j---jsdoc-add-jsdoc)
- [API](#api)
- [Copyright](#copyright)

Glossary

TODO

  • Test using zoroaster's masks.
  • Gather all types across the whole documentation first to be able to independently link them even if they are in separate files.
  • Replace the source in example with a require call in addition to import statement.
  • Implement caching.
  • Trigger compilation whenever an embedded example changes.

(c) Art Deco 2018

Keywords

FAQs

Package last updated on 01 Sep 2018

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc