diff --git a/README.md b/README.md index 0934f9d..898fdf5 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,12 @@ sfab --folder ./docs --destination ./_site --verbose --serve /hubot/ --watch-pat npx @hubot-friends/sfab --folder ./docs --destination ./_site --verbose --serve /hubot/ --watch-path ./docs --scripts ./sfab-hooks ``` +# Copy another folder to destination + +```sh +sfab --folder ./docs --destination ./_site --verbose --serve /hubot/ --watch-path ./docs --copy ./resources +``` + ## Example Hook ```javascript @@ -82,15 +88,19 @@ export default () => { } } }, - async transformed(transformedFilePath) { + async transformed(viewKey, transformedFilePath, model, html, viewModel) { // model is the object passed to the temlating engines, viewModel is the object that comes from markdown meta data or html item props. // do something during transformation }, async copied(filePath) { - // file wsa copied to this filePath. + // file was copied to this filePath. }, async partial(partialName, partial, handebars) { // partial was registered. passing handlebars if you want to register more. } } } +``` + +```sh +npm start -- --folder ../../hubotio/hubot/docs --destination ../../hubotio/hubot/_site --verbose --serve /hubot/ --watch-path ../../hubotio/hubot/docs --scripts ../../hubotio/hubot/sfab-hooks ``` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3edbc94..9354428 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { - "name": "hubot-sfab", + "name": "@hubot-friends/sfab", "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "hubot-sfab", + "name": "@hubot-friends/sfab", "version": "1.0.1", "dependencies": { "cheerio": "^1.0.0-rc.12", - "express": "^4.18.2", + "express": "^5.0.0-beta.1", "handlebars": "^4.7.7", "markdown-it": "^13.0.1", "markdown-it-meta": "^0.0.1" @@ -39,31 +39,36 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz", + "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==" }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.0.0-beta.1.tgz", + "integrity": "sha512-I1v2bt2OdYqtmk8nEFZuEf+9Opb30DphYwTPDbgg/OorSAoJOuTpWyDrZaSWQw7FdoevbBRCP2+9z/halXSWcA==", "dependencies": { - "bytes": "3.1.2", + "bytes": "3.1.1", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", + "depd": "~1.1.2", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">= 0.10" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" } }, "node_modules/boolbase": { @@ -72,25 +77,13 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", "engines": { "node": ">= 0.8" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/cheerio": { "version": "1.0.0-rc.12", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", @@ -147,9 +140,9 @@ } }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", "engines": { "node": ">= 0.6" } @@ -186,29 +179,25 @@ } }, "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dependencies": { "ms": "2.0.0" } }, "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==" }, "node_modules/dom-serializer": { "version": "2.0.0", @@ -322,63 +311,72 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "5.0.0-beta.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.0.0-beta.1.tgz", + "integrity": "sha512-KPtBrlZoQu2Ps0Ce/Imqtq73AB0KBJ8Gx59yZQ3pmDJU2/LhcoZETo03oSgtTQufbcLXt/WBITk/jMjl/WMyrQ==", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "accepts": "~1.3.7", + "array-flatten": "3.0.0", + "body-parser": "2.0.0-beta.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.4.1", "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", + "debug": "3.1.0", + "depd": "~1.1.2", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "~1.1.2", "fresh": "0.5.2", - "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "2.4.1", + "mime-types": "~2.1.34", + "on-finished": "~2.3.0", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-is-absolute": "1.0.1", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.9.6", "range-parser": "~1.2.1", + "router": "2.0.0-beta.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "1.0.0-beta.1", + "serve-static": "2.0.0-beta.1", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~1.5.0", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 4" } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "2.4.1", + "on-finished": "~2.3.0", "parseurl": "~1.3.3", - "statuses": "2.0.1", + "statuses": "~1.5.0", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -395,24 +393,6 @@ "node": ">= 0.6" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -433,28 +413,6 @@ "uglify-js": "^3.1.4" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/htmlparser2": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", @@ -485,18 +443,18 @@ } }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dependencies": { - "depd": "2.0.0", + "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, "node_modules/iconv-lite": { @@ -600,17 +558,6 @@ "node": ">= 0.6" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -667,18 +614,10 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "dependencies": { "ee-first": "1.1.1" }, @@ -728,10 +667,18 @@ "node": ">= 0.8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", + "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==" }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -746,12 +693,9 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", "engines": { "node": ">=0.6" }, @@ -768,12 +712,12 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", + "bytes": "3.1.1", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -781,6 +725,22 @@ "node": ">= 0.8" } }, + "node_modules/router": { + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/router/-/router-2.0.0-beta.1.tgz", + "integrity": "sha512-GLoYgkhAGAiwVda5nt6Qd4+5RAPuQ4WIYLlZ+mxfYICI+22gnIB3eCfmhgV8+uJNPS1/39DOYi/vdrrz0/ouKA==", + "dependencies": { + "array-flatten": "3.0.0", + "methods": "~1.1.2", + "parseurl": "~1.3.3", + "path-to-regexp": "3.2.0", + "setprototypeof": "1.2.0", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -806,26 +766,25 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.0.0-beta.1.tgz", + "integrity": "sha512-OKTRokcl/oo34O8+6aUpj8Jf2Bjw2D0tZzmX0/RvyfVC9ZOZW+HPAWAlhS817IsRaCnzYX1z++h2kHFr2/KNRg==", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", + "debug": "3.1.0", + "destroy": "~1.0.4", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", + "http-errors": "1.8.1", + "mime-types": "~2.1.34", "ms": "2.1.3", - "on-finished": "2.4.1", + "on-finished": "~2.3.0", "range-parser": "~1.2.1", - "statuses": "2.0.1" + "statuses": "~1.5.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.10" } }, "node_modules/send/node_modules/ms": { @@ -834,17 +793,17 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.0.0-beta.1.tgz", + "integrity": "sha512-DEJ9on/tQeFO2Omj7ovT02lCp1YgP4Kb8W2lv2o/4keTFAbgc8HtH3yPd47++2wv9lvQeqiA7FHFDe5+8c4XpA==", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "1.0.0-beta.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.10" } }, "node_modules/setprototypeof": { @@ -852,19 +811,6 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -879,11 +825,11 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, "node_modules/toidentifier": { diff --git a/package.json b/package.json index 48b2d3e..b557841 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "author": "Joey Guerra", "dependencies": { "cheerio": "^1.0.0-rc.12", - "express": "^4.18.2", + "express": "^5.0.0-beta.1", "handlebars": "^4.7.7", "markdown-it": "^13.0.1", "markdown-it-meta": "^0.0.1" diff --git a/sfab.mjs b/sfab.mjs index 7f90087..714b26c 100644 --- a/sfab.mjs +++ b/sfab.mjs @@ -47,7 +47,7 @@ const markdown = new MarkdownIt({ }).use(markdownItMeta) const app = express() -app.engine('html', async (filename, options, cb) => { +const xmlEngine = async (filename, options, cb) => { const html = await File.readFile(filename, 'utf-8') const $ = cheerio(html) const props = {} @@ -64,6 +64,7 @@ app.engine('html', async (filename, options, cb) => { props[prop] = content }) props.uri = filename.replace(`${__dirname}/${WWW}`, '') + props.uri = props.uri.replace(pathToFileURL(options.settings.args.folder).pathname, '') props.relativeLink = props.uri.replace(/^\//, '') $('meta').each((i, el) => { const $el = $(el) @@ -77,15 +78,19 @@ app.engine('html', async (filename, options, cb) => { const template = handlebars.compile(html) log('rendering ', filename) - cb(null, template({...props, ...options})) -}) -const posts = [] + const viewModel = {...props, ...options} + cb(null, template(viewModel), viewModel) +} + +app.engine('html', xmlEngine) +app.engine('xml', xmlEngine) + app.engine('md', async (filePath, options, callback)=>{ try{ let data = await File.readFile(filePath, 'utf-8') let output = markdown.render(data) output = `{{#> ${markdown.meta.layout}}}\n${output}{{/${markdown.meta.layout}}}\n` - markdown.meta.permalink = filePath.replace(`${__dirname}`, '').replace('.md', '.html') + markdown.meta.permalink = filePath.replace(pathToFileURL(options.settings.args.folder).pathname, '').replace('.md', '.html') markdown.meta.relativeLink = markdown.meta.permalink.replace(/^\//, '') markdown.meta.tags = markdown.meta?.tags ?? [] let template = handlebars.compile(output) @@ -96,10 +101,10 @@ app.engine('md', async (filePath, options, callback)=>{ console.error(e) } if(markdown.meta.published) markdown.meta.displayDate = markdown.meta.published.toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }) - if(markdown.meta.should_publish == 'yes') posts.push(markdown.meta) - callback(null, template({...markdown.meta, ...options})) + const viewModel = {...markdown.meta, ...options} + callback(null, template(viewModel), viewModel) }catch(e){ - callback(e, null) + callback(e, null, null) } }) @@ -127,151 +132,157 @@ handlebars.registerHelper('unescapeAmp', function(){ return this.source.url.replaceAll('&', '&') }) handlebars.registerHelper('current', (a, b)=>{ + console.log(a, b) if(!a) return '' return a.endsWith(b) ? ' current' : '' }) -const registerPartials = async (folder, handlebars, hooks) => { - const files = await File.readdir(folder, { withFileTypes: true }) - for await (let file of files) { - const filePath = `${folder}/${file.name}` - if (file.isDirectory()) { - await registerPartials(filePath, handlebars, hooks) - } else { - if(!(filePath.toLowerCase().includes('layouts') || filePath.toLowerCase().includes('partials'))) continue - if (!filePath.endsWith('.html')) continue - - const partialName = filePath.split(`${path.sep}${args.folder.split(path.sep).pop()}${path.sep}`).pop().replace(/^\//, '') //filePath.split(path.sep).slice(1).join(path.sep) - const partial = await File.readFile(filePath, 'utf-8') - log(folder, partialName) - handlebars.registerPartial(partialName, partial) - for await (let hook of hooks) { - if (!hook?.partial) continue - await hook.partial(partialName, partial, handlebars) - } - } +class SiteFabricator { + #hooks = [] + constructor(app) { + this.app = app } -} - -const render = async (files, app, source, destination, hooks = []) => { - const uris = [] - app.set('views', [source]) - for await (let file of files) { - if(file.path.toLowerCase().includes('/layouts') || file.path.toLowerCase().includes('/partials')) continue - - if (!(file.name.endsWith('.html') || file.name.endsWith('.md'))) { - copyFilesFromSourceToDestination([file], source, destination, hooks) - continue + use(hook) { + this.#hooks.push(hook) + } + async transform (files, source, destination) { + try{await File.mkdir(destination)}catch(e){} + for await (let folder of [source]) { + await this.registerPartials(folder, handlebars) } - - const destinationFolderToCreate = file.path.replace(source, destination) - await File.mkdir(destinationFolderToCreate, { recursive: true }) - - const viewKey = path.join(file.path.replace(source, ''), file.name).replace(/^\//, '') - let defaultEngine = path.extname(file.name).replace('.', '') - app.set('view engine', defaultEngine) - let model = {} - for await (let hook of hooks) { - if (!hook?.model) continue - model = Object.assign(model, await hook.model(file, model)) + await this.render(files, this.app, source, destination) + for await (let hook of this.#hooks) { + if (!hook?.done) continue + await hook.done() } - - app.render(viewKey, model, async (err, html) => { - if (err) { - console.error('error rendering', err) - console.error('file', file.name) - throw err + } + async copyFilesFromSourceToDestination (files, source, destination) { + for await (let file of files) { + if (file.name == '.DS_Store') continue + const folderStructure = file.path.replace(source, '').replace(/^.?\//, '') + try { await File.mkdir(path.join(destination, folderStructure), { recursive: true }) } catch (e) { log(e.message) } + if (file.isDirectory()) { + const filesInFolder = [] + for await (let file of await this.getFilesRecursively(path.join(source, folderStructure), { withFileTypes: true })) { + filesInFolder.push(file) + } + await this.copyFilesFromSourceToDestination(filesInFolder, path.join(source, file.path), path.join(destination, folderStructure)) + continue } - html = html.split('\n').map(line => line.trim().replace(/^\s+/, '')).join('\n') - - const transformedFilePath = path.join(destinationFolderToCreate, file.name.replace('.md', '.html')) - log('Creating', transformedFilePath) - await File.writeFile(transformedFilePath, html, 'utf-8') - for await (let hook of hooks) { - if (!hook?.transformed) continue - await hook.transformed(transformedFilePath) + createReadStream(path.join(file.path, file.name)).pipe(createWriteStream(path.join(destination, folderStructure, file.name))) + for await (let hook of this.#hooks) { + if (!hook?.copied) continue + await hook.copied(path.join(destination, folderStructure, file.name)) } - uris.push(transformedFilePath.replace(destination.replace('./', ''), '')) - }) + } } - return uris -} - -const copyFilesFromSourceToDestination = async (files, source, destination, hooks) => { - for await (let file of files) { - if (file.name == '.DS_Store') continue - const folderStructure = file.path.replace(source, '').replace(/^.?\//, '') - try { await File.mkdir(path.join(destination, folderStructure), { recursive: true }) } catch (e) { log(e.message) } - if (file.isDirectory()) { - const filesInFolder = [] - for await (let file of await getFilesRecursively(path.join(source, folderStructure), { withFileTypes: true })) { - filesInFolder.push(file) - } - await copyFilesFromSourceToDestination(filesInFolder, path.join(source, file.path), path.join(destination, folderStructure), hooks) - continue + async render(files, app, source, destination) { + const uris = [] + app.set('views', [source]) + for await (let file of files) { + if(file.path.toLowerCase().includes('/layouts') || file.path.toLowerCase().includes('/partials')) continue + + if(['.html', '.md', '.xml'].indexOf(path.extname(file.name)) == -1) { + await this.copyFilesFromSourceToDestination([file], source, destination) + continue + } + + const destinationFolderToCreate = file.path.replace(source, destination) + await File.mkdir(destinationFolderToCreate, { recursive: true }) + + const viewKey = path.join(file.path.replace(source, ''), file.name).replace(/^\//, '') + let defaultEngine = path.extname(file.name).replace('.', '') + app.set('view engine', defaultEngine) + let model = {} + for await (let hook of this.#hooks) { + if (!hook?.model) continue + model = Object.assign(model, await hook.model(file, model)) + } + app.render(viewKey, model, async (err, html, viewModel) => { + if (err) { + console.error('error rendering', err) + console.error('file', file.name) + throw err + } + html = html.split('\n').map(line => line.trim().replace(/^\s+/, '')).join('\n') + + const transformedFilePath = path.join(destinationFolderToCreate, file.name.replace('.md', '.html')) + log('Creating', transformedFilePath) + await File.writeFile(transformedFilePath, html, 'utf-8') + for await (let hook of this.#hooks) { + if (!hook?.transformed) continue + await hook.transformed(viewKey, transformedFilePath, file, model, html, viewModel) + } + uris.push(transformedFilePath.replace(destination.replace('./', ''), '')) + }) } - createReadStream(path.join(file.path, file.name)).pipe(createWriteStream(path.join(destination, folderStructure, file.name))) - for await (let hook of hooks) { - if (!hook?.copied) continue - await hook.copied(path.join(destination, folderStructure, file.name)) + return uris + } + async loadScripts(scripts, args) { + const all = [] + for await (let script of scripts) { + const filePath = path.resolve(script.path, script.name) + all.push(await this.loadScript(filePath, args)) } + return all } -} - -const getFilesRecursively = async function *(folder) { - try { - for await (let file of await File.readdir(folder, { withFileTypes: true })) { + async loadScript(filePath, args) { + let mod = null + try { + mod = await (await import(filePath)).default(this, args) + this.use(mod) + } catch (e) { + console.error(`Error running script ${filePath}`, e) + } + return mod + } + async registerPartials(folder, handlebars) { + const files = await File.readdir(folder, { withFileTypes: true }) + for await (let file of files) { + const filePath = `${folder}/${file.name}` if (file.isDirectory()) { - yield* getFilesRecursively(path.join(folder, file.name)) + await this.registerPartials(filePath, handlebars) } else { - file.path = (file.path ?? folder) // file.path wasn't added until node 20.1.0 - yield file + if(!(filePath.toLowerCase().includes('layouts') || filePath.toLowerCase().includes('partials'))) continue + if (!filePath.endsWith('.html')) continue + + const partialName = filePath.split(`${path.sep}${args.folder.split(path.sep).pop()}${path.sep}`).pop().replace(/^\//, '') + const partial = await File.readFile(filePath, 'utf-8') + log(folder, partialName) + handlebars.registerPartial(partialName, partial) + for await (let hook of this.#hooks) { + if (!hook?.partial) continue + await hook.partial(partialName, partial, handlebars) + } } - } - } catch(e) { - console.error(e) - } -} - -const loadScripts = async (scripts, app, args) => { - const all = [] - for await (let script of scripts) { - const filePath = path.resolve(script.path, script.name) - all.push(await loadScript(filePath, app, args)) - } - return all -} - -const loadScript = async (filePath, app, args) => { - let mod = null - try { - mod = await (await import(filePath)).default(app, args) - } catch (e) { - console.error(` - Error running script ${filePath} - `, e) + } } - return mod -} - -const transform = async (files, source, destination, hooks) => { - try{await File.mkdir(destination)}catch(e){} - for await (let folder of [source]) { - await registerPartials(folder, handlebars, hooks) + async *getFilesRecursively (folder) { + try { + for await (let file of await File.readdir(folder, { withFileTypes: true })) { + if (file.isDirectory()) { + yield* this.getFilesRecursively(path.join(folder, file.name)) + } else { + file.path = (file.path ?? folder) // file.path wasn't added until node 20.1.0 + yield file + } + } + } catch(e) { + console.error(e) + } } - await render(files, app, source, destination, hooks) } - - +app.settings.args = args const scriptsFolder = [] let scripts = [] +const sfab = new SiteFabricator(app, args) stream.createGroup('handlers:scripts', 'events:args:scripts', async messages => { const scriptsFolder = [] - for await(let script of await getFilesRecursively(args.scripts, { withFileTypes: true })) { + for await(let script of await sfab.getFilesRecursively(args.scripts, { withFileTypes: true })) { scriptsFolder.push(script) } - scripts = await loadScripts(scriptsFolder, app, args) + scripts = await sfab.loadScripts(scriptsFolder, args) }) stream.createGroup('handlers:help', 'events:args:help', async messages => { console.log(help) @@ -280,21 +291,21 @@ stream.createGroup('handlers:help', 'events:args:help', async messages => { stream.createGroup('handlers:folder', 'events:args:folder', async messages => { const filesInFolder = [] const folderWithoutDot = pathToFileURL(args.folder).pathname - for await (let file of await getFilesRecursively(folderWithoutDot, { withFileTypes: true })) { + for await (let file of await sfab.getFilesRecursively(folderWithoutDot, { withFileTypes: true })) { filesInFolder.push(file) } - await transform(filesInFolder, folderWithoutDot, pathToFileURL(args.destination).pathname ?? DESTINATION, scripts) + await sfab.transform(filesInFolder, folderWithoutDot, pathToFileURL(args.destination).pathname ?? DESTINATION, scripts) }) stream.createGroup('handlers:file', 'events:args:file', async messages => { const parts = args.file.replace('./', '').split(path.sep) const file = parts.pop() const filesInFolder = [] - for await (let file of await getFilesRecursively(parts.join(path.sep), { withFileTypes: true })) { + for await (let file of await sfab.getFilesRecursively(parts.join(path.sep), { withFileTypes: true })) { filesInFolder.push(file) } const files = filesInFolder.filter(f => f.name == file) - await transform(files, parts[0], args.destination ?? DESTINATION, scripts) + await sfab.transform(files, parts[0], args.destination ?? DESTINATION, scripts) }) stream.createGroup('handlers:copy', 'events:args:copy', async messages => { const filesInFolder = [] @@ -302,21 +313,20 @@ stream.createGroup('handlers:copy', 'events:args:copy', async messages => { args.copy = [args.copy] } for await (let folderWithoutDot of args.copy) { - folderWithoutDot = folderWithoutDot.replace('./', '') - for await (let file of await getFilesRecursively(folderWithoutDot, { withFileTypes: true })) { + folderWithoutDot = folderWithoutDot.replace(/^\.\//, '') + for await (let file of await sfab.getFilesRecursively(folderWithoutDot, { withFileTypes: true })) { filesInFolder.push(file) } - await copyFilesFromSourceToDestination(filesInFolder, folderWithoutDot, args.destination ?? DESTINATION, scripts) + await sfab.copyFilesFromSourceToDestination(filesInFolder, folderWithoutDot, args.destination ?? DESTINATION, scripts) } }) stream.createGroup('handlers:serve', 'events:args:serve', async messages => { console.log(messages, args) - app.listen(args.port ?? 3001, ()=>{ + sfab.app.listen(args.port ?? 3001, ()=>{ console.log(`listening on http://localhost:${args.port ?? 3001}`) if(args.serve) { - app.use(args.serve, express.static(args.destination ?? DESTINATION)) + sfab.app.use(args.serve, express.static(args.destination ?? DESTINATION)) } - }) stream.deleteGroup('handlers:serve') })