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 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 match &realpath.path_result {
125 Ok(path) => {
126 results.insert(path.clone());
127 }
128 Err(e) => bail!(e.as_error_message(file, &realpath)),
129 }
130 }
131 PatternMatch::Directory(..) => {}
132 }
133 }
134
135 Ok(Vc::cell(
136 results
137 .into_iter()
138 .map(async |p| {
139 Ok(ResolvedVc::upcast(
140 TracedModuleReference::new(Vc::upcast(RawModule::new(Vc::upcast(
141 FileSource::new(p),
142 ))))
143 .to_resolved()
144 .await?,
145 ))
146 })
147 .try_join()
148 .await?,
149 ))
150}