turbopack_ecmascript/references/
util.rs1use anyhow::Result;
2use bincode::{Decode, Encode};
3use swc_core::{
4 common::{
5 Span,
6 errors::{DiagnosticId, HANDLER},
7 },
8 ecma::ast::Expr,
9 quote,
10};
11use turbo_rcstr::{RcStr, rcstr};
12use turbo_tasks::{NonLocalValue, ResolvedVc, Vc, trace::TraceRawVcs, turbofmt};
13use turbo_tasks_fs::FileSystemPath;
14use turbopack_core::{
15 self,
16 chunk::ChunkingType,
17 issue::{
18 Issue, IssueExt, IssueSeverity, IssueSource, IssueStage, OptionIssueSource,
19 OptionStyledString, StyledString,
20 },
21 resolve::{ModuleResolveResult, parse::Request, pattern::Pattern},
22};
23
24use crate::errors;
25
26pub fn throw_module_not_found_expr(request: &str) -> Expr {
29 let message = format!("Cannot find module '{request}'");
30 quote!(
31 "(() => { const e = new Error($message); e.code = 'MODULE_NOT_FOUND'; throw e; })()"
32 as Expr,
33 message: Expr = message.into()
34 )
35}
36
37pub fn throw_module_not_found_expr_async(request: &str) -> Expr {
40 let message = format!("Cannot find module '{request}'");
41 quote!(
42 "Promise.resolve().then(() => { const e = new Error($message); e.code = 'MODULE_NOT_FOUND'; throw e; })"
43 as Expr,
44 message: Expr = message.into()
45 )
46}
47
48pub fn throw_module_not_found_error_expr(request: &str, message: &str) -> Expr {
51 let message = format!("Cannot find module '{request}': {message}");
52 quote!(
53 "(() => { const e = new Error($message); e.code = 'MODULE_NOT_FOUND'; throw e; })()"
54 as Expr,
55 message: Expr = message.into()
56 )
57}
58
59#[turbo_tasks::function]
60pub async fn request_to_string(request: Vc<Request>) -> Result<Vc<RcStr>> {
61 Ok(Vc::cell(
62 request
63 .await?
64 .request()
65 .unwrap_or(rcstr!("unknown")),
67 ))
68}
69
70const TOO_MANY_MATCHES_LIMIT: usize = 10000;
72
73pub async fn check_and_emit_too_many_matches_warning(
74 result: Vc<ModuleResolveResult>,
75 issue_source: IssueSource,
76 context_dir: FileSystemPath,
77 pattern: ResolvedVc<Pattern>,
78) -> Result<()> {
79 let num_matches = result.await?.primary.len();
80 if num_matches > TOO_MANY_MATCHES_LIMIT {
81 TooManyMatchesWarning {
82 source: issue_source,
83 context_dir,
84 num_matches,
85 pattern,
86 }
87 .resolved_cell()
88 .emit();
89 }
90 Ok(())
91}
92
93#[turbo_tasks::value(shared)]
94struct TooManyMatchesWarning {
95 source: IssueSource,
96 context_dir: FileSystemPath,
97 num_matches: usize,
98 pattern: ResolvedVc<Pattern>,
99}
100
101#[turbo_tasks::value_impl]
102impl Issue for TooManyMatchesWarning {
103 #[turbo_tasks::function]
104 async fn title(&self) -> Result<Vc<StyledString>> {
105 Ok(StyledString::Text(
106 turbofmt!(
107 "The file pattern {} matches {} files in {}",
108 self.pattern,
109 self.num_matches,
110 self.context_dir
111 )
112 .await?,
113 )
114 .cell())
115 }
116
117 #[turbo_tasks::function]
118 fn description(&self) -> Vc<OptionStyledString> {
119 Vc::cell(Some(
120 StyledString::Text(rcstr!(
121 "Overly broad patterns can lead to build performance issues and over bundling."
122 ))
123 .resolved_cell(),
124 ))
125 }
126
127 #[turbo_tasks::function]
128 async fn file_path(&self) -> Vc<FileSystemPath> {
129 self.source.file_path()
130 }
131
132 #[turbo_tasks::function]
133 fn stage(&self) -> Vc<IssueStage> {
134 IssueStage::Resolve.cell()
135 }
136
137 fn severity(&self) -> IssueSeverity {
138 IssueSeverity::Warning
139 }
140
141 #[turbo_tasks::function]
142 fn source(&self) -> Vc<OptionIssueSource> {
143 Vc::cell(Some(self.source))
144 }
145}
146
147#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encode, Decode, TraceRawVcs, NonLocalValue)]
148pub enum SpecifiedChunkingType {
149 Parallel,
150 Shared,
151 None,
152}
153
154impl SpecifiedChunkingType {
155 pub fn as_chunking_type(&self, inherit_async: bool, hoisted: bool) -> Option<ChunkingType> {
156 match self {
157 SpecifiedChunkingType::Parallel => Some(ChunkingType::Parallel {
158 inherit_async,
159 hoisted,
160 }),
161 SpecifiedChunkingType::Shared => Some(ChunkingType::Shared {
162 inherit_async,
163 merge_tag: None,
164 }),
165 SpecifiedChunkingType::None => None,
166 }
167 }
168}
169
170pub fn parse_chunking_type_annotation(
171 span: Span,
172 chunking_type_annotation: &str,
173) -> Option<SpecifiedChunkingType> {
174 match chunking_type_annotation {
175 "parallel" => Some(SpecifiedChunkingType::Parallel),
176 "shared" => Some(SpecifiedChunkingType::Shared),
177 "none" => Some(SpecifiedChunkingType::None),
178 _ => {
179 HANDLER.with(|handler| {
180 handler.span_err_with_code(
181 span,
182 &format!(
183 "Unknown specified chunking-type: \"{chunking_type_annotation}\", \
184 expected \"parallel\", \"shared\" or \"none\""
185 ),
186 DiagnosticId::Error(
187 errors::failed_to_analyze::ecmascript::CHUNKING_TYPE.into(),
188 ),
189 );
190 });
191 None
192 }
193 }
194}