@ashnazg/mochadocs
v1.1.0
Published
### 1.1 * Scanning a path for test JS's (including the default path of "test/") now reads mjs and cjs in, not just js.
Downloads
4
Readme
Purpose
I really wanted something like sphinx or doxygen but with the least possible syntax and the most DRY I could think of.
Principles:
- Readable code is the best documentation.
- Readable tests is the 2nd best documentation.
- While real design docs shouldn't be crammed into this format, the unit tests file be a significant (hyperlinked) participant reduces how much pure prose has to be written.
Releases
1.1
- Scanning a path for test JS's (including the default path of "test/") now reads mjs and cjs in, not just js.
Reads Mocha test files and emits a README.md
Default behavior
reads from ./test/
and writes ./README.md
The file you're reading was created by running:
const mochadocs = require('@ashnazg/mochadocs');
await mochadocs();
... and there are asserts on this page because you're reading the unit tests.
what's emitted
- Lines that begin with
///
are emitted as markdown. describe(...)
is parsed to make h2'sit(...)
is parsed to make h3's- Lines of code or vanilla comments are also emitted, but wrapped in a pre-block.
Supports Transclusion
A line that is exactly ///{{filename}}
will try to cat that filename in.
big Nota Bene: This is relative to the PWD of the process, NOT relative to the file the transclusion is present in.
The Release section on this page was transcluded from another file by saying:
///{{CHANGELOG.md}}
Options
Defaults
if no params given, defaults to:
await mochadocs({path: 'test'});
if you don't need any other options, you can pass it just the path string:
await mochadocs('test');
the full config format is:
await mochadocs({
path: 'test',
to: 'README.md',
libname: '@ashnazg/mochadocs',
glossary: { // for auto-linking frequently used terms
acronym1: 'path1',
},
indent: '\t\t'
});
Support for space-indented files
Visible code blocks are defined as non-///
lines that start with conf.indent
and are between the opening line of the current it(...)
and the it-closer, which is the first line of code with less indentation thatn conf.indent.
so for a 2-space file like:
describe('group', function() {
it('thing', function() {
visible_code();
});
function invisibleHelper() {
invisible_code();
};
});
You'd use:
await mochadocs({
indent: ' ' // four spaces
});
When the indent drops below the conf.indent, code blocks are not emitted til the next it(
-- this is so the invisibleHelper's contents don't pollute the unit test's
code printout.
Explicit output
you can write the output to a specific place:
await mochadocs({to: 'different.md'});
{
const lines = (await fs.promises.readFile('different.md', 'utf8')).split('\n');
assert(lines[length_of_preamble] === '## Reads Mocha test files and emits a README.md');
}
or pass null as the destination and it'll return the markdown file as a list of lines:
{
const lines = await mochadocs({to: null});
assert(lines[length_of_preamble] === '## Reads Mocha test files and emits a README.md');
}
Explicit input, file
path can be a filename:
const lines = await mochadocs({path: 'test/basics.js', to: null});
assert(lines[length_of_preamble] === '## Reads Mocha test files and emits a README.md');
Explicit input, dir
... or a directory, in which it'll read all *.js
, including subfolders, and process them after sorting them by 'path/filename'.
const lines = await mochadocs({path: 'test', to: null});
assert(lines[length_of_preamble] === '## Reads Mocha test files and emits a README.md');
Replaces require('..')
since in a library's unit tests, require('..')
is useful, but not helpful documentation,
This is converted to require('LIBNAME')
.
const lines = await mochadocs({path: 'test', to: null}); // reading the output of section "default"
const hits = lines.filter(line => line === "const mochadocs = require('@ashnazg/mochadocs');");
assert(hits.length === 1);
Configuring require('..') replacement
By default, mochadocs assumes that ./package.json
is available and will use that to know your lib's name.
if that's not a useful guess (wrong path or wrong name) you can pass in a library name of your choosing as a third param:
const lines = await mochadocs({path: 'test', to: null, libname: 'custom_lib_name'});
const hits = lines.filter(line => line === "const mochadocs = require('custom_lib_name');");
assert(hits.length === 1);
Automatic Glossary
To allow good linkage to other relevant topics without repetitive writing, you can pass in a glossary map and if that key is found in the non-code blocks, it'll be emitted as a link:
lines = await mochadocs({
to: null,
glossary: {
GLOSSARY: 'https://en.wikipedia.org/wiki/Glossary'
}
});
Glossary Rules
Only links exact words
Given the above config, the word "GLOSSARY" is now a link.
// search term without creating a false positive:
{
const key = `[GLOS` + `SARY](https://en.wikipedia.org/wiki/Glossary)`;
const hits = lines.filter(line => line.includes(key));
assert(hits.length === 1, `${hits.length} hits`);
}
... but GLOSSARYFOO is not, because the replacer respects word boundaries.
{
const key = `GLOS` + `SARYFOO`;
const hits = lines.filter(line => line.includes(key));
assert(hits.length === 1, `${hits.length} hits`);
}
Longer glossary terms take precedence
await mochadocs({
glossary: {
GLOSSARY: 'https://en.wikipedia.org/wiki/Glossary',
SHORT_NAME: '#glossary-rules',
SHORT_NAME_IN_LONGER_NAME: '#longer-glossary-terms-take-precedence'
}
});
SHORT_NAME_IN_LONGER_NAME should be a clean link, and its internals should not be affected by the SHORT_NAME term, as replacement is not allowed to go into recursive expansion.
const lines = (await fs.promises.readFile('README.md', 'utf8')).split('\n');
const key = `[SHORT_NAM` + `E_IN_LONGER_NAME](#longer-glossary-terms-take-precedence) should be a clean link, and its internals should not be affected by the [SHORT_NAME](#glossary-rules) term`;
const hits = lines.filter(line => line.includes(key));
assert(hits.length === 1, `${hits.length} hits`);
Existing links are unaffected
this predefined link SHORT_NAME is not further processed.
const lines = (await fs.promises.readFile('README.md', 'utf8')).split('\n');
// here's the unwanted form:
const key = `[[SHORT` + `_NAME](test1)](#existing-links-are-unaffected)`;
const hits = lines.filter(line => line.includes(key));
assert(hits.length === 0, `${hits.length} hits`);
Release 1.0.2
fixed a bug where a really short input file leaves the code pre unclosed
const lines = await mochadocs({path: 'samples/trailing-pre-missing.js', to: null});
assert(lines[lines.length-1] === '```', 'simple did not close pre');
Release 1.0.1
- Suppressed codeblocks that aren't inside an
it(...)
Release 1.0
- Wrote the thing