turbopack_core/
node_addon_module.rs1use std::sync::LazyLock;
2
3use anyhow::{Result, bail};
4use regex::Regex;
5use turbo_rcstr::rcstr;
6use turbo_tasks::{FxIndexSet, ResolvedVc, TryJoinIterExt, Vc};
7use turbo_tasks_fs::{FileSystemEntryType, FileSystemPath};
8
9use crate::{
10 chunk::TracedMode,
11 file_source::FileSource,
12 ident::AssetIdent,
13 module::{Module, ModuleSideEffects},
14 raw_module::RawModule,
15 reference::{ModuleReferences, TracedModuleReference},
16 resolve::pattern::{Pattern, PatternMatch, read_matches},
17 source::{OptionSource, Source},
18};
19
20#[turbo_tasks::value]
22pub struct NodeAddonModule {
23 source: ResolvedVc<Box<dyn Source>>,
24}
25
26#[turbo_tasks::value_impl]
27impl NodeAddonModule {
28 #[turbo_tasks::function]
29 pub fn new(source: ResolvedVc<Box<dyn Source>>) -> Vc<NodeAddonModule> {
30 NodeAddonModule { source }.cell()
31 }
32}
33
34#[turbo_tasks::value_impl]
35impl Module for NodeAddonModule {
36 #[turbo_tasks::function]
37 async fn ident(&self) -> Result<Vc<AssetIdent>> {
38 Ok(self
39 .source
40 .ident()
41 .owned()
42 .await?
43 .with_modifier(rcstr!("node addon"))
44 .into_vc())
45 }
46
47 #[turbo_tasks::function]
48 fn source(&self) -> Vc<OptionSource> {
49 Vc::cell(Some(self.source))
50 }
51
52 #[turbo_tasks::function]
53 async fn references(&self) -> Result<Vc<ModuleReferences>> {
54 static SHARP_PKG_REGEX: LazyLock<Regex> =
55 LazyLock::new(|| Regex::new(r"^@img/sharp-([\w-]+)/").unwrap());
56 let ident = self.source.ident().await?;
57 let module_path = &ident.path;
58
59 let sharp_package_arch = module_path
62 .path
63 .rsplit_once("node_modules/")
64 .and_then(|(.., p)| SHARP_PKG_REGEX.captures(p))
65 .and_then(|c| c.get(1))
66 .map(|m| m.as_str());
67
68 if let Some(arch) = sharp_package_arch {
86 let package_name = format!("@img/sharp-libvips-{arch}");
87 for folder in [
88 "../..",
91 "../../..",
92 "../../node_modules",
93 "../../../node_modules",
94 ]
95 .iter()
96 .filter_map(|p| module_path.parent().join(p).ok()?.join(&package_name).ok())
97 {
98 if matches!(
99 &*folder.get_type().await?,
100 FileSystemEntryType::Directory | FileSystemEntryType::Symlink
101 ) {
102 return Ok(dir_references(folder));
103 }
104 }
105 };
106
107 Ok(ModuleReferences::empty())
109 }
110
111 #[turbo_tasks::function]
112 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
113 ModuleSideEffects::SideEffectful.cell()
115 }
116}
117
118#[turbo_tasks::function]
119async fn dir_references(package_dir: FileSystemPath) -> Result<Vc<ModuleReferences>> {
120 let matches = read_matches(
121 package_dir.clone(),
122 rcstr!(""),
123 true,
124 Pattern::new(Pattern::Dynamic),
125 )
126 .await?;
127
128 let mut results: FxIndexSet<FileSystemPath> = FxIndexSet::default();
129 for pat_match in matches.into_iter() {
130 match pat_match {
131 PatternMatch::File(_, file) => {
132 let realpath = file.realpath_with_links().await?;
133 results.extend(realpath.symlinks.iter().cloned());
134 match &realpath.path_result {
135 Ok(path) => {
136 results.insert(path.clone());
137 }
138 Err(e) => bail!(e.as_error_message(file, &realpath).await?),
139 }
140 }
141 PatternMatch::Directory(..) => {}
142 }
143 }
144
145 Ok(Vc::cell(
146 results
147 .into_iter()
148 .map(async |p| {
149 Ok(ResolvedVc::upcast(
150 TracedModuleReference::new(
151 Vc::upcast(RawModule::new(Vc::upcast(FileSource::new(p)))),
152 TracedMode::Entry,
153 )
154 .to_resolved()
155 .await?,
156 ))
157 })
158 .try_join()
159 .await?,
160 ))
161}