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