Skip to content

Commit 1a0006f

Browse files
Mad-Katmartindisch
andauthored
perf(swc): switch to xxHash32 and Base62 (jantimon#23)
- Replaced `sha3_256` with `xxHash32` as it produces a smaller output (32bit vs 256bit) - Replaced Base64 with Base62 as it generates [a-zA-Z0-9] and removed the obsolete replacement of certain characters Co-authored-by: Luca Schneider <luca.schneider@digitecgalaxus.ch> Co-authored-by: Martin Disch <martindisch@gmail.com>
1 parent 813a192 commit 1a0006f

File tree

6 files changed

+46
-70
lines changed

6 files changed

+46
-70
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@
6363
"@babel/preset-typescript": "^7.18.6",
6464
"@babel/runtime": "^7.20.1",
6565
"@babel/types": "^7.20.2",
66-
"@swc/core": "1.2.211",
67-
"@swc/jest": "^0.2.21",
66+
"@swc/core": "1.3.27",
67+
"@swc/jest": "^0.2.24",
6868
"@types/jest": "^29.2.3",
6969
"jest": "^29.3.1",
7070
"terser": "5.15.1",

swc/Cargo.lock

Lines changed: 19 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

swc/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ swc_core = {version = "0.58.0", features = [
2424
"ecma_ast",
2525
"common",
2626
]}
27-
sha3 = "0.10.6"
28-
base64 = "0.21.0"
2927
pathdiff = "0.2.1"
3028
regex = "1.7.1"
3129
lazy_static = "1.4.0"
30+
xxhash-rust = { version = "0.8.6", features = ["xxh32"] }
31+
base62 = "2.0.2"
3232

3333
[dev-dependencies]
3434
swc_ecma_parser = "0.124.2"

swc/src/hash.rs

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,31 @@
1-
use base64::{engine::general_purpose, Engine as _};
2-
use sha3::{Digest, Sha3_256};
1+
use xxhash_rust::xxh32::xxh32;
32

4-
/// Creates a CSS identifier of the given length using SHA3.
5-
///
6-
/// In CSS, identifiers (including element names, classes, and IDs in
7-
/// selectors) can contain only the characters \[a-zA-Z0-9\] and ISO 10646
8-
/// characters U+00A0 and higher, plus the hyphen (-) and the underscore (_);
9-
/// they cannot start with a digit, two hyphens, or a hyphen followed by a
10-
/// digit.
11-
pub fn hash(data: impl AsRef<[u8]>, length: usize) -> String {
12-
let mut hasher = Sha3_256::new();
13-
hasher.update(data);
14-
let hash_base64 = general_purpose::URL_SAFE_NO_PAD.encode(hasher.finalize());
15-
16-
let first_char = hash_base64.chars().next().unwrap();
17-
// Ensure that the identifier starts with [_a-zA-Z]
18-
let first_char = if first_char.is_ascii_digit() || first_char == '-' {
19-
'_'
20-
} else {
21-
first_char
22-
};
23-
let css_safe_string = format!("{first_char}{}", &hash_base64[1..length]);
24-
25-
css_safe_string
3+
/// Creates a CSS identifier using xxHash (https://cyan4973.github.io/xxHash/).
4+
/// Shortened to base62 (https://en.wikipedia.org/wiki/Base62) to avoid invalid characters.
5+
pub fn hash(data: &str) -> String {
6+
let hash = xxh32(data.as_bytes(), 0);
7+
base62::encode(hash)
268
}
279

2810
#[cfg(test)]
2911
mod tests {
3012
use super::*;
3113

3214
#[test]
33-
fn hey() {
34-
assert_eq!(hash("hey", 5), "XldIH");
15+
fn short() {
16+
assert_eq!(hash("hey"), "2Hy69D");
17+
}
18+
19+
#[test]
20+
fn longer() {
21+
assert_eq!(hash("hey how are you doing?"), "34D1Ek");
3522
}
3623

3724
#[test]
38-
fn hey_long() {
39-
assert_eq!(hash("hey", 15), "XldIHZZJaTy1TcB");
25+
fn longest() {
26+
assert_eq!(
27+
hash("hey how are you doing? I am doing fine, thanks for asking."),
28+
"1XQ5hm"
29+
);
4030
}
4131
}

swc/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use hash::hash;
2626
#[serde(rename_all = "camelCase")]
2727
#[serde(deny_unknown_fields)]
2828
pub struct Config {
29-
/// Prefix variables with a readable name, e.g. `primary--1isauia0`.
29+
/// Prefix variables with a readable name, e.g. `primary--2Hy69D0`.
3030
#[serde(default = "bool::default")]
3131
pub display_name: bool,
3232
/// The hash for a css-variable depends on the file name including createVar().
@@ -175,7 +175,7 @@ pub fn process_transform(program: Program, metadata: TransformPluginProgramMetad
175175
.get_context(&TransformPluginMetadataContextKind::Filename)
176176
.expect("failed to get filename");
177177
let deterministic_path = relative_posix_path(&config.base_path, &file_name);
178-
let hashed_filename = hash(deterministic_path, 5);
178+
let hashed_filename = hash(&deterministic_path);
179179

180180
program.fold_with(&mut as_folder(TransformVisitor::new(
181181
config,

test/swc/swc.test.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ describe("createVar", () => {
55
const foo = createVar();
66
const bar = createVar();
77
const baz = createVar();
8-
expect(foo.name).toMatchInlineSnapshot(`"--foo--_llxb0"`);
9-
expect(bar.name).toMatchInlineSnapshot( `"--bar--_llxb1"`);
10-
expect(baz.name).toMatchInlineSnapshot( `"--baz--_llxb2"`);
8+
expect(foo.name).toMatchInlineSnapshot(`"--foo--2hzhhy0"`);
9+
expect(bar.name).toMatchInlineSnapshot( `"--bar--2hzhhy1"`);
10+
expect(baz.name).toMatchInlineSnapshot( `"--baz--2hzhhy2"`);
1111
});
12-
1312
});

0 commit comments

Comments
 (0)