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 IIFE expression that throws a "Cannot find module" error for the
27/// given request string
28pub fn throw_module_not_found_error_expr(request: &str, message: &str) -> Expr {
29    let message = format!("Cannot find module '{request}': {message}");
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#[turbo_tasks::function]
38pub async fn request_to_string(request: Vc<Request>) -> Result<Vc<RcStr>> {
39    Ok(Vc::cell(
40        request
41            .await?
42            .request()
43            // TODO: Handle Request::Dynamic, Request::Alternatives
44            .unwrap_or(rcstr!("unknown")),
45    ))
46}
47
48/// If a pattern resolves to more than 10000 results, it's likely a mistake so issue a warning.
49const TOO_MANY_MATCHES_LIMIT: usize = 10000;
50
51pub async fn check_and_emit_too_many_matches_warning(
52    result: Vc<ModuleResolveResult>,
53    issue_source: IssueSource,
54    context_dir: FileSystemPath,
55    pattern: ResolvedVc<Pattern>,
56) -> Result<()> {
57    let num_matches = result.await?.primary.len();
58    if num_matches > TOO_MANY_MATCHES_LIMIT {
59        TooManyMatchesWarning {
60            source: issue_source,
61            context_dir,
62            num_matches,
63            pattern,
64        }
65        .resolved_cell()
66        .emit();
67    }
68    Ok(())
69}
70
71#[turbo_tasks::value(shared)]
72struct TooManyMatchesWarning {
73    source: IssueSource,
74    context_dir: FileSystemPath,
75    num_matches: usize,
76    pattern: ResolvedVc<Pattern>,
77}
78
79#[turbo_tasks::value_impl]
80impl Issue for TooManyMatchesWarning {
81    #[turbo_tasks::function]
82    async fn title(&self) -> Result<Vc<StyledString>> {
83        Ok(StyledString::Text(
84            format!(
85                "The file pattern {pattern} matches {num_matches} files in {context_dir}",
86                pattern = self.pattern.to_string().await?,
87                context_dir = self.context_dir.value_to_string().await?,
88                num_matches = self.num_matches
89            )
90            .into(),
91        )
92        .cell())
93    }
94
95    #[turbo_tasks::function]
96    fn description(&self) -> Vc<OptionStyledString> {
97        Vc::cell(Some(
98            StyledString::Text(rcstr!(
99                "Overly broad patterns can lead to build performance issues and over bundling."
100            ))
101            .resolved_cell(),
102        ))
103    }
104
105    #[turbo_tasks::function]
106    async fn file_path(&self) -> Vc<FileSystemPath> {
107        self.source.file_path()
108    }
109
110    #[turbo_tasks::function]
111    fn stage(&self) -> Vc<IssueStage> {
112        IssueStage::Resolve.cell()
113    }
114
115    fn severity(&self) -> IssueSeverity {
116        IssueSeverity::Warning
117    }
118
119    #[turbo_tasks::function]
120    fn source(&self) -> Vc<OptionIssueSource> {
121        Vc::cell(Some(self.source))
122    }
123}