Skip to content

Commit 66acc83

Browse files
authored
Turbopack next/font: Use a custom enum instead of Result for failed local font files (vercel#78941)
Previously, we used `anyhow::Result` for this, but this conflicts with how we use `Error` in Turbo Tasks as system-level exceptions. Test Plan: `pnpm test-dev-turbo test/e2e/app-dir/next-font/next-font.test.ts`
1 parent 3719be2 commit 66acc83

File tree

4 files changed

+103
-57
lines changed

4 files changed

+103
-57
lines changed

crates/next-core/src/next_font/font_fallback.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ impl FontFallback {
6060
}
6161

6262
#[turbo_tasks::value(transparent)]
63-
pub(crate) struct FontFallbacks(Vec<ResolvedVc<FontFallback>>);
63+
pub(crate) struct FontFallbacks(pub Vec<ResolvedVc<FontFallback>>);
6464

6565
#[turbo_tasks::value_impl]
6666
impl FontFallbacks {
Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
1-
use thiserror::Error;
1+
use std::fmt::Display;
2+
3+
use serde::{Deserialize, Serialize};
24
use turbo_rcstr::RcStr;
5+
use turbo_tasks::{trace::TraceRawVcs, NonLocalValue};
6+
7+
pub(crate) enum FontResult<T> {
8+
Ok(T),
9+
FontFileNotFound(FontFileNotFound),
10+
}
11+
12+
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, NonLocalValue, TraceRawVcs)]
13+
pub(crate) struct FontFileNotFound(pub RcStr);
314

4-
#[derive(Debug, Error)]
5-
pub enum FontError {
6-
#[error("could not find font file")]
7-
FontFileNotFound(RcStr),
15+
impl Display for FontFileNotFound {
16+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17+
write!(f, "Font file not found: Can't resolve {}'", self.0)
18+
}
819
}

crates/next-core/src/next_font/local/font_fallback.rs

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use turbo_tasks::{ResolvedVc, Vc};
77
use turbo_tasks_fs::{FileContent, FileSystemPath};
88

99
use super::{
10+
errors::{FontFileNotFound, FontResult},
1011
options::{FontDescriptor, FontDescriptors, FontWeight, NextFontLocalOptions},
1112
request::AdjustFontFallback,
1213
};
@@ -15,10 +16,15 @@ use crate::next_font::{
1516
AutomaticFontFallback, DefaultFallbackFont, FontAdjustment, FontFallback, FontFallbacks,
1617
DEFAULT_SANS_SERIF_FONT, DEFAULT_SERIF_FONT,
1718
},
18-
local::errors::FontError,
1919
util::{get_scoped_font_family, FontFamilyType},
2020
};
2121

22+
#[turbo_tasks::value(shared)]
23+
pub(crate) enum FontFallbackResult {
24+
Ok(ResolvedVc<FontFallbacks>),
25+
FontFileNotFound(FontFileNotFound),
26+
}
27+
2228
// From
2329
// https://github.com/vercel/next.js/blob/7457be0c74e64b4d0617943ed27f4d557cc916be/packages/font/src/local/get-fallback-metrics-from-font-file.ts#L34
2430
static AVG_CHARACTERS: &str = "aaabcdeeeefghiijklmnnoopqrrssttuvwxyz ";
@@ -29,56 +35,76 @@ static BOLD_WEIGHT: f64 = 700.0;
2935
pub(super) async fn get_font_fallbacks(
3036
lookup_path: Vc<FileSystemPath>,
3137
options_vc: Vc<NextFontLocalOptions>,
32-
) -> Result<Vc<FontFallbacks>> {
38+
) -> Result<Vc<FontFallbackResult>> {
3339
let options = &*options_vc.await?;
34-
let mut font_fallbacks = vec![];
3540
let scoped_font_family =
3641
get_scoped_font_family(FontFamilyType::Fallback.cell(), options_vc.font_family());
3742

43+
let mut font_fallbacks = vec![];
3844
match options.adjust_font_fallback {
39-
AdjustFontFallback::Arial => font_fallbacks.push(
40-
FontFallback::Automatic(AutomaticFontFallback {
41-
scoped_font_family: scoped_font_family.to_resolved().await?,
42-
local_font_family: ResolvedVc::cell("Arial".into()),
43-
adjustment: Some(
44-
get_font_adjustment(lookup_path, options_vc, &DEFAULT_SANS_SERIF_FONT).await?,
45+
AdjustFontFallback::Arial => {
46+
let adjustment =
47+
get_font_adjustment(lookup_path, options_vc, &DEFAULT_SANS_SERIF_FONT).await?;
48+
49+
match adjustment {
50+
FontResult::Ok(adjustment) => font_fallbacks.push(
51+
FontFallback::Automatic(AutomaticFontFallback {
52+
scoped_font_family: scoped_font_family.to_resolved().await?,
53+
local_font_family: ResolvedVc::cell("Arial".into()),
54+
adjustment: Some(adjustment),
55+
})
56+
.resolved_cell(),
4557
),
46-
})
47-
.resolved_cell(),
48-
),
49-
AdjustFontFallback::TimesNewRoman => font_fallbacks.push(
50-
FontFallback::Automatic(AutomaticFontFallback {
51-
scoped_font_family: scoped_font_family.to_resolved().await?,
52-
local_font_family: ResolvedVc::cell("Times New Roman".into()),
53-
adjustment: Some(
54-
get_font_adjustment(lookup_path, options_vc, &DEFAULT_SERIF_FONT).await?,
58+
FontResult::FontFileNotFound(err) => {
59+
return Ok(FontFallbackResult::FontFileNotFound(err).cell())
60+
}
61+
};
62+
}
63+
AdjustFontFallback::TimesNewRoman => {
64+
let adjustment =
65+
get_font_adjustment(lookup_path, options_vc, &DEFAULT_SERIF_FONT).await?;
66+
67+
match adjustment {
68+
FontResult::Ok(adjustment) => font_fallbacks.push(
69+
FontFallback::Automatic(AutomaticFontFallback {
70+
scoped_font_family: scoped_font_family.to_resolved().await?,
71+
local_font_family: ResolvedVc::cell("Times New Roman".into()),
72+
adjustment: Some(adjustment),
73+
})
74+
.resolved_cell(),
5575
),
56-
})
57-
.resolved_cell(),
58-
),
76+
FontResult::FontFileNotFound(err) => {
77+
return Ok(FontFallbackResult::FontFileNotFound(err).cell())
78+
}
79+
};
80+
}
5981
AdjustFontFallback::None => (),
6082
};
6183

6284
if let Some(fallback) = &options.fallback {
6385
font_fallbacks.push(FontFallback::Manual(fallback.clone()).resolved_cell());
6486
}
6587

66-
Ok(Vc::cell(font_fallbacks))
88+
Ok(FontFallbackResult::Ok(FontFallbacks(font_fallbacks).resolved_cell()).cell())
6789
}
6890

6991
async fn get_font_adjustment(
7092
lookup_path: Vc<FileSystemPath>,
7193
options: Vc<NextFontLocalOptions>,
7294
fallback_font: &DefaultFallbackFont,
73-
) -> Result<FontAdjustment> {
95+
) -> Result<FontResult<FontAdjustment>> {
7496
let options = &*options.await?;
7597
let main_descriptor = pick_font_for_fallback_generation(&options.fonts)?;
7698
let font_file = &*lookup_path
7799
.join(main_descriptor.path.clone())
78100
.read()
79101
.await?;
80102
let font_file_rope = match font_file {
81-
FileContent::NotFound => bail!(FontError::FontFileNotFound(main_descriptor.path.clone())),
103+
FileContent::NotFound => {
104+
return Ok(FontResult::FontFileNotFound(FontFileNotFound(
105+
main_descriptor.path.clone(),
106+
)));
107+
}
82108
FileContent::Content(file) => file.content(),
83109
};
84110

@@ -106,12 +132,12 @@ async fn get_font_adjustment(
106132
None => 1.0,
107133
};
108134

109-
Ok(FontAdjustment {
135+
Ok(FontResult::Ok(FontAdjustment {
110136
ascent: font.hhea_table.ascender as f64 / (units_per_em * size_adjust),
111137
descent: font.hhea_table.descender as f64 / (units_per_em * size_adjust),
112138
line_gap: font.hhea_table.line_gap as f64 / (units_per_em * size_adjust),
113139
size_adjust,
114-
})
140+
}))
115141
}
116142

117143
fn calc_average_width(font: &mut Font<DynamicFontTableProvider>) -> Option<f32> {

crates/next-core/src/next_font/local/mod.rs

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use anyhow::{bail, Context, Result};
2+
use font_fallback::FontFallbackResult;
23
use indoc::formatdoc;
34
use serde::{Deserialize, Serialize};
45
use turbo_rcstr::RcStr;
@@ -31,12 +32,12 @@ use super::{
3132
use crate::{
3233
next_app::metadata::split_extension,
3334
next_font::{
34-
local::{errors::FontError, options::FontWeight},
35+
local::options::FontWeight,
3536
util::{get_request_hash, get_request_id},
3637
},
3738
};
3839

39-
mod errors;
40+
pub mod errors;
4041
pub mod font_fallback;
4142
pub mod options;
4243
pub mod request;
@@ -106,33 +107,26 @@ impl BeforeResolvePlugin for NextFontLocalResolvePlugin {
106107
let request_hash = get_request_hash(&query).await?;
107108
let qstr = qstring::QString::from(query.as_str());
108109
let options_vc = font_options_from_query_map(**query_vc);
109-
let font_fallbacks = get_font_fallbacks(lookup_path, options_vc);
110-
let properties = get_font_css_properties(options_vc, font_fallbacks).await;
111110

111+
let font_fallbacks = &*get_font_fallbacks(lookup_path, options_vc).await?;
112112
let lookup_path = lookup_path.to_resolved().await?;
113-
if let Err(e) = &properties {
114-
for source_error in e.chain() {
115-
if let Some(FontError::FontFileNotFound(font_path)) =
116-
source_error.downcast_ref::<FontError>()
117-
{
118-
FontResolvingIssue {
119-
origin_path: lookup_path,
120-
font_path: ResolvedVc::cell(font_path.clone()),
121-
}
122-
.resolved_cell()
123-
.emit();
124-
125-
return Ok(ResolveResultOption::some(*ResolveResult::primary(
126-
ResolveResultItem::Error(ResolvedVc::cell(
127-
format!("Font file not found: Can't resolve {}'", font_path)
128-
.into(),
129-
)),
130-
)));
113+
let font_fallbacks = match font_fallbacks {
114+
FontFallbackResult::FontFileNotFound(err) => {
115+
FontResolvingIssue {
116+
origin_path: lookup_path,
117+
font_path: ResolvedVc::cell(err.0.clone()),
131118
}
119+
.resolved_cell()
120+
.emit();
121+
122+
return Ok(ResolveResultOption::some(*ResolveResult::primary(
123+
ResolveResultItem::Error(ResolvedVc::cell(err.to_string().into())),
124+
)));
132125
}
133-
}
126+
FontFallbackResult::Ok(font_fallbacks) => *font_fallbacks,
127+
};
134128

135-
let properties = properties?;
129+
let properties = get_font_css_properties(options_vc, *font_fallbacks).await?;
136130
let file_content = formatdoc!(
137131
r#"
138132
import cssModule from "@vercel/turbopack-next/internal/font/local/cssmodule.module.css?{}";
@@ -194,7 +188,22 @@ impl BeforeResolvePlugin for NextFontLocalResolvePlugin {
194188
)
195189
.into(),
196190
);
197-
let fallback = get_font_fallbacks(lookup_path, options);
191+
let fallback = &*get_font_fallbacks(lookup_path, options).await?;
192+
let fallback = match fallback {
193+
FontFallbackResult::FontFileNotFound(err) => {
194+
FontResolvingIssue {
195+
origin_path: lookup_path.to_resolved().await?,
196+
font_path: ResolvedVc::cell(err.0.clone()),
197+
}
198+
.resolved_cell()
199+
.emit();
200+
201+
return Ok(ResolveResultOption::some(*ResolveResult::primary(
202+
ResolveResultItem::Error(ResolvedVc::cell(err.to_string().into())),
203+
)));
204+
}
205+
FontFallbackResult::Ok(font_fallbacks) => **font_fallbacks,
206+
};
198207

199208
let stylesheet = build_stylesheet(
200209
font_options_from_query_map(**query_vc),

0 commit comments

Comments
 (0)