Skip to content

Commit 98e64c8

Browse files
committed
next analyze: tag polyfill modules through to UI
1 parent fcf9600 commit 98e64c8

File tree

9 files changed

+227
-21
lines changed

9 files changed

+227
-21
lines changed

apps/bundle-analyzer/app/globals.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
--sidebar-accent-foreground: oklch(0.205 0 0);
3333
--sidebar-border: oklch(0.922 0 0);
3434
--sidebar-ring: oklch(0.708 0 0);
35+
--polyfill: #de2670;
36+
--polyfill-foreground: #fce7f3;
3537
}
3638

3739
.dark {
@@ -62,6 +64,8 @@
6264
--sidebar-accent-foreground: oklch(0.985 0 0);
6365
--sidebar-border: oklch(0.269 0 0);
6466
--sidebar-ring: oklch(0.439 0 0);
67+
--polyfill: #de2670;
68+
--polyfill-foreground: #7c1d3f;
6569
}
6670

6771
@theme inline {
@@ -98,6 +102,8 @@
98102
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
99103
--color-sidebar-border: var(--sidebar-border);
100104
--color-sidebar-ring: var(--sidebar-ring);
105+
--color-polyfill: var(--polyfill);
106+
--color-polyfill-foreground: var(--polyfill-foreground);
101107
}
102108

103109
@layer base {

apps/bundle-analyzer/app/page.tsx

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import type React from 'react'
4-
import { useEffect, useMemo, useRef, useState } from 'react'
4+
import { useEffect, useMemo, useRef, useState, useCallback } from 'react'
55
import useSWR from 'swr'
66
import { ImportChain } from '@/components/import-chain'
77
import {
@@ -119,6 +119,22 @@ export default function Home() {
119119
}
120120
}, [analyzeData, environmentFilter, typeFilter])
121121

122+
const isModulePolyfillChunk = useCallback(
123+
(sourceIndex: number) => {
124+
if (!analyzeData) return false
125+
return analyzeData.isPolyfillModule(sourceIndex)
126+
},
127+
[analyzeData]
128+
)
129+
130+
const isNoModulePolyfillChunk = useCallback(
131+
(sourceIndex: number) => {
132+
if (!analyzeData) return false
133+
return analyzeData.isPolyfillNoModule(sourceIndex)
134+
},
135+
[analyzeData]
136+
)
137+
122138
const handleMouseDown = () => {
123139
setIsResizing(true)
124140
}
@@ -267,6 +283,8 @@ export default function Home() {
267283
onHoveredNodeChange={setHoveredNodeInfo}
268284
searchQuery={searchQuery}
269285
filterSource={filterSource}
286+
isModulePolyfillChunk={isModulePolyfillChunk}
287+
isNoModulePolyfillChunk={isNoModulePolyfillChunk}
270288
/>
271289
</div>
272290

@@ -289,12 +307,37 @@ export default function Home() {
289307
{selectedSourceIndex != null &&
290308
analyzeData.source(selectedSourceIndex) && (
291309
<>
292-
<p className="text-xs text-muted-foreground mt-2">
293-
Output Size:{' '}
294-
{formatBytes(
295-
analyzeData.getSourceOutputSize(selectedSourceIndex)
310+
<dl className="space-y-2">
311+
<div>
312+
<dt className="text-xs text-muted-foreground inline">
313+
Output Size:{' '}
314+
</dt>
315+
<dd className="text-xs text-muted-foreground inline">
316+
{formatBytes(
317+
analyzeData.getSourceOutputSize(
318+
selectedSourceIndex
319+
)
320+
)}
321+
</dd>
322+
</div>
323+
{(isModulePolyfillChunk(selectedSourceIndex) ||
324+
isNoModulePolyfillChunk(selectedSourceIndex)) && (
325+
<div className="flex items-center gap-2">
326+
<dt className="inline-flex items-center rounded-md bg-pink-50 dark:bg-pink-900/30 px-2 py-1 text-xs font-medium text-pink-800 dark:text-pink-300 ring-1 ring-inset ring-pink-800/10 dark:ring-pink-300/20 shrink-0">
327+
Polyfill
328+
</dt>
329+
<dd className="text-xs text-muted-foreground">
330+
Next.js built-in polyfills
331+
{isNoModulePolyfillChunk(selectedSourceIndex) ? (
332+
<>
333+
. <pre>polyfill-nomodule.js</pre> is only sent
334+
to legacy browsers.
335+
</>
336+
) : null}
337+
</dd>
338+
</div>
296339
)}
297-
</p>
340+
</dl>
298341
{modulesData && (
299342
<ImportChain
300343
key={selectedSourceIndex}

apps/bundle-analyzer/components/treemap-visualizer.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { darken, lighten } from 'polished'
3+
import { darken, lighten, readableColor } from 'polished'
44
import type React from 'react'
55
import { useEffect, useMemo, useRef, useState } from 'react'
66
import type { AnalyzeData } from '@/lib/analyze-data'
@@ -23,6 +23,8 @@ interface TreemapVisualizerProps {
2323
onHoveredNodeChangeDelayed?: (nodeInfo: LayoutNodeInfo | null) => void
2424
searchQuery?: string
2525
filterSource?: (sourceIndex: number) => boolean
26+
isModulePolyfillChunk?: (sourceIndex: number) => boolean
27+
isNoModulePolyfillChunk?: (sourceIndex: number) => boolean
2628
}
2729

2830
function getFileColor(node: {
@@ -184,6 +186,8 @@ function drawTreemap(
184186
searchQuery: string,
185187
originalData: LayoutNode,
186188
immediateHoveredSourceIndex: number | undefined,
189+
isModulePolyfillChunk: (sourceIndex: number) => boolean,
190+
isNoModuePolyfillChunk: (sourceIndex: number) => boolean,
187191
currentPath: string[] = [],
188192
parentFadedOut = false,
189193
insideActiveSubtree = false
@@ -241,6 +245,8 @@ function drawTreemap(
241245
searchQuery,
242246
originalData,
243247
immediateHoveredSourceIndex,
248+
isModulePolyfillChunk,
249+
isNoModuePolyfillChunk,
244250
path,
245251
parentFadedOut,
246252
insideActiveSubtree
@@ -305,6 +311,13 @@ function drawTreemap(
305311

306312
if (type === 'file') {
307313
let color = getFileColor(node)
314+
const isPolyfill =
315+
sourceIndex !== undefined &&
316+
(isModulePolyfillChunk(sourceIndex) ||
317+
isNoModuePolyfillChunk(sourceIndex))
318+
if (isPolyfill) {
319+
color = '#DE2670'
320+
}
308321

309322
// Apply brightness boost to immediately hovered node
310323
if (isImmediateHovered) {
@@ -320,7 +333,9 @@ function drawTreemap(
320333
ctx.strokeRect(rect.x, rect.y, rect.width, rect.height)
321334

322335
if (rect.width > 60 && rect.height > 30) {
323-
ctx.fillStyle = colors.text
336+
// Use readableColor to ensure good contrast against the background
337+
const textColor = isPolyfill ? readableColor(color) : colors.text
338+
ctx.fillStyle = textColor
324339
ctx.font = '12px sans-serif'
325340
ctx.textAlign = 'center'
326341
ctx.textBaseline = 'middle'
@@ -467,6 +482,8 @@ function drawTreemap(
467482
searchQuery,
468483
originalData,
469484
immediateHoveredSourceIndex,
485+
isModulePolyfillChunk,
486+
isNoModuePolyfillChunk,
470487
path,
471488
childFadeOut,
472489
childInsideActiveSubtree
@@ -556,6 +573,8 @@ export function TreemapVisualizer({
556573
onHoveredNodeChangeDelayed,
557574
searchQuery = '',
558575
filterSource,
576+
isModulePolyfillChunk = () => false,
577+
isNoModulePolyfillChunk = () => false,
559578
}: TreemapVisualizerProps) {
560579
const canvasRef = useRef<HTMLCanvasElement>(null)
561580
const containerRef = useRef<HTMLDivElement>(null)
@@ -707,7 +726,9 @@ export function TreemapVisualizer({
707726
focusedAncestorChain,
708727
searchQuery,
709728
layout,
710-
hoveredNode?.sourceIndex
729+
hoveredNode?.sourceIndex,
730+
isModulePolyfillChunk,
731+
isNoModulePolyfillChunk
711732
)
712733
}, [
713734
layout,
@@ -719,6 +740,8 @@ export function TreemapVisualizer({
719740
focusedAncestorChain,
720741
searchQuery,
721742
hoveredNode,
743+
isModulePolyfillChunk,
744+
isNoModulePolyfillChunk,
722745
])
723746

724747
const handleClick = (e: React.MouseEvent<HTMLCanvasElement>) => {

apps/bundle-analyzer/lib/analyze-data.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,20 @@ export class AnalyzeData {
388388
return { client, server, traced, js, css, json, asset }
389389
}
390390

391+
isPolyfillModule(index: number): boolean {
392+
const fullSourcePath = this.getFullSourcePath(index)
393+
return fullSourcePath.endsWith(
394+
'node_modules/next/dist/build/polyfills/polyfill-module.js'
395+
)
396+
}
397+
398+
isPolyfillNoModule(index: number): boolean {
399+
const fullSourcePath = this.getFullSourcePath(index)
400+
return fullSourcePath.endsWith(
401+
'node_modules/next/dist/build/polyfills/polyfill-nomodule.js'
402+
)
403+
}
404+
391405
// Get the raw header for debugging
392406
getRawAnalyzeHeader(): AnalyzeDataHeader {
393407
return this.analyzeHeader

crates/next-api/src/analyze.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ pub async fn analyze_output_assets(output_assets: Vc<OutputAssets>) -> Result<Vc
367367
// Skip source maps.
368368
continue;
369369
}
370+
370371
let output_file_index = builder.add_output_file(AnalyzeOutputFile { filename });
371372
let chunk_parts = split_output_asset_into_parts(*asset).await?;
372373
for chunk_part in chunk_parts {

crates/next-api/src/app.rs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,16 @@ use turbopack_core::{
6262
chunk_group_info::{ChunkGroup, ChunkGroupEntry},
6363
},
6464
output::{OutputAsset, OutputAssets, OutputAssetsWithReferenced},
65-
raw_output::RawOutput,
6665
reference::all_assets_from_entries,
6766
reference_type::{CommonJsReferenceSubType, CssReferenceSubType, ReferenceType},
6867
resolve::{origin::PlainResolveOrigin, parse::Request, pattern::Pattern},
6968
source::Source,
69+
source_map::SourceMapAsset,
7070
virtual_output::VirtualOutputAsset,
7171
};
72-
use turbopack_ecmascript::resolve::cjs_resolve;
72+
use turbopack_ecmascript::{
73+
resolve::cjs_resolve, single_file_ecmascript_output::SingleFileEcmascriptOutput,
74+
};
7375

7476
use crate::{
7577
dynamic_imports::{NextDynamicChunkAvailability, collect_next_dynamic_chunks},
@@ -1300,11 +1302,11 @@ impl AppEndpoint {
13001302

13011303
let manifest_path_prefix = &app_entry.original_name;
13021304

1303-
// polyfill-nomodule.js is a pre-compiled asset distributed as part of next,
1304-
// load it as a RawModule.
1305+
// polyfill-nomodule.js is a pre-compiled asset distributed as part of next
13051306
let next_package = get_next_package(project.project_path().owned().await?).await?;
1306-
let polyfill_source =
1307-
FileSource::new(next_package.join("dist/build/polyfills/polyfill-nomodule.js")?);
1307+
let polyfill_source_path =
1308+
next_package.join("dist/build/polyfills/polyfill-nomodule.js")?;
1309+
let polyfill_source = FileSource::new(polyfill_source_path.clone());
13081310
let polyfill_output_path = client_chunking_context
13091311
.chunk_path(
13101312
Some(Vc::upcast(polyfill_source)),
@@ -1314,13 +1316,26 @@ impl AppEndpoint {
13141316
)
13151317
.owned()
13161318
.await?;
1317-
let polyfill_output_asset = ResolvedVc::upcast(
1318-
RawOutput::new(polyfill_output_path, Vc::upcast(polyfill_source))
1319-
.to_resolved()
1320-
.await?,
1321-
);
1319+
1320+
let polyfill_output = SingleFileEcmascriptOutput::new(
1321+
polyfill_output_path.clone(),
1322+
polyfill_source_path,
1323+
Vc::upcast(polyfill_source),
1324+
)
1325+
.to_resolved()
1326+
.await?;
1327+
1328+
let polyfill_output_asset = ResolvedVc::upcast(polyfill_output);
13221329
client_assets.insert(polyfill_output_asset);
13231330

1331+
let polyfill_source_map_asset = SourceMapAsset::new_fixed(
1332+
polyfill_output_path.clone(),
1333+
*ResolvedVc::upcast(polyfill_output),
1334+
)
1335+
.to_resolved()
1336+
.await?;
1337+
client_assets.insert(ResolvedVc::upcast(polyfill_source_map_asset));
1338+
13241339
let client_assets: ResolvedVc<OutputAssets> =
13251340
ResolvedVc::cell(client_assets.into_iter().collect::<Vec<_>>());
13261341

crates/next-api/src/route.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use turbo_tasks::{
99
};
1010
use turbopack_core::{
1111
module_graph::{GraphEntries, ModuleGraph},
12-
output::OutputAssets,
12+
output::{OptionOutputAsset, OutputAssets},
1313
};
1414

1515
use crate::{operation::OptionEndpoint, paths::ServerPath, project::Project};
@@ -72,6 +72,10 @@ pub trait Endpoint {
7272
}
7373
#[turbo_tasks::function]
7474
fn module_graphs(self: Vc<Self>) -> Vc<ModuleGraphs>;
75+
#[turbo_tasks::function]
76+
fn polyfill_asset(self: Vc<Self>) -> Vc<OptionOutputAsset> {
77+
Vc::cell(None)
78+
}
7579
}
7680

7781
#[derive(

turbopack/crates/turbopack-ecmascript/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod path_visitor;
2424
pub mod references;
2525
pub mod runtime_functions;
2626
pub mod side_effect_optimization;
27+
pub mod single_file_ecmascript_output;
2728
pub mod source_map;
2829
pub(crate) mod static_code;
2930
mod swc_comments;

0 commit comments

Comments
 (0)