Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
aa80366
refactor(storybook): upgrade from Storybook 8.6 to 9.1
pettinarip Mar 30, 2026
a7a216c
refactor(storybook): upgrade from Storybook 9.1 to 10.3
pettinarip Mar 30, 2026
62be248
refactor: switch to Turbopack as default bundler
pettinarip Mar 31, 2026
e074b37
fix: remove ineffective turbopackIgnore comments from non-import calls
pettinarip Apr 1, 2026
b8af051
fix: replace dynamic import() with fs.readFile() for tutorial markdown
pettinarip Apr 1, 2026
4494f81
fix: silence sentry debug logging and remove deprecated disableLogger
pettinarip Apr 1, 2026
5f1aae5
fix: inline path constants and suppress turbopack file-tracing warnings
pettinarip Apr 1, 2026
0ba90b1
refactor: server-render contributors and staking, drop all-contributo…
pettinarip Apr 24, 2026
ebcd109
Merge remote-tracking branch 'origin/dev' into refactor/turbopack-ado…
pettinarip Apr 24, 2026
f2ab413
fix(storybook): remove vite added by sb10 automigration
pettinarip Apr 24, 2026
bed7aea
fix: address review feedback on contributors/sentry/turbopack config
pettinarip Apr 24, 2026
55a915e
fix: restore class ordering in files untouched by this branch
pettinarip Apr 24, 2026
6862af1
chore: trim redundant comments and add turbopack file-tracer solution…
pettinarip Apr 27, 2026
4c35300
fix(turbopack): broaden ignoreIssue path for nft list warning
pettinarip Apr 27, 2026
6ef1a94
Merge remote-tracking branch 'origin/dev' into refactor/turbopack-ado…
pettinarip Apr 27, 2026
f94cdf6
Merge remote-tracking branch 'origin/dev' into HEAD
pettinarip Apr 28, 2026
00ed4ad
Address PR review feedback (#17906)
pettinarip Apr 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions app/[locale]/staking/_components/staking.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"use client"

import { type HTMLAttributes, ReactNode } from "react"
import { getTranslations } from "next-intl/server"

import type {
ChildOnlyProp,
Expand Down Expand Up @@ -32,7 +31,6 @@ import { ListItem, UnorderedList } from "@/components/ui/list"

import { cn } from "@/lib/utils/cn"

import useTranslation from "@/hooks/useTranslation"
import rhino from "@/public/images/upgrades/upgrade_rhino.png"

type BenefitsType = {
Expand Down Expand Up @@ -109,12 +107,12 @@ type Props = PageWithContributorsProps & {
data: StakingStatsData
}

const StakingPage = ({
const StakingPage = async ({
data,
contributors,
lastEditLocaleTimestamp,
}: Props) => {
const { t } = useTranslation("page-staking")
const t = await getTranslations("page-staking")

const heroContent = {
title: t("page-staking-hero-title"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
title: "Turbopack File Tracer Doesn't Resolve String Constants Imported from Other Modules"
slug: "turbopack-file-tracer-doesnt-fold-cross-module-string-constants"
category: "build-errors"
severity: "low"
symptoms:
- "Turbopack build emits 'Overly broad patterns' warnings pointing at path.join() calls in the MDX pipeline"
- "Warning pattern drops the literal path prefix and widens to mostly <dynamic> when the prefix comes from an imported constant"
- "Pattern match count jumps ~4x (e.g. 28k → 110k files) compared to the same join() with an inlined literal"
components:
- "src/lib/md/compile.ts"
- "src/lib/utils/md.ts"
- "src/lib/i18n/translationRegistry.ts"
- "next.config.js (turbopack.ignoreIssue)"
tags:
- "turbopack"
- "next.js"
- "file-tracing"
- "nft"
- "mdx"
- "output-file-tracing"
resolved_at: "2026-04-01"
pr: "#17906"
---

# Turbopack File Tracer Doesn't Resolve String Constants Imported from Other Modules

## Problem

Turbopack's file tracer emits "Overly broad patterns can lead to build performance issues and over bundling" warnings on `path.join()` calls that mix a module-level constant with dynamic arguments:

```ts
// src/lib/md/compile.ts
import { CONTENT_DIR, CONTENT_PATH } from "../constants"
// ...
const mdPath = join(CONTENT_PATH, ...slugArray)
const mdDir = join(CONTENT_DIR, ...slugArray)
```

The constants are plain string literals (`CONTENT_DIR = "public/content"`) defined in `src/lib/constants.ts`. Logically the tracer should treat the call as equivalent to `join("public/content", ...slugArray)`, but it does not.

## Root Cause

Next's file-tracing pass (the analyzer behind `outputFileTracing`) uses a local per-file evaluator. It can resolve literals that appear **in the same file** as the `join()` call, but it does **not** follow imports to resolve literals declared in another module. When it sees an imported binding, it treats that argument as unknown — a fully dynamic segment.

Measured in build logs with `ignoreIssue` temporarily disabled:

| Call site | With literal at call site | With imported constant |
|---|---:|---:|
| `src/lib/utils/md.ts:23` — `join(contentRoot, dir)` | 28,040 files matched | 110,708 files matched |
| `src/lib/i18n/translationRegistry.ts` — `join(TRANSLATIONS_DIR, locale, slug, "index.md")` | 25,044 files matched | 39,272 files matched |

Same warning **count** in both variants (the tracer still emits one warning per site), but the **scope** — the set of files the tracer considers in-scope for that site — is dramatically larger with imported constants. The literal prefix `"public/content"` dissolves into `<dynamic>` and the scope balloons to "anywhere under project root."

This is not a bundler-correctness issue. The main Turbopack bundler does fold cross-module literals. It's specifically the file-trace pass (based on Node File Trace) that has a narrower evaluator.

## Resolution

Two changes, both in PR #17906, commit `5f1aae57`:

1. **Inline the literal at the call site** in files that feed `path.join(...)` into `fs`/MDX reads with dynamic suffixes:
- `src/lib/md/compile.ts` — inline `"/content"` and `"public/content"` instead of importing `CONTENT_PATH`/`CONTENT_DIR`
- `src/lib/utils/md.ts` — inline `"public/content"` in `getContentRoot()` and in `getTutorialsData()`
- `src/lib/i18n/translationRegistry.ts` — inline `"public/content/translations"` instead of importing `TRANSLATIONS_DIR`

2. **Suppress the residual warnings** in `next.config.js`:

```js
turbopack: {
ignoreIssue: [
{ path: "**/src/lib/**", description: /Overly broad patterns/ },
// "Encountered unexpected file in NFT list" attaches to the project
// root (e.g. `./next.config.js`) — not to the src/lib file that
// contains the dynamic fs call — so this rule must match anywhere.
{ path: "**", title: /Encountered unexpected file/ },
],
}
```

`outputFileTracingExcludes` already prevents these patterns from over-bundling, so the warnings are cosmetic — but narrowing the pattern first (step 1) keeps the tracer's actual walked set small.

## Why not just use `ignoreIssue` alone?

Both steps are needed. `ignoreIssue` only silences the log output — it doesn't change what the tracer walks. Inlining narrows the tracer's scope to the correct subtree (`public/content/**`) instead of the whole project tree. If `ignoreIssue` is ever removed or its matcher tightened, the inlined form is what keeps the warnings manageable.

## Don't revert the inlining

Reviewers may be tempted to replace the inlined `"public/content"` strings with imports of `CONTENT_DIR` for consistency with `videos.ts`, `editPath.ts`, `contributors.ts`, and `fetchGitHubContributors.ts` (which all import the constant). That works in those files because they combine the constant with a **static suffix** — no dynamic spread — so the tracer never widens anyway:

```ts
// fine — static suffix, no dynamic spread
const videosDir = join(process.cwd(), CONTENT_DIR, "videos")
```

The rule:

- **Constant + fully-static suffix** → keep the constant. Tracer is happy.
- **Constant + dynamic spread feeding `fs`/MDX reads** → inline the literal. Otherwise the tracer loses the prefix.

## Related

- Ineffective `turbopackIgnore` magic comments: the comment only works on `import()` / `require()` calls, not on `path.join()` / `fs.readFile()` / `existsSync()`. Removed from `src/lib/md/rehypeImg.ts` in PR #17906, commit `e074b37c`.
- Dynamic `import()` on templated paths was replaced with `fsp.readFile()` for a different reason — the tracer statically matches the template against all files, causing `pnpm dev` to watch tens of thousands of markdown files. See PR #17906, commit `b8af0517`.

## References

- PR #17906 ([refactor: adopt Turbopack as default bundler](https://github.com/ethereum/ethereum-org-website/pull/17906))
- Commit `5f1aae57` (inline path constants and suppress turbopack file-tracing warnings)
- [Next.js outputFileTracing](https://nextjs.org/docs/app/api-reference/config/next-config-js/output)
- [`@vercel/nft`](https://github.com/vercel/nft) — the underlying file tracer
33 changes: 17 additions & 16 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const { PHASE_DEVELOPMENT_SERVER } = require("next/constants")

const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
})

const createNextIntlPlugin = require("next-intl/plugin")

const { withSentryConfig } = require("@sentry/nextjs")
Expand Down Expand Up @@ -48,12 +44,6 @@ module.exports = (phase) => {
"https://ethereum.org",
},
webpack: (config) => {
// Parse .all-contributorsrc as JSON (no .json extension)
config.module.rules.push({
test: /\.all-contributorsrc$/,
type: "json",
})

config.module.rules.push({
test: /\.ya?ml$/,
use: "yaml-loader",
Expand Down Expand Up @@ -111,6 +101,22 @@ module.exports = (phase) => {
"*.md": { loaders: ["raw-loader"], as: "*.js" },
"*.mp3": { as: "*.static" },
},
// Suppress file-tracing warnings from the MDX pipeline. These files
// use dynamic path.join/readFile to read markdown content at runtime.
// outputFileTracingExcludes already prevents over-bundling.
ignoreIssue: [
{
path: "**/src/lib/**",
description: /Overly broad patterns/,
},
// "Encountered unexpected file in NFT list" surfaces on the project
// root (e.g. `./next.config.js`) even though the underlying fs.*
// calls live in src/lib/md/*. Match anywhere so it's suppressed.
{
path: "**",
title: /Encountered unexpected file/,
},
],
},
// Replaces config.externals.push("pino-pretty", "lokijs", "encoding")
serverExternalPackages: ["pino-pretty", "lokijs", "encoding"],
Expand Down Expand Up @@ -253,17 +259,12 @@ module.exports = (phase) => {
}
}

return withBundleAnalyzer(withNextIntl(nextConfig))
return withNextIntl(nextConfig)
}

module.exports = withSentryConfig(module.exports, {
org: "ethereumorg-ow",
project: "ethorg",
silent: true,
widenClientFileUpload: true,
webpack: {
treeshake: {
removeDebugLogging: true,
},
},
})
13 changes: 0 additions & 13 deletions overrides.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,3 @@ declare module "*.mp4" {
const src: string
export default src
}

declare module "*/.all-contributorsrc" {
const content: {
contributors: Array<{
login: string
name: string
avatar_url: string
profile?: string
contributions: Array<string>
}>
}
export default content
}
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"license": "MIT",
"private": true,
"scripts": {
"dev": "next dev --webpack",
"dev:turbo": "next dev",
"build": "next build --webpack",
"build:turbo": "next build",
"dev": "next dev",
"dev:webpack": "next dev --webpack",
"build": "next build",
"build:webpack": "next build --webpack",
"start": "next start",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
Expand Down Expand Up @@ -111,7 +111,6 @@
"@chromatic-com/storybook": "5.1.1",
"@google/genai": "^1.46.0",
"@netlify/plugin-nextjs": "^5.15.9",
"@next/bundle-analyzer": "^16.2.1",
"@playwright/test": "^1.52.0",
"@sentry/nextjs": "^10.46.0",
"@storybook/addon-docs": "10.3.3",
Expand Down
Loading
Loading