From 07d2820645b5d771a3f5ba8c792db0e2e6c5b035 Mon Sep 17 00:00:00 2001 From: Jude Gao Date: Wed, 22 Jan 2025 11:41:04 -0500 Subject: [PATCH 01/13] Track `use cache` usage (#75007) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We’re updating the SWC transform to [track](https://nextjs.org/telemetry) how the "use cache" directive is used during builds. Each use of "use cache" is counted based on the handler it’s linked to. By default, "use cache" counts toward a feature called "useCache/default". If a custom handler is defined in the Next.js config (e.g., "custom"), then "use cache: custom" will count toward "useCache/custom". The build process will collect these counts and combine them across all source files and workers. This data is reported under the NEXT_BUILD_FEATURE_USAGE event, allowing us to filter by "useCache/*" to see how often each cache handler is used. *Only support webpack prod build for now. Closes NDX-683 --- Cargo.lock | 2 + crates/napi/Cargo.toml | 1 + crates/napi/src/lib.rs | 16 ++++++- crates/napi/src/transform.rs | 23 ++++++++-- .../next_shared/transforms/server_actions.rs | 2 +- crates/next-custom-transforms/Cargo.toml | 1 + .../src/chain_transforms.rs | 3 ++ .../src/transforms/server_actions.rs | 35 ++++++++++++++- crates/next-custom-transforms/tests/errors.rs | 3 ++ .../next-custom-transforms/tests/fixture.rs | 4 ++ crates/next-custom-transforms/tests/full.rs | 1 + crates/wasm/src/lib.rs | 1 + packages/next/src/build/build-context.ts | 2 +- packages/next/src/build/index.ts | 13 ++++++ packages/next/src/build/webpack-build/impl.ts | 3 +- .../next/src/build/webpack-build/index.ts | 9 +++- packages/next/src/build/webpack-config.ts | 11 ++++- .../build/webpack/loaders/next-swc-loader.ts | 27 ++++++++---- .../telemetry-plugin.ts | 37 +++++++++++++--- ...pdate-telemetry-loader-context-from-swc.ts | 29 +++++++++++++ .../use-cache-tracker-utils.ts | 34 +++++++++++++++ packages/next/src/telemetry/events/build.ts | 6 ++- test/integration/telemetry/_app/layout.jsx | 7 +++ .../funtion-level-use-cache/page.jsx | 19 ++++++++ .../telemetry/_app/use-cache/page.jsx | 5 +++ .../telemetry/_app/use-cache/page2/page.jsx | 9 ++++ .../telemetry/next.config.use-cache | 8 ++++ .../integration/telemetry/test/config.test.js | 43 +++++++++++++++++++ 28 files changed, 323 insertions(+), 31 deletions(-) rename packages/next/src/build/webpack/plugins/{ => telemetry-plugin}/telemetry-plugin.ts (87%) create mode 100644 packages/next/src/build/webpack/plugins/telemetry-plugin/update-telemetry-loader-context-from-swc.ts create mode 100644 packages/next/src/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.ts create mode 100644 test/integration/telemetry/_app/layout.jsx create mode 100644 test/integration/telemetry/_app/use-cache/funtion-level-use-cache/page.jsx create mode 100644 test/integration/telemetry/_app/use-cache/page.jsx create mode 100644 test/integration/telemetry/_app/use-cache/page2/page.jsx create mode 100644 test/integration/telemetry/next.config.use-cache diff --git a/Cargo.lock b/Cargo.lock index fbed46cce1b94..3d5882969dbb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4525,6 +4525,7 @@ version = "0.0.0" dependencies = [ "anyhow", "chrono", + "dashmap 6.1.0", "easy-error", "either", "fxhash", @@ -4561,6 +4562,7 @@ dependencies = [ "anyhow", "backtrace", "console-subscriber", + "dashmap 6.1.0", "dhat", "fxhash", "getrandom", diff --git a/crates/napi/Cargo.toml b/crates/napi/Cargo.toml index 950f2e1c3284b..3773bde60b0ae 100644 --- a/crates/napi/Cargo.toml +++ b/crates/napi/Cargo.toml @@ -73,6 +73,7 @@ tracing-chrome = "0.5.0" url = { workspace = true } urlencoding = { workspace = true } once_cell = { workspace = true } +dashmap = "6.1.0" swc_core = { workspace = true, features = [ "base_concurrent", diff --git a/crates/napi/src/lib.rs b/crates/napi/src/lib.rs index b734f33518132..0c208bd5c1af1 100644 --- a/crates/napi/src/lib.rs +++ b/crates/napi/src/lib.rs @@ -35,19 +35,18 @@ DEALINGS IN THE SOFTWARE. extern crate napi_derive; use std::{ - env, panic::set_hook, sync::{Arc, Once}, }; use backtrace::Backtrace; +use dashmap::DashMap; use fxhash::FxHashSet; use napi::bindgen_prelude::*; use swc_core::{ base::{Compiler, TransformOutput}, common::{FilePathMapping, SourceMap}, }; - #[cfg(not(target_arch = "wasm32"))] pub mod css; pub mod mdx; @@ -97,6 +96,7 @@ pub fn complete_output( env: &Env, output: TransformOutput, eliminated_packages: FxHashSet, + use_cache_telemetry_tracker: DashMap, ) -> napi::Result { let mut js_output = env.create_object()?; js_output.set_named_property("code", env.create_string_from_std(output.code)?)?; @@ -109,6 +109,18 @@ pub fn complete_output( env.create_string_from_std(serde_json::to_string(&eliminated_packages)?)?, )?; } + if !use_cache_telemetry_tracker.is_empty() { + js_output.set_named_property( + "useCacheTelemetryTracker", + env.create_string_from_std(serde_json::to_string( + &use_cache_telemetry_tracker + .iter() + .map(|entry| (entry.key().clone(), *entry.value())) + .collect::>(), + )?)?, + )?; + } + Ok(js_output) } diff --git a/crates/napi/src/transform.rs b/crates/napi/src/transform.rs index dc2c71444a456..e76635f3ce446 100644 --- a/crates/napi/src/transform.rs +++ b/crates/napi/src/transform.rs @@ -35,6 +35,7 @@ use std::{ }; use anyhow::{anyhow, bail, Context as _}; +use dashmap::DashMap; use fxhash::FxHashSet; use napi::bindgen_prelude::*; use next_custom_transforms::chain_transforms::{custom_before_pass, TransformOptions}; @@ -81,12 +82,14 @@ fn skip_filename() -> bool { } impl Task for TransformTask { - type Output = (TransformOutput, FxHashSet); + type Output = (TransformOutput, FxHashSet, DashMap); type JsValue = Object; fn compute(&mut self) -> napi::Result { GLOBALS.set(&Default::default(), || { let eliminated_packages: Rc>> = Default::default(); + let use_cache_telemetry_tracker: Rc> = Default::default(); + let res = catch_unwind(AssertUnwindSafe(|| { try_with_handler( self.c.cm.clone(), @@ -143,6 +146,7 @@ impl Task for TransformTask { comments.clone(), eliminated_packages.clone(), unresolved_mark, + use_cache_telemetry_tracker.clone(), ) }, |_| noop_pass(), @@ -161,7 +165,13 @@ impl Task for TransformTask { match res { Ok(res) => res - .map(|o| (o, eliminated_packages.replace(Default::default()))) + .map(|o| { + ( + o, + eliminated_packages.replace(Default::default()), + (*use_cache_telemetry_tracker).clone(), + ) + }) .convert_err(), Err(err) => Err(napi::Error::new( Status::GenericFailure, @@ -174,9 +184,14 @@ impl Task for TransformTask { fn resolve( &mut self, env: Env, - (output, eliminated_packages): Self::Output, + (output, eliminated_packages, use_cache_telemetry_tracker): Self::Output, ) -> napi::Result { - complete_output(&env, output, eliminated_packages) + complete_output( + &env, + output, + eliminated_packages, + use_cache_telemetry_tracker, + ) } } diff --git a/crates/next-core/src/next_shared/transforms/server_actions.rs b/crates/next-core/src/next_shared/transforms/server_actions.rs index 2c8bc7c9ac06f..8ca94a6103198 100644 --- a/crates/next-core/src/next_shared/transforms/server_actions.rs +++ b/crates/next-core/src/next_shared/transforms/server_actions.rs @@ -61,8 +61,8 @@ impl CustomTransformer for NextServerActions { cache_kinds: self.cache_kinds.await?.clone_value(), }, ctx.comments.clone(), + Default::default(), ); - program.mutate(actions); Ok(()) } diff --git a/crates/next-custom-transforms/Cargo.toml b/crates/next-custom-transforms/Cargo.toml index 789196d6dac4b..c4c2f11e1dab5 100644 --- a/crates/next-custom-transforms/Cargo.toml +++ b/crates/next-custom-transforms/Cargo.toml @@ -30,6 +30,7 @@ sha1 = "0.10.1" tracing = { version = "0.1.37" } anyhow = { workspace = true } lazy_static = { workspace = true } +dashmap = "6.1.0" swc_core = { workspace = true, features = [ "base", diff --git a/crates/next-custom-transforms/src/chain_transforms.rs b/crates/next-custom-transforms/src/chain_transforms.rs index 857ff40488557..1861086ca8b2a 100644 --- a/crates/next-custom-transforms/src/chain_transforms.rs +++ b/crates/next-custom-transforms/src/chain_transforms.rs @@ -1,5 +1,6 @@ use std::{cell::RefCell, path::PathBuf, rc::Rc, sync::Arc}; +use dashmap::DashMap; use either::Either; use fxhash::FxHashSet; use modularize_imports; @@ -125,6 +126,7 @@ pub fn custom_before_pass<'a, C>( comments: C, eliminated_packages: Rc>>, unresolved_mark: Mark, + use_cache_telemetry_tracker: Rc>, ) -> impl Pass + 'a where C: Clone + Comments + 'a, @@ -303,6 +305,7 @@ where &file.name, config.clone(), comments.clone(), + use_cache_telemetry_tracker, )), None => Either::Right(noop_pass()), }, diff --git a/crates/next-custom-transforms/src/transforms/server_actions.rs b/crates/next-custom-transforms/src/transforms/server_actions.rs index 3ac1fe0ec6f7d..0e0d396c286db 100644 --- a/crates/next-custom-transforms/src/transforms/server_actions.rs +++ b/crates/next-custom-transforms/src/transforms/server_actions.rs @@ -2,8 +2,10 @@ use std::{ collections::{BTreeMap, HashSet}, convert::{TryFrom, TryInto}, mem::{replace, take}, + rc::Rc, }; +use dashmap::DashMap; use hex::encode as hex_encode; use indoc::formatdoc; use rustc_hash::FxHashSet; @@ -117,7 +119,12 @@ enum ServerActionsErrorKind { pub type ActionsMap = BTreeMap; #[tracing::instrument(level = tracing::Level::TRACE, skip_all)] -pub fn server_actions(file_name: &FileName, config: Config, comments: C) -> impl Pass { +pub fn server_actions( + file_name: &FileName, + config: Config, + comments: C, + use_cache_telemetry_tracker: Rc>, +) -> impl Pass { visit_mut_pass(ServerActions { config, comments, @@ -155,6 +162,8 @@ pub fn server_actions(file_name: &FileName, config: Config, comment arrow_or_fn_expr_ident: None, exported_local_ids: HashSet::new(), + + use_cache_telemetry_tracker, }) } @@ -210,6 +219,8 @@ struct ServerActions { arrow_or_fn_expr_ident: Option, exported_local_ids: HashSet, + + use_cache_telemetry_tracker: Rc>, } impl ServerActions { @@ -351,6 +362,7 @@ impl ServerActions { has_file_directive: self.file_directive.is_some(), is_allowed_position: true, location: DirectiveLocation::FunctionBody, + use_cache_telemetry_tracker: self.use_cache_telemetry_tracker.clone(), }; body.stmts.retain(|stmt| { @@ -377,6 +389,7 @@ impl ServerActions { has_file_directive: false, is_allowed_position: true, location: DirectiveLocation::Module, + use_cache_telemetry_tracker: self.use_cache_telemetry_tracker.clone(), }; stmts.retain(|item| { @@ -2605,6 +2618,7 @@ struct DirectiveVisitor<'a> { directive: Option, has_file_directive: bool, is_allowed_position: bool, + use_cache_telemetry_tracker: Rc>, } impl DirectiveVisitor<'_> { @@ -2658,6 +2672,8 @@ impl DirectiveVisitor<'_> { } else // `use cache` or `use cache: foo` if value == "use cache" || value.starts_with("use cache: ") { + // Increment telemetry counter tracking usage of "use cache" directives + if in_fn_body && !allow_inline { emit_error(ServerActionsErrorKind::InlineUseCacheInClientComponent { span: *span, @@ -2679,6 +2695,7 @@ impl DirectiveVisitor<'_> { self.directive = Some(Directive::UseCache { cache_kind: RcStr::from("default"), }); + self.increment_cache_usage_counter("default"); } else { // Slice the value after "use cache: " let cache_kind = RcStr::from(value.split_at("use cache: ".len()).1); @@ -2690,6 +2707,7 @@ impl DirectiveVisitor<'_> { }); } + self.increment_cache_usage_counter(&cache_kind); self.directive = Some(Directive::UseCache { cache_kind }); } @@ -2758,6 +2776,21 @@ impl DirectiveVisitor<'_> { false } + + // Increment telemetry counter tracking usage of "use cache" directives + fn increment_cache_usage_counter(&mut self, cache_kind: &str) { + let entry = self + .use_cache_telemetry_tracker + .entry(cache_kind.to_string()); + match entry { + dashmap::mapref::entry::Entry::Occupied(mut occupied) => { + *occupied.get_mut() += 1; + } + dashmap::mapref::entry::Entry::Vacant(vacant) => { + vacant.insert(1); + } + } + } } pub(crate) struct ClosureReplacer<'a> { diff --git a/crates/next-custom-transforms/tests/errors.rs b/crates/next-custom-transforms/tests/errors.rs index 7bee850a1c62e..e4690e01201c8 100644 --- a/crates/next-custom-transforms/tests/errors.rs +++ b/crates/next-custom-transforms/tests/errors.rs @@ -191,6 +191,7 @@ fn react_server_actions_server_errors(input: PathBuf) { cache_kinds: FxHashSet::default(), }, tr.comments.as_ref().clone(), + Default::default(), ), ) }, @@ -231,6 +232,7 @@ fn react_server_actions_client_errors(input: PathBuf) { cache_kinds: FxHashSet::default(), }, tr.comments.as_ref().clone(), + Default::default(), ), ) }, @@ -289,6 +291,7 @@ fn use_cache_not_allowed(input: PathBuf) { cache_kinds: FxHashSet::from_iter(["x".into()]), }, tr.comments.as_ref().clone(), + Default::default(), ), ) }, diff --git a/crates/next-custom-transforms/tests/fixture.rs b/crates/next-custom-transforms/tests/fixture.rs index c4c05b78258c3..5a81894ed089a 100644 --- a/crates/next-custom-transforms/tests/fixture.rs +++ b/crates/next-custom-transforms/tests/fixture.rs @@ -572,6 +572,7 @@ fn server_actions_server_fixture(input: PathBuf) { cache_kinds: FxHashSet::from_iter(["x".into()]), }, _tr.comments.as_ref().clone(), + Default::default(), ), ) }, @@ -605,6 +606,7 @@ fn next_font_with_directive_fixture(input: PathBuf) { cache_kinds: FxHashSet::default(), }, _tr.comments.as_ref().clone(), + Default::default(), ), ) }, @@ -631,6 +633,7 @@ fn server_actions_client_fixture(input: PathBuf) { cache_kinds: FxHashSet::default(), }, _tr.comments.as_ref().clone(), + Default::default(), ), ) }, @@ -933,6 +936,7 @@ fn test_source_maps(input: PathBuf) { cache_kinds: FxHashSet::from_iter([]), }, _tr.comments.as_ref().clone(), + Default::default(), ), ) }, diff --git a/crates/next-custom-transforms/tests/full.rs b/crates/next-custom-transforms/tests/full.rs index 4ddb585d9d02e..b19adcea856b0 100644 --- a/crates/next-custom-transforms/tests/full.rs +++ b/crates/next-custom-transforms/tests/full.rs @@ -102,6 +102,7 @@ fn test(input: &Path, minify: bool) { comments.clone(), Default::default(), unresolved_mark, + Default::default(), ) }, |_| noop_pass(), diff --git a/crates/wasm/src/lib.rs b/crates/wasm/src/lib.rs index 83ef221769b70..d22a95081f527 100644 --- a/crates/wasm/src/lib.rs +++ b/crates/wasm/src/lib.rs @@ -107,6 +107,7 @@ pub fn transform_sync(s: JsValue, opts: JsValue) -> Result { comments.clone(), Default::default(), unresolved_mark, + Default::default(), ) }, |_| noop_pass(), diff --git a/packages/next/src/build/build-context.ts b/packages/next/src/build/build-context.ts index c6d3b120fccdd..f041eee208fad 100644 --- a/packages/next/src/build/build-context.ts +++ b/packages/next/src/build/build-context.ts @@ -4,7 +4,7 @@ import type { __ApiPreviewProps } from '../server/api-utils' import type { NextConfigComplete } from '../server/config-shared' import type { Span } from '../trace' import type getBaseWebpackConfig from './webpack-config' -import type { TelemetryPluginState } from './webpack/plugins/telemetry-plugin' +import type { TelemetryPluginState } from './webpack/plugins/telemetry-plugin/telemetry-plugin' import type { Telemetry } from '../telemetry/storage' // A layer for storing data that is used by plugins to communicate with each diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 6445ca6dd2fc6..47798a405cd21 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -217,6 +217,7 @@ import { } from '../server/lib/utils' import { InvariantError } from '../shared/lib/invariant-error' import { HTML_LIMITED_BOT_UA_RE_STRING } from '../shared/lib/router/utils/is-bot' +import type { UseCacheTrackerKey } from './webpack/plugins/telemetry-plugin/use-cache-tracker-utils' type Fallback = null | boolean | string @@ -3637,6 +3638,18 @@ export default async function build( NextBuildContext.telemetryState.packagesUsedInServerSideProps ) ) + const useCacheTracker = NextBuildContext.telemetryState.useCacheTracker + + for (const [key, value] of Object.entries(useCacheTracker)) { + telemetry.record( + eventBuildFeatureUsage([ + { + featureName: key as UseCacheTrackerKey, + invocationCount: value, + }, + ]) + ) + } } if (ssgPages.size > 0 || appDir) { diff --git a/packages/next/src/build/webpack-build/impl.ts b/packages/next/src/build/webpack-build/impl.ts index f12874727040e..afc62313e14be 100644 --- a/packages/next/src/build/webpack-build/impl.ts +++ b/packages/next/src/build/webpack-build/impl.ts @@ -17,7 +17,7 @@ import type { NextError } from '../../lib/is-error' import { TelemetryPlugin, type TelemetryPluginState, -} from '../webpack/plugins/telemetry-plugin' +} from '../webpack/plugins/telemetry-plugin/telemetry-plugin' import { NextBuildContext, resumePluginState, @@ -353,6 +353,7 @@ export async function webpackBuildImpl( usages: telemetryPlugin?.usages() || [], packagesUsedInServerSideProps: telemetryPlugin?.packagesUsedInServerSideProps() || [], + useCacheTracker: telemetryPlugin?.getUseCacheTracker() || {}, }, } } diff --git a/packages/next/src/build/webpack-build/index.ts b/packages/next/src/build/webpack-build/index.ts index 3537041cf7a19..3eaaa8002d120 100644 --- a/packages/next/src/build/webpack-build/index.ts +++ b/packages/next/src/build/webpack-build/index.ts @@ -10,6 +10,7 @@ import { formatNodeOptions, getParsedNodeOptionsWithoutInspect, } from '../../server/lib/utils' +import { mergeUseCacheTrackers } from '../webpack/plugins/telemetry-plugin/use-cache-tracker-utils' const debug = origDebug('next:build:webpack-build') @@ -82,7 +83,13 @@ async function webpackBuildWithWorker( prunedBuildContext.pluginState = pluginState if (curResult.telemetryState) { - NextBuildContext.telemetryState = curResult.telemetryState + NextBuildContext.telemetryState = { + ...curResult.telemetryState, + useCacheTracker: mergeUseCacheTrackers( + NextBuildContext.telemetryState?.useCacheTracker, + curResult.telemetryState.useCacheTracker + ), + } } combinedResult.duration += curResult.duration diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index af8bcd9dcdf7a..57370cfccc6ac 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -52,7 +52,7 @@ import { NextTypesPlugin } from './webpack/plugins/next-types-plugin' import type { Feature, SWC_TARGET_TRIPLE, -} from './webpack/plugins/telemetry-plugin' +} from './webpack/plugins/telemetry-plugin/telemetry-plugin' import type { Span } from '../trace' import type { MiddlewareMatcher } from './analysis/get-page-static-info' import loadJsConfig, { @@ -1907,7 +1907,9 @@ export default async function getBaseWebpackConfig( new CssChunkingPlugin(config.experimental.cssChunking === 'strict'), !dev && isClient && - new (require('./webpack/plugins/telemetry-plugin').TelemetryPlugin)( + new ( + require('./webpack/plugins/telemetry-plugin/telemetry-plugin') as typeof import('./webpack/plugins/telemetry-plugin/telemetry-plugin') + ).TelemetryPlugin( new Map( [ ['swcLoader', useSWCLoader], @@ -1937,6 +1939,11 @@ export default async function getBaseWebpackConfig( ].filter<[Feature, boolean]>(Boolean as any) ) ), + !dev && + isNodeServer && + new ( + require('./webpack/plugins/telemetry-plugin/telemetry-plugin') as typeof import('./webpack/plugins/telemetry-plugin/telemetry-plugin') + ).TelemetryPlugin(new Map()), ].filter(Boolean as any as ExcludesFalse), } diff --git a/packages/next/src/build/webpack/loaders/next-swc-loader.ts b/packages/next/src/build/webpack/loaders/next-swc-loader.ts index bce034cfeb410..7adca9314ea58 100644 --- a/packages/next/src/build/webpack/loaders/next-swc-loader.ts +++ b/packages/next/src/build/webpack/loaders/next-swc-loader.ts @@ -33,6 +33,12 @@ import { getLoaderSWCOptions } from '../../swc/options' import path, { isAbsolute } from 'path' import { babelIncludeRegexes } from '../../webpack-config' import { isResourceInPackages } from '../../handle-externals' +import type { TelemetryLoaderContext } from '../plugins/telemetry-plugin/telemetry-plugin' +import { + updateTelemetryLoaderCtxFromTransformOutput, + type SwcTransformTelemetryOutput, +} from '../plugins/telemetry-plugin/update-telemetry-loader-context-from-swc' +import type { LoaderContext } from 'webpack' const maybeExclude = ( excludePath: string, @@ -72,7 +78,7 @@ const FORCE_TRANSPILE_CONDITIONS = /(next\/font|next\/dynamic|use server|use client)/ async function loaderTransform( - this: any, + this: LoaderContext & TelemetryLoaderContext, source?: string, inputSourceMap?: any ) { @@ -115,7 +121,7 @@ async function loaderTransform( bundleLayer, esm, } = loaderOptions - const isPageFile = filename.startsWith(pagesDir) + const isPageFile = pagesDir ? filename.startsWith(pagesDir) : false const relativeFilePathFromRoot = path.relative(rootDir, filename) const swcOptions = getLoaderSWCOptions({ @@ -179,14 +185,17 @@ async function loaderTransform( this.mode === 'development' } - return transform(source as any, programmaticOptions).then((output) => { - if (output.eliminatedPackages && this.eliminatedPackages) { - for (const pkg of JSON.parse(output.eliminatedPackages)) { - this.eliminatedPackages.add(pkg) - } + return transform(source as any, programmaticOptions).then( + ( + output: { + code: string + map?: string + } & SwcTransformTelemetryOutput + ) => { + updateTelemetryLoaderCtxFromTransformOutput(this, output) + return [output.code, output.map ? JSON.parse(output.map) : undefined] } - return [output.code, output.map ? JSON.parse(output.map) : undefined] - }) + ) } const EXCLUDED_PATHS = diff --git a/packages/next/src/build/webpack/plugins/telemetry-plugin.ts b/packages/next/src/build/webpack/plugins/telemetry-plugin/telemetry-plugin.ts similarity index 87% rename from packages/next/src/build/webpack/plugins/telemetry-plugin.ts rename to packages/next/src/build/webpack/plugins/telemetry-plugin/telemetry-plugin.ts index 8de21f37c33b2..fcaa9ba01c56e 100644 --- a/packages/next/src/build/webpack/plugins/telemetry-plugin.ts +++ b/packages/next/src/build/webpack/plugins/telemetry-plugin/telemetry-plugin.ts @@ -1,5 +1,9 @@ import type { webpack } from 'next/dist/compiled/webpack/webpack' import { NormalModule } from 'next/dist/compiled/webpack/webpack' +import { + createUseCacheTracker, + type UseCacheTrackerKey, +} from './use-cache-tracker-utils' /** * List of target triples next-swc native binary supports. @@ -44,7 +48,7 @@ export type Feature = | 'skipTrailingSlashRedirect' | 'modularizeImports' | 'esmExternals' - + | UseCacheTrackerKey interface FeatureUsage { featureName: Feature invocationCount: number @@ -111,8 +115,15 @@ const BUILD_FEATURES: Array = [ 'esmExternals', ] +export type TelemetryLoaderContext = { + eliminatedPackages?: Set + useCacheTracker?: Map +} + const eliminatedPackages = new Set() +const useCacheTracker = createUseCacheTracker() + /** * Determine if there is a feature of interest in the specified 'module'. */ @@ -217,13 +228,20 @@ export class TelemetryPlugin implements webpack.WebpackPluginInstance { callback() } ) + if (compiler.options.mode === 'production' && !compiler.watchMode) { - compiler.hooks.compilation.tap(TelemetryPlugin.name, (compilation) => { - const moduleHooks = NormalModule.getCompilationHooks(compilation) - moduleHooks.loader.tap(TelemetryPlugin.name, (loaderContext: any) => { - loaderContext.eliminatedPackages = eliminatedPackages - }) - }) + compiler.hooks.thisCompilation.tap( + TelemetryPlugin.name, + (compilation) => { + const moduleHooks = NormalModule.getCompilationHooks(compilation) + moduleHooks.loader.tap(TelemetryPlugin.name, (loaderContext) => { + ;(loaderContext as TelemetryLoaderContext).eliminatedPackages = + eliminatedPackages + ;(loaderContext as TelemetryLoaderContext).useCacheTracker = + useCacheTracker + }) + } + ) } } @@ -234,6 +252,10 @@ export class TelemetryPlugin implements webpack.WebpackPluginInstance { packagesUsedInServerSideProps(): string[] { return Array.from(eliminatedPackages) } + + getUseCacheTracker(): Record { + return Object.fromEntries(useCacheTracker) + } } export type TelemetryPluginState = { @@ -241,4 +263,5 @@ export type TelemetryPluginState = { packagesUsedInServerSideProps: ReturnType< TelemetryPlugin['packagesUsedInServerSideProps'] > + useCacheTracker: ReturnType } diff --git a/packages/next/src/build/webpack/plugins/telemetry-plugin/update-telemetry-loader-context-from-swc.ts b/packages/next/src/build/webpack/plugins/telemetry-plugin/update-telemetry-loader-context-from-swc.ts new file mode 100644 index 0000000000000..30cd103296316 --- /dev/null +++ b/packages/next/src/build/webpack/plugins/telemetry-plugin/update-telemetry-loader-context-from-swc.ts @@ -0,0 +1,29 @@ +import type { TelemetryLoaderContext } from './telemetry-plugin' +export type SwcTransformTelemetryOutput = { + eliminatedPackages?: string + useCacheTelemetryTracker?: string +} + +export function updateTelemetryLoaderCtxFromTransformOutput( + ctx: TelemetryLoaderContext, + output: SwcTransformTelemetryOutput +) { + if (output.eliminatedPackages && ctx.eliminatedPackages) { + for (const pkg of JSON.parse(output.eliminatedPackages)) { + ctx.eliminatedPackages.add(pkg) + } + } + + if (output.useCacheTelemetryTracker && ctx.useCacheTracker) { + for (const [key, value] of JSON.parse(output.useCacheTelemetryTracker)) { + const prefixedKey = `useCache/${key}` as const + const numericValue = Number(value) + if (!isNaN(numericValue)) { + ctx.useCacheTracker.set( + prefixedKey, + (ctx.useCacheTracker.get(prefixedKey) || 0) + numericValue + ) + } + } + } +} diff --git a/packages/next/src/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.ts b/packages/next/src/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.ts new file mode 100644 index 0000000000000..b9839be10c895 --- /dev/null +++ b/packages/next/src/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.ts @@ -0,0 +1,34 @@ +export type UseCacheTrackerKey = `useCache/${string}` + +export const createUseCacheTracker = () => new Map() + +/** + * Example usage: + * + * const tracker1 = { 'useCache/file1': 1, 'useCache/file2': 2 }; + * const tracker2 = { 'useCache/file2': 3, 'useCache/file3': 4 }; + * const merged = mergeUseCacheTrackers(tracker1, tracker2); + * + * // Result: { 'useCache/file1': 1, 'useCache/file2': 5, 'useCache/file3': 4 } + */ +export const mergeUseCacheTrackers = ( + tracker1: Record | undefined, + tracker2: Record | undefined +): Record => { + const mergedTracker: Record = { ...tracker1 } + + if (tracker2) { + for (const key in tracker2) { + if (Object.prototype.hasOwnProperty.call(tracker2, key)) { + const typedKey = key as UseCacheTrackerKey + if (mergedTracker[typedKey] !== undefined) { + mergedTracker[typedKey] += tracker2[typedKey] + } else { + mergedTracker[typedKey] = tracker2[typedKey] + } + } + } + } + + return mergedTracker +} diff --git a/packages/next/src/telemetry/events/build.ts b/packages/next/src/telemetry/events/build.ts index 9f99fb1594133..e66a4d9fb65a6 100644 --- a/packages/next/src/telemetry/events/build.ts +++ b/packages/next/src/telemetry/events/build.ts @@ -1,5 +1,6 @@ -import type { TelemetryPlugin } from '../../build/webpack/plugins/telemetry-plugin' -import type { SWC_TARGET_TRIPLE } from '../../build/webpack/plugins/telemetry-plugin' +import type { TelemetryPlugin } from '../../build/webpack/plugins/telemetry-plugin/telemetry-plugin' +import type { SWC_TARGET_TRIPLE } from '../../build/webpack/plugins/telemetry-plugin/telemetry-plugin' +import type { UseCacheTrackerKey } from '../../build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils' const REGEXP_DIRECTORY_DUNDER = /[\\/]__[^\\/]+(? + {children} + + ) +} diff --git a/test/integration/telemetry/_app/use-cache/funtion-level-use-cache/page.jsx b/test/integration/telemetry/_app/use-cache/funtion-level-use-cache/page.jsx new file mode 100644 index 0000000000000..bc22c6faed610 --- /dev/null +++ b/test/integration/telemetry/_app/use-cache/funtion-level-use-cache/page.jsx @@ -0,0 +1,19 @@ +async function getCachedRandom(n) { + 'use cache' + return String(Math.ceil(Math.random() * n)) +} + +async function getCachedRandomV2(n) { + 'use cache: custom' + return String(Math.ceil(Math.random() * n)) +} + +export default async function Page() { + const random = await getCachedRandom(10) + const randomV2 = await getCachedRandomV2(10) + return ( +

+ Hello from cached page! {random} {randomV2} +

+ ) +} diff --git a/test/integration/telemetry/_app/use-cache/page.jsx b/test/integration/telemetry/_app/use-cache/page.jsx new file mode 100644 index 0000000000000..7f5347ab9e6ca --- /dev/null +++ b/test/integration/telemetry/_app/use-cache/page.jsx @@ -0,0 +1,5 @@ +'use cache' + +export default async function Page() { + return

Hello from cached page!

+} diff --git a/test/integration/telemetry/_app/use-cache/page2/page.jsx b/test/integration/telemetry/_app/use-cache/page2/page.jsx new file mode 100644 index 0000000000000..709c0dd079813 --- /dev/null +++ b/test/integration/telemetry/_app/use-cache/page2/page.jsx @@ -0,0 +1,9 @@ +async function getCachedRandom(n) { + 'use cache: custom' + return String(Math.ceil(Math.random() * n)) +} + +export default async function Page() { + 'use cache: custom' + return 'Page 2: ' + getCachedRandom(10) +} diff --git a/test/integration/telemetry/next.config.use-cache b/test/integration/telemetry/next.config.use-cache new file mode 100644 index 0000000000000..c4367991973f5 --- /dev/null +++ b/test/integration/telemetry/next.config.use-cache @@ -0,0 +1,8 @@ +module.exports = { + experimental: { + dynamicIO: true, + cacheHandlers: { + custom: require.resolve('next/dist/server/lib/cache-handlers/default'), + }, + }, +} diff --git a/test/integration/telemetry/test/config.test.js b/test/integration/telemetry/test/config.test.js index 51b0079e1e561..7acbc94a969da 100644 --- a/test/integration/telemetry/test/config.test.js +++ b/test/integration/telemetry/test/config.test.js @@ -713,6 +713,49 @@ describe('config telemetry', () => { ) } }) + + // TODO: support use cache tracking in Turbopack + ;(process.env.TURBOPACK ? it.skip : it)( + 'emits telemetry for useCache directive', + async () => { + // use cache depends on dynamicIO flag + await fs.rename( + path.join(appDir, 'next.config.use-cache'), + path.join(appDir, 'next.config.js') + ) + + await fs.move(path.join(appDir, '_app'), path.join(appDir, 'app')) + + const { stderr } = await nextBuild(appDir, [], { + stderr: true, + env: { NEXT_TELEMETRY_DEBUG: 1 }, + }) + + await fs.rename( + path.join(appDir, 'next.config.js'), + path.join(appDir, 'next.config.use-cache') + ) + + await fs.move(path.join(appDir, 'app'), path.join(appDir, '_app')) + + const featureUsageEvents = findAllTelemetryEvents( + stderr, + 'NEXT_BUILD_FEATURE_USAGE' + ) + + // eslint-disable-next-line jest/no-standalone-expect + expect(featureUsageEvents).toContainEqual({ + featureName: 'useCache/default', + invocationCount: 2, + }) + + // eslint-disable-next-line jest/no-standalone-expect + expect(featureUsageEvents).toContainEqual({ + featureName: 'useCache/custom', + invocationCount: 3, + }) + } + ) } ) }) From 72be937a553c380c685d63af1898fbc1dde4715c Mon Sep 17 00:00:00 2001 From: Vercel Release Bot <88769842+vercel-release-bot@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:26:48 +0100 Subject: [PATCH 02/13] Upgrade React from `5b51a2b9-20250116` to `9b62ee71-20250122` (#75187) --- package.json | 30 +- .../cjs/react-dom-client.development.js | 68 +- .../cjs/react-dom-client.production.js | 66 +- .../cjs/react-dom-profiling.development.js | 68 +- .../cjs/react-dom-profiling.profiling.js | 68 +- ...t-dom-server-legacy.browser.development.js | 395 +++++++++--- ...ct-dom-server-legacy.browser.production.js | 497 +++++++++++---- ...eact-dom-server-legacy.node.development.js | 395 +++++++++--- ...react-dom-server-legacy.node.production.js | 500 +++++++++++---- .../react-dom-server.browser.development.js | 422 ++++++++++--- .../react-dom-server.browser.production.js | 490 +++++++++++---- .../cjs/react-dom-server.bun.production.js | 592 ++++++++++++------ .../cjs/react-dom-server.edge.development.js | 422 ++++++++++--- .../cjs/react-dom-server.edge.production.js | 493 +++++++++++---- .../cjs/react-dom-server.node.development.js | 422 ++++++++++--- .../cjs/react-dom-server.node.production.js | 493 +++++++++++---- .../react-dom-unstable_testing.development.js | 68 +- .../react-dom-unstable_testing.production.js | 66 +- .../cjs/react-dom.development.js | 2 +- .../cjs/react-dom.production.js | 2 +- .../cjs/react-dom.react-server.development.js | 2 +- .../cjs/react-dom.react-server.production.js | 2 +- .../react-dom-experimental/package.json | 4 +- .../cjs/react-dom-client.development.js | 10 +- .../cjs/react-dom-client.production.js | 10 +- .../cjs/react-dom-profiling.development.js | 10 +- .../cjs/react-dom-profiling.profiling.js | 10 +- ...t-dom-server-legacy.browser.development.js | 379 ++++++++--- ...ct-dom-server-legacy.browser.production.js | 490 +++++++++++---- ...eact-dom-server-legacy.node.development.js | 379 ++++++++--- ...react-dom-server-legacy.node.production.js | 493 +++++++++++---- .../react-dom-server.browser.development.js | 381 ++++++++--- .../react-dom-server.browser.production.js | 458 ++++++++++---- .../cjs/react-dom-server.bun.production.js | 506 ++++++++++----- .../cjs/react-dom-server.edge.development.js | 381 ++++++++--- .../cjs/react-dom-server.edge.production.js | 461 ++++++++++---- .../cjs/react-dom-server.node.development.js | 381 ++++++++--- .../cjs/react-dom-server.node.production.js | 461 ++++++++++---- .../react-dom/cjs/react-dom.development.js | 2 +- .../react-dom/cjs/react-dom.production.js | 2 +- .../cjs/react-dom.react-server.development.js | 2 +- .../cjs/react-dom.react-server.production.js | 2 +- .../next/src/compiled/react-dom/package.json | 4 +- .../cjs/react.development.js | 10 +- .../cjs/react.production.js | 11 +- .../cjs/react.react-server.development.js | 2 +- .../cjs/react.react-server.production.js | 2 +- .../next/src/compiled/react-is/package.json | 2 +- ...om-turbopack-client.browser.development.js | 8 +- ...r-dom-turbopack-client.edge.development.js | 4 +- ...r-dom-turbopack-client.node.development.js | 4 +- ...om-turbopack-server.browser.development.js | 17 +- ...r-dom-turbopack-server.edge.development.js | 17 +- ...r-dom-turbopack-server.node.development.js | 17 +- .../package.json | 4 +- ...om-turbopack-client.browser.development.js | 8 +- ...r-dom-turbopack-client.edge.development.js | 4 +- ...r-dom-turbopack-client.node.development.js | 4 +- ...om-turbopack-server.browser.development.js | 17 +- ...r-dom-turbopack-server.edge.development.js | 17 +- ...r-dom-turbopack-server.node.development.js | 17 +- .../react-server-dom-turbopack/package.json | 4 +- ...-dom-webpack-client.browser.development.js | 8 +- ...ver-dom-webpack-client.edge.development.js | 4 +- ...ver-dom-webpack-client.node.development.js | 4 +- ...bpack-client.node.unbundled.development.js | 4 +- ...-dom-webpack-server.browser.development.js | 17 +- ...ver-dom-webpack-server.edge.development.js | 17 +- ...ver-dom-webpack-server.node.development.js | 17 +- ...bpack-server.node.unbundled.development.js | 17 +- .../package.json | 4 +- ...-dom-webpack-client.browser.development.js | 8 +- ...ver-dom-webpack-client.edge.development.js | 4 +- ...ver-dom-webpack-client.node.development.js | 4 +- ...bpack-client.node.unbundled.development.js | 4 +- ...-dom-webpack-server.browser.development.js | 17 +- ...ver-dom-webpack-server.edge.development.js | 17 +- ...ver-dom-webpack-server.node.development.js | 17 +- ...bpack-server.node.unbundled.development.js | 17 +- .../react-server-dom-webpack/package.json | 4 +- .../compiled/react/cjs/react.development.js | 3 +- .../compiled/react/cjs/react.production.js | 4 +- .../cjs/react.react-server.development.js | 2 +- .../cjs/react.react-server.production.js | 2 +- .../next/src/compiled/unistore/unistore.js | 2 +- pnpm-lock.yaml | 476 +++++++------- 86 files changed, 8365 insertions(+), 2866 deletions(-) diff --git a/package.json b/package.json index 68cda2e6d081f..9ff2177cba074 100644 --- a/package.json +++ b/package.json @@ -213,16 +213,16 @@ "pretty-ms": "7.0.0", "random-seed": "0.3.0", "react": "19.0.0", - "react-builtin": "npm:react@19.1.0-canary-5b51a2b9-20250116", + "react-builtin": "npm:react@19.1.0-canary-9b62ee71-20250122", "react-dom": "19.0.0", - "react-dom-builtin": "npm:react-dom@19.1.0-canary-5b51a2b9-20250116", - "react-dom-experimental-builtin": "npm:react-dom@0.0.0-experimental-5b51a2b9-20250116", - "react-experimental-builtin": "npm:react@0.0.0-experimental-5b51a2b9-20250116", - "react-is-builtin": "npm:react-is@19.1.0-canary-5b51a2b9-20250116", - "react-server-dom-turbopack": "19.1.0-canary-5b51a2b9-20250116", - "react-server-dom-turbopack-experimental": "npm:react-server-dom-turbopack@0.0.0-experimental-5b51a2b9-20250116", - "react-server-dom-webpack": "19.1.0-canary-5b51a2b9-20250116", - "react-server-dom-webpack-experimental": "npm:react-server-dom-webpack@0.0.0-experimental-5b51a2b9-20250116", + "react-dom-builtin": "npm:react-dom@19.1.0-canary-9b62ee71-20250122", + "react-dom-experimental-builtin": "npm:react-dom@0.0.0-experimental-9b62ee71-20250122", + "react-experimental-builtin": "npm:react@0.0.0-experimental-9b62ee71-20250122", + "react-is-builtin": "npm:react-is@19.1.0-canary-9b62ee71-20250122", + "react-server-dom-turbopack": "19.1.0-canary-9b62ee71-20250122", + "react-server-dom-turbopack-experimental": "npm:react-server-dom-turbopack@0.0.0-experimental-9b62ee71-20250122", + "react-server-dom-webpack": "19.1.0-canary-9b62ee71-20250122", + "react-server-dom-webpack-experimental": "npm:react-server-dom-webpack@0.0.0-experimental-9b62ee71-20250122", "react-ssr-prepass": "1.0.8", "react-virtualized": "9.22.3", "relay-compiler": "13.0.2", @@ -232,8 +232,8 @@ "resolve-from": "5.0.0", "sass": "1.54.0", "satori": "0.10.9", - "scheduler-builtin": "npm:scheduler@0.26.0-canary-5b51a2b9-20250116", - "scheduler-experimental-builtin": "npm:scheduler@0.0.0-experimental-5b51a2b9-20250116", + "scheduler-builtin": "npm:scheduler@0.26.0-canary-9b62ee71-20250122", + "scheduler-experimental-builtin": "npm:scheduler@0.0.0-experimental-9b62ee71-20250122", "seedrandom": "3.0.5", "semver": "7.3.7", "shell-quote": "1.7.3", @@ -274,10 +274,10 @@ "@types/react": "19.0.0", "@types/react-dom": "19.0.0", "jest-snapshot": "30.0.0-alpha.6", - "react": "19.1.0-canary-5b51a2b9-20250116", - "react-dom": "19.1.0-canary-5b51a2b9-20250116", - "react-is": "19.1.0-canary-5b51a2b9-20250116", - "scheduler": "0.26.0-canary-5b51a2b9-20250116" + "react": "19.1.0-canary-9b62ee71-20250122", + "react-dom": "19.1.0-canary-9b62ee71-20250122", + "react-is": "19.1.0-canary-9b62ee71-20250122", + "scheduler": "0.26.0-canary-9b62ee71-20250122" }, "patchedDependencies": { "webpack-sources@3.2.3": "patches/webpack-sources@3.2.3.patch", diff --git a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-client.development.js b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-client.development.js index a7d45d264e991..333f872d78d52 100644 --- a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-client.development.js +++ b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-client.development.js @@ -14846,14 +14846,31 @@ ? props.name : instance.autoName; } - function getViewTransitionClassName(className, eventClassName) { - return null == eventClassName - ? className - : "none" === eventClassName - ? eventClassName - : null != className - ? className + " " + eventClassName - : eventClassName; + function getClassNameByType(classByType) { + if (null == classByType || "string" === typeof classByType) + return classByType; + var className = null, + activeTypes = pendingTransitionTypes; + if (null !== activeTypes) + for (var i = 0; i < activeTypes.length; i++) { + var match = classByType[activeTypes[i]]; + if (null != match) { + if ("none" === match) return "none"; + className = null == className ? match : className + (" " + match); + } + } + return null == className ? classByType.default : className; + } + function getViewTransitionClassName(defaultClass, eventClass) { + defaultClass = getClassNameByType(defaultClass); + eventClass = getClassNameByType(eventClass); + return null == eventClass + ? defaultClass + : "none" === eventClass + ? eventClass + : null != defaultClass && "none" !== defaultClass + ? defaultClass + " " + eventClass + : eventClass; } function markUpdate(workInProgress) { workInProgress.flags |= 4; @@ -17005,10 +17022,14 @@ pendingEffectsRemainingLanes = didIncludeRenderPhaseUpdate; pendingPassiveTransitions = transitions; pendingRecoverableErrors = recoverableErrors; - pendingViewTransitionEvents = null; pendingEffectsRenderEndTime = completedRenderEndTime; pendingSuspendedCommitReason = suspendedCommitReason; - recoverableErrors = (lanes & 335544192) === lanes ? 10262 : 10256; + pendingViewTransitionEvents = null; + (lanes & 335544192) === lanes + ? ((pendingTransitionTypes = ReactSharedInternals.V), + (ReactSharedInternals.V = null), + (recoverableErrors = 10262)) + : ((pendingTransitionTypes = null), (recoverableErrors = 10256)); 0 !== finishedWork.actualDuration || 0 !== (finishedWork.subtreeFlags & recoverableErrors) || 0 !== (finishedWork.flags & recoverableErrors) @@ -17054,6 +17075,7 @@ (shouldStartViewTransition && startViewTransition( root.containerInfo, + pendingTransitionTypes, flushMutationEffects, flushLayoutEffects, flushAfterMutationEffects, @@ -17351,13 +17373,17 @@ } } recoverableErrors = pendingViewTransitionEvents; + onRecoverableError = pendingTransitionTypes; + pendingTransitionTypes = null; if (null !== recoverableErrors) for ( - pendingViewTransitionEvents = null, onRecoverableError = 0; - onRecoverableError < recoverableErrors.length; - onRecoverableError++ + pendingViewTransitionEvents = null, + null === onRecoverableError && (onRecoverableError = []), + recoverableError = 0; + recoverableError < recoverableErrors.length; + recoverableError++ ) - (0, recoverableErrors[onRecoverableError])(); + (0, recoverableErrors[recoverableError])(onRecoverableError); 0 !== (pendingEffectsLanes & 3) && flushPendingEffects(); ensureRootIsScheduled(root); suspendedCommitReason = root.pendingLanes; @@ -21222,6 +21248,7 @@ } function startViewTransition( rootContainer, + transitionTypes, mutationCallback, layoutCallback, afterMutationCallback, @@ -21268,7 +21295,7 @@ ); afterMutationCallback(); }, - types: null + types: transitionTypes }); ownerDocument.__reactViewTransition = transition; transition.ready.then(void 0, function (reason) { @@ -26218,6 +26245,7 @@ pendingPassiveTransitions = null, pendingRecoverableErrors = null, pendingViewTransitionEvents = null, + pendingTransitionTypes = null, pendingSuspendedCommitReason = IMMEDIATE_COMMIT, NESTED_UPDATE_LIMIT = 50, nestedUpdateCount = 0, @@ -26794,11 +26822,11 @@ }; (function () { var isomorphicReactPackageVersion = React.version; - if ("19.1.0-experimental-5b51a2b9-20250116" !== isomorphicReactPackageVersion) + if ("19.1.0-experimental-9b62ee71-20250122" !== isomorphicReactPackageVersion) throw Error( 'Incompatible React versions: The "react" and "react-dom" packages must have the exact same version. Instead got:\n - react: ' + (isomorphicReactPackageVersion + - "\n - react-dom: 19.1.0-experimental-5b51a2b9-20250116\nLearn more: https://react.dev/warnings/version-mismatch") + "\n - react-dom: 19.1.0-experimental-9b62ee71-20250122\nLearn more: https://react.dev/warnings/version-mismatch") ); })(); ("function" === typeof Map && @@ -26835,10 +26863,10 @@ !(function () { var internals = { bundleType: 1, - version: "19.1.0-experimental-5b51a2b9-20250116", + version: "19.1.0-experimental-9b62ee71-20250122", rendererPackageName: "react-dom", currentDispatcherRef: ReactSharedInternals, - reconcilerVersion: "19.1.0-experimental-5b51a2b9-20250116" + reconcilerVersion: "19.1.0-experimental-9b62ee71-20250122" }; internals.overrideHookState = overrideHookState; internals.overrideHookStateDeletePath = overrideHookStateDeletePath; @@ -26982,7 +27010,7 @@ listenToAllSupportedEvents(container); return new ReactDOMHydrationRoot(initialChildren); }; - exports.version = "19.1.0-experimental-5b51a2b9-20250116"; + exports.version = "19.1.0-experimental-9b62ee71-20250122"; "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && diff --git a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-client.production.js b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-client.production.js index 3bdf97b581476..b4ba8c00ebf79 100644 --- a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-client.production.js +++ b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-client.production.js @@ -10455,14 +10455,31 @@ function getViewTransitionName(props, instance) { ? props.name : instance.autoName; } -function getViewTransitionClassName(className, eventClassName) { - return null == eventClassName - ? className - : "none" === eventClassName - ? eventClassName - : null != className - ? className + " " + eventClassName - : eventClassName; +function getClassNameByType(classByType) { + if (null == classByType || "string" === typeof classByType) + return classByType; + var className = null, + activeTypes = pendingTransitionTypes; + if (null !== activeTypes) + for (var i = 0; i < activeTypes.length; i++) { + var match = classByType[activeTypes[i]]; + if (null != match) { + if ("none" === match) return "none"; + className = null == className ? match : className + (" " + match); + } + } + return null == className ? classByType.default : className; +} +function getViewTransitionClassName(defaultClass, eventClass) { + defaultClass = getClassNameByType(defaultClass); + eventClass = getClassNameByType(eventClass); + return null == eventClass + ? defaultClass + : "none" === eventClass + ? eventClass + : null != defaultClass && "none" !== defaultClass + ? defaultClass + " " + eventClass + : eventClass; } function markUpdate(workInProgress) { workInProgress.flags |= 4; @@ -11142,6 +11159,7 @@ var DefaultAsyncDispatcher = { pendingPassiveTransitions = null, pendingRecoverableErrors = null, pendingViewTransitionEvents = null, + pendingTransitionTypes = null, nestedUpdateCount = 0, rootWithNestedUpdates = null; function requestUpdateLane() { @@ -12010,7 +12028,11 @@ function commitRoot( pendingPassiveTransitions = transitions; pendingRecoverableErrors = recoverableErrors; pendingViewTransitionEvents = null; - recoverableErrors = (lanes & 335544192) === lanes ? 10262 : 10256; + (lanes & 335544192) === lanes + ? ((pendingTransitionTypes = ReactSharedInternals.V), + (ReactSharedInternals.V = null), + (recoverableErrors = 10262)) + : ((pendingTransitionTypes = null), (recoverableErrors = 10256)); 0 !== (finishedWork.subtreeFlags & recoverableErrors) || 0 !== (finishedWork.flags & recoverableErrors) ? ((root.callbackNode = null), @@ -12045,6 +12067,7 @@ function commitRoot( (shouldStartViewTransition && startViewTransition( root.containerInfo, + pendingTransitionTypes, flushMutationEffects, flushLayoutEffects, flushAfterMutationEffects, @@ -12257,13 +12280,17 @@ function flushSpawnedWork() { } } recoverableErrors = pendingViewTransitionEvents; + onRecoverableError = pendingTransitionTypes; + pendingTransitionTypes = null; if (null !== recoverableErrors) for ( - pendingViewTransitionEvents = null, onRecoverableError = 0; - onRecoverableError < recoverableErrors.length; - onRecoverableError++ + pendingViewTransitionEvents = null, + null === onRecoverableError && (onRecoverableError = []), + recoverableError = 0; + recoverableError < recoverableErrors.length; + recoverableError++ ) - (0, recoverableErrors[onRecoverableError])(); + (0, recoverableErrors[recoverableError])(onRecoverableError); 0 !== (pendingEffectsLanes & 3) && flushPendingEffects(); ensureRootIsScheduled(root); passiveSubtreeMask = root.pendingLanes; @@ -14609,6 +14636,7 @@ function measureInstance(instance) { } function startViewTransition( rootContainer, + transitionTypes, mutationCallback, layoutCallback, afterMutationCallback, @@ -14653,7 +14681,7 @@ function startViewTransition( ); afterMutationCallback(); }, - types: null + types: transitionTypes }); ownerDocument.__reactViewTransition = transition; transition.ready.then(spawnedWorkCallback, spawnedWorkCallback); @@ -16373,14 +16401,14 @@ ReactDOMHydrationRoot.prototype.unstable_scheduleHydration = function (target) { }; var isomorphicReactPackageVersion$jscomp$inline_1799 = React.version; if ( - "19.1.0-experimental-5b51a2b9-20250116" !== + "19.1.0-experimental-9b62ee71-20250122" !== isomorphicReactPackageVersion$jscomp$inline_1799 ) throw Error( formatProdErrorMessage( 527, isomorphicReactPackageVersion$jscomp$inline_1799, - "19.1.0-experimental-5b51a2b9-20250116" + "19.1.0-experimental-9b62ee71-20250122" ) ); ReactDOMSharedInternals.findDOMNode = function (componentOrElement) { @@ -16402,10 +16430,10 @@ ReactDOMSharedInternals.findDOMNode = function (componentOrElement) { }; var internals$jscomp$inline_2330 = { bundleType: 0, - version: "19.1.0-experimental-5b51a2b9-20250116", + version: "19.1.0-experimental-9b62ee71-20250122", rendererPackageName: "react-dom", currentDispatcherRef: ReactSharedInternals, - reconcilerVersion: "19.1.0-experimental-5b51a2b9-20250116" + reconcilerVersion: "19.1.0-experimental-9b62ee71-20250122" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_2331 = __REACT_DEVTOOLS_GLOBAL_HOOK__; @@ -16511,4 +16539,4 @@ exports.hydrateRoot = function (container, initialChildren, options) { listenToAllSupportedEvents(container); return new ReactDOMHydrationRoot(initialChildren); }; -exports.version = "19.1.0-experimental-5b51a2b9-20250116"; +exports.version = "19.1.0-experimental-9b62ee71-20250122"; diff --git a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-profiling.development.js b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-profiling.development.js index b4f60a3a04858..cf0a7739f7689 100644 --- a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-profiling.development.js +++ b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-profiling.development.js @@ -14854,14 +14854,31 @@ ? props.name : instance.autoName; } - function getViewTransitionClassName(className, eventClassName) { - return null == eventClassName - ? className - : "none" === eventClassName - ? eventClassName - : null != className - ? className + " " + eventClassName - : eventClassName; + function getClassNameByType(classByType) { + if (null == classByType || "string" === typeof classByType) + return classByType; + var className = null, + activeTypes = pendingTransitionTypes; + if (null !== activeTypes) + for (var i = 0; i < activeTypes.length; i++) { + var match = classByType[activeTypes[i]]; + if (null != match) { + if ("none" === match) return "none"; + className = null == className ? match : className + (" " + match); + } + } + return null == className ? classByType.default : className; + } + function getViewTransitionClassName(defaultClass, eventClass) { + defaultClass = getClassNameByType(defaultClass); + eventClass = getClassNameByType(eventClass); + return null == eventClass + ? defaultClass + : "none" === eventClass + ? eventClass + : null != defaultClass && "none" !== defaultClass + ? defaultClass + " " + eventClass + : eventClass; } function markUpdate(workInProgress) { workInProgress.flags |= 4; @@ -17013,10 +17030,14 @@ pendingEffectsRemainingLanes = didIncludeRenderPhaseUpdate; pendingPassiveTransitions = transitions; pendingRecoverableErrors = recoverableErrors; - pendingViewTransitionEvents = null; pendingEffectsRenderEndTime = completedRenderEndTime; pendingSuspendedCommitReason = suspendedCommitReason; - recoverableErrors = (lanes & 335544192) === lanes ? 10262 : 10256; + pendingViewTransitionEvents = null; + (lanes & 335544192) === lanes + ? ((pendingTransitionTypes = ReactSharedInternals.V), + (ReactSharedInternals.V = null), + (recoverableErrors = 10262)) + : ((pendingTransitionTypes = null), (recoverableErrors = 10256)); 0 !== finishedWork.actualDuration || 0 !== (finishedWork.subtreeFlags & recoverableErrors) || 0 !== (finishedWork.flags & recoverableErrors) @@ -17062,6 +17083,7 @@ (shouldStartViewTransition && startViewTransition( root.containerInfo, + pendingTransitionTypes, flushMutationEffects, flushLayoutEffects, flushAfterMutationEffects, @@ -17359,13 +17381,17 @@ } } recoverableErrors = pendingViewTransitionEvents; + onRecoverableError = pendingTransitionTypes; + pendingTransitionTypes = null; if (null !== recoverableErrors) for ( - pendingViewTransitionEvents = null, onRecoverableError = 0; - onRecoverableError < recoverableErrors.length; - onRecoverableError++ + pendingViewTransitionEvents = null, + null === onRecoverableError && (onRecoverableError = []), + recoverableError = 0; + recoverableError < recoverableErrors.length; + recoverableError++ ) - (0, recoverableErrors[onRecoverableError])(); + (0, recoverableErrors[recoverableError])(onRecoverableError); 0 !== (pendingEffectsLanes & 3) && flushPendingEffects(); ensureRootIsScheduled(root); suspendedCommitReason = root.pendingLanes; @@ -21230,6 +21256,7 @@ } function startViewTransition( rootContainer, + transitionTypes, mutationCallback, layoutCallback, afterMutationCallback, @@ -21276,7 +21303,7 @@ ); afterMutationCallback(); }, - types: null + types: transitionTypes }); ownerDocument.__reactViewTransition = transition; transition.ready.then(void 0, function (reason) { @@ -26271,6 +26298,7 @@ pendingPassiveTransitions = null, pendingRecoverableErrors = null, pendingViewTransitionEvents = null, + pendingTransitionTypes = null, pendingSuspendedCommitReason = IMMEDIATE_COMMIT, NESTED_UPDATE_LIMIT = 50, nestedUpdateCount = 0, @@ -26847,11 +26875,11 @@ }; (function () { var isomorphicReactPackageVersion = React.version; - if ("19.1.0-experimental-5b51a2b9-20250116" !== isomorphicReactPackageVersion) + if ("19.1.0-experimental-9b62ee71-20250122" !== isomorphicReactPackageVersion) throw Error( 'Incompatible React versions: The "react" and "react-dom" packages must have the exact same version. Instead got:\n - react: ' + (isomorphicReactPackageVersion + - "\n - react-dom: 19.1.0-experimental-5b51a2b9-20250116\nLearn more: https://react.dev/warnings/version-mismatch") + "\n - react-dom: 19.1.0-experimental-9b62ee71-20250122\nLearn more: https://react.dev/warnings/version-mismatch") ); })(); ("function" === typeof Map && @@ -26888,10 +26916,10 @@ !(function () { var internals = { bundleType: 1, - version: "19.1.0-experimental-5b51a2b9-20250116", + version: "19.1.0-experimental-9b62ee71-20250122", rendererPackageName: "react-dom", currentDispatcherRef: ReactSharedInternals, - reconcilerVersion: "19.1.0-experimental-5b51a2b9-20250116" + reconcilerVersion: "19.1.0-experimental-9b62ee71-20250122" }; internals.overrideHookState = overrideHookState; internals.overrideHookStateDeletePath = overrideHookStateDeletePath; @@ -27365,7 +27393,7 @@ exports.useFormStatus = function () { return resolveDispatcher().useHostTransitionStatus(); }; - exports.version = "19.1.0-experimental-5b51a2b9-20250116"; + exports.version = "19.1.0-experimental-9b62ee71-20250122"; "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && diff --git a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-profiling.profiling.js b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-profiling.profiling.js index f328af5051a23..9dd76539c2efb 100644 --- a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-profiling.profiling.js +++ b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-profiling.profiling.js @@ -11404,14 +11404,31 @@ function getViewTransitionName(props, instance) { ? props.name : instance.autoName; } -function getViewTransitionClassName(className, eventClassName) { - return null == eventClassName - ? className - : "none" === eventClassName - ? eventClassName - : null != className - ? className + " " + eventClassName - : eventClassName; +function getClassNameByType(classByType) { + if (null == classByType || "string" === typeof classByType) + return classByType; + var className = null, + activeTypes = pendingTransitionTypes; + if (null !== activeTypes) + for (var i = 0; i < activeTypes.length; i++) { + var match = classByType[activeTypes[i]]; + if (null != match) { + if ("none" === match) return "none"; + className = null == className ? match : className + (" " + match); + } + } + return null == className ? classByType.default : className; +} +function getViewTransitionClassName(defaultClass, eventClass) { + defaultClass = getClassNameByType(defaultClass); + eventClass = getClassNameByType(eventClass); + return null == eventClass + ? defaultClass + : "none" === eventClass + ? eventClass + : null != defaultClass && "none" !== defaultClass + ? defaultClass + " " + eventClass + : eventClass; } function markUpdate(workInProgress) { workInProgress.flags |= 4; @@ -12158,6 +12175,7 @@ var DefaultAsyncDispatcher = { pendingPassiveTransitions = null, pendingRecoverableErrors = null, pendingViewTransitionEvents = null, + pendingTransitionTypes = null, pendingSuspendedCommitReason = 0, nestedUpdateCount = 0, rootWithNestedUpdates = null; @@ -13293,10 +13311,14 @@ function commitRoot( pendingEffectsRemainingLanes = didIncludeRenderPhaseUpdate; pendingPassiveTransitions = transitions; pendingRecoverableErrors = recoverableErrors; - pendingViewTransitionEvents = null; pendingEffectsRenderEndTime = completedRenderEndTime; pendingSuspendedCommitReason = suspendedCommitReason; - recoverableErrors = (lanes & 335544192) === lanes ? 10262 : 10256; + pendingViewTransitionEvents = null; + (lanes & 335544192) === lanes + ? ((pendingTransitionTypes = ReactSharedInternals.V), + (ReactSharedInternals.V = null), + (recoverableErrors = 10262)) + : ((pendingTransitionTypes = null), (recoverableErrors = 10256)); 0 !== finishedWork.actualDuration || 0 !== (finishedWork.subtreeFlags & recoverableErrors) || 0 !== (finishedWork.flags & recoverableErrors) @@ -13339,6 +13361,7 @@ function commitRoot( (shouldStartViewTransition && startViewTransition( root.containerInfo, + pendingTransitionTypes, flushMutationEffects, flushLayoutEffects, flushAfterMutationEffects, @@ -13600,13 +13623,17 @@ function flushSpawnedWork() { } } recoverableErrors = pendingViewTransitionEvents; + onRecoverableError = pendingTransitionTypes; + pendingTransitionTypes = null; if (null !== recoverableErrors) for ( - pendingViewTransitionEvents = null, onRecoverableError = 0; - onRecoverableError < recoverableErrors.length; - onRecoverableError++ + pendingViewTransitionEvents = null, + null === onRecoverableError && (onRecoverableError = []), + recoverableError = 0; + recoverableError < recoverableErrors.length; + recoverableError++ ) - (0, recoverableErrors[onRecoverableError])(); + (0, recoverableErrors[recoverableError])(onRecoverableError); 0 !== (pendingEffectsLanes & 3) && flushPendingEffects(); ensureRootIsScheduled(root); suspendedCommitReason = root.pendingLanes; @@ -16024,6 +16051,7 @@ function measureInstance(instance) { } function startViewTransition( rootContainer, + transitionTypes, mutationCallback, layoutCallback, afterMutationCallback, @@ -16068,7 +16096,7 @@ function startViewTransition( ); afterMutationCallback(); }, - types: null + types: transitionTypes }); ownerDocument.__reactViewTransition = transition; transition.ready.then(spawnedWorkCallback, spawnedWorkCallback); @@ -17805,14 +17833,14 @@ ReactDOMHydrationRoot.prototype.unstable_scheduleHydration = function (target) { }; var isomorphicReactPackageVersion$jscomp$inline_1947 = React.version; if ( - "19.1.0-experimental-5b51a2b9-20250116" !== + "19.1.0-experimental-9b62ee71-20250122" !== isomorphicReactPackageVersion$jscomp$inline_1947 ) throw Error( formatProdErrorMessage( 527, isomorphicReactPackageVersion$jscomp$inline_1947, - "19.1.0-experimental-5b51a2b9-20250116" + "19.1.0-experimental-9b62ee71-20250122" ) ); ReactDOMSharedInternals.findDOMNode = function (componentOrElement) { @@ -17834,10 +17862,10 @@ ReactDOMSharedInternals.findDOMNode = function (componentOrElement) { }; var internals$jscomp$inline_2471 = { bundleType: 0, - version: "19.1.0-experimental-5b51a2b9-20250116", + version: "19.1.0-experimental-9b62ee71-20250122", rendererPackageName: "react-dom", currentDispatcherRef: ReactSharedInternals, - reconcilerVersion: "19.1.0-experimental-5b51a2b9-20250116" + reconcilerVersion: "19.1.0-experimental-9b62ee71-20250122" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_2472 = __REACT_DEVTOOLS_GLOBAL_HOOK__; @@ -18104,7 +18132,7 @@ exports.useFormState = function (action, initialState, permalink) { exports.useFormStatus = function () { return ReactSharedInternals.H.useHostTransitionStatus(); }; -exports.version = "19.1.0-experimental-5b51a2b9-20250116"; +exports.version = "19.1.0-experimental-9b62ee71-20250122"; "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && diff --git a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js index 66e8f2437040a..a1ded42b70cfb 100644 --- a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js +++ b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js @@ -762,6 +762,9 @@ moduleScriptResources: {} }; } + function createPreambleState() { + return { htmlChunks: null, headChunks: null, bodyChunks: null }; + } function createFormatContext(insertionMode, selectedValue, tagScope) { return { insertionMode: insertionMode, @@ -821,16 +824,26 @@ null, parentContext.tagScope ); + case "head": + if (parentContext.insertionMode < HTML_MODE) + return createFormatContext( + HTML_HEAD_MODE, + null, + parentContext.tagScope + ); + break; + case "html": + if (parentContext.insertionMode === ROOT_HTML_MODE) + return createFormatContext( + HTML_HTML_MODE, + null, + parentContext.tagScope + ); } - return parentContext.insertionMode >= HTML_TABLE_MODE + return parentContext.insertionMode >= HTML_TABLE_MODE || + parentContext.insertionMode < HTML_MODE ? createFormatContext(HTML_MODE, null, parentContext.tagScope) - : parentContext.insertionMode === ROOT_HTML_MODE - ? "html" === type - ? createFormatContext(HTML_HTML_MODE, null, parentContext.tagScope) - : createFormatContext(HTML_MODE, null, parentContext.tagScope) - : parentContext.insertionMode === HTML_HTML_MODE - ? createFormatContext(HTML_MODE, null, parentContext.tagScope) - : parentContext; + : parentContext; } function pushStyleAttribute(target, style) { if ("object" !== typeof style) @@ -1499,6 +1512,7 @@ props, resumableState, renderState, + preambleState, hoistableState, formatContext, textEmbedded, @@ -2653,13 +2667,13 @@ case "missing-glyph": break; case "head": - if ( - formatContext.insertionMode < HTML_MODE && - null === renderState.headChunks - ) { - renderState.headChunks = []; + if (formatContext.insertionMode < HTML_MODE) { + var preamble = preambleState || renderState.preamble; + if (preamble.headChunks) + throw Error("The `` tag may only be rendered once."); + preamble.headChunks = []; var JSCompiler_inline_result$jscomp$9 = pushStartGenericElement( - renderState.headChunks, + preamble.headChunks, props, "head" ); @@ -2670,24 +2684,42 @@ "head" ); return JSCompiler_inline_result$jscomp$9; - case "html": - if ( - formatContext.insertionMode === ROOT_HTML_MODE && - null === renderState.htmlChunks - ) { - renderState.htmlChunks = [doctypeChunk]; + case "body": + if (formatContext.insertionMode < HTML_MODE) { + var preamble$jscomp$0 = preambleState || renderState.preamble; + if (preamble$jscomp$0.bodyChunks) + throw Error("The `` tag may only be rendered once."); + preamble$jscomp$0.bodyChunks = []; var JSCompiler_inline_result$jscomp$10 = pushStartGenericElement( - renderState.htmlChunks, + preamble$jscomp$0.bodyChunks, props, - "html" + "body" ); } else JSCompiler_inline_result$jscomp$10 = pushStartGenericElement( target$jscomp$0, props, - "html" + "body" ); return JSCompiler_inline_result$jscomp$10; + case "html": + if (formatContext.insertionMode === ROOT_HTML_MODE) { + var preamble$jscomp$1 = preambleState || renderState.preamble; + if (preamble$jscomp$1.htmlChunks) + throw Error("The `` tag may only be rendered once."); + preamble$jscomp$1.htmlChunks = [doctypeChunk]; + var JSCompiler_inline_result$jscomp$11 = pushStartGenericElement( + preamble$jscomp$1.htmlChunks, + props, + "html" + ); + } else + JSCompiler_inline_result$jscomp$11 = pushStartGenericElement( + target$jscomp$0, + props, + "html" + ); + return JSCompiler_inline_result$jscomp$11; default: if (-1 !== type.indexOf("-")) { target$jscomp$0.push(startChunkForTag(type)); @@ -2754,6 +2786,15 @@ ((chunk = ""), endTagCache.set(tag, chunk)); return chunk; } + function hoistPreambleState(renderState, preambleState) { + renderState = renderState.preamble; + null === renderState.htmlChunks && + (renderState.htmlChunks = preambleState.htmlChunks); + null === renderState.headChunks && + (renderState.headChunks = preambleState.headChunks); + null === renderState.bodyChunks && + (renderState.bodyChunks = preambleState.bodyChunks); + } function writeBootstrap(destination, renderState) { renderState = renderState.bootstrapChunks; for (var i = 0; i < renderState.length - 1; i++) @@ -2777,6 +2818,7 @@ switch (formatContext.insertionMode) { case ROOT_HTML_MODE: case HTML_HTML_MODE: + case HTML_HEAD_MODE: case HTML_MODE: return ( destination.push(startSegmentHTML), @@ -2841,6 +2883,7 @@ switch (formatContext.insertionMode) { case ROOT_HTML_MODE: case HTML_HTML_MODE: + case HTML_HEAD_MODE: case HTML_MODE: return destination.push(endSegmentHTML); case SVG_MODE: @@ -3337,8 +3380,7 @@ segmentPrefix: idPrefix + "S:", boundaryPrefix: idPrefix + "B:", startInlineScript: "