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 file_source::FileSource,
11 ident::AssetIdent,
12 module::{Module, ModuleSideEffects},
13 raw_module::RawModule,
14 reference::{ModuleReferences, TracedModuleReference},
15 resolve::pattern::{Pattern, PatternMatch, read_matches},
16 source::{OptionSource, Source},
17};
18
19#[turbo_tasks::value]
21pub struct NodeAddonModule {
22 source: ResolvedVc<Box<dyn Source>>,
23}
24
25#[turbo_tasks::value_impl]
26impl NodeAddonModule {
27 #[turbo_tasks::function]
28 pub fn new(source: ResolvedVc<Box<dyn Source>>) -> Vc<NodeAddonModule> {
29 NodeAddonModule { source }.cell()
30 }
31}
32
33#[turbo_tasks::value_impl]
34impl Module for NodeAddonModule {
35 #[turbo_tasks::function]
36 fn ident(&self) -> Vc<AssetIdent> {
37 self.source.ident().with_modifier(rcstr!("node addon"))
38 }
39
40 #[turbo_tasks::function]
41 fn source(&self) -> Vc<OptionSource> {
42 Vc::cell(Some(self.source))
43 }
44
45 #[turbo_tasks::function]
46 async fn references(&self) -> Result<Vc<ModuleReferences>> {
47 static SHARP_BINARY_REGEX: LazyLock<Regex> =
48 LazyLock::new(|| Regex::new("/sharp-(\\w+-\\w+).node$").unwrap());
49 let module_path = self.source.ident().path().await?;
50
51 if SHARP_BINARY_REGEX.is_match(&module_path.path) {
69 let arch = SHARP_BINARY_REGEX
72 .captures(&module_path.path)
73 .unwrap()
74 .get(1)
75 .unwrap()
76 .as_str();
77
78 let package_name = format!("@img/sharp-libvips-{arch}");
79 for folder in [
80 "../..",
83 "../../..",
84 "../../node_modules",
85 "../../../node_modules",
86 ]
87 .iter()
88 .filter_map(|p| module_path.parent().join(p).ok()?.join(&package_name).ok())
89 {
90 if matches!(
91 &*folder.get_type().await?,
92 FileSystemEntryType::Directory | FileSystemEntryType::Symlink
93 ) {
94 return Ok(dir_references(folder));
95 }
96 }
97 };
98
99 Ok(ModuleReferences::empty())
101 }
102
103 #[turbo_tasks::function]
104 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
105 ModuleSideEffects::SideEffectful.cell()
107 }
108}
109
110#[turbo_tasks::function]
111async fn dir_references(package_dir: FileSystemPath) -> Result<Vc<ModuleReferences>> {
112 let matches = read_matches(
113 package_dir.clone(),
114 rcstr!(""),
115 true,
116 Pattern::new(Pattern::Dynamic),
117 )
118 .await?;
119
120 let mut results: FxIndexSet<FileSystemPath> = FxIndexSet::default();
121 for pat_match in matches.into_iter() {
122 match pat_match {
123 PatternMatch::File(_, file) => {
124 let realpath = file.realpath_with_links().await?;
125 results.extend(realpath.symlinks.iter().cloned());
126 match &realpath.path_result {
127 Ok(path) => {
128 results.insert(path.clone());
129 }
130 Err(e) => bail!(e.as_error_message(file, &realpath)),
131 }
132 }
133 PatternMatch::Directory(..) => {}
134 }
135 }
136
137 Ok(Vc::cell(
138 results
139 .into_iter()
140 .map(async |p| {
141 Ok(ResolvedVc::upcast(
142 TracedModuleReference::new(Vc::upcast(RawModule::new(Vc::upcast(
143 FileSource::new(p),
144 ))))
145 .to_resolved()
146 .await?,
147 ))
148 })
149 .try_join()
150 .await?,
151 ))
152}