]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Add a tabular bundlewatch script (#41957)
authorMark Otto <markd.otto@gmail.com>
Sun, 21 Dec 2025 19:16:35 +0000 (11:16 -0800)
committerMark Otto <markdotto@gmail.com>
Fri, 9 Jan 2026 04:14:08 +0000 (20:14 -0800)
* Script for better bundlewatch locally

* Fix linter

.bundlewatch.config.json
build/bundlewatch-table.mjs [new file with mode: 0644]
package.json

index 9e8e216ace41341af001207716e7852d2b40a1c8..52a4dcfb6d27c07c0fd4f66f92c545c78a1de1bc 100644 (file)
@@ -2,43 +2,43 @@
   "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",
@@ -50,7 +50,7 @@
     },
     {
       "path": "./dist/js/bootstrap.js",
-      "maxSize": "30.5 kB"
+      "maxSize": "30.25 kB"
     },
     {
       "path": "./dist/js/bootstrap.min.js",
diff --git a/build/bundlewatch-table.mjs b/build/bundlewatch-table.mjs
new file mode 100644 (file)
index 0000000..e475988
--- /dev/null
@@ -0,0 +1,93 @@
+#!/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)
index 6f7915dc19eea820ec7b94069e12420f7d500080..7417a776bd84a0857985a27f53ae7a4e82e93303 100644 (file)
@@ -41,6 +41,7 @@
   "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",