Repository: vercel/next.js
Pull request: [turbopack] Followup for `#[derive(ValueToString)]`

## What

Follow-up to #89788, addressing review feedback from @lukesandberg.

## Why

Several small issues were flagged during review that didn't block merge but should be cleaned up.

## How

- **Remove `is_pure_constant`**: Instead of checking whether a format string contains braces, unescape `{{`/`}}` at macro expansion time so `rcstr!` works for all constant format strings (no runtime `format!` needed)
- **`require_context.rs`**: Replace awkward inline `if` expression in `#[value_to_string(...)]` attribute with a `Display` impl
- **`webpack/mod.rs`**: Make `WebpackChunkAssetReference::chunk_id()` non-pub since it's only used internally

Changed files:
filename: turbopack/crates/turbo-tasks-macros/src/derive/value_to_string_macro.rs
patch: @@ -173,11 +173,6 @@ fn parse_attr(attr: &Attribute) -> syn::Result<AttrForm> {
     }
 }
 
-/// Returns true if the format string has no `{` or `}` and can use `rcstr!` directly.
-fn is_pure_constant(fmt: &str) -> bool {
-    !fmt.contains('{') && !fmt.contains('}')
-}
-
 /// If `fmt` is exactly `{field_name}` (single field, no format specifier, no surrounding text),
 /// returns a `self.field_name` expression. This lets us skip `format!` entirely and delegate
 /// directly to `ValueToStringify::to_stringify`.
@@ -287,12 +282,12 @@ fn struct_format_auto_fields_body(fmt: &str) -> (bool, TokenStream2) {
     let (transformed_fmt, field_refs) = parse_format_fields(fmt);
 
     if field_refs.is_empty() {
-        let value_expr = if is_pure_constant(fmt) {
-            quote! { turbo_rcstr::rcstr!(#transformed_fmt) }
-        } else {
-            quote! { turbo_rcstr::RcStr::from(format!(#transformed_fmt)) }
-        };
-        return (false, quote! { turbo_tasks::Vc::cell(#value_expr) });
+        // No fields to resolve — unescape `{{`/`}}` at compile time and use rcstr!
+        let unescaped = transformed_fmt.replace("{{", "{").replace("}}", "}");
+        return (
+            false,
+            quote! { turbo_tasks::Vc::cell(turbo_rcstr::rcstr!(#unescaped)) },
+        );
     }
 
     let resolves: Vec<TokenStream2> = field_refs
@@ -417,8 +412,9 @@ fn generate_enum_format_auto_fields(
         *needs_async = true;
     }
 
-    let value_expr = if field_refs.is_empty() && is_pure_constant(fmt) {
-        quote! { turbo_rcstr::rcstr!(#transformed_fmt) }
+    let value_expr = if field_refs.is_empty() {
+        let unescaped = transformed_fmt.replace("{{", "{").replace("}}", "}");
+        quote! { turbo_rcstr::rcstr!(#unescaped) }
     } else {
         quote! { turbo_rcstr::RcStr::from(format!(#transformed_fmt)) }
     };

filename: turbopack/crates/turbopack-ecmascript/src/references/require_context.rs
patch: @@ -228,7 +228,6 @@ impl RequireContextMap {
 /// wrapped in `__turbopack_module_context__`;
 #[turbo_tasks::value]
 #[derive(Hash, Debug, ValueToString)]
-#[value_to_string("require.context {}/{}", self.dir, {if self.include_subdirs { "**" } else { "*" }})]
 pub struct RequireContextAssetReference {
     pub inner: ResolvedVc<RequireContextAsset>,
     pub dir: RcStr,
@@ -238,6 +237,17 @@ pub struct RequireContextAssetReference {
     pub error_mode: ResolveErrorMode,
 }
 
+impl std::fmt::Display for RequireContextAssetReference {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "require.context {}/{}",
+            self.dir,
+            if self.include_subdirs { "**" } else { "*" },
+        )
+    }
+}
+
 impl RequireContextAssetReference {
     pub async fn new(
         source: ResolvedVc<Box<dyn Source>>,

filename: turbopack/crates/turbopack-ecmascript/src/webpack/mod.rs
patch: @@ -80,7 +80,7 @@ pub struct WebpackChunkAssetReference {
 }
 
 impl WebpackChunkAssetReference {
-    pub fn chunk_id(&self) -> RcStr {
+    fn chunk_id(&self) -> RcStr {
         match &self.chunk_id {
             Lit::Str(s) => RcStr::from(s.value.to_string_lossy().to_string()),
             Lit::Num(n) => RcStr::from(n.to_string()),