Skip to content

Commit 648faf7

Browse files
authored
feat(formatjs): Support hash encodings in id interpolation pattern (#438)
FormatJs Babel plugin allows using any binary-to-text encoding supported by NodeJS Buffer. Currently, this includes "base64", "base64url", and "hex". See https://nodejs.org/api/buffer.html#buffers-and-character-encodings. Add support for "hex" and "base64url" encodings. Use "hex" by default to match the behavior of the Babel plugin. - Fixes #401
1 parent 006e96c commit 648faf7

File tree

5 files changed

+57
-7
lines changed

5 files changed

+57
-7
lines changed

.changeset/tired-streets-grab.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@swc/plugin-formatjs": minor
3+
---
4+
5+
add support for hex and base64url digest encodings in idInterpolationPattern placeholders

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/formatjs/__tests__/wasm.test.ts

+26
Original file line numberDiff line numberDiff line change
@@ -224,4 +224,30 @@ describe("formatjs swc plugin", () => {
224224

225225
expect(output).toMatch(/id: "zL\/jyT\"/);
226226
});
227+
228+
it("should be able to use different encodings in interpolation", async () => {
229+
const input = `
230+
import { FormattedMessage } from 'react-intl';
231+
232+
export function Greeting() {
233+
return (
234+
<FormattedMessage
235+
defaultMessage="Hello, World!"
236+
description="Greeting message"
237+
/>
238+
);
239+
}
240+
`;
241+
242+
const hexOutput = await transformCode(input, {
243+
idInterpolationPattern: "[sha512:contenthash:hex:9]",
244+
});
245+
246+
const base64UrlOutput = await transformCode(input, {
247+
idInterpolationPattern: "[sha512:contenthash:base64url:12]",
248+
});
249+
250+
expect(hexOutput).toMatch(/id: "[0-9a-f]{9}"/);
251+
expect(base64UrlOutput).toMatch(/id: "[a-zA-Z0-9-_]{12}"/);
252+
});
227253
});

packages/formatjs/transform/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ custom_transform = []
1616
[dependencies]
1717
base64ct = { workspace = true, features = ["alloc"] }
1818
digest = { workspace = true }
19+
hex = { workspace = true }
1920
once_cell = { workspace = true }
2021
regex = { workspace = true }
2122
serde = { workspace = true, features = ["derive"] }

packages/formatjs/transform/src/lib.rs

+24-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{
44
path::Path,
55
};
66

7-
use base64ct::{Base64, Encoding};
7+
use base64ct::{Base64, Base64UrlUnpadded, Encoding};
88
use digest::DynDigest;
99
use once_cell::sync::Lazy;
1010
use regex::{Captures, Regex as Regexp};
@@ -491,13 +491,14 @@ fn interpolate_name(filename: &str, interpolate_pattern: &str, content: &str) ->
491491
};
492492

493493
let mut url = interpolate_pattern.to_string();
494-
let r = Regexp::new(r#"\[(?:([^:\]]+):)?(?:hash|contenthash)(?::([a-z]+\d*))?(?::(\d+))?\]"#)
495-
.unwrap();
494+
let r =
495+
Regexp::new(r#"\[(?:([^:\]]+):)?(?:hash|contenthash)(?::([a-z][a-z0-9]*))?(?::(\d+))?\]"#)
496+
.unwrap();
496497

497498
url = r
498499
.replace(url.as_str(), |cap: &Captures| {
499500
let hash_type = cap.get(1);
500-
// let digest_type = cap.get(2);
501+
let digest_encoding_type = cap.get(2);
501502
let max_length = cap.get(3);
502503

503504
// TODO: support more hash_types than sha1 and sha512
@@ -507,12 +508,28 @@ fn interpolate_name(filename: &str, interpolate_pattern: &str, content: &str) ->
507508
};
508509
hasher.update(content.as_bytes());
509510
let hash = hasher.finalize();
510-
let base64_hash = Base64::encode_string(&hash);
511+
let encoded_hash = match digest_encoding_type.map(|m| m.as_str()) {
512+
Some("base64") => Base64::encode_string(&hash),
513+
Some("base64url") => Base64UrlUnpadded::encode_string(&hash),
514+
Some("hex") | None => hex::encode(&hash),
515+
Some(other) => {
516+
swc_core::plugin::errors::HANDLER.with(|handler| {
517+
handler.warn(&format!(
518+
"[React Intl] Unsupported encoding type `{}` in \
519+
`idInterpolationPattern`, must be one of `hex`, `base64`, or \
520+
`base64url`.",
521+
other
522+
))
523+
});
524+
525+
hex::encode(&hash)
526+
}
527+
};
511528

512529
if let Some(max_length) = max_length {
513-
base64_hash[0..max_length.as_str().parse::<usize>().unwrap()].to_string()
530+
encoded_hash[0..max_length.as_str().parse::<usize>().unwrap()].to_string()
514531
} else {
515-
base64_hash
532+
encoded_hash
516533
}
517534
})
518535
.to_string();

0 commit comments

Comments
 (0)