diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml
index df1c8388..064c8d2b 100644
--- a/.github/workflows/bundle-size.yml
+++ b/.github/workflows/bundle-size.yml
@@ -62,12 +62,12 @@ jobs:
}
function formatDelta(current, previous) {
- if (previous === undefined) return 'โ';
+ if (previous === undefined) return { bytes: 'โ', pct: '' };
const diff = current - previous;
- if (diff === 0) return '0 B';
- const sign = diff > 0 ? '+' : '';
- const pct = ((diff / previous) * 100).toFixed(1);
- return `${sign}${formatBytes(diff)} (${sign}${pct}%)`;
+ if (diff === 0) return { bytes: '0 B', pct: '0%' };
+ const sign = diff > 0 ? '+' : '-';
+ const pct = Math.abs((diff / previous) * 100).toFixed(1);
+ return { bytes: `${sign}${formatBytes(Math.abs(diff))}`, pct: `${sign}${pct}%` };
}
function statusIcon(current, previous) {
@@ -79,36 +79,126 @@ jobs:
return pct > 10 ? '๐ด' : '๐บ';
}
- const rows = pr.map(entry => {
- const prev = baseMap[entry.name];
- return `| ${entry.name} | ${prev !== undefined ? formatBytes(prev) : 'โ'} | ${formatBytes(entry.size)} | ${formatDelta(entry.size, prev)} | ${statusIcon(entry.size, prev)} |`;
- });
+ // Size bar: proportional block characters
+ function sizeBar(bytes, maxBytes) {
+ const width = 8;
+ const filled = Math.max(1, Math.round((bytes / maxBytes) * width));
+ return 'โ'.repeat(filled) + 'โ'.repeat(width - filled);
+ }
- const total = pr.reduce((sum, e) => sum + e.size, 0);
- const baseTotal = base.reduce((sum, e) => sum + e.size, 0);
+ // Group entries by package: @videojs/utils/* -> utils, @videojs/store/* -> store, etc.
+ const groups = new Map();
+ for (const entry of pr) {
+ const match = entry.name.match(/^@videojs\/([^/]+)/);
+ const pkg = match ? match[1] : 'other';
+ if (!groups.has(pkg)) groups.set(pkg, []);
+ groups.get(pkg).push(entry);
+ }
+
+ // First pass: compute per-package totals
+ const pkgData = [];
+ let grandTotalPr = 0;
+ let grandTotalBase = 0;
+
+ for (const [pkg, entries] of groups) {
+ const pkgTotalPr = entries.reduce((s, e) => s + e.size, 0);
+ const pkgTotalBase = entries.reduce((s, e) => s + (baseMap[e.name] ?? 0), 0);
+ const hasBase = entries.some(e => baseMap[e.name] !== undefined);
+ grandTotalPr += pkgTotalPr;
+ grandTotalBase += pkgTotalBase;
+
+ const pkgPrev = entries.length > 1 ? pkgTotalBase : baseMap[entries[0].name];
+ const pkgCur = entries.length > 1 ? pkgTotalPr : entries[0].size;
+ const changed = hasBase && pkgCur !== pkgPrev;
+
+ pkgData.push({ pkg, entries, pkgTotalPr, pkgTotalBase, hasBase, pkgPrev, pkgCur, changed });
+ }
+
+ const maxPkgSize = Math.max(...pkgData.map(p => p.pkgCur));
+
+ // Overview table
+ const overview = [];
+ overview.push('| Package | Size | | Diff | % | |');
+ overview.push('|---|--:|---|--:|--:|:-:|');
+
+ for (const p of pkgData) {
+ const d = formatDelta(p.pkgCur, p.pkgPrev);
+ const icon = p.hasBase ? statusIcon(p.pkgCur, p.pkgPrev) : '';
+ const bar = `\`${sizeBar(p.pkgCur, maxPkgSize)}\``;
+ overview.push(
+ `| **@videojs/${p.pkg}** | **${formatBytes(p.pkgCur)}** | ${bar} | ${p.hasBase ? d.bytes : 'โ'} | ${p.hasBase ? d.pct : ''} | ${icon} |`
+ );
+ }
+
+ // Detail sections
+ const details = [];
+ details.push('#### Subpath Breakdown');
+ details.push('');
+
+ for (const p of pkgData) {
+ const { pkg, entries, pkgTotalPr, pkgTotalBase, hasBase } = p;
+
+ // Subpath display: @videojs/utils/dom -> ./dom, @videojs/store -> .
+ const displayName = (name) => {
+ const sub = name.replace(`@videojs/${pkg}`, '');
+ return sub ? `.${sub}` : '.';
+ };
+
+ details.push(``);
+ details.push(`@videojs/${pkg}
`);
+ details.push('');
+ details.push('| Subpath | Base | PR | Diff | % | |');
+ details.push('|---|--:|--:|--:|--:|:-:|');
+
+ for (const entry of entries) {
+ const prev = baseMap[entry.name];
+ const d = formatDelta(entry.size, prev);
+ details.push(
+ `| \`${displayName(entry.name)}\` | ${prev !== undefined ? formatBytes(prev) : 'โ'} | **${formatBytes(entry.size)}** | ${d.bytes} | ${d.pct} | ${statusIcon(entry.size, prev)} |`
+ );
+ }
+
+ if (entries.length > 1) {
+ const d = formatDelta(pkgTotalPr, pkgTotalBase);
+ details.push(
+ `| **total** | **${hasBase ? formatBytes(pkgTotalBase) : 'โ'}** | **${formatBytes(pkgTotalPr)}** | **${hasBase ? d.bytes : 'โ'}** | **${hasBase ? d.pct : ''}** | |`
+ );
+ }
+
+ details.push('');
+ details.push(' ');
+ details.push('');
+ }
+
+ const grandDelta = formatDelta(grandTotalPr, grandTotalBase);
const marker = '';
const body = [
marker,
'### ๐ฆ Bundle Size Report',
'',
- '| Package | Base | PR | Delta | |',
- '|---|---|---|---|---|',
- ...rows,
- `| **Total** | **${base.length ? formatBytes(baseTotal) : 'โ'}** | **${formatBytes(total)}** | **${base.length ? formatDelta(total, baseTotal) : 'โ'}** | |`,
+ ...overview,
+ '',
+ `**Total: ${formatBytes(grandTotalPr)}**${grandTotalBase ? ` ยท ${grandDelta.bytes} ยท ${grandDelta.pct}` : ''}`,
+ '',
+ '---',
+ '',
+ ...details,
+ '---',
'',
'',
'โน๏ธ How to interpret
',
'',
- 'Sizes are minified + brotli compressed, measured via [size-limit](https://github.com/ai/size-limit) with esbuild tree-shaking.',
+ 'Each package shows its own code size with workspace and peer dependencies externalized.',
+ 'Sizes are minified + brotli, measured via [size-limit](https://github.com/ai/size-limit) with esbuild.',
'',
'| Icon | Meaning |',
'|---|---|',
'| โ
| No change |',
- '| ๐บ | Size increased โค 10% |',
- '| ๐ด | Size increased > 10% |',
- '| ๐ฝ | Size decreased |',
- '| ๐ | New entry (no baseline) |',
+ '| ๐บ | Increased โค 10% |',
+ '| ๐ด | Increased > 10% |',
+ '| ๐ฝ | Decreased |',
+ '| ๐ | New (no baseline) |',
'',
'Run `pnpm size` locally to check current sizes.',
' ',
diff --git a/.size-limit.json b/.size-limit.json
index 9f36a8bd..a84b2329 100644
--- a/.size-limit.json
+++ b/.size-limit.json
@@ -7,31 +7,71 @@
{
"name": "@videojs/store/lit",
"path": "packages/store/dist/default/lit.js",
- "import": "*"
+ "import": "*",
+ "ignore": ["@videojs/utils", "@lit/reactive-element", "@lit/context"]
},
{
"name": "@videojs/store/react",
"path": "packages/store/dist/default/react.js",
- "import": "*"
+ "import": "*",
+ "ignore": ["@videojs/utils", "react"]
},
{
"name": "@videojs/core",
"path": "packages/core/dist/default/index.js",
- "import": "*"
+ "import": "*",
+ "ignore": ["@videojs/utils", "@videojs/store"]
},
{
"name": "@videojs/core/dom",
"path": "packages/core/dist/default/dom.js",
- "import": "*"
- },
- {
- "name": "@videojs/react",
- "path": "packages/react/dist/default/index.js",
- "import": "*"
+ "import": "*",
+ "ignore": ["@videojs/utils", "@videojs/store"]
},
{
"name": "@videojs/html",
"path": "packages/html/dist/default/index.js",
"import": "*"
+ },
+ {
+ "name": "@videojs/react",
+ "path": "packages/react/dist/default/index.js",
+ "import": "*",
+ "ignore": ["react"]
+ },
+ {
+ "name": "@videojs/utils/array",
+ "path": "packages/utils/dist/array.js",
+ "import": "*"
+ },
+ {
+ "name": "@videojs/utils/dom",
+ "path": "packages/utils/dist/dom.js",
+ "import": "*"
+ },
+ {
+ "name": "@videojs/utils/events",
+ "path": "packages/utils/dist/events.js",
+ "import": "*"
+ },
+ {
+ "name": "@videojs/utils/function",
+ "path": "packages/utils/dist/function.js",
+ "import": "*"
+ },
+ {
+ "name": "@videojs/utils/object",
+ "path": "packages/utils/dist/object.js",
+ "import": "*"
+ },
+ {
+ "name": "@videojs/utils/predicate",
+ "path": "packages/utils/dist/predicate.js",
+ "import": "*"
+ },
+ {
+ "name": "@videojs/utils/time",
+ "path": "packages/utils/dist/time.js",
+ "import": "*"
}
]
diff --git a/package.json b/package.json
index f5b72a57..6e4ea38d 100644
--- a/package.json
+++ b/package.json
@@ -35,18 +35,19 @@
"typecheck": "tsc --build"
},
"devDependencies": {
- "@size-limit/preset-small-lib": "^12.0.0",
"@biomejs/biome": "^2.3.11",
"@commitlint/cli": "^20.1.0",
"@commitlint/config-conventional": "^20.0.0",
"@commitlint/format": "^20.0.0",
+ "@size-limit/esbuild": "^12.0.0",
+ "@size-limit/file": "^12.0.0",
"@types/node": "^22.18.6",
"lint-staged": "^16.2.3",
"react": "^18.0.0",
"react-compiler-runtime": "^1.0.0",
"react-dom": "^18.0.0",
- "size-limit": "^12.0.0",
"simple-git-hooks": "^2.13.1",
+ "size-limit": "^12.0.0",
"tsx": "^4.21.0",
"turbo": "^2.5.8",
"typescript": "^5.9.3"
diff --git a/packages/html/package.json b/packages/html/package.json
index 9299f322..a9273dcd 100644
--- a/packages/html/package.json
+++ b/packages/html/package.json
@@ -4,13 +4,14 @@
"version": "0.1.0-alpha.1",
"description": "HTML library for building media players",
"license": "Apache-2.0",
- "keywords": [
- "media",
- "player",
- "html",
- "ui",
- "components",
- "videojs"
+ "main": "dist/default/index.js",
+ "module": "dist/default/index.js",
+ "types": "dist/dev/index.d.ts",
+ "sideEffects": [
+ "./dist/*/define/**/*.js"
+ ],
+ "files": [
+ "dist"
],
"exports": {
".": {
@@ -49,15 +50,6 @@
"default": "./dist/default/define/media/*.js"
}
},
- "sideEffects": [
- "./dist/*/define/**/*.js"
- ],
- "main": "dist/default/index.js",
- "module": "dist/default/index.js",
- "types": "dist/dev/index.d.ts",
- "files": [
- "dist"
- ],
"scripts": {
"build": "tsdown",
"build:watch": "tsdown --watch ./src --no-clean",
@@ -81,5 +73,13 @@
},
"publishConfig": {
"access": "public"
- }
+ },
+ "keywords": [
+ "media",
+ "player",
+ "html",
+ "ui",
+ "components",
+ "videojs"
+ ]
}
diff --git a/packages/react/package.json b/packages/react/package.json
index 477750c7..97265e36 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -4,13 +4,12 @@
"version": "0.1.0-alpha.1",
"description": "React library for building media players",
"license": "Apache-2.0",
- "keywords": [
- "media",
- "player",
- "react",
- "components",
- "hooks",
- "videojs"
+ "main": "dist/default/index.js",
+ "module": "dist/default/index.js",
+ "types": "dist/dev/index.d.ts",
+ "sideEffects": false,
+ "files": [
+ "dist"
],
"exports": {
".": {
@@ -19,12 +18,6 @@
"default": "./dist/default/index.js"
}
},
- "main": "dist/default/index.js",
- "module": "dist/default/index.js",
- "types": "dist/dev/index.d.ts",
- "files": [
- "dist"
- ],
"scripts": {
"build": "tsdown",
"build:watch": "tsdown --watch ./src --no-clean",
@@ -53,5 +46,13 @@
},
"publishConfig": {
"access": "public"
- }
+ },
+ "keywords": [
+ "media",
+ "player",
+ "react",
+ "components",
+ "hooks",
+ "videojs"
+ ]
}
diff --git a/packages/store/package.json b/packages/store/package.json
index e3d34cde..8e31219d 100644
--- a/packages/store/package.json
+++ b/packages/store/package.json
@@ -4,12 +4,12 @@
"version": "0.1.0-alpha.1",
"description": "Reactive state management for external systems.",
"license": "Apache-2.0",
- "keywords": [
- "store",
- "media",
- "player",
- "videojs",
- "reactive"
+ "main": "dist/default/index.js",
+ "module": "dist/default/index.js",
+ "types": "dist/dev/index.d.ts",
+ "sideEffects": false,
+ "files": [
+ "dist"
],
"exports": {
".": {
@@ -28,12 +28,6 @@
"default": "./dist/default/react.js"
}
},
- "main": "dist/default/index.js",
- "module": "dist/default/index.js",
- "types": "dist/dev/index.d.ts",
- "files": [
- "dist"
- ],
"scripts": {
"build": "tsdown",
"build:watch": "tsdown --watch ./src --no-clean",
@@ -75,5 +69,12 @@
},
"publishConfig": {
"access": "public"
- }
+ },
+ "keywords": [
+ "store",
+ "media",
+ "player",
+ "videojs",
+ "reactive"
+ ]
}
diff --git a/packages/utils/package.json b/packages/utils/package.json
index 72b97f91..5e25dbaf 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -4,11 +4,7 @@
"version": "0.1.0-alpha.1",
"description": "Utility functions and helpers for Video.js",
"license": "Apache-2.0",
- "keywords": [
- "media",
- "utils",
- "videojs"
- ],
+ "sideEffects": false,
"exports": {
"./array": {
"types": "./dist/array.d.ts",
@@ -61,5 +57,10 @@
},
"publishConfig": {
"access": "public"
- }
+ },
+ "keywords": [
+ "media",
+ "utils",
+ "videojs"
+ ]
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 555245d4..96282fd1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,7 +20,10 @@ importers:
'@commitlint/format':
specifier: ^20.0.0
version: 20.2.0
- '@size-limit/preset-small-lib':
+ '@size-limit/esbuild':
+ specifier: ^12.0.0
+ version: 12.0.0(size-limit@12.0.0(jiti@2.6.1))
+ '@size-limit/file':
specifier: ^12.0.0
version: 12.0.0(size-limit@12.0.0(jiti@2.6.1))
'@types/node':
@@ -2518,11 +2521,6 @@ packages:
peerDependencies:
size-limit: 12.0.0
- '@size-limit/preset-small-lib@12.0.0':
- resolution: {integrity: sha512-HHHVQjZmj+8vg7qsHs1dd3Hmn8ygUsE5O2CfxnbCbHOGyUw7VodZGERh/+5ogVrF2DYza/DIo2PnCJZZETdTRA==}
- peerDependencies:
- size-limit: 12.0.0
-
'@so-ric/colorspace@1.1.6':
resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==}
@@ -6095,7 +6093,7 @@ packages:
tar@7.5.2:
resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==}
engines: {node: '>=18'}
- deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me
+ deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
tar@7.5.7:
resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==}
@@ -9134,12 +9132,6 @@ snapshots:
dependencies:
size-limit: 12.0.0(jiti@2.6.1)
- '@size-limit/preset-small-lib@12.0.0(size-limit@12.0.0(jiti@2.6.1))':
- dependencies:
- '@size-limit/esbuild': 12.0.0(size-limit@12.0.0(jiti@2.6.1))
- '@size-limit/file': 12.0.0(size-limit@12.0.0(jiti@2.6.1))
- size-limit: 12.0.0(jiti@2.6.1)
-
'@so-ric/colorspace@1.1.6':
dependencies:
color: 5.0.3
@@ -9709,7 +9701,7 @@ snapshots:
sirv: 3.0.2
tinyglobby: 0.2.15
tinyrainbow: 2.0.0
- vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.3)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
+ vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.3)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@27.3.0(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
'@vitest/utils@3.2.4':
dependencies: