next/font: Align with Next.js integration test suite (vercel/turbo#4039)
This:
* Adds a compile-time feature, `__internal_nextjs_integration_test`,
which is set when building Turbopack for the Next.js integration test
suite.
* When built with `__internal_nextjs_integration_test`, expects the
environment variable `NEXT_FONT_GOOGLE_MOCKED_RESPONSES` to be set with
an absolute path to a JavaScript file exporting a mapping of urls to
stylesheet text. This is used in place of making real requests to Google
Fonts.
* Addresses some small bugs in next/font/google to bring the
implementation in line with the urls generated by Next.js without
Turbopack.
* Changes classnames generated by next/font to use 6 character hashes,
bringing it in alignment with Next.js without Turbopack.
Test Plan:
* Adjusted existing unit tests
* In Next.js, `node run-tests.js -c 1 test/e2e/next-font/index.test.ts`
### Description
<!--
✍️ Write a short summary of your work.
If necessary, include relevant screenshots.
-->
### Testing Instructions
<!--
Give a quick description of steps to test your changes.
-->
<!--
When the below is checked (default) our PR bot will automatically
assign labels to your PR based on the content to help the team
organize and review it faster.
-->
- [x] Auto label
This commit is contained in:
parent
ad12551513
commit
5f76b5d9e4
19 changed files with 339 additions and 127 deletions
|
@ -40,3 +40,5 @@ turbo-tasks-build = { workspace = true }
|
|||
next-font-local = []
|
||||
native-tls = ["turbo-tasks-fetch/native-tls"]
|
||||
rustls-tls = ["turbo-tasks-fetch/rustls-tls"]
|
||||
# Internal only. Enabled when building for the Next.js integration test suite.
|
||||
__internal_nextjs_integration_test = []
|
||||
|
|
|
@ -99,9 +99,10 @@ async fn next_client_transition(
|
|||
ty,
|
||||
next_config,
|
||||
);
|
||||
let client_runtime_entries = get_client_runtime_entries(project_path, env, ty, next_config);
|
||||
let client_runtime_entries =
|
||||
get_client_runtime_entries(project_path, env, ty, next_config, execution_context);
|
||||
let client_resolve_options_context =
|
||||
get_client_resolve_options_context(project_path, ty, next_config);
|
||||
get_client_resolve_options_context(project_path, ty, next_config, execution_context);
|
||||
|
||||
Ok(NextClientTransition {
|
||||
is_app: true,
|
||||
|
@ -137,6 +138,7 @@ fn next_ssr_client_module_transition(
|
|||
project_path,
|
||||
ty,
|
||||
next_config,
|
||||
execution_context,
|
||||
),
|
||||
ssr_environment: get_server_compile_time_info(ty, process_env, server_addr),
|
||||
}
|
||||
|
@ -157,7 +159,7 @@ fn next_layout_entry_transition(
|
|||
let ty = Value::new(ServerContextType::AppRSC { app_dir });
|
||||
let rsc_compile_time_info = get_server_compile_time_info(ty, process_env, server_addr);
|
||||
let rsc_resolve_options_context =
|
||||
get_server_resolve_options_context(project_path, ty, next_config);
|
||||
get_server_resolve_options_context(project_path, ty, next_config, execution_context);
|
||||
let rsc_module_options_context =
|
||||
get_server_module_options_context(project_path, execution_context, ty, next_config);
|
||||
|
||||
|
@ -179,6 +181,7 @@ fn next_route_transition(
|
|||
next_config: NextConfigVc,
|
||||
server_addr: ServerAddrVc,
|
||||
output_path: FileSystemPathVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> TransitionVc {
|
||||
let server_ty = Value::new(ServerContextType::AppRoute { app_dir });
|
||||
|
||||
|
@ -194,7 +197,7 @@ fn next_route_transition(
|
|||
)
|
||||
.build();
|
||||
let edge_resolve_options_context =
|
||||
get_edge_resolve_options_context(project_path, server_ty, next_config);
|
||||
get_edge_resolve_options_context(project_path, server_ty, next_config, execution_context);
|
||||
|
||||
NextEdgeTransition {
|
||||
edge_compile_time_info,
|
||||
|
@ -235,6 +238,7 @@ fn app_context(
|
|||
next_config,
|
||||
server_addr,
|
||||
output_path,
|
||||
execution_context,
|
||||
),
|
||||
);
|
||||
transitions.insert(
|
||||
|
@ -295,7 +299,7 @@ fn app_context(
|
|||
TransitionsByNameVc::cell(transitions),
|
||||
get_server_compile_time_info(ssr_ty, env, server_addr),
|
||||
get_server_module_options_context(project_path, execution_context, ssr_ty, next_config),
|
||||
get_server_resolve_options_context(project_path, ssr_ty, next_config),
|
||||
get_server_resolve_options_context(project_path, ssr_ty, next_config, execution_context),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -36,7 +36,8 @@ pub async fn get_fallback_page(
|
|||
next_config: NextConfigVc,
|
||||
) -> Result<DevHtmlAssetVc> {
|
||||
let ty = Value::new(ClientContextType::Fallback);
|
||||
let resolve_options_context = get_client_resolve_options_context(project_path, ty, next_config);
|
||||
let resolve_options_context =
|
||||
get_client_resolve_options_context(project_path, ty, next_config, execution_context);
|
||||
let module_options_context = get_client_module_options_context(
|
||||
project_path,
|
||||
execution_context,
|
||||
|
@ -50,10 +51,10 @@ pub async fn get_fallback_page(
|
|||
client_compile_time_info.environment(),
|
||||
ty,
|
||||
);
|
||||
let entries = get_client_runtime_entries(project_path, env, ty, next_config);
|
||||
let entries = get_client_runtime_entries(project_path, env, ty, next_config, execution_context);
|
||||
|
||||
let mut import_map = ImportMap::empty();
|
||||
insert_next_shared_aliases(&mut import_map, project_path).await?;
|
||||
insert_next_shared_aliases(&mut import_map, project_path, execution_context).await?;
|
||||
insert_alias_option(
|
||||
&mut import_map,
|
||||
project_path,
|
||||
|
|
|
@ -84,8 +84,10 @@ pub async fn get_client_resolve_options_context(
|
|||
project_path: FileSystemPathVc,
|
||||
ty: Value<ClientContextType>,
|
||||
next_config: NextConfigVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> Result<ResolveOptionsContextVc> {
|
||||
let next_client_import_map = get_next_client_import_map(project_path, ty, next_config);
|
||||
let next_client_import_map =
|
||||
get_next_client_import_map(project_path, ty, next_config, execution_context);
|
||||
let next_client_fallback_import_map = get_next_client_fallback_import_map(ty);
|
||||
let next_client_resolved_map = get_next_client_resolved_map(project_path, project_path);
|
||||
let module_options_context = ResolveOptionsContext {
|
||||
|
@ -119,7 +121,8 @@ pub async fn get_client_module_options_context(
|
|||
next_config: NextConfigVc,
|
||||
) -> Result<ModuleOptionsContextVc> {
|
||||
let custom_rules = get_next_client_transforms_rules(ty.into_value()).await?;
|
||||
let resolve_options_context = get_client_resolve_options_context(project_path, ty, next_config);
|
||||
let resolve_options_context =
|
||||
get_client_resolve_options_context(project_path, ty, next_config, execution_context);
|
||||
let enable_react_refresh =
|
||||
assert_can_resolve_react_refresh(project_path, resolve_options_context)
|
||||
.await?
|
||||
|
@ -182,7 +185,8 @@ pub fn get_client_asset_context(
|
|||
ty: Value<ClientContextType>,
|
||||
next_config: NextConfigVc,
|
||||
) -> AssetContextVc {
|
||||
let resolve_options_context = get_client_resolve_options_context(project_path, ty, next_config);
|
||||
let resolve_options_context =
|
||||
get_client_resolve_options_context(project_path, ty, next_config, execution_context);
|
||||
let module_options_context = get_client_module_options_context(
|
||||
project_path,
|
||||
execution_context,
|
||||
|
@ -244,8 +248,10 @@ pub async fn get_client_runtime_entries(
|
|||
env: ProcessEnvVc,
|
||||
ty: Value<ClientContextType>,
|
||||
next_config: NextConfigVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> Result<RuntimeEntriesVc> {
|
||||
let resolve_options_context = get_client_resolve_options_context(project_root, ty, next_config);
|
||||
let resolve_options_context =
|
||||
get_client_resolve_options_context(project_root, ty, next_config, execution_context);
|
||||
let enable_react_refresh =
|
||||
assert_can_resolve_react_refresh(project_root, resolve_options_context)
|
||||
.await?
|
||||
|
|
|
@ -63,6 +63,7 @@ impl NextClientChunksTransitionVc {
|
|||
project_path,
|
||||
ty,
|
||||
next_config,
|
||||
execution_context,
|
||||
),
|
||||
client_compile_time_info,
|
||||
server_root,
|
||||
|
|
|
@ -10,6 +10,7 @@ use turbopack_core::{
|
|||
ServerAddrVc,
|
||||
},
|
||||
};
|
||||
use turbopack_node::execution_context::ExecutionContextVc;
|
||||
|
||||
use crate::{
|
||||
next_config::NextConfigVc, next_import_map::get_next_edge_import_map,
|
||||
|
@ -47,8 +48,10 @@ pub async fn get_edge_resolve_options_context(
|
|||
project_path: FileSystemPathVc,
|
||||
ty: Value<ServerContextType>,
|
||||
next_config: NextConfigVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> Result<ResolveOptionsContextVc> {
|
||||
let next_edge_import_map = get_next_edge_import_map(project_path, ty, next_config);
|
||||
let next_edge_import_map =
|
||||
get_next_edge_import_map(project_path, ty, next_config, execution_context);
|
||||
|
||||
let resolve_options_context = ResolveOptionsContext {
|
||||
enable_node_modules: Some(project_path.root().resolve().await?),
|
||||
|
|
|
@ -3,11 +3,9 @@ use indexmap::IndexMap;
|
|||
use indoc::formatdoc;
|
||||
use once_cell::sync::Lazy;
|
||||
use turbo_tasks::primitives::{OptionStringVc, OptionU16Vc, StringVc, U32Vc};
|
||||
use turbo_tasks_fetch::fetch;
|
||||
use turbo_tasks_fs::{json::parse_json_with_source_context, FileContent, FileSystemPathVc};
|
||||
use turbo_tasks_hash::hash_xxh3_hash64;
|
||||
use turbopack_core::{
|
||||
issue::IssueSeverity,
|
||||
resolve::{
|
||||
options::{
|
||||
ImportMapResult, ImportMapResultVc, ImportMapping, ImportMappingReplacement,
|
||||
|
@ -19,6 +17,7 @@ use turbopack_core::{
|
|||
},
|
||||
virtual_asset::VirtualAssetVc,
|
||||
};
|
||||
use turbopack_node::execution_context::ExecutionContextVc;
|
||||
|
||||
use self::options::FontWeights;
|
||||
use crate::{
|
||||
|
@ -123,13 +122,17 @@ impl ImportMappingReplacement for NextFontGoogleReplacer {
|
|||
#[turbo_tasks::value(shared)]
|
||||
pub struct NextFontGoogleCssModuleReplacer {
|
||||
project_path: FileSystemPathVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl NextFontGoogleCssModuleReplacerVc {
|
||||
#[turbo_tasks::function]
|
||||
pub fn new(project_path: FileSystemPathVc) -> Self {
|
||||
Self::cell(NextFontGoogleCssModuleReplacer { project_path })
|
||||
pub fn new(project_path: FileSystemPathVc, execution_context: ExecutionContextVc) -> Self {
|
||||
Self::cell(NextFontGoogleCssModuleReplacer {
|
||||
project_path,
|
||||
execution_context,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,22 +161,31 @@ impl ImportMappingReplacement for NextFontGoogleCssModuleReplacer {
|
|||
let css_virtual_path = next_js_file_path("internal/font/google")
|
||||
.join(&format!("/{}.module.css", get_request_id(*query_vc).await?));
|
||||
|
||||
// When running Next.js integration tests, use the mock data available in
|
||||
// process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES instead of making real
|
||||
// requests to Google Fonts.
|
||||
#[cfg(feature = "__internal_nextjs_integration_test")]
|
||||
let stylesheet_str = get_mock_stylesheet(&stylesheet_url.await?, self.execution_context)
|
||||
.await?
|
||||
.map(StringVc::cell);
|
||||
|
||||
#[cfg(not(feature = "__internal_nextjs_integration_test"))]
|
||||
let stylesheet_str = {
|
||||
use turbo_tasks_fetch::fetch;
|
||||
use turbopack_core::issue::IssueSeverity;
|
||||
|
||||
let stylesheet_res = fetch(
|
||||
stylesheet_url,
|
||||
OptionStringVc::cell(Some(
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like \
|
||||
Gecko) Chrome/104.0.0.0 Safari/537.36"
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, \
|
||||
like Gecko) Chrome/104.0.0.0 Safari/537.36"
|
||||
.to_owned(),
|
||||
)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let stylesheet = match &*stylesheet_res {
|
||||
Ok(r) => Some(
|
||||
update_stylesheet(r.await?.body.to_string(), options, scoped_font_family)
|
||||
.await?
|
||||
.clone_value(),
|
||||
),
|
||||
match &*stylesheet_res {
|
||||
Ok(r) => Some(r.await?.body.to_string()),
|
||||
Err(err) => {
|
||||
// Inform the user of the failure to retreive the stylesheet, but don't
|
||||
// propagate this error. We don't want e.g. offline connections to prevent page
|
||||
|
@ -188,6 +200,16 @@ impl ImportMappingReplacement for NextFontGoogleCssModuleReplacer {
|
|||
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let stylesheet = match stylesheet_str {
|
||||
Some(s) => Some(
|
||||
update_stylesheet(s, options, scoped_font_family)
|
||||
.await?
|
||||
.clone_value(),
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let properties = get_font_css_properties(scoped_font_family, options).await?;
|
||||
|
@ -252,11 +274,16 @@ async fn update_stylesheet(
|
|||
#[turbo_tasks::function]
|
||||
async fn get_scoped_font_family(query_vc: QueryMapVc) -> Result<StringVc> {
|
||||
let options = font_options_from_query_map(query_vc).await?;
|
||||
let hash = {
|
||||
let mut hash = format!("{:x?}", *get_request_hash(query_vc).await?);
|
||||
hash.truncate(6);
|
||||
hash
|
||||
};
|
||||
|
||||
Ok(StringVc::cell(format!(
|
||||
"__{}_{:x?}",
|
||||
"__{}_{}",
|
||||
options.font_family.replace(' ', "_"),
|
||||
*get_request_hash(query_vc).await?
|
||||
hash
|
||||
)))
|
||||
}
|
||||
|
||||
|
@ -378,3 +405,99 @@ async fn font_options_from_query_map(query: QueryMapVc) -> Result<NextFontGoogle
|
|||
self::options::options_from_request(&parse_json_with_source_context(json)?, &FONT_DATA)
|
||||
.map(NextFontGoogleOptionsVc::cell)
|
||||
}
|
||||
|
||||
#[cfg(feature = "__internal_nextjs_integration_test")]
|
||||
async fn get_mock_stylesheet(
|
||||
url: &str,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> Result<Option<String>> {
|
||||
use std::{collections::HashMap, path::Path};
|
||||
|
||||
use turbo_tasks::{CompletionVc, Value};
|
||||
use turbo_tasks_env::{CommandLineProcessEnvVc, ProcessEnv};
|
||||
use turbo_tasks_fs::{
|
||||
json::parse_json_rope_with_source_context, DiskFileSystemVc, File, FileSystem,
|
||||
};
|
||||
use turbopack::evaluate_context::node_evaluate_asset_context;
|
||||
use turbopack_core::{context::AssetContext, ident::AssetIdentVc};
|
||||
use turbopack_ecmascript::{
|
||||
EcmascriptInputTransformsVc, EcmascriptModuleAssetType, EcmascriptModuleAssetVc,
|
||||
};
|
||||
use turbopack_node::{
|
||||
evaluate::{evaluate, JavaScriptValue},
|
||||
execution_context::ExecutionContext,
|
||||
};
|
||||
|
||||
let env = CommandLineProcessEnvVc::new().as_process_env();
|
||||
let mocked_response_js = &*env.read("NEXT_FONT_GOOGLE_MOCKED_RESPONSES").await?;
|
||||
let mocked_response_js = mocked_response_js
|
||||
.as_ref()
|
||||
.context("Expected env NEXT_FONT_GOOGLE_MOCKED_RESPONSES")?;
|
||||
|
||||
let response_path = Path::new(&mocked_response_js);
|
||||
let mock_fs = DiskFileSystemVc::new(
|
||||
"mock".to_string(),
|
||||
response_path
|
||||
.parent()
|
||||
.context("Must be valid path")?
|
||||
.to_str()
|
||||
.context("Must exist")?
|
||||
.to_string(),
|
||||
)
|
||||
.as_file_system();
|
||||
|
||||
let ExecutionContext {
|
||||
env,
|
||||
project_root: _,
|
||||
intermediate_output_path,
|
||||
} = *execution_context.await?;
|
||||
let context = node_evaluate_asset_context(None, None);
|
||||
let loader_path = mock_fs.root().join("loader.js");
|
||||
let mocked_response_asset = EcmascriptModuleAssetVc::new(
|
||||
VirtualAssetVc::new(
|
||||
loader_path,
|
||||
File::from(format!(
|
||||
"import data from './{}'; export default function load() {{ return data; }};",
|
||||
response_path
|
||||
.file_name()
|
||||
.context("Must exist")?
|
||||
.to_string_lossy(),
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
.into(),
|
||||
context,
|
||||
Value::new(EcmascriptModuleAssetType::Ecmascript),
|
||||
EcmascriptInputTransformsVc::cell(vec![]),
|
||||
context.compile_time_info(),
|
||||
)
|
||||
.into();
|
||||
|
||||
let root = mock_fs.root();
|
||||
let val = evaluate(
|
||||
loader_path,
|
||||
mocked_response_asset,
|
||||
root,
|
||||
env,
|
||||
AssetIdentVc::from_path(loader_path),
|
||||
context,
|
||||
intermediate_output_path,
|
||||
None,
|
||||
vec![],
|
||||
CompletionVc::immutable(),
|
||||
/* debug */ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match &*val {
|
||||
JavaScriptValue::Value(val) => {
|
||||
let mock_map: HashMap<String, Option<String>> =
|
||||
parse_json_rope_with_source_context(val)?;
|
||||
Ok((mock_map.get(url).context("url not found")?).clone())
|
||||
}
|
||||
JavaScriptValue::Error => panic!("Unexpected error evaluating JS"),
|
||||
JavaScriptValue::Stream(_) => {
|
||||
unimplemented!("Stream not supported now");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ pub fn options_from_request(
|
|||
|
||||
let display = argument
|
||||
.and_then(|a| a.display.to_owned())
|
||||
.unwrap_or_else(|| "optional".to_owned());
|
||||
.unwrap_or_else(|| "swap".to_owned());
|
||||
|
||||
if !ALLOWED_DISPLAY_VALUES.contains(&display.as_ref()) {
|
||||
return Err(anyhow!(
|
||||
|
@ -247,7 +247,7 @@ mod tests {
|
|||
font_family: "ABeeZee".to_owned(),
|
||||
weights: FontWeights::Variable,
|
||||
styles: indexset! {"normal".to_owned()},
|
||||
display: "optional".to_owned(),
|
||||
display: "swap".to_owned(),
|
||||
preload: true,
|
||||
selected_variable_axes: None,
|
||||
fallback: None,
|
||||
|
|
|
@ -116,11 +116,13 @@ pub(crate) fn get_stylesheet_url(
|
|||
if axes.wght.is_empty() {
|
||||
let mut variant = vec![];
|
||||
if let Some(variable_axes) = &axes.variable_axes {
|
||||
if !variable_axes.is_empty() {
|
||||
for (key, val) in variable_axes {
|
||||
variant.push((key.as_str(), &val[..]));
|
||||
}
|
||||
}
|
||||
variants.push(variant);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for wght in &axes.wght {
|
||||
if axes.ital.is_empty() {
|
||||
|
@ -135,6 +137,10 @@ pub(crate) fn get_stylesheet_url(
|
|||
} else {
|
||||
for ital in &axes.ital {
|
||||
let mut variant = vec![];
|
||||
|
||||
// If Normal is the only requested variant, it's safe to omit the ital axis
|
||||
// entirely. Otherwise, include all variants.
|
||||
if matches!(ital, FontItal::Italic) || axes.ital.len() > 1 {
|
||||
variant.push((
|
||||
"ital",
|
||||
match ital {
|
||||
|
@ -142,6 +148,8 @@ pub(crate) fn get_stylesheet_url(
|
|||
FontItal::Italic => "1",
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
variant.push(("wght", &wght[..]));
|
||||
if let Some(variable_axes) = &axes.variable_axes {
|
||||
for (key, val) in variable_axes {
|
||||
|
@ -170,9 +178,15 @@ pub(crate) fn get_stylesheet_url(
|
|||
});
|
||||
}
|
||||
|
||||
let first_variant = variants
|
||||
.first()
|
||||
.context("Requires at least one font variant")?;
|
||||
let first_variant = variants.first();
|
||||
match first_variant {
|
||||
None => Ok(format!(
|
||||
"{}?family={}&display={}",
|
||||
root_url,
|
||||
font_family.replace(' ', "+"),
|
||||
display
|
||||
)),
|
||||
Some(first_variant) => {
|
||||
// Always use the first variant's keys. There's an implicit invariant from the
|
||||
// code above that the keys across each variant are identical, and therefore
|
||||
// will be sorted identically across variants.
|
||||
|
@ -195,6 +209,7 @@ pub(crate) fn get_stylesheet_url(
|
|||
})
|
||||
.collect::<Vec<String>>();
|
||||
variant_values.sort();
|
||||
|
||||
// An encoding of the series of sorted variant values, with variants delimited
|
||||
// by `;` and the values within a variant delimited by `,` e.g.
|
||||
// `"0,10..100,500;1,10.100;500"`
|
||||
|
@ -209,6 +224,8 @@ pub(crate) fn get_stylesheet_url(
|
|||
display
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -418,7 +435,7 @@ mod tests {
|
|||
},
|
||||
"optional"
|
||||
)?,
|
||||
"https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,500&display=optional"
|
||||
"https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@500&display=optional"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
@ -441,7 +458,7 @@ mod tests {
|
|||
},
|
||||
"optional"
|
||||
)?,
|
||||
"https://fonts.googleapis.com/css2?family=Roboto+Serif:ital,opsz,wdth,wght,GRAD@0,8..144,50..150,500,-50..100&display=optional"
|
||||
"https://fonts.googleapis.com/css2?family=Roboto+Serif:opsz,wdth,wght,GRAD@8..144,50..150,500,-50..100&display=optional"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
@ -493,6 +510,44 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stylesheet_url_variable_font_without_anything() -> Result<()> {
|
||||
assert_eq!(
|
||||
get_stylesheet_url(
|
||||
GOOGLE_FONTS_STYLESHEET_URL,
|
||||
"Nabla",
|
||||
&FontAxes {
|
||||
wght: indexset! {},
|
||||
ital: indexset! {},
|
||||
variable_axes: None,
|
||||
},
|
||||
"swap"
|
||||
)?,
|
||||
"https://fonts.googleapis.com/css2?family=Nabla&display=swap"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stylesheet_url_variable_font_with_empty_variable_axes() -> Result<()> {
|
||||
assert_eq!(
|
||||
get_stylesheet_url(
|
||||
GOOGLE_FONTS_STYLESHEET_URL,
|
||||
"Nabla",
|
||||
&FontAxes {
|
||||
wght: indexset! {},
|
||||
ital: indexset! {},
|
||||
variable_axes: Some(vec![]),
|
||||
},
|
||||
"swap"
|
||||
)?,
|
||||
"https://fonts.googleapis.com/css2?family=Nabla&display=swap"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stylesheet_url_no_variable() -> Result<()> {
|
||||
assert_eq!(
|
||||
|
|
|
@ -16,6 +16,7 @@ use turbopack_core::{
|
|||
resolve, AliasPattern, ExportsValue, ResolveAliasMapVc,
|
||||
},
|
||||
};
|
||||
use turbopack_node::execution_context::ExecutionContextVc;
|
||||
|
||||
use crate::{
|
||||
embed_js::{next_js_fs, VIRTUAL_PACKAGE_NAME},
|
||||
|
@ -31,10 +32,11 @@ pub async fn get_next_client_import_map(
|
|||
project_path: FileSystemPathVc,
|
||||
ty: Value<ClientContextType>,
|
||||
next_config: NextConfigVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> Result<ImportMapVc> {
|
||||
let mut import_map = ImportMap::empty();
|
||||
|
||||
insert_next_shared_aliases(&mut import_map, project_path).await?;
|
||||
insert_next_shared_aliases(&mut import_map, project_path, execution_context).await?;
|
||||
|
||||
insert_alias_option(
|
||||
&mut import_map,
|
||||
|
@ -166,10 +168,11 @@ pub async fn get_next_server_import_map(
|
|||
project_path: FileSystemPathVc,
|
||||
ty: Value<ServerContextType>,
|
||||
next_config: NextConfigVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> Result<ImportMapVc> {
|
||||
let mut import_map = ImportMap::empty();
|
||||
|
||||
insert_next_shared_aliases(&mut import_map, project_path).await?;
|
||||
insert_next_shared_aliases(&mut import_map, project_path, execution_context).await?;
|
||||
|
||||
insert_alias_option(
|
||||
&mut import_map,
|
||||
|
@ -223,10 +226,11 @@ pub async fn get_next_edge_import_map(
|
|||
project_path: FileSystemPathVc,
|
||||
ty: Value<ServerContextType>,
|
||||
next_config: NextConfigVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> Result<ImportMapVc> {
|
||||
let mut import_map = ImportMap::empty();
|
||||
|
||||
insert_next_shared_aliases(&mut import_map, project_path).await?;
|
||||
insert_next_shared_aliases(&mut import_map, project_path, execution_context).await?;
|
||||
|
||||
insert_alias_option(
|
||||
&mut import_map,
|
||||
|
@ -354,6 +358,7 @@ pub async fn insert_next_server_special_aliases(
|
|||
pub async fn insert_next_shared_aliases(
|
||||
import_map: &mut ImportMap,
|
||||
project_path: FileSystemPathVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> Result<()> {
|
||||
let package_root = next_js_fs().root();
|
||||
|
||||
|
@ -384,7 +389,10 @@ pub async fn insert_next_shared_aliases(
|
|||
|
||||
import_map.insert_alias(
|
||||
AliasPattern::exact("@vercel/turbopack-next/internal/font/google/cssmodule.module.css"),
|
||||
ImportMapping::Dynamic(NextFontGoogleCssModuleReplacerVc::new(project_path).into()).into(),
|
||||
ImportMapping::Dynamic(
|
||||
NextFontGoogleCssModuleReplacerVc::new(project_path, execution_context).into(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
||||
import_map.insert_singleton_alias("@swc/helpers", get_next_package(project_path));
|
||||
|
|
|
@ -47,8 +47,10 @@ pub async fn get_server_resolve_options_context(
|
|||
project_path: FileSystemPathVc,
|
||||
ty: Value<ServerContextType>,
|
||||
next_config: NextConfigVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> Result<ResolveOptionsContextVc> {
|
||||
let next_server_import_map = get_next_server_import_map(project_path, ty, next_config);
|
||||
let next_server_import_map =
|
||||
get_next_server_import_map(project_path, ty, next_config, execution_context);
|
||||
let foreign_code_context_condition = foreign_code_context_condition(next_config).await?;
|
||||
let root_dir = project_path.root().resolve().await?;
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ pub async fn create_page_source(
|
|||
next_config,
|
||||
);
|
||||
let client_resolve_options_context =
|
||||
get_client_resolve_options_context(project_path, client_ty, next_config);
|
||||
get_client_resolve_options_context(project_path, client_ty, next_config, execution_context);
|
||||
|
||||
let client_chunking_context = get_client_chunking_context(
|
||||
project_path,
|
||||
|
@ -115,7 +115,7 @@ pub async fn create_page_source(
|
|||
);
|
||||
|
||||
let client_runtime_entries =
|
||||
get_client_runtime_entries(project_path, env, client_ty, next_config);
|
||||
get_client_runtime_entries(project_path, env, client_ty, next_config, execution_context);
|
||||
|
||||
let next_client_transition = NextClientTransition {
|
||||
is_app: false,
|
||||
|
@ -144,7 +144,7 @@ pub async fn create_page_source(
|
|||
)
|
||||
.build();
|
||||
let edge_resolve_options_context =
|
||||
get_edge_resolve_options_context(project_path, server_ty, next_config);
|
||||
get_edge_resolve_options_context(project_path, server_ty, next_config, execution_context);
|
||||
|
||||
let next_edge_transition = NextEdgeTransition {
|
||||
edge_compile_time_info,
|
||||
|
@ -160,7 +160,7 @@ pub async fn create_page_source(
|
|||
|
||||
let server_compile_time_info = get_server_compile_time_info(server_ty, env, server_addr);
|
||||
let server_resolve_options_context =
|
||||
get_server_resolve_options_context(project_path, server_ty, next_config);
|
||||
get_server_resolve_options_context(project_path, server_ty, next_config, execution_context);
|
||||
|
||||
let server_module_options_context =
|
||||
get_server_module_options_context(project_path, execution_context, server_ty, next_config);
|
||||
|
|
|
@ -262,6 +262,7 @@ fn edge_transition_map(
|
|||
project_path: FileSystemPathVc,
|
||||
output_path: FileSystemPathVc,
|
||||
next_config: NextConfigVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> TransitionsByNameVc {
|
||||
let edge_compile_time_info = get_edge_compile_time_info(server_addr, Value::new(Middleware));
|
||||
|
||||
|
@ -278,6 +279,7 @@ fn edge_transition_map(
|
|||
project_path,
|
||||
Value::new(ServerContextType::Middleware),
|
||||
next_config,
|
||||
execution_context,
|
||||
);
|
||||
|
||||
let next_edge_transition = NextEdgeTransition {
|
||||
|
@ -322,6 +324,7 @@ pub async fn route(
|
|||
project_path,
|
||||
intermediate_output_path,
|
||||
next_config,
|
||||
execution_context,
|
||||
)),
|
||||
);
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ pub async fn create_web_entry_source(
|
|||
compile_time_info.environment(),
|
||||
ty,
|
||||
);
|
||||
let entries = get_client_runtime_entries(project_path, env, ty, next_config);
|
||||
let entries = get_client_runtime_entries(project_path, env, ty, next_config, execution_context);
|
||||
|
||||
let runtime_entries = entries.resolve_entries(context);
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ when:
|
|||
path: /css2
|
||||
query_param:
|
||||
- name: family
|
||||
value: "Inter:ital,wght@0,100..900"
|
||||
value: "Inter:wght@100..900"
|
||||
- name: display
|
||||
value: optional
|
||||
value: swap
|
||||
then:
|
||||
status: 200
|
||||
body: |-
|
||||
|
@ -14,7 +14,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ function runTests() {
|
|||
expect(interNoArgs).toEqual({
|
||||
className: "className__inter_34ab8b4d__7bdff866",
|
||||
style: {
|
||||
fontFamily: "'__Inter_34ab8b4d'",
|
||||
fontFamily: "'__Inter_34ab8b'",
|
||||
fontStyle: "normal",
|
||||
},
|
||||
});
|
||||
|
|
|
@ -3,9 +3,9 @@ when:
|
|||
path: /css2
|
||||
query_param:
|
||||
- name: family
|
||||
value: "Inter:ital,wght@0,100..900"
|
||||
value: "Inter:wght@100..900"
|
||||
- name: display
|
||||
value: optional
|
||||
value: swap
|
||||
then:
|
||||
status: 200
|
||||
body: |-
|
||||
|
@ -14,7 +14,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ then:
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: optional;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ function runTests() {
|
|||
expect(interNoArgs).toEqual({
|
||||
className: "className__inter_34ab8b4d__7bdff866",
|
||||
style: {
|
||||
fontFamily: "'__Inter_34ab8b4d'",
|
||||
fontFamily: "'__Inter_34ab8b'",
|
||||
fontStyle: "normal",
|
||||
},
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ function runTests() {
|
|||
it("includes a rule styling the exported className", async () => {
|
||||
const matchingRule = await getRuleMatchingClassName(interNoArgs.className);
|
||||
expect(matchingRule).toBeTruthy();
|
||||
expect(matchingRule.style.fontFamily).toEqual("__Inter_34ab8b4d");
|
||||
expect(matchingRule.style.fontFamily).toEqual("__Inter_34ab8b");
|
||||
expect(matchingRule.style.fontStyle).toEqual("normal");
|
||||
});
|
||||
|
||||
|
@ -37,7 +37,7 @@ function runTests() {
|
|||
expect(interWithVariableName).toEqual({
|
||||
className: "className__inter_c6e282f1__e152ac0c",
|
||||
style: {
|
||||
fontFamily: "'__Inter_c6e282f1'",
|
||||
fontFamily: "'__Inter_c6e282'",
|
||||
fontStyle: "normal",
|
||||
},
|
||||
variable: "variable__inter_c6e282f1__e152ac0c",
|
||||
|
@ -47,7 +47,7 @@ function runTests() {
|
|||
interWithVariableName.variable
|
||||
);
|
||||
expect(matchingRule.styleMap.get("--my-font").toString().trim()).toBe(
|
||||
'"__Inter_c6e282f1"'
|
||||
'"__Inter_c6e282"'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -37,6 +37,10 @@ custom_allocator = ["turbo-malloc/custom_allocator"]
|
|||
next-font-local = ["next-core/next-font-local"]
|
||||
native-tls = ["next-core/native-tls"]
|
||||
rustls-tls = ["next-core/rustls-tls"]
|
||||
# Internal only. Enabled when building for the Next.js integration test suite.
|
||||
__internal_nextjs_integration_test = [
|
||||
"next-core/__internal_nextjs_integration_test",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true, features = ["backtrace"] }
|
||||
|
|
Loading…
Reference in a new issue