rehype-format
Advanced tools
Comparing version 3.1.0 to 4.0.0
196
index.js
@@ -1,27 +0,43 @@ | ||
'use strict' | ||
/** | ||
* @typedef {import('hast').Root} Root | ||
* @typedef {Root['children'][number]} Child | ||
* @typedef {import('hast').Element} Element | ||
* @typedef {Root|Child} Node | ||
* | ||
* @typedef Options | ||
* Configuration. | ||
* @property {number|string} [indent=2] | ||
* Indentation per level (`number`, `string`, default: `2`). | ||
* When number, uses that amount of spaces. | ||
* When `string`, uses that per indentation level. | ||
* @property {boolean} [indentInitial=true] | ||
* Whether to indent the first level (`boolean`, default: `true`). | ||
* This is usually the `<html>`, thus not indenting `head` and `body`. | ||
* @property {string[]} [blanks=[]] | ||
* List of tag names to join with a blank line (`Array.<string>`, 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. | ||
*/ | ||
var minify = require('rehype-minify-whitespace')({newlines: true}) | ||
var visit = require('unist-util-visit-parents') | ||
var embedded = require('hast-util-embedded') | ||
var phrasing = require('hast-util-phrasing') | ||
var whitespace = require('hast-util-whitespace') | ||
var is = require('hast-util-is-element') | ||
var sensitive = require('html-whitespace-sensitive-tag-names') | ||
var repeat = require('repeat-string') | ||
import rehypeMinifyWhitespace from 'rehype-minify-whitespace' | ||
import {visitParents, SKIP} from 'unist-util-visit-parents' | ||
import {embedded} from 'hast-util-embedded' | ||
import {phrasing} from 'hast-util-phrasing' | ||
import {whitespace} from 'hast-util-whitespace' | ||
import {isElement} from 'hast-util-is-element' | ||
import {whitespaceSensitiveTagNames} from 'html-whitespace-sensitive-tag-names' | ||
module.exports = format | ||
const minify = rehypeMinifyWhitespace({newlines: true}) | ||
var double = '\n\n' | ||
var single = '\n' | ||
var space = ' ' | ||
var re = / *\n/g | ||
/** | ||
* @type {import('unified').Plugin<[Options?] | void[], Root>} | ||
*/ | ||
export default function rehypeFormat(options = {}) { | ||
let indent = options.indent || 2 | ||
let indentInitial = options.indentInitial | ||
function format(options) { | ||
var settings = options || {} | ||
var indent = settings.indent || 2 | ||
var indentInitial = settings.indentInitial | ||
var blanks = settings.blanks || [] | ||
if (typeof indent === 'number') { | ||
indent = repeat(space, indent) | ||
indent = ' '.repeat(indent) | ||
} | ||
@@ -34,35 +50,34 @@ | ||
return transform | ||
return (tree) => { | ||
/** @type {boolean|undefined} */ | ||
let head | ||
function transform(tree) { | ||
var head = false | ||
// @ts-expect-error: fine, it’s a sync transformer. | ||
minify(tree) | ||
visit(tree, visitor) | ||
// eslint-disable-next-line complexity | ||
visitParents(tree, (node, parents) => { | ||
let index = -1 | ||
function visitor(node, parents) { | ||
var children = node.children || [] | ||
var length = children.length | ||
var level = parents.length | ||
var index = -1 | ||
var result | ||
var previous | ||
var child | ||
var newline | ||
if (!('children' in node)) { | ||
return | ||
} | ||
if (is(node, 'head')) { | ||
if (isElement(node, 'head')) { | ||
head = true | ||
} | ||
if (head && is(node, 'body')) { | ||
head = false | ||
if (head && isElement(node, 'body')) { | ||
head = undefined | ||
} | ||
if (is(node, sensitive)) { | ||
return visit.SKIP | ||
if (isElement(node, whitespaceSensitiveTagNames)) { | ||
return SKIP | ||
} | ||
const children = node.children | ||
let level = parents.length | ||
// Don’t indent content of whitespace-sensitive nodes / inlines. | ||
if (!length || !padding(node, head)) { | ||
if (children.length === 0 || !padding(node, head)) { | ||
return | ||
@@ -75,24 +90,34 @@ } | ||
/** @type {boolean|undefined} */ | ||
let eol | ||
// Indent newlines in `text`. | ||
while (++index < length) { | ||
child = children[index] | ||
while (++index < children.length) { | ||
const child = children[index] | ||
if (child.type === 'text' || child.type === 'comment') { | ||
if (child.value.indexOf('\n') !== -1) { | ||
newline = true | ||
if (child.value.includes('\n')) { | ||
eol = true | ||
} | ||
child.value = child.value.replace(re, '$&' + repeat(indent, level)) | ||
child.value = child.value.replace( | ||
/ *\n/g, | ||
'$&' + String(indent).repeat(level) | ||
) | ||
} | ||
} | ||
result = [] | ||
/** @type {Child[]} */ | ||
const result = [] | ||
/** @type {Child|undefined} */ | ||
let previous | ||
index = -1 | ||
while (++index < length) { | ||
child = children[index] | ||
while (++index < children.length) { | ||
const child = children[index] | ||
if (padding(child, head) || (newline && index === 0)) { | ||
if (padding(child, head) || (eol && !index)) { | ||
addBreak(result, level, child) | ||
newline = true | ||
eol = true | ||
} | ||
@@ -104,3 +129,3 @@ | ||
if (newline || padding(previous, head)) { | ||
if (previous && (eol || padding(previous, head))) { | ||
// Ignore trailing whitespace (if that already existed), as we’ll add | ||
@@ -114,30 +139,23 @@ // properly indented whitespace. | ||
addBreak(result, level - 1) | ||
newline = true | ||
} | ||
node.children = result | ||
} | ||
}) | ||
} | ||
function blank(node) { | ||
return ( | ||
node && | ||
node.type === 'element' && | ||
blanks.length !== 0 && | ||
blanks.indexOf(node.tagName) !== -1 | ||
) | ||
} | ||
/** | ||
* @param {Child[]} list | ||
* @param {number} level | ||
* @param {Child} [next] | ||
* @returns {void} | ||
*/ | ||
function addBreak(list, level, next) { | ||
var tail = list[list.length - 1] | ||
var previous = whitespace(tail) ? list[list.length - 2] : tail | ||
var replace = | ||
(blank(previous) && blank(next) ? double : single) + repeat(indent, level) | ||
const tail = list[list.length - 1] | ||
const previous = 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') { | ||
if (whitespace(tail)) { | ||
tail.value = replace | ||
} else { | ||
tail.value += replace | ||
} | ||
tail.value = whitespace(tail) ? replace : tail.value + replace | ||
} else { | ||
@@ -147,14 +165,30 @@ list.push({type: 'text', value: replace}) | ||
} | ||
/** | ||
* @param {Node|undefined} node | ||
* @returns {boolean} | ||
*/ | ||
function blank(node) { | ||
return Boolean( | ||
node && | ||
node.type === 'element' && | ||
options.blanks && | ||
options.blanks.length > 0 && | ||
options.blanks.includes(node.tagName) | ||
) | ||
} | ||
} | ||
/** | ||
* @param {Node} node | ||
* @param {boolean|undefined} head | ||
* @returns {boolean} | ||
*/ | ||
function padding(node, head) { | ||
if (node.type === 'root') { | ||
return true | ||
} | ||
if (node.type === 'element') { | ||
return head || is(node, 'script') || embedded(node) || !phrasing(node) | ||
} | ||
return false | ||
return ( | ||
node.type === 'root' || | ||
(node.type === 'element' | ||
? head || isElement(node, 'script') || embedded(node) || !phrasing(node) | ||
: false) | ||
) | ||
} |
{ | ||
"name": "rehype-format", | ||
"version": "3.1.0", | ||
"version": "4.0.0", | ||
"description": "rehype plugin to format HTML", | ||
@@ -27,45 +27,43 @@ "license": "MIT", | ||
], | ||
"sideEffects": false, | ||
"type": "module", | ||
"main": "index.js", | ||
"types": "index.d.ts", | ||
"files": [ | ||
"index.d.ts", | ||
"index.js" | ||
], | ||
"dependencies": { | ||
"hast-util-embedded": "^1.0.0", | ||
"hast-util-is-element": "^1.0.0", | ||
"hast-util-phrasing": "^1.0.0", | ||
"hast-util-whitespace": "^1.0.0", | ||
"html-whitespace-sensitive-tag-names": "^1.0.0", | ||
"rehype-minify-whitespace": "^4.0.0", | ||
"repeat-string": "^1.0.0", | ||
"unist-util-visit-parents": "^3.0.0" | ||
"@types/hast": "^2.3.2", | ||
"hast-util-embedded": "^2.0.0", | ||
"hast-util-is-element": "^2.0.0", | ||
"hast-util-phrasing": "^2.0.0", | ||
"hast-util-whitespace": "^2.0.0", | ||
"html-whitespace-sensitive-tag-names": "^2.0.0", | ||
"rehype-minify-whitespace": "^5.0.0", | ||
"unified": "^10.0.0", | ||
"unist-util-visit-parents": "^5.0.0" | ||
}, | ||
"devDependencies": { | ||
"bail": "^1.0.0", | ||
"browserify": "^16.0.0", | ||
"is-hidden": "^1.0.0", | ||
"negate": "^1.0.0", | ||
"nyc": "^15.0.0", | ||
"@types/tape": "^4.0.0", | ||
"c8": "^7.0.0", | ||
"is-hidden": "^2.0.0", | ||
"prettier": "^2.0.0", | ||
"rehype": "^11.0.0", | ||
"remark-cli": "^8.0.0", | ||
"remark-preset-wooorm": "^7.0.0", | ||
"rehype": "^12.0.0", | ||
"remark-cli": "^9.0.0", | ||
"remark-preset-wooorm": "^8.0.0", | ||
"rimraf": "^3.0.0", | ||
"tape": "^5.0.0", | ||
"tinyify": "^2.0.0", | ||
"to-vfile": "^6.0.0", | ||
"xo": "^0.32.0" | ||
"to-vfile": "^7.0.0", | ||
"type-coverage": "^2.0.0", | ||
"typescript": "^4.0.0", | ||
"xo": "^0.42.0" | ||
}, | ||
"scripts": { | ||
"format": "remark . -qfo && prettier . --write && xo --fix", | ||
"build-bundle": "browserify . -s rehypeFormat > rehype-format.js", | ||
"build-mangle": "browserify . -s rehypeFormat -p tinyify > rehype-format.min.js", | ||
"build": "npm run build-bundle && npm run build-mangle", | ||
"test-api": "node test", | ||
"test-coverage": "nyc --reporter lcov tape test/index.js", | ||
"test": "npm run format && npm run build && npm run test-coverage" | ||
"build": "rimraf \"*.d.ts\" \"test/**/*.d.ts\" && tsc && type-coverage", | ||
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", | ||
"test-api": "node --conditions development test/index.js", | ||
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node --conditions development test/index.js", | ||
"test": "npm run build && npm run format && npm run test-coverage" | ||
}, | ||
"nyc": { | ||
"check-coverage": true, | ||
"lines": 100, | ||
"functions": 100, | ||
"branches": 100 | ||
}, | ||
"prettier": { | ||
@@ -80,12 +78,3 @@ "tabWidth": 2, | ||
"xo": { | ||
"prettier": true, | ||
"esnext": false, | ||
"rules": { | ||
"unicorn/no-fn-reference-in-iterator": "off", | ||
"unicorn/prefer-includes": "off", | ||
"unicorn/prefer-optional-catch-binding": "off" | ||
}, | ||
"ignores": [ | ||
"rehype-format.js" | ||
] | ||
"prettier": true | ||
}, | ||
@@ -96,3 +85,9 @@ "remarkConfig": { | ||
] | ||
}, | ||
"typeCoverage": { | ||
"atLeast": 100, | ||
"detail": true, | ||
"strict": true, | ||
"ignoreCatch": true | ||
} | ||
} |
@@ -15,2 +15,5 @@ # rehype-format | ||
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): | ||
Node 12+ is needed to use it and it must be `import`ed instead of `require`d. | ||
[npm][]: | ||
@@ -38,14 +41,17 @@ | ||
And our script, `example.js`, looks as follows: | ||
And our module, `example.js`, looks as follows: | ||
```js | ||
var vfile = require('to-vfile') | ||
var report = require('vfile-reporter') | ||
var rehype = require('rehype') | ||
var format = require('rehype-format') | ||
import {readSync} from 'to-vfile' | ||
import {reporter} from 'vfile-reporter' | ||
import {rehype} from 'rehype' | ||
import rehypeFormat from 'rehype-format' | ||
const file = readSync('index.html') | ||
rehype() | ||
.use(format) | ||
.process(vfile.readSync('index.html'), function (err, file) { | ||
console.error(report(err || file)) | ||
.use(rehypeFormat) | ||
.process(file) | ||
.then((file) => { | ||
console.error(reporter(file)) | ||
console.log(String(file)) | ||
@@ -78,6 +84,9 @@ }) | ||
### `rehype().use(format[, options])` | ||
This package exports no identifiers. | ||
The default export is `rehypeFormat`. | ||
Format white space in the processed tree. | ||
### `unified().use(rehypeFormat[, options])` | ||
Format whitespace in the processed tree. | ||
* Collapse all white space (to a single space or newline) | ||
@@ -149,5 +158,5 @@ * Remove unneeded white space | ||
[build-badge]: https://img.shields.io/travis/rehypejs/rehype-format.svg | ||
[build-badge]: https://github.com/rehypejs/rehype-format/workflows/main/badge.svg | ||
[build]: https://travis-ci.org/rehypejs/rehype-format | ||
[build]: https://github.com/rehypejs/rehype-format/actions | ||
@@ -172,5 +181,5 @@ [coverage-badge]: https://img.shields.io/codecov/c/github/rehypejs/rehype-format.svg | ||
[chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg | ||
[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg | ||
[chat]: https://spectrum.chat/unified/rehype | ||
[chat]: https://github.com/rehypejs/rehype/discussions | ||
@@ -177,0 +186,0 @@ [npm]: https://docs.npmjs.com/cli/install |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14765
5
194
200
Yes
9
1
+ Added@types/hast@^2.3.2
+ Addedunified@^10.0.0
+ Added@types/hast@2.3.10(transitive)
+ Addedbail@2.0.2(transitive)
+ Addedextend@3.0.2(transitive)
+ Addedhast-util-embedded@2.0.1(transitive)
+ Addedhast-util-has-property@2.0.1(transitive)
+ Addedhast-util-is-body-ok-link@2.0.0(transitive)
+ Addedhast-util-is-element@2.1.3(transitive)
+ Addedhast-util-phrasing@2.0.2(transitive)
+ Addedhast-util-whitespace@2.0.1(transitive)
+ Addedhtml-whitespace-sensitive-tag-names@2.0.0(transitive)
+ Addedis-buffer@2.0.5(transitive)
+ Addedis-plain-obj@4.1.0(transitive)
+ Addedrehype-minify-whitespace@5.0.1(transitive)
+ Addedtrough@2.2.0(transitive)
+ Addedunified@10.1.2(transitive)
+ Addedunist-util-is@5.2.1(transitive)
+ Addedunist-util-stringify-position@3.0.3(transitive)
+ Addedunist-util-visit-parents@5.1.3(transitive)
+ Addedvfile@5.3.7(transitive)
+ Addedvfile-message@3.1.4(transitive)
- Removedrepeat-string@^1.0.0
- Removedhast-util-embedded@1.0.6(transitive)
- Removedhast-util-has-property@1.0.4(transitive)
- Removedhast-util-is-body-ok-link@1.0.4(transitive)
- Removedhast-util-is-element@1.1.0(transitive)
- Removedhast-util-phrasing@1.0.5(transitive)
- Removedhast-util-whitespace@1.0.4(transitive)
- Removedhtml-whitespace-sensitive-tag-names@1.0.3(transitive)
- Removedrehype-minify-whitespace@4.0.5(transitive)
- Removedrepeat-string@1.6.1(transitive)
- Removedunist-util-is@4.1.0(transitive)
- Removedunist-util-visit-parents@3.1.1(transitive)
Updatedhast-util-embedded@^2.0.0
Updatedhast-util-is-element@^2.0.0
Updatedhast-util-phrasing@^2.0.0
Updatedhast-util-whitespace@^2.0.0