Skip to main content

turbopack_core/
package_json.rs

1use std::ops::Deref;
2
3use anyhow::Result;
4use async_trait::async_trait;
5use serde_json::Value as JsonValue;
6use turbo_rcstr::{RcStr, rcstr};
7use turbo_tasks::{NonLocalValue, ReadRef, ResolvedVc, Vc, debug::ValueDebugFormat};
8use turbo_tasks_fs::{FileJsonContent, FileSystemPath};
9
10use super::issue::Issue;
11use crate::{
12    asset::Asset,
13    issue::{IssueExt, IssueSource, IssueStage, StyledString},
14    source::Source,
15};
16
17/// PackageJson wraps the parsed JSON content of a `package.json` file. The
18/// wrapper is necessary so that we can reference the [FileJsonContent]'s inner
19/// [serde_json::Value] without cloning it.
20#[derive(PartialEq, Eq, ValueDebugFormat, NonLocalValue)]
21pub struct PackageJson(ReadRef<FileJsonContent>);
22
23impl Deref for PackageJson {
24    type Target = JsonValue;
25    fn deref(&self) -> &Self::Target {
26        match &*self.0 {
27            FileJsonContent::Content(json) => json,
28            _ => unreachable!("PackageJson is guaranteed to hold Content"),
29        }
30    }
31}
32
33#[turbo_tasks::value(transparent, serialization = "skip")]
34pub struct OptionPackageJson(Option<PackageJson>);
35
36/// Reads a package.json file (if it exists). If the file is unparsable, it
37/// emits a useful [Issue] pointing to the invalid location.
38#[turbo_tasks::function]
39pub async fn read_package_json(path: ResolvedVc<Box<dyn Source>>) -> Result<Vc<OptionPackageJson>> {
40    let read = path.content().parse_json().await?;
41    match &*read {
42        FileJsonContent::Content(_) => Ok(OptionPackageJson(Some(PackageJson(read))).cell()),
43        FileJsonContent::NotFound => Ok(OptionPackageJson(None).cell()),
44        FileJsonContent::Unparsable(e) => {
45            let error_message = RcStr::from(format!(
46                "package.json is not parseable: invalid JSON: {}",
47                e.message
48            ));
49
50            let source = IssueSource::from_unparsable_json(path, e);
51            PackageJsonIssue {
52                error_message,
53                source,
54            }
55            .resolved_cell()
56            .emit();
57            Ok(OptionPackageJson(None).cell())
58        }
59    }
60}
61
62/// Reusable Issue struct representing any problem with a `package.json`
63#[turbo_tasks::value(shared)]
64pub struct PackageJsonIssue {
65    pub error_message: RcStr,
66    pub source: IssueSource,
67}
68
69#[async_trait]
70#[turbo_tasks::value_impl]
71impl Issue for PackageJsonIssue {
72    async fn title(&self) -> Result<StyledString> {
73        Ok(StyledString::Text(rcstr!(
74            "Error parsing package.json file"
75        )))
76    }
77
78    fn stage(&self) -> IssueStage {
79        IssueStage::Parse
80    }
81
82    async fn file_path(&self) -> Result<FileSystemPath> {
83        self.source.file_path().await
84    }
85
86    async fn description(&self) -> Result<Option<StyledString>> {
87        Ok(Some(StyledString::Text(self.error_message.clone())))
88    }
89
90    fn source(&self) -> Option<IssueSource> {
91        Some(self.source)
92    }
93}