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
3816d60
... +11 ...
66c3200
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
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 | 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 | 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.
index.js
Files | Coverage |
---|---|
index.js | 100.00% |
Project Totals (1 files) | 100.00% |
66c3200
a76fb9d
f9e30d5
f2298e0
8cae1b5
dc8b1d3
b433d4d
24e00c4
7a821b5
f4a778c
5095350
515c99f
3816d60