From 1d9bb05fc90acedba971b96c1bbefb2aee1fa930 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Thu, 19 Sep 2024 12:22:53 +0200 Subject: [PATCH] Refactor to externalize core as `hast-util-format` --- index.js | 2 +- lib/index.js | 189 ++------------------------------------------------- package.json | 8 +-- readme.md | 6 +- 4 files changed, 11 insertions(+), 194 deletions(-) diff --git a/index.js b/index.js index a49a278..a92a857 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ /** - * @typedef {import('./lib/index.js').Options} Options + * @typedef {import('hast-util-format').Options} Options */ export {default} from './lib/index.js' diff --git a/lib/index.js b/lib/index.js index 96f7b56..b7038f6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,33 +1,9 @@ /** - * @import {Nodes, RootContent, Root} from 'hast' + * @import {Options} from 'hast-util-format' + * @import {Root} from 'hast' */ -/** - * @typedef Options - * Configuration. - * @property {Array | null | undefined} [blanks=[]] - * List of tag names to join with a blank line (default: `[]`); these tags, - * when next to each other, are joined by a blank line (`\n\n`); for example, - * when `['head', 'body']` is given, a blank line is added between these two. - * @property {number | string | null | undefined} [indent=2] - * Indentation per level (default: `2`); when number, uses that amount of - * spaces; when `string`, uses that per indentation level. - * @property {boolean | null | undefined} [indentInitial=true] - * Whether to indent the first level (default: `true`); this is usually the - * ``, thus not indenting `head` and `body`. - */ - -import {embedded} from 'hast-util-embedded' -import {isElement} from 'hast-util-is-element' -import {phrasing} from 'hast-util-phrasing' -import {whitespace} from 'hast-util-whitespace' -import {whitespaceSensitiveTagNames} from 'html-whitespace-sensitive-tag-names' -import rehypeMinifyWhitespace from 'rehype-minify-whitespace' -import {SKIP, visitParents} from 'unist-util-visit-parents' - -/** @type {Options} */ -const emptyOptions = {} -const transformWhitespace = rehypeMinifyWhitespace({newlines: true}) +import {format} from 'hast-util-format' /** * Format whitespace in HTML. @@ -38,19 +14,6 @@ const transformWhitespace = rehypeMinifyWhitespace({newlines: true}) * Transform. */ export default function rehypeFormat(options) { - const settings = options || emptyOptions - let indent = settings.indent || 2 - let indentInitial = settings.indentInitial - - if (typeof indent === 'number') { - indent = ' '.repeat(indent) - } - - // Default to indenting the initial level. - if (indentInitial === null || indentInitial === undefined) { - indentInitial = true - } - /** * Transform. * @@ -60,150 +23,6 @@ export default function rehypeFormat(options) { * Nothing. */ return function (tree) { - /** @type {boolean | undefined} */ - let head - - transformWhitespace(tree) - - // eslint-disable-next-line complexity - visitParents(tree, function (node, parents) { - let index = -1 - - if (!('children' in node)) { - return - } - - if (isElement(node, 'head')) { - head = true - } - - if (head && isElement(node, 'body')) { - head = undefined - } - - if (isElement(node, whitespaceSensitiveTagNames)) { - return SKIP - } - - const children = node.children - let level = parents.length - - // Don’t indent content of whitespace-sensitive nodes / inlines. - if (children.length === 0 || !padding(node, head)) { - return - } - - if (!indentInitial) { - level-- - } - - /** @type {boolean | undefined} */ - let eol - - // Indent newlines in `text`. - while (++index < children.length) { - const child = children[index] - - if (child.type === 'text' || child.type === 'comment') { - if (child.value.includes('\n')) { - eol = true - } - - child.value = child.value.replace( - / *\n/g, - '$&' + String(indent).repeat(level) - ) - } - } - - /** @type {Array} */ - const result = [] - /** @type {RootContent | undefined} */ - let previous - - index = -1 - - while (++index < children.length) { - const child = children[index] - - if (padding(child, head) || (eol && !index)) { - addBreak(result, level, child) - eol = true - } - - previous = child - result.push(child) - } - - if (previous && (eol || padding(previous, head))) { - // Ignore trailing whitespace (if that already existed), as we’ll add - // properly indented whitespace. - if (whitespace(previous)) { - result.pop() - previous = result[result.length - 1] - } - - addBreak(result, level - 1) - } - - node.children = result - }) - } - - /** - * @param {Array} list - * Nodes. - * @param {number} level - * Indentation level. - * @param {RootContent | undefined} [next] - * Next node. - * @returns {undefined} - * Nothing. - */ - function addBreak(list, level, next) { - const tail = list[list.length - 1] - const previous = tail && whitespace(tail) ? list[list.length - 2] : tail - const replace = - (blank(previous) && blank(next) ? '\n\n' : '\n') + - String(indent).repeat(Math.max(level, 0)) - - if (tail && tail.type === 'text') { - tail.value = whitespace(tail) ? replace : tail.value + replace - } else { - list.push({type: 'text', value: replace}) - } + format(tree, options) } - - /** - * @param {Nodes | undefined} node - * Node. - * @returns {boolean} - * Whether `node` is a blank. - */ - function blank(node) { - return Boolean( - node && - node.type === 'element' && - settings.blanks && - settings.blanks.length > 0 && - settings.blanks.includes(node.tagName) - ) - } -} - -/** - * @param {Nodes} node - * Node. - * @param {boolean | undefined} head - * Whether the node is in `head`. - * @returns {boolean} - * Whether `node` should be padded. - */ -function padding(node, head) { - return ( - node.type === 'root' || - (node.type === 'element' - ? head || isElement(node, 'script') || embedded(node) || !phrasing(node) - : false) - ) } diff --git a/package.json b/package.json index b853ccc..f1d9348 100644 --- a/package.json +++ b/package.json @@ -35,13 +35,7 @@ ], "dependencies": { "@types/hast": "^3.0.0", - "hast-util-embedded": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "hast-util-phrasing": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "html-whitespace-sensitive-tag-names": "^3.0.0", - "rehype-minify-whitespace": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" + "hast-util-format": "^1.0.0" }, "devDependencies": { "@types/node": "^22.0.0", diff --git a/readme.md b/readme.md index d777af6..603a41a 100644 --- a/readme.md +++ b/readme.md @@ -50,7 +50,9 @@ This is a rehype plugin that changes whitespace in hast. This package is useful when you want to improve the readability of HTML source code as it adds insignificant but pretty whitespace between elements. -A different package, [`rehype-stringify`][rehype-stringify], controls how HTML +The package [`hast-util-format`][hast-util-format] does the same as this plugin +at the utility level. +A different plugin, [`rehype-stringify`][rehype-stringify], controls how HTML is actually printed: which quotes to use, whether to put a `/` on ``, etc. Yet another project, [`rehype-minify`][rehype-minify], does the inverse: improve @@ -357,6 +359,8 @@ abide by its terms. [transformer]: https://github.com/unifiedjs/unified#transformer +[hast-util-format]: https://github.com/syntax-tree/hast-util-format + [rehype]: https://github.com/rehypejs/rehype [rehype-stringify]: https://github.com/rehypejs/rehype/tree/main/packages/rehype-stringify