60 个版本
0.2.4 | 2020年7月11日 |
---|---|
0.2.3 | 2020年7月10日 |
0.1.12 | 2020年7月10日 |
0.1.3 | 2020年6月19日 |
0.0.4 | 2019年12月30日 |
#173 in 压缩
156 个月下载量
用于 pagong
2MB
47K SLoC
hyperbuild
A fast one-pass in-place HTML minifier written in Rust with context-aware whitespace handling.
Also supports JS minification by plugging into esbuild.
可用形式
- macOS 和 Linux 的 CLI。
- Rust 库。
- Node.js、Python、Java 和 Ruby 的原生库。
特性
- 一次遍历完成压缩,没有回溯或 DOM/AST 构建。
- 处理过程中不分配额外的堆内存,从而提高性能。
- 上下文感知的空白处理允许在保留所需空格的同时实现最大压缩。
- 通过大型测试套件和广泛的 模糊测试 进行充分测试。
性能
Node.js 版本的速度和有效性与 html-minfier 和 minimize 相比,运行在流行的已压缩网页上。更多详情请参阅 bench 文件夹。
用法
CLI
预编译的二进制文件适用于 x86-64 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 项目添加为依赖项,并使用 pip
或 pipenv
进行安装。
使用
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 具有高级上下文感知空格压缩功能,可以进行如下操作:
- 在
pre
和code
中保持空格不变,这些标签对空格敏感。 - 在内容标签中剪除和合并空格,因为渲染时空格总是会合并。
- 在布局标签中移除空格,这允许使用内联布局同时保留格式化代码。
方法
有三种空格压缩方法。当处理文本内容时,hyperbuild 会根据包含的元素选择使用哪种方法。
压缩空格
适用于:任何元素,除了 对空格敏感 的元素。
将文本节点中的连续空格字符序列缩减为一个空格(U+0020)。
之前 | 之后 |
---|---|
|
|
销毁整个空格
移除标签之间的只包含空格字符的任何文本节点。
之前 | 之后 |
---|---|
|
|
修剪空格
移除任何标签的前导/尾随空格。
之前 | 之后 |
---|---|
|
|
元素类型
hyperbuild 根据假设的元素使用方式来识别元素。通过这些假设,它可以应用最佳的空格压缩策略。
组 | 元素 | 预期子元素 |
---|---|---|
格式化 | a 、strong 和其他元素 |
格式化元素,文本。 |
内容 | h1 、p 和其他元素 |
格式化元素,文本。 |
布局 | div 、ul 和其他元素 |
布局元素,内容元素。 |
内容优先 | label 、li 和其他元素 |
类似于内容,但可能只有一个子元素作为布局。 |
格式化元素
空格被压缩。
格式化元素通常是内联元素,它们围绕内容元素中的某些文本,因此其空格不会被修剪,因为它们可能是内容的一部分。
内容元素
空格被修剪和压缩。
内容元素通常代表连续且完整的单位内容,如段落。因此,空格很重要,但连续的空格序列很可能是由于格式化引起的。
之前
<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>
布局元素
空格被修剪和压缩。整个空格被移除。
这些元素应只包含其他元素,不包含任何文本。这使得可以删除整个空白,这在使用: inline-block
时非常有用,因为元素之间的空白(例如缩进)不会改变布局和样式。
之前
<ul>↵
··<li>A</li>↵
··<li>B</li>↵
··<li>C</li>↵
</ul>
之后
<ul><li>A</li><li>B</li><li>C</li></ul>
内容优先元素
空格被修剪和压缩。
这些元素通常像内容元素,但偶尔也会用作具有一个子元素的布局元素。整个空白不会删除,因为它可能包含内容,但在用作布局时是OK的,因为只有一个子元素,空白会被修剪。
之前
<li>↵
··<article>↵
····<section></section>↵
····<section></section>↵
··</article>↵
</li>
之后
<li><article><section></section><section></section></article></li>
标签
可选的闭合标签被删除。
属性
属性值中的任何实体都会解码,然后计算并使用值的最短表示。
- 双引号,任何
"
都会被编码。 - 单引号,任何
'
都会被编码。 - 未引用的,任何
"
/'
的第一个字符(如果适用),>
的最后一个字符(如果适用),以及任何空白都会被编码。
class
和d
属性(在解码之后)的空白会被修剪并合并。
布尔属性的值会被删除。如果其值在处理之后为空或默认值,一些其他属性也会被完全删除。
对于值为等于JavaScript MIME类型的script
标签上的type
属性会被删除。
如果属性值在处理之后为空,除了名称之外的所有内容都会被完全删除(即没有=
),因为空属性隐式地与具有空字符串值的属性相同。
如果可能,会删除属性之间的空格。
实体
如果有效,会解码实体(见相关的解析部分),并且其解码的UTF-8字符长度较短或相等。
不引用有效Unicode标量值的数字实体会被替换为替换字符。
如果解码后意外形成了实体,会编码前导的 ampersand,例如&amp;
变为&amp;
。这样做是因为&
等于或短于字符实体中所有其他字符的实体表示([&#a-zA-Z0-9;]
),并且没有其他以amp
开头的冲突实体名称。
在移除注释后,可能会意外地得到一个实体,例如:&am<!-- -->p
。
在文本中的任何解码后,左箭头会被编码为<
,如果可能的话;否则为<
。
注释
注释已被移除。
忽略
感叹号、处理指令和空元素不会被移除,因为这假定它们的声明有特殊原因。
解析
仅支持UTF-8/ASCII编码的HTML代码。
出于性能和代码复杂性的考虑,hyperbuild不支持语法检查或标准执行。
例如,这意味着自闭合标签、声明多个<body>
元素、使用不正确的属性名称和值或编写类似于<br>alert('');</br>
的内容都不是错误。
然而,有一些语法要求是为了速度和合理性。
标签
标签名称区分大小写。例如,这意味着P
不会被视为内容元素,bR
不会被考虑为空标签,而Script
的内容不会被解析为JavaScript。
标签不得省略。空标签不得有单独的结束标签,例如:</input>
。
实体
有效的实体会被解码,包括在属性值中。
它们被解释为表示其解码值的字符。这意味着	
被视为空白字符,可以压缩。
格式错误的实体被字面地解释为字符序列。
如果命名实体的引用不符合规范,则被视为格式错误。
不引用有效Unicode Scalar Value的数字字符引用被视为格式错误。
属性
反引号(`
)不是有效的引号,也不会被解释为引号。然而,在Internet Explorer中,反引号是有效的属性值引号。
某些属性的特别处理需要区分大小写的名称和值。例如,CLASS
不会被视为压缩的属性,而type="Text/JavaScript"
在<script>
上不会被移除。
脚本和样式
script
和 style
标签必须分别用 </script>
和 </style>
关闭(区分大小写)。
hyperbuild 不处理 转义和双重转义的 脚本内容。
问题和贡献
欢迎提出拉取请求和任何贡献!
如果 hyperbuild 做了一些意外的事情,误解了一些语法,或者错误地保留/删除了一些代码,请 提出一个问题,并提供一些相关的代码,这些代码可以用来重现和调查问题。
依赖项
约 175-780KB
约 16K SLoC