Преглед на файлове

Refactoring processor API

* changed: path -> paths, required, always an array
* removed: written, all processors now handle this for themselves
* changed: ref, required, all processors now handle this for themselves
* removed: result, any data to be added to state is now to be returned
  as detail
* changed: cache methods moved from util.js to cache.js
Craig Fletcher преди 5 месеца
родител
ревизия
3b580f85ed
променени са 8 файла, в които са добавени 274 реда и са изтрити 623 реда
  1. 27 416
      package-lock.json
  2. 1 1
      package.json
  3. 200 0
      src/cache.js
  4. 1 1
      src/defaults.js
  5. 2 2
      src/index.js
  6. 7 18
      src/lib.js
  7. 27 13
      src/processors.js
  8. 9 172
      src/util.js

+ 27 - 416
package-lock.json

@@ -14,9 +14,9 @@
         "gray-matter": "^4.0.3",
         "handlebars": "^4.7.8",
         "html-minifier-terser": "^7.2.0",
-        "json-stable-stringify": "^1.3.0",
         "marked": "^15.0.6",
         "marked-code-preview": "^1.3.7",
+        "safe-stable-stringify": "^2.5.0",
         "sass": "^1.83.1",
         "sharp": "^0.34.2",
         "svgo": "^3.3.2"
@@ -1217,10 +1217,11 @@
       "license": "ISC"
     },
     "node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "version": "1.1.12",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -1386,53 +1387,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/call-bind": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
-      "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bind-apply-helpers": "^1.0.0",
-        "es-define-property": "^1.0.0",
-        "get-intrinsic": "^1.2.4",
-        "set-function-length": "^1.2.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/call-bind-apply-helpers": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
-      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/call-bound": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
-      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bind-apply-helpers": "^1.0.2",
-        "get-intrinsic": "^1.3.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/call-me-maybe": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
@@ -2033,23 +1987,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/define-data-property": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
-      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0",
-        "es-errors": "^1.3.0",
-        "gopd": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/define-property": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
@@ -2324,20 +2261,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/dunder-proto": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
-      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bind-apply-helpers": "^1.0.1",
-        "es-errors": "^1.3.0",
-        "gopd": "^1.2.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
     "node_modules/duplexer3": {
       "version": "0.1.5",
       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
@@ -2406,36 +2329,6 @@
         "errno": "cli.js"
       }
     },
-    "node_modules/es-define-property": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
-      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-errors": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
-      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-object-atoms": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
-      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
     "node_modules/escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -3550,48 +3443,12 @@
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
       "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "dev": true,
       "license": "MIT",
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/get-intrinsic": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
-      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bind-apply-helpers": "^1.0.2",
-        "es-define-property": "^1.0.1",
-        "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.1.1",
-        "function-bind": "^1.1.2",
-        "get-proto": "^1.0.1",
-        "gopd": "^1.2.0",
-        "has-symbols": "^1.1.0",
-        "hasown": "^2.0.2",
-        "math-intrinsics": "^1.1.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/get-proto": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
-      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
-      "license": "MIT",
-      "dependencies": {
-        "dunder-proto": "^1.0.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
     "node_modules/get-proxy": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz",
@@ -3745,18 +3602,6 @@
         "node": ">= 4"
       }
     },
-    "node_modules/gopd": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
-      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/got": {
       "version": "9.6.0",
       "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
@@ -3841,18 +3686,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/has-property-descriptors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
-      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/has-symbol-support-x": {
       "version": "1.4.2",
       "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz",
@@ -3862,18 +3695,6 @@
         "node": "*"
       }
     },
-    "node_modules/has-symbols": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
-      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/has-to-string-tag-x": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz",
@@ -3958,6 +3779,7 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
       "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "function-bind": "^1.1.2"
@@ -4345,46 +4167,12 @@
       "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
       "dev": true
     },
-    "node_modules/json-stable-stringify": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz",
-      "integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.4",
-        "isarray": "^2.0.5",
-        "jsonify": "^0.0.1",
-        "object-keys": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/json-stable-stringify-without-jsonify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
       "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
       "dev": true
     },
-    "node_modules/json-stable-stringify/node_modules/isarray": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
-      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
-      "license": "MIT"
-    },
-    "node_modules/jsonify": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz",
-      "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==",
-      "license": "Public Domain",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/keyv": {
       "version": "4.5.4",
       "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -4612,15 +4400,6 @@
         "marked": ">=7.0.0"
       }
     },
-    "node_modules/math-intrinsics": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
-      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
     "node_modules/mdn-data": {
       "version": "2.0.30",
       "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
@@ -5076,15 +4855,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/object-keys": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
-      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
     "node_modules/object-visit": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
@@ -5979,6 +5749,15 @@
         "ret": "~0.1.10"
       }
     },
+    "node_modules/safe-stable-stringify": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+      "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/sass": {
       "version": "1.83.1",
       "license": "MIT",
@@ -6039,23 +5818,6 @@
         "node": ">=10"
       }
     },
-    "node_modules/set-function-length": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
-      "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
-      "license": "MIT",
-      "dependencies": {
-        "define-data-property": "^1.1.4",
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
     "node_modules/set-value": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
@@ -7978,9 +7740,9 @@
       "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",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "version": "1.1.12",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
       "dev": true,
       "requires": {
         "balanced-match": "^1.0.0",
@@ -8101,35 +7863,6 @@
         }
       }
     },
-    "call-bind": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
-      "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
-      "requires": {
-        "call-bind-apply-helpers": "^1.0.0",
-        "es-define-property": "^1.0.0",
-        "get-intrinsic": "^1.2.4",
-        "set-function-length": "^1.2.2"
-      }
-    },
-    "call-bind-apply-helpers": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
-      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
-      "requires": {
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2"
-      }
-    },
-    "call-bound": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
-      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
-      "requires": {
-        "call-bind-apply-helpers": "^1.0.2",
-        "get-intrinsic": "^1.3.0"
-      }
-    },
     "call-me-maybe": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
@@ -8569,16 +8302,6 @@
       "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
       "dev": true
     },
-    "define-data-property": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
-      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
-      "requires": {
-        "es-define-property": "^1.0.0",
-        "es-errors": "^1.3.0",
-        "gopd": "^1.0.1"
-      }
-    },
     "define-property": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
@@ -8780,16 +8503,6 @@
         }
       }
     },
-    "dunder-proto": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
-      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
-      "requires": {
-        "call-bind-apply-helpers": "^1.0.1",
-        "es-errors": "^1.3.0",
-        "gopd": "^1.2.0"
-      }
-    },
     "duplexer3": {
       "version": "0.1.5",
       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
@@ -8840,24 +8553,6 @@
         "prr": "~1.0.1"
       }
     },
-    "es-define-property": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
-      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="
-    },
-    "es-errors": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
-      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
-    },
-    "es-object-atoms": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
-      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
-      "requires": {
-        "es-errors": "^1.3.0"
-      }
-    },
     "escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -9557,33 +9252,8 @@
     "function-bind": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
-      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
-    },
-    "get-intrinsic": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
-      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
-      "requires": {
-        "call-bind-apply-helpers": "^1.0.2",
-        "es-define-property": "^1.0.1",
-        "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.1.1",
-        "function-bind": "^1.1.2",
-        "get-proto": "^1.0.1",
-        "gopd": "^1.2.0",
-        "has-symbols": "^1.1.0",
-        "hasown": "^2.0.2",
-        "math-intrinsics": "^1.1.0"
-      }
-    },
-    "get-proto": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
-      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
-      "requires": {
-        "dunder-proto": "^1.0.1",
-        "es-object-atoms": "^1.0.0"
-      }
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "dev": true
     },
     "get-proxy": {
       "version": "2.1.0",
@@ -9688,11 +9358,6 @@
         }
       }
     },
-    "gopd": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
-      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
-    },
     "got": {
       "version": "9.6.0",
       "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
@@ -9754,25 +9419,12 @@
       "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
       "dev": true
     },
-    "has-property-descriptors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
-      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
-      "requires": {
-        "es-define-property": "^1.0.0"
-      }
-    },
     "has-symbol-support-x": {
       "version": "1.4.2",
       "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz",
       "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==",
       "dev": true
     },
-    "has-symbols": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
-      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
-    },
     "has-to-string-tag-x": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz",
@@ -9838,6 +9490,7 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
       "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "dev": true,
       "requires": {
         "function-bind": "^1.1.2"
       }
@@ -10099,36 +9752,12 @@
       "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
       "dev": true
     },
-    "json-stable-stringify": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz",
-      "integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==",
-      "requires": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.4",
-        "isarray": "^2.0.5",
-        "jsonify": "^0.0.1",
-        "object-keys": "^1.1.1"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "2.0.5",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
-          "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
-        }
-      }
-    },
     "json-stable-stringify-without-jsonify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
       "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
       "dev": true
     },
-    "jsonify": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz",
-      "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg=="
-    },
     "keyv": {
       "version": "4.5.4",
       "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -10293,11 +9922,6 @@
         "attributes-parser": "^2.2.3"
       }
     },
-    "math-intrinsics": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
-      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="
-    },
     "mdn-data": {
       "version": "2.0.30",
       "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
@@ -10639,11 +10263,6 @@
         }
       }
     },
-    "object-keys": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
-      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
-    },
     "object-visit": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
@@ -11261,6 +10880,11 @@
         "ret": "~0.1.10"
       }
     },
+    "safe-stable-stringify": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+      "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="
+    },
     "sass": {
       "version": "1.83.1",
       "requires": {
@@ -11296,19 +10920,6 @@
       "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
       "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="
     },
-    "set-function-length": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
-      "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
-      "requires": {
-        "define-data-property": "^1.1.4",
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.2"
-      }
-    },
     "set-value": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",

+ 1 - 1
package.json

@@ -26,9 +26,9 @@
     "gray-matter": "^4.0.3",
     "handlebars": "^4.7.8",
     "html-minifier-terser": "^7.2.0",
-    "json-stable-stringify": "^1.3.0",
     "marked": "^15.0.6",
     "marked-code-preview": "^1.3.7",
+    "safe-stable-stringify": "^2.5.0",
     "sass": "^1.83.1",
     "sharp": "^0.34.2",
     "svgo": "^3.3.2"

+ 200 - 0
src/cache.js

@@ -0,0 +1,200 @@
+import fs from "node:fs/promises"
+import path from "path"
+import { createHash } from "crypto"
+import { createReadStream } from "fs"
+import stableStringify from "safe-stable-stringify"
+import {
+  fileExists,
+  checkPathExists,
+  getValueAtPath,
+  removeCwd,
+  getDeepestPropertiesForKey,
+} from "./util.js"
+
+export function hashObject(obj) {
+  const str = stableStringify(obj)
+  if (!hashCache.has(str)) {
+    const hashString = createHash("md5")
+      .update(str)
+      .digest("hex")
+    hashCache.set(str, hashString)
+    return hashString
+  } else {
+    const cachedHashString = hashCache.get(str)
+    return cachedHashString
+  }
+}
+
+const hashCache = new Map()
+console.log("Created new hashCache")
+export async function getFileHash(filePath, algorithm = "md5") {
+  return new Promise((resolve, reject) => {
+    if (!hashCache.has(filePath)) {
+      const hash = createHash(algorithm)
+      const stream = createReadStream(filePath)
+
+      stream.on("error", reject)
+      stream.on("data", chunk => hash.update(chunk))
+      stream.on("end", () => {
+        const hashString = hash.digest("hex")
+        hashCache.set(filePath, hashString)
+        resolve(hashString)
+      })
+    } else {
+      const cachedHashString = hashCache.get(filePath)
+      resolve(cachedHashString)
+    }
+  })
+}
+
+async function getFileHashes(pathDeps) {
+  return Promise.all(
+    Object.keys(pathDeps).map(async filePath => {
+      const hash = await getFileHash(filePath)
+      if (hash !== pathDeps[filePath]) {
+        return Promise.reject({ filePath, hash })
+      }
+
+      return Promise.resolve(pathDeps[filePath])
+    }),
+  )
+}
+
+function getStatePropsHash(state, props) {
+  const stateValues = props.reduce((depmap, dep) => {
+    const value = getValueAtPath(state, dep)
+    return { ...depmap, [dep]: value }
+  }, {})
+  return hashObject(stateValues)
+}
+
+export async function checkCache(name, currentState, opts) {
+  const existingCacheObject = await readCache(opts.cacheDir, name)
+  if (existingCacheObject) {
+    const outFiles = existingCacheObject.taskResult.paths
+    const outFilesExist = await checkPathExists(outFiles, opts.outDir)
+    if (outFilesExist) {
+      const stateHash = getStatePropsHash(
+        currentState,
+        existingCacheObject.deps.state.props,
+      )
+      if (stateHash === existingCacheObject.deps.state.hash) {
+        try {
+          await getFileHashes(existingCacheObject.deps.paths)
+          return { hit: true, taskResult: existingCacheObject.taskResult }
+        } catch (e) {
+          const updates = {
+            deps: {
+              paths: [e],
+            },
+          }
+          return { hit: false, reason: "File hash mismatch", updates }
+        }
+      }
+      const updates = {
+        deps: {
+          state: {
+            ...existingCacheObject.deps.state,
+            hash: stateHash,
+          },
+        },
+      }
+      return { hit: false, reason: "State hash mismatch", updates }
+    }
+    if (opts.clean) {
+      await Promise.all(
+        outFiles.map(
+          async outFile =>
+          await fs.rm(path.join(opts.outDir, outFile), { force: true }),
+        ),
+      )
+    }
+    return { hit: false, reason: "Missing output file(s)" }
+  }
+  return { hit: false, reason: "Missing cache file" }
+}
+
+export async function updateCache(
+  cacheDir,
+  name,
+  pathDeps,
+  stateDeps,
+  taskResult,
+  updates,
+) {
+  const cacheDirExists = await fileExists(cacheDir)
+  if (!cacheDirExists) {
+    await fs.mkdir(cacheDir, { recursive: true })
+  }
+  const accessedState = getDeepestPropertiesForKey([...stateDeps], "path")
+  const deps = {
+    paths: [...new Set(removeCwd(pathDeps))],
+    state: accessedState.reduce(
+      (as, { path, value }) => ({ ...as, [path]: value }),
+      {},
+    ),
+  }
+  const statePropsList = Object.keys(deps.state)
+  const updatesStateHash = updates?.deps?.state?.props || []
+  const stateDepsHash =
+    JSON.stringify(statePropsList) === JSON.stringify(updatesStateHash)
+    ? updates?.deps?.state?.hash
+    : hashObject(deps.state)
+
+  const updatesPathsCache =
+    updates?.deps?.paths?.reduce(
+      (pc, { filePath, hash }) => ({
+        ...pc,
+        [filePath]: hash,
+      }),
+      {},
+    ) || {}
+  const pathsCache = (await Promise.all(
+    deps.paths.map(async filePath => {
+      const hash = updatesPathsCache[filePath]
+        ? updatesPathsCache[filePath]
+        : await getFileHash(filePath)
+      return {
+        hash,
+        filePath,
+      }
+    }),
+  )).reduce((pc, { filePath, hash }) => ({ ...pc, [filePath]: hash }), {})
+  const cacheObject = {
+    deps: {
+      state: {
+        hash: stateDepsHash,
+        props: Object.keys(deps.state),
+      },
+      paths: pathsCache,
+    },
+    taskResult,
+  }
+  return await writeCache(cacheDir, name, cacheObject)
+}
+
+async function writeCache(cacheDir, name, cache) {
+  if (!cacheDir) {
+    return false
+  }
+  return fs.writeFile(
+    path.join(cacheDir, `${name}.cache`),
+    JSON.stringify(cache),
+    "utf8",
+  )
+}
+
+async function readCache(cacheDir, name) {
+  if (!cacheDir) {
+    return false
+  }
+  try {
+    const content = await fs.readFile(
+      path.join(cacheDir, `${name}.cache`),
+      "utf8",
+    )
+    return JSON.parse(content)
+  } catch {
+    return false
+  }
+}

+ 1 - 1
src/defaults.js

@@ -62,7 +62,7 @@ export const opts = {
   outDir: "dist/",
   runDir: process.cwd(),
   cacheDir: ".cache",
-  logLevel: "info",
+  logLevel: "debug",
   defaultTemplate: "default",
   include: {
     styles: [{ pattern: "~/.rhedyn/styles/*.scss" }],

+ 2 - 2
src/index.js

@@ -23,7 +23,7 @@ async function runTask(meta, task) {
     ...meta,
     resources: {
       ...meta.resources,
-      [task.name]: allResults.reduce((obj, path) => ({ ...obj, [path.ref]: path }), {}),
+      [task.name]: allResults.reduce((obj, taskResult) => ({ ...obj, [taskResult.ref]: taskResult }), {}),
     },
   }
   return { taskResult, cachedResults, processedResults }
@@ -41,7 +41,7 @@ const taskRunner = tasks.reduce(
     const endTime = performance.now()
     const timeTaken = (endTime - startTime)
     const hrTime = timeTaken > 1000 ? `${Number.parseFloat(timeTaken / 1000).toFixed(2)}s` : `${Number.parseFloat(timeTaken).toFixed(2)}ms`
-    const filesWritten = processedResults.reduce((acc, cur) => acc + cur.path.length, 0)
+    const filesWritten = processedResults.reduce((acc, cur) => acc + cur.paths.length, 0)
     log.info(`written: ${filesWritten} | processed: ${processedResults.length} | from cache: ${cachedResults.length} | ${hrTime}`)
     log.groupEnd()
     return taskResult

+ 7 - 18
src/lib.js

@@ -1,5 +1,8 @@
 import {
   checkCache,
+  updateCache,
+} from "./cache.js"
+import {
   createTrackedObject,
   fileExists,
   readFilesByGlob,
@@ -7,9 +10,7 @@ import {
   removeCwd,
   replaceFileExtension,
   slugifyString,
-  updateCache,
 } from "./util.js"
-import fs from "node:fs/promises"
 import path from "path"
 import process from "node:process"
 import { getLogger } from "./logging.js"
@@ -51,10 +52,6 @@ export async function processFiles(config, meta) {
       )
 
       const fileOutputDir = path.dirname(fileOutputPath)
-      const exists = await fileExists(fileOutputDir)
-      if (!exists) {
-        await fs.mkdir(fileOutputDir, { recursive: true })
-      }
 
       const stateObject = {
         filePath,
@@ -84,26 +81,18 @@ export async function processFiles(config, meta) {
         : { proxy: stateObject }
 
       const {
-        result,
         detail,
-        written,
+        paths,
         deps: processorDeps,
         ref,
       } = await config.processor(state.proxy)
 
-      if (!written) {
-        await fs.writeFile(fileOutputPath, result, {
-          encoding: "utf8",
-        })
-      }
-
-      const taskRef = ref ? slugifyString(ref) : slugifyString(fileOutputPath)
       const taskResult = {
         detail,
-        path: written ? result : [fileOutputPath.replace(meta.opts.outDir, "")],
-        ref: taskRef,
+        paths: paths.map(fileOutputPath => fileOutputPath.replace(meta.opts.outDir, "")),
+        ref,
       }
-      log.debug(`Wrote ${taskResult.path.length} files for ${filePath}`)
+      log.debug(`Wrote ${taskResult.paths.length} files for ${filePath}`)
       if (meta.opts.cacheDir) {
         log.debug(`Updating cache for ${filePath}`)
         const processorPathDeps = processorDeps?.paths || []

+ 27 - 13
src/processors.js

@@ -5,6 +5,7 @@ import {
   getCleanPath,
   getHref,
   slugifyString,
+  writeFile,
 } from "./util.js"
 import fs from "fs/promises"
 import handlebars from "handlebars"
@@ -94,44 +95,59 @@ export async function renderMarkdownWithTemplate({
     minifyJS: true,
   })
 
+  await writeFile(fileOutputPath, minifiedHtml)
+
   return {
     detail: { ...data, href },
-    result: minifiedHtml,
+    paths: [fileOutputPath],
     deps: {
       paths: [template.path],
     },
+    ref: slugifyString(fileOutputPath),
   }
 }
 
-export async function compileSass({ filePath }) {
+export async function compileSass({ filePath, fileOutputPath, meta }) {
   const result = await sass.compileAsync(filePath, { style: "compressed" })
+  await writeFile(fileOutputPath, result.css)
   return {
-    result: result.css,
+    paths: [fileOutputPath],
+    ref: slugifyString(fileOutputPath),
+    detail: {
+      href: fileOutputPath.replace(meta.opts.outDir, "")
+    },
     deps: {
       paths: [...result.loadedUrls.map(item => item.pathname)],
     },
   }
 }
 
-export async function optimiseSvg({ filePath }) {
+export async function optimiseSvg({ filePath, fileOutputPath }) {
   const svgString = await fs.readFile(filePath, "utf8")
   const result = optimize(svgString, {
     plugins: ["preset-default"],
   })
+  await writeFile(fileOutputPath, result.data)
   return {
-    result: result.data,
+    paths: [fileOutputPath],
+    ref: slugifyString(fileOutputPath),
   }
 }
 
-export async function copy({ filePath }) {
+export async function copy({ filePath, fileOutputPath }) {
   const fileContent = await fs.readFile(filePath, "utf8")
-  return { result: fileContent }
+  await writeFile(fileOutputPath, fileContent)
+  return {
+    paths: [fileOutputPath],
+    ref: slugifyString(fileOutputPath),
+  }
 }
 
 export async function imageToWebP({ filePath, meta, fileOutputDir, config }) {
   const sourceExtension = path.extname(filePath)
   const outputExtension = config.outputFileExtension
   const base = path.basename(filePath, sourceExtension)
+  await fs.mkdir(fileOutputDir, { recursive: true })
 
   const original = sharp(filePath)
   const metadata = await original.metadata()
@@ -164,9 +180,8 @@ export async function imageToWebP({ filePath, meta, fileOutputDir, config }) {
   const imageRef = getCleanPath(path.join(filePath), meta)
 
   return {
-    result: srcSet.map(src => src[0]),
+    paths: srcSet.map(src => src[0]),
     detail: { imageRef, srcSet, aspectRatio },
-    written: true,
     ref: imageRef,
   }
 }
@@ -210,7 +225,7 @@ export async function generateFavicons({ filePath, meta, fileOutputDir }) {
     await Promise.all(
       response.images.map(async image => {
         const outputPath = path.join(fileOutputDir, image.name)
-        await fs.writeFile(outputPath, image.contents)
+        await writeFile(outputPath, image.contents)
       }),
     )
 
@@ -218,7 +233,7 @@ export async function generateFavicons({ filePath, meta, fileOutputDir }) {
     await Promise.all(
       response.files.map(async file => {
         const outputPath = path.join(fileOutputDir, file.name)
-        await fs.writeFile(outputPath, file.contents)
+        await writeFile(outputPath, file.contents)
       }),
     )
 
@@ -228,7 +243,7 @@ export async function generateFavicons({ filePath, meta, fileOutputDir }) {
       detail: {
         htmlMeta,
       },
-      result: [
+      paths: [
         ...response.images.map(img =>
           getCleanPath(path.join(fileOutputDir, img.name), meta),
         ),
@@ -236,7 +251,6 @@ export async function generateFavicons({ filePath, meta, fileOutputDir }) {
           getCleanPath(path.join(fileOutputDir, file.name), meta),
         ),
       ],
-      written: true,
       ref: "metatags",
     }
   } catch (error) {

+ 9 - 172
src/util.js

@@ -2,9 +2,6 @@ import fs from "node:fs/promises"
 import os from "node:os"
 import path from "path"
 import { glob } from "glob"
-import { createHash } from "crypto"
-import { createReadStream } from "fs"
-import stableStringify from "json-stable-stringify"
 
 export async function fileExists(filePath) {
   try {
@@ -190,28 +187,12 @@ export function getValueAtPath(obj, path) {
   return val
 }
 
-export function hashObject(obj) {
-  const str = stableStringify(obj)
-  return createHash("md5")
-    .update(str)
-    .digest("hex")
-}
-
-export async function getFileHash(filePath, algorithm = "md5") {
-  return new Promise((resolve, reject) => {
-    const hash = createHash(algorithm)
-    const stream = createReadStream(filePath)
-
-    stream.on("error", reject)
-    stream.on("data", chunk => hash.update(chunk))
-    stream.on("end", () => resolve(hash.digest("hex")))
-  })
-}
-
 export async function checkPathExists(files, baseDir) {
   if (Array.isArray(files)) {
     return (await Promise.all(
-      files.map(file => fileExists(path.join(baseDir, file))),
+      files.map(file => {
+        return fileExists(path.join(baseDir, file))
+      }),
     )).every(item => !!item)
   }
   return fileExists(path.join(baseDir, files))
@@ -226,154 +207,10 @@ export function generateRandomId(length = 8) {
   return result
 }
 
-async function getFileHashes(pathDeps) {
-  return Promise.all(
-    Object.keys(pathDeps).map(async filePath => {
-      const hash = await getFileHash(filePath)
-      if (hash !== pathDeps[filePath]) {
-        return Promise.reject({ filePath, hash })
-      }
-
-      return Promise.resolve(pathDeps[filePath])
-    }),
-  )
-}
-
-function getStatePropsHash(state, props) {
-  const stateValues = props.reduce((depmap, dep) => {
-    const value = getValueAtPath(state, dep)
-    return { ...depmap, [dep]: value }
-  }, {})
-  return hashObject(stateValues)
-}
-
-export async function checkCache(name, currentState, opts) {
-  const existingCacheObject = await readCache(opts.cacheDir, name)
-  if (existingCacheObject) {
-    const outFiles = existingCacheObject.taskResult.path
-    const outFilesExist = await checkPathExists(outFiles, opts.outDir)
-    if (outFilesExist) {
-      const stateHash = getStatePropsHash(
-        currentState,
-        existingCacheObject.deps.state.props,
-      )
-      if (stateHash === existingCacheObject.deps.state.hash) {
-        try {
-          await getFileHashes(existingCacheObject.deps.paths)
-          return { hit: true, taskResult: existingCacheObject.taskResult }
-        } catch (e) {
-          const updates = {
-            deps: {
-              paths: [e],
-            },
-          }
-          return { hit: false, reason: "File hash mismatch", updates }
-        }
-      }
-      const updates = {
-        deps: {
-          state: {
-            ...existingCacheObject.deps.state,
-            hash: stateHash,
-          },
-        },
-      }
-      return { hit: false, reason: "State hash mismatch", updates }
-    }
-    if (opts.clean) {
-      await Promise.all(
-        outFiles.map(
-          async outFile =>
-            await fs.rm(path.join(opts.outDir, outFile), { force: true }),
-        ),
-      )
-    }
-    return { hit: false, reason: "Missing output file(s)" }
-  }
-  return { hit: false, reason: "Missing cache file" }
-}
-
-export async function updateCache(
-  cacheDir,
-  name,
-  pathDeps,
-  stateDeps,
-  taskResult,
-  updates,
-) {
-  const cacheDirExists = await fileExists(cacheDir)
-  if (!cacheDirExists) {
-    await fs.mkdir(cacheDir, { recursive: true })
-  }
-  const accessedState = getDeepestPropertiesForKey([...stateDeps], "path")
-  const deps = {
-    paths: [...new Set(removeCwd(pathDeps))],
-    state: accessedState.reduce(
-      (as, { path, value }) => ({ ...as, [path]: value }),
-      {},
-    ),
-  }
-  const statePropsList = Object.keys(deps.state)
-  const updatesStateHash = updates?.deps?.state?.props || []
-  const stateDepsHash =
-    JSON.stringify(statePropsList) === JSON.stringify(updatesStateHash)
-      ? updates?.deps?.state?.hash
-      : hashObject(deps.state)
-
-  const updatesPathsCache =
-    updates?.deps?.paths?.reduce(
-      (pc, { filePath, hash }) => ({
-        ...pc,
-        [filePath]: hash,
-      }),
-      {},
-    ) || {}
-  const pathsCache = (await Promise.all(
-    deps.paths.map(async filePath => {
-      const hash = updatesPathsCache[filePath]
-        ? updatesPathsCache[filePath]
-        : await getFileHash(filePath)
-      return {
-        hash,
-        filePath,
-      }
-    }),
-  )).reduce((pc, { filePath, hash }) => ({ ...pc, [filePath]: hash }), {})
-  const cacheObject = {
-    deps: {
-      state: {
-        hash: stateDepsHash,
-        props: Object.keys(deps.state),
-      },
-      paths: pathsCache,
-    },
-    taskResult,
-  }
-  return await writeCache(cacheDir, name, cacheObject)
-}
-
-async function writeCache(cacheDir, name, cache) {
-  if (!cacheDir) {
-    return false
-  }
-  return fs.writeFile(
-    path.join(cacheDir, `${name}.cache`),
-    JSON.stringify(cache),
-    "utf8",
-  )
-}
-
-async function readCache(cacheDir, name) {
-  if (!cacheDir) {
-    return false
-  }
-  try {
-    const content = await fs.readFile(
-      path.join(cacheDir, `${name}.cache`),
-      "utf8",
-    )
-    return JSON.parse(content)
-  } catch {
-    return false
-  }
+export async function writeFile(filePath, content) {
+  const fileDir = path.dirname(filePath)
+  await fs.mkdir(fileDir, { recursive: true })
+  return await fs.writeFile(filePath, content, {
+    encoding: "utf8",
+  })
 }