Quellcode durchsuchen

Refactor and tidy:

* Make all methods async
* Add template cache
* Clone for better sharp performance
* Minify HTML
* Add copy processor
Craig Fletcher vor 5 Monaten
Ursprung
Commit
8233d7aca0
5 geänderte Dateien mit 483 neuen und 99 gelöschten Zeilen
  1. 362 10
      package-lock.json
  2. 1 0
      package.json
  3. 11 2
      src/defaults.js
  4. 83 72
      src/processors.js
  5. 26 15
      src/util.js

+ 362 - 10
package-lock.json

@@ -11,6 +11,7 @@
       "dependencies": {
         "gray-matter": "^4.0.3",
         "handlebars": "^4.7.8",
+        "html-minifier-terser": "^7.2.0",
         "marked": "^15.0.6",
         "marked-code-preview": "^1.3.7",
         "sass": "^1.83.1",
@@ -620,6 +621,64 @@
         "url": "https://opencollective.com/libvips"
       }
     },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.8",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+      "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/set-array": "^1.2.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "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/set-array": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/source-map": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+      "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+      "license": "MIT"
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.25",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
     "node_modules/@mrmlnc/readdir-enhanced": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
@@ -828,7 +887,6 @@
       "version": "8.14.0",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
       "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
-      "dev": true,
       "bin": {
         "acorn": "bin/acorn"
       },
@@ -1172,6 +1230,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "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/cache-base": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
@@ -1271,6 +1335,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/caw": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz",
@@ -1365,6 +1439,18 @@
         "node": ">= 0.4"
       }
     },
+    "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/cli-cursor": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@@ -1466,7 +1552,6 @@
       "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==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/component-emitter": {
@@ -1928,6 +2013,16 @@
         "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/download": {
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/download/-/download-7.1.0.tgz",
@@ -3119,6 +3214,36 @@
         "node": ">= 0.4"
       }
     },
+    "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/html-minifier-terser/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/http-cache-semantics": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
@@ -3584,6 +3709,15 @@
         "node": ">=4"
       }
     },
+    "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/lowercase-keys": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@@ -3982,6 +4116,16 @@
         "node": ">=4"
       }
     },
+    "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/node-addon-api": {
       "version": "7.1.1",
       "license": "MIT",
@@ -4335,6 +4479,16 @@
         "node": ">=4"
       }
     },
+    "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",
@@ -4347,6 +4501,16 @@
         "node": ">=6"
       }
     },
+    "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/pascalcase": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -4652,6 +4816,15 @@
         "node": ">=0.10.0"
       }
     },
+    "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/repeat-element": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
@@ -5265,6 +5438,16 @@
         "urix": "^0.1.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/source-map-url": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
@@ -5524,6 +5707,24 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/terser": {
+      "version": "5.43.1",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
+      "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.14.0",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/through": {
       "version": "2.3.8",
       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -5664,8 +5865,7 @@
     "node_modules/tslib": {
       "version": "2.8.1",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
-      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
-      "optional": true
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
     },
     "node_modules/tunnel-agent": {
       "version": "0.6.0",
@@ -6231,6 +6431,49 @@
       "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==",
       "optional": true
     },
+    "@jridgewell/gen-mapping": {
+      "version": "0.3.8",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+      "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+      "requires": {
+        "@jridgewell/set-array": "^1.2.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@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/set-array": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="
+    },
+    "@jridgewell/source-map": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+      "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+      "requires": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25"
+      }
+    },
+    "@jridgewell/sourcemap-codec": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+    },
+    "@jridgewell/trace-mapping": {
+      "version": "0.3.25",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+      "requires": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
     "@mrmlnc/readdir-enhanced": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
@@ -6366,8 +6609,7 @@
     "acorn": {
       "version": "8.14.0",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
-      "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
-      "dev": true
+      "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="
     },
     "acorn-jsx": {
       "version": "5.3.2",
@@ -6597,6 +6839,11 @@
       "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==",
       "dev": true
     },
+    "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=="
+    },
     "cache-base": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
@@ -6673,6 +6920,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"
+      }
+    },
     "caw": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz",
@@ -6740,6 +6996,14 @@
         }
       }
     },
+    "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"
+      }
+    },
     "cli-cursor": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@@ -6814,8 +7078,7 @@
     "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==",
-      "dev": true
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
     },
     "component-emitter": {
       "version": "1.3.1",
@@ -7141,6 +7404,15 @@
         "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"
+      }
+    },
     "download": {
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/download/-/download-7.1.0.tgz",
@@ -8028,6 +8300,27 @@
         "function-bind": "^1.1.2"
       }
     },
+    "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"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "10.0.1",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+          "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="
+        }
+      }
+    },
     "http-cache-semantics": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
@@ -8362,6 +8655,14 @@
         }
       }
     },
+    "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"
+      }
+    },
     "lowercase-keys": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@@ -8653,6 +8954,15 @@
         }
       }
     },
+    "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"
+      }
+    },
     "node-addon-api": {
       "version": "7.1.1",
       "optional": true
@@ -8912,6 +9222,15 @@
         "p-finally": "^1.0.0"
       }
     },
+    "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",
@@ -8921,6 +9240,15 @@
         "callsites": "^3.0.0"
       }
     },
+    "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"
+      }
+    },
     "pascalcase": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -9130,6 +9458,11 @@
         }
       }
     },
+    "relateurl": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+      "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog=="
+    },
     "repeat-element": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
@@ -9553,6 +9886,15 @@
         "urix": "^0.1.0"
       }
     },
+    "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"
+      }
+    },
     "source-map-url": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
@@ -9741,6 +10083,17 @@
         "xtend": "^4.0.0"
       }
     },
+    "terser": {
+      "version": "5.43.1",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
+      "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
+      "requires": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.14.0",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      }
+    },
     "through": {
       "version": "2.3.8",
       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -9845,8 +10198,7 @@
     "tslib": {
       "version": "2.8.1",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
-      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
-      "optional": true
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
     },
     "tunnel-agent": {
       "version": "0.6.0",

+ 1 - 0
package.json

@@ -23,6 +23,7 @@
   "dependencies": {
     "gray-matter": "^4.0.3",
     "handlebars": "^4.7.8",
+    "html-minifier-terser": "^7.2.0",
     "marked": "^15.0.6",
     "marked-code-preview": "^1.3.7",
     "sass": "^1.83.1",

+ 11 - 2
src/defaults.js

@@ -2,7 +2,8 @@ import {
   compileSass,
   optimiseSvg,
   optimiseImage,
-  renderMarkdownWithTemplate
+  renderMarkdownWithTemplate,
+  copy
 } from "./processors.js";
 
 export const tasks = [
@@ -26,7 +27,7 @@ export const tasks = [
     inputDirs: ["images/content/"],
     inputFileExtension: ".jpg",
     name: "images",
-    outputDir: "images/",
+    outputDir: "static/images/",
     outputFileExtension: ".webp",
     processor: optimiseImage
   },
@@ -37,6 +38,14 @@ export const tasks = [
     outputDir: "./",
     outputFileExtension: ".html",
     processor: renderMarkdownWithTemplate
+  },
+  {
+    inputDirs: ["static/"],
+    inputFileExtension: "",
+    name: "static files",
+    outputDir: "",
+    outputFileExtension: "",
+    processor: copy
   }
 ];
 

+ 83 - 72
src/processors.js

@@ -1,6 +1,6 @@
 import * as sass from "sass";
-import { firstFound } from "./util.js";
-import fs from "fs";
+import { firstFound, stripFileExtension, getCleanPath } from "./util.js";
+import fs from "fs/promises";
 import handlebars from "handlebars";
 import { marked } from "marked";
 import markedCodePreview from "marked-code-preview";
@@ -8,6 +8,7 @@ import matter from "gray-matter";
 import { optimize } from "svgo";
 import sharp from "sharp";
 import path from "path";
+import { minify } from "html-minifier-terser";
 
 const imageSizes = [
   "640w",
@@ -18,98 +19,108 @@ const imageSizes = [
   "1920w",
   "2560w"
 ];
-
-function stripFileExtension(filePath) {
-  if (typeof filePath !== "string") return "";
-
-  const parts = filePath.split("/");
-  const fileName = parts.pop();
-
-  const nameWithoutExt = fileName.replace(/\.[^/.]+$/, "");
-
-  return [...parts, nameWithoutExt].join("/");
-}
-
-const renderer = meta => ({
-  image({ href, title, text }) {
-    const hrefWithoutFileExtension = stripFileExtension(href);
-    const attrs = [`alt="${text}"`];
-
-    const foundSrcSet = meta.resources.images.find(imageResource => {
-      return (
-        stripFileExtension(imageResource.path[0]) === hrefWithoutFileExtension
-      );
+const templateCache = new Map();
+
+function createMarkdownRenderer(meta) {
+  return marked
+    .use({ gfm: true })
+    .use(markedCodePreview)
+    .use({
+      renderer: {
+        image({ href, title, text }) {
+          const hrefWithoutExt = stripFileExtension(href);
+          const attrs = [`alt="${text}"`];
+
+          const foundSrcSet = meta.resources.images.find(({ path }) => {
+            return stripFileExtension(path[0]) === hrefWithoutExt;
+          });
+
+          if (foundSrcSet) {
+            const srcSetString = foundSrcSet.path[1]
+              .map(src => src.join(" "))
+              .join(", ");
+            const defaultSrc = foundSrcSet.path[1][0][0];
+            attrs.push(`src="${defaultSrc}"`);
+            attrs.push(`srcset="${srcSetString}"`);
+            attrs.push(`sizes="(min-width: 800px) 40vw, 100vw"`);
+            attrs.push(
+              `style="aspect-ratio: ${foundSrcSet.path[2].aspectRatio}"`
+            );
+          } else {
+            attrs.push(`src="${href}"`);
+          }
+
+          if (title) {
+            attrs.push(`title="${title}"`);
+          }
+
+          return `<img ${attrs.join(" ")} >`;
+        }
+      }
     });
-    if (foundSrcSet) {
-      const srcSetString = foundSrcSet.path[1]
-        .map(src => src.join(" "))
-        .join(", ");
-      const defaultSrc = foundSrcSet.path[1][0][0];
-      attrs.push(`src="${defaultSrc}"`);
-      attrs.push(`srcset="${srcSetString}"`);
-      attrs.push(
-        `sizes="(min-width: 1600px) 25vw, (min-width: 800px) 50vw, 100vw"`
-      );
-      attrs.push(`style="aspect-ratio: ${foundSrcSet.path[2].aspectRatio}"`);
-    } else {
-      attrs.push(`src="${href}"`);
-    }
-
-    if (title) {
-      attrs.push(`title="${title}"`);
-    }
-
-    return `<img ${attrs.join(" ")} >`;
-  }
-});
-
-const markedRenderer = marked.use({ gfm: true }).use(markedCodePreview);
+}
 
-export function renderMarkdownWithTemplate(filePath, meta) {
-  const content = fs.readFileSync(filePath, "utf8");
+export async function renderMarkdownWithTemplate(filePath, meta) {
+  const content = await fs.readFile(filePath, "utf8");
   const { data, content: markdown } = matter(content);
   const templateName = data.template || meta.opts.defaultTemplate;
 
-  const template = handlebars.compile(
-    fs.readFileSync(
-      firstFound(meta.opts.templateDirs, `${templateName}.hbs`),
-      "utf8"
-    )
-  );
+  if (!templateCache.has(templateName)) {
+    const templatePath = firstFound(
+      meta.opts.templateDirs,
+      `${templateName}.hbs`
+    );
+    if (!templatePath) throw new Error(`Template not found: ${templateName}`);
+    const templateContent = await fs.readFile(templatePath, "utf8");
+    templateCache.set(templateName, handlebars.compile(templateContent));
+  }
+
+  const template = templateCache.get(templateName);
+  const renderer = createMarkdownRenderer(meta);
   const html = template({
     ...data,
     ...meta,
-    content: markedRenderer.use({ renderer: renderer(meta) })(markdown)
+    content: renderer(markdown)
+  });
+  const minifiedHtml = await minify(html, {
+    collapseWhitespace: true,
+    removeComments: true,
+    removeRedundantAttributes: true,
+    removeEmptyAttributes: true,
+    minifyCSS: true,
+    minifyJS: true
   });
   return {
     detail: data,
-    result: html
+    result: minifiedHtml
   };
 }
 
-export function compileSass(filePath) {
-  return {
-    result: sass.compile(filePath, { style: "compressed" }).css.toString()
-  };
+export async function compileSass(filePath) {
+  const result = await sass.compileAsync(filePath, { style: "compressed" });
+  return { result: result.css.toString() };
 }
 
-export function optimiseSvg(filePath) {
-  const svgString = fs.readFileSync(filePath, "utf8");
+export async function optimiseSvg(filePath) {
+  const svgString = await fs.readFile(filePath, "utf8");
   const result = optimize(svgString, {
     plugins: ["preset-default"]
   });
   return { result: result.data };
 }
 
-function getCleanPath(path, meta) {
-  return path.replace(meta.opts.runDir, "").replace(meta.opts.baseDir, "/");
+export async function copy(filePath) {
+  const fileContent = await fs.readFile(filePath, "utf8");
+  return { result: fileContent };
 }
 
 export async function optimiseImage(filePath, meta, fileOutputPath) {
   const sourceExtension = path.extname(filePath);
   const outputExtension = ".webp";
-  const base = path.basename(filePath).slice(0, -sourceExtension.length);
-  const metadata = await sharp(filePath).metadata();
+  const base = path.basename(filePath, sourceExtension);
+
+  const original = sharp(filePath);
+  const metadata = await original.metadata();
   const { width, height } = metadata;
 
   if (!width || !height) {
@@ -126,7 +137,8 @@ export async function optimiseImage(filePath, meta, fileOutputPath) {
         `${base}-${sizeNum}${outputExtension}`
       );
 
-      await sharp(filePath)
+      await original
+        .clone()
         .resize(sizeNum)
         .webp({ quality: 80 })
         .toFile(outputFile);
@@ -134,10 +146,9 @@ export async function optimiseImage(filePath, meta, fileOutputPath) {
       return [getCleanPath(outputFile, meta), size];
     })
   );
-  const imageRef = getCleanPath(
-    path.join(fileOutputPath, `${base}${outputExtension}`),
-    meta
-  );
+
+  const imageRef = getCleanPath(path.join(filePath), meta);
+
   return {
     result: [imageRef, srcset, { aspectRatio }],
     written: true

+ 26 - 15
src/util.js

@@ -1,39 +1,50 @@
-import fs from "fs"
-import os from "node:os"
-import path from "path"
+import fs from "fs";
+import os from "node:os";
+import path from "path";
 
 export function readDirectoryRecursively(dir, files = []) {
   if (!fs.existsSync(dir)) {
-    return files
+    return files;
   }
-  const contents = fs.readdirSync(dir, { withFileTypes: true })
+  const contents = fs.readdirSync(dir, { withFileTypes: true });
   for (const item of contents) {
-    const itemPath = path.join(dir, item.name)
+    const itemPath = path.join(dir, item.name);
     if (item.isDirectory()) {
-      readDirectoryRecursively(itemPath, files)
+      readDirectoryRecursively(itemPath, files);
     } else {
-      files.push(itemPath)
+      files.push(itemPath);
     }
   }
-  return files
+  return files;
 }
 
 export function resolvePath(unresolvedPath) {
-  return path.resolve(unresolvedPath.replace(/^~/, os.homedir()))
+  return path.resolve(unresolvedPath.replace(/^~/, os.homedir()));
 }
 
 export function firstFound(dirs, fileName) {
   for (const dir of dirs) {
-    const filePath = resolvePath(path.join(dir, fileName))
+    const filePath = resolvePath(path.join(dir, fileName));
     if (fs.existsSync(filePath)) {
-      return filePath
+      return filePath;
     }
   }
-  return null
+  return null;
 }
 
 export function removeBasePaths(baseDirs, fullPath) {
   return baseDirs.reduce((cleanedPath, dir) => {
-    return cleanedPath.replace(dir, "")
-  }, fullPath)
+    return cleanedPath.replace(dir, "");
+  }, fullPath);
+}
+
+export function stripFileExtension(filePath) {
+  return path.join(
+    path.dirname(filePath),
+    path.basename(filePath, path.extname(filePath))
+  );
+}
+
+export function getCleanPath(filePath, meta) {
+  return filePath.replace(meta.opts.runDir, "").replace(meta.opts.baseDir, "/");
 }