rehypejs / rehype-format

Compare 3816d60 ... +11 ... 66c3200

Coverage Reach
index.js

No flags found

Use flags to group coverage reports by test type, project and/or folders.
Then setup custom commit statuses and notifications for each flag.

e.g., #unittest #integration

#production #enterprise

#frontend #backend

Learn more about Codecov Flags here.

Showing 1 of 9 files from the diff.
Other files ignored by Codecov

@@ -1,90 +1,119 @@
Loading
1 -
'use strict'
2 -
3 -
var minify = require('rehype-minify-whitespace')({newlines: true})
4 -
var visit = require('unist-util-visit-parents')
5 -
var embedded = require('hast-util-embedded')
6 -
var phrasing = require('hast-util-phrasing')
7 -
var whitespace = require('hast-util-whitespace')
8 -
var is = require('hast-util-is-element')
9 -
var sensitive = require('html-whitespace-sensitive-tag-names')
10 -
var repeat = require('repeat-string')
11 -
12 -
module.exports = format
13 -
14 -
function format(options) {
15 -
  var settings = options || {}
16 -
  var indent = settings.indent || 2
17 -
  var indentInitial = settings.indentInitial
1 +
/**
2 +
 * @typedef {import('hast').Root} Root
3 +
 * @typedef {Root['children'][number]} Child
4 +
 * @typedef {import('hast').Element} Element
5 +
 * @typedef {Root|Child} Node
6 +
 *
7 +
 * @typedef Options
8 +
 *   Configuration.
9 +
 * @property {number|string} [indent=2]
10 +
 *   Indentation per level (`number`, `string`, default: `2`).
11 +
 *   When number, uses that amount of spaces.
12 +
 *   When `string`, uses that per indentation level.
13 +
 * @property {boolean} [indentInitial=true]
14 +
 *   Whether to indent the first level (`boolean`, default: `true`).
15 +
 *   This is usually the `<html>`, thus not indenting `head` and `body`.
16 +
 * @property {string[]} [blanks=[]]
17 +
 *   List of tag names to join with a blank line (`Array.<string>`, default:
18 +
 *   `[]`).
19 +
 *   These tags, when next to each other, are joined by a blank line (`\n\n`).
20 +
 *   For example, when `['head', 'body']` is given, a blank line is added
21 +
 *   between these two.
22 +
 */
23 +
24 +
import rehypeMinifyWhitespace from 'rehype-minify-whitespace'
25 +
import {visitParents, SKIP} from 'unist-util-visit-parents'
26 +
import {embedded} from 'hast-util-embedded'
27 +
import {phrasing} from 'hast-util-phrasing'
28 +
import {whitespace} from 'hast-util-whitespace'
29 +
import {isElement} from 'hast-util-is-element'
30 +
import {whitespaceSensitiveTagNames} from 'html-whitespace-sensitive-tag-names'
31 +
32 +
const minify = rehypeMinifyWhitespace({newlines: true})
33 +
34 +
/**
35 +
 * @type {import('unified').Plugin<[Options?] | void[], Root>}
36 +
 */
37 +
export default function rehypeFormat(options = {}) {
38 +
  let indent = options.indent || 2
39 +
  let indentInitial = options.indentInitial
18 40
19 41
  if (typeof indent === 'number') {
20 -
    indent = repeat(' ', indent)
42 +
    indent = ' '.repeat(indent)
21 43
  }
22 44
23 45
  // Default to indenting the initial level.
24 46
  if (indentInitial === null || indentInitial === undefined) {
25 47
    indentInitial = true
26 48
  }
27 49
28 -
  return transform
29 -
30 -
  function transform(tree) {
31 -
    var head
50 +
  return (tree) => {
51 +
    /** @type {boolean|undefined} */
52 +
    let head
32 53
54 +
    // @ts-expect-error: fine, it’s a sync transformer.
33 55
    minify(tree)
34 56
35 -
    visit(tree, visitor)
57 +
    // eslint-disable-next-line complexity
58 +
    visitParents(tree, (node, parents) => {
59 +
      let index = -1
36 60
37 -
    function visitor(node, parents) {
38 -
      var children = node.children || []
39 -
      var level = parents.length
40 -
      var index = -1
41 -
      var result
42 -
      var previous
43 -
      var child
44 -
      var eol
61 +
      if (!('children' in node)) {
62 +
        return
63 +
      }
45 64
46 -
      if (is(node, 'head')) {
65 +
      if (isElement(node, 'head')) {
47 66
        head = true
48 67
      }
49 68
50 -
      if (head && is(node, 'body')) {
51 -
        head = null
69 +
      if (head && isElement(node, 'body')) {
70 +
        head = undefined
52 71
      }
53 72
54 -
      if (is(node, sensitive)) {
55 -
        return visit.SKIP
73 +
      if (isElement(node, whitespaceSensitiveTagNames)) {
74 +
        return SKIP
56 75
      }
57 76
77 +
      const children = node.children
78 +
      let level = parents.length
79 +
58 80
      // Don’t indent content of whitespace-sensitive nodes / inlines.
59 -
      if (!children.length || !padding(node, head)) {
81 +
      if (children.length === 0 || !padding(node, head)) {
60 82
        return
61 83
      }
62 84
63 85
      if (!indentInitial) {
64 86
        level--
65 87
      }
66 88
89 +
      /** @type {boolean|undefined} */
90 +
      let eol
91 +
67 92
      // Indent newlines in `text`.
68 93
      while (++index < children.length) {
69 -
        child = children[index]
94 +
        const child = children[index]
70 95
71 96
        if (child.type === 'text' || child.type === 'comment') {
72 -
          if (child.value.indexOf('\n') !== -1) {
97 +
          if (child.value.includes('\n')) {
73 98
            eol = true
74 99
          }
75 100
76 101
          child.value = child.value.replace(
77 102
            / *\n/g,
78 -
            '$&' + repeat(indent, level)
103 +
            '$&' + String(indent).repeat(level)
79 104
          )
80 105
        }
81 106
      }
82 107
83 -
      result = []
108 +
      /** @type {Child[]} */
109 +
      const result = []
110 +
      /** @type {Child|undefined} */
111 +
      let previous
112 +
84 113
      index = -1
85 114
86 115
      while (++index < children.length) {
87 -
        child = children[index]
116 +
        const child = children[index]
88 117
89 118
        if (padding(child, head) || (eol && !index)) {
90 119
          addBreak(result, level, child)
@@ -95,7 +124,7 @@
Loading
95 124
        result.push(child)
96 125
      }
97 126
98 -
      if (eol || padding(previous, head)) {
127 +
      if (previous && (eol || padding(previous, head))) {
99 128
        // Ignore trailing whitespace (if that already existed), as we’ll add
100 129
        // properly indented whitespace.
101 130
        if (whitespace(previous)) {
@@ -107,38 +136,54 @@
Loading
107 136
      }
108 137
109 138
      node.children = result
110 -
    }
111 -
  }
112 -
113 -
  function blank(node) {
114 -
    return (
115 -
      node &&
116 -
      node.type === 'element' &&
117 -
      settings.blanks &&
118 -
      settings.blanks.length &&
119 -
      settings.blanks.indexOf(node.tagName) > -1
120 -
    )
139 +
    })
121 140
  }
122 141
142 +
  /**
143 +
   * @param {Child[]} list
144 +
   * @param {number} level
145 +
   * @param {Child} [next]
146 +
   * @returns {void}
147 +
   */
123 148
  function addBreak(list, level, next) {
124 -
    var tail = list[list.length - 1]
125 -
    var previous = whitespace(tail) ? list[list.length - 2] : tail
126 -
    var replace =
127 -
      (blank(previous) && blank(next) ? '\n\n' : '\n') + repeat(indent, level)
149 +
    const tail = list[list.length - 1]
150 +
    const previous = whitespace(tail) ? list[list.length - 2] : tail
151 +
    const replace =
152 +
      (blank(previous) && blank(next) ? '\n\n' : '\n') +
153 +
      String(indent).repeat(Math.max(level, 0))
128 154
129 155
    if (tail && tail.type === 'text') {
130 156
      tail.value = whitespace(tail) ? replace : tail.value + replace
131 157
    } else {
132 158
      list.push({type: 'text', value: replace})
133 159
    }
134 160
  }
161 +
162 +
  /**
163 +
   * @param {Node|undefined} node
164 +
   * @returns {boolean}
165 +
   */
166 +
  function blank(node) {
167 +
    return Boolean(
168 +
      node &&
169 +
        node.type === 'element' &&
170 +
        options.blanks &&
171 +
        options.blanks.length > 0 &&
172 +
        options.blanks.includes(node.tagName)
173 +
    )
174 +
  }
135 175
}
136 176
177 +
/**
178 +
 * @param {Node} node
179 +
 * @param {boolean|undefined} head
180 +
 * @returns {boolean}
181 +
 */
137 182
function padding(node, head) {
138 183
  return (
139 184
    node.type === 'root' ||
140 185
    (node.type === 'element'
141 -
      ? head || is(node, 'script') || embedded(node) || !phrasing(node)
186 +
      ? head || isElement(node, 'script') || embedded(node) || !phrasing(node)
142 187
      : false)
143 188
  )
144 189
}

Learn more Showing 1 files with coverage changes found.

Changes in index.js
+4
Loading file...
Files Coverage
index.js 100.00%
Project Totals (1 files) 100.00%
Loading