turbopack_core/
node_addon_module.rs1use std::sync::LazyLock;
2
3use anyhow::Result;
4use regex::Regex;
5use turbo_rcstr::rcstr;
6use turbo_tasks::{FxIndexSet, ResolvedVc, TryJoinIterExt, Vc};
7use turbo_tasks_fs::{FileSystemEntryType, FileSystemPath};
8
9use crate::{
10 asset::{Asset, AssetContent},
11 file_source::FileSource,
12 ident::AssetIdent,
13 module::Module,
14 raw_module::RawModule,
15 reference::{ModuleReferences, TracedModuleReference},
16 resolve::pattern::{Pattern, PatternMatch, read_matches},
17 source::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 fn ident(&self) -> Vc<AssetIdent> {
38 self.source.ident().with_modifier(rcstr!("node addon"))
39 }
40
41 #[turbo_tasks::function]
42 async fn references(&self) -> Result<Vc<ModuleReferences>> {
43 static SHARP_BINARY_REGEX: LazyLock<Regex> =
44 LazyLock::new(|| Regex::new("/sharp-(\\w+-\\w+).node$").unwrap());
45 let module_path = self.source.ident().path().await?;
46
47 if SHARP_BINARY_REGEX.is_match(&module_path.path) {
65 let arch = SHARP_BINARY_REGEX
68 .captures(&module_path.path)
69 .unwrap()
70 .get(1)
71 .unwrap()
72 .as_str();
73
74 let package_name = format!("@img/sharp-libvips-{arch}");
75 for folder in [
76 "../..",
79 "../../..",
80 "../../node_modules",
81 "../../../node_modules",
82 ]
83 .iter()
84 .filter_map(|p| module_path.parent().join(p).ok()?.join(&package_name).ok())
85 {
86 if matches!(
87 &*folder.get_type().await?,
88 FileSystemEntryType::Directory | FileSystemEntryType::Symlink
89 ) {
90 return Ok(dir_references(folder));
91 }
92 }
93 };
94
95 Ok(ModuleReferences::empty())
97 }
98}
99
100#[turbo_tasks::value_impl]
101impl Asset for NodeAddonModule {
102 #[turbo_tasks::function]
103 fn content(&self) -> Vc<AssetContent> {
104 self.source.content()
105 }
106}
107
108#[turbo_tasks::function]
109async fn dir_references(package_dir: FileSystemPath) -> Result<Vc<ModuleReferences>> {
110 let matches = read_matches(
111 package_dir.clone(),
112 rcstr!(""),
113 true,
114 Pattern::new(Pattern::Dynamic),
115 )
116 .await?;
117
118 let mut results: FxIndexSet<FileSystemPath> = FxIndexSet::default();
119 for pat_match in matches.into_iter() {
120 match pat_match {
121 PatternMatch::File(_, file) => {
122 let realpath = file.realpath_with_links().await?;
123 results.extend(realpath.symlinks.iter().cloned());
124 results.insert(realpath.path.clone());
125 }
126 PatternMatch::Directory(..) => {}
127 }
128 }
129
130 Ok(Vc::cell(
131 results
132 .into_iter()
133 .map(async |p| {
134 Ok(ResolvedVc::upcast(
135 TracedModuleReference::new(Vc::upcast(RawModule::new(Vc::upcast(
136 FileSource::new(p),
137 ))))
138 .to_resolved()
139 .await?,
140 ))
141 })
142 .try_join()
143 .await?,
144 ))
145}