跳转到主要内容

快速无分配HTML压缩器,具有智能空白处理

项目描述

hyperbuild

一个快速的、单次遍历原地HTML压缩器,用Rust编写,具有上下文感知的空白处理。

还支持通过esbuild插件进行JS压缩。

提供以下形式

  • 适用于macOS和Linux的命令行界面。
  • Rust库。
  • 适用于Node.js、Python、Java和Ruby的本地库。

功能

  • 压缩在单次遍历中完成,没有回溯或DOM/AST构建。
  • 处理过程中不分配额外的堆内存,从而提高了性能。
  • 上下文感知的空白处理允许最大程度的压缩,同时保留所需的空间。
  • 经过大型测试套件和广泛的模糊测试,测试良好。

性能

html-minfierminimize相比,Node.js版本的运行速度和有效性,在流行的已压缩网页上运行。有关更多详细信息,请参阅bench文件夹。

Chart showing speed of HTML minifiers Chart showing effectiveness of HTML minifiers

用法

命令行界面

提供适用于x86-64 macOS和Linux的预编译二进制文件。

获取

macOS | Linux

使用

使用--help参数获取更多详细信息。

hyperbuild --src /path/to/src.html --out /path/to/output.min.html

API

Rust
获取
[dependencies]
hyperbuild = { version = "0.2.4", features = ["js-esbuild"] }

使用js-esbuild功能构建需要安装Go编译器,以构建JS压缩器

如果未启用js-esbuild功能,则cfg.minify_js将没有效果。

使用
use hyperbuild::{Cfg, FriendlyError, hyperbuild, hyperbuild_copy, hyperbuild_friendly_error, hyperbuild_truncate};

fn main() {
    let mut code = b"<p>  Hello, world!  </p>".to_vec();
    let cfg = &Cfg {
        minify_js: false,
    };

    // Minifies a slice in-place and returns the new minified length,
    // but leaves any original code after the minified code intact.
    match hyperbuild(&mut code, cfg) {
        Ok(minified_len) => {}
        Err((error_type, error_position)) => {}
    };

    // Creates a vector copy containing only minified code
    // instead of minifying in-place.
    match hyperbuild_copy(&code, cfg) {
        Ok(minified) => {}
        Err((error_type, error_position)) => {}
    };

    // Minifies a vector in-place, and then truncates the
    // vector to the new minified length.
    match hyperbuild_truncate(&mut code, cfg) {
        Ok(()) => {}
        Err((error_type, error_position)) => {}
    };

    // Identical to `hyperbuild` except with FriendlyError instead.
    // `code_context` is a string of a visual representation of the source,
    // with line numbers and position markers to aid in debugging syntax.
    match hyperbuild_friendly_error(&mut code, cfg) {
        Ok(minified_len) => {}
        Err(FriendlyError { position, message, code_context }) => {
            eprintln!("Failed at character {}:", position);
            eprintln!("{}", message);
            eprintln!("{}", code_context);
        }
    };
}
Node.js

hyperbuild 在 npm 上 可用,作为一个 Node.js 原生模块,并支持 Node.js 8 及以上版本。

获取

使用 npm

npm i hyperbuild

使用 Yarn

yarn add hyperbuild
使用
const hyperbuild = require("hyperbuild");

const cfg = { minifyJs: false };
const minified = hyperbuild.minify("<p>  Hello, world!  </p>", cfg);

// Alternatively, minify in place to avoid copying.
const source = Buffer.from("<p>  Hello, world!  </p>", cfg);
hyperbuild.minifyInPlace(source);

hyperbuild 也适用于 TypeScript

import * as hyperbuild from "hyperbuild";
import * as fs from "fs";

const cfg = { minifyJs: false };
const minified = hyperbuild.minify("<p>  Hello, world!  </p>", cfg);
hyperbuild.minifyInPlace(fs.readFileSync("source.html"), cfg);
Java

hyperbuild 通过 JNI 提供,并支持 Java 7 及以上版本。

获取

作为 Maven 依赖添加

<dependency>
  <groupId>in.wilsonl.hyperbuild</groupId>
  <artifactId>hyperbuild</artifactId>
  <version>0.2.4</version>
</dependency>
使用
import in.wilsonl.hyperbuild.Hyperbuild;

Hyperbuild.Configuration cfg = new Hyperbuild.Configuration.Builder()
    .setMinifyJs(false)
    .build();
try {
    String minified = Hyperbuild.minify("<p>  Hello, world!  </p>", cfg);
} catch (Hyperbuild.SyntaxException e) {
    System.err.println(e.getMessage());
}

// Alternatively, minify in place:
assert source instanceof ByteBuffer && source.isDirect();
Hyperbuild.minifyInPlace(source, cfg);
Python

hyperbuild 在 PyPI 上可用,作为一个 原生模块,并支持 CPython (默认 Python 解释器) 3.5 及以上版本。

获取

将 PyPI 项目作为依赖项添加,并使用 pippipenv 进行安装。

使用
import hyperbuild

try:
    minified = hyperbuild.minify("<p>  Hello, world!  </p>", minify_js=False)
except SyntaxError as e:
    print(e)
Ruby

hyperbuild 已发布于 RubyGems,作为一个适用于 macOS 和 Linux 的 原生模块,并支持 Ruby 2.5 及以上版本。

获取

将库作为依赖项添加到 Gemfile*.gemspec

使用
require 'hyperbuild'

print Hyperbuild.minify("<p>  Hello, world!  </p>", { :minify_js => false })

压缩

空白字符

hyperbuild 具有高级的上下文感知空白字符压缩功能,例如:

  • precode 中保留空白字符,它们对空白字符敏感。
  • 在内容标签中裁剪和合并空白字符,因为渲染时空白字符已经被合并。
  • 在布局标签中移除空白字符,这允许使用内联布局同时保持格式化代码。

方法

有三种空白字符压缩方法。在处理文本内容时,hyperbuild 根据包含元素选择使用哪种方法。

合并空白字符

适用对象:空白字符敏感 元素之外的所有元素。

将文本节点中的空白字符序列减少到单个空格 (U+0020)。

之前之后
<p>↵
··The·quick·brown·fox↵
··jumps·over·the·lazy↵
··dog.↵
</p>
<p>·The·quick·brown·fox·jumps·over·the·lazy·dog.·</p>
移除整个空白字符

适用对象:空白字符敏感内容内容优先格式化 元素之外的所有元素。

移除位于标签之间且只包含空白字符的任何文本节点。

之前之后
<ul>↵
··<li>A</li>↵
··<li>B</li>↵
··<li>C</li></ul>
<ul>↵
··<li>A</li><li>B</li><li>C</li></ul>
裁剪空白字符

适用对象:空白字符敏感格式化 元素之外的所有元素。

移除任何标签的前导/尾随空白字符。

之前之后
<p>↵
··Hey,·I·<em>just</em>·found↵
··out·about·this·<strong>cool</strong>·website!↵
··<sup>[1]</sup></p>
<p>Hey,·I·<em>just</em>·found↵
··out·about·this·<strong>cool</strong>·website!↵
··<sup>[1]</sup></p>

元素类型

hyperbuild 根据假设的元素使用方式来识别元素。通过这些假设,它可以应用最佳空白字符压缩策略。

分组 元素 期望子元素
格式化 astrong 和其他元素 格式化元素,文本。
内容 h1p 和其他元素 格式化元素,文本。
布局 divul 和其他元素 布局元素,内容元素。
内容优先 labelli 和其他元素 类似于内容,但可能只有一个子元素时可以作为布局。
格式化元素

空白字符被合并。

格式化元素通常是内联元素,它们围绕内容元素中的某些文本进行包装,因此其空白字符不会被裁剪,因为它们可能是内容的一部分。

内容元素

空白字符被裁剪和合并。

内容元素通常代表一个连续的完整内容单元,如段落。因此,空白字符很重要,但它们的序列很可能是由于格式化导致的。

之前
<p>↵
··Hey,·I·<em>just</em>·found↵
··out·about·this·<strong>cool</strong>·website!↵
··<sup>[1]</sup></p>
之后
<p>Hey,·I·<em>just</em>·found·out·about·this·<strong>cool</strong>·website!·<sup>[1]</sup></p>
布局元素

空白字符被裁剪和合并。整个空白字符被移除。

这些元素应只包含其他元素,不包含任何文本。这使得可以移除整个空白字符,这在使用 display: inline-block 时非常有用,这样元素之间的空白字符(例如缩进)就不会改变布局和样式。

之前
<ul>↵
··<li>A</li>↵
··<li>B</li>↵
··<li>C</li></ul>
之后
<ul><li>A</li><li>B</li><li>C</li></ul>
内容优先元素

空白字符被裁剪和合并。

这些元素通常像内容元素一样,但偶尔也会用作具有一个子元素的布局元素。整个空白不会删除,因为它可能包含内容,但作为布局使用是可以的,因为只有一个子元素,空白会被修剪。

之前
<li>↵
··<article>↵
····<section></section>↵
····<section></section>↵
··</article></li>
之后
<li><article><section></section><section></section></article></li>

标签

可选的闭合标签会被删除。

属性

属性值中的任何实体都会解码,然后计算并使用值的简短表示。

  • 双引号,其中的任何 " 都会被编码。
  • 单引号,其中的任何 ' 都会被编码。
  • 未加引号,其中的 "/' 的第一个字符(如果适用),> 的最后一个字符(如果适用),以及任何空白都会被编码。

classd 属性在其解码后的空白会被修剪并合并。

布尔属性值会被删除。如果其值在处理后的结果为空或默认值,则一些其他属性会被完全删除。

script 标签上的 type 属性,其值等于一个 JavaScript MIME 类型,会被删除。

如果属性值在处理后的结果为空,则除了名称之外的所有内容都会被完全删除(即没有 =),因为空属性隐式地与具有空字符串值的属性相同。

如果可能,会在属性之间删除空白。

实体

如果有效,实体会被解码(参见相关解析部分),并且其解码字符作为 UTF-8 的长度更短或相等。

不引用有效 Unicode 标量值 的数字实体会被替换为 替换字符

如果解码后无意中形成了一个实体,则编码前导的 ampersand,例如 &&#97;&#109;&#112;; 变为 &ampamp;。这是因为它等于或短于所有其他字符的实体表示形式([&#a-zA-Z0-9;]),并且没有其他以 amp 开头的冲突实体名称。

在删除注释后可能会得到一个无意中的实体,例如 &am<!-- -->p

在解码后的文本中的左尖括号(<)会被编码为 &LT,如果可能的话,否则为 &LT;

注释

注释会被删除。

忽略

感叹号、处理指令和空元素不会被删除,因为假设它们的声明有特殊原因。

解析

仅支持 UTF-8/ASCII 编码的 HTML 代码。

由于性能和代码复杂性的原因,hyperbuild 不进行语法检查或标准强制。

例如,这意味着自闭合标签、声明多个 <body> 元素、使用错误的属性名称和值或写入 <br>alert('');</br> 这样的内容都不是错误。

然而,有一些语法要求是为了速度和合理性。

标签

标签名是区分大小写的。例如,这意味着 P 不会被视为内容元素,bR 不会被视为空标签,并且 Script 的内容不会被解析为 JavaScript。

标签不得被省略。空标签不得有单独的闭合标签,例如 </input>

实体

格式良好的实体会被解码,包括在属性值中。

它们会被解释为代表其解码值的字符。这意味着 &#9; 被视为空白字符,可以被最小化。

格式不正确的实体会被字面地解释为一组字符。

如果命名实体按照规范是一个无效引用,则被视为格式不正确。

不引用有效Unicode标量值的数字字符引用被认为是格式错误的。

属性

反引号(`)不是有效的引号,也不会被当作引号处理。然而,在Internet Explorer中,反引号是有效的属性值引号。

某些属性的特别处理需要区分大小写的名称和值。例如,CLASS不会被识别为压缩的属性,且在<script>上的type="Text/JavaScript"也不会被删除。

脚本和样式

scriptstyle标签必须分别以</script></style>关闭(区分大小写)。

hyperbuild不处理转义和双重转义的脚本内容。

问题和贡献

欢迎拉取请求和任何贡献!

如果hyperbuild做了预期之外的事情,误解了某些语法,或者错误地保留了/删除了某些代码,请在这里提出问题,并附上一些相关的代码,以便重现和调查问题。

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

hyperbuild-0.2.4.tar.gz (36.6 MB 查看哈希值

上传时间:

构建分布

hyperbuild-0.2.4-py3-none-any.whl (36.9 MB 查看哈希值

上传时间: Python 3

由以下赞助

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面