Skip to content

[turbopack] fix feature usage telemetry#93100

Merged
lukesandberg merged 5 commits into
canaryfrom
fix_telemetry
May 11, 2026
Merged

[turbopack] fix feature usage telemetry#93100
lukesandberg merged 5 commits into
canaryfrom
fix_telemetry

Conversation

@lukesandberg
Copy link
Copy Markdown
Contributor

@lukesandberg lukesandberg commented Apr 21, 2026

Report Turbopack feature-usage telemetry

Turbopack never reported NEXT_BUILD_FEATURE_USAGE telemetry for production builds. This PR wires it up and fixes a correctness bug in how the counts were computed, then cleans up the API surface that carried them across the napi boundary.

Changes

  • JS: turbopackBuild() now records EVENT_BUILD_FEATURE_USAGE events after writeAllEntrypointsToDisk via a new eventBuildFeatureUsageFromTurbopackDiagnostics helper. Dev is out of scope — webpack's TelemetryPlugin is !dev && isClient too.
  • Rust: aligned feature names with the JS EventBuildFeatureUsage['featureName'] union — SWC triple is now swc/target/<triple>; dropped persistentCaching (redundant with turbopackFileSystemCache) and turbotrace: false (hardcoded).

Correctness fix: count unique importers, not resolves

Previously feature-usage counts for module imports (next/image, next/font/google, …) were computed from a BeforeResolvePlugin that emitted one event per resolve. Turbopack caches resolves, so the emission fired at most once per unique request — the count was effectively 1 for every feature that was imported anywhere. Webpack's equivalent counts unique importing modules via moduleGraph.getIncomingConnections(module).size.

This PR replaces the resolve-plugin emission with a single whole-app module-graph traversal on Project. For each tracked feature, we accumulate the set of unique parent modules of each matching node (mirroring webpack's "unique origin modules" semantics). Fonts are matched against their synthesized /target.css?… virtual modules produced by the SWC font-loader transform — matching webpack's FEATURE_MODULE_REGEXP_MAP approach. Paths are matched via phf_map! tables in next_telemetry.rs.

Incidental simplifications

While in here, the Diagnostic collectibles subsystem got right-sized and then removed entirely, since feature usage was its only consumer:

  • Project::project_feature_usage() returns a structured Vc<ProjectFeatureUsageSummary> instead of emitting diagnostics. Surfaced to JS as a dedicated project.featureUsage(): Promise<BuildFeatureUsage[]> napi method, called once at build's end.
  • TurbopackResult<T> loses its diagnostics: BuildFeatureUsage[] field — it's now just { result, issues }. Every napi result type and ~10 construction sites are correspondingly simpler.
  • Deleted turbopack_core::diagnostics entirely (Diagnostic trait, DiagnosticExt, DiagnosticContextExt, CapturedDiagnostics, PlainBuildFeatureUsage). Deleted FeatureUsageTelemetry, ModuleFeatureReportResolvePlugin, get_diagnostics() aggregation, the feature_usage/diagnostics fields on AllWrittenEntrypointsWithIssues/OperationResult/EntrypointsWithIssues/WrittenEndpointWithIssues/HmrUpdateWithIssues/HmrChunkNamesWithIssues/EndpointIssuesAndDiags/WriteAnalyzeResult, and the defensive drop_collectibles::<Box<dyn Diagnostic>>() scrub in entrypoints_without_collectibles_operation.

Feature-usage telemetry now flows as a plain return value end-to-end: Project::project_feature_usage() → napi projectFeatureUsage() → JS project.featureUsage()telemetry.record(). No collectibles, no peeking, no emission-as-side-effect.

Tests

Un-skipped four previously webpack-only integration tests in test/integration/telemetry/test/config.test.ts: image/script/dynamic, next/legacy/image, transpilePackages, and middleware options. All pass under Turbopack. The remaining three skipped tests (swc flags, @vercel/og, useCache) cover features Turbopack doesn't emit yet — left skipped with TODOs.

Added unit test for the helper at packages/next/src/telemetry/events/build.test.ts. Updated the Turbopack next-rs-api snapshot to reflect the new diagnostic shape.

Copy link
Copy Markdown
Contributor Author

lukesandberg commented Apr 21, 2026

@lukesandberg lukesandberg force-pushed the more_ineffective_tasks branch 2 times, most recently from 80f135b to 905545d Compare April 21, 2026 18:54
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 21, 2026

Merging this PR will degrade performance by 3.4%

⚡ 1 improved benchmark
❌ 1 regressed benchmark
✅ 15 untouched benchmarks
⏩ 3 skipped benchmarks1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation packages-bundle.js[full] 951.7 ms 985.2 ms -3.4%
Simulation app-page-turbo.runtime.prod.js[full] 641.6 ms 619.1 ms +3.64%

Comparing fix_telemetry (06b101f) with more_ineffective_tasks (905545d)2

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on more_ineffective_tasks (e550aff) during the generation of this report, so 5d582a5 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@lukesandberg lukesandberg force-pushed the more_ineffective_tasks branch from 905545d to e550aff Compare April 22, 2026 01:17
@lukesandberg lukesandberg changed the title fix telemetry [turbopack] fix feature usage telemetry Apr 22, 2026
Comment thread crates/next-api/src/project.rs Outdated
Comment thread crates/next-api/src/project.rs
Comment thread crates/next-api/src/project.rs Outdated
Comment thread crates/next-api/src/project.rs Outdated
Comment thread crates/next-napi-bindings/src/next_api/project.rs Outdated
Comment thread packages/next/src/build/swc/types.ts Outdated
Comment thread packages/next/src/build/turbopack-build/impl.ts Outdated
Comment thread test/integration/telemetry/test/config.test.ts Outdated
Copy link
Copy Markdown
Contributor

@vercel vercel Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Suggestion:

Test file next-rs-api.test.ts references .diagnostics on TurbopackResult objects, but diagnostics was removed from TurbopackResult in commit 06b101f, causing all diagnostics assertions to fail at runtime (accessing undefined).

Fix on Vercel

@lukesandberg lukesandberg changed the base branch from more_ineffective_tasks to graphite-base/93100 April 25, 2026 01:21
@lukesandberg lukesandberg changed the base branch from graphite-base/93100 to more_ineffective_tasks April 25, 2026 19:00
@github-actions github-actions Bot added created-by: Turbopack team PRs by the Turbopack team. tests Turbopack Related to Turbopack with Next.js. type: next labels Apr 25, 2026
@lukesandberg lukesandberg force-pushed the more_ineffective_tasks branch from 157d145 to f579205 Compare April 25, 2026 19:01
Comment thread crates/next-api/src/project.rs Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 25, 2026

Tests Passed

Commit: edc1992

@lukesandberg lukesandberg force-pushed the fix_telemetry branch 2 times, most recently from 6d0776f to ab525f2 Compare April 26, 2026 18:17
@lukesandberg lukesandberg force-pushed the more_ineffective_tasks branch from 19ba862 to b45210d Compare April 26, 2026 18:17
@lukesandberg lukesandberg marked this pull request as ready for review April 26, 2026 18:18
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 26, 2026

Stats from current PR

✅ No significant changes detected

📊 All Metrics
📖 Metrics Glossary

Dev Server Metrics:

  • Listen = TCP port starts accepting connections
  • First Request = HTTP server returns successful response
  • Cold = Fresh build (no cache)
  • Warm = With cached build artifacts

Build Metrics:

  • Fresh = Clean build (no .next directory)
  • Cached = With existing .next directory

Change Thresholds:

  • Time: Changes < 50ms AND < 10%, OR < 2% are insignificant
  • Size: Changes < 1KB AND < 1% are insignificant
  • All other changes are flagged to catch regressions

⚡ Dev Server

Metric Canary PR Change Trend
Cold (Listen) 812ms 812ms ▅▃▅▇▃
Cold (Ready in log) 791ms 790ms ▂▁▄▅▄
Cold (First Request) 1.240s 1.237s ▂▂▅▆▃
Warm (Listen) 811ms 810ms ▅▃▃█▁
Warm (Ready in log) 792ms 793ms ▂▂▄▃▄
Warm (First Request) 611ms 614ms ▂▃▅▃▄
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 810ms 809ms █████
Cold (Ready in log) 779ms 778ms ███▇▇
Cold (First Request) 3.146s 3.155s ▄██▆▄
Warm (Listen) 810ms 810ms █████
Warm (Ready in log) 780ms 780ms ▇██▇▆
Warm (First Request) 3.157s 3.159s ▃▇█▄▄

⚡ Production Builds

Metric Canary PR Change Trend
Fresh Build 4.734s 4.726s ▁▁▅█▄
Cached Build 4.762s 4.766s ▁▃▄█▅
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 23.872s 23.677s ▅███▅
Cached Build 24.056s 23.791s ▆▆█▆▄
node_modules Size 505 MB 505 MB ▁▂▂██
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles
Canary PR Change
0-_gc56kf9tfo.js gzip 154 B N/A -
047ad-p-61fjn.js gzip 156 B N/A -
04hm05ar7kldw.js gzip 5.73 kB N/A -
0714gv_gbisrs.js gzip 70.8 kB N/A -
0cz1d0mv5g_q7.js gzip 39.4 kB 39.4 kB
0dvitrl5zg37g.js gzip 8.82 kB N/A -
0sf7ysou-72zd.js gzip 8.71 kB N/A -
11ny9t-iyn9yw.js gzip 154 B N/A -
157abun3hwc_s.js gzip 10.3 kB N/A -
1bhal_i5byhy7.js gzip 162 B N/A -
1elt1qium-r2m.css gzip 115 B 115 B
1jfwzy3ri2pyr.js gzip 154 B N/A -
1jj68jv9537mc.js gzip 13.8 kB N/A -
1jpaub6y8xlfr.js gzip 2.3 kB N/A -
1ot0mvscrc_uf.js gzip 233 B N/A -
2_m3xv2uq3sjc.js gzip 1.46 kB N/A -
24y34mwgrkqp4.js gzip 8.78 kB N/A -
28lrkt28g7fpx.js gzip 155 B N/A -
2c-fd4y1zozz8.js gzip 8.79 kB N/A -
2d7416h_xd36x.js gzip 8.71 kB N/A -
2extn3odmmem_.js gzip 12.9 kB N/A -
2fyhyy7niw9r6.js gzip 7.61 kB N/A -
2lyuhit6rn8fy.js gzip 9.44 kB N/A -
2q0gr8wfr3jwl.js gzip 8.77 kB N/A -
2qmtk0zius6fd.js gzip 157 B N/A -
2t9e75oz6r0zp.js gzip 8.76 kB N/A -
2uku_olcn15b7.js gzip 8.79 kB N/A -
2yl-53vckyqxz.js gzip 49.5 kB N/A -
30r8mm-46bdqy.js gzip 220 B 220 B
3c03ks8ic59bl.js gzip 160 B N/A -
3c03yiy5ds-h6.js gzip 65.5 kB N/A -
3d0blqiuhma_c.js gzip 155 B N/A -
3inab2jybr4k9.js gzip 450 B N/A -
3jkm5tdjvaf_q.js gzip 13.1 kB N/A -
3mix8ikliw_bj.js gzip 169 B N/A -
3mt67agm5wp40.js gzip 10.6 kB N/A -
3saabek4kohwi.js gzip 10 kB N/A -
3x_ftwxjtc4jr.js gzip 152 B N/A -
3x0-g47j0wg_t.js gzip 154 B N/A -
40pcd44z40i4a.js gzip 156 B N/A -
4189xmby9yu1p.js gzip 13.6 kB N/A -
turbopack-02..93y4.js gzip 4.2 kB N/A -
turbopack-0m..eii3.js gzip 4.2 kB N/A -
turbopack-0t..cm46.js gzip 4.2 kB N/A -
turbopack-13..1jkn.js gzip 4.2 kB N/A -
turbopack-17..eh9f.js gzip 4.18 kB N/A -
turbopack-1s..s1zo.js gzip 4.21 kB N/A -
turbopack-23..--2_.js gzip 4.2 kB N/A -
turbopack-28..zluk.js gzip 4.2 kB N/A -
turbopack-2a..kxyc.js gzip 4.2 kB N/A -
turbopack-2s..sur4.js gzip 4.2 kB N/A -
turbopack-2z..x5dq.js gzip 4.19 kB N/A -
turbopack-3h..65h_.js gzip 4.2 kB N/A -
turbopack-3s..rdjl.js gzip 4.2 kB N/A -
turbopack-3y..gfl4.js gzip 4.2 kB N/A -
0_i7nqgx23st7.js gzip N/A 10 kB -
05e40c15cx1dd.js gzip N/A 7.61 kB -
06puhytyxk31p.js gzip N/A 8.82 kB -
0cgwxw9wez5uz.js gzip N/A 155 B -
0hmgj4-w9knwk.js gzip N/A 49.5 kB -
0m34gln_kt4fg.js gzip N/A 5.73 kB -
0wjn_zvopg8j7.js gzip N/A 155 B -
1g3q1ww01thnl.js gzip N/A 2.3 kB -
1hraqxuiymq6v.js gzip N/A 8.79 kB -
1l9un1sl77287.js gzip N/A 1.46 kB -
1oj2783a6mjmk.js gzip N/A 153 B -
1tu42mhgspxaj.js gzip N/A 152 B -
2_n5io6i3e7-d.js gzip N/A 65.6 kB -
21-eavqb1k_36.js gzip N/A 13.9 kB -
2147zgtf14z-q.js gzip N/A 234 B -
23bz3xsg-5-1s.js gzip N/A 8.71 kB -
25m28ppgv1r-l.js gzip N/A 156 B -
27441mytv7pbm.js gzip N/A 9.43 kB -
2cjkwjgm1zcfs.js gzip N/A 8.71 kB -
2feeljlwn03ka.js gzip N/A 156 B -
2scd8zaoyb8md.js gzip N/A 8.79 kB -
2st_qs6p_9us0.js gzip N/A 13.1 kB -
2zo2exm1d8qj1.js gzip N/A 13.6 kB -
31gszfpoi49o_.js gzip N/A 157 B -
3g88fx8gbrpbb.js gzip N/A 159 B -
3hn75zuxly9az.js gzip N/A 10.3 kB -
3hqh7m128tvsn.js gzip N/A 8.77 kB -
3hqti_t-zy1x4.js gzip N/A 449 B -
3k_ox9mqvj991.js gzip N/A 162 B -
3lwd3rbxfr02f.js gzip N/A 157 B -
3mnawenie1flm.js gzip N/A 8.76 kB -
3sxqvx3vn2_or.js gzip N/A 155 B -
3ubsozlu6zs38.js gzip N/A 10.6 kB -
3vb9z2m8f0e3l.js gzip N/A 70.8 kB -
41_yhm_31fcf8.js gzip N/A 155 B -
41mf-x3mmsxae.js gzip N/A 12.9 kB -
41ovqpfyyufzz.js gzip N/A 168 B -
43iwfqjnx1cy_.js gzip N/A 8.78 kB -
turbopack-0g..-9g6.js gzip N/A 4.2 kB -
turbopack-1b..n71_.js gzip N/A 4.2 kB -
turbopack-1e..hu78.js gzip N/A 4.2 kB -
turbopack-1g..7-n-.js gzip N/A 4.2 kB -
turbopack-1l..qd49.js gzip N/A 4.21 kB -
turbopack-1t..oruv.js gzip N/A 4.2 kB -
turbopack-2n..xgmy.js gzip N/A 4.18 kB -
turbopack-2q..tzzk.js gzip N/A 4.2 kB -
turbopack-2s..khje.js gzip N/A 4.2 kB -
turbopack-31..xepd.js gzip N/A 4.2 kB -
turbopack-3q..wh7s.js gzip N/A 4.2 kB -
turbopack-3t..pfdy.js gzip N/A 4.2 kB -
turbopack-3y..qhsx.js gzip N/A 4.2 kB -
turbopack-43..rc4q.js gzip N/A 4.2 kB -
Total 468 kB 468 kB ⚠️ +58 B

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 717 B 717 B
Total 717 B 717 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 429 B 433 B
Total 429 B 433 B ⚠️ +4 B

📦 Webpack

Client

Main Bundles
Canary PR Change
2258-HASH.js gzip 61.1 kB N/A -
2266-HASH.js gzip 4.69 kB N/A -
3317.HASH.js gzip 169 B N/A -
4866-HASH.js gzip 5.64 kB N/A -
9e302639-HASH.js gzip 62.7 kB N/A -
framework-HASH.js gzip 59.5 kB 59.5 kB
main-app-HASH.js gzip 256 B 254 B
main-HASH.js gzip 39.9 kB 39.9 kB
webpack-HASH.js gzip 1.68 kB 1.68 kB
175fd0fd-HASH.js gzip N/A 62.7 kB -
2596-HASH.js gzip N/A 5.63 kB -
34-HASH.js gzip N/A 61 kB -
5691.HASH.js gzip N/A 169 B -
9156-HASH.js gzip N/A 4.68 kB -
Total 236 kB 236 kB ✅ -88 B
Polyfills
Canary PR Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Total 39.4 kB 39.4 kB
Pages
Canary PR Change
_app-HASH.js gzip 193 B 193 B
_error-HASH.js gzip 181 B 182 B
css-HASH.js gzip 334 B 332 B
dynamic-HASH.js gzip 1.79 kB 1.81 kB
edge-ssr-HASH.js gzip 255 B 255 B
head-HASH.js gzip 351 B 348 B
hooks-HASH.js gzip 385 B 384 B
image-HASH.js gzip 580 B 580 B
index-HASH.js gzip 257 B 259 B
link-HASH.js gzip 2.51 kB 2.52 kB
routerDirect..HASH.js gzip 318 B 319 B
script-HASH.js gzip 387 B 386 B
withRouter-HASH.js gzip 316 B 316 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 7.97 kB 7.99 kB ⚠️ +19 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 126 kB 126 kB
page.js gzip 275 kB 270 kB 🟢 5.26 kB (-2%)
Total 401 kB 396 kB ✅ -5.48 kB
Middleware
Canary PR Change
middleware-b..fest.js gzip 615 B 614 B
middleware-r..fest.js gzip 155 B 155 B
middleware.js gzip 44.4 kB 44.9 kB 🔴 +449 B (+1%)
edge-runtime..pack.js gzip 842 B 842 B
Total 46 kB 46.5 kB ⚠️ +448 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 719 B 717 B
Total 719 B 717 B ✅ -2 B
Build Cache
Canary PR Change
0.pack gzip 4.46 MB 4.45 MB
index.pack gzip 113 kB 114 kB
index.pack.old gzip 116 kB 114 kB 🟢 1.85 kB (-2%)
Total 4.68 MB 4.68 MB ✅ -1.68 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 349 kB 349 kB
app-page-exp..prod.js gzip 194 kB 194 kB
app-page-tur...dev.js gzip 349 kB 349 kB
app-page-tur..prod.js gzip 194 kB 194 kB
app-page-tur...dev.js gzip 345 kB 345 kB
app-page-tur..prod.js gzip 192 kB 192 kB
app-page.run...dev.js gzip 346 kB 346 kB
app-page.run..prod.js gzip 192 kB 192 kB
app-route-ex...dev.js gzip 77.5 kB 77.5 kB
app-route-ex..prod.js gzip 52.9 kB 52.9 kB
app-route-tu...dev.js gzip 77.6 kB 77.6 kB
app-route-tu..prod.js gzip 52.9 kB 52.9 kB
app-route-tu...dev.js gzip 77.2 kB 77.2 kB
app-route-tu..prod.js gzip 52.7 kB 52.7 kB
app-route.ru...dev.js gzip 77.1 kB 77.1 kB
app-route.ru..prod.js gzip 52.7 kB 52.7 kB
dist_client_...dev.js gzip 324 B 324 B
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 318 B 318 B
dist_client_...dev.js gzip 317 B 317 B
pages-api-tu...dev.js gzip 44.3 kB 44.3 kB
pages-api-tu..prod.js gzip 33.8 kB 33.8 kB
pages-api.ru...dev.js gzip 44.3 kB 44.3 kB
pages-api.ru..prod.js gzip 33.7 kB 33.7 kB
pages-turbo....dev.js gzip 53.7 kB 53.7 kB
pages-turbo...prod.js gzip 39.4 kB 39.4 kB
pages.runtim...dev.js gzip 53.6 kB 53.6 kB
pages.runtim..prod.js gzip 39.3 kB 39.3 kB
server.runti..prod.js gzip 63.2 kB 63.2 kB
use-cache-pr...dev.js gzip 69.7 kB 69.7 kB
use-cache-pr...dev.js gzip 69.7 kB 69.7 kB
use-cache-pr...dev.js gzip 68 kB 68 kB
use-cache-pr...dev.js gzip 68 kB 68 kB
Total 3.36 MB 3.36 MB ✅ -1 B
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/b3f4b1fb028b8ef923647fe26bfa5208d28e4795/next

Commit: b3f4b1f

@lukesandberg lukesandberg changed the base branch from more_ineffective_tasks to graphite-base/93100 May 6, 2026 21:45
Comment thread crates/next-api/src/project.rs
Comment thread test/development/basic/next-rs-api.test.ts Outdated
@lukesandberg lukesandberg force-pushed the graphite-base/93100 branch from b45210d to f0c1ffc Compare May 8, 2026 22:36
@lukesandberg lukesandberg changed the base branch from graphite-base/93100 to canary May 8, 2026 22:36
Copy link
Copy Markdown
Contributor Author

lukesandberg commented May 11, 2026

Merge activity

  • May 11, 12:50 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 11, 12:50 AM UTC: @lukesandberg merged this pull request with Graphite.

@lukesandberg lukesandberg merged commit e8f8f49 into canary May 11, 2026
184 of 185 checks passed
@lukesandberg lukesandberg deleted the fix_telemetry branch May 11, 2026 00:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

created-by: Turbopack team PRs by the Turbopack team. tests Turbopack Related to Turbopack with Next.js. type: next

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants