Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions packages/docs/src/components/DependencyCharts.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
import ChartTabs from './ChartTabs.astro'
import DepsChart from './DepsChart.astro'
import DuplicateDependencyChart from './DuplicateDependencyChart.astro'
import ProdDepsChart from './ProdDepsChart.astro'
---

Expand All @@ -9,7 +10,9 @@ import ProdDepsChart from './ProdDepsChart.astro'
label="Dependency graphs"
tab1Label="Deps"
tab2Label="Prod Deps"
tab3Label="Dup. Deps"
>
<DepsChart slot="panel-1" />
<ProdDepsChart slot="panel-2" />
<DuplicateDependencyChart slot="panel-3" />
</ChartTabs>
1 change: 1 addition & 0 deletions packages/docs/src/components/DependencyStatsTable.astro
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const depsColumns = [
},
{ key: 'prodDependencies', header: 'Prod Deps' },
{ key: 'devDependencies', header: 'Dev Deps' },
{ key: 'duplicateDependencies', header: 'Dup. Deps' },
{ key: 'nodeModulesSize', header: 'Size' },
{ key: 'nodeModulesSizeProdOnly', header: 'Size (Prod Only)' },
{
Expand Down
10 changes: 10 additions & 0 deletions packages/docs/src/components/DuplicateDependencyChart.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
import { chartDuplicateDependencyData } from '../lib/collections'
import ComparisonBarChart from './ComparisonBarChart.astro'
---

<ComparisonBarChart
title="Duplicate Dependencies"
data={chartDuplicateDependencyData}
valueFormat="count"
/>
1 change: 1 addition & 0 deletions packages/docs/src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const devtimeCollection = defineCollection({
buildOutputSize: z.number(),
nodeModulesSize: z.number(),
nodeModulesSizeProdOnly: z.number(),
duplicateDependencies: z.number().optional(),
timingMeasuredAt: z.string(),
runner: z.string(),
frameworkVersion: z.string().optional(),
Expand Down
9 changes: 9 additions & 0 deletions packages/docs/src/lib/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const depsStats = starterStats.map((f) => ({
isFocused: f.isFocused,
prodDependencies: f.prodDependencies,
devDependencies: f.devDependencies,
duplicateDependencies: f.duplicateDependencies,
nodeModulesSize: formatBytesToMB(f.nodeModulesSize),
nodeModulesSizeProdOnly: formatBytesToMB(f.nodeModulesSizeProdOnly),
graph: 'View',
Expand All @@ -39,4 +40,12 @@ const buildInstallData = starterStats.map((f) => ({
buildOutput: formatBytesToMB(f.buildOutputSize),
}))

export const chartDuplicateDependencyData = starterStats
.filter((f) => f?.name != null && Number.isFinite(f.duplicateDependencies))
.map((f) => ({
name: f.name,
value: f.duplicateDependencies!,
focused: f.isFocused,
}))

export { ssrStats, depsStats, buildInstallData }
135 changes: 134 additions & 1 deletion packages/docs/src/pages/framework/[slug].astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
---
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { getCollection } from 'astro:content'
import BackLink from '../../components/BackLink.astro'
import DevTimeChart from '../../components/DevTimeChart.astro'
Expand All @@ -10,7 +13,27 @@ import Layout from '../../layouts/Layout.astro'
import { buildInstallData, depsStats } from '../../lib/collections'
import { getFrameworkSlug } from '../../lib/utils'

interface E18eMessage {
severity: string
message: string
fixableBy?: string
}

export async function getStaticPaths() {
function readE18eMessages(packageName: string): E18eMessage[] {
try {
const packagesDir = fileURLToPath(
new URL('../../../../', import.meta.url),
)
const filePath = join(packagesDir, packageName, 'e18e-stats.json')
const content = readFileSync(filePath, 'utf-8')
const data = JSON.parse(content) as { messages?: E18eMessage[] }
return data.messages ?? []
} catch {
return []
}
}

const devtimeEntries = await getCollection('devtime')
const runtimeEntries = await getCollection('runtime')
const baseline = runtimeEntries.find(
Expand All @@ -22,18 +45,29 @@ export async function getStaticPaths() {
const runtimeEntry = runtimeEntries.find(
(e) => e.data.package === runtimePackage,
)
const e18eMessages = readE18eMessages(entry.data.package)
return {
params: { slug },
props: {
devtime: entry.data,
runtime: runtimeEntry?.data,
baseline,
e18eMessages,
},
}
})
}

const { devtime, runtime, baseline } = Astro.props
const { devtime, runtime, baseline, e18eMessages } = Astro.props

const duplicateDependencyMessages = e18eMessages.filter((m) =>
m.message.startsWith('[duplicate dependency]'),
)

function parseDupeName(message: string): string {
const match = message.match(/\[duplicate dependency\] (\S+) has/)
return match?.[1] ?? 'unknown'
}

const measuredDateDisplay = (() => {
const d = new Date(devtime.timingMeasuredAt)
Expand All @@ -48,6 +82,9 @@ const buildEntry = buildInstallData.find((e) => e.package === devtime.package)!
const depsColumns = [
{ key: 'prodDependencies', header: 'Prod Deps' },
{ key: 'devDependencies', header: 'Dev Deps' },
...(devtime.duplicateDependencies != null
? [{ key: 'duplicateDependencies', header: 'Dup. Deps' }]
: []),
{ key: 'nodeModulesSize', header: 'Size' },
{ key: 'nodeModulesSizeProdOnly', header: 'Size (Prod Only)' },
{
Expand All @@ -61,6 +98,9 @@ const depsData = [
{
prodDependencies: String(depsEntry.prodDependencies),
devDependencies: String(depsEntry.devDependencies),
...(devtime.duplicateDependencies != null
? { duplicateDependencies: String(devtime.duplicateDependencies) }
: {}),
nodeModulesSize: depsEntry.nodeModulesSize,
nodeModulesSizeProdOnly: depsEntry.nodeModulesSizeProdOnly,
graph: depsEntry.graph,
Expand Down Expand Up @@ -170,6 +210,37 @@ const ssrData = [
{buildEntry.buildOutput}
</p>

{
duplicateDependencyMessages.length > 0 && (
<section class="dupe-deps-section">
<h2>Duplicate Dependencies</h2>
<p class="dupe-deps-intro">
{duplicateDependencyMessages.length} duplicate{' '}
{duplicateDependencyMessages.length === 1
? 'dependency'
: 'dependencies'}{' '}
detected across this starter's node_modules.
</p>
<details class="dupe-deps-details">
<summary>
View {duplicateDependencyMessages.length} duplicate{' '}
{duplicateDependencyMessages.length === 1
? 'dependency'
: 'dependencies'}
</summary>
<ul class="dupe-list">
{duplicateDependencyMessages.map((msg) => (
<li class="dupe-item">
<strong class="dupe-name">{parseDupeName(msg.message)}</strong>
<pre class="dupe-message">{msg.message}</pre>
</li>
))}
</ul>
</details>
</section>
)
}

{
runtime && (
<>
Expand Down Expand Up @@ -200,4 +271,66 @@ const ssrData = [
.build-output strong {
color: var(--ft-text);
}

.dupe-deps-intro {
color: var(--ft-muted);
font-size: 14px;
margin: 0 0 0.75em;
}

.dupe-deps-details {
border: 1px solid var(--ft-border);
border-radius: 8px;
overflow: hidden;
}

.dupe-deps-details summary {
padding: 10px 14px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
user-select: none;
color: var(--ft-text);
background: var(--ft-bg-muted);
}

.dupe-deps-details summary:hover {
background: var(--ft-border);
}

.dupe-deps-details[open] summary {
border-bottom: 1px solid var(--ft-border);
}

.dupe-list {
list-style: none;
margin: 0;
padding: 0;
}

.dupe-item {
padding: 12px 14px;
border-bottom: 1px solid var(--ft-border);
}

.dupe-item:last-child {
border-bottom: none;
}

.dupe-name {
display: block;
font-size: 14px;
color: var(--ft-text);
margin-bottom: 6px;
}

.dupe-message {
margin: 0;
font-size: 12px;
line-height: 1.5;
color: var(--ft-muted);
white-space: pre-wrap;
word-break: break-word;
font-family: ui-monospace, 'SFMono-Regular', Menlo, monospace;
}
</style>
Loading