Skip to main content

turbopack_ecmascript/references/
util.rs

1use 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
26/// Creates a IIFE expression that throws a "Cannot find module" error for the
27/// given request string
28pub 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
37/// Creates a Promise that rejects with a "Cannot find module" error for the
38/// given request string. Use this for async contexts (dynamic imports).
39pub 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
48/// Creates a IIFE expression that throws a "Cannot find module" error for the
49/// given request string
50pub 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            // TODO: Handle Request::Dynamic, Request::Alternatives
66            .unwrap_or(rcstr!("unknown")),
67    ))
68}
69
70/// If a pattern resolves to more than 10000 results, it's likely a mistake so issue a warning.
71const 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}