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
4 changes: 2 additions & 2 deletions packages/docs/src/components/DependencyStatsTable.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const depsColumns = [
href: (row: Record<string, unknown>) =>
`/framework/${getFrameworkSlug(row.package as string)}`,
},
{ key: 'prodDependencies', header: 'Prod Deps' },
{ key: 'devDependencies', header: 'Dev Deps' },
{ key: 'directDependencies', header: 'Deps Dev / Prod' },
{ key: 'allDependencies', header: 'All Deps' },
{ key: 'duplicateDependencies', header: 'Dup. Deps' },
{ key: 'nodeModulesSize', header: 'node_modules' },
{ key: 'nodeModulesSizeProdOnly', header: 'node_modules (prod)' },
Expand Down
1 change: 1 addition & 0 deletions packages/docs/src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const devtimeCollection = defineCollection({
order: z.number(),
prodDependencies: z.number(),
devDependencies: z.number(),
allDependencies: z.number(),
installTime: timeSchema,
coldBuildTime: timeSchema,
warmBuildTime: timeSchema,
Expand Down
3 changes: 2 additions & 1 deletion packages/docs/src/content/devtime/starter-astro.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"type": "starter-kit",
"prodDependencies": 1,
"devDependencies": 2,
"timingMeasuredAt": "2026-07-14T22:25:50.706Z",
"allDependencies": 0,
"timingMeasuredAt": "2026-07-14T07:44:26.499Z",
"runner": "ubuntu-latest",
"installTimeMs": 3160,
"coldBuildTimeMs": 2595,
Expand Down
3 changes: 2 additions & 1 deletion packages/docs/src/content/devtime/starter-mastro.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"type": "starter-kit",
"prodDependencies": 2,
"devDependencies": 2,
"timingMeasuredAt": "2026-07-14T22:25:50.706Z",
"allDependencies": 0,
"timingMeasuredAt": "2026-07-14T07:44:26.499Z",
"runner": "ubuntu-latest",
"frameworkVersion": "unknown",
"installTime": {
Expand Down
1 change: 1 addition & 0 deletions packages/docs/src/content/devtime/starter-next-js.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"type": "starter-kit",
"prodDependencies": 3,
"devDependencies": 6,
"allDependencies": 0,
"package": "starter-next-js",
"isFocused": true,
"installTimeMs": 7166,
Expand Down
1 change: 1 addition & 0 deletions packages/docs/src/content/devtime/starter-nuxt.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"type": "starter-kit",
"prodDependencies": 3,
"devDependencies": 2,
"allDependencies": 0,
"package": "starter-nuxt",
"isFocused": true,
"installTimeMs": 7579,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"type": "starter-kit",
"prodDependencies": 6,
"devDependencies": 8,
"allDependencies": 0,
"package": "starter-react-router",
"isFocused": true,
"installTimeMs": 2687,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"type": "starter-kit",
"prodDependencies": 5,
"devDependencies": 0,
"timingMeasuredAt": "2026-07-14T22:25:50.706Z",
"allDependencies": 0,
"timingMeasuredAt": "2026-07-14T07:44:26.499Z",
"runner": "ubuntu-latest",
"installTimeMs": 3935,
"nodeModulesSize": 168325120,
Expand Down
1 change: 1 addition & 0 deletions packages/docs/src/content/devtime/starter-sveltekit.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"type": "starter-kit",
"prodDependencies": 0,
"devDependencies": 7,
"allDependencies": 0,
"package": "starter-sveltekit",
"isFocused": true,
"installTimeMs": 2878,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"type": "starter-kit",
"prodDependencies": 13,
"devDependencies": 11,
"allDependencies": 0,
"package": "starter-tanstack-start-react",
"isFocused": true,
"installTimeMs": 4572,
Expand Down
3 changes: 3 additions & 0 deletions packages/docs/src/content/docs/methodology.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ of a typical new project.

- Duplicate dependency details come from e18e dependency analysis messages
collected for each starter package.
- All dependency counts come from the starter package's `pnpm-lock.yaml`
`packages` entries, which include direct and transitive resolved package
instances.
- A duplicate dependency means multiple installed versions of the same package
were found in the starter's dependency tree.
- Framework detail pages show the package name, installed versions, and the
Expand Down
5 changes: 5 additions & 0 deletions packages/docs/src/lib/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,13 @@ export const depsStats = starterStats.map((f) => ({
name: f.name,
package: f.package,
isFocused: f.isFocused,
directDependencies: [
f.devDependencies.toLocaleString(),
f.prodDependencies.toLocaleString(),
].join(' / '),
prodDependencies: f.prodDependencies,
devDependencies: f.devDependencies,
allDependencies: f.allDependencies.toLocaleString(),
duplicateDependencies: f.duplicateDependencies,
nodeModulesSize: formatBytesToMB(f.nodeModulesSize),
nodeModulesSizeProdOnly: formatBytesToMB(f.nodeModulesSizeProdOnly),
Expand Down
8 changes: 4 additions & 4 deletions packages/docs/src/pages/framework/[slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ const depsEntry = depsStats.find((e) => e.package === devtime.package)!
const buildEntry = buildInstallData.find((e) => e.package === devtime.package)!

const depsColumns = [
{ key: 'prodDependencies', header: 'Prod Deps' },
{ key: 'devDependencies', header: 'Dev Deps' },
{ key: 'directDependencies', header: 'Deps Dev / Prod' },
{ key: 'allDependencies', header: 'All Deps' },
...(devtime.duplicateDependencies != null
? [{ key: 'duplicateDependencies', header: 'Dup. Deps' }]
: []),
Expand All @@ -92,8 +92,8 @@ const depsColumns = [

const depsData = [
{
prodDependencies: String(depsEntry.prodDependencies),
devDependencies: String(depsEntry.devDependencies),
directDependencies: depsEntry.directDependencies,
allDependencies: depsEntry.allDependencies,
...(devtime.duplicateDependencies != null
? { duplicateDependencies: String(devtime.duplicateDependencies) }
: {}),
Expand Down
1 change: 1 addition & 0 deletions packages/starter-astro/ci-stats.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 1,
"devDependencies": 2,
"allDependencies": 0,
"duplicateDependencies": 17,
"depInstallSize": 171119612,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-astro/stats/5.16.15.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 1,
"devDependencies": 2,
"allDependencies": 0,
"duplicateDependencies": 17,
"depInstallSize": 171119612,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-mastro/ci-stats.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"buildOutputSize": 12288,
"prodDependencies": 2,
"devDependencies": 2,
"allDependencies": 0,
"duplicateDependencies": 0,
"depInstallSize": 28724892,
"e18eMessages": [],
Expand Down
1 change: 1 addition & 0 deletions packages/starter-next-js/ci-stats.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 3,
"devDependencies": 6,
"allDependencies": 0,
"duplicateDependencies": 12,
"depInstallSize": 530810717,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-next-js/stats/16.1.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 3,
"devDependencies": 6,
"allDependencies": 0,
"duplicateDependencies": 12,
"depInstallSize": 530810717,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-nuxt/ci-stats.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 3,
"devDependencies": 2,
"allDependencies": 0,
"duplicateDependencies": 63,
"depInstallSize": 180604928,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-nuxt/stats/4.2.2.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 3,
"devDependencies": 2,
"allDependencies": 0,
"duplicateDependencies": 63,
"depInstallSize": 180604928,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-react-router/ci-stats.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 6,
"devDependencies": 8,
"allDependencies": 0,
"duplicateDependencies": 9,
"depInstallSize": 107182276,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-react-router/stats/7.10.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 6,
"devDependencies": 8,
"allDependencies": 0,
"duplicateDependencies": 9,
"depInstallSize": 107182276,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-solid-start/ci-stats.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 5,
"devDependencies": 0,
"allDependencies": 0,
"duplicateDependencies": 79,
"depInstallSize": 143557200,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-solid-start/stats/1.2.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 5,
"devDependencies": 0,
"allDependencies": 0,
"duplicateDependencies": 79,
"depInstallSize": 143557200,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-sveltekit/ci-stats.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 0,
"devDependencies": 7,
"allDependencies": 0,
"duplicateDependencies": 1,
"depInstallSize": 68539509,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-sveltekit/stats/2.49.4.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 0,
"devDependencies": 7,
"allDependencies": 0,
"duplicateDependencies": 1,
"depInstallSize": 68539509,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-tanstack-start-react/ci-stats.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 13,
"devDependencies": 11,
"allDependencies": 0,
"duplicateDependencies": 14,
"depInstallSize": 208163833,
"e18eMessages": [
Expand Down
1 change: 1 addition & 0 deletions packages/starter-tanstack-start-react/stats/1.145.3.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"prodDependencies": 13,
"devDependencies": 11,
"allDependencies": 0,
"duplicateDependencies": 14,
"depInstallSize": 208163833,
"e18eMessages": [
Expand Down
20 changes: 6 additions & 14 deletions packages/stats-generator/src/collect-stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,13 @@ import { getFrameworks } from './get-frameworks.ts'
import { packagesDir } from './constants.ts'
import { saveStats } from './save-stats.ts'
import { getCIStats } from './get-ci-stats.ts'
import { getDependencyCountsFromPackageMetadata } from './utils.ts'
import type {
BrowserBaselineStats,
FrameworkStats,
PackageJson,
FrameworkConfig,
} from './types.ts'

async function getDependencyCountsFromPackageJson(pkgDir: string) {
const packageJsonPath = join(packagesDir, pkgDir, 'package.json')
const content = await readFile(packageJsonPath, 'utf-8')
const packageJson = JSON.parse(content) as PackageJson

return {
prodDependencies: Object.keys(packageJson.dependencies ?? {}).length,
devDependencies: Object.keys(packageJson.devDependencies ?? {}).length,
}
}

async function getBrowserBaselineStats(pkgDir: string) {
const browserBaselineStatsPath = join(
packagesDir,
Expand Down Expand Up @@ -51,8 +40,11 @@ async function processStarter(framework: FrameworkConfig, order: number) {

const hasDependencies = measurements.some((m) => m.type === 'dependencies')
const dependencyStats =
hasDependencies && ciStats.prodDependencies === undefined
? await getDependencyCountsFromPackageJson(pkgDir)
hasDependencies &&
(ciStats.prodDependencies === undefined ||
ciStats.devDependencies === undefined ||
ciStats.allDependencies === undefined)
? getDependencyCountsFromPackageMetadata(pkgDir)
: {}
const hasBrowserBaseline = measurements.some(
(m) => m.type === 'browserBaseline',
Expand Down
17 changes: 14 additions & 3 deletions packages/stats-generator/src/save-ci-stats.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { join } from 'node:path'
import { getFrameworks } from './get-frameworks.ts'
import { packagesDir } from './constants.ts'
import { normalizeCIStats, readJsonFile, writeJsonFile } from './utils.ts'
import {
getDependencyCountsFromPackageMetadata,
normalizeCIStats,
readJsonFile,
writeJsonFile,
} from './utils.ts'
import type {
CIStats,
InstallStats,
Expand Down Expand Up @@ -134,13 +139,19 @@ async function main() {
const e18eStats = readJsonFile<E18eStats>(e18eArtifactPath)
if (e18eStats) {
console.info(` ✓ Found e18e stats artifact`)
const packageDependencyCounts =
getDependencyCountsFromPackageMetadata(packageName)
const duplicateEntry = e18eStats.stats.extraStats?.find(
(s) => s.name === 'duplicateDependencyCount',
)
stats = {
...stats,
const dependencyCounts = {
prodDependencies: e18eStats.stats.dependencyCount.production,
devDependencies: e18eStats.stats.dependencyCount.development,
allDependencies: packageDependencyCounts.allDependencies,
}
stats = {
...stats,
...dependencyCounts,
duplicateDependencies:
typeof duplicateEntry?.value === 'number'
? duplicateEntry.value
Expand Down
1 change: 1 addition & 0 deletions packages/stats-generator/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export interface CIStats {
// Dependency stats (from e18e analysis)
prodDependencies?: number
devDependencies?: number
allDependencies?: number
duplicateDependencies?: number
depInstallSize?: number
e18eMessages?: Array<{
Expand Down
47 changes: 46 additions & 1 deletion packages/stats-generator/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { readFile } from 'node:fs/promises'
import { join, dirname } from 'node:path'
import { packagesDir } from './constants.ts'
import { getFrameworks } from './get-frameworks.ts'
import type { CIStats, FrameworkConfig, TestConfig } from './types.ts'
import type {
CIStats,
FrameworkConfig,
PackageJson,
TestConfig,
} from './types.ts'

/**
* Get directory size in bytes using du command.
Expand Down Expand Up @@ -48,6 +53,46 @@ export function writeJsonFile(filePath: string, data: unknown): void {
writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`)
}

function countPnpmLockPackages(lockfileContent: string): number {
const packageEntryPattern = /^ {2}\S.*:\s*$/
const lines = lockfileContent.split(/\r?\n/)
const packagesIndex = lines.findIndex((line) => line === 'packages:')
if (packagesIndex === -1) {
return 0
}

let count = 0
for (const line of lines.slice(packagesIndex + 1)) {
if (/^\S/.test(line)) {
break
}

if (packageEntryPattern.test(line)) {
count += 1
}
}

return count
}

export function getDependencyCountsFromPackageMetadata(packageName: string) {
const packageJsonPath = join(packagesDir, packageName, 'package.json')
const packageJson = JSON.parse(
readFileSync(packageJsonPath, 'utf-8'),
) as PackageJson

const lockfilePath = join(packagesDir, packageName, 'pnpm-lock.yaml')
const allDependencies = existsSync(lockfilePath)
? countPnpmLockPackages(readFileSync(lockfilePath, 'utf-8'))
: undefined

return {
prodDependencies: Object.keys(packageJson.dependencies ?? {}).length,
devDependencies: Object.keys(packageJson.devDependencies ?? {}).length,
allDependencies,
}
}

export async function getFrameworkVersion(
packageName: string,
frameworkPackage: string,
Expand Down
Loading