Skip to content

Commit

Permalink
Refactor to externalize core as hast-util-format
Browse files Browse the repository at this point in the history
  • Loading branch information
wooorm committed Sep 19, 2024
1 parent 5cdf41e commit 1d9bb05
Show file tree
Hide file tree
Showing 4 changed files with 11 additions and 194 deletions.
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @typedef {import('./lib/index.js').Options} Options
* @typedef {import('hast-util-format').Options} Options
*/

export {default} from './lib/index.js'
189 changes: 4 additions & 185 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -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<string> | 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
* `<html>`, 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.
Expand All @@ -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.
*
Expand All @@ -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<RootContent>} */
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<RootContent>} 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)
)
}
8 changes: 1 addition & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 5 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<img />`,
etc.
Yet another project, [`rehype-minify`][rehype-minify], does the inverse: improve
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 1d9bb05

Please sign in to comment.