fix(build): replace regex HTML minifier

This commit is contained in:
Timo
2026-07-14 16:30:13 +02:00
parent 6e5a790707
commit 8b6a1412cc
3 changed files with 435 additions and 38 deletions
+404 -23
View File
@@ -14,11 +14,34 @@
"esbuild": "^0.28.1",
"eslint": "^10.4.0",
"fs-extra": "^11.2.0",
"htmlnano": "^3.4.0",
"sharp": "^0.34.5",
"svgo": "^4.0.1",
"vitest": "^4.1.9"
}
},
"node_modules/@babel/code-frame": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.29.7",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/code-frame/node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@babel/helper-string-parser": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
@@ -79,29 +102,6 @@
"node": ">=18"
}
},
"node_modules/@emnapi/core": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
"integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.2",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
"integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/wasi-threads": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
@@ -1680,12 +1680,20 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/relateurl": {
"version": "0.2.33",
"resolved": "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.33.tgz",
"integrity": "sha512-bTQCKsVbIdzLqZhLkF5fcJQreE4y1ro4DIyVrlDNSCJRRwHhB8Z+4zXXa8jN6eDvc2HbRsEYgbvrnGvi54EpSw==",
"dev": true,
"license": "MIT"
},
"node_modules/@vitest/coverage-v8": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz",
"integrity": "sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@bcoe/v8-coverage": "^1.0.2",
"@vitest/utils": "4.1.9",
@@ -1843,6 +1851,7 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -1941,6 +1950,13 @@
"node": ">= 14"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/assertion-error": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
@@ -1998,6 +2014,7 @@
"integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"peerDependencies": {
"bare-abort-controller": "*"
},
@@ -2162,6 +2179,16 @@
"node": ">=8.0.0"
}
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/chai": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
@@ -2233,6 +2260,33 @@
"dev": true,
"license": "MIT"
},
"node_modules/cosmiconfig": {
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz",
"integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==",
"dev": true,
"license": "MIT",
"dependencies": {
"env-paths": "^2.2.1",
"import-fresh": "^3.3.0",
"js-yaml": "^4.1.0",
"parse-json": "^5.2.0"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
},
"peerDependencies": {
"typescript": ">=4.9.5"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
@@ -2476,6 +2530,26 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/env-paths": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/error-ex": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
"integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-arrayish": "^0.2.1"
}
},
"node_modules/es-module-lexer": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.2.0.tgz",
@@ -2490,6 +2564,7 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"bin": {
"esbuild": "bin/esbuild"
},
@@ -2544,6 +2619,7 @@
"integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.2",
@@ -2982,6 +3058,158 @@
"dev": true,
"license": "MIT"
},
"node_modules/htmlnano": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-3.4.0.tgz",
"integrity": "sha512-5rgW9c/830dlDiWLlsT3ZLBs52UAupymGwkwr4rn2yyuzrNY0RZwgr6F4q2AkUqrRuuHb3kPelqYsUgtzQDtQw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/relateurl": "^0.2.33",
"commander": "^15.0.0",
"cosmiconfig": "^9.0.0",
"posthtml": "^0.16.5",
"tinyglobby": "^0.2.17"
},
"bin": {
"htmlnano": "dist/bin.js"
},
"peerDependencies": {
"cssnano": "^7.0.0 || ^8.0.0",
"postcss": "^8.3.11",
"purgecss": "^8.0.0",
"relateurl": "^0.2.7",
"srcset": "^5.0.1",
"svgo": "^4.0.0",
"terser": "^5.21.0",
"uncss": "^0.17.3"
},
"peerDependenciesMeta": {
"cssnano": {
"optional": true
},
"postcss": {
"optional": true
},
"purgecss": {
"optional": true
},
"relateurl": {
"optional": true
},
"srcset": {
"optional": true
},
"svgo": {
"optional": true
},
"terser": {
"optional": true
},
"uncss": {
"optional": true
}
}
},
"node_modules/htmlnano/node_modules/commander": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz",
"integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=22.12.0"
}
},
"node_modules/htmlparser2": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
"integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==",
"dev": true,
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "MIT",
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.2",
"domutils": "^2.8.0",
"entities": "^3.0.1"
}
},
"node_modules/htmlparser2/node_modules/dom-serializer": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
"dev": true,
"license": "MIT",
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/htmlparser2/node_modules/dom-serializer/node_modules/entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"dev": true,
"license": "BSD-2-Clause",
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/htmlparser2/node_modules/domhandler": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^2.2.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/htmlparser2/node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/htmlparser2/node_modules/entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -3013,6 +3241,23 @@
"node": ">= 4"
}
},
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -3030,6 +3275,13 @@
"dev": true,
"license": "ISC"
},
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
"dev": true,
"license": "MIT"
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -3063,6 +3315,13 @@
"node": ">=0.10.0"
}
},
"node_modules/is-json": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz",
"integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==",
"dev": true,
"license": "ISC"
},
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -3152,6 +3411,29 @@
"dev": true,
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -3159,6 +3441,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"dev": true,
"license": "MIT"
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -3517,6 +3806,13 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true,
"license": "MIT"
},
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -3745,6 +4041,38 @@
"dev": true,
"license": "BlueOak-1.0.0"
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
"json-parse-even-better-errors": "^2.3.0",
"lines-and-columns": "^1.1.6"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -3885,6 +4213,46 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/posthtml": {
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.7.tgz",
"integrity": "sha512-7Hc+IvlQ7hlaIfQFZnxlRl0jnpWq2qwibORBhQYIb0QbNtuicc5ZxvKkVT71HJ4Py1wSZ/3VR1r8LfkCtoCzhw==",
"dev": true,
"license": "MIT",
"dependencies": {
"posthtml-parser": "^0.11.0",
"posthtml-render": "^3.0.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/posthtml-parser": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz",
"integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==",
"dev": true,
"license": "MIT",
"dependencies": {
"htmlparser2": "^7.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/posthtml-render": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz",
"integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-json": "^2.0.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -3962,6 +4330,16 @@
"node": ">=10"
}
},
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/rolldown": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz",
@@ -4297,6 +4675,7 @@
"integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"commander": "^11.1.0",
"css-select": "^5.1.0",
@@ -4448,6 +4827,7 @@
"integrity": "sha512-6YYPbRXTxx6bRXmOn7XdnQAy5DQNHhDgtjhDHI13oe4pY93kkcdGJWxpGwOm++/Wh0QpQhDrpIoVMrmrsI5AGQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"lightningcss": "^1.32.0",
"picomatch": "^4.0.4",
@@ -4526,6 +4906,7 @@
"integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/expect": "4.1.9",
"@vitest/mocker": "4.1.9",
+1
View File
@@ -21,6 +21,7 @@
"esbuild": "^0.28.1",
"eslint": "^10.4.0",
"fs-extra": "^11.2.0",
"htmlnano": "^3.4.0",
"sharp": "^0.34.5",
"svgo": "^4.0.1",
"vitest": "^4.1.9"
+30 -15
View File
@@ -7,6 +7,7 @@ const path = require('path');
const crypto = require('crypto');
const sharp = require('sharp');
const esbuild = require('esbuild');
const htmlnano = require('htmlnano');
const { optimize: svgoOptimize } = require('svgo');
// CSS minifier: simple regex-based (proven, 27% reduction, no deps)
@@ -24,16 +25,29 @@ function minifyCSS(code) {
if (minifyCSS('.parent :is(.a, .b) { color: red; }') !== '.parent :is(.a,.b){color:red}') {
throw new Error('CSS minifier must preserve descendant combinators before functional pseudo-classes');
}
// HTML minifier: comments + indentation only. Newlines are preserved so
// inline scripts/JSON-LD stay valid; <pre> blocks keep their formatting.
function minifyHTML(html) {
return html.split(/(<pre\b[\s\S]*?<\/pre>)/).map((part, i) => {
if (i % 2 === 1) return part;
return part
.replace(/<!--(?!\[)[\s\S]*?-->/g, '')
.replace(/\n[ \t]+/g, '\n')
.replace(/\n{2,}/g, '\n');
}).join('');
// HTML only: parser-based comment removal and conservative whitespace collapse.
// CSS, JS, JSON, and SVG keep their dedicated build steps below.
const HTML_MINIFY_OPTIONS = Object.freeze({
skipConfigLoading: true,
removeComments: 'safe',
collapseWhitespace: 'conservative'
});
const HTML_MINIFY_PRESET = Object.freeze({});
async function minifyHTML(html) {
return (await htmlnano.process(html, HTML_MINIFY_OPTIONS, HTML_MINIFY_PRESET)).html;
}
async function verifyHTMLMinifier() {
const pre = '<pre> keep\n exactly </pre>';
const jsonLd = '<script type="application/ld+json">{"name":"KoalaSync"}</script>';
const conditional = '<!--[if IE]>legacy<![endif]-->';
const result = await minifyHTML(`<!<!--nested-->--unsafe-->${pre}${jsonLd}${conditional}`);
const ordinaryComments = result.replace(conditional, '');
if (ordinaryComments.includes('<!--') || !result.includes(pre) || !result.includes(conditional)) {
throw new Error('HTML minifier security/preservation invariant failed');
}
JSON.parse(result.match(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/)[1]);
}
// ── Country-flag font subset (committed artifact, validated here) ────────
@@ -144,6 +158,7 @@ function minifyInlineSvgs(html) {
}
async function compile() {
await verifyHTMLMinifier();
console.log('Starting KoalaSync i18n compilation...');
const websiteDir = __dirname;
const wwwDir = path.join(websiteDir, 'www');
@@ -611,15 +626,15 @@ async function compile() {
// ── 8. Post-process ALL HTML files ──
console.log('Post-processing HTML...');
function walkHtml(dir, fn) {
async function walkHtml(dir, fn) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const p = path.join(dir, entry.name);
if (entry.isDirectory()) { walkHtml(p, fn); }
else if (entry.name.endsWith('.html')) { fn(p); }
if (entry.isDirectory()) { await walkHtml(p, fn); }
else if (entry.name.endsWith('.html')) { await fn(p); }
}
}
walkHtml(wwwDir, (filePath) => {
await walkHtml(wwwDir, async (filePath) => {
let html = fs.readFileSync(filePath, 'utf8');
// 8a. Replace hashed asset refs and inject SRI (Subresource Integrity)
@@ -661,7 +676,7 @@ async function compile() {
}
// 8e. Minify HTML (comments + indentation)
html = minifyHTML(html);
html = await minifyHTML(html);
fs.writeFileSync(filePath, html);
});