refactor(parser): move JSXIdentifier conversion code into parser (#5345)

Outside of the parser, a `JSXIdentifier` in a `JSXElementName::Identifier` will never be a identifier reference. So move the code for deciding if a `JSXElementName` is `JSXElementName::Identifier` or `JSXElementName::IdentifierReference`, and the code for converting from one to the other, into the parser - which is only place it should be used.
This commit is contained in:
overlookmotel 2024-08-30 14:47:09 +00:00
parent 292f217da8
commit d236554512
3 changed files with 19 additions and 32 deletions

View file

@ -351,12 +351,6 @@ impl<'a> fmt::Display for IdentifierReference<'a> {
}
}
impl<'a> From<JSXIdentifier<'a>> for IdentifierReference<'a> {
fn from(value: JSXIdentifier<'a>) -> Self {
IdentifierReference { span: value.span, name: value.name, reference_id: Cell::default() }
}
}
impl<'a> Hash for BindingIdentifier<'a> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);

View file

@ -19,28 +19,6 @@ impl<'a> fmt::Display for JSXIdentifier<'a> {
}
}
impl<'a> JSXIdentifier<'a> {
/// Determines whether the given current identifier is a reference.
///
/// References begin with a capital letter, `_` or `$`.
/// <https://babeljs.io/repl#?code_lz=DwMQ9mAED0B8DcAoYAzCMHIPpqnJwAJLhkkA&presets=react>
// `name.chars().next().unwrap()` cannot panic because name is never an empty string.
#[allow(clippy::missing_panics_doc)]
pub fn is_reference(&self) -> bool {
// The identifier has already been checked to be valid, so when first char is ASCII, it can only
// be `a-z`, `A-Z`, `_` or `$`. But compiler doesn't know that, so we can help it create faster
// code by taking that invariant into account.
// `b < b'a'` matches `A-Z`, `_` and `$`.
// Use a fast path for common case of ASCII characters, to avoid the more expensive
// `char::is_uppercase` in most cases.
let name = self.name.as_str();
match name.as_bytes()[0] {
b if b.is_ascii() => b < b'a',
_ => name.chars().next().unwrap().is_uppercase(),
}
}
}
impl<'a> fmt::Display for JSXNamespacedName<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.namespace.name, self.property.name)

View file

@ -154,8 +154,23 @@ impl<'a> ParserImpl<'a> {
.map(JSXElementName::MemberExpression);
}
let element_name = if identifier.is_reference() {
JSXElementName::IdentifierReference(self.ast.alloc(identifier.into()))
// References begin with a capital letter, `_` or `$` e.g. `<Foo>`, `<_foo>`, `<$foo>`.
// https://babeljs.io/repl#?code_lz=DwMQ9mAED0B8DcAoYAzCMHIPpqnJwAJLhkkA&presets=react
// The identifier has already been checked to be valid, so when first char is ASCII, it can only
// be `a-z`, `A-Z`, `_` or `$`. But compiler doesn't know that, so we can help it create faster
// code by taking that invariant into account.
// `b < b'a'` matches `A-Z`, `_` and `$`.
// Use a fast path for common case of ASCII characters, to avoid the more expensive
// `char::is_uppercase` in most cases.
let name = identifier.name.as_str();
let is_reference = match name.as_bytes()[0] {
b if b.is_ascii() => b < b'a',
_ => name.chars().next().unwrap().is_uppercase(),
};
let element_name = if is_reference {
let identifier = self.ast.identifier_reference(identifier.span, identifier.name);
JSXElementName::IdentifierReference(self.ast.alloc(identifier))
} else {
JSXElementName::Identifier(self.ast.alloc(identifier))
};
@ -171,8 +186,8 @@ impl<'a> ParserImpl<'a> {
object: JSXIdentifier<'a>,
) -> Result<Box<'a, JSXMemberExpression<'a>>> {
let mut span = span;
let mut object =
JSXMemberExpressionObject::IdentifierReference(self.ast.alloc(object.into()));
let object = self.ast.identifier_reference(object.span, object.name);
let mut object = JSXMemberExpressionObject::IdentifierReference(self.ast.alloc(object));
let mut property = None;
while self.eat(Kind::Dot) && !self.at(Kind::Eof) {