1 | /// Returns whether a character has the Unicode `ID_Start` properly. |
2 | /// |
3 | /// This is only ever-so-slightly different from `XID_Start` in a few edge |
4 | /// cases, so we handle those edge cases manually and delegate everything else |
5 | /// to `unicode-ident`. |
6 | fn is_id_start(c: char) -> bool { |
7 | match c { |
8 | ' \u{037A}' | ' \u{0E33}' | ' \u{0EB3}' | ' \u{309B}' | ' \u{309C}' | ' \u{FC5E}' |
9 | | ' \u{FC5F}' | ' \u{FC60}' | ' \u{FC61}' | ' \u{FC62}' | ' \u{FC63}' | ' \u{FDFA}' |
10 | | ' \u{FDFB}' | ' \u{FE70}' | ' \u{FE72}' | ' \u{FE74}' | ' \u{FE76}' | ' \u{FE78}' |
11 | | ' \u{FE7A}' | ' \u{FE7C}' | ' \u{FE7E}' | ' \u{FF9E}' | ' \u{FF9F}' => true, |
12 | _ => unicode_ident::is_xid_start(ch:c), |
13 | } |
14 | } |
15 | |
16 | /// Returns whether a character has the Unicode `ID_Continue` properly. |
17 | /// |
18 | /// This is only ever-so-slightly different from `XID_Continue` in a few edge |
19 | /// cases, so we handle those edge cases manually and delegate everything else |
20 | /// to `unicode-ident`. |
21 | fn is_id_continue(c: char) -> bool { |
22 | match c { |
23 | ' \u{037A}' | ' \u{309B}' | ' \u{309C}' | ' \u{FC5E}' | ' \u{FC5F}' | ' \u{FC60}' |
24 | | ' \u{FC61}' | ' \u{FC62}' | ' \u{FC63}' | ' \u{FDFA}' | ' \u{FDFB}' | ' \u{FE70}' |
25 | | ' \u{FE72}' | ' \u{FE74}' | ' \u{FE76}' | ' \u{FE78}' | ' \u{FE7A}' | ' \u{FE7C}' |
26 | | ' \u{FE7E}' => true, |
27 | _ => unicode_ident::is_xid_continue(ch:c), |
28 | } |
29 | } |
30 | |
31 | /// Returns whether a string is a valid JavaScript identifier. |
32 | /// Defined at https://tc39.es/ecma262/#prod-IdentifierName. |
33 | pub fn is_valid_ident(name: &str) -> bool { |
34 | !name.is_empty() |
35 | && name.chars().enumerate().all(|(i: usize, char: char)| { |
36 | if i == 0 { |
37 | is_id_start(char) || char == '$' || char == '_' |
38 | } else { |
39 | is_id_continue(char) || char == '$' || char == ' \u{200C}' || char == ' \u{200D}' |
40 | } |
41 | }) |
42 | } |
43 | |