Skip to content

Commit a302e32

Browse files
authored
refactor(prettier): Verify printing (new|call|import)_expr (#8885)
Part of #5068 Now all AST nodes are once verified. ✌🏻
1 parent 571fb70 commit a302e32

3 files changed

Lines changed: 118 additions & 42 deletions

File tree

crates/oxc_prettier/src/format/js.rs

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,27 +1259,7 @@ impl<'a> Format<'a> for ParenthesizedExpression<'a> {
12591259

12601260
impl<'a> Format<'a> for ImportExpression<'a> {
12611261
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
1262-
wrap!(p, self, ImportExpression, {
1263-
// TODO: Use `print_call_expression`?
1264-
let mut parts = Vec::new_in(p.allocator);
1265-
parts.push(text!("import"));
1266-
parts.push(text!("("));
1267-
let mut indent_parts = Vec::new_in(p.allocator);
1268-
indent_parts.push(softline!());
1269-
indent_parts.push(self.source.format(p));
1270-
if !self.arguments.is_empty() {
1271-
for arg in &self.arguments {
1272-
indent_parts.push(text!(","));
1273-
indent_parts.push(line!());
1274-
indent_parts.push(arg.format(p));
1275-
}
1276-
}
1277-
parts.push(group!(p, [indent!(p, indent_parts)]));
1278-
parts.push(softline!());
1279-
parts.push(text!(")"));
1280-
1281-
group!(p, parts)
1282-
})
1262+
wrap!(p, self, ImportExpression, { call_expression::print_import_expression(p, self) })
12831263
}
12841264
}
12851265

crates/oxc_prettier/src/format/print/call_arguments.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,52 @@ pub fn print_call_arguments<'a>(
210210
group!(p, parts, should_break, None)
211211
}
212212

213+
// In Prettier, `callArguments()` is also used for `ImportExpression`.
214+
// But it only passes 2/10 branches! so let's inline it here.
215+
pub fn print_import_source_and_arguments<'a>(
216+
p: &mut Prettier<'a>,
217+
expr: &ImportExpression<'a>,
218+
) -> Doc<'a> {
219+
let mut parts = Vec::new_in(p.allocator);
220+
221+
// TODO:
222+
// if (shouldExpandLastArg(args, printedArguments, options)) {
223+
// return conditionalGroup([
224+
// ["(", ...headArgs, lastArg, ")"],
225+
// ["(", ...headArgs, group(lastArg, { shouldBreak: true }), ")"],
226+
// allArgsBrokenOut(),
227+
// ]);
228+
// }
229+
230+
// TODO:
231+
// return group([
232+
// "(",
233+
// indent([softline, ...printedArguments]),
234+
// ifBreak(maybeTrailingComma),
235+
// softline,
236+
// ")",
237+
// ])
238+
239+
parts.push(text!("("));
240+
241+
let mut indent_parts = Vec::new_in(p.allocator);
242+
indent_parts.push(softline!());
243+
indent_parts.push(expr.source.format(p));
244+
if !expr.arguments.is_empty() {
245+
for arg in &expr.arguments {
246+
indent_parts.push(text!(","));
247+
indent_parts.push(line!());
248+
indent_parts.push(arg.format(p));
249+
}
250+
}
251+
parts.push(group!(p, [indent!(p, indent_parts)]));
252+
parts.push(softline!());
253+
254+
parts.push(text!(")"));
255+
256+
group!(p, parts)
257+
}
258+
213259
/// * Reference <https://github.com/prettier/prettier/blob/3.3.3/src/language-js/print/call-arguments.js#L247-L272>
214260
fn should_expand_first_arg<'a>(arguments: &Vec<'a, Argument<'a>>) -> bool {
215261
if arguments.len() != 2 {

crates/oxc_prettier/src/format/print/call_expression.rs

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use oxc_allocator::Vec;
22
use oxc_ast::ast::*;
3-
use oxc_span::{GetSpan, Span};
43

5-
use crate::{format::print::call_arguments, group, ir::Doc, text, Format, Prettier};
4+
use crate::{
5+
array, dynamic_text, format::print::call_arguments, group, indent, ir::Doc, line, softline,
6+
text, Format, Prettier,
7+
};
68

79
pub enum CallExpressionLike<'a, 'b> {
810
CallExpression(&'b CallExpression<'a>),
@@ -43,38 +45,86 @@ impl<'a> CallExpressionLike<'a, '_> {
4345
}
4446
}
4547

46-
impl GetSpan for CallExpressionLike<'_, '_> {
47-
fn span(&self) -> Span {
48-
match self {
49-
CallExpressionLike::CallExpression(call) => call.span,
50-
CallExpressionLike::NewExpression(new) => new.span,
51-
}
52-
}
53-
}
54-
5548
pub fn print_call_expression<'a>(
5649
p: &mut Prettier<'a>,
57-
expression: &CallExpressionLike<'a, '_>,
50+
expr: &CallExpressionLike<'a, '_>,
5851
) -> Doc<'a> {
52+
// TODO:
53+
// if (
54+
// isTemplateLiteralSingleArg ||
55+
// // Dangling comments are not handled, all these special cases should have arguments #9668
56+
// // We want to keep CommonJS- and AMD-style require calls, and AMD-style
57+
// // define calls, as a unit.
58+
// // e.g. `define(["some/lib"], (lib) => {`
59+
// isCommonsJsOrAmdModuleDefinition(path) ||
60+
// // Keep test declarations on a single line
61+
// // e.g. `it('long name', () => {`
62+
// isTestCall(node, path.parent)
63+
// ) {
64+
// const printed = [];
65+
// iterateCallArgumentsPath(path, () => printed.push(print()));
66+
// if (!printed[0].label?.embed) {
67+
// return [
68+
// isNew ? "new " : "",
69+
// printCallee(path, print),
70+
// optional,
71+
// printFunctionTypeParameters(path, options, print),
72+
// "(",
73+
// join(", ", printed),
74+
// ")",
75+
// ];
76+
// }
77+
// }
78+
79+
// TODO:
80+
// if (
81+
// !isNew &&
82+
// isMemberish(node.callee) &&
83+
// !path.call(
84+
// (path) => pathNeedsParens(path, options),
85+
// "callee",
86+
// ...(node.callee.type === "ChainExpression" ? ["expression"] : []),
87+
// )
88+
// )
89+
// return printMemberChain(path, options, print);
90+
5991
let mut parts = Vec::new_in(p.allocator);
6092

61-
if expression.is_new() {
93+
if expr.is_new() {
6294
parts.push(text!("new "));
6395
};
64-
65-
parts.push(expression.callee().format(p));
66-
67-
if let Some(type_parameters) = expression.type_parameters() {
96+
parts.push(expr.callee().format(p));
97+
if expr.optional() {
98+
parts.push(text!("?."));
99+
}
100+
if let Some(type_parameters) = expr.type_parameters() {
68101
parts.push(type_parameters.format(p));
69102
}
103+
parts.push(call_arguments::print_call_arguments(p, expr));
70104

71-
if expression.optional() {
72-
parts.push(text!("?."));
105+
if expr.callee().is_call_expression() {
106+
return group!(p, parts);
73107
}
74108

75-
parts.push(call_arguments::print_call_arguments(p, expression));
109+
array!(p, parts)
110+
}
111+
112+
// In Prettier, `printCallExpression()` has 4 branches.
113+
// But for `ImportExpression`, it only passes the 1st and 3rd branches.
114+
// - if (isTemplateLiteralSingleArg) return [callee, "(", source, ")"];
115+
// - return group([callee, callArguments([source, arguments])]);
116+
pub fn print_import_expression<'a>(p: &mut Prettier<'a>, expr: &ImportExpression<'a>) -> Doc<'a> {
117+
let callee_doc = {
118+
if let Some(phase) = &expr.phase {
119+
array!(p, [text!("import."), dynamic_text!(p, phase.as_str())]);
120+
}
121+
text!("import")
122+
};
123+
124+
// TODO: isTemplateLiteralSingleArg branch
125+
// return [callee, "(", source, ")"];
76126

77-
group!(p, parts)
127+
group!(p, [callee_doc, call_arguments::print_import_source_and_arguments(p, expr)])
78128
}
79129

80130
/// <https://github.com/prettier/prettier/blob/7aecca5d6473d73f562ca3af874831315f8f2581/src/language-js/print/call-expression.js#L93-L116>

0 commit comments

Comments
 (0)