"files": [
{
"path": "./dist/css/bootstrap-grid.css",
- "maxSize": "10.0 kB"
+ "maxSize": "9.5 kB"
},
{
"path": "./dist/css/bootstrap-grid.min.css",
- "maxSize": "9.0 kB"
+ "maxSize": "8.5 kB"
},
{
"path": "./dist/css/bootstrap-reboot.css",
- "maxSize": "5.5 kB"
+ "maxSize": "5.25 kB"
},
{
"path": "./dist/css/bootstrap-reboot.min.css",
- "maxSize": "4.5 kB"
+ "maxSize": "4.25 kB"
},
{
"path": "./dist/css/bootstrap-utilities.css",
- "maxSize": "15.25 kB"
+ "maxSize": "14.5 kB"
},
{
"path": "./dist/css/bootstrap-utilities.min.css",
- "maxSize": "13.5 kB"
+ "maxSize": "12.75 kB"
},
{
"path": "./dist/css/bootstrap.css",
- "maxSize": "37.75 kB"
+ "maxSize": "35.5 kB"
},
{
"path": "./dist/css/bootstrap.min.css",
- "maxSize": "33.75 kB"
+ "maxSize": "32.0 kB"
},
{
"path": "./dist/js/bootstrap.bundle.js",
- "maxSize": "44.0 kB"
+ "maxSize": "43.5 kB"
},
{
"path": "./dist/js/bootstrap.bundle.min.js",
- "maxSize": "23.5 kB"
+ "maxSize": "22.5 kB"
},
{
"path": "./dist/js/bootstrap.esm.js",
},
{
"path": "./dist/js/bootstrap.js",
- "maxSize": "30.5 kB"
+ "maxSize": "30.25 kB"
},
{
"path": "./dist/js/bootstrap.min.js",
--- /dev/null
+#!/usr/bin/env node
+
+import { execSync } from 'node:child_process'
+
+// Run bundlewatch and capture output
+let stdout
+let exitCode = 0
+
+try {
+ stdout = execSync('npx bundlewatch --config .bundlewatch.config.json', {
+ encoding: 'utf8',
+ stdio: ['pipe', 'pipe', 'pipe']
+ })
+} catch (error) {
+ stdout = error.stdout || ''
+ exitCode = error.status || 1
+}
+
+// Parse lines that contain PASS or FAIL
+const lines = stdout.split('\n').filter(l => l.startsWith('PASS') || l.startsWith('FAIL'))
+
+if (lines.length === 0) {
+ console.log(stdout)
+ process.exit(exitCode)
+}
+
+// Parse size string to number (KB)
+const parseSize = str => Number.parseFloat(str.replace('KB', ''))
+
+// Calculate column widths and headroom
+const rows = lines.map(line => {
+ const match = line.match(/(PASS|FAIL)\s+(.+?):\s+([\d.]+KB)\s+([<>])\s+([\d.]+KB)/)
+ if (match) {
+ const sizeNum = parseSize(match[3])
+ const maxNum = parseSize(match[5])
+ const headroomNum = maxNum - sizeNum
+ const headroom = `${headroomNum.toFixed(2)}KB`
+ return {
+ status: match[1],
+ file: match[2],
+ size: match[3],
+ max: match[5],
+ headroomNum,
+ headroom: match[1] === 'PASS' ? `+${headroom}` : `-${Math.abs(headroomNum).toFixed(2)}KB`
+ }
+ }
+
+ return null
+}).filter(Boolean)
+
+const maxFileLen = Math.max(...rows.map(r => r.file.length), 4)
+const maxSizeLen = Math.max(...rows.map(r => r.size.length), 4)
+const maxMaxLen = Math.max(...rows.map(r => r.max.length), 3)
+const maxHeadroomLen = Math.max(...rows.map(r => r.headroom.length), 8)
+
+// Build table
+const hr = `+-${'-'.repeat(maxFileLen)}-+-${'-'.repeat(maxSizeLen)}-+-${'-'.repeat(maxMaxLen)}-+-${'-'.repeat(maxHeadroomLen)}-+`
+
+console.log('')
+console.log('bundlewatch results')
+console.log(hr)
+console.log(`| ${'File'.padEnd(maxFileLen)} | ${'Size'.padStart(maxSizeLen)} | ${'Max'.padStart(maxMaxLen)} | ${'Headroom'.padStart(maxHeadroomLen)} |`)
+console.log(hr)
+
+const green = '\u001B[32m'
+const red = '\u001B[31m'
+const reset = '\u001B[0m'
+
+for (const row of rows) {
+ const sizeColor = row.status === 'PASS' ? green : red
+ const coloredSize = `${sizeColor}${row.size.padStart(maxSizeLen)}${reset}`
+ const headroomColor = row.headroomNum > 0.25 ? red : ''
+ const headroomReset = row.headroomNum > 0.25 ? reset : ''
+ const coloredHeadroom = `${headroomColor}${row.headroom.padStart(maxHeadroomLen)}${headroomReset}`
+ console.log(`| ${row.file.padEnd(maxFileLen)} | ${coloredSize} | ${row.max.padStart(maxMaxLen)} | ${coloredHeadroom} |`)
+}
+
+console.log(hr)
+
+// Summary
+const passed = rows.filter(r => r.status === 'PASS').length
+const failed = rows.filter(r => r.status === 'FAIL').length
+
+console.log('')
+if (failed > 0) {
+ console.log(`\u001B[31mbundlewatch FAIL\u001B[0m - ${passed} passed, ${failed} failed`)
+} else {
+ console.log(`\u001B[32mbundlewatch PASS\u001B[0m - ${passed}/${rows.length} files within limits`)
+}
+
+console.log('')
+
+process.exit(exitCode)
"scripts": {
"start": "npm-run-all --parallel watch docs-serve",
"bundlewatch": "bundlewatch --config .bundlewatch.config.json",
+ "bundlewatch:table": "node build/bundlewatch-table.mjs",
"css": "npm-run-all css-compile css-prefix css-minify css-docs",
"css-compile": "sass --style expanded --source-map --embed-sources --no-error-css scss/bootstrap.scss:dist/css/bootstrap.css scss/bootstrap-grid.scss:dist/css/bootstrap-grid.css scss/bootstrap-reboot.scss:dist/css/bootstrap-reboot.css scss/bootstrap-utilities.scss:dist/css/bootstrap-utilities.css",
"css-docs": "node build/generate-utilities-json.mjs",