Skip to content

Commit

Permalink
Refactor code-style
Browse files Browse the repository at this point in the history
*   Add more docs to JSDoc
*   Add support for `null` in input of API types
  • Loading branch information
wooorm committed Jan 25, 2023
1 parent f93734b commit d4c5cac
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 47 deletions.
40 changes: 19 additions & 21 deletions lib/contents.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
/**
* @typedef {import('mdast').Root|import('mdast').Content} Node
* @typedef {import('mdast').List} List
* @typedef {import('mdast').ListItem} ListItem
* @typedef {import('mdast').PhrasingContent} PhrasingContent
* @typedef {import('mdast').StaticPhrasingContent} StaticPhrasingContent
* @typedef {import('./search.js').SearchEntry} SearchEntry
*
*/

/**
* @typedef ContentsOptions
* @property {boolean} [tight=false]
* Build configuration.
* @property {boolean | null | undefined} [tight=false]
* Whether to compile list-items tightly.
* @property {boolean} [ordered=false]
* @property {boolean | null | undefined} [ordered=false]
* Whether to compile list-items as an ordered list, otherwise they are
* unordered.
* @property {string|null} [prefix=null]
* @property {string | null | undefined} [prefix=null]
* Add a prefix to links to headings in the table of contents.
* Useful for example when later going from mdast to hast and sanitizing with
* `hast-util-sanitize`.
Expand Down Expand Up @@ -61,11 +63,12 @@ export function contents(map, settings) {
* Insert an entry into `parent`.
*
* @param {SearchEntry} entry
* @param {List|ListItem} parent
* @param {List | ListItem} parent
* @param {ContentsOptions} settings
*/
function insert(entry, parent, settings) {
let index = -1
const tail = parent.children[parent.children.length - 1]

if (parent.type === 'list') {
if (entry.depth === 1) {
Expand All @@ -87,7 +90,8 @@ function insert(entry, parent, settings) {
]
})
} else if (parent.children.length > 0) {
insert(entry, parent.children[parent.children.length - 1], settings)
const tail = parent.children[parent.children.length - 1]
insert(entry, tail, settings)
} else {
/** @type {ListItem} */
const item = {type: 'listItem', spread: false, children: []}
Expand All @@ -96,17 +100,9 @@ function insert(entry, parent, settings) {
}
}
// List item
else if (
parent.children[parent.children.length - 1] &&
parent.children[parent.children.length - 1].type === 'list'
) {
else if (tail && tail.type === 'list') {
entry.depth--
insert(
entry,
// @ts-expect-error It’s a `list`, we just checked.
parent.children[parent.children.length - 1],
settings
)
insert(entry, tail, settings)
} else {
/** @type {List} */
const item = {
Expand Down Expand Up @@ -154,16 +150,18 @@ function all(nodes) {

/**
* @param {PhrasingContent} node
* @returns {StaticPhrasingContent|Array<StaticPhrasingContent>}
* @returns {StaticPhrasingContent | Array<StaticPhrasingContent>}
*/
function one(node) {
if (node.type === 'footnoteReference') {
return []
}

if (
node.type === 'link' ||
node.type === 'linkReference' ||
node.type === 'footnote' ||
node.type === 'footnoteReference'
node.type === 'footnote'
) {
// @ts-expect-error Looks like a parent.
return all(node.children)
}

Expand Down
33 changes: 23 additions & 10 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
/**
* @typedef {import('mdast').Root|import('mdast').Content} Node
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Content} Content
* @typedef {import('mdast').List} List
* @typedef {import('./search.js').SearchOptions} SearchOptions
* @typedef {import('./contents.js').ContentsOptions} ContentsOptions
*/

/**
* @typedef {Root | Content} Node
* @typedef {SearchOptions & ContentsOptions & ExtraOptions} Options
*
* @typedef ExtraOptions
* @property {string} [heading]
* Extra configuration fields.
* @property {string | null | undefined} [heading]
* Heading to look for, wrapped in `new RegExp('^(' + value + ')$', 'i')`.
*
* @typedef Result
* @property {number|null} index
* @property {number|null} endIndex
* @property {List|null} map
* Results.
* @property {number | null} index
* Where the contents section starts, if looking for a heading.
* @property {number | null} endIndex
* Where the contents section ends, if looking for a heading.
* @property {List | null} map
* Built table of contents (`List`).
*/

import {search} from './search.js'
Expand All @@ -32,14 +42,17 @@ import {toExpression} from './to-expression.js'
* Only top-level headings (those not in blockquotes or lists), are used.
* This default behavior can be changed by passing `options.parents`.
*
* @param {Node} node
* @param {Options} [options]
* @param {Node} tree
* Tree to search and generate from.
* @param {Options | null | undefined} [options]
* Configuration.
* @returns {Result}
* Results.
*/
export function toc(node, options) {
export function toc(tree, options) {
const settings = options || {}
const heading = settings.heading ? toExpression(settings.heading) : null
const result = search(node, heading, settings)
const heading = settings.heading ? toExpression(settings.heading) : undefined
const result = search(tree, heading, settings)

return {
index: heading ? result.index : null,
Expand Down
50 changes: 34 additions & 16 deletions lib/search.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,53 @@
/**
* @typedef {import('mdast').Root|import('mdast').Content} Node
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Content} Content
* @typedef {import('mdast').Heading} Heading
* @typedef {import('mdast').PhrasingContent} PhrasingContent
* @typedef {string} IsType
* @typedef {Record<string, unknown>} IsProps
* @typedef {import('unist-util-is').TestFunctionAnything} IsTestFunctionAnything
* @typedef {import('unist-util-is').Test} Test
*/

/**
* @typedef {Root | Content} Node
* @typedef {Heading['depth']} Rank
*
*/

/**
* @typedef SearchOptions
* @property {string} [skip]
* Search configuration.
* @property {string | null | undefined} [skip]
* Headings to skip, wrapped in `new RegExp('^(' + value + ')$', 'i')`.
* Any heading matching this expression will not be present in the table of
* contents.
* @property {IsType|IsProps|IsTestFunctionAnything|Array<IsType|IsProps|IsTestFunctionAnything>} [parents]
* @property {Test} [parents]
* Allow headings to be children of certain node types (default: the to `toc`
* given `tree`, to only allow top-level headings).
*
* Internally, uses `unist-util-is` to check, so `parents` can be any
* `is`-compatible test.
* @property {Heading['depth']} [maxDepth=6]
* @property {Rank | null | undefined} [maxDepth=6]
* Maximum heading depth to include in the table of contents.
*
* This is inclusive: when set to `3`, level three headings are included
* (those with three hashes, `###`).
*
* @typedef SearchEntry
* @property {Heading['depth']} depth
* @property {Array<PhrasingContent>} children
* Entry.
* @property {string} id
* ID of entry.
* @property {Array<PhrasingContent>} children
* Contents of entry.
* @property {Rank} depth
* Rank of entry.
*
* @typedef SearchResult
* Results.
* @property {number} index
* Where the contents section starts, if looking for a heading.
* @property {number} endIndex
* Where the contents section ends, if looking for a heading.
* @property {Array<SearchEntry>} map
* List of entries.
*/

import Slugger from 'github-slugger'
Expand All @@ -44,20 +62,20 @@ const slugs = new Slugger()
* Search a node for a toc.
*
* @param {Node} root
* @param {RegExp|null} expression
* @param {RegExp | undefined} expression
* @param {SearchOptions} settings
* @returns {SearchResult}
*/
export function search(root, expression, settings) {
const skip = settings.skip && toExpression(settings.skip)
const skip = settings.skip ? toExpression(settings.skip) : undefined
const parents = convert(settings.parents || ((d) => d === root))
/** @type {Array<SearchEntry>} */
const map = []
/** @type {number|undefined} */
/** @type {number | undefined} */
let index
/** @type {number} */
/** @type {number | undefined} */
let endIndex
/** @type {Heading} */
/** @type {Heading | undefined} */
let opening

slugs.reset()
Expand Down Expand Up @@ -103,10 +121,10 @@ export function search(root, expression, settings) {
})

return {
index: index || -1,
index: index === undefined ? -1 : index,
// <sindresorhus/eslint-plugin-unicorn#980>
// @ts-expect-error Looks like a parent.
endIndex: index ? endIndex || root.children.length : -1, // eslint-disable-line unicorn/explicit-length-check
endIndex: index === undefined ? -1 : endIndex || root.children.length, // eslint-disable-line unicorn/explicit-length-check
map
}
}

0 comments on commit d4c5cac

Please sign in to comment.