Implement support for WASM and Deno

This commit is contained in:
Wilson Lin 2022-06-22 00:43:09 +10:00
parent cc0d78b9da
commit 3e20d50eeb
21 changed files with 278 additions and 65 deletions

View File

@ -6,7 +6,6 @@ This folder contains various GitHub Actions that are run upon every new version
GitHub currently doesn't provide ARM64 macOS and Linux runners, so we run self-hosted versions. Jobs run on self-hosted machines aren't isolated (e.g. files created during a job run persist on the real filesystem), and most @actions/* don't have ARM64 builds yet, so self-hosted machines should have tools preinstalled before starting the GitHub Actions runner. These include:
- Go
- Rust
- Node.js
- Python/pyenv

View File

@ -24,11 +24,6 @@ jobs:
profile: minimal
default: true
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: '^1.14.0'
- name: Run prebuild steps
shell: bash
run: bash ./prebuild.sh

View File

@ -54,16 +54,6 @@ jobs:
profile: minimal
default: true
- name: Set up GCC (Windows)
if: runner.os == 'Windows'
run: .\.github\workflows\gcc.ps1
- name: Set up Go
if: runner.name != 'macos-arm64'
uses: actions/setup-go@v2
with:
go-version: '^1.14.0'
- name: Run prebuild steps
shell: bash
run: bash ./prebuild.sh

50
.github/workflows/deno.yml vendored Normal file
View File

@ -0,0 +1,50 @@
name: Build and publish Deno package
on:
push:
tags:
- 'v*'
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
- name: Get version
id: version
shell: bash
run: echo ::set-output name=VERSION::"$([[ "$GITHUB_REF" == refs/tags/v* ]] && echo ${GITHUB_REF#refs/tags/v} || echo '0.0.0')"
- name: Set up Rust
if: runner.name != 'macos-arm64'
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
default: true
- name: Run prebuild steps
shell: bash
run: bash ./prebuild.sh
- name: Install wasm-pack
working-directory: ./wasm
shell: bash
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Build native module
working-directory: ./wasm
shell: bash
run: TARGET=web ./build
- name: Install B2 CLI
run: |
wget -O b2 https://github.com/Backblaze/B2_Command_Line_Tool/releases/latest/download/b2-linux
chmod +x b2
- name: Upload to B2
run: |
./b2 authorize-account ${{ secrets.CICD_CLI_B2_KEY_ID }} ${{ secrets.CICD_CLI_B2_APPLICATION_KEY }}
./b2 sync --compareVersions size --delete wasm/pkg/ b2://${{ secrets.CICD_CLI_B2_BUCKET_NAME }}/minify-html/deno/${{ steps.version.outputs.VERSION }}/

View File

@ -16,11 +16,6 @@ jobs:
profile: minimal
default: true
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: '^1.14.0'
- name: Run prebuild steps
shell: bash
run: bash ./prebuild.sh

View File

@ -1,4 +0,0 @@
Invoke-WebRequest 'https://wilsonl.in/TDM-GCC-64.7z' -OutFile C:\gcc.7z
7z x C:\gcc.7z -oC:\
$oldpath = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value "C:\TDM-GCC-64\bin;$oldpath"

View File

@ -32,15 +32,6 @@ jobs:
profile: minimal
default: true
- name: Set up GCC (Windows)
if: runner.os == 'Windows'
run: .\.github\workflows\gcc.ps1
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: '^1.14.0'
- name: Run prebuild steps
shell: bash
run: bash ./prebuild.sh
@ -54,7 +45,7 @@ jobs:
with:
name: ${{ matrix.ARCH }}
path: ./java/target/rust/release/${{ matrix.FILE }}
package:
runs-on: ubuntu-18.04
needs: build

View File

@ -34,16 +34,6 @@ jobs:
profile: minimal
default: true
- name: Set up GCC (Windows)
if: runner.os == 'Windows'
run: .\.github\workflows\gcc.ps1
- name: Set up Go
if: runner.name != 'macos-arm64'
uses: actions/setup-go@v2
with:
go-version: '^1.14.0'
- name: Run prebuild steps
shell: bash
run: bash ./prebuild.sh

View File

@ -44,16 +44,6 @@ jobs:
profile: minimal
default: true
- name: Set up GCC (Windows)
if: runner.os == 'Windows'
run: .\.github\workflows\gcc.ps1
- name: Set up Go
if: runner.name != 'macos-arm64'
uses: actions/setup-go@v2
with:
go-version: '^1.14.0'
- name: Run prebuild steps
shell: bash
run: bash ./prebuild.sh

View File

@ -65,15 +65,6 @@ jobs:
profile: minimal
default: true
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: '^1.14.0'
- name: Set up GCC (Windows)
if: runner.os == 'Windows'
run: .\.github\workflows\gcc.ps1
- name: Run prebuild steps
shell: bash
run: bash ./prebuild.sh

58
.github/workflows/wasm.yml vendored Normal file
View File

@ -0,0 +1,58 @@
name: Build and publish WASM package
on:
push:
tags:
- 'v*'
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
- name: Get version
id: version
shell: bash
run: echo ::set-output name=VERSION::"$([[ "$GITHUB_REF" == refs/tags/v* ]] && echo ${GITHUB_REF#refs/tags/v} || echo '0.0.0')"
- name: Set up Rust
if: runner.name != 'macos-arm64'
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
default: true
- name: Run prebuild steps
shell: bash
run: bash ./prebuild.sh
- name: Install wasm-pack
working-directory: ./wasm
shell: bash
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Build native module
working-directory: ./wasm
shell: bash
run: TARGET=bundle ./build
- name: Set up Node.js
uses: actions/setup-node@master
with:
node-version: 17.x
- name: Pack and publish package
working-directory: ./wasm/pkg
run: |
cat << 'EOF' > .npmrc
package-lock=false
//registry.npmjs.org/:_authToken=${{ secrets.NPM_AUTH_TOKEN }}
EOF
# npm refuses to work with symlinks.
cp ../../README.md .
if [[ "${{ steps.version.outputs.VERSION }}" != "0.0.0" ]]; then
npm publish --access public
fi

View File

@ -1,11 +1,13 @@
<h1>
minify-html
<img width="24" src="https://wilsonl.in/minify-html/icon/cli.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/deno.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/java.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/nodejs.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/python.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/ruby.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/rust.png">
<img width="24" src="https://wilsonl.in/minify-html/icon/wasm.png">
</h1>
A Rust HTML minifier meticulously optimised for speed and effectiveness, with bindings for other languages.
@ -64,6 +66,30 @@ Check out the [docs](https://docs.rs/minify-html) for API and usage examples.
</details>
<details>
<summary><img width="24" src="https://wilsonl.in/minify-html/icon/deno.png"> <strong>Deno</strong></summary>
- Package: [https://wilsonl.in/minify-html/deno/0.8.1/index.js]
- Binding: [WASM](https://webassembly.org/)
- Platforms: All
### Use
```ts
import init, {minify} from "https://wilsonl.in/minify-html/deno/0.8.1/index.js";
const encoder = new TextEncoder();
const decoder = new TextDecoder();
await init();
const minified = decoder.decode(minify(encoder.encode("<p> Hello, world! </p>"), { keep_spaces_between_attributes: true, keep_comments: true }));
```
All [`Cfg` fields](https://docs.rs/minify-html/latest/minify_html/struct.Cfg.html) are available as snake_case properties on the object provided as the second argument; if any are not set, they default to `false`.
</details>
<details>
<summary><img width="24" src="https://wilsonl.in/minify-html/icon/nodejs.png"> <strong>Node.js</strong></summary>
@ -183,6 +209,32 @@ All [`Cfg` fields](https://docs.rs/minify-html/latest/minify_html/struct.Cfg.htm
</details>
<details>
<summary><img width="24" src="https://wilsonl.in/minify-html/icon/wasm.png"> <strong>WASM</strong></summary>
- Package: [@minify-html/wasm](https://npmjs.org/package/@minify-html/wasm)
- Binding: [WASM](https://webassembly.org/)
- Platforms: All
A bundler may be required to use the WebAssembly module, see [this](https://rustwasm.github.io/wasm-bindgen/reference/deployment.html#bundlers) for more details.
### Use
```ts
import init, {minify} from "https://wilsonl.in/minify-html/deno/0.8.1/index.js";
const encoder = new TextEncoder();
const decoder = new TextDecoder();
await init();
const minified = decoder.decode(minify(encoder.encode("<p> Hello, world! </p>"), { keep_spaces_between_attributes: true, keep_comments: true }));
```
All [`Cfg` fields](https://docs.rs/minify-html/latest/minify_html/struct.Cfg.html) are available as snake_case properties on the object provided as the second argument; if any are not set, they default to `false`.
</details>
## Minification
Note that some of the minification done can result in HTML that will not pass validation, but remain interpreted and rendered correctly by the browser; essentially, the laxness of the browser is taken advantage of for better minification. To prevent this, refer to these configuration options:

1
format
View File

@ -20,6 +20,7 @@ for dir in \
ruby \
rust/main \
rust/onepass \
wasm \
; do
pushd "$dir" >/dev/null
cargo fmt

BIN
icon/deno.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
icon/wasm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -105,6 +105,7 @@ for (const f of [
"java/Cargo.toml",
"python/Cargo.toml",
"ruby/Cargo.toml",
"wasm/Cargo.toml",
]) {
replaceInFile(
f,
@ -124,7 +125,7 @@ for (const f of ["README.md"]) {
for (const f of ["README.md"]) {
replaceInFile(
f,
/(wilsonl\.in\/minify-html\/bin\/)\d+\.\d+\.\d+/g,
/(wilsonl\.in\/minify-html\/(?:bin|deno)\/)\d+\.\d+\.\d+/g,
`$1${NEW_VERSION}`
);
}

6
wasm/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
/target
**/*.rs.bk
Cargo.lock
bin/
pkg/
wasm-pack.log

37
wasm/Cargo.toml Normal file
View File

@ -0,0 +1,37 @@
[package]
name = "index"
publish = false
version = "0.8.1"
authors = ["Wilson Lin <code@wilsonl.in>"]
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies]
js-sys = "0.3.58"
minify-html = { path = "../rust/main" }
wasm-bindgen = "0.2.63"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
#
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = { version = "0.4.5", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.3.13"
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"

22
wasm/build Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -Eeuxo pipefail
pushd "$(dirname "$0")" >/dev/null
target="${TARGET:-bundler}"
rm -rf pkg
wasm-pack build --target "$target"
pushd pkg
# `bundler` is for WASM/npm, `web` is for Deno.
if [[ "$target" = "bundler" ]]; then
# Use -i.tmp to support macOS and Linux.
sed -i.tmp -E 's%"name": "index"%"name": "@minify-html/'$pkg_suffix'"%' package.json
rm package.json.tmp .gitignore
elif [[ "$target" = "web" ]]; then
rm package.json .gitignore
fi
popd
popd >/dev/null

39
wasm/src/lib.rs Normal file
View File

@ -0,0 +1,39 @@
mod utils;
use js_sys::Reflect;
use wasm_bindgen::prelude::*;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
macro_rules! get_prop {
($cfg:expr, $x:literal) => {
Reflect::get($cfg, &JsValue::from_str($x))
.ok()
.and_then(|p| p.as_bool())
.unwrap_or(false)
};
}
#[wasm_bindgen]
pub fn minify(code: &[u8], cfg: &JsValue) -> Vec<u8> {
let cfg = minify_html::Cfg {
do_not_minify_doctype: get_prop!(cfg, "do_not_minify_doctype"),
ensure_spec_compliant_unquoted_attribute_values: get_prop!(
cfg,
"ensure_spec_compliant_unquoted_attribute_values"
),
keep_closing_tags: get_prop!(cfg, "keep_closing_tags"),
keep_html_and_head_opening_tags: get_prop!(cfg, "keep_html_and_head_opening_tags"),
keep_spaces_between_attributes: get_prop!(cfg, "keep_spaces_between_attributes"),
keep_comments: get_prop!(cfg, "keep_comments"),
minify_css: get_prop!(cfg, "minify_css"),
minify_js: get_prop!(cfg, "minify_js"),
remove_bangs: get_prop!(cfg, "remove_bangs"),
remove_processing_instructions: get_prop!(cfg, "remove_processing_instructions"),
};
minify_html::minify(code, &cfg)
}

10
wasm/src/utils.rs Normal file
View File

@ -0,0 +1,10 @@
pub fn set_panic_hook() {
// When the `console_error_panic_hook` feature is enabled, we can call the
// `set_panic_hook` function at least once during initialization, and then
// we will get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
}