From f01b3783a139d56f55fff5f22637a26c3703742a Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 27 Mar 2026 10:19:02 -0600 Subject: [PATCH 1/7] add --minify option to run minifiers on ecmarkup outputs and assets --- package-lock.json | 792 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 8 +- src/args.ts | 5 + src/cli.ts | 8 + src/minify.ts | 75 +++++ test/cli.ts | 15 + 6 files changed, 870 insertions(+), 33 deletions(-) create mode 100644 src/minify.ts diff --git a/package-lock.json b/package-lock.json index db727516..7f064544 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "chalk": "^4.1.2", + "clean-css": "^5.3.3", "command-line-args": "^5.2.0", "command-line-usage": "^6.1.1", "dedent-js": "^1.0.1", @@ -19,20 +20,25 @@ "grammarkdown": "^3.3.2", "highlight.js": "11.0.1", "html-escape": "^1.0.2", + "html-minifier-terser": "^7.2.0", "js-yaml": "^3.13.1", "jsdom": "^25.0.1", "nwsapi": "2.2.0", "parse5": "^6.0.1", "prex": "^0.4.7", - "promise-debounce": "^1.0.1" + "promise-debounce": "^1.0.1", + "svgo": "^4.0.1", + "terser": "^5.46.1" }, "bin": { "ecmarkup": "bin/ecmarkup.js", "emu-format": "bin/emu-format.js" }, "devDependencies": { + "@types/clean-css": "^4.2.11", "@types/command-line-args": "^5.2.0", "@types/command-line-usage": "^5.0.2", + "@types/html-minifier-terser": "^7.0.2", "@types/js-yaml": "^3.12.1", "@types/jsdom": "^16.2.13", "@types/node": "^24.10.13", @@ -47,7 +53,7 @@ "typescript": "^5.5.4" }, "engines": { - "node": ">= 18" + "node": ">= 24" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -353,6 +359,51 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -397,6 +448,17 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@types/clean-css": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.11.tgz", + "integrity": "sha512-Y8n81lQVTAfP2TOdtJJEsCoYl1AnOkqDqMvXb9/7pfgZZ7r8YrEyurrAvAoAjHOGXKRybay+5CsExqIH6liccw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "source-map": "^0.6.0" + } + }, "node_modules/@types/command-line-args": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.0.tgz", @@ -409,6 +471,13 @@ "integrity": "sha512-n7RlEEJ+4x4TS7ZQddTmNSxP+zziEG0TNsMfiRIxcIVXt71ENJ9ojeXmGO3wPoTdn7pJcU2xc3CJYMktNT6DPg==", "dev": true }, + "node_modules/@types/html-minifier-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-7.0.2.tgz", + "integrity": "sha512-mm2HqV22l8lFQh4r2oSsOEVea+m0qqxEmwpc9kC1p/XzmjLWrReR9D/GRs8Pex2NX/imyEH9c5IU/7tMBQCHOA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/js-yaml": { "version": "3.12.7", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.7.tgz", @@ -671,11 +740,10 @@ "dev": true }, "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "peer": true, + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -786,6 +854,12 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -807,6 +881,12 @@ "node": ">=8" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -829,6 +909,16 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "node_modules/camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", @@ -853,6 +943,18 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, "node_modules/cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -1002,6 +1104,15 @@ "node": ">=8" } }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1022,6 +1133,80 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, "node_modules/cssstyle": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", @@ -1129,6 +1314,71 @@ "node": ">=6.0.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/ecmarkdown": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/ecmarkdown/-/ecmarkdown-8.1.0.tgz", @@ -1426,18 +1676,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -1785,6 +2023,27 @@ "resolved": "https://registry.npmjs.org/html-escape/-/html-escape-1.0.2.tgz", "integrity": "sha1-X6eHwFaAkP4zLtWzz0qk9kZCGnQ=" }, + "node_modules/html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -2120,6 +2379,21 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2182,6 +2456,16 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -2203,6 +2487,18 @@ "semver": "bin/semver" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -2312,6 +2608,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2341,6 +2647,16 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", @@ -2377,6 +2693,12 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -2545,6 +2867,15 @@ "node": ">=6" } }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2669,6 +3000,15 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -2721,6 +3061,34 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -2813,6 +3181,40 @@ "node": ">=8" } }, + "node_modules/svgo": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", + "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -2864,6 +3266,30 @@ "node": ">=8" } }, + "node_modules/terser": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", + "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3516,6 +3942,43 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "requires": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" + }, + "@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3545,6 +4008,16 @@ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true }, + "@types/clean-css": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.11.tgz", + "integrity": "sha512-Y8n81lQVTAfP2TOdtJJEsCoYl1AnOkqDqMvXb9/7pfgZZ7r8YrEyurrAvAoAjHOGXKRybay+5CsExqIH6liccw==", + "dev": true, + "requires": { + "@types/node": "*", + "source-map": "^0.6.0" + } + }, "@types/command-line-args": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.0.tgz", @@ -3557,6 +4030,12 @@ "integrity": "sha512-n7RlEEJ+4x4TS7ZQddTmNSxP+zziEG0TNsMfiRIxcIVXt71ENJ9ojeXmGO3wPoTdn7pJcU2xc3CJYMktNT6DPg==", "dev": true }, + "@types/html-minifier-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-7.0.2.tgz", + "integrity": "sha512-mm2HqV22l8lFQh4r2oSsOEVea+m0qqxEmwpc9kC1p/XzmjLWrReR9D/GRs8Pex2NX/imyEH9c5IU/7tMBQCHOA==", + "dev": true + }, "@types/js-yaml": { "version": "3.12.7", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.7.tgz", @@ -3718,11 +4197,9 @@ "dev": true }, "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "peer": true + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==" }, "acorn-jsx": { "version": "5.3.2", @@ -3804,6 +4281,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3822,6 +4304,11 @@ "fill-range": "^7.0.1" } }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -3838,6 +4325,15 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", @@ -3853,6 +4349,14 @@ "supports-color": "^7.1.0" } }, + "clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "requires": { + "source-map": "~0.6.0" + } + }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -3973,6 +4477,11 @@ } } }, + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3990,6 +4499,56 @@ "which": "^2.0.1" } }, + "css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + } + }, + "css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "requires": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + } + }, + "css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==" + }, + "csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "requires": { + "css-tree": "~2.2.0" + }, + "dependencies": { + "css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "requires": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + } + }, + "mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" + } + } + }, "cssstyle": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", @@ -4065,6 +4624,48 @@ "esutils": "^2.0.2" } }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "ecmarkdown": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/ecmarkdown/-/ecmarkdown-8.1.0.tgz", @@ -4266,14 +4867,6 @@ "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" - }, - "dependencies": { - "acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true - } } }, "esprima": { @@ -4542,6 +5135,20 @@ "resolved": "https://registry.npmjs.org/html-escape/-/html-escape-1.0.2.tgz", "integrity": "sha1-X6eHwFaAkP4zLtWzz0qk9kZCGnQ=" }, + "html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", + "requires": { + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" + } + }, "http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -4795,6 +5402,19 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + } + }, + "mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==" + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4842,6 +5462,15 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -4862,6 +5491,14 @@ } } }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "requires": { + "boolbase": "^1.0.0" + } + }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -4941,6 +5578,15 @@ "p-limit": "^3.0.2" } }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4964,6 +5610,15 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", @@ -4991,6 +5646,11 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -5101,6 +5761,11 @@ "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==" }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -5180,6 +5845,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==" + }, "saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -5215,6 +5885,25 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -5291,6 +5980,27 @@ } } }, + "svgo": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", + "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==", + "requires": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "dependencies": { + "commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==" + } + } + }, "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -5329,6 +6039,24 @@ } } }, + "terser": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", + "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", + "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/package.json b/package.json index d5c0fdb5..88f1d537 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "license": "MIT", "dependencies": { "chalk": "^4.1.2", + "clean-css": "^5.3.3", "command-line-args": "^5.2.0", "command-line-usage": "^6.1.1", "dedent-js": "^1.0.1", @@ -59,16 +60,21 @@ "grammarkdown": "^3.3.2", "highlight.js": "11.0.1", "html-escape": "^1.0.2", + "html-minifier-terser": "^7.2.0", "js-yaml": "^3.13.1", "jsdom": "^25.0.1", "nwsapi": "2.2.0", "parse5": "^6.0.1", "prex": "^0.4.7", - "promise-debounce": "^1.0.1" + "promise-debounce": "^1.0.1", + "svgo": "^4.0.1", + "terser": "^5.46.1" }, "devDependencies": { + "@types/clean-css": "^4.2.11", "@types/command-line-args": "^5.2.0", "@types/command-line-usage": "^5.0.2", + "@types/html-minifier-terser": "^7.0.2", "@types/js-yaml": "^3.12.1", "@types/jsdom": "^16.2.13", "@types/node": "^24.10.13", diff --git a/src/args.ts b/src/args.ts index 9c4453cc..19ddd520 100644 --- a/src/args.ts +++ b/src/args.ts @@ -59,6 +59,11 @@ export const options = [ type: Boolean, description: 'Enforce some style and correctness checks', }, + { + name: 'minify', + type: Boolean, + description: 'Minify generated HTML, CSS, and SVG output', + }, { name: 'error-formatter', type: String, diff --git a/src/cli.ts b/src/cli.ts index 0cf037da..253f2d9a 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -8,6 +8,7 @@ import * as utils from './utils'; import { options } from './args'; import { parse } from './arg-parser'; import type { ExportedBiblio } from './Biblio'; +import { minifyGeneratedFiles } from './minify'; const debounce: (_: () => Promise) => () => Promise = require('promise-debounce'); @@ -182,6 +183,13 @@ const build = debounce(async function build() { const spec = await ecmarkup.build(args.files[0], utils.readFile, opts); + if (args.minify) { + spec.generatedFiles = await minifyGeneratedFiles( + spec.generatedFiles, + args.verbose ? utils.logVerbose : undefined, + ); + } + if (args.verbose) { utils.logVerbose(warned ? 'Completed with errors.' : 'Done.'); } diff --git a/src/minify.ts b/src/minify.ts new file mode 100644 index 00000000..d4625654 --- /dev/null +++ b/src/minify.ts @@ -0,0 +1,75 @@ +import { minify as htmlMinify } from 'html-minifier-terser'; +import * as CleanCSS from 'clean-css'; +import { optimize as svgoOptimize, type Config as SvgoConfig, type PluginConfig } from 'svgo'; +import { minify as terserMinify } from 'terser'; + +type GeneratedFiles = Map; +type Log = (str: string) => void; + +const htmlMinifierOptions = { + caseSensitive: true, + collapseBooleanAttributes: true, + collapseWhitespace: true, + decodeEntities: true, + html5: true, + minifyCSS: true, + minifyJS: true, + removeAttributeQuotes: true, + removeComments: true, + removeEmptyAttributes: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + sortAttributes: true, + sortClassName: true, + useShortDoctype: true, +}; + +const cleanCss = new CleanCSS({ level: 2 }); + +const svgoConfig: SvgoConfig = { + multipass: true, + plugins: [ + { + name: 'preset-default', + params: { overrides: { removeViewBox: false } }, + } as PluginConfig, + ], +}; + +export async function minifyGeneratedFiles( + files: GeneratedFiles, + log?: Log, +): Promise { + const result: GeneratedFiles = new Map(); + + for (const [key, value] of files) { + const ext = key != null ? key.substring(key.lastIndexOf('.')) : null; + + if (ext === '.html' || key === null) { + const html = typeof value === 'string' ? value : (value as Buffer).toString('utf-8'); + log?.(`Minifying ${key ?? 'stdout'}...`); + const minified = await htmlMinify(html, htmlMinifierOptions); + result.set(key, minified); + } else if (ext === '.css') { + const css = typeof value === 'string' ? value : (value as Buffer).toString('utf-8'); + log?.(`Minifying ${key}...`); + const output = cleanCss.minify(css); + result.set(key, output.styles); + } else if (ext === '.svg') { + const svg = typeof value === 'string' ? value : (value as Buffer).toString('utf-8'); + log?.(`Minifying ${key}...`); + const optimized = svgoOptimize(svg, svgoConfig); + result.set(key, Buffer.from(optimized.data, 'utf-8')); + } else if (ext === '.js') { + const js = typeof value === 'string' ? value : (value as Buffer).toString('utf-8'); + log?.(`Minifying ${key}...`); + const output = await terserMinify(js); + result.set(key, output.code!); + } else { + result.set(key, value); + } + } + + return result; +} diff --git a/test/cli.ts b/test/cli.ts index 6d7c918f..72363406 100644 --- a/test/cli.ts +++ b/test/cli.ts @@ -35,6 +35,21 @@ describe('ecmarkup#cli', { timeout: 4000 }, () => { }); }); }); + + it('--minify produces smaller output', () => { + const normal = execSync( + `${execPath} ./bin/ecmarkup.js test/baselines/sources/example.html`, + { encoding: 'utf8' }, + ); + const minified = execSync( + `${execPath} ./bin/ecmarkup.js --minify test/baselines/sources/example.html`, + { encoding: 'utf8' }, + ); + assert( + minified.length < normal.length, + `Expected minified (${minified.length}) to be smaller than normal (${normal.length})`, + ); + }); }); describe('emu-format --check', { timeout: 4000 }, () => { From de54a71a66c1e53fa35d9c3be9370bbafcde0b07 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 27 Mar 2026 10:29:31 -0600 Subject: [PATCH 2/7] use path.extname --- src/minify.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/minify.ts b/src/minify.ts index d4625654..2beaa9e1 100644 --- a/src/minify.ts +++ b/src/minify.ts @@ -2,6 +2,7 @@ import { minify as htmlMinify } from 'html-minifier-terser'; import * as CleanCSS from 'clean-css'; import { optimize as svgoOptimize, type Config as SvgoConfig, type PluginConfig } from 'svgo'; import { minify as terserMinify } from 'terser'; +import * as path from 'path'; type GeneratedFiles = Map; type Log = (str: string) => void; @@ -44,7 +45,7 @@ export async function minifyGeneratedFiles( const result: GeneratedFiles = new Map(); for (const [key, value] of files) { - const ext = key != null ? key.substring(key.lastIndexOf('.')) : null; + const ext = key != null ? path.extname(key) : null; if (ext === '.html' || key === null) { const html = typeof value === 'string' ? value : (value as Buffer).toString('utf-8'); From b4a85256bbc213abb1708893bad75d6a15baca0c Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 27 Mar 2026 10:32:16 -0600 Subject: [PATCH 3/7] enable minification by default --- src/args.ts | 4 ++-- src/cli.ts | 2 +- test/cli.ts | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/args.ts b/src/args.ts index 19ddd520..ce49ec0d 100644 --- a/src/args.ts +++ b/src/args.ts @@ -60,9 +60,9 @@ export const options = [ description: 'Enforce some style and correctness checks', }, { - name: 'minify', + name: 'no-minify', type: Boolean, - description: 'Minify generated HTML, CSS, and SVG output', + description: 'Disable minification of generated output', }, { name: 'error-formatter', diff --git a/src/cli.ts b/src/cli.ts index 253f2d9a..c0d00520 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -183,7 +183,7 @@ const build = debounce(async function build() { const spec = await ecmarkup.build(args.files[0], utils.readFile, opts); - if (args.minify) { + if (!args['no-minify']) { spec.generatedFiles = await minifyGeneratedFiles( spec.generatedFiles, args.verbose ? utils.logVerbose : undefined, diff --git a/test/cli.ts b/test/cli.ts index 72363406..4da56488 100644 --- a/test/cli.ts +++ b/test/cli.ts @@ -36,18 +36,18 @@ describe('ecmarkup#cli', { timeout: 4000 }, () => { }); }); - it('--minify produces smaller output', () => { - const normal = execSync( + it('minifies by default and --no-minify disables it', () => { + const minified = execSync( `${execPath} ./bin/ecmarkup.js test/baselines/sources/example.html`, { encoding: 'utf8' }, ); - const minified = execSync( - `${execPath} ./bin/ecmarkup.js --minify test/baselines/sources/example.html`, + const unminified = execSync( + `${execPath} ./bin/ecmarkup.js --no-minify test/baselines/sources/example.html`, { encoding: 'utf8' }, ); assert( - minified.length < normal.length, - `Expected minified (${minified.length}) to be smaller than normal (${normal.length})`, + minified.length < unminified.length, + `Expected minified (${minified.length}) to be smaller than unminified (${unminified.length})`, ); }); }); From 15e607cffe348eff86fd6c345402d020c85527b4 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 27 Mar 2026 11:17:18 -0600 Subject: [PATCH 4/7] fix lint errors --- src/minify.ts | 8 ++++---- test/cli.ts | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/minify.ts b/src/minify.ts index 2beaa9e1..197b02a1 100644 --- a/src/minify.ts +++ b/src/minify.ts @@ -48,22 +48,22 @@ export async function minifyGeneratedFiles( const ext = key != null ? path.extname(key) : null; if (ext === '.html' || key === null) { - const html = typeof value === 'string' ? value : (value as Buffer).toString('utf-8'); + const html = typeof value === 'string' ? value : value.toString('utf-8'); log?.(`Minifying ${key ?? 'stdout'}...`); const minified = await htmlMinify(html, htmlMinifierOptions); result.set(key, minified); } else if (ext === '.css') { - const css = typeof value === 'string' ? value : (value as Buffer).toString('utf-8'); + const css = typeof value === 'string' ? value : value.toString('utf-8'); log?.(`Minifying ${key}...`); const output = cleanCss.minify(css); result.set(key, output.styles); } else if (ext === '.svg') { - const svg = typeof value === 'string' ? value : (value as Buffer).toString('utf-8'); + const svg = typeof value === 'string' ? value : value.toString('utf-8'); log?.(`Minifying ${key}...`); const optimized = svgoOptimize(svg, svgoConfig); result.set(key, Buffer.from(optimized.data, 'utf-8')); } else if (ext === '.js') { - const js = typeof value === 'string' ? value : (value as Buffer).toString('utf-8'); + const js = typeof value === 'string' ? value : value.toString('utf-8'); log?.(`Minifying ${key}...`); const output = await terserMinify(js); result.set(key, output.code!); diff --git a/test/cli.ts b/test/cli.ts index 4da56488..ee1bb64e 100644 --- a/test/cli.ts +++ b/test/cli.ts @@ -37,10 +37,9 @@ describe('ecmarkup#cli', { timeout: 4000 }, () => { }); it('minifies by default and --no-minify disables it', () => { - const minified = execSync( - `${execPath} ./bin/ecmarkup.js test/baselines/sources/example.html`, - { encoding: 'utf8' }, - ); + const minified = execSync(`${execPath} ./bin/ecmarkup.js test/baselines/sources/example.html`, { + encoding: 'utf8', + }); const unminified = execSync( `${execPath} ./bin/ecmarkup.js --no-minify test/baselines/sources/example.html`, { encoding: 'utf8' }, From 3192ce37aadd9ffb90b8b975ef9a6fcd88fcfa20 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sat, 28 Mar 2026 08:06:56 -0600 Subject: [PATCH 5/7] run minifiers in ecmarkup.build, not just through the CLI --- src/Spec.ts | 5 +++++ src/cli.ts | 11 +++-------- src/ecmarkup.ts | 1 + test/baselines.ts | 1 + test/build.ts | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/Spec.ts b/src/Spec.ts index 5211bf76..122d3894 100644 --- a/src/Spec.ts +++ b/src/Spec.ts @@ -47,6 +47,7 @@ import { import { lint } from './lint/lint'; import { CancellationToken } from 'prex'; import type { JSDOM } from 'jsdom'; +import { minifyGeneratedFiles } from './minify'; import { getProductions, rhsMatches, getLocationInGrammarFile } from './lint/utils'; import type { AugmentedGrammarEle } from './Grammar'; import { zip } from './utils'; @@ -701,6 +702,10 @@ export default class Spec { : this.opts.outfile ?? null; this.generatedFiles.set(file, this.toHTML()); + if (this.opts.minify || this.opts.minify === undefined) { + this.generatedFiles = await minifyGeneratedFiles(this.generatedFiles, this.log); + } + return this; } diff --git a/src/cli.ts b/src/cli.ts index c0d00520..5136b459 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -8,7 +8,6 @@ import * as utils from './utils'; import { options } from './args'; import { parse } from './arg-parser'; import type { ExportedBiblio } from './Biblio'; -import { minifyGeneratedFiles } from './minify'; const debounce: (_: () => Promise) => () => Promise = require('promise-debounce'); @@ -130,6 +129,9 @@ const build = debounce(async function build() { if (args['assets-dir'] != null) { opts.assetsDir = args['assets-dir']; } + if (args['no-minify']) { + opts.minify = false; + } let warned = false; @@ -183,13 +185,6 @@ const build = debounce(async function build() { const spec = await ecmarkup.build(args.files[0], utils.readFile, opts); - if (!args['no-minify']) { - spec.generatedFiles = await minifyGeneratedFiles( - spec.generatedFiles, - args.verbose ? utils.logVerbose : undefined, - ); - } - if (args.verbose) { utils.logVerbose(warned ? 'Completed with errors.' : 'Done.'); } diff --git a/src/ecmarkup.ts b/src/ecmarkup.ts index d642a6fe..c4b8f331 100644 --- a/src/ecmarkup.ts +++ b/src/ecmarkup.ts @@ -41,6 +41,7 @@ export interface Options { printable?: boolean; markEffects?: boolean; lintSpec?: boolean; + minify?: boolean; cssOut?: never; jsOut?: never; assets?: 'none' | 'inline' | 'external'; diff --git a/test/baselines.ts b/test/baselines.ts index 47b286e7..a29e6d6b 100644 --- a/test/baselines.ts +++ b/test/baselines.ts @@ -26,6 +26,7 @@ function build(file: string, options: Options) { ), { extraBiblios: [ecma262biblio as ExportedBiblio], + minify: false, ...options, }, ); diff --git a/test/build.ts b/test/build.ts index 3fd21889..99b950c1 100644 --- a/test/build.ts +++ b/test/build.ts @@ -27,4 +27,42 @@ describe('ecmarkup#build', () => { assert.equal(typeof result, 'string'); assert(result.includes(`
`)); }); + + describe('minify option', () => { + async function buildWithMinify(minify?: boolean) { + const spec = await build('root.html', file => fetch(file), { + toc: false, + copyright: false, + assets: 'none', + minify, + }); + return spec.generatedFiles.get(null) as string; + } + + it('minifies by default when option is omitted', async () => { + const defaultOutput = await buildWithMinify(undefined); + const unminified = await buildWithMinify(false); + assert( + defaultOutput.length < unminified.length, + `Expected default (${defaultOutput.length}) to be smaller than unminified (${unminified.length})`, + ); + }); + + it('minifies when minify is true', async () => { + const minified = await buildWithMinify(true); + const unminified = await buildWithMinify(false); + assert( + minified.length < unminified.length, + `Expected minified (${minified.length}) to be smaller than unminified (${unminified.length})`, + ); + }); + + it('does not minify when minify is false', async () => { + const unminified = await buildWithMinify(false); + assert( + unminified.includes(`
`), + 'Expected unminified output to preserve whitespace and attribute quotes', + ); + }); + }); }); From 0babdb6dca519bae57d573a7a4efe70c7a8665a8 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sat, 28 Mar 2026 08:16:29 -0600 Subject: [PATCH 6/7] unnecessary Buffer.from Co-authored-by: Kevin Gibbons --- src/minify.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/minify.ts b/src/minify.ts index 197b02a1..28ebad1f 100644 --- a/src/minify.ts +++ b/src/minify.ts @@ -61,7 +61,7 @@ export async function minifyGeneratedFiles( const svg = typeof value === 'string' ? value : value.toString('utf-8'); log?.(`Minifying ${key}...`); const optimized = svgoOptimize(svg, svgoConfig); - result.set(key, Buffer.from(optimized.data, 'utf-8')); + result.set(key, optimized.data); } else if (ext === '.js') { const js = typeof value === 'string' ? value : value.toString('utf-8'); log?.(`Minifying ${key}...`); From 0c1cb8f07931f408c398499779fb0dccbb1bb61e Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sat, 28 Mar 2026 08:23:08 -0600 Subject: [PATCH 7/7] missing async --- test/build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/build.ts b/test/build.ts index 99b950c1..e03f0b87 100644 --- a/test/build.ts +++ b/test/build.ts @@ -30,7 +30,7 @@ describe('ecmarkup#build', () => { describe('minify option', () => { async function buildWithMinify(minify?: boolean) { - const spec = await build('root.html', file => fetch(file), { + const spec = await build('root.html', async file => fetch(file), { toc: false, copyright: false, assets: 'none',