Skip to content

Commit

Permalink
Implement package.json's imports field (#4895)
Browse files Browse the repository at this point in the history
### Description

The [imports field](https://nodejs.org/api/packages.html#imports) is
similar to `exports`. While `exports` allows a package to control how
others can import it, `imports` allows it control how it wants to import
other packages.

The majority of the code can be shared with the already-implemented
`ExportsField` and `AliasMap`, with just special parsing logic. Besides
that, I just needed to setup the conditional for imports and some
cleanup.

### Testing Instructions

```bash
RUST_BACKTRACE=1 cargo nextest run -E 'test(snapshot__imports__subpath_imports)'
```

Fixes WEB-50
Pair: vercel/next.js#49636
fix #4799

---------

Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com>
  • Loading branch information
jridgewell and sokra committed May 18, 2023
1 parent 0e22bda commit e3a5cfb
Show file tree
Hide file tree
Showing 25 changed files with 2,390 additions and 296 deletions.
2 changes: 1 addition & 1 deletion crates/turbo-tasks-memory/tests/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::sync::Mutex;

use anyhow::Result;
use turbo_tasks::{debug::ValueDebug, TaskInput};
use turbo_tasks::debug::ValueDebug;
use turbo_tasks_testing::{register, run};

register!();
Expand Down
1 change: 0 additions & 1 deletion crates/turbopack-core/src/issue/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
pub mod analyze;
pub mod code_gen;
pub mod package_json;
pub mod resolve;
pub mod unsupported_module;

Expand Down
34 changes: 0 additions & 34 deletions crates/turbopack-core/src/issue/package_json.rs

This file was deleted.

1 change: 1 addition & 0 deletions crates/turbopack-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod error;
pub mod ident;
pub mod introspect;
pub mod issue;
pub mod package_json;
pub mod plugin;
pub mod proxied_asset;
pub mod reference;
Expand Down
85 changes: 85 additions & 0 deletions crates/turbopack-core/src/package_json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use std::{fmt::Write, ops::Deref};

use anyhow::Result;
use serde_json::Value as JsonValue;
use turbo_tasks::{debug::ValueDebugFormat, primitives::StringVc, trace::TraceRawVcs};
use turbo_tasks_fs::{FileContent, FileJsonContent, FileJsonContentReadRef, FileSystemPathVc};

use super::issue::{Issue, IssueVc};

/// PackageJson wraps the parsed JSON content of a `package.json` file. The
/// wrapper is necessary so that we can reference the [FileJsonContent]'s inner
/// [serde_json::Value] without cloning it.
#[derive(PartialEq, Eq, ValueDebugFormat, TraceRawVcs)]
pub struct PackageJson(FileJsonContentReadRef);

impl Deref for PackageJson {
type Target = JsonValue;
fn deref(&self) -> &Self::Target {
match &*self.0 {
FileJsonContent::Content(json) => json,
_ => unreachable!("PackageJson is guaranteed to hold Content"),
}
}
}

#[turbo_tasks::value(transparent, serialization = "none")]
pub struct OptionPackageJson(Option<PackageJson>);

/// Reads a package.json file (if it exists). If the file is unparseable, it
/// emits a useful [Issue] pointing to the invalid location.
#[turbo_tasks::function]
pub async fn read_package_json(path: FileSystemPathVc) -> Result<OptionPackageJsonVc> {
let read = path.read_json().await?;
match &*read {
FileJsonContent::Content(_) => Ok(OptionPackageJson(Some(PackageJson(read))).cell()),
FileJsonContent::NotFound => Ok(OptionPackageJson(None).cell()),
FileJsonContent::Unparseable(e) => {
let mut message = "package.json is not parseable: invalid JSON: ".to_string();
if let FileContent::Content(content) = &*path.read().await? {
let text = content.content().to_str()?;
e.write_with_content(&mut message, &text)?;
} else {
write!(message, "{}", e)?;
}
PackageJsonIssue {
error_message: message,
path,
}
.cell()
.as_issue()
.emit();
Ok(OptionPackageJson(None).cell())
}
}
}

/// Reusable Issue struct representing any problem with a `package.json`
#[turbo_tasks::value(shared)]
pub struct PackageJsonIssue {
pub path: FileSystemPathVc,
pub error_message: String,
}

#[turbo_tasks::value_impl]
impl Issue for PackageJsonIssue {
#[turbo_tasks::function]
fn title(&self) -> StringVc {
StringVc::cell("Error parsing package.json file".to_string())
}

#[turbo_tasks::function]
fn category(&self) -> StringVc {
StringVc::cell("parse".to_string())
}

#[turbo_tasks::function]
fn context(&self) -> FileSystemPathVc {
self.path
}

#[turbo_tasks::function]
fn description(&self) -> StringVc {
StringVc::cell(self.error_message.clone())
}
}

0 comments on commit e3a5cfb

Please sign in to comment.