diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 5c32810279af1..8dc68c29a00e0 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -6,26 +6,13 @@ on: pull_request: types: [opened, synchronize] +# NOTE: anything in `afterBuild` inherits environment variables defined in +# `build_reusable.yml` (not these!) because that job executes within the context +# of that workflow. Environment variables are not automatically passed to +# reusable workflows. env: - NAPI_CLI_VERSION: 2.14.7 - TURBO_VERSION: 2.3.3 NODE_MAINTENANCE_VERSION: 18 NODE_LTS_VERSION: 20 - # disable backtrace for test snapshots - RUST_BACKTRACE: 0 - - TURBO_TEAM: 'vercel' - TURBO_CACHE: 'remote:rw' - NEXT_TELEMETRY_DISABLED: 1 - # we build a dev binary for use in CI so skip downloading - # canary next-swc binaries in the monorepo - NEXT_SKIP_NATIVE_POSTINSTALL: 1 - DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }} - NEXT_JUNIT_TEST_REPORT: 'true' - DD_ENV: 'ci' - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - NEXT_TEST_JOB: 1 - NEXT_TEST_PREFER_OFFLINE: 1 jobs: optimize-ci: diff --git a/.gitignore b/.gitignore index f78dbd04e33ff..838bf3c3768a6 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,7 @@ pids coverage # test output -test/**/out* +test/**/out/* test/**/next-env.d.ts .DS_Store /e2e-tests @@ -42,7 +42,7 @@ test/traces .nvmrc # examples -examples/**/out +examples/**/out/* examples/**/.env*.local pr-stats.md diff --git a/Cargo.lock b/Cargo.lock index 46456ce2bb86f..8a970397ac972 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4583,10 +4583,12 @@ dependencies = [ "rustc-hash 2.1.1", "serde", "serde_json", + "serde_path_to_error", "swc_core", "swc_relay", "thiserror 1.0.69", "tracing", + "turbo-esregex", "turbo-rcstr", "turbo-tasks", "turbo-tasks-build", @@ -9332,6 +9334,7 @@ dependencies = [ "smallvec", "tempfile", "thread_local", + "tracing", "twox-hash 2.1.0", "zstd", ] @@ -9547,6 +9550,7 @@ dependencies = [ "triomphe 0.1.12", "turbo-rcstr", "turbo-tasks", + "turbo-tasks-backend", "turbo-tasks-build", "turbo-tasks-hash", "turbo-tasks-memory", @@ -9690,6 +9694,7 @@ dependencies = [ "smallvec", "tokio", "tracing", + "turbo-esregex", "turbo-rcstr", "turbo-tasks", "turbo-tasks-build", diff --git a/crates/next-api/src/middleware.rs b/crates/next-api/src/middleware.rs index 765842cf26851..dc1c3144a1cce 100644 --- a/crates/next-api/src/middleware.rs +++ b/crates/next-api/src/middleware.rs @@ -44,7 +44,6 @@ use crate::{ #[turbo_tasks::value] pub struct MiddlewareEndpoint { project: ResolvedVc, - build_id: RcStr, asset_context: ResolvedVc>, source: ResolvedVc>, app_dir: Option>, @@ -56,7 +55,6 @@ impl MiddlewareEndpoint { #[turbo_tasks::function] pub fn new( project: ResolvedVc, - build_id: RcStr, asset_context: ResolvedVc>, source: ResolvedVc>, app_dir: Option>, @@ -64,7 +62,6 @@ impl MiddlewareEndpoint { ) -> Vc { Self { project, - build_id, asset_context, source, app_dir, diff --git a/crates/next-api/src/project.rs b/crates/next-api/src/project.rs index ba17325370d3e..dd7f33929d386 100644 --- a/crates/next-api/src/project.rs +++ b/crates/next-api/src/project.rs @@ -738,6 +738,11 @@ impl Project { *self.mode } + #[turbo_tasks::function] + pub(super) async fn is_watch_enabled(&self) -> Result> { + Ok(Vc::cell(self.watch.enable)) + } + #[turbo_tasks::function] pub(super) async fn per_page_module_graph(&self) -> Result> { Ok(Vc::cell(*self.mode.await? == NextMode::Development)) @@ -944,7 +949,7 @@ impl Project { // At this point all modules have been computed and we can get rid of the node.js // process pools - if self.await?.watch.enable { + if *self.is_watch_enabled().await? { turbopack_node::evaluate::scale_down(); } else { turbopack_node::evaluate::scale_zero(); @@ -1412,7 +1417,6 @@ impl Project { Ok(Vc::upcast(MiddlewareEndpoint::new( self, - self.await?.build_id.clone(), middleware_asset_context, source, app_dir.as_deref().copied(), diff --git a/crates/next-core/Cargo.toml b/crates/next-core/Cargo.toml index 73d335742cd6c..d7f4bba95914c 100644 --- a/crates/next-core/Cargo.toml +++ b/crates/next-core/Cargo.toml @@ -37,6 +37,7 @@ remove_console = "0.37.0" itertools = { workspace = true } auto-hash-map = { workspace = true } percent-encoding = "2.3.1" +serde_path_to_error = { workspace = true } swc_core = { workspace = true, features = [ "base", @@ -60,6 +61,7 @@ modularize_imports = { workspace = true } swc_relay = { workspace = true } turbo-rcstr = { workspace = true } +turbo-esregex = { workspace = true } turbo-tasks = { workspace = true } turbo-tasks-bytes = { workspace = true } turbo-tasks-env = { workspace = true } diff --git a/crates/next-core/src/next_config.rs b/crates/next-core/src/next_config.rs index 1ceb4e98498e9..9fdd199c3d21a 100644 --- a/crates/next-core/src/next_config.rs +++ b/crates/next-core/src/next_config.rs @@ -10,7 +10,10 @@ use turbo_tasks::{ use turbo_tasks_env::EnvMap; use turbo_tasks_fs::FileSystemPath; use turbopack::module_options::{ - module_options_context::MdxTransformOptions, LoaderRuleItem, OptionWebpackRules, + module_options_context::{ + ConditionItem, ConditionPath, MdxTransformOptions, OptionWebpackConditions, + }, + LoaderRuleItem, OptionWebpackRules, }; use turbopack_core::{ issue::{Issue, IssueSeverity, IssueStage, OptionStyledString, StyledString}, @@ -541,11 +544,57 @@ pub struct TurbopackConfig { /// This option has been replaced by `rules`. pub loaders: Option, pub rules: Option>, + #[turbo_tasks(trace_ignore)] + pub conditions: Option>, pub resolve_alias: Option>, pub resolve_extensions: Option>, pub module_ids: Option, } +#[derive(Clone, Debug, PartialEq, Serialize, TraceRawVcs, NonLocalValue)] +pub struct ConfigConditionItem(ConditionItem); + +impl<'de> Deserialize<'de> for ConfigConditionItem { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct RegexComponents { + source: RcStr, + flags: RcStr, + } + + #[derive(Deserialize)] + struct ConfigPath { + path: RegexOrGlob, + } + + #[derive(Deserialize)] + #[serde(tag = "type", rename_all = "lowercase")] + enum RegexOrGlob { + Regexp { value: RegexComponents }, + Glob { value: String }, + } + + let config_path = ConfigPath::deserialize(deserializer)?; + let condition_item = match config_path.path { + RegexOrGlob::Regexp { value } => { + let regex = turbo_esregex::EsRegex::new(&value.source, &value.flags) + .map_err(serde::de::Error::custom)?; + ConditionItem { + path: ConditionPath::Regex(regex.resolved_cell()), + } + } + RegexOrGlob::Glob { value } => ConditionItem { + path: ConditionPath::Glob(value.into()), + }, + }; + + Ok(ConfigConditionItem(condition_item)) + } +} + #[derive( Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, NonLocalValue, OperationValue, )] @@ -1065,7 +1114,8 @@ impl NextConfig { #[turbo_tasks::function] pub async fn from_string(string: Vc) -> Result> { let string = string.await?; - let config: NextConfig = serde_json::from_str(&string) + let mut jdeserializer = serde_json::Deserializer::from_str(&string); + let config: NextConfig = serde_path_to_error::deserialize(&mut jdeserializer) .with_context(|| format!("failed to parse next.config.js: {}", string))?; Ok(config.cell()) } @@ -1227,6 +1277,22 @@ impl NextConfig { Vc::cell(Some(ResolvedVc::cell(rules))) } + #[turbo_tasks::function] + pub fn webpack_conditions(&self) -> Vc { + let Some(config_conditions) = self.turbopack.as_ref().and_then(|t| t.conditions.as_ref()) + else { + return Vc::cell(None); + }; + + let conditions = FxIndexMap::from_iter( + config_conditions + .iter() + .map(|(k, v)| (k.clone(), v.0.clone())), + ); + + Vc::cell(Some(ResolvedVc::cell(conditions))) + } + #[turbo_tasks::function] pub fn persistent_caching_enabled(&self) -> Result> { Ok(Vc::cell( diff --git a/crates/next-core/src/next_import_map.rs b/crates/next-core/src/next_import_map.rs index 937dc1cd58cdc..cb6459fc3003a 100644 --- a/crates/next-core/src/next_import_map.rs +++ b/crates/next-core/src/next_import_map.rs @@ -965,6 +965,13 @@ async fn insert_next_shared_aliases( "next/dist/build/webpack/loaders/next-flight-loader/cache-wrapper", ), ); + import_map.insert_exact_alias( + "private-next-rsc-track-dynamic-import", + request_to_import_mapping( + project_path, + "next/dist/build/webpack/loaders/next-flight-loader/track-dynamic-import", + ), + ); insert_turbopack_dev_alias(import_map).await?; insert_package_alias( diff --git a/crates/next-core/src/next_server/transforms.rs b/crates/next-core/src/next_server/transforms.rs index 4cd409dbcfc65..4eaf62f308516 100644 --- a/crates/next-core/src/next_server/transforms.rs +++ b/crates/next-core/src/next_server/transforms.rs @@ -12,8 +12,8 @@ use crate::{ next_shared::transforms::{ get_next_dynamic_transform_rule, get_next_font_transform_rule, get_next_image_rule, get_next_lint_transform_rule, get_next_modularize_imports_rule, - get_next_pages_transforms_rule, get_server_actions_transform_rule, - next_amp_attributes::get_next_amp_attr_rule, + get_next_pages_transforms_rule, get_next_track_dynamic_imports_transform_rule, + get_server_actions_transform_rule, next_amp_attributes::get_next_amp_attr_rule, next_cjs_optimizer::get_next_cjs_optimizer_rule, next_disallow_re_export_all_in_page::get_next_disallow_export_all_in_page_rule, next_edge_node_api_assert::next_edge_node_api_assert, @@ -178,6 +178,10 @@ pub async fn get_next_server_transforms_rules( ServerContextType::Middleware { .. } | ServerContextType::Instrumentation { .. } => false, }; + if is_app_dir && *next_config.enable_dynamic_io().await? { + rules.push(get_next_track_dynamic_imports_transform_rule(mdx_rs)); + } + if !foreign_code { rules.push( get_next_dynamic_transform_rule(true, is_server_components, is_app_dir, mode, mdx_rs) diff --git a/crates/next-core/src/next_shared/transforms/mod.rs b/crates/next-core/src/next_shared/transforms/mod.rs index b4f085dec56a1..281bd938b67f9 100644 --- a/crates/next-core/src/next_shared/transforms/mod.rs +++ b/crates/next-core/src/next_shared/transforms/mod.rs @@ -16,6 +16,7 @@ pub(crate) mod next_pure; pub(crate) mod next_react_server_components; pub(crate) mod next_shake_exports; pub(crate) mod next_strip_page_exports; +pub(crate) mod next_track_dynamic_imports; pub(crate) mod react_remove_properties; pub(crate) mod relay; pub(crate) mod remove_console; @@ -30,6 +31,7 @@ pub use next_dynamic::get_next_dynamic_transform_rule; pub use next_font::get_next_font_transform_rule; pub use next_lint::get_next_lint_transform_rule; pub use next_strip_page_exports::get_next_pages_transforms_rule; +pub use next_track_dynamic_imports::get_next_track_dynamic_imports_transform_rule; pub use server_actions::get_server_actions_transform_rule; use turbo_tasks::{ReadRef, ResolvedVc, Value}; use turbo_tasks_fs::FileSystemPath; diff --git a/crates/next-core/src/next_shared/transforms/next_track_dynamic_imports.rs b/crates/next-core/src/next_shared/transforms/next_track_dynamic_imports.rs new file mode 100644 index 0000000000000..653df152afce8 --- /dev/null +++ b/crates/next-core/src/next_shared/transforms/next_track_dynamic_imports.rs @@ -0,0 +1,24 @@ +use anyhow::Result; +use async_trait::async_trait; +use next_custom_transforms::transforms::track_dynamic_imports::*; +use swc_core::ecma::ast::Program; +use turbopack::module_options::ModuleRule; +use turbopack_ecmascript::{CustomTransformer, TransformContext}; + +use super::get_ecma_transform_rule; + +pub fn get_next_track_dynamic_imports_transform_rule(mdx_rs: bool) -> ModuleRule { + get_ecma_transform_rule(Box::new(NextTrackDynamicImports {}), mdx_rs, false) +} + +#[derive(Debug)] +struct NextTrackDynamicImports {} + +#[async_trait] +impl CustomTransformer for NextTrackDynamicImports { + #[tracing::instrument(level = tracing::Level::TRACE, name = "next_track_dynamic_imports", skip_all)] + async fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Result<()> { + program.mutate(track_dynamic_imports(ctx.unresolved_mark)); + Ok(()) + } +} diff --git a/crates/next-core/src/next_shared/webpack_rules/mod.rs b/crates/next-core/src/next_shared/webpack_rules/mod.rs index 9cb41113084b6..d2a9ce348fd52 100644 --- a/crates/next-core/src/next_shared/webpack_rules/mod.rs +++ b/crates/next-core/src/next_shared/webpack_rules/mod.rs @@ -15,19 +15,22 @@ pub async fn webpack_loader_options( project_path: ResolvedVc, next_config: Vc, foreign: bool, - conditions: Vec, + condition_strs: Vec, ) -> Result>> { - let rules = *next_config.webpack_rules(conditions).await?; + let rules = *next_config.webpack_rules(condition_strs).await?; let rules = *maybe_add_sass_loader(next_config.sass_config(), rules.map(|v| *v)).await?; let rules = if foreign { rules } else { *maybe_add_babel_loader(*project_path, rules.map(|v| *v)).await? }; + + let conditions = next_config.webpack_conditions().to_resolved().await?; Ok(if let Some(rules) = rules { Some( WebpackLoadersOptions { rules, + conditions, loader_runner_package: Some(loader_runner_package_mapping().to_resolved().await?), } .resolved_cell(), diff --git a/crates/next-custom-transforms/src/chain_transforms.rs b/crates/next-custom-transforms/src/chain_transforms.rs index 2858887423783..1f303bcea0ffe 100644 --- a/crates/next-custom-transforms/src/chain_transforms.rs +++ b/crates/next-custom-transforms/src/chain_transforms.rs @@ -121,6 +121,9 @@ pub struct TransformOptions { #[serde(default)] pub css_env: Option, + + #[serde(default)] + pub track_dynamic_imports: bool, } pub fn custom_before_pass<'a, C>( @@ -333,6 +336,14 @@ where )), None => Either::Right(noop_pass()), }, + match &opts.track_dynamic_imports { + true => Either::Left( + crate::transforms::track_dynamic_imports::track_dynamic_imports( + unresolved_mark, + ), + ), + false => Either::Right(noop_pass()), + }, match &opts.cjs_require_optimizer { Some(config) => Either::Left(visit_mut_pass( crate::transforms::cjs_optimizer::cjs_optimizer( diff --git a/crates/next-custom-transforms/src/react_compiler.rs b/crates/next-custom-transforms/src/react_compiler.rs index 398de1e5ad0d7..553019bc4e2ed 100644 --- a/crates/next-custom-transforms/src/react_compiler.rs +++ b/crates/next-custom-transforms/src/react_compiler.rs @@ -1,5 +1,5 @@ use swc_core::ecma::{ - ast::{Callee, Expr, FnDecl, FnExpr, Program, ReturnStmt}, + ast::{Callee, Expr, FnDecl, FnExpr, Pat, Program, ReturnStmt, Stmt, VarDeclarator}, visit::{Visit, VisitWith}, }; @@ -34,6 +34,13 @@ impl Visit for Finder { node.visit_children_with(self); } + fn visit_expr(&mut self, node: &Expr) { + if self.found { + return; + } + node.visit_children_with(self); + } + fn visit_fn_decl(&mut self, node: &FnDecl) { let old = self.is_interested; self.is_interested = node.ident.sym.starts_with("use") @@ -68,4 +75,26 @@ impl Visit for Finder { node.visit_children_with(self); } + + fn visit_stmt(&mut self, node: &Stmt) { + if self.found { + return; + } + node.visit_children_with(self); + } + + fn visit_var_declarator(&mut self, node: &VarDeclarator) { + let old = self.is_interested; + + if let Pat::Ident(ident) = &node.name { + self.is_interested = ident.sym.starts_with("use") + || ident.sym.starts_with(|c: char| c.is_ascii_uppercase()); + } else { + self.is_interested = false; + } + + node.visit_children_with(self); + + self.is_interested = old; + } } diff --git a/crates/next-custom-transforms/src/transforms/mod.rs b/crates/next-custom-transforms/src/transforms/mod.rs index 71618dd378b24..529a59180c134 100644 --- a/crates/next-custom-transforms/src/transforms/mod.rs +++ b/crates/next-custom-transforms/src/transforms/mod.rs @@ -18,6 +18,7 @@ pub mod react_server_components; pub mod server_actions; pub mod shake_exports; pub mod strip_page_exports; +pub mod track_dynamic_imports; pub mod warn_for_edge_runtime; //[TODO] PACK-1564: need to decide reuse vs. turbopack specific diff --git a/crates/next-custom-transforms/src/transforms/track_dynamic_imports.rs b/crates/next-custom-transforms/src/transforms/track_dynamic_imports.rs new file mode 100644 index 0000000000000..fc2ba0fb441fb --- /dev/null +++ b/crates/next-custom-transforms/src/transforms/track_dynamic_imports.rs @@ -0,0 +1,124 @@ +use swc_core::{ + common::{source_map::PURE_SP, util::take::Take, Mark, SyntaxContext}, + ecma::{ + ast::*, + utils::{prepend_stmt, private_ident, quote_ident, quote_str}, + visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith}, + }, + quote, +}; + +pub fn track_dynamic_imports(unresolved_mark: Mark) -> impl VisitMut + Pass { + visit_mut_pass(ImportReplacer::new(unresolved_mark)) +} + +struct ImportReplacer { + unresolved_ctxt: SyntaxContext, + has_dynamic_import: bool, + wrapper_function_local_ident: Ident, +} + +impl ImportReplacer { + pub fn new(unresolved_mark: Mark) -> Self { + ImportReplacer { + unresolved_ctxt: SyntaxContext::empty().apply_mark(unresolved_mark), + has_dynamic_import: false, + wrapper_function_local_ident: private_ident!("$$trackDynamicImport__"), + } + } +} + +impl VisitMut for ImportReplacer { + noop_visit_mut_type!(); + + fn visit_mut_program(&mut self, program: &mut Program) { + program.visit_mut_children_with(self); + // if we wrapped a dynamic import while visiting the children, we need to import the wrapper + + if self.has_dynamic_import { + let import_args = MakeNamedImportArgs { + original_ident: quote_ident!("trackDynamicImport").into(), + local_ident: self.wrapper_function_local_ident.clone(), + source: "private-next-rsc-track-dynamic-import", + unresolved_ctxt: self.unresolved_ctxt, + }; + match program { + Program::Module(module) => { + prepend_stmt(&mut module.body, make_named_import_esm(import_args)); + } + Program::Script(script) => { + // CJS modules can still use `import()`. for CJS, we have to inject the helper + // using `require` instead of `import` to avoid accidentally turning them + // into ESM modules. + prepend_stmt(&mut script.body, make_named_import_cjs(import_args)); + } + } + } + } + + fn visit_mut_expr(&mut self, expr: &mut Expr) { + expr.visit_mut_children_with(self); + + // before: `import(...)` + // after: `$$trackDynamicImport__(import(...))` + + if let Expr::Call(CallExpr { + callee: Callee::Import(_), + .. + }) = expr + { + self.has_dynamic_import = true; + let replacement_expr = quote!( + "$wrapper_fn($expr)" as Expr, + wrapper_fn = self.wrapper_function_local_ident.clone(), + expr: Expr = expr.take() + ) + .with_span(PURE_SP); + *expr = replacement_expr + } + } +} + +struct MakeNamedImportArgs<'a> { + original_ident: Ident, + local_ident: Ident, + source: &'a str, + unresolved_ctxt: SyntaxContext, +} + +fn make_named_import_esm(args: MakeNamedImportArgs) -> ModuleItem { + let MakeNamedImportArgs { + original_ident, + local_ident, + source, + .. + } = args; + let mut item = quote!( + "import { $original_ident as $local_ident } from 'dummy'" as ModuleItem, + original_ident = original_ident, + local_ident = local_ident, + ); + // the import source cannot be parametrized in `quote!()`, so patch it manually + let decl = item.as_mut_module_decl().unwrap().as_mut_import().unwrap(); + decl.src = Box::new(source.into()); + item +} + +fn make_named_import_cjs(args: MakeNamedImportArgs) -> Stmt { + let MakeNamedImportArgs { + original_ident, + local_ident, + source, + unresolved_ctxt, + } = args; + quote!( + "const { [$original_name]: $local_ident } = $require($source)" as Stmt, + original_name: Expr = quote_str!(original_ident.sym).into(), + local_ident = local_ident, + source: Expr = quote_str!(source).into(), + // the builtin `require` is considered an unresolved identifier. + // we have to match that, or it won't be recognized as + // a proper `require()` call. + require = quote_ident!(unresolved_ctxt, "require") + ) +} diff --git a/crates/next-custom-transforms/tests/fixture.rs b/crates/next-custom-transforms/tests/fixture.rs index 4463df7d3053a..bcca6c667aa81 100644 --- a/crates/next-custom-transforms/tests/fixture.rs +++ b/crates/next-custom-transforms/tests/fixture.rs @@ -21,6 +21,7 @@ use next_custom_transforms::transforms::{ server_actions::{self, server_actions, ServerActionsMode}, shake_exports::{shake_exports, Config as ShakeExportsConfig}, strip_page_exports::{next_transform_strip_page_exports, ExportFilter}, + track_dynamic_imports::track_dynamic_imports, warn_for_edge_runtime::warn_for_edge_runtime, }; use rustc_hash::FxHashSet; @@ -930,6 +931,29 @@ fn test_source_maps(input: PathBuf) { ); } +#[fixture("tests/fixture/track-dynamic-imports/**/input.js")] +fn track_dynamic_imports_fixture(input: PathBuf) { + let output = input.parent().unwrap().join("output.js"); + test_fixture( + syntax(), + &|_tr| { + let unresolved_mark = Mark::new(); + let top_level_mark = Mark::new(); + ( + resolver(unresolved_mark, top_level_mark, false), + track_dynamic_imports(unresolved_mark), + ) + }, + &input, + &output, + FixtureTestConfig { + // auto detect script/module to test CJS handling + module: None, + ..Default::default() + }, + ); +} + fn lint_to_fold(r: R) -> impl Pass where R: Visit, diff --git a/crates/next-custom-transforms/tests/fixture/server-actions/modules.d.ts b/crates/next-custom-transforms/tests/fixture/server-actions/modules.d.ts index d4fdfac42e46a..f49bb27f64b98 100644 --- a/crates/next-custom-transforms/tests/fixture/server-actions/modules.d.ts +++ b/crates/next-custom-transforms/tests/fixture/server-actions/modules.d.ts @@ -49,3 +49,6 @@ declare module 'components' { declare module 'navigation' { export function redirect(href: string): void } + +// Some tests generate `data:text/javascript,...` imports +declare module 'data:text/*' diff --git a/crates/next-custom-transforms/tests/fixture/server-actions/tsconfig.json b/crates/next-custom-transforms/tests/fixture/server-actions/tsconfig.json index 297f2bdda85f8..c3bcd7b15bc33 100644 --- a/crates/next-custom-transforms/tests/fixture/server-actions/tsconfig.json +++ b/crates/next-custom-transforms/tests/fixture/server-actions/tsconfig.json @@ -29,6 +29,8 @@ "./server-graph/25/output.js", "./server-graph/28/output.js", "./server-graph/30/output.js", + // FIXME: buggy renaming of anonymous functions + "./server-graph/51/output.js", // Excluded because of weird TS behavior around `action.bind(...)` making args optional // (but only if no JSDoc type annotations are present) "./server-graph/24/output.js" diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/1/input.js b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/1/input.js new file mode 100644 index 0000000000000..f93d860c24e40 --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/1/input.js @@ -0,0 +1,4 @@ +export default async function Page() { + const { foo } = await import('some-module') + return foo() +} diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/1/output.js b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/1/output.js new file mode 100644 index 0000000000000..bcf4d19be33bb --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/1/output.js @@ -0,0 +1,5 @@ +import { trackDynamicImport as $$trackDynamicImport__ } from "private-next-rsc-track-dynamic-import"; +export default async function Page() { + const { foo } = await /*#__PURE__*/ $$trackDynamicImport__(import('some-module')); + return foo(); +} diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/2/input.js b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/2/input.js new file mode 100644 index 0000000000000..7d0bd4f650227 --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/2/input.js @@ -0,0 +1,4 @@ +export default async function Page() { + await import((await import('get-name')).default) + return null +} diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/2/output.js b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/2/output.js new file mode 100644 index 0000000000000..20c99119ffd0f --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/2/output.js @@ -0,0 +1,5 @@ +import { trackDynamicImport as $$trackDynamicImport__ } from "private-next-rsc-track-dynamic-import"; +export default async function Page() { + await /*#__PURE__*/ $$trackDynamicImport__(import((await /*#__PURE__*/ $$trackDynamicImport__(import('get-name'))).default)); + return null; +} diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/3/input.js b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/3/input.js new file mode 100644 index 0000000000000..cf5719d3390ff --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/3/input.js @@ -0,0 +1,8 @@ +export default async function Page() { + const { foo } = await import('some-module') + // name conflict + $$trackDynamicImport__() + return foo() +} + +export function $$trackDynamicImport__() {} diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/3/output.js b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/3/output.js new file mode 100644 index 0000000000000..81f97fbe33fad --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/3/output.js @@ -0,0 +1,9 @@ +import { trackDynamicImport as $$trackDynamicImport__ } from "private-next-rsc-track-dynamic-import"; +export default async function Page() { + const { foo } = await /*#__PURE__*/ $$trackDynamicImport__(import('some-module')); + // name conflict + $$trackDynamicImport__1(); + return foo(); +} +function $$trackDynamicImport__1() {} +export { $$trackDynamicImport__1 as $$trackDynamicImport__ }; diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/4/input.js b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/4/input.js new file mode 100644 index 0000000000000..8f7c41948fc83 --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/4/input.js @@ -0,0 +1,6 @@ +const promise = import('some-module') + +export default async function Page() { + const { foo } = await promise + return foo() +} diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/4/output.js b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/4/output.js new file mode 100644 index 0000000000000..6bc4be721895e --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/4/output.js @@ -0,0 +1,6 @@ +import { trackDynamicImport as $$trackDynamicImport__ } from "private-next-rsc-track-dynamic-import"; +const promise = /*#__PURE__*/ $$trackDynamicImport__(import('some-module')); +export default async function Page() { + const { foo } = await promise; + return foo(); +} diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/5/input.js b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/5/input.js new file mode 100644 index 0000000000000..a762d9c472280 --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/5/input.js @@ -0,0 +1,6 @@ +async function foo() { + const { foo } = await import('some-module') + return foo() +} + +exports.foo = foo diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/5/output.js b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/5/output.js new file mode 100644 index 0000000000000..64811de98f9a3 --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/5/output.js @@ -0,0 +1,6 @@ +const { ["trackDynamicImport"]: $$trackDynamicImport__ } = require("private-next-rsc-track-dynamic-import"); +async function foo() { + const { foo } = await /*#__PURE__*/ $$trackDynamicImport__(import('some-module')); + return foo(); +} +exports.foo = foo; diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/index.ts b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/index.ts new file mode 100644 index 0000000000000..20915445a3590 --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/index.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/modules.d.ts b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/modules.d.ts new file mode 100644 index 0000000000000..fb65a5a2b6500 --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/modules.d.ts @@ -0,0 +1,7 @@ +declare module 'some-module' { + export function foo(): null +} +declare module 'get-name' { + const name: string + export default name +} diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/next.d.ts b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/next.d.ts new file mode 100644 index 0000000000000..5178b71fb3cb8 --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/next.d.ts @@ -0,0 +1,3 @@ +declare module 'private-next-rsc-track-dynamic-import' { + export function trackDynamicImport(promise: Promise): Promise +} diff --git a/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/tsconfig.json b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/tsconfig.json new file mode 100644 index 0000000000000..5da6bee985c0c --- /dev/null +++ b/crates/next-custom-transforms/tests/fixture/track-dynamic-imports/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "noEmit": true, + "rootDir": ".", + + "allowJs": true, + "checkJs": true, + + "lib": ["ESNext", "DOM"], + "skipLibCheck": true, + + "strict": true, + "jsx": "preserve", + + "target": "ESNext", + "esModuleInterop": true, + "module": "Preserve", + "moduleResolution": "bundler", + "moduleDetection": "force" + }, + "files": ["./index.ts"], // loads ambient declarations for modules used in tests + "include": ["./**/*/input.js", "./**/*/output.js"] +} diff --git a/crates/next-custom-transforms/tests/full.rs b/crates/next-custom-transforms/tests/full.rs index cc535bcf60c60..0d6e3cd87a841 100644 --- a/crates/next-custom-transforms/tests/full.rs +++ b/crates/next-custom-transforms/tests/full.rs @@ -82,6 +82,7 @@ fn test(input: &Path, minify: bool) { prefer_esm: false, debug_function_name: false, css_env: None, + track_dynamic_imports: false, }; let unresolved_mark = Mark::new(); diff --git a/crates/next-error-code-swc-plugin/Cargo.lock b/crates/next-error-code-swc-plugin/Cargo.lock index b795f628f807e..661ab3ef1eabb 100644 --- a/crates/next-error-code-swc-plugin/Cargo.lock +++ b/crates/next-error-code-swc-plugin/Cargo.lock @@ -2,17 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -55,16 +44,28 @@ version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "ast_node" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94741d66bdda032fcbf33e621b4e3a888d7d11bd3ac4446d82c5593a136936ff" +checksum = "91fb5864e2f5bf9fd9797b94b2dfd1554d4c3092b535008b27d7e15c86675a2f" dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.90", + "syn", ] [[package]] @@ -75,9 +76,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64-simd" @@ -135,24 +136,25 @@ dependencies = [ [[package]] name = "bytecheck" -version = "0.6.12" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +checksum = "50690fb3370fb9fe3550372746084c46f2ac8c9685c583d2be10eefd89d3d1a3" dependencies = [ "bytecheck_derive", "ptr_meta", + "rancor", "simdutf8", ] [[package]] name = "bytecheck_derive" -version = "0.6.12" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +checksum = "efb7846e0cb180355c2dec69e721edafa36919850f1a9f52ffba4ebc0393cb71" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -207,6 +209,15 @@ dependencies = [ "thiserror 2.0.6", ] +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.2.3" @@ -222,6 +233,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + [[package]] name = "cpufeatures" version = "0.2.16" @@ -262,7 +286,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.90", + "syn", ] [[package]] @@ -273,7 +297,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -310,7 +334,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -320,7 +344,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.90", + "syn", ] [[package]] @@ -353,7 +377,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -407,7 +431,7 @@ checksum = "8d7ccf961415e7aa17ef93dcb6c2441faaa8e768abe09e659b908089546f74c5" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.90", + "syn", ] [[package]] @@ -426,39 +450,19 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.11", + "ahash", "allocator-api2", ] @@ -488,15 +492,15 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hstr" -version = "0.2.12" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412" +checksum = "71399f53a92ef72ee336a4b30201c6e944827e14e0af23204c291aad9c24cc85" dependencies = [ "hashbrown 0.14.5", "new_debug_unreachable", "once_cell", "phf", - "rustc-hash 1.1.0", + "rustc-hash 2.1.0", "triomphe", ] @@ -615,7 +619,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -670,7 +674,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -762,7 +766,27 @@ checksum = "23c9b935fbe1d6cbd1dac857b54a688145e2d93f48db36010514d0f612d0ad67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", +] + +[[package]] +name = "munge" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e22e7961c873e8b305b176d2a4e1d41ce7ba31bc1c52d2a107a89568ec74c55" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ac7d860b767c6398e88fe93db73ce53eb496057aa6895ffa4d60cb02e1d1c6b" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -833,9 +857,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "outref" @@ -855,6 +879,25 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" +[[package]] +name = "par-core" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757892557993c69e82f9de0f9051e87144278aa342f03bf53617bbf044554484" +dependencies = [ + "once_cell", +] + +[[package]] +name = "par-iter" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a5b20f31e9ba82bfcbbb54a67aa40be6cebec9f668ba5753be138f9523c531a" +dependencies = [ + "either", + "par-core", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -914,7 +957,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -962,22 +1005,22 @@ dependencies = [ [[package]] name = "ptr_meta" -version = "0.1.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" dependencies = [ "ptr_meta_derive", ] [[package]] name = "ptr_meta_derive" -version = "0.1.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -995,6 +1038,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rancor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" +dependencies = [ + "ptr_meta", +] + [[package]] name = "rand" version = "0.8.5" @@ -1071,40 +1123,41 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rend" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" dependencies = [ "bytecheck", ] [[package]] name = "rkyv" -version = "0.7.45" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +checksum = "1e147371c75553e1e2fcdb483944a8540b8438c31426279553b9a8182a9b7b65" dependencies = [ - "bitvec", "bytecheck", "bytes", - "hashbrown 0.12.3", + "hashbrown 0.15.2", + "indexmap", + "munge", "ptr_meta", + "rancor", "rend", "rkyv_derive", - "seahash", "tinyvec", "uuid", ] [[package]] name = "rkyv_derive" -version = "0.7.45" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +checksum = "246b40ac189af6c675d124b802e8ef6d5246c53e17367ce9501f8f66a81abb7a" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -1171,12 +1224,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - [[package]] name = "semver" version = "0.9.0" @@ -1218,7 +1265,7 @@ checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -1350,7 +1397,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.90", + "syn", ] [[package]] @@ -1361,36 +1408,38 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "swc_allocator" -version = "1.0.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cacc28f0ada8e4e31a720dd849ff06864b10e6ab0a1aaa99c06456cfe046af" +checksum = "cc6b926f0d94bbb34031fe5449428cfa1268cdc0b31158d6ad9c97e0fc1e79dd" dependencies = [ + "allocator-api2", "bumpalo", "hashbrown 0.14.5", "ptr_meta", - "rustc-hash 1.1.0", + "rustc-hash 2.1.0", "triomphe", ] [[package]] name = "swc_atoms" -version = "2.0.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7211e5c57ea972f32b8a104d7006c4a68d094ec30c6a73bcd20d4d6c473c7c" +checksum = "9d7077ba879f95406459bc0c81f3141c529b34580bc64d7ab7bd15e7118a0391" dependencies = [ "bytecheck", "hstr", "once_cell", + "rancor", "rkyv", - "rustc-hash 1.1.0", + "rustc-hash 2.1.0", "serde", ] [[package]] name = "swc_common" -version = "4.0.1" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f87a21612a324493fd065e9c6fea960b4031088a213db782e2ca71d2fabb3ec" +checksum = "5e36654ec9a8b089c329ab7522aa70eb39cc3e4e3dfd70f9176a74414bdec00e" dependencies = [ "anyhow", "ast_node", @@ -1403,8 +1452,9 @@ dependencies = [ "num-bigint", "once_cell", "parking_lot", + "rancor", "rkyv", - "rustc-hash 1.1.0", + "rustc-hash 2.1.0", "serde", "siphasher", "sourcemap", @@ -1420,9 +1470,9 @@ dependencies = [ [[package]] name = "swc_core" -version = "5.0.4" +version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92086975747587872715a20f78fc51e7047bac58f3a6a17d4ed5a9643f3fd0a2" +checksum = "6f806c64573a1b9e7ea5e263fc24a015bc18a1a8c2a56dd36860380ec3e80a71" dependencies = [ "once_cell", "swc_allocator", @@ -1435,21 +1485,25 @@ dependencies = [ "swc_plugin", "swc_plugin_macro", "swc_plugin_proxy", + "swc_transform_common", "vergen", ] [[package]] name = "swc_ecma_ast" -version = "4.0.1" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bdab7759509c1b37ec77bd9fc231f525b888d9609c2963ce71995da1b27357c" +checksum = "0613d84468a6bb6d45d13c5a3368b37bd21f3067a089f69adac630dcb462a018" dependencies = [ "bitflags", "bytecheck", "is-macro", "num-bigint", + "once_cell", "phf", + "rancor", "rkyv", + "rustc-hash 2.1.0", "scoped-tls", "string_enum", "swc_atoms", @@ -1460,14 +1514,17 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "4.0.2" +version = "11.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e474f6c2671524dbb179b44a36425cb1a58928f0f7211c45043f0951a1842c5d" +checksum = "b01b3de365a86b8f982cc162f257c82f84bda31d61084174a3be37e8ab15c0f4" dependencies = [ + "ascii", + "compact_str", "memchr", "num-bigint", "once_cell", "regex", + "rustc-hash 2.1.0", "serde", "sourcemap", "swc_allocator", @@ -1480,27 +1537,55 @@ dependencies = [ [[package]] name = "swc_ecma_codegen_macros" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9a42f479a6475647e248fa9750982c87cd985e19d1016a1fc18a70682305d1" +checksum = "e99e1931669a67c83e2c2b4375674f6901d1480994a76aa75b23f1389e6c5076" dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.90", + "syn", +] + +[[package]] +name = "swc_ecma_lexer" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d11c8e71901401b9aae2ece4946eeb7674b14b8301a53768afbbeeb0e48b599" +dependencies = [ + "arrayvec", + "bitflags", + "either", + "new_debug_unreachable", + "num-bigint", + "num-traits", + "phf", + "rustc-hash 2.1.0", + "serde", + "smallvec", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "tracing", + "typed-arena", ] [[package]] name = "swc_ecma_parser" -version = "5.0.0" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c5ab8bd4cc4a4956514699c84d1a25cdb5a33f5ec760ec64ce712e973019c9" +checksum = "250786944fbc05f6484eda9213df129ccfe17226ae9ad51b62fce2f72135dbee" dependencies = [ + "arrayvec", + "bitflags", "either", "new_debug_unreachable", "num-bigint", "num-traits", "phf", + "rustc-hash 2.1.0", "serde", "smallvec", "smartstring", @@ -1508,15 +1593,16 @@ dependencies = [ "swc_atoms", "swc_common", "swc_ecma_ast", + "swc_ecma_lexer", "tracing", "typed-arena", ] [[package]] name = "swc_ecma_testing" -version = "4.0.0" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0397cdbbdcfec2048da1291f44e2d433471fab9bfb430f8f879a831242d636" +checksum = "977386a831e9464cc99e914d5682621efca49c443e5c737a00a2babd6d1589aa" dependencies = [ "anyhow", "hex", @@ -1527,16 +1613,17 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "5.0.1" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb4000822f02b54af0be4f668649fa1e5555f1e3392479d17a277eb81a841f0" +checksum = "6856da3da598f4da001b7e4ce225ee8970bc9d5cbaafcaf580190cf0a6031ec5" dependencies = [ "better_scoped_tls", "bitflags", "indexmap", "once_cell", + "par-core", "phf", - "rustc-hash 1.1.0", + "rustc-hash 2.1.0", "serde", "smallvec", "swc_atoms", @@ -1550,9 +1637,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_testing" -version = "5.0.0" +version = "16.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21721599724e9f9c40467ff9cdd20f045f134c26e5fe794b1ee6708798c724ed" +checksum = "93a905befc831be30430ab1e4af5aa6f2052ea397f44e1747c28a4d3859f4f84" dependencies = [ "ansi_term", "anyhow", @@ -1562,6 +1649,7 @@ dependencies = [ "serde_json", "sha2", "sourcemap", + "swc_allocator", "swc_common", "swc_ecma_ast", "swc_ecma_codegen", @@ -1576,14 +1664,16 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "5.0.1" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb9a28511d17d1e6c5dfcf209368a1da4a542270c450fba7f27faf22c34df22" +checksum = "bb6ecf7485a130df25c4ba4e27cfde0cc7bf45f453f40cf0c52eb69b3a4235d0" dependencies = [ "indexmap", "num_cpus", "once_cell", - "rustc-hash 1.1.0", + "par-core", + "par-iter", + "rustc-hash 2.1.0", "ryu-js", "swc_atoms", "swc_common", @@ -1595,9 +1685,9 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "4.0.1" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5af5332117aa0424e418556f74e9cee335dc47eb7ae35dddbd9fd65fc01452c" +checksum = "249dc9eede1a4ad59a038f9cfd61ce67845bd2c1392ade3586d714e7181f3c1a" dependencies = [ "new_debug_unreachable", "num-bigint", @@ -1616,19 +1706,22 @@ checksum = "e96e15288bf385ab85eb83cff7f9e2d834348da58d0a31b33bdb572e66ee413e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] name = "swc_error_reporters" -version = "5.0.0" +version = "11.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a3c124af5d297d98e6c18776ba04024087cde14602621017e8e9c6cd1c2d1" +checksum = "e3b5be5f151485ec9372c23bbb132c4a829c879632db8b790439779b873970be" dependencies = [ "anyhow", "miette", "once_cell", "parking_lot", + "serde", + "serde_derive", + "serde_json", "swc_common", ] @@ -1640,7 +1733,7 @@ checksum = "a509f56fca05b39ba6c15f3e58636c3924c78347d63853632ed2ffcb6f5a0ac7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -1654,23 +1747,26 @@ dependencies = [ [[package]] name = "swc_plugin_macro" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0917ccfdcd3fa6cf41bdacef2388702a3b274f9ea708d930e1e8db37c7c3e1c6" +checksum = "ace467dfafbbdf3aecff786b8605b35db57d945e92fd88800569aa2cba0cdf61" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] name = "swc_plugin_proxy" -version = "4.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6749c4027aad79cf648ffce6633100ea01a7b0d6cf17299cfa68ce141897c26c" +checksum = "edbd6dddc6f98f7ded495b918c80bc59c78d9b297ed98081e22def0f27a117f9" dependencies = [ "better_scoped_tls", + "bytecheck", + "rancor", "rkyv", + "rustc-hash 2.1.0", "swc_common", "swc_ecma_ast", "swc_trace_macro", @@ -1679,34 +1775,37 @@ dependencies = [ [[package]] name = "swc_trace_macro" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c78717a841565df57f811376a3d19c9156091c55175e12d378f3a522de70cef" +checksum = "559185db338f1bcb50297aafd4f79c0956c84dc71a66da4cffb57acf9d93fd88" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] -name = "swc_visit" -version = "2.0.0" +name = "swc_transform_common" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9138b6a36bbe76dd6753c4c0794f7e26480ea757bee499738bedbbb3ae3ec5f3" +checksum = "6d73c21cecc518e0107f890012a747fa679cb0faf04f32fc8f5bd618040eb8fe" dependencies = [ - "either", - "new_debug_unreachable", + "better_scoped_tls", + "once_cell", + "rustc-hash 2.1.0", + "serde", + "serde_json", + "swc_common", ] [[package]] -name = "syn" -version = "1.0.109" +name = "swc_visit" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "9138b6a36bbe76dd6753c4c0794f7e26480ea757bee499738bedbbb3ae3ec5f3" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "either", + "new_debug_unreachable", ] [[package]] @@ -1728,7 +1827,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -1761,9 +1860,9 @@ dependencies = [ [[package]] name = "testing" -version = "4.0.0" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6b200c27382caadd583563c79cdf870d854e14c4c078731d447ecbfe27c35f" +checksum = "987241734b96bd71228f0395ab38e05b71ec7c6ded958538c5d3a1b67f6465ce" dependencies = [ "ansi_term", "cargo_metadata 0.18.1", @@ -1771,6 +1870,7 @@ dependencies = [ "once_cell", "pretty_assertions", "regex", + "rustc-hash 2.1.0", "serde", "serde_json", "swc_common", @@ -1793,7 +1893,7 @@ dependencies = [ "quote", "regex", "relative-path", - "syn 2.0.90", + "syn", ] [[package]] @@ -1832,7 +1932,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -1843,7 +1943,7 @@ checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -1900,7 +2000,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -2060,12 +2160,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - [[package]] name = "winapi" version = "0.3.9" @@ -2217,7 +2311,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", "synstructure", ] @@ -2238,7 +2332,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] [[package]] @@ -2258,7 +2352,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", "synstructure", ] @@ -2281,5 +2375,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn", ] diff --git a/crates/next-error-code-swc-plugin/Cargo.toml b/crates/next-error-code-swc-plugin/Cargo.toml index 8f2ef123802de..ac4fb2991cab1 100644 --- a/crates/next-error-code-swc-plugin/Cargo.toml +++ b/crates/next-error-code-swc-plugin/Cargo.toml @@ -11,4 +11,4 @@ md5 = "0.7" rustc-hash = "2.1.0" serde = "1.0" serde_json = "1.0" -swc_core = { version = "5.0.*", features = ["ecma_plugin_transform"] } +swc_core = { version = "23.0.*", features = ["ecma_plugin_transform"] } diff --git a/docs/01-app/01-getting-started/05-css.mdx b/docs/01-app/01-getting-started/05-css.mdx index 020c5de2319df..c340cddd7efee 100644 --- a/docs/01-app/01-getting-started/05-css.mdx +++ b/docs/01-app/01-getting-started/05-css.mdx @@ -6,6 +6,7 @@ related: title: Next Steps description: Learn more about the features mentioned in this page. links: + - app/guides/tailwind-css - app/guides/sass - app/guides/css-in-js --- @@ -14,10 +15,10 @@ Next.js provides several ways to use CSS in your application, including: - [CSS Modules](#css-modules) - [Global CSS](#global-css) -- [Tailwind CSS](#tailwind-css) +- [External Stylesheets](#external-stylesheets) +- [Tailwind CSS](/docs/app/guides/tailwind-css) - [Sass](/docs/app/guides/sass) - [CSS-in-JS](/docs/app/guides/css-in-js) -- [External Stylesheets](#external-stylesheets) ## CSS Modules @@ -93,96 +94,6 @@ export default function RootLayout({ children }) { > **Good to know:** Global styles can be imported into any layout, page, or component inside the `app` directory. However, since Next.js uses React's built-in support for stylesheets to integrate with Suspense, this currently does not remove stylesheets as you navigate between routes which can lead to conflicts. We recommend using global styles for _truly_ global CSS, and [CSS Modules](#css-modules) for scoped CSS. -## Tailwind CSS - -[Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework that integrates seamlessly with Next.js. - -### Installing Tailwind - -To start using Tailwind, install the necessary Tailwind CSS packages: - -```bash filename="Terminal" -npm install tailwindcss @tailwindcss/postcss postcss -``` - -### Configuring Tailwind - -Create a `postcss.config.mjs` file in the root of your project and add the `@tailwindcss/postcss` plugin to your PostCSS configuration: - -```js filename="postcss.config.mjs" highlight={4} -/** @type {import('tailwindcss').Config} */ -export default { - plugins: { - '@tailwindcss/postcss': {}, - }, -} -``` - -### Using Tailwind - -Add the [Tailwind directives](https://tailwindcss.com/docs/functions-and-directives#directives) to your [Global Stylesheet](#global-css): - -```css filename="app/globals.css" -@import 'tailwindcss'; -``` - -Then, import the styles in the [root layout](/docs/app/api-reference/file-conventions/layout#root-layouts): - -```tsx filename="app/layout.tsx" switcher -import type { Metadata } from 'next' -// These styles apply to every route in the application -import './globals.css' - -export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', -} - -export default function RootLayout({ - children, -}: { - children: React.ReactNode -}) { - return ( - - {children} - - ) -} -``` - -```jsx filename="app/layout.js" switcher -// These styles apply to every route in the application -import './globals.css' - -export const metadata = { - title: 'Create Next App', - description: 'Generated by create next app', -} - -export default function RootLayout({ children }) { - return ( - - {children} - - ) -} -``` - -You can then start writing Tailwind's utility classes in your application. - -```tsx filename="app/page.tsx" switcher -export default function Page() { - return

Hello, Next.js!

-} -``` - -```jsx filename="app/page.js" switcher -export default function Page() { - return

Hello, Next.js!

-} -``` - ## External stylesheets Stylesheets published by external packages can be imported anywhere in the `app` directory, including colocated components: diff --git a/docs/01-app/01-getting-started/10-metadata-and-og-images.mdx b/docs/01-app/01-getting-started/10-metadata-and-og-images.mdx index 79e1e6625c9a3..c1462f2314b9c 100644 --- a/docs/01-app/01-getting-started/10-metadata-and-og-images.mdx +++ b/docs/01-app/01-getting-started/10-metadata-and-og-images.mdx @@ -7,10 +7,13 @@ related: description: Learn more about the Metadata APIs mentioned in this page. links: - app/api-reference/functions/generate-metadata + - app/api-reference/functions/generate-viewport + - app/api-reference/functions/image-response - app/api-reference/file-conventions/metadata - app/api-reference/file-conventions/metadata/app-icons - app/api-reference/file-conventions/metadata/opengraph-image - - app/api-reference/functions/image-response + - app/api-reference/file-conventions/metadata/robots + - app/api-reference/file-conventions/metadata/sitemap --- The Metadata APIs can be used to define your application metadata for improved SEO and web shareability and include: @@ -59,7 +62,7 @@ export const metadata = { export default function Page() {} ``` -You can view a full list of available options, in the [generate metadata documentation](/docs/app/api-reference/functions/generate-metadata#metadata-fields). +You can view a full list of available options, in the [`generateMetadata` documentation](/docs/app/api-reference/functions/generate-metadata#metadata-fields). ## Generated metadata @@ -177,6 +180,17 @@ export default async function Page({ params }) { } ``` +## File-based metadata + +The following special files are available for metadata: + +- [favicon.ico, apple-icon.jpg, and icon.jpg](/docs/app/api-reference/file-conventions/metadata/app-icons) +- [opengraph-image.jpg and twitter-image.jpg](/docs/app/api-reference/file-conventions/metadata/opengraph-image) +- [robots.txt](/docs/app/api-reference/file-conventions/metadata/robots) +- [sitemap.xml](/docs/app/api-reference/file-conventions/metadata/sitemap) + +You can use these for static metadata, or you can programmatically generate these files with code. + ## Favicons Favicons are small icons that represent your site in bookmarks and search results. To add a favicon to your application, create a `favicon.ico` and add to the root of the app folder. diff --git a/docs/01-app/02-guides/css-in-js.mdx b/docs/01-app/02-guides/css-in-js.mdx index b1360b9e34b18..bc8f342e64459 100644 --- a/docs/01-app/02-guides/css-in-js.mdx +++ b/docs/01-app/02-guides/css-in-js.mdx @@ -32,7 +32,7 @@ The following are currently working on support: > **Good to know**: We're testing out different CSS-in-JS libraries and we'll be adding more examples for libraries that support React 18 features and/or the `app` directory. -If you want to style Server Components, we recommend using [CSS Modules](/docs/app/building-your-application/styling/css) or other solutions that output CSS files, like PostCSS or [Tailwind CSS](/docs/app/building-your-application/styling/tailwind-css). +If you want to style Server Components, we recommend using [CSS Modules](/docs/app/building-your-application/styling/css) or other solutions that output CSS files, like PostCSS or [Tailwind CSS](/docs/app/guides/tailwind-css). ## Configuring CSS-in-JS in `app` diff --git a/docs/01-app/02-guides/json-ld.mdx b/docs/01-app/02-guides/json-ld.mdx new file mode 100644 index 0000000000000..64ed24e65476d --- /dev/null +++ b/docs/01-app/02-guides/json-ld.mdx @@ -0,0 +1,77 @@ +--- +title: How to implement JSON-LD in your Next.js application +nav_title: JSON-LD +description: Learn how to add JSON-LD to your Next.js application to describe your content to search engines and AI. +--- + +[JSON-LD](https://json-ld.org/) is a format for structured data that can be used by search engines and AI to to help them understand the structure of the page beyond pure content. For example, you can use it to describe a person, an event, an organization, a movie, a book, a recipe, and many other types of entities. + +Our current recommendation for JSON-LD is to render structured data as a `