Summary
Perry implements Unicode normalization for the valid String.prototype.normalize() forms, but invalid form values silently fall back to NFC. Node throws a RangeError when the supplied form is anything other than "NFC", "NFD", "NFKC", or "NFKD".
Node behavior
Local Node v25.9.0 probe:
undefined="é":e9
NFC="é":e9
NFD="é":65+301
NFKC="é":e9
NFKD="é":65+301
BAD=RangeError:The normalization form should be one of NFC, NFD, NFKC, NFKD.
null=RangeError:The normalization form should be one of NFC, NFD, NFKC, NFKD.
=RangeError:The normalization form should be one of NFC, NFD, NFKC, NFKD.
Probe shape:
"e\u0301".normalize()
"e\u0301".normalize("NFC")
"e\u0301".normalize("NFD")
"e\u0301".normalize("NFKC")
"e\u0301".normalize("NFKD")
"e\u0301".normalize("BAD")
"e\u0301".normalize(null)
"e\u0301".normalize("")
The no-argument call defaults to NFC. Supplying null is not the same as omitting the argument: Node applies ToString(null), gets "null", and throws because it is not a valid normalization form.
Perry evidence
crates/perry-codegen/src/lower_string_method.rs lowers normalize with zero or one argument and passes the provided argument through to js_string_normalize.
crates/perry-runtime/src/string/compare.rs::js_string_normalize() handles the four valid forms, but its wildcard match arm returns str_data.nfc().collect() for everything else.
- The runtime comment currently says
Pass null/empty for default NFC, which contradicts Node behavior for explicit null and empty string arguments.
test-files/test_gap_string_methods.ts only covers the valid forms and the no-argument NFC default, so the invalid-form error path is currently untested.
Duplicate check
Searched issues and PRs for:
String.normalize
normalize RangeError
String.prototype.normalize invalid
- PR search for
String.normalize OR String.prototype.normalize OR normalize RangeError
No existing focused issue showed up. The broad docs mention normalize() as a parity gap, but there is no open tracker for this concrete invalid-form behavior.
Expected fix shape
- Keep the current valid-form behavior for omitted /
"NFC" / "NFD" / "NFKC" / "NFKD".
- Convert explicit form arguments with JS
ToString semantics where needed.
- Throw a Node-compatible
RangeError for invalid forms, including "BAD", null, and "".
- Add a regression test covering both valid forms and invalid-form errors.
Summary
Perry implements Unicode normalization for the valid
String.prototype.normalize()forms, but invalidformvalues silently fall back to NFC. Node throws aRangeErrorwhen the supplied form is anything other than"NFC","NFD","NFKC", or"NFKD".Node behavior
Local Node v25.9.0 probe:
Probe shape:
The no-argument call defaults to NFC. Supplying
nullis not the same as omitting the argument: Node appliesToString(null), gets"null", and throws because it is not a valid normalization form.Perry evidence
crates/perry-codegen/src/lower_string_method.rslowersnormalizewith zero or one argument and passes the provided argument through tojs_string_normalize.crates/perry-runtime/src/string/compare.rs::js_string_normalize()handles the four valid forms, but its wildcard match arm returnsstr_data.nfc().collect()for everything else.Pass null/empty for default NFC, which contradicts Node behavior for explicitnulland empty string arguments.test-files/test_gap_string_methods.tsonly covers the valid forms and the no-argument NFC default, so the invalid-form error path is currently untested.Duplicate check
Searched issues and PRs for:
String.normalizenormalize RangeErrorString.prototype.normalize invalidString.normalize OR String.prototype.normalize OR normalize RangeErrorNo existing focused issue showed up. The broad docs mention
normalize()as a parity gap, but there is no open tracker for this concrete invalid-form behavior.Expected fix shape
"NFC"/"NFD"/"NFKC"/"NFKD".ToStringsemantics where needed.RangeErrorfor invalid forms, including"BAD",null, and"".