diff --git a/.vscode/launch.json b/.vscode/launch.json index 505e715..b9a5e24 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,68 +4,12 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "name": "restore", - "program": "${workspaceFolder}/restore", - "request": "launch", - "skipFiles": [ - "/**" - ], - "type": "node" - }, - { - "name": "stuff", - "program": "${workspaceFolder}/stuff.js", - "request": "launch", - "skipFiles": [ - "/**" - ], - "type": "node" - }, - { - "name": "analysis", - "program": "${workspaceFolder}/analysis.js", - "request": "launch", - "skipFiles": [ - "/**" - ], - "type": "node" - }, { "type": "pwa-chrome", "request": "launch", - "name": "client", - "url": "http://localhost:3001", - "webRoot": "${workspaceFolder}/site" - }, - { - "type": "pwa-chrome", - "request": "launch", - "name": "client2", + "name": "frontend", "url": "http://localhost:3000", - "webRoot": "${workspaceFolder}/site" + "webRoot": "${workspaceFolder}/site/output" }, - { - "type": "node", - "request": "attach", - "name": "server", - "port": 9230, - "address": "localhost", - "localRoot": "${workspaceFolder}", - "remoteRoot": "/heisse-preise", - "protocol": "inspector", - "restart": true, - "continueOnAttach": true - }, - ], - "compounds": [ - { - "name": "client-server", - "configurations": [ - "client", - "server" - ], - "stopAll": true - } ] } \ No newline at end of file diff --git a/template.js b/bundle.js old mode 100644 new mode 100755 similarity index 62% rename from template.js rename to bundle.js index 405d20f..7e146c4 --- a/template.js +++ b/bundle.js @@ -1,5 +1,7 @@ const fs = require("fs"); const path = require("path"); +const chokidar = require("chokidar"); +const esbuild = require("esbuild"); function deleteDirectory(directory) { if (fs.existsSync(directory)) { @@ -36,6 +38,7 @@ function processFile(inputFile, outputFile) { console.log(`${inputFile} -> ${outputFile}`); const fileDir = path.dirname(inputFile); const data = fs.readFileSync(inputFile, "utf8"); + if (data.includes(`require("`)) return; const replacedData = replaceFileContents(data, fileDir); fs.writeFileSync(outputFile, replacedData); } @@ -64,6 +67,40 @@ function generateSite(inputDir, outputDir, deleteOutput) { }); } -exports.generateSite = generateSite; +function generateSiteAndWatch(inputDir, outputDir, deleteDir = true, watch = false) { + generateSite(inputDir, outputDir, deleteDir); + if (!watch) return; -// generateSite("site", "site/output", true); + const watcher = chokidar.watch(inputDir, { ignored: /(^|[\/\\])\../ }); + let initialScan = true; + watcher.on("ready", () => (initialScan = false)); + watcher.on("all", (event, filePath) => { + if (initialScan) return; + if (path.resolve(filePath).startsWith(path.resolve(outputDir))) return; + console.log(`File ${filePath} has been ${event}`); + generateSite(inputDir, outputDir, false); + }); + console.log(`Watching directory for changes: ${inputDir}`); +} + +async function bundle(inputDir, outputDir, liveReload) { + let buildContext = await esbuild.context({ + entryPoints: { + "carts-new": `${inputDir}/carts-new.js`, + }, + bundle: true, + sourcemap: true, + outdir: outputDir, + logLevel: "debug", + }); + if (!liveReload) { + await buildContext.rebuild(); + } else { + buildContext.watch(); + } + generateSiteAndWatch(inputDir, outputDir, false, liveReload); +} + +exports.deleteDirectory = deleteDirectory; +exports.generateSite = generateSiteAndWatch; +exports.bundle = bundle; diff --git a/package-lock.json b/package-lock.json index 48fa277..f9c38c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,13 +17,378 @@ "node-html-parser": "^6.1.5" }, "devDependencies": { + "concurrently": "^8.1.0", + "esbuild": "^0.17.19", "husky": "^8.0.3", - "nodemon": "^2.0.22", "prettier": "^2.8.8", "pretty-quick": "^3.1.3", "socket.io": "^4.6.2" } }, + "node_modules/@babel/runtime": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.3.tgz", + "integrity": "sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", @@ -57,12 +422,6 @@ "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==", "dev": true }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -75,6 +434,15 @@ "node": ">= 0.6" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -249,6 +617,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -275,6 +680,20 @@ "fsevents": "~2.3.2" } }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -338,6 +757,57 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/concurrently": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.1.0.tgz", + "integrity": "sha512-0AB6eOAtaW/r/kX2lCdolaWtT191ICeuJjEJvI9hT3zbPFuZ/iZaJwMRKwbuwADome7OKxk73L7od+fsveZ7tA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.29.3", + "lodash": "^4.17.21", + "rxjs": "^7.8.0", + "shell-quote": "^1.8.0", + "spawn-command": "0.0.2-1", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.1" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -442,6 +912,22 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -531,6 +1017,12 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -621,6 +1113,52 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -824,6 +1362,15 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", @@ -875,15 +1422,6 @@ "node": ">= 0.4.0" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/has-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", @@ -973,12 +1511,6 @@ "node": ">= 4" } }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1011,6 +1543,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1060,6 +1601,12 @@ "node": ">=8" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1193,64 +1740,6 @@ "he": "1.2.0" } }, - "node_modules/nodemon": { - "version": "2.0.22", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", - "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", - "dev": true, - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^3.2.7", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^5.7.1", - "simple-update-notifier": "^1.0.7", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/nodemon/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1509,12 +1998,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -1580,6 +2063,30 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1590,15 +2097,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -1667,6 +2165,15 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -1686,27 +2193,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "node_modules/simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", - "dev": true, - "dependencies": { - "semver": "~7.0.0" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/socket.io": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.2.tgz", @@ -1792,6 +2278,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", + "dev": true + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -1800,6 +2292,32 @@ "node": ">= 0.8" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -1809,18 +2327,6 @@ "node": ">=6" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1840,18 +2346,21 @@ "node": ">=0.6" } }, - "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, - "dependencies": { - "nopt": "~1.0.10" - }, "bin": { - "nodetouch": "bin/nodetouch.js" + "tree-kill": "cli.js" } }, + "node_modules/tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "dev": true + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1864,12 +2373,6 @@ "node": ">= 0.6" } }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1909,6 +2412,23 @@ "node": ">= 8" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -1935,6 +2455,42 @@ "optional": true } } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } } } } diff --git a/package.json b/package.json index 7cb345c..1ba71eb 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "prepare": "husky install", "dev": "NODE_ENV=development PORT=$PORT node server.js", "start": "NODE_ENV=production PORT=$PORT node server.js", - "format": "npx prettier --write ." + "format": "npx prettier --write .", + "build": "bundle.js" }, "repository": { "type": "git", @@ -18,13 +19,6 @@ "bugs": { "url": "https://github.com/badlogic/heissepreise/issues" }, - "nodemonConfig": { - "ignore": [ - "data/*", - "site/output/*", - "node_modules/*" - ] - }, "homepage": "https://github.com/badlogic/heissepreise#readme", "dependencies": { "axios": "^1.4.0", @@ -35,8 +29,9 @@ "node-html-parser": "^6.1.5" }, "devDependencies": { + "concurrently": "^8.1.0", + "esbuild": "^0.17.19", "husky": "^8.0.3", - "nodemon": "^2.0.22", "prettier": "^2.8.8", "pretty-quick": "^3.1.3", "socket.io": "^4.6.2" diff --git a/pages.js b/pages.js index 81b7c92..376e3d4 100644 --- a/pages.js +++ b/pages.js @@ -1,7 +1,7 @@ const fs = require("fs"); const path = require("path"); const analysis = require("./analysis.js"); -const template = require("./template.js"); +const bundle = require("./bundle.js"); const outputDir = path.resolve("docs"); const dataDir = path.join(outputDir, "data"); @@ -26,7 +26,7 @@ function deleteFiles(folderPath) { try { if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir); deleteFiles(outputDir); - template.generateSite("site", outputDir, false); + bundle.bundle("site", outputDir, false); const data = analysis.readJSON(`${dataDir}/latest-canonical.json`); analysis.writeJSON(`${dataDir}/latest-canonical.json`, data, analysis.FILE_COMPRESSOR); diff --git a/server.js b/server.js index bc5c8c7..9039c9d 100644 --- a/server.js +++ b/server.js @@ -1,9 +1,9 @@ const fs = require("fs"); const path = require("path"); const http = require("http"); -const chokidar = require("chokidar"); const analysis = require("./analysis"); -const template = require("./template"); +const bundle = require("./bundle"); +const chokidar = require("chokidar"); const express = require("express"); const compression = require("compression"); @@ -37,21 +37,6 @@ function scheduleFunction(hour, minute, second, func) { }, delay); } -function generateSiteAndWatch(inputDir, outputDir) { - template.generateSite(inputDir, outputDir, true); - const watcher = chokidar.watch(inputDir, { ignored: /(^|[\/\\])\../ }); - - let initialScan = true; - watcher.on("ready", () => (initialScan = false)); - watcher.on("all", (event, filePath) => { - if (initialScan) return; - if (path.resolve(filePath).startsWith(path.resolve(outputDir))) return; - console.log(`File ${filePath} has been ${event}`); - template.generateSite(inputDir, outputDir, false); - }); - console.log(`Watching directory for changes: ${inputDir}`); -} - (async () => { const dataDir = "data"; @@ -81,7 +66,11 @@ function generateSiteAndWatch(inputDir, outputDir) { fs.mkdirSync(dataDir); } - generateSiteAndWatch("site", "site/output"); + const outputDir = "site/output"; + bundle.deleteDirectory(outputDir); + fs.mkdirSync(outputDir); + fs.mkdirSync(outputDir + "/data"); + bundle.bundle("site", outputDir, liveReload); analysis.migrateCompression(dataDir, ".json", ".json.br"); analysis.migrateCompression(dataDir, ".json.gz", ".json.br"); diff --git a/site/carts-new.html b/site/carts-new.html new file mode 100644 index 0000000..1517794 --- /dev/null +++ b/site/carts-new.html @@ -0,0 +1,17 @@ +%%_templates/_header.html%% %%_templates/_menu.html%% + +
+
+

Warenkörbe

+
+ + + +
+
+ +
+
+ + +%%_templates/_footer.html%% diff --git a/site/carts-new.js b/site/carts-new.js new file mode 100644 index 0000000..d645d18 --- /dev/null +++ b/site/carts-new.js @@ -0,0 +1,57 @@ +const { dom, downloadJSON } = require("./misc"); +const model = require("./model"); + +function render(carts) {} + +(async () => { + await model.load(); + const carts = model.carts.carts; + + document.querySelector("#new").addEventListener("click", () => { + let name = prompt("Name für Warenkorb eingeben:"); + if (!name || name.trim().length == 0) return; + name = name.trim(); + if (carts.some((cart) => cart.name === name)) { + alert("Warenkorb mit Namen '" + name + "' existiert bereits"); + return; + } + model.carts.add(name); + location.href = `/cart.html?name=${encodeURIComponent(name)}`; + }); + + document.querySelector("#export").addEventListener("click", () => { + downloadJSON("carts.json", carts); + }); + + document.querySelector("#import").addEventListener("click", () => {}); + + document.querySelector("#fileInput").addEventListener("change", function (event) { + const reader = new FileReader(); + reader.onload = (event) => { + const importedCarts = JSON.parse(event.target.result); + for (const importedCart of importedCarts) { + const items = []; + for (const cartItem of importedCart.items) { + const item = model.items.lookup[cartItem.store + cartItem.id]; + if (!item) continue; + items.push(item); + } + importedCart.items = items; + + const index = carts.findIndex((cart) => cart.name === importedCart.name); + if (index != -1) { + if (confirm("Existierenden Warenkorb '" + importedCart.name + " überschreiben?")) { + carts[index] = importedCart; + } + } else { + carts.push(importedCart); + } + } + model.carts.save(); + render(carts); + }; + reader.readAsText(event.target.files[0]); + }); + + render(carts); +})(); diff --git a/site/misc.js b/site/misc.js new file mode 100644 index 0000000..26cabac --- /dev/null +++ b/site/misc.js @@ -0,0 +1,46 @@ +if (typeof window !== "undefined") { + function setupLiveEdit() { + if (window.location.host.indexOf("localhost") < 0 && window.location.host.indexOf("127.0.0.1") < 0) return; + var script = document.createElement("script"); + script.type = "text/javascript"; + script.onload = () => { + let lastChangeTimestamp = null; + let socket = io({ transports: ["websocket"] }); + socket.on("connect", () => console.log("Connected")); + socket.on("disconnect", () => console.log("Disconnected")); + socket.on("message", (timestamp) => { + if (lastChangeTimestamp != timestamp) { + setTimeout(() => location.reload(), 100); + lastChangeTimestamp = timestamp; + } + }); + }; + script.src = "js/socket.io.js"; + document.body.appendChild(script); + } + setupLiveEdit(); +} + +exports.fetchJSON = async (url) => { + const response = await fetch(url); + return await response.json(); +}; + +exports.downloadJSON = (filename, content) => { + const json = JSON.stringify(content, null, 2); + const blob = new Blob([json], { type: "text/plain" }); + const element = document.createElement("a"); + element.href = URL.createObjectURL(blob); + element.download = filename; + element.style.display = "none"; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); + URL.revokeObjectURL(element.href); +}; + +exports.dom = (html) => { + const div = document.createElement("div"); + element.innerHTML = html; + return element.children[0]; +}; diff --git a/site/model/carts.js b/site/model/carts.js new file mode 100644 index 0000000..b011a07 --- /dev/null +++ b/site/model/carts.js @@ -0,0 +1,43 @@ +const misc = require("../misc"); + +exports.carts = []; + +exports.load = async (itemsLookup) => { + const val = localStorage.getItem("carts"); + const carts = (exports.carts = val ? JSON.parse(val) : []); + + // Add Momentum cart if it is not in the list of carts + if (!carts.some((cart) => cart.name === "Momentum Eigenmarken Vergleich")) { + const momentumCart = await misc.fetchJSON("data/momentum-cart.json"); + carts.unshift(momentumCart); + } + + // Update items in cart to their latest version. + for (const cart of carts) { + const items = []; + for (const cartItem of cart.items) { + const item = itemsLookup[cartItem.store + cartItem.id]; + if (!item) items.push(cartItem); + else items.push(item); + } + cart.items = items; + } + exports.save(); +}; + +exports.save = () => { + localStorage.setItem("carts", JSON.stringify(exports.carts, null, 2)); +}; + +exports.add = (name) => { + exports.carts.push({ + name: name, + items: [], + }); + exports.save(); +}; + +exports.remove = (name) => { + exports.carts = exports.carts.filter((cart) => cart.name !== name); + exports.save(); +}; diff --git a/site/model/categories.js b/site/model/categories.js new file mode 100644 index 0000000..de54c1d --- /dev/null +++ b/site/model/categories.js @@ -0,0 +1,116 @@ +// These are a match of the Billa categories, which are organized in a 2-level hierarchy. +// Each category in the top level gets a code from 1-Z, each sub category also gets a code. +// Together the two codes from a unique id for the category, which we store in the item.category +// field. E.g. "Obst & Gemüse > Salate" has the code "13", "Kühlwaren > Tofu" has the code "4C" +exports.categories = [ + { + name: "Obst & Gemüse", + subcategories: ["Obst", "Gemüse", "Salate", "Trockenfrüchte & Nüsse"], + }, + { + name: "Brot & Gebäck", + subcategories: ["Aufbackbrötchen & Toast", "Brot & Gebäck", "Knäckebrot & Zwieback", "Kuchen & Co.", "Semmelwürfel & Brösel"], + }, + { + name: "Getränke", + subcategories: ["Alkoholfreie Getränke", "Bier & Radler", "Kaffee, Tee & Co.", "Sekt & Champagner", "Spirituosen", "Wein", "Mineralwasser"], + }, + { + name: "Kühlwaren", + subcategories: [ + "Schnelle Küche", + "Eier", + "Fleisch", + "Käse, Aufstriche & Salate", + "Milchprodukte", + "Feinkostplatten & Brötchen", + "Blätterteig, Strudelteig", + "Wurst, Schinken & Speck", + "Feinkost", + "Fisch", + "Unbekannt", // Not available in Billa hierarchy, left blank + "Tofu", + ], + }, + { + name: "Tiefkühl", + subcategories: [ + "Eis", + "Unbekannt", // Not available in Billa hierarchy, left blank + "Fertiggerichte", + "Fisch & Garnelen", + "Gemüse & Kräuter", + "Pommes Frites & Co.", + "Pizza & Baguette", + "Desserts & Früchte", + ], + }, + { + name: "Grundnahrungsmittel", + subcategories: [ + "Asia & Mexican Produkte", + "Baby", + "Backen", + "Essig & Öl", + "Fertiggerichte", + "Gewürze & Würzmittel", + "Honig, Marmelade & Co.", + "Konserven & Sauerwaren", + "Kuchen & Co.", + "Mehl & Getreideprodukte", + "Müsli & Cerealien", + "Reis, Teigwaren & Sugo", + "Saucen & Dressings", + "Spezielle Ernährung", + "Zucker & Süßstoffe", + "Fixprodukte", + ], + }, + { + name: "Süßes & Salziges", + subcategories: ["Biskotten & Eiswaffeln", "Für kluge Naschkatzen", "Müsliriegel", "Chips & Co.", "Süßes"], + }, + { + name: "Pflege", + subcategories: [ + "Baby", + "Damenhygiene", + "Deodorants", + "Haarpflege & Haarfarben", + "Pflaster & Verbandsmaterial", + "Haut- & Lippenpflege", + "Mund- & Zahnhygiene", + "Rasierbedarf", + "Seife & Duschbäder", + "Sonnen- & Gelsenschutzmittel", + "Verhütungsmittel", + "Fußpflege", + "Strumpfhosen & Socken", + ], + }, + { + name: "Haushalt", + subcategories: [ + "Büro- & Schulartikel", + "Garten", + "Kleben & Befestigen", + "Küchenartikel", + "Küchenrollen & WC-Papier", + "Lampen & Batterien", + "Müllsäcke, Gefrierbeutel & Co.", + "Raumsprays & Kerzen", + "Reinigen & Pflegen", + "Taschentücher & Servietten", + "Waschmittel & Weichspüler", + "Schuhpflege", + "Kunststoffbehälter", + "Insektenschutz", + "Spielwaren", + "Hygiene-Schutzartikel", + ], + }, + { + name: "Haustier", + subcategories: ["Hunde", "Katzen", "Nager", "Vögel"], + }, +]; diff --git a/site/model/index.js b/site/model/index.js new file mode 100644 index 0000000..6ad5195 --- /dev/null +++ b/site/model/index.js @@ -0,0 +1,9 @@ +exports.stores = require("./stores"); +exports.categories = require("./categories"); +exports.items = require("./items"); +exports.carts = require("./carts"); + +exports.load = async () => { + await exports.items.load(); + await exports.carts.load(exports.items.lookup); +}; diff --git a/site/model/items.js b/site/model/items.js new file mode 100644 index 0000000..cdb96d3 --- /dev/null +++ b/site/model/items.js @@ -0,0 +1,104 @@ +const { stores, STORE_KEYS } = require("./stores"); + +function decompress(compressedItems) { + const items = []; + const storeLookup = compressedItems.stores; + const data = compressedItems.data; + const numItems = compressedItems.n; + let i = 0; + while (items.length < numItems) { + const store = storeLookup[data[i++]]; + const id = data[i++]; + const name = data[i++]; + const numPrices = data[i++]; + const prices = []; + for (let j = 0; j < numPrices; j++) { + const date = data[i++]; + const price = data[i++]; + prices.push({ + date: date.substring(0, 4) + "-" + date.substring(4, 6) + "-" + date.substring(6, 8), + price, + }); + } + const unit = data[i++]; + const quantity = data[i++]; + const isWeighted = data[i++] == 1; + const bio = data[i++] == 1; + const url = stores[store].getUrl({ id, name, url: data[i++] }); + + items.push({ + store, + id, + name, + price: prices[0].price, + priceHistory: prices, + isWeighted, + unit, + quantity, + bio, + url, + }); + } + return items; +} + +exports.items = []; +exports.lookup = {}; + +exports.load = async () => { + now = performance.now(); + const compressedItemsPerStore = []; + for (const store of STORE_KEYS) { + compressedItemsPerStore.push( + new Promise(async (resolve) => { + const now = performance.now(); + try { + const response = await fetch(`data/latest-canonical.${store}.compressed.json`); + const json = await response.json(); + console.log(`Loading compressed items for ${store} took ${(performance.now() - now) / 1000} secs`); + resolve(decompress(json)); + } catch (e) { + console.error(e); + console.log( + `Error while loading compressed items for ${store}. It took ${(performance.now() - now) / 1000} secs, continueing...` + ); + resolve([]); + } + }) + ); + } + const items = [].concat(...(await Promise.all(compressedItemsPerStore))); + console.log("Loading compressed items in parallel took " + (performance.now() - now) / 1000 + " secs"); + + const lookup = {}; + now = performance.now(); + for (const item of items) { + lookup[item.store + item.id] = item; + item.search = item.name + " " + item.quantity + " " + item.unit; + item.search = item.search.toLowerCase().replace(",", "."); + + item.numPrices = item.priceHistory.length; + item.priceOldest = item.priceHistory[item.priceHistory.length - 1].price; + item.dateOldest = item.priceHistory[item.priceHistory.length - 1].date; + item.date = item.priceHistory[0].date; + let highestPriceBefore = -1; + let lowestPriceBefore = 100000; + for (let i = 1; i < item.priceHistory.length; i++) { + const price = item.priceHistory[i]; + if (i < 10) { + item["price" + i] = price.price; + item["date" + i] = price.date; + } + highestPriceBefore = Math.max(highestPriceBefore, price.price); + lowestPriceBefore = Math.min(lowestPriceBefore, price.price); + } + if (highestPriceBefore == -1) highestPriceBefore = item.price; + if (lowestPriceBefore == 100000) lowestPriceBefore = item.price; + item.highestBefore = highestPriceBefore; + item.lowestBefore = lowestPriceBefore; + } + console.log("Processing items took " + (performance.now() - now) / 1000 + " secs"); + + exports.items = items; + exports.lookup = lookup; +}; diff --git a/site/model/stores.js b/site/model/stores.js new file mode 100644 index 0000000..78f079a --- /dev/null +++ b/site/model/stores.js @@ -0,0 +1,65 @@ +exports.stores = { + billa: { + name: "Billa", + budgetBrands: ["clever"], + color: "yellow", + getUrl: (item) => `https://shop.billa.at${item.url}`, + }, + spar: { + name: "Spar", + budgetBrands: ["s-budget"], + color: "green", + getUrl: (item) => `https://www.interspar.at/shop/lebensmittel${item.url}`, + }, + hofer: { + name: "Hofer", + budgetBrands: ["milfina"], + color: "purple", + getUrl: (item) => `https://www.roksh.at/hofer/produkte/${item.url}`, + }, + lidl: { + name: "Lidl", + budgetBrands: ["milbona"], + color: "pink", + getUrl: (item) => `https://www.lidl.at${item.url}`, + }, + mpreis: { + name: "MPREIS", + budgetBrands: [], + color: "rose", + getUrl: (item) => `https://www.mpreis.at/shop/p/${item.id}`, + }, + dm: { + name: "DM", + budgetBrands: ["balea"], + color: "orange", + getUrl: (item) => `https://www.dm.at/product-p${item.id}.html`, + }, + unimarkt: { + name: "Unimarkt", + budgetBrands: ["jeden tag", "unipur"], + color: "blue", + getUrl: (item) => `https://shop.unimarkt.at/${item.url}`, + }, + penny: { + name: "Penny", + budgetBrands: ["bravo", "echt bio!", "san fabio", "federike", "blik", "berida", "today", "ich bin österreich"], + color: "purple", + getUrl: (item) => `https://www.penny.at/produkte/${item.url}`, + }, + dmDe: { + name: "DM DE", + budgetBrands: ["balea"], + color: "teal", + getUrl: (item) => `https://www.dm.de/product-p${item.id}.html`, + }, + reweDe: { + name: "REWE DE", + budgetBrands: ["ja!"], + color: "stone", + getUrl: (item) => `https://shop.rewe.de/p/${item.name.toLowerCase().replace(/ /g, "-")}/${item.id}`, + }, +}; + +exports.STORE_KEYS = Object.keys(exports.stores); +exports.BUDGET_BRANDS = [...new Set([].concat(...Object.values(exports.stores).map((store) => store.budgetBrands)))];