diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 3bda3abedefb5..5ccb8f55f9c8c 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -1073,6 +1073,15 @@ pub enum TSModuleDeclarationBody<'a> { TSModuleBlock(Box<'a, TSModuleBlock<'a>>) = 1, } +impl TSModuleDeclarationBody<'_> { + pub fn is_empty(&self) -> bool { + match self { + TSModuleDeclarationBody::TSModuleDeclaration(declaration) => declaration.body.is_none(), + TSModuleDeclarationBody::TSModuleBlock(block) => block.body.len() == 0, + } + } +} + // See serializer in serialize.rs #[ast(visit)] #[derive(Debug)] diff --git a/crates/oxc_prettier/src/format/call_expression.rs b/crates/oxc_prettier/src/format/call_expression.rs index 63b6bc0df5851..7837edbcc403c 100644 --- a/crates/oxc_prettier/src/format/call_expression.rs +++ b/crates/oxc_prettier/src/format/call_expression.rs @@ -38,6 +38,15 @@ impl<'a, 'b> CallExpressionLike<'a, 'b> { CallExpressionLike::NewExpression(new) => &new.arguments, } } + + pub fn type_parameters( + &self, + ) -> &Option>> { + match self { + CallExpressionLike::CallExpression(call) => &call.type_parameters, + CallExpressionLike::NewExpression(new) => &new.type_parameters, + } + } } impl GetSpan for CallExpressionLike<'_, '_> { @@ -61,6 +70,10 @@ pub(super) fn print_call_expression<'a>( parts.push(expression.callee().format(p)); + if let Some(type_parameters) = expression.type_parameters() { + parts.push(type_parameters.format(p)); + } + if expression.optional() { parts.push(ss!("?.")); } diff --git a/crates/oxc_prettier/src/format/function_parameters.rs b/crates/oxc_prettier/src/format/function_parameters.rs index b36012f862f4c..fa273d3ad9da5 100644 --- a/crates/oxc_prettier/src/format/function_parameters.rs +++ b/crates/oxc_prettier/src/format/function_parameters.rs @@ -74,6 +74,26 @@ pub(super) fn print_function_parameters<'a>( let mut printed = p.vec(); let len = params.items.len(); let has_rest = params.rest.is_some(); + + if let AstKind::Function(function) = p.parent_kind() { + if let Some(this_param) = &function.this_param { + parts.push(this_param.format(p)); + + if params.items.len() > 0 { + printed.push(ss!(",")); + + if should_hug_the_only_function_parameter { + printed.push(space!()); + } else if p.is_next_line_empty(this_param.span) { + printed.extend(hardline!()); + printed.extend(hardline!()); + } else { + printed.push(line!()); + } + } + } + } + for (i, param) in params.items.iter().enumerate() { if let Some(accessibility) = ¶m.accessibility { printed.push(ss!(accessibility.as_str())); @@ -119,8 +139,9 @@ pub(super) fn print_function_parameters<'a>( indented.extend(printed); let indented = indent!(p, Doc::Array(indented)); parts.push(indented); - let has_rest_parameter = params.rest.is_some(); - parts.push(if_break!(p, if has_rest_parameter { "" } else { "," })); + let skip_dangling_comma = params.rest.is_some() + || matches!(p.parent_kind(), AstKind::Function(func) if func.this_param.is_some()); + parts.push(if_break!(p, if skip_dangling_comma { "" } else { "," })); parts.push(softline!()); if need_parens { parts.push(ss!(")")); diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index 40f2d891b3c8e..4480d27ca91d9 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -797,7 +797,17 @@ impl<'a> Format<'a> for TSArrayType<'a> { impl<'a> Format<'a> for TSConditionalType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + let mut parts = p.vec(); + + parts.push(self.check_type.format(p)); + parts.push(ss!(" extends ")); + parts.push(self.extends_type.format(p)); + parts.push(ss!(" ? ")); + parts.push(self.true_type.format(p)); + parts.push(ss!(" : ")); + parts.push(self.false_type.format(p)); + + Doc::Array(parts) } } @@ -822,11 +832,6 @@ impl<'a> Format<'a> for TSFunctionType<'a> { parts.push(type_parameters.format(p)); } - if let Some(this_param) = &self.this_param { - parts.push(this_param.format(p)); - parts.push(ss!(", ")); - } - parts.push(self.params.format(p)); parts.push(ss!(" => ")); @@ -835,6 +840,7 @@ impl<'a> Format<'a> for TSFunctionType<'a> { Doc::Array(parts) } } + impl<'a> Format<'a> for TSThisParameter<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); @@ -851,7 +857,27 @@ impl<'a> Format<'a> for TSThisParameter<'a> { impl<'a> Format<'a> for TSImportType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + let mut parts = p.vec(); + + if self.is_type_of { + parts.push(ss!("typeof ")); + } + + parts.push(ss!("import(")); + parts.push(self.parameter.format(p)); + // ToDo: attributes + parts.push(ss!(")")); + + if let Some(qualifier) = &self.qualifier { + parts.push(ss!(".")); + parts.push(qualifier.format(p)); + } + + if let Some(type_parameters) = &self.type_parameters { + parts.push(type_parameters.format(p)); + } + + Doc::Array(parts) } } @@ -1040,7 +1066,28 @@ impl<'a> Format<'a> for TSTypeOperator<'a> { impl<'a> Format<'a> for TSTypePredicate<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + let mut parts = p.vec(); + + if self.asserts { + parts.push(ss!("asserts ")); + } + parts.push(self.parameter_name.format(p)); + + if let Some(type_annotation) = &self.type_annotation { + parts.push(ss!(" is ")); + parts.push(type_annotation.type_annotation.format(p)); + } + + Doc::Array(parts) + } +} + +impl<'a> Format<'a> for TSTypePredicateName<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + match self { + TSTypePredicateName::Identifier(it) => it.format(p), + TSTypePredicateName::This(it) => it.format(p), + } } } @@ -1251,14 +1298,17 @@ impl<'a> Format<'a> for TSModuleDeclaration<'a> { parts.push(ss!(" {")); if let Some(body) = &self.body { - let mut indent_parts = p.vec(); + if !body.is_empty() { + let mut indent_parts = p.vec(); - indent_parts.extend(hardline!()); - indent_parts.push(body.format(p)); - parts.push(Doc::Indent(indent_parts)); + indent_parts.extend(hardline!()); + indent_parts.push(body.format(p)); + + parts.push(Doc::Indent(indent_parts)); + parts.extend(hardline!()); + } } - parts.extend(hardline!()); parts.push(ss!("}")); Doc::Array(parts) @@ -1288,8 +1338,15 @@ impl<'a> Format<'a> for TSModuleDeclarationBody<'a> { impl<'a> Format<'a> for TSModuleBlock<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); + let mut add_line = false; for body_part in &self.body { + if add_line { + parts.push(line!()); + } else { + add_line = true; + } + parts.push(body_part.format(p)); } @@ -1616,7 +1673,7 @@ impl<'a> Format<'a> for TSExportAssignment<'a> { impl<'a> Format<'a> for TSNamespaceExportDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + array!(p, ss!(" as namespace "), self.id.format(p), ss!(";")) } } @@ -1707,10 +1764,10 @@ impl<'a> Format<'a> for Expression<'a> { Self::JSXElement(el) => el.format(p), Self::JSXFragment(fragment) => fragment.format(p), Self::TSAsExpression(expr) => expr.format(p), - Self::TSSatisfiesExpression(expr) => expr.expression.format(p), - Self::TSTypeAssertion(expr) => expr.expression.format(p), - Self::TSNonNullExpression(expr) => expr.expression.format(p), - Self::TSInstantiationExpression(expr) => expr.expression.format(p), + Self::TSSatisfiesExpression(expr) => expr.format(p), + Self::TSTypeAssertion(expr) => expr.format(p), + Self::TSNonNullExpression(expr) => expr.format(p), + Self::TSInstantiationExpression(expr) => expr.format(p), } } } @@ -2222,11 +2279,11 @@ impl<'a> Format<'a> for SimpleAssignmentTarget<'a> { match self { Self::AssignmentTargetIdentifier(ident) => ident.format(p), match_member_expression!(Self) => self.to_member_expression().format(p), - Self::TSAsExpression(expr) => expr.expression.format(p), - Self::TSSatisfiesExpression(expr) => expr.expression.format(p), - Self::TSNonNullExpression(expr) => expr.expression.format(p), - Self::TSTypeAssertion(expr) => expr.expression.format(p), - Self::TSInstantiationExpression(expr) => expr.expression.format(p), + Self::TSAsExpression(expr) => expr.format(p), + Self::TSSatisfiesExpression(expr) => expr.format(p), + Self::TSNonNullExpression(expr) => expr.format(p), + Self::TSTypeAssertion(expr) => expr.format(p), + Self::TSInstantiationExpression(expr) => expr.format(p), } } } @@ -2465,25 +2522,25 @@ impl<'a> Format<'a> for TSClassImplements<'a> { impl<'a> Format<'a> for TSTypeAssertion<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + array!(p, ss!("<"), self.type_annotation.format(p), ss!(">"), self.expression.format(p)) } } impl<'a> Format<'a> for TSSatisfiesExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + array!(p, self.expression.format(p), ss!(" satisfies "), self.type_annotation.format(p)) } } impl<'a> Format<'a> for TSInstantiationExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + array!(p, self.expression.format(p), self.type_parameters.format(p)) } } impl<'a> Format<'a> for TSNonNullExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + array!(p, self.expression.format(p), ss!("!")) } } @@ -2868,7 +2925,31 @@ impl<'a> Format<'a> for RegExpFlags { impl<'a> Format<'a> for TSIndexSignature<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + let mut parts = p.vec(); + + if self.readonly { + parts.push(ss!("readonly ")); + } + + parts.push(ss!("[")); + for param in &self.parameters { + parts.push(param.format(p)); + } + parts.push(ss!("]: ")); + parts.push(self.type_annotation.type_annotation.format(p)); + + Doc::Array(parts) + } +} + +impl<'a> Format<'a> for TSIndexSignatureName<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + array!( + p, + ss!(self.name.as_str()), + ss!(": "), + self.type_annotation.type_annotation.format(p) + ) } } @@ -2893,13 +2974,41 @@ impl<'a> Format<'a> for TSPropertySignature<'a> { impl<'a> Format<'a> for TSCallSignatureDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + let mut parts = p.vec(); + + if let Some(type_parameters) = &self.type_parameters { + parts.push(type_parameters.format(p)); + } + + parts.push(self.params.format(p)); + + if let Some(return_type) = &self.return_type { + parts.push(ss!(": ")); + parts.push(return_type.type_annotation.format(p)); + } + + Doc::Array(parts) } } impl<'a> Format<'a> for TSConstructSignatureDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + let mut parts = p.vec(); + + parts.push(ss!("new ")); + + if let Some(type_parameters) = &self.type_parameters { + parts.push(type_parameters.format(p)); + } + + parts.push(self.params.format(p)); + + if let Some(return_type) = &self.return_type { + parts.push(ss!(": ")); + parts.push(return_type.type_annotation.format(p)); + } + + Doc::Array(parts) } } diff --git a/crates/oxc_prettier/src/format/module.rs b/crates/oxc_prettier/src/format/module.rs index 01fd52bb8ee38..bc71534349960 100644 --- a/crates/oxc_prettier/src/format/module.rs +++ b/crates/oxc_prettier/src/format/module.rs @@ -66,9 +66,10 @@ fn print_semicolon_after_export_declaration<'a>( }; match declaration { - Declaration::TSInterfaceDeclaration(_) | Declaration::VariableDeclaration(_) => { - None - } + Declaration::TSInterfaceDeclaration(_) + | Declaration::VariableDeclaration(_) + | Declaration::ClassDeclaration(_) + | Declaration::TSModuleDeclaration(_) => None, _ => Some(ss!(";")), } } diff --git a/tasks/prettier_conformance/prettier.ts.snap.md b/tasks/prettier_conformance/prettier.ts.snap.md index 215d02a2fc7ff..d3282cf1dc1ee 100644 --- a/tasks/prettier_conformance/prettier.ts.snap.md +++ b/tasks/prettier_conformance/prettier.ts.snap.md @@ -1,4 +1,4 @@ -ts compatibility: 173/526 (32.89%) +ts compatibility: 214/526 (40.68%) # Failed @@ -28,7 +28,6 @@ ts compatibility: 173/526 (32.89%) * arrows/type_params.ts ### as -* as/array-pattern.ts * as/as-const-embedded.ts * as/as.ts * as/assignment2.ts @@ -40,22 +39,15 @@ ts compatibility: 173/526 (32.89%) ### assert * assert/comment.ts -* assert/index.ts ### assignment * assignment/issue-10846.ts * assignment/issue-10848.tsx * assignment/issue-10850.ts -* assignment/issue-12413.ts * assignment/issue-2482.ts * assignment/issue-3122.ts -* assignment/issue-6783.ts -* assignment/lone-arg.ts * assignment/parenthesized.ts -### break-calls -* break-calls/type_args.ts - ### call-signature * call-signature/call-signature.ts @@ -69,14 +61,12 @@ ts compatibility: 173/526 (32.89%) ### chain-expression * chain-expression/test.ts -* chain-expression/test2.ts ### class * class/constructor.ts * class/empty-method-body.ts * class/extends_implements.ts * class/generics.ts -* class/methods.ts * class/parameter-properties.ts * class/quoted-property.ts @@ -116,17 +106,14 @@ ts compatibility: 173/526 (32.89%) ### compiler * compiler/castOfAwait.ts * compiler/castParentheses.ts -* compiler/castTest.ts * compiler/checkInfiniteExpansionTermination.ts * compiler/commentInNamespaceDeclarationWithIdentifierPathName.ts * compiler/contextualSignatureInstantiation2.ts * compiler/declareDottedModuleName.ts * compiler/es5ExportDefaultClassDeclaration4.ts * compiler/functionOverloadsOnGenericArity1.ts -* compiler/globalIsContextualKeyword.ts * compiler/indexSignatureWithInitializer.ts * compiler/mappedTypeWithCombinedTypeMappers.ts -* compiler/modifiersOnInterfaceIndexSignature1.ts * compiler/privacyGloImport.ts ### conditional-types @@ -143,8 +130,6 @@ ts compatibility: 173/526 (32.89%) * conformance/classes/mixinClassesAnonymous.ts ### conformance/classes/classDeclarations/classAbstractKeyword -* conformance/classes/classDeclarations/classAbstractKeyword/classAbstractImportInstantiation.ts -* conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInAModule.ts * conformance/classes/classDeclarations/classAbstractKeyword/classAbstractWithInterface.ts ### conformance/classes/constructorDeclarations/constructorParameters @@ -154,9 +139,6 @@ ts compatibility: 173/526 (32.89%) ### conformance/comments * conformance/comments/comments.ts -### conformance/declarationEmit/typePredicates -* conformance/declarationEmit/typePredicates/declarationEmitThisPredicatesWithPrivateName01.ts - ### conformance/es6/Symbols * conformance/es6/Symbols/symbolProperty15.ts @@ -183,13 +165,6 @@ ts compatibility: 173/526 (32.89%) * conformance/parser/ecmascript5/Statements/parserES5ForOfStatement21.ts * conformance/parser/ecmascript5/Statements/parserForInStatement2.ts -### conformance/types/ambient -* conformance/types/ambient/ambientDeclarations.ts - -### conformance/types/any -* conformance/types/any/anyAsConstructor.ts -* conformance/types/any/anyAsGenericFunctionCall.ts - ### conformance/types/firstTypeNode * conformance/types/firstTypeNode/firstTypeNode.ts @@ -205,13 +180,6 @@ ts compatibility: 173/526 (32.89%) ### conformance/types/moduleDeclaration * conformance/types/moduleDeclaration/kind-detection.ts -* conformance/types/moduleDeclaration/moduleDeclaration.ts - -### conformance/types/namespaceExportDeclaration -* conformance/types/namespaceExportDeclaration/exportAsNamespace.d.ts - -### conformance/types/nonNullExpression -* conformance/types/nonNullExpression/nonNullExpression.ts ### conformance/types/parameterProperty * conformance/types/parameterProperty/parameterProperty.ts @@ -255,12 +223,6 @@ ts compatibility: 173/526 (32.89%) * cursor/property-signature.ts * cursor/rest.ts -### custom/abstract -* custom/abstract/abstractNewlineHandling.ts - -### custom/call -* custom/call/callSignature.ts - ### custom/computedProperties * custom/computedProperties/symbol.ts @@ -271,7 +233,6 @@ ts compatibility: 173/526 (32.89%) ### custom/module * custom/module/global.ts -* custom/module/moduleNamespace.ts * custom/module/nestedNamespace.ts ### custom/new @@ -288,8 +249,6 @@ ts compatibility: 173/526 (32.89%) * custom/typeParameters/variables.ts ### declare -* declare/declare_function.ts -* declare/declare_interface.ts * declare/object-type-in-declare-function.ts ### decorator-auto-accessors @@ -328,13 +287,11 @@ ts compatibility: 173/526 (32.89%) * enum/enum.ts ### error-recovery -* error-recovery/generic.ts * error-recovery/index-signature.ts * error-recovery/jsdoc_only_types.ts ### export * export/comment.ts -* export/export-class.ts * export/export-type-star-from-2.ts * export/export-type-star-from.ts @@ -360,9 +317,6 @@ ts compatibility: 173/526 (32.89%) ### import-require * import-require/type-imports.ts -### import-type -* import-type/import-type.ts - ### index-signature * index-signature/index-signature.ts * index-signature/static.ts @@ -371,11 +325,7 @@ ts compatibility: 173/526 (32.89%) * infer-extends/basic.ts ### instantiation-expression -* instantiation-expression/basic.ts -* instantiation-expression/binary-expr.ts * instantiation-expression/inferface-asi.ts -* instantiation-expression/logical-expr.ts -* instantiation-expression/new.ts * instantiation-expression/property-access.ts ### interface @@ -447,9 +397,6 @@ ts compatibility: 173/526 (32.89%) ### multiparser-css * multiparser-css/issue-6259.ts -### never -* never/type-argument.src.ts - ### new * new/new-signature.ts @@ -459,17 +406,12 @@ ts compatibility: 173/526 (32.89%) ### non-null * non-null/braces.ts -* non-null/member-chain.ts * non-null/optional-chain.ts * non-null/parens.ts ### nosemi -* nosemi/index-signature.ts * nosemi/type.ts -### optional-call -* optional-call/type-parameters.ts - ### optional-type * optional-type/complex.ts @@ -477,9 +419,6 @@ ts compatibility: 173/526 (32.89%) * optional-variance/basic.ts * optional-variance/with-jsx.tsx -### predicate-types -* predicate-types/predicate-types.ts - ### prettier-ignore * prettier-ignore/issue-14238.ts * prettier-ignore/mapped-types.ts @@ -502,7 +441,6 @@ ts compatibility: 173/526 (32.89%) * satisfies-operators/argument-expansion.ts * satisfies-operators/assignment.ts * satisfies-operators/basic.ts -* satisfies-operators/comments-unstable.ts * satisfies-operators/comments.ts * satisfies-operators/export-default-as.ts * satisfies-operators/expression-statement.ts @@ -516,9 +454,6 @@ ts compatibility: 173/526 (32.89%) * satisfies-operators/ternary.ts * satisfies-operators/types-comments.ts -### semi -* semi/no-semi.ts - ### static-blocks * static-blocks/nested.ts @@ -559,11 +494,9 @@ ts compatibility: 173/526 (32.89%) ### type-arguments-bit-shift-left-like * type-arguments-bit-shift-left-like/1.ts -* type-arguments-bit-shift-left-like/2.ts * type-arguments-bit-shift-left-like/3.ts * type-arguments-bit-shift-left-like/4.ts * type-arguments-bit-shift-left-like/5.tsx -* type-arguments-bit-shift-left-like/6.ts ### type-member-get-set * type-member-get-set/type-member-get-set.ts