Improve the NFT error message and ignore comment handling (#95144)

Fixes #95125 in two ways

The OP admits that they misread the message in a few ways, but we can
fix all of them

1. Be specific about what function might need an annotation in the error
message. A user with a nearby `path.join` might think that that is where
the annotation belongs instead of on the function with the warning.

2. Be more flexible with how these `ignore` comments are interpreted
`fs.readFileSync(path.join(process.cwd(), someVar))` shouldn't require 2
annotations. Handle this by checking for and detecting `turbopackIgnore`
comments in all expressions! This will then bubble up through the linker
and just cause us to ignore corresponding fs access apis.
This commit is contained in:
Luke Sandberg
2026-07-14 08:13:12 +02:00
committed by GitHub
parent d774f7db29
commit e0b2cebde5
15 changed files with 404 additions and 142 deletions
+26 -13
View File
@@ -7,7 +7,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
use tracing::Instrument;
use turbo_rcstr::{RcStr, rcstr};
use turbo_tasks::{
FxIndexMap, FxIndexSet, ReadRef, ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, Vc,
FxIndexMap, FxIndexSet, ReadRef, ResolvedVc, TraitRef, TryFlatJoinIterExt, TryJoinIterExt, Vc,
};
use turbo_tasks_fs::{
DirectoryEntry, FileSystemPath,
@@ -23,6 +23,7 @@ use turbopack_core::{
module::{Module, Modules},
module_graph::{GraphTraversalAction, ModuleGraph},
raw_module::RawModule,
reference::DynamicTraceReference,
};
use crate::project::Project;
@@ -182,7 +183,7 @@ async fn get_glob_includes(
let glob_result = project_root_path.read_glob(glob).await?;
// Walk the full glob_result using an explicit stack to avoid async recursion overheads.
// Use a BTreeSet to get determinstic order (return value of `read_glob` has random order).
// Use a BTreeSet to get deterministic order (return value of `read_glob` has random order).
let mut result = vec![];
let mut stack = VecDeque::new();
stack.push_back(glob_result);
@@ -334,13 +335,14 @@ pub async fn traced_modules_for_entries(
)?;
for (parent, reference) in forbidden_issues {
ForbiddenTracedFileIssue::new(
parent.ident().await?.path.clone(),
reference.into_trait_ref().await?.source(),
)
.to_resolved()
.await?
.emit();
let reference = reference.into_trait_ref().await?;
let source = reference.source();
let origin_fn_name = TraitRef::try_downcast::<Box<dyn DynamicTraceReference>>(reference)
.map(|traced| traced.origin_fn_name());
ForbiddenTracedFileIssue::new(parent.ident().await?.path.clone(), source, origin_fn_name)
.to_resolved()
.await?
.emit();
}
Ok(Vc::cell(traced_modules.into_iter().collect()))
@@ -439,6 +441,9 @@ pub async fn traced_module_data_for_graph(
struct ForbiddenTracedFileIssue {
parent: FileSystemPath,
issue_source: Option<IssueSource>,
/// The dynamic function whose access triggered the trace (e.g.
/// `fs.readFileSync`), used to name the offending call in the message.
origin_fn_name: Option<RcStr>,
}
#[turbo_tasks::value_impl]
@@ -447,10 +452,12 @@ impl ForbiddenTracedFileIssue {
pub async fn new(
parent: FileSystemPath,
issue_source: Option<IssueSource>,
origin_fn_name: Option<RcStr>,
) -> Result<Vc<Self>> {
Ok(Self {
parent,
issue_source,
origin_fn_name,
}
.cell())
}
@@ -499,17 +506,23 @@ impl Issue for ForbiddenTracedFileIssue {
StyledString::Text(rcstr!("To resolve this, you can")),
StyledString::Line(vec![
StyledString::Text(rcstr!(
"- make sure they are statically scoped to some subfolder: "
"- make sure the path is statically scoped to some subfolder, for example "
)),
StyledString::Code(rcstr!("path.join(process.cwd(), 'data', bar)")),
StyledString::Text(rcstr!(", or")),
]),
StyledString::Text(rcstr!("- only use them in development, or")),
StyledString::Line(vec![
StyledString::Text(rcstr!("- add ignore comments: ")),
StyledString::Code(rcstr!(
"path.join(/*turbopackIgnore: true*/ process.cwd(), bar)"
StyledString::Text(rcstr!(
"- opt out by adding an ignore comment to the highlighted call: "
)),
StyledString::Code(
format!(
"{fn_name}(/*turbopackIgnore: true*/ ...)",
fn_name = self.origin_fn_name.as_deref().unwrap_or("someFsOperation")
)
.into(),
),
StyledString::Text(rcstr!(", or")),
]),
StyledString::Text(rcstr!("- remove them.")),
@@ -1,6 +1,10 @@
import joinCwd from './join-cwd'
import { readBareArg, readJoinedArg } from './read-ignored'
export default function Page() {
joinCwd('index.test.ts')
// These use `turbopackIgnore` and must NOT produce a tracing warning.
readBareArg('index.test.ts')
readJoinedArg('index.test.ts')
return <h1>My Page</h1>
}
@@ -0,0 +1,18 @@
import fs from 'fs'
import path from 'path'
// `turbopackIgnore` on the first argument of the fs call silences the
// dynamic-fs tracing warning. This is the placement the warning recommends.
export function readBareArg(dynamicPath) {
return fs.readFileSync(/* turbopackIgnore: true */ dynamicPath, 'utf8')
}
// The comment also works when placed on a nested `path.join(...)` argument of
// the fs call (issue #95125): ignoring the `path.join` bubbles up and silences
// the enclosing `fs.readFileSync` whole-project tracing warning too.
export function readJoinedArg(f) {
return fs.readFileSync(
path.join(/* turbopackIgnore: true */ process.cwd(), f),
'utf8'
)
}
@@ -30,31 +30,43 @@ import stripAnsi from 'strip-ansi'
)
.trim()
expect(output).toMatchInlineSnapshot(`
"Turbopack build encountered 1 warning:
./app/join-cwd.js:4:10
Warning: Dynamic filesystem access causes tracing of the whole project
2 |
3 | export default function (f) {
> 4 | return path.join(process.cwd(), f)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
5 | }
6 |
expect(stripAnsi(output)).toMatchInlineSnapshot(`
"Turbopack build encountered 1 warning:
./app/join-cwd.js:4:10
Warning: Dynamic filesystem access causes tracing of the whole project
2 |
3 | export default function (f) {
> 4 | return path.join(process.cwd(), f)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
5 | }
6 |
Static analysis determined that this filesystem access causes the whole project to be traced and included in the output.
This is usually unintentional and leads to all source files (including the public folder) to be deployed as part of the server code.
This can slow down deployments or lead to failures when size limits are exceeded.
To resolve this, you can
- make sure they are statically scoped to some subfolder: path.join(process.cwd(), 'data', bar), or
- only use them in development, or
- add ignore comments: path.join(/*turbopackIgnore: true*/ process.cwd(), bar), or
- remove them.
Static analysis determined that this filesystem access causes the whole project to be traced and included in the output.
This is usually unintentional and leads to all source files (including the public folder) to be deployed as part of the server code.
This can slow down deployments or lead to failures when size limits are exceeded.
To resolve this, you can
- make sure the path is statically scoped to some subfolder, for example path.join(process.cwd(), 'data', bar), or
- only use them in development, or
- opt out by adding an ignore comment to the highlighted call: path.join(/*turbopackIgnore: true*/ ...), or
- remove them.
Import trace:
Server Component:
./app/join-cwd.js
./app/page.js"
`)
Import trace:
Server Component:
./app/join-cwd.js
./app/page.js"
`)
})
it('should not warn for fs calls annotated with turbopackIgnore', async () => {
// The build above (in the previous test) already ran; assert that none of
// the `turbopackIgnore`-annotated accesses in ./app/read-ignored.js produced
// a warning. This covers both the comment on the fs call's own argument
// (`readBareArg`) and the comment on a nested `path.join(...)` argument
// (`readJoinedArg`, issue #95125). Only ./app/join-cwd.js should warn.
expect(next.cliOutput).toContain(
'Turbopack build encountered 1 warning:'
)
expect(next.cliOutput).not.toContain('./app/read-ignored.js')
})
})
@@ -3,7 +3,7 @@
#![allow(clippy::needless_return)] // tokio macro-generated code doesn't respect this
use anyhow::Result;
use turbo_tasks::{ReadRef, ResolvedVc, Vc};
use turbo_tasks::{ReadRef, ResolvedVc, TraitRef, Vc};
use turbo_tasks_testing::{Registration, register, run_once};
static REGISTRATION: Registration = register!();
@@ -130,6 +130,21 @@ async fn test_sidecast() -> Result<()> {
.await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_trait_ref_downcast() -> Result<()> {
run_once(&REGISTRATION, || async {
let as_base: Vc<Box<dyn BaseTrait>> = Vc::upcast(ImplementsSubImplemented.cell());
let ref_base = as_base.into_trait_ref().await?;
// The concrete value implements SubImplemented, so downcasting the already-read
// trait ref to it succeeds without another cell read.
assert!(TraitRef::try_downcast::<Box<dyn SubImplemented>>(ref_base.clone()).is_some());
// It does not implement SubNotImplemented, so the downcast returns None.
assert!(TraitRef::try_downcast::<Box<dyn SubNotImplemented>>(ref_base).is_none());
Ok(())
})
.await
}
#[turbo_tasks::value_trait]
trait TraitA {}
@@ -147,3 +162,22 @@ impl TraitA for ImplementsAAndB {}
#[turbo_tasks::value_impl]
impl TraitB for ImplementsAAndB {}
#[turbo_tasks::value_trait]
trait BaseTrait {}
// Two sub-traits of `BaseTrait`; the test's value implements only the first.
#[turbo_tasks::value_trait]
trait SubImplemented: BaseTrait {}
#[turbo_tasks::value_trait]
trait SubNotImplemented: BaseTrait {}
#[turbo_tasks::value]
struct ImplementsSubImplemented;
#[turbo_tasks::value_impl]
impl BaseTrait for ImplementsSubImplemented {}
#[turbo_tasks::value_impl]
impl SubImplemented for ImplementsSubImplemented {}
@@ -2,6 +2,7 @@ use std::{fmt::Debug, marker::PhantomData};
use crate::{
Vc, VcValueTrait, registry::get_value_type, task::shared_reference::TypedSharedReference,
vc::UpcastStrict,
};
/// Similar to a [`ReadRef<T>`][crate::ReadRef], but contains a value trait object instead.
@@ -111,4 +112,18 @@ where
let value_type = get_value_type(shared_reference.type_id);
(value_type.raw_cell)(shared_reference).into()
}
/// Attempts to downcast this trait reference to a sub-trait `K`, where `K`
/// is of the form `Box<dyn L>` and `L: T` is a value trait.
///
/// [`ResolvedVc::try_downcast`]: crate::ResolvedVc::try_downcast
/// [`ResolvedVc`]: crate::ResolvedVc
pub fn try_downcast<K>(this: TraitRef<T>) -> Option<TraitRef<K>>
where
K: UpcastStrict<T> + VcValueTrait + ?Sized,
{
get_value_type(this.shared_reference.type_id)
.has_trait(&<K as VcValueTrait>::get_trait_type_id())
.then(|| TraitRef::new(this.shared_reference))
}
}
@@ -46,6 +46,17 @@ pub trait ModuleReference: ValueToString {
}
}
/// A [`ModuleReference`] created by tracing a dynamic filesystem access (e.g.
/// `fs.readFileSync`, `path.join`) for the purpose of Node File Tracing.
#[turbo_tasks::value_trait]
pub trait DynamicTraceReference: ModuleReference {
/// The name of the function/call that created this reference (e.g.
/// `fs.readFileSync`). Used to name the offending call in tracing
/// diagnostics so the suggested fix refers to the actual call rather than an
/// example.
fn origin_fn_name(&self) -> RcStr;
}
/// Multiple [ModuleReference]s
#[turbo_tasks::value(transparent)]
pub struct ModuleReferences(Vec<ResolvedVc<Box<dyn ModuleReference>>>);
@@ -4,7 +4,7 @@ use anyhow::{Ok, Result};
use rustc_hash::FxHashSet;
use swc_core::{
base::try_with_handler,
common::{GLOBALS, Mark, SourceMap, SyntaxContext, comments::Comments, sync::Lrc},
common::{GLOBALS, Mark, SourceMap, Spanned, SyntaxContext, comments::Comments, sync::Lrc},
ecma::{ast::*, atoms::atom},
};
use turbo_rcstr::{RcStr, rcstr};
@@ -142,6 +142,23 @@ impl EvalContext {
}
pub fn eval<'a>(&self, arena: &'a Bump, e: &Expr) -> JsValue<'a> {
let value = self.eval_inner(arena, e);
// A `turbopackIgnore` comment on this expression opts it out of static
// analysis. Downgrade it to an unknown so the opt-out lives on the value
// itself and bubbles up to any consumer (e.g. an enclosing
// `fs.readFileSync(...)`, or through a variable). A dynamic (unknown) path
// isn't rooted at the project directory, so tracing skips it instead of
// pulling in the whole project. The attribute is keyed to the annotated
// call's callee position, so only that exact expression matches — nested
// subexpressions are unaffected.
if self.imports.get_attributes(e.span()).ignore {
JsValue::unknown(value, true, rcstr!("turbopackIgnore"))
} else {
value
}
}
fn eval_inner<'a>(&self, arena: &'a Bump, e: &Expr) -> JsValue<'a> {
debug_assert!(
GLOBALS.is_set(),
"Eval requires globals from its parsed result"
@@ -2344,6 +2344,7 @@ where
Pattern::new(pat),
collect_affecting_sources,
get_issue_source(),
format!("fs.{name}").into(),
)
.to_resolved()
.await?,
@@ -2379,6 +2380,7 @@ where
get_traced_project_dir().await?,
Pattern::new(pat),
get_issue_source(),
rcstr!("fs.readdir"),
)
.to_resolved()
.await?,
@@ -2435,6 +2437,7 @@ where
get_traced_project_dir().await?,
Pattern::new(pat),
get_issue_source(),
rcstr!("path.resolve"),
)
.to_resolved()
.await?,
@@ -2486,6 +2489,7 @@ where
get_traced_project_dir().await?,
Pattern::new(pat),
get_issue_source(),
rcstr!("path.join"),
)
.to_resolved()
.await?,
@@ -2552,6 +2556,7 @@ where
span.lo.to_u32(),
span.hi.to_u32(),
),
format!("child_process.{name}").into(),
)
.to_resolved()
.await?,
@@ -2788,6 +2793,7 @@ where
get_traced_project_dir().await?,
Pattern::new(abs_pattern),
get_issue_source(),
rcstr!("express().set"),
)
.to_resolved()
.await?,
@@ -2859,6 +2865,7 @@ where
get_traced_project_dir().await?,
Pattern::new(abs_pattern),
get_issue_source(),
rcstr!("strong-globalize.SetRootDir"),
)
.to_resolved()
.await?,
@@ -2929,6 +2936,7 @@ where
context_dir.clone(),
Pattern::new(Pattern::Constant(dir.into())),
get_issue_source(),
rcstr!("protobufjs.load"),
)
.to_resolved()
})
@@ -1,6 +1,6 @@
use anyhow::{Result, bail};
use tracing::Instrument;
use turbo_rcstr::rcstr;
use turbo_rcstr::{RcStr, rcstr};
use turbo_tasks::{ResolvedVc, ValueToString, Vc};
use turbo_tasks_fs::FileSystemPath;
use turbopack_core::{
@@ -8,7 +8,7 @@ use turbopack_core::{
file_source::FileSource,
issue::IssueSource,
raw_module::RawModule,
reference::ModuleReference,
reference::{DynamicTraceReference, ModuleReference},
resolve::{
ModuleResolveResult, RequestKey,
pattern::{Pattern, PatternMatch, read_matches},
@@ -26,6 +26,9 @@ pub struct FileSourceReference {
path: ResolvedVc<Pattern>,
collect_affecting_sources: bool,
issue_source: IssueSource,
/// The dynamic function whose access triggered this reference (e.g.
/// `fs.readFileSync`), used to name the call in diagnostics.
origin_fn_name: RcStr,
}
#[turbo_tasks::value_impl]
@@ -36,12 +39,14 @@ impl FileSourceReference {
path: ResolvedVc<Pattern>,
collect_affecting_sources: bool,
issue_source: IssueSource,
origin_fn_name: RcStr,
) -> Vc<Self> {
Self::cell(FileSourceReference {
context_dir,
path,
collect_affecting_sources,
issue_source,
origin_fn_name,
})
}
}
@@ -89,6 +94,13 @@ impl ModuleReference for FileSourceReference {
}
}
#[turbo_tasks::value_impl]
impl DynamicTraceReference for FileSourceReference {
fn origin_fn_name(&self) -> RcStr {
self.origin_fn_name.clone()
}
}
#[turbo_tasks::value]
#[derive(Hash, Debug, ValueToString)]
#[value_to_string("directory assets {path}")]
@@ -96,6 +108,9 @@ pub struct DirAssetReference {
context_dir: FileSystemPath,
path: ResolvedVc<Pattern>,
issue_source: IssueSource,
/// The dynamic function whose access triggered this reference (e.g.
/// `fs.readdir`), used to name the call in diagnostics.
origin_fn_name: RcStr,
}
#[turbo_tasks::value_impl]
@@ -105,11 +120,13 @@ impl DirAssetReference {
context_dir: FileSystemPath,
path: ResolvedVc<Pattern>,
issue_source: IssueSource,
origin_fn_name: RcStr,
) -> Vc<Self> {
Self::cell(DirAssetReference {
context_dir,
path,
issue_source,
origin_fn_name,
})
}
}
@@ -222,3 +239,10 @@ impl ModuleReference for DirAssetReference {
Some(self.issue_source)
}
}
#[turbo_tasks::value_impl]
impl DynamicTraceReference for DirAssetReference {
fn origin_fn_name(&self) -> RcStr {
self.origin_fn_name.clone()
}
}
@@ -331,9 +331,15 @@
span: 245..252,
},
Call {
func: FreeVar(
"require",
),
func: Unknown {
original_value: Some(
FreeVar(
"require",
),
),
reason: "turbopackIgnore",
has_side_effects: true,
},
args: [
Value(
Constant(
@@ -515,9 +521,15 @@
span: 356..363,
},
Call {
func: FreeVar(
"require",
),
func: Unknown {
original_value: Some(
FreeVar(
"require",
),
),
reason: "turbopackIgnore",
has_side_effects: true,
},
args: [
Value(
Constant(
@@ -702,9 +714,15 @@
span: 488..495,
},
Call {
func: FreeVar(
"require",
),
func: Unknown {
original_value: Some(
FreeVar(
"require",
),
),
reason: "turbopackIgnore",
has_side_effects: true,
},
args: [
Value(
Constant(
@@ -1,14 +1,33 @@
exported_t_r (const after eval) = FreeVar(require)("exported_t_r")
exported_t_r (const after eval) = ???*0*
- *0* ???*1*("exported_t_r")
⚠️ turbopackIgnore
⚠️ This value might have side effects
- *1* FreeVar(require)
⚠️ turbopackIgnore
⚠️ This value might have side effects
t_i (const after eval) = await(FreeVar(import)("t_i"))
t_i (const after eval) = await(???*0*)
- *0* FreeVar(import)("t_i")
⚠️ turbopackIgnore
⚠️ This value might have side effects
t_r (const after eval) = (
| FreeVar(require)("w_i_f")
| FreeVar(require)("t_i_f")
| FreeVar(require)("t_r_t")
| FreeVar(require)("t_r_f")
)
t_r (const after eval) = (FreeVar(require)("w_i_f") | FreeVar(require)("t_i_f") | ???*0* | FreeVar(require)("t_r_f"))
- *0* ???*1*("t_r_t")
⚠️ turbopackIgnore
⚠️ This value might have side effects
- *1* FreeVar(require)
⚠️ turbopackIgnore
⚠️ This value might have side effects
w_i (const after eval) = await(FreeVar(import)("w_i"))
w_i (const after eval) = await(???*0*)
- *0* FreeVar(import)("w_i")
⚠️ turbopackIgnore
⚠️ This value might have side effects
w_r (const after eval) = (FreeVar(require)("w_r_t") | FreeVar(require)("w_r_f"))
w_r (const after eval) = (???*0* | FreeVar(require)("w_r_f"))
- *0* ???*1*("w_r_t")
⚠️ turbopackIgnore
⚠️ This value might have side effects
- *1* FreeVar(require)
⚠️ turbopackIgnore
⚠️ This value might have side effects
@@ -1,47 +1,65 @@
[
(
"exported_t_r",
Call(
3,
FreeVar(
"require",
),
[
Constant(
Str(
Atom(
"exported_t_r",
Unknown {
original_value: Some(
Call(
3,
Unknown {
original_value: Some(
FreeVar(
"require",
),
),
),
reason: "turbopackIgnore",
has_side_effects: true,
},
[
Constant(
Str(
Atom(
"exported_t_r",
),
),
),
],
),
],
),
),
reason: "turbopackIgnore",
has_side_effects: true,
},
),
(
"t_i",
Awaited(
4,
Call(
3,
FreeVar(
"import",
),
[
Constant(
Str(
Atom(
"t_i",
),
2,
Unknown {
original_value: Some(
Call(
3,
FreeVar(
"import",
),
[
Constant(
Str(
Atom(
"t_i",
),
),
),
],
),
],
),
),
reason: "turbopackIgnore",
has_side_effects: true,
},
),
),
(
"t_r",
Alternatives {
total_nodes: 13,
total_nodes: 11,
values: [
Call(
3,
@@ -73,21 +91,33 @@
),
],
),
Call(
3,
FreeVar(
"require",
),
[
Constant(
Str(
Atom(
"t_r_t",
Unknown {
original_value: Some(
Call(
3,
Unknown {
original_value: Some(
FreeVar(
"require",
),
),
),
reason: "turbopackIgnore",
has_side_effects: true,
},
[
Constant(
Str(
Atom(
"t_r_t",
),
),
),
],
),
],
),
),
reason: "turbopackIgnore",
has_side_effects: true,
},
Call(
3,
FreeVar(
@@ -110,44 +140,62 @@
(
"w_i",
Awaited(
4,
Call(
3,
FreeVar(
"import",
),
[
Constant(
Str(
Atom(
"w_i",
),
2,
Unknown {
original_value: Some(
Call(
3,
FreeVar(
"import",
),
[
Constant(
Str(
Atom(
"w_i",
),
),
),
],
),
],
),
),
reason: "turbopackIgnore",
has_side_effects: true,
},
),
),
(
"w_r",
Alternatives {
total_nodes: 7,
total_nodes: 5,
values: [
Call(
3,
FreeVar(
"require",
),
[
Constant(
Str(
Atom(
"w_r_t",
Unknown {
original_value: Some(
Call(
3,
Unknown {
original_value: Some(
FreeVar(
"require",
),
),
),
reason: "turbopackIgnore",
has_side_effects: true,
},
[
Constant(
Str(
Atom(
"w_r_t",
),
),
),
],
),
],
),
),
reason: "turbopackIgnore",
has_side_effects: true,
},
Call(
3,
FreeVar(
@@ -15,10 +15,9 @@
0 -> 7 free var = FreeVar(require)
0 -> 8 call = ???*0*("w_r_t")
- *0* require*1*
⚠️ ignored require
- *0* FreeVar(require)
⚠️ turbopackIgnore
⚠️ This value might have side effects
- *1* require: The require method from CommonJS
0 -> 9 free var = FreeVar(require)
@@ -28,10 +27,9 @@
0 -> 11 free var = FreeVar(require)
0 -> 12 call = ???*0*("t_r_t")
- *0* require*1*
⚠️ ignored require
- *0* FreeVar(require)
⚠️ turbopackIgnore
⚠️ This value might have side effects
- *1* require: The require method from CommonJS
0 -> 13 free var = FreeVar(require)
@@ -41,7 +39,6 @@
0 -> 15 free var = FreeVar(require)
0 -> 16 call = ???*0*("exported_t_r")
- *0* require*1*
⚠️ ignored require
- *0* FreeVar(require)
⚠️ turbopackIgnore
⚠️ This value might have side effects
- *1* require: The require method from CommonJS
@@ -1,9 +1,33 @@
exported_t_r = module<exported_t_r, {}>
exported_t_r = ???*0*
- *0* ???*1*("exported_t_r")
⚠️ turbopackIgnore
⚠️ This value might have side effects
- *1* FreeVar(require)
⚠️ turbopackIgnore
⚠️ This value might have side effects
t_i = module<t_i, {}>
t_i = ???*0*
- *0* FreeVar(import)("t_i")
⚠️ turbopackIgnore
⚠️ This value might have side effects
t_r = (module<w_i_f, {}> | module<t_i_f, {}> | module<t_r_t, {}> | module<t_r_f, {}>)
t_r = (module<w_i_f, {}> | module<t_i_f, {}> | ???*0* | module<t_r_f, {}>)
- *0* ???*1*("t_r_t")
⚠️ turbopackIgnore
⚠️ This value might have side effects
- *1* FreeVar(require)
⚠️ turbopackIgnore
⚠️ This value might have side effects
w_i = module<w_i, {}>
w_i = ???*0*
- *0* FreeVar(import)("w_i")
⚠️ turbopackIgnore
⚠️ This value might have side effects
w_r = (module<w_r_t, {}> | module<w_r_f, {}>)
w_r = (???*0* | module<w_r_f, {}>)
- *0* ???*1*("w_r_t")
⚠️ turbopackIgnore
⚠️ This value might have side effects
- *1* FreeVar(require)
⚠️ turbopackIgnore
⚠️ This value might have side effects