feat(prettier): support quoteProps option in PropertyKey (#1578)

This commit is contained in:
Dunqing 2023-11-29 18:32:30 +08:00 committed by GitHub
parent 93d5b0f879
commit f19032e102
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 5 deletions

View file

@ -17,6 +17,7 @@ mod function_parameters;
mod misc;
mod module;
mod object;
mod property;
mod statement;
mod string;
mod template_literal;
@ -27,6 +28,7 @@ use std::borrow::Cow;
use oxc_allocator::{Box, Vec};
use oxc_ast::{ast::*, AstKind};
use oxc_span::GetSpan;
use oxc_syntax::identifier::is_identify_name;
use crate::{
array,
@ -1515,14 +1517,50 @@ impl<'a> Format<'a> for ObjectProperty<'a> {
impl<'a> Format<'a> for PropertyKey<'a> {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
wrap!(p, self, PropertyKey, {
// Perf: Cache the result of `need_quote` to avoid checking it in each PropertyKey
let need_quote = p.options.quote_props.is_consistent()
&& match p.parent_parent_kind() {
Some(AstKind::ObjectExpression(a)) => a.properties.iter().any(|x| match x {
ObjectPropertyKind::ObjectProperty(p) => {
property::is_property_key_has_quote(&p.key)
}
ObjectPropertyKind::SpreadProperty(_) => false,
}),
Some(AstKind::ClassBody(a)) => a.body.iter().any(|x| match x {
ClassElement::PropertyDefinition(p) => {
property::is_property_key_has_quote(&p.key)
}
_ => false,
}),
_ => false,
};
match self {
PropertyKey::Identifier(ident) => ident.format(p),
PropertyKey::Identifier(ident) => {
if need_quote {
Doc::Str(string::print_string(p, &ident.name, p.options.single_quote))
} else {
ident.format(p)
}
}
PropertyKey::PrivateIdentifier(ident) => ident.format(p),
PropertyKey::Expression(expr) => match expr {
Expression::StringLiteral(literal) => {
let value = literal.value.as_bytes();
if !&value[0].is_ascii_digit() && !value.contains(&b'_') {
p.str(&literal.value)
let unquote = if need_quote {
false
} else {
is_identify_name(literal.value.as_str())
};
if !unquote || p.options.quote_props.is_preserve() {
literal.format(p)
} else {
p.str(literal.value.as_str())
}
}
Expression::NumberLiteral(literal) => {
if need_quote {
Doc::Str(string::print_string(p, literal.raw, p.options.single_quote))
} else {
literal.format(p)
}

View file

@ -0,0 +1,27 @@
use oxc_ast::ast::{Expression, PropertyKey};
use oxc_syntax::identifier::is_identify_name;
pub(super) fn is_property_key_has_quote(key: &PropertyKey<'_>) -> bool {
matches!(key, PropertyKey::Expression(Expression::StringLiteral(literal)) if is_string_prop_safe_to_unquote(literal.value.as_str()))
}
pub(super) fn is_string_prop_safe_to_unquote(value: &str) -> bool {
!is_identify_name(value) && !is_simple_number(value)
}
// Matches “simple” numbers like `123` and `2.5` but not `1_000`, `1e+100` or `0b10`.
pub(super) fn is_simple_number(str: &str) -> bool {
let mut bytes = str.as_bytes().iter();
let mut has_dot = false;
bytes.next().is_some_and(u8::is_ascii_digit)
&& bytes.all(|c| {
if c == &b'.' {
if has_dot {
return false;
}
has_dot = true;
return true;
}
c.is_ascii_digit()
})
}

View file

@ -124,6 +124,28 @@ pub enum QuoteProps {
Preserve,
}
impl QuoteProps {
pub fn is_preserve(self) -> bool {
matches!(self, Self::Preserve)
}
pub fn is_consistent(self) -> bool {
matches!(self, Self::Consistent)
}
}
impl FromStr for QuoteProps {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"as_needed" => Self::AsNeeded,
"consistent" => Self::Consistent,
"preserve" => Self::Preserve,
_ => Self::default(),
})
}
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
pub enum TrailingComma {
/// Trailing commas wherever possible (including function parameters and calls).

View file

@ -108,3 +108,8 @@ pub fn is_identifier_part(c: char) -> bool {
}
is_id_continue_unicode(c) || c == ZWNJ || c == ZWJ
}
pub fn is_identify_name(name: &str) -> bool {
let mut chars = name.chars();
chars.next().is_some_and(is_identifier_start_all) && chars.all(is_identifier_part)
}

View file

@ -8,7 +8,7 @@ use oxc_ast::{
VisitMut,
};
use oxc_parser::Parser;
use oxc_prettier::{EndOfLine, PrettierOptions, TrailingComma};
use oxc_prettier::{EndOfLine, PrettierOptions, QuoteProps, TrailingComma};
use oxc_span::{Atom, GetSpan, SourceType};
#[derive(Default)]
@ -89,6 +89,10 @@ impl VisitMut<'_> for SpecParser {
options.end_of_line =
EndOfLine::from_str(literal.value.as_str()).unwrap();
}
"quoteProps" => {
options.quote_props =
QuoteProps::from_str(literal.value.as_str()).unwrap();
}
_ => {}
},
_ => {}