Skip to main content

turbopack_ecmascript/references/
util.rs

1use anyhow::Result;
2use swc_core::{ecma::ast::Expr, quote};
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{ResolvedVc, ValueToString, Vc};
5use turbo_tasks_fs::FileSystemPath;
6use turbopack_core::{
7    self,
8    issue::{
9        Issue, IssueExt, IssueSeverity, IssueSource, IssueStage, OptionIssueSource,
10        OptionStyledString, StyledString,
11    },
12    resolve::{ModuleResolveResult, parse::Request, pattern::Pattern},
13};
14
15/// Creates a IIFE expression that throws a "Cannot find module" error for the
16/// given request string
17pub fn throw_module_not_found_expr(request: &str) -> Expr {
18    let message = format!("Cannot find module '{request}'");
19    quote!(
20        "(() => { const e = new Error($message); e.code = 'MODULE_NOT_FOUND'; throw e; })()"
21            as Expr,
22        message: Expr = message.into()
23    )
24}
25
26/// Creates a Promise that rejects with a "Cannot find module" error for the
27/// given request string. Use this for async contexts (dynamic imports).
28pub fn throw_module_not_found_expr_async(request: &str) -> Expr {
29    let message = format!("Cannot find module '{request}'");
30    quote!(
31        "Promise.resolve().then(() => { 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 IIFE expression that throws a "Cannot find module" error for the
38/// given request string
39pub fn throw_module_not_found_error_expr(request: &str, message: &str) -> Expr {
40    let message = format!("Cannot find module '{request}': {message}");
41    quote!(
42        "(() => { const e = new Error($message); e.code = 'MODULE_NOT_FOUND'; throw e; })()"
43            as Expr,
44        message: Expr = message.into()
45    )
46}
47
48#[turbo_tasks::function]
49pub async fn request_to_string(request: Vc<Request>) -> Result<Vc<RcStr>> {
50    Ok(Vc::cell(
51        request
52            .await?
53            .request()
54            // TODO: Handle Request::Dynamic, Request::Alternatives
55            .unwrap_or(rcstr!("unknown")),
56    ))
57}
58
59/// If a pattern resolves to more than 10000 results, it's likely a mistake so issue a warning.
60const TOO_MANY_MATCHES_LIMIT: usize = 10000;
61
62pub async fn check_and_emit_too_many_matches_warning(
63    result: Vc<ModuleResolveResult>,
64    issue_source: IssueSource,
65    context_dir: FileSystemPath,
66    pattern: ResolvedVc<Pattern>,
67) -> Result<()> {
68    let num_matches = result.await?.primary.len();
69    if num_matches > TOO_MANY_MATCHES_LIMIT {
70        TooManyMatchesWarning {
71            source: issue_source,
72            context_dir,
73            num_matches,
74            pattern,
75        }
76        .resolved_cell()
77        .emit();
78    }
79    Ok(())
80}
81
82#[turbo_tasks::value(shared)]
83struct TooManyMatchesWarning {
84    source: IssueSource,
85    context_dir: FileSystemPath,
86    num_matches: usize,
87    pattern: ResolvedVc<Pattern>,
88}
89
90#[turbo_tasks::value_impl]
91impl Issue for TooManyMatchesWarning {
92    #[turbo_tasks::function]
93    async fn title(&self) -> Result<Vc<StyledString>> {
94        Ok(StyledString::Text(
95            format!(
96                "The file pattern {pattern} matches {num_matches} files in {context_dir}",
97                pattern = self.pattern.to_string().await?,
98                context_dir = self.context_dir.value_to_string().await?,
99                num_matches = self.num_matches
100            )
101            .into(),
102        )
103        .cell())
104    }
105
106    #[turbo_tasks::function]
107    fn description(&self) -> Vc<OptionStyledString> {
108        Vc::cell(Some(
109            StyledString::Text(rcstr!(
110                "Overly broad patterns can lead to build performance issues and over bundling."
111            ))
112            .resolved_cell(),
113        ))
114    }
115
116    #[turbo_tasks::function]
117    async fn file_path(&self) -> Vc<FileSystemPath> {
118        self.source.file_path()
119    }
120
121    #[turbo_tasks::function]
122    fn stage(&self) -> Vc<IssueStage> {
123        IssueStage::Resolve.cell()
124    }
125
126    fn severity(&self) -> IssueSeverity {
127        IssueSeverity::Warning
128    }
129
130    #[turbo_tasks::function]
131    fn source(&self) -> Vc<OptionIssueSource> {
132        Vc::cell(Some(self.source))
133    }
134}