turbopack_ecmascript/chunk/
factory_group.rs1use std::io::Write;
2
3use anyhow::Result;
4use turbopack_core::code_builder::CodeBuilder;
5
6use crate::{chunk::CodeModuleIdAndPath, utils::StringifyJs};
7
8const MIXED_ARROW_PREFIX: &str = "\n(()=>{\"use strict\";return[";
9const MIXED_FUNCTION_PREFIX: &str = "\n(function(){\"use strict\";return[";
10const MIXED_SUFFIX: &str = "\n]})(),";
11const ALL_STRICT_ARROW_PREFIX: &str = "(()=>{\"use strict\";";
12const ALL_STRICT_FUNCTION_PREFIX: &str = "(function(){\"use strict\";";
13const ALL_STRICT_SUFFIX: &str = "})()";
14
15const MIXED_STRICT_THRESHOLD: usize = 3;
20const ALL_STRICT_ARROW_THRESHOLD: usize = 2;
21const ALL_STRICT_FUNCTION_THRESHOLD: usize = 3;
22
23#[derive(Clone, Copy, PartialEq, Eq)]
24pub enum StrictFactoryMode {
25 Flat,
26 Mixed,
27 AllStrict,
28}
29
30pub fn strict_factory_mode(
32 chunk_items: &[CodeModuleIdAndPath],
33 supports_arrow_functions: bool,
34) -> StrictFactoryMode {
35 let mut strict = 0;
36 let mut non_strict = 0;
37 for item in chunk_items {
38 if item.strict {
39 strict += 1;
40 } else {
41 non_strict += 1;
42 }
43 }
44 if strict == 0 {
45 return StrictFactoryMode::Flat;
46 }
47
48 let mode = if non_strict == 0 {
49 StrictFactoryMode::AllStrict
50 } else {
51 StrictFactoryMode::Mixed
52 };
53 let threshold = match (mode, supports_arrow_functions) {
54 (StrictFactoryMode::Mixed, _) => MIXED_STRICT_THRESHOLD,
55 (StrictFactoryMode::AllStrict, true) => ALL_STRICT_ARROW_THRESHOLD,
56 (StrictFactoryMode::AllStrict, false) => ALL_STRICT_FUNCTION_THRESHOLD,
57 (StrictFactoryMode::Flat, _) => unreachable!(),
58 };
59 if strict >= threshold {
60 mode
61 } else {
62 StrictFactoryMode::Flat
63 }
64}
65
66pub fn strict_chunk_wrapper(
68 mode: StrictFactoryMode,
69 supports_arrow_functions: bool,
70) -> Option<(&'static str, &'static str)> {
71 (mode == StrictFactoryMode::AllStrict).then(|| {
72 (
73 all_strict_prefix(supports_arrow_functions),
74 ALL_STRICT_SUFFIX,
75 )
76 })
77}
78
79const fn mixed_prefix(supports_arrow_functions: bool) -> &'static str {
80 if supports_arrow_functions {
81 MIXED_ARROW_PREFIX
82 } else {
83 MIXED_FUNCTION_PREFIX
84 }
85}
86
87const fn all_strict_prefix(supports_arrow_functions: bool) -> &'static str {
88 if supports_arrow_functions {
89 ALL_STRICT_ARROW_PREFIX
90 } else {
91 ALL_STRICT_FUNCTION_PREFIX
92 }
93}
94
95pub fn write_module_factories(
97 code: &mut CodeBuilder,
98 chunk_items: &[CodeModuleIdAndPath],
99 mode: StrictFactoryMode,
100 supports_arrow_functions: bool,
101) -> Result<()> {
102 if mode == StrictFactoryMode::Mixed {
103 *code += mixed_prefix(supports_arrow_functions);
104 write_factories(code, chunk_items, |strict| strict)?;
105 *code += MIXED_SUFFIX;
106 write_factories(code, chunk_items, |strict| !strict)?;
107 } else {
108 write_factories(code, chunk_items, |_| true)?;
109 }
110 Ok(())
111}
112
113fn write_factories(
114 code: &mut CodeBuilder,
115 chunk_items: &[CodeModuleIdAndPath],
116 include: impl Fn(bool) -> bool,
117) -> Result<()> {
118 for item in chunk_items {
119 if include(item.strict) {
120 write!(code, "\n{}, ", StringifyJs(&item.id))?;
121 code.push_code(&item.code);
122 write!(code, ",")?;
123 }
124 }
125 Ok(())
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 const STRICT_MODE_DIRECTIVE_LEN: usize = "\"use strict\";".len();
138
139 fn minified_len(wrapper: &str) -> usize {
140 wrapper.lines().map(str::trim).map(str::len).sum()
143 }
144
145 fn assert_threshold(threshold: usize, wrapper: &str) {
148 let wrapper_len = minified_len(wrapper);
149 assert!((threshold - 1) * STRICT_MODE_DIRECTIVE_LEN <= wrapper_len);
150 assert!(threshold * STRICT_MODE_DIRECTIVE_LEN > wrapper_len);
151 }
152
153 #[test]
154 fn strict_thresholds_match_wrapper_sizes() {
155 assert_threshold(
156 MIXED_STRICT_THRESHOLD,
157 &format!("{MIXED_ARROW_PREFIX}{MIXED_SUFFIX}"),
158 );
159 assert_threshold(
160 MIXED_STRICT_THRESHOLD,
161 &format!("{MIXED_FUNCTION_PREFIX}{MIXED_SUFFIX}"),
162 );
163 assert_threshold(
164 ALL_STRICT_ARROW_THRESHOLD,
165 &format!("{ALL_STRICT_ARROW_PREFIX}{ALL_STRICT_SUFFIX}"),
166 );
167 assert_threshold(
168 ALL_STRICT_FUNCTION_THRESHOLD,
169 &format!("{ALL_STRICT_FUNCTION_PREFIX}{ALL_STRICT_SUFFIX}"),
170 );
171 }
172}