Skip to main content

turbopack_core/
package_json.rs

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