Skip to main content

turbopack_core/
file_source.rs

1use anyhow::{Result, bail};
2use turbo_rcstr::RcStr;
3use turbo_tasks::Vc;
4use turbo_tasks_fs::{
5    FileContent, FileSystemEntryType, FileSystemPath, LinkContent, WriteLinkContent,
6    WriteLinkTargetType,
7};
8
9use crate::{
10    asset::{Asset, AssetContent},
11    ident::AssetIdent,
12    source::Source,
13};
14
15/// The raw [Source]. It represents raw content from a path without any
16/// references to other [Source]s.
17#[turbo_tasks::value]
18pub struct FileSource {
19    path: FileSystemPath,
20    query: RcStr,
21    fragment: RcStr,
22}
23
24impl FileSource {
25    pub fn new(path: FileSystemPath) -> Vc<Self> {
26        FileSource::new_with_query_and_fragment(path, RcStr::default(), RcStr::default())
27    }
28    pub fn new_with_query(path: FileSystemPath, query: RcStr) -> Vc<Self> {
29        FileSource::new_with_query_and_fragment(path, query, RcStr::default())
30    }
31}
32
33#[turbo_tasks::value_impl]
34impl FileSource {
35    #[turbo_tasks::function]
36    pub fn new_with_query_and_fragment(
37        path: FileSystemPath,
38        query: RcStr,
39        fragment: RcStr,
40    ) -> Vc<Self> {
41        Self::cell(FileSource {
42            path,
43            query,
44            fragment,
45        })
46    }
47}
48
49#[turbo_tasks::value_impl]
50impl Source for FileSource {
51    #[turbo_tasks::function]
52    fn ident(&self) -> Vc<AssetIdent> {
53        AssetIdent::from_path(self.path.clone())
54            .with_query(self.query.clone())
55            .with_fragment(self.fragment.clone())
56            .into_vc()
57    }
58
59    #[turbo_tasks::function]
60    fn description(&self) -> Vc<RcStr> {
61        Vc::cell(format!("file content of {}", self.path).into())
62    }
63}
64
65#[turbo_tasks::value_impl]
66impl Asset for FileSource {
67    #[turbo_tasks::function]
68    async fn content(&self) -> Result<Vc<AssetContent>> {
69        let file_type = &*self.path.get_type().await?;
70        match file_type {
71            FileSystemEntryType::Symlink => match &*self.path.read_link().await? {
72                LinkContent::Link { target } => {
73                    let target_fs_path = target.file_system_path();
74                    let write_target_type = match *target_fs_path.get_type().await? {
75                        FileSystemEntryType::Directory => {
76                            WriteLinkTargetType::DirectoryOrJunctionPoint
77                        }
78                        FileSystemEntryType::Symlink
79                            if *target_fs_path.is_junction_point().await? =>
80                        {
81                            WriteLinkTargetType::DirectoryOrJunctionPoint
82                        }
83                        _ => WriteLinkTargetType::FileNonPortable,
84                    };
85                    Ok(AssetContent::Redirect(WriteLinkContent {
86                        target: target_fs_path.clone(),
87                        target_type: write_target_type,
88                    })
89                    .cell())
90                }
91                LinkContent::NotFound => {
92                    // This should not normally happen because the path was already identified as
93                    // a symlink, but it may be removed between get_type and read_link.
94                    Ok(AssetContent::File(FileContent::NotFound.resolved_cell()).cell())
95                }
96                LinkContent::Invalid { reason } => bail!("Invalid symlink: {reason}"),
97            },
98            FileSystemEntryType::File => {
99                Ok(AssetContent::File(self.path.read().to_resolved().await?).cell())
100            }
101            FileSystemEntryType::NotFound => {
102                Ok(AssetContent::File(FileContent::NotFound.resolved_cell()).cell())
103            }
104            _ => bail!("Invalid file type {:?}", file_type),
105        }
106    }
107}