Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

rehype-minify-whitespace

Package Overview
Dependencies
Maintainers
2
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rehype-minify-whitespace - npm Package Compare versions

Comparing version 4.0.5 to 5.0.0

block.d.ts

125

block.js
// See: <https://html.spec.whatwg.org/#the-css-user-agent-style-sheet-and-presentational-hints>
module.exports = [
// Contribute whitespace intrinsically.
'br',
'wbr',
// Similar to block.
'li',
'table',
'caption',
'colgroup',
'col',
'thead',
'tbody',
'tfoot',
'tr',
'td',
'th',
'summary',
'optgroup',
'option',
// Page
'html',
'head',
'body',
// Flow content
'address',
'blockquote',
'center', // Legacy
'dialog',
'div',
'figure',
'figcaption',
'footer',
'form',
'header',
'hr',
'legend',
'listing', // Legacy
'main',
'p',
'plaintext', // Legacy
'pre',
'xmp', // Legacy
// Sections and headings
'article',
'aside',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'hgroup',
'nav',
'section',
// Lists
'dir', // Legacy
'dd',
'dl',
'dt',
'menu',
'ol',
'ul',
// Block-like:
'li',
'th',
'td'
export const blocks = [
'address', // Flow content.
'article', // Sections and headings.
'aside', // Sections and headings.
'blockquote', // Flow content.
'body', // Page.
'br', // Contribute whitespace intrinsically.
'caption', // Similar to block.
'center', // Flow content, legacy.
'col', // Similar to block.
'colgroup', // Similar to block.
'dd', // Lists.
'dialog', // Flow content.
'dir', // Lists, legacy.
'div', // Flow content.
'dl', // Lists.
'dt', // Lists.
'figcaption', // Flow content.
'figure', // Flow content.
'footer', // Flow content.
'form', // Flow content.
'h1', // Sections and headings.
'h2', // Sections and headings.
'h3', // Sections and headings.
'h4', // Sections and headings.
'h5', // Sections and headings.
'h6', // Sections and headings.
'head', // Page.
'header', // Flow content.
'hgroup', // Sections and headings.
'hr', // Flow content.
'html', // Page.
'legend', // Flow content.
'li', // Block-like.
'li', // Similar to block.
'listing', // Flow content, legacy
'main', // Flow content.
'menu', // Lists.
'nav', // Sections and headings.
'ol', // Lists.
'optgroup', // Similar to block.
'option', // Similar to block.
'p', // Flow content.
'plaintext', // Flow content, legacy
'pre', // Flow content.
'section', // Sections and headings.
'summary', // Similar to block.
'table', // Similar to block.
'tbody', // Similar to block.
'td', // Block-like.
'td', // Similar to block.
'tfoot', // Similar to block.
'th', // Block-like.
'th', // Similar to block.
'thead', // Similar to block.
'tr', // Similar to block.
'ul', // Lists.
'wbr', // Contribute whitespace intrinsically.
'xmp' // Flow content, legacy
]

@@ -1,2 +0,2 @@

module.exports = [
export const content = [
// Form.

@@ -3,0 +3,0 @@ 'button',

@@ -13,39 +13,67 @@ /**

'use strict'
/**
* @typedef {import('hast').Root} Root
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Text} Text
* @typedef {Root|Root['children'][number]} Node
*
* @typedef Options
* @property {boolean} [newlines=false]
* If `newlines: true`, collapses whitespace containing newlines to `'\n'`
* instead of `' '`.
* The default is to collapse to a single space.
*
* @typedef {'pre'|'nowrap'|'pre-wrap'|'normal'} Whitespace
*
* @typedef Context
* @property {ReturnType<collapseFactory>} collapse
* @property {Whitespace} whitespace
* @property {boolean} [before]
* @property {boolean} [after]
*
* @typedef Result
* @property {boolean} remove
* @property {boolean} ignore
* @property {boolean} stripAtStart
*/
var is = require('hast-util-is-element')
var embedded = require('hast-util-embedded')
var convert = require('unist-util-is/convert')
var whitespace = require('hast-util-whitespace')
var blocks = require('./block')
var contents = require('./content')
var skippables = require('./skippable')
import {isElement} from 'hast-util-is-element'
import {embedded} from 'hast-util-embedded'
import {convert} from 'unist-util-is'
import {whitespace} from 'hast-util-whitespace'
import {blocks} from './block.js'
import {content as contents} from './content.js'
import {skippable as skippables} from './skippable.js'
module.exports = minifyWhitespace
const ignorableNode = convert(['doctype', 'comment'])
var ignorableNode = convert(['doctype', 'comment'])
var parent = convert(['element', 'root'])
var root = convert(['root'])
var element = convert(['element'])
var text = convert(['text'])
function minifyWhitespace(options) {
var collapse = collapseFactory(
(options || {}).newlines ? replaceNewlines : replaceWhitespace
/**
* Collapse whitespace.
*
* Normally, collapses to a single space.
* If `newlines: true`, collapses whitespace containing newlines to `'\n'`
* instead of `' '`.
*
* @type {import('unified').Plugin<[Options?] | void[], Root>}
*/
export default function rehypeMinifyWhitespace(options = {}) {
const collapse = collapseFactory(
options.newlines ? replaceNewlines : replaceWhitespace
)
return transform
function transform(tree) {
minify(tree, {collapse: collapse, whitespace: 'normal'})
return (tree) => {
minify(tree, {collapse, whitespace: 'normal'})
}
}
function minify(node, options) {
var settings
/**
* @param {Node} node
* @param {Context} context
* @returns {Result}
*/
function minify(node, context) {
if ('children' in node) {
const settings = Object.assign({}, context)
if (parent(node)) {
settings = Object.assign({}, options)
if (root(node) || blocklike(node)) {
if (node.type === 'root' || blocklike(node)) {
settings.before = true

@@ -55,3 +83,3 @@ settings.after = true

settings.whitespace = inferWhiteSpace(node, options)
settings.whitespace = inferWhiteSpace(node, context)

@@ -61,10 +89,10 @@ return all(node, settings)

if (text(node)) {
if (options.whitespace === 'normal') {
return minifyText(node, options)
if (node.type === 'text') {
if (context.whitespace === 'normal') {
return minifyText(node, context)
}
// Naïve collapse, but no trimming:
if (options.whitespace === 'nowrap') {
node.value = options.collapse(node.value)
if (context.whitespace === 'nowrap') {
node.value = context.collapse(node.value)
}

@@ -76,16 +104,17 @@

return {
remove: false,
ignore: ignorableNode(node),
stripAtStart: false
}
return {remove: false, ignore: ignorableNode(node), stripAtStart: false}
}
function minifyText(node, options) {
var value = options.collapse(node.value)
var start = 0
var end = value.length
var result = {remove: false, ignore: false, stripAtStart: false}
/**
* @param {Text} node
* @param {Context} context
* @returns {Result}
*/
function minifyText(node, context) {
const value = context.collapse(node.value)
const result = {remove: false, ignore: false, stripAtStart: false}
let start = 0
let end = value.length
if (options.before && removable(value.charAt(0))) {
if (context.before && removable(value.charAt(0))) {
start++

@@ -95,3 +124,3 @@ }

if (start !== end && removable(value.charAt(end - 1))) {
if (options.after) {
if (context.after) {
end--

@@ -112,15 +141,19 @@ } else {

function all(parent, options) {
var before = options.before
var after = options.after
var children = parent.children
var length = children.length
var index = -1
var result
/**
* @param {Root|Element} parent
* @param {Context} context
* @returns {Result}
*/
function all(parent, context) {
let before = context.before
const after = context.after
const children = parent.children
let length = children.length
let index = -1
while (++index < length) {
result = minify(
const result = minify(
children[index],
Object.assign({}, options, {
before: before,
Object.assign({}, context, {
before,
after: collapsableAfter(children, index, after)

@@ -145,19 +178,17 @@ })

return {
remove: false,
ignore: false,
stripAtStart: before || after
}
return {remove: false, ignore: false, stripAtStart: Boolean(before || after)}
}
/**
* @param {Node[]} nodes
* @param {number} index
* @param {boolean|undefined} [after]
* @returns {boolean|undefined}
*/
function collapsableAfter(nodes, index, after) {
var length = nodes.length
var node
var result
while (++index < nodes.length) {
const node = nodes[index]
let result = inferBoundary(node)
while (++index < length) {
node = nodes[index]
result = inferBoundary(node)
if (result === undefined && node.children && !skippable(node)) {
if (result === undefined && 'children' in node && !skippable(node)) {
result = collapsableAfter(node.children, -1)

@@ -174,11 +205,16 @@ }

// Infer two types of boundaries:
//
// 1. `true` — boundary for which whitespace around it does not contribute
// anything
// 2. `false` — boundary for which whitespace around it *does* contribute
//
// No result (`undefined`) is returned if it is unknown.
/**
* Infer two types of boundaries:
*
* 1. `true` — boundary for which whitespace around it does not contribute
* anything
* 2. `false` — boundary for which whitespace around it *does* contribute
*
* No result (`undefined`) is returned if it is unknown.
*
* @param {Node} node
* @returns {boolean|undefined}
*/
function inferBoundary(node) {
if (element(node)) {
if (node.type === 'element') {
if (content(node)) {

@@ -194,3 +230,3 @@ return false

// children.
} else if (text(node)) {
} else if (node.type === 'text') {
if (!whitespace(node)) {

@@ -204,19 +240,40 @@ return false

// Infer whether a node is skippable.
/**
* Infer whether a node is skippable.
*
* @param {Node} node
* @returns {boolean}
*/
function content(node) {
return embedded(node) || is(node, contents)
return embedded(node) || isElement(node, contents)
}
// See: <https://html.spec.whatwg.org/#the-css-user-agent-style-sheet-and-presentational-hints>
/**
* See: <https://html.spec.whatwg.org/#the-css-user-agent-style-sheet-and-presentational-hints>
*
* @param {Element} node
* @returns {boolean}
*/
function blocklike(node) {
return is(node, blocks)
return isElement(node, blocks)
}
/**
* @param {Element|Root} node
* @returns {boolean}
*/
function skippable(node) {
/* istanbul ignore next - currently only used on elements, but just to make sure. */
var props = node.properties || {}
return ignorableNode(node) || is(node, skippables) || props.hidden
return (
Boolean(
'properties' in node && node.properties && node.properties.hidden
) ||
ignorableNode(node) ||
isElement(node, skippables)
)
}
/**
* @param {string} character
* @returns {boolean}
*/
function removable(character) {

@@ -226,7 +283,14 @@ return character === ' ' || character === '\n'

/**
* @param {string} value
* @returns {string}
*/
function replaceNewlines(value) {
var match = /\r?\n|\r/.exec(value)
const match = /\r?\n|\r/.exec(value)
return match ? match[0] : ' '
}
/**
* @returns {string}
*/
function replaceWhitespace() {

@@ -236,4 +300,12 @@ return ' '

/**
* @param {(value: string) => string} replace
*/
function collapseFactory(replace) {
return collapse
/**
* @param {string} value
* @returns {string}
*/
function collapse(value) {

@@ -244,23 +316,30 @@ return String(value).replace(/[\t\n\v\f\r ]+/g, replace)

// We don’t support void elements here (so `nobr wbr` -> `normal` is ignored).
function inferWhiteSpace(node, options) {
var props = node.properties || {}
/**
* We don’t support void elements here (so `nobr wbr` -> `normal` is ignored).
*
* @param {Root|Element} node
* @param {Context} context
* @returns {Whitespace}
*/
function inferWhiteSpace(node, context) {
if ('tagName' in node && node.properties) {
switch (node.tagName) {
case 'listing':
case 'plaintext':
case 'xmp':
return 'pre'
case 'nobr':
return 'nowrap'
case 'pre':
return node.properties.wrap ? 'pre-wrap' : 'pre'
case 'td':
case 'th':
return node.properties.noWrap ? 'nowrap' : context.whitespace
case 'textarea':
return 'pre-wrap'
default:
}
}
switch (node.tagName) {
case 'listing':
case 'plaintext':
case 'xmp':
return 'pre'
case 'nobr':
return 'nowrap'
case 'pre':
return props.wrap ? 'pre-wrap' : 'pre'
case 'td':
case 'th':
return props.noWrap ? 'nowrap' : options.whitespace
case 'textarea':
return 'pre-wrap'
default:
return options.whitespace
}
return context.whitespace
}
{
"name": "rehype-minify-whitespace",
"version": "4.0.5",
"version": "5.0.0",
"description": "rehype plugin to collapse whitespace",

@@ -29,15 +29,35 @@ "license": "MIT",

],
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"block.d.ts",
"block.js",
"content.d.ts",
"content.js",
"index.d.ts",
"index.js",
"skippable.d.ts",
"skippable.js"
],
"dependencies": {
"hast-util-embedded": "^1.0.0",
"hast-util-is-element": "^1.0.0",
"hast-util-whitespace": "^1.0.4",
"unist-util-is": "^4.0.0"
"@types/hast": "^2.0.0",
"hast-util-embedded": "^2.0.0",
"hast-util-is-element": "^2.0.0",
"hast-util-whitespace": "^2.0.0",
"unified": "^10.0.0",
"unist-util-is": "^5.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage",
"test": "node --conditions development test.js"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

@@ -21,2 +21,5 @@ <!--This file is generated by `build-packages.js`-->

This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:

@@ -28,2 +31,5 @@

This package exports no identifiers.
The default export is `rehypeMinifyWhitespace`
## Use

@@ -34,6 +40,11 @@

```diff
import {unified} from 'unified'
import rehypeParse from 'rehype-parse'
+import rehypeMinifyWhitespace from 'rehype-minify-whitespace'
import rehypeStringify from 'rehype-stringify'
unified()
.use(require('rehype-parse'))
+ .use(require('rehype-minify-whitespace'))
.use(require('rehype-stringify'))
.use(rehypeParse)
+ .use(rehypeMinifyWhitespace)
.use(rehypeStringify)
.process('<span>some html</span>', function (err, file) {

@@ -48,3 +59,3 @@ console.error(report(err || file))

```sh
rehype input.html --use minify-whitespace > output.html
rehype input.html --use minify-whitespace --output output.html
```

@@ -81,5 +92,5 @@

[build-badge]: https://img.shields.io/travis/rehypejs/rehype-minify.svg
[build-badge]: https://github.com/rehypejs/rehype-minify/workflows/main/badge.svg
[build]: https://travis-ci.org/rehypejs/rehype-minify
[build]: https://github.com/rehypejs/rehype-minify/actions

@@ -108,2 +119,4 @@ [coverage-badge]: https://img.shields.io/codecov/c/github/rehypejs/rehype-minify.svg

[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install

@@ -110,0 +123,0 @@

@@ -1,2 +0,2 @@

module.exports = [
export const skippable = [
'area',

@@ -3,0 +3,0 @@ 'base',

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc