Skip to content

Commit 053e166

Browse files
committed
feat: add functionExpression.indentInsideIife option
Add a new boolean config option to control whether the body of a function expression inside an IIFE is indented. Defaults to `true` (existing behavior). When set to `false`, `(function () { ... })();` will not indent its body.
1 parent c2aef70 commit 053e166

6 files changed

Lines changed: 139 additions & 31 deletions

File tree

src/configuration/builder.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,14 @@ impl ConfigurationBuilder {
348348
self.insert("functionExpression.spaceAfterFunctionKeyword", value.into())
349349
}
350350

351+
/// Whether to indent the body of a function expression used in an IIFE.
352+
///
353+
/// `true` (default) - Indents the body as normal.
354+
/// `false` - Does not indent the body of the function in `(function() { ... })();`.
355+
pub fn function_expression_indent_inside_iife(&mut self, value: bool) -> &mut Self {
356+
self.insert("functionExpression.indentInsideIife", value.into())
357+
}
358+
351359
/// Whether to add a space before the parentheses of a get accessor.
352360
///
353361
/// `true` - Ex. `get myProp ()`

src/configuration/resolve_config.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ pub fn resolve_config(config: ConfigKeyMap, global_config: &GlobalConfiguration)
289289
function_declaration_space_before_parentheses: get_value(&mut config, "functionDeclaration.spaceBeforeParentheses", false, &mut diagnostics),
290290
function_expression_space_before_parentheses: get_value(&mut config, "functionExpression.spaceBeforeParentheses", false, &mut diagnostics),
291291
function_expression_space_after_function_keyword: get_value(&mut config, "functionExpression.spaceAfterFunctionKeyword", false, &mut diagnostics),
292+
function_expression_indent_inside_iife: get_value(&mut config, "functionExpression.indentInsideIife", true, &mut diagnostics),
292293
get_accessor_space_before_parentheses: get_value(&mut config, "getAccessor.spaceBeforeParentheses", false, &mut diagnostics),
293294
if_statement_space_after_if_keyword: get_value(&mut config, "ifStatement.spaceAfterIfKeyword", true, &mut diagnostics),
294295
import_declaration_space_surrounding_named_imports: get_value(&mut config, "importDeclaration.spaceSurroundingNamedImports", true, &mut diagnostics),
@@ -353,8 +354,8 @@ pub fn resolve_config(config: ConfigKeyMap, global_config: &GlobalConfiguration)
353354

354355
#[cfg(test)]
355356
mod tests {
356-
use dprint_core::configuration::resolve_global_config;
357357
use dprint_core::configuration::NewLineKind;
358+
use dprint_core::configuration::resolve_global_config;
358359

359360
use super::super::builder::ConfigurationBuilder;
360361
use super::*;

src/configuration/types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,8 @@ pub struct Configuration {
604604
pub function_expression_space_before_parentheses: bool,
605605
#[serde(rename = "functionExpression.spaceAfterFunctionKeyword")]
606606
pub function_expression_space_after_function_keyword: bool,
607+
#[serde(rename = "functionExpression.indentInsideIife")]
608+
pub function_expression_indent_inside_iife: bool,
607609
#[serde(rename = "getAccessor.spaceBeforeParentheses")]
608610
pub get_accessor_space_before_parentheses: bool,
609611
#[serde(rename = "ifStatement.spaceAfterIfKeyword")]

src/generation/context.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use deno_ast::swc::common::comments::Comment;
2-
use deno_ast::swc::parser::token::TokenAndSpan;
3-
use deno_ast::view::*;
41
use deno_ast::MediaType;
52
use deno_ast::SourcePos;
63
use deno_ast::SourceRange;
74
use deno_ast::SourceRanged;
85
use deno_ast::SourceRangedForSpanned;
6+
use deno_ast::swc::common::comments::Comment;
7+
use deno_ast::swc::parser::token::TokenAndSpan;
8+
use deno_ast::view::*;
99
use dprint_core::formatting::ConditionReference;
1010
use dprint_core::formatting::IndentLevel;
1111
use dprint_core::formatting::IsStartOfLine;
@@ -68,6 +68,7 @@ pub struct Context<'a> {
6868
stored_lsil: FxHashMap<(SourcePos, SourcePos), LineStartIndentLevel>,
6969
stored_ln: FxHashMap<(SourcePos, SourcePos), LineNumber>,
7070
stored_il: FxHashMap<(SourcePos, SourcePos), IndentLevel>,
71+
pub skip_iife_body_indent: bool,
7172
pub end_statement_or_member_lns: Stack<LineNumber>,
7273
before_comments_start_info_stack: Stack<(SourceRange, LineNumber, IsStartOfLine)>,
7374
if_stmt_last_brace_condition_ref: Option<ConditionReference>,
@@ -102,6 +103,7 @@ impl<'a> Context<'a> {
102103
stored_lsil: FxHashMap::default(),
103104
stored_ln: FxHashMap::default(),
104105
stored_il: FxHashMap::default(),
106+
skip_iife_body_indent: false,
105107
end_statement_or_member_lns: Default::default(),
106108
before_comments_start_info_stack: Default::default(),
107109
if_stmt_last_brace_condition_ref: None,

src/generation/generate.rs

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
use deno_ast::swc::common::comments::Comment;
2-
use deno_ast::swc::common::comments::CommentKind;
3-
use deno_ast::swc::parser::token::BinOpToken;
4-
use deno_ast::swc::parser::token::Token;
5-
use deno_ast::swc::parser::token::TokenAndSpan;
6-
use deno_ast::view::*;
71
use deno_ast::CommentsIterator;
82
use deno_ast::MediaType;
93
use deno_ast::ParsedSource;
104
use deno_ast::SourcePos;
115
use deno_ast::SourceRange;
126
use deno_ast::SourceRanged;
137
use deno_ast::SourceRangedForSpanned;
8+
use deno_ast::swc::common::comments::Comment;
9+
use deno_ast::swc::common::comments::CommentKind;
10+
use deno_ast::swc::parser::token::BinOpToken;
11+
use deno_ast::swc::parser::token::Token;
12+
use deno_ast::swc::parser::token::TokenAndSpan;
13+
use deno_ast::view::*;
1414
use dprint_core::formatting::condition_resolvers;
1515
use dprint_core::formatting::conditions::*;
1616
use dprint_core::formatting::ir_helpers::*;
@@ -115,11 +115,7 @@ fn gen_node_with_inner_gen<'a>(node: Node<'a>, context: &mut Context<'a>, inner_
115115
// keep the leading text, but leave the trailing text to be formatted if on a separate line
116116
let node_text = node.text_fast(context.program);
117117
let end_trim = node_text.trim_end();
118-
if node_text[end_trim.len()..].contains('\n') {
119-
end_trim
120-
} else {
121-
node_text
122-
}
118+
if node_text[end_trim.len()..].contains('\n') { end_trim } else { node_text }
123119
} else {
124120
node.text_fast(context.program)
125121
};
@@ -1134,11 +1130,7 @@ fn gen_export_named_decl<'a>(node: &NamedExport<'a>, context: &mut Context<'a>)
11341130
items.push_sc(sc!(";"));
11351131
}
11361132

1137-
if should_single_line {
1138-
with_no_new_lines(items)
1139-
} else {
1140-
items
1141-
}
1133+
if should_single_line { with_no_new_lines(items) } else { items }
11421134
}
11431135

11441136
fn gen_function_decl<'a>(node: &FnDecl<'a>, context: &mut Context<'a>) -> PrintItems {
@@ -1349,11 +1341,7 @@ fn gen_import_decl<'a>(node: &ImportDecl<'a>, context: &mut Context<'a>) -> Prin
13491341
items.push_sc(sc!(";"));
13501342
}
13511343

1352-
if should_single_line {
1353-
with_no_new_lines(items)
1354-
} else {
1355-
items
1356-
}
1344+
if should_single_line { with_no_new_lines(items) } else { items }
13571345
}
13581346

13591347
fn gen_import_equals_decl<'a>(node: &TsImportEqualsDecl<'a>, context: &mut Context<'a>) -> PrintItems {
@@ -2638,7 +2626,21 @@ fn gen_expr_with_type_args<'a>(node: &TsExprWithTypeArgs<'a>, context: &mut Cont
26382626
items
26392627
}
26402628

2629+
fn is_iife_fn_expr(node: &FnExpr) -> bool {
2630+
let parent = node.parent();
2631+
if parent.kind() == NodeKind::ParenExpr {
2632+
if let Some(grandparent) = parent.parent() {
2633+
return grandparent.kind() == NodeKind::CallExpr;
2634+
}
2635+
}
2636+
false
2637+
}
2638+
26412639
fn gen_fn_expr<'a>(node: &FnExpr<'a>, context: &mut Context<'a>) -> PrintItems {
2640+
if !context.config.function_expression_indent_inside_iife && is_iife_fn_expr(node) {
2641+
context.skip_iife_body_indent = true;
2642+
}
2643+
26422644
let items = gen_function_decl_or_expr(
26432645
FunctionDeclOrExprNode {
26442646
node: node.into(),
@@ -7775,11 +7777,7 @@ fn gen_close_paren_with_type<'a>(opts: GenCloseParenWithTypeOptions<'a>, context
77757777
items.extend(generated_type_node);
77767778
items.push_info(type_node_end_ln);
77777779

7778-
if use_new_line_group {
7779-
new_line_group(items)
7780-
} else {
7781-
items
7782-
}
7780+
if use_new_line_group { new_line_group(items) } else { items }
77837781
} else {
77847782
items
77857783
};
@@ -9475,6 +9473,8 @@ struct GenBlockOptions<'a> {
94759473

94769474
fn gen_block<'a>(gen_inner: impl FnOnce(Vec<Node<'a>>, &mut Context<'a>) -> PrintItems, opts: GenBlockOptions<'a>, context: &mut Context<'a>) -> PrintItems {
94779475
let mut items = PrintItems::new();
9476+
let skip_indent = context.skip_iife_body_indent;
9477+
context.skip_iife_body_indent = false;
94789478
let before_open_token_ln = LineNumber::new("after_open_token_info");
94799479
let first_member_range = opts.children.first().map(|x| x.range());
94809480
let range = opts.range;
@@ -9493,7 +9493,8 @@ fn gen_block<'a>(gen_inner: impl FnOnce(Vec<Node<'a>>, &mut Context<'a>) -> Prin
94939493
items.push_signal(Signal::NewLine);
94949494
}
94959495
items.push_line_and_column(start_inner_lc);
9496-
items.extend(ir_helpers::with_indent(gen_inner(opts.children, context)));
9496+
let inner = gen_inner(opts.children, context);
9497+
items.extend(if skip_indent { inner } else { ir_helpers::with_indent(inner) });
94979498
items.push_line_and_column(end_inner_lc);
94989499

94999500
if is_tokens_same_line_and_empty {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
~~ functionExpression.indentInsideIife: false ~~
2+
== should not indent body of IIFE ==
3+
(function() {
4+
const a = "foobar";
5+
return { a };
6+
})();
7+
8+
[expect]
9+
(function() {
10+
const a = "foobar";
11+
return { a };
12+
})();
13+
14+
== should not indent body of named IIFE ==
15+
(function init() {
16+
const a = "foobar";
17+
return { a };
18+
})();
19+
20+
[expect]
21+
(function init() {
22+
const a = "foobar";
23+
return { a };
24+
})();
25+
26+
== should not indent body of IIFE with args ==
27+
(function(x) {
28+
const a = x;
29+
return { a };
30+
})(42);
31+
32+
[expect]
33+
(function(x) {
34+
const a = x;
35+
return { a };
36+
})(42);
37+
38+
== should still indent body of regular function expression ==
39+
const fn = function() {
40+
const a = "foobar";
41+
return { a };
42+
};
43+
44+
[expect]
45+
const fn = function() {
46+
const a = "foobar";
47+
return { a };
48+
};
49+
50+
== should still indent body of function expression in call argument ==
51+
call(function() {
52+
const a = "foobar";
53+
return { a };
54+
});
55+
56+
[expect]
57+
call(function() {
58+
const a = "foobar";
59+
return { a };
60+
});
61+
62+
== should not affect arrow function IIFE ==
63+
(() => {
64+
const a = "foobar";
65+
return { a };
66+
})();
67+
68+
[expect]
69+
(() => {
70+
const a = "foobar";
71+
return { a };
72+
})();
73+
74+
== should handle IIFE with nested function ==
75+
(function() {
76+
function inner() {
77+
return 1;
78+
}
79+
return inner();
80+
})();
81+
82+
[expect]
83+
(function() {
84+
function inner() {
85+
return 1;
86+
}
87+
return inner();
88+
})();
89+
90+
== should handle empty IIFE ==
91+
(function() {})();
92+
93+
[expect]
94+
(function() {})();

0 commit comments

Comments
 (0)