mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 12:41:09 +01:00
[tgui] Reworks dev server into Bun + TS (#91695)
## About The Pull Request This started out as just a small fix for dev server crashing but lo, I cannot keep my hands off of such a juicy rework, it now uses Bun's own websocket server and is written entirely in typescript. It makes much more use of console.log (you can see these in client) as well ## Why It's Good For The Game Lets devs use tgui-dev again ## Changelog
This commit is contained in:
+17
-62
@@ -68,14 +68,9 @@
|
||||
"name": "tgui-dev-server",
|
||||
"version": "6.0.0",
|
||||
"dependencies": {
|
||||
"@rspack/core": "^1.3.7",
|
||||
"axios": "^1.8.4",
|
||||
"axios": "^1.10.0",
|
||||
"source-map": "^0.7.4",
|
||||
"stacktrace-parser": "^0.1.11",
|
||||
"ws": "^8.18.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ws": "^8.18.1",
|
||||
},
|
||||
},
|
||||
"packages/tgui-panel": {
|
||||
@@ -208,18 +203,6 @@
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||
|
||||
"@module-federation/error-codes": ["@module-federation/error-codes@0.14.3", "", {}, "sha512-sBJ3XKU9g5Up31jFeXPFsD8AgORV7TLO/cCSMuRewSfgYbG/3vSKLJmfHrO6+PvjZSb9VyV2UaF02ojktW65vw=="],
|
||||
|
||||
"@module-federation/runtime": ["@module-federation/runtime@0.14.3", "", { "dependencies": { "@module-federation/error-codes": "0.14.3", "@module-federation/runtime-core": "0.14.3", "@module-federation/sdk": "0.14.3" } }, "sha512-7ZHpa3teUDVhraYdxQGkfGHzPbjna4LtwbpudgzAxSLLFxLDNanaxCuSeIgSM9c+8sVUNC9kvzUgJEZB0krPJw=="],
|
||||
|
||||
"@module-federation/runtime-core": ["@module-federation/runtime-core@0.14.3", "", { "dependencies": { "@module-federation/error-codes": "0.14.3", "@module-federation/sdk": "0.14.3" } }, "sha512-xMFQXflLVW/AJTWb4soAFP+LB4XuhE7ryiLIX8oTyUoBBgV6U2OPghnFljPjeXbud72O08NYlQ1qsHw1kN/V8Q=="],
|
||||
|
||||
"@module-federation/runtime-tools": ["@module-federation/runtime-tools@0.14.3", "", { "dependencies": { "@module-federation/runtime": "0.14.3", "@module-federation/webpack-bundler-runtime": "0.14.3" } }, "sha512-QBETX7iMYXdSa3JtqFlYU+YkpymxETZqyIIRiqg0gW+XGpH3jgU68yjrme2NBJp7URQi/CFZG8KWtfClk0Pjgw=="],
|
||||
|
||||
"@module-federation/sdk": ["@module-federation/sdk@0.14.3", "", {}, "sha512-THJZMfbXpqjQOLblCQ8jjcBFFXsGRJwUWE9l/Q4SmuCSKMgAwie7yLT0qSGrHmyBYrsUjAuy+xNB4nfKP0pnGw=="],
|
||||
|
||||
"@module-federation/webpack-bundler-runtime": ["@module-federation/webpack-bundler-runtime@0.14.3", "", { "dependencies": { "@module-federation/runtime": "0.14.3", "@module-federation/sdk": "0.14.3" } }, "sha512-hIyJFu34P7bY2NeMIUHAS/mYUHEY71VTAsN0A0AqEJFSVPszheopu9VdXq0VDLrP9KQfuXT8SDxeYeJXyj0mgA=="],
|
||||
|
||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||
|
||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||
@@ -264,51 +247,27 @@
|
||||
|
||||
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
|
||||
|
||||
"@rspack/binding": ["@rspack/binding@1.3.15", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "1.3.15", "@rspack/binding-darwin-x64": "1.3.15", "@rspack/binding-linux-arm64-gnu": "1.3.15", "@rspack/binding-linux-arm64-musl": "1.3.15", "@rspack/binding-linux-x64-gnu": "1.3.15", "@rspack/binding-linux-x64-musl": "1.3.15", "@rspack/binding-win32-arm64-msvc": "1.3.15", "@rspack/binding-win32-ia32-msvc": "1.3.15", "@rspack/binding-win32-x64-msvc": "1.3.15" } }, "sha512-utNPuJglLO5lW9XbwIqjB7+2ilMo6JkuVLTVdnNVKU94FW7asn9F/qV+d+MgjUVqU1QPCGm0NuGO9xhbgeJ7pg=="],
|
||||
"@swc/core": ["@swc/core@1.12.4", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.4", "@swc/core-darwin-x64": "1.12.4", "@swc/core-linux-arm-gnueabihf": "1.12.4", "@swc/core-linux-arm64-gnu": "1.12.4", "@swc/core-linux-arm64-musl": "1.12.4", "@swc/core-linux-x64-gnu": "1.12.4", "@swc/core-linux-x64-musl": "1.12.4", "@swc/core-win32-arm64-msvc": "1.12.4", "@swc/core-win32-ia32-msvc": "1.12.4", "@swc/core-win32-x64-msvc": "1.12.4" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-hn30ebV4njAn0NAUM+3a0qCF+MJgqTNSrfA/hUAbC6TVjOQy2OYGQwkUvCu/V7S2+rZxrUsTpKOnZ7qqECZV9Q=="],
|
||||
|
||||
"@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@1.3.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-f+DnVRENRdVe+ufpZeqTtWAUDSTnP48jVo7x9KWsXf8XyJHUi+eHKEPrFoy1HvL1/k5yJ3HVnFBh1Hb9cNIwSg=="],
|
||||
"@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HihKfeitjZU2ab94Zf893sxzFryLKX0TweGsNXXOLNtkSMLw50auuYfpRM0BOL9/uXXtuCWgRIF6P030SAX5xQ=="],
|
||||
|
||||
"@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@1.3.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-TfUvEIBqYUT2OK01BYXb2MNcZeZIhAnJy/5aj0qV0uy4KlvwW63HYcKWa1sFd4Ac7bnGShDkanvP3YEuHOFOyg=="],
|
||||
"@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-meYCXHyYb6RDdu2N5PNAf0EelyxPBFhRcVo4kBFLuvuNb0m6EUg///VWy8MUMXq9/s9uzGS9kJVXXdRdr/d6FA=="],
|
||||
|
||||
"@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@1.3.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-D/YjYk9snKvYm1Elotq8/GsEipB4ZJWVv/V8cZ+ohhFNOPzygENi6JfyI06TryBTQiN0/JDZqt/S9RaWBWnMqw=="],
|
||||
"@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.4", "", { "os": "linux", "cpu": "arm" }, "sha512-szfDbf7mE8V64of0q/LSqbk+em+T+TD3uqnH40Z7Qu/aL8vi5CHgyLjWG2SLkLLpyjgkAUF6AKrupgnBYcC2NA=="],
|
||||
|
||||
"@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@1.3.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-lJbBsPMOiR0hYPCSM42yp7QiZjfo0ALtX7ws2wURpsQp3BMfRVAmXU3Ixpo2XCRtG1zj8crHaCmAWOJTS0smsA=="],
|
||||
"@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-n0IY76w+Scx8m3HIVRvLkoResuwsQgjDfAk9bxn99dq4leQO+mE0fkPl0Yw/1BIsPh+kxGfopIJH9zsZ1Z2YrA=="],
|
||||
|
||||
"@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@1.3.15", "", { "os": "linux", "cpu": "x64" }, "sha512-qGB8ucHklrzNg6lsAS36VrBsCbOw0acgpQNqTE5cuHWrp1Pu3GFTRiFEogenxEmzoRbohMZt0Ev5grivrcgKBQ=="],
|
||||
"@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-wE5jmFi5cEQyLy8WmCWmNwfKETrnzy2D8YNi/xpYWpLPWqPhcelpa6tswkfYlbsMmmOh7hQNoTba1QdGu0jvHQ=="],
|
||||
|
||||
"@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@1.3.15", "", { "os": "linux", "cpu": "x64" }, "sha512-qRn6e40fLQP+N2rQD8GAj/h4DakeTIho32VxTIaHRVuzw68ZD7VmKkwn55ssN370ejmey35ZdoNFNE12RBrMZA=="],
|
||||
"@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6S50Xd/7ePjEwrXyHMxpKTZ+KBrgUwMA8hQPbArUOwH4S5vHBr51heL0iXbUkppn1bkSr0J0IbOove5hzn+iqQ=="],
|
||||
|
||||
"@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@1.3.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-7uJ7dWhO1nWXJiCss6Rslz8hoAxAhFpwpbWja3eHgRb7O4NPHg6MWw63AQSI2aFVakreenfu9yXQqYfpVWJ2dA=="],
|
||||
"@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-hbYRyaHhC13vYKuGG5BrAG5fjjWEQFfQetuFp/4QKEoXDzdnabJoixxWTQACDL3m0JW32nJ+gUzsYIPtFYkwXg=="],
|
||||
|
||||
"@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@1.3.15", "", { "os": "win32", "cpu": "ia32" }, "sha512-UsaWTYCjDiSCB0A0qETgZk4QvhwfG8gCrO4SJvA+QSEWOmgSai1YV70prFtLLIiyT9mDt1eU3tPWl1UWPRU/EQ=="],
|
||||
"@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-e6EbfjPL8GA/bb1lc9Omtxjlz+1ThTsAuBsy4Q3Kpbuh6B3jclg8KzxU/6t91v23wG593mieTyR5f3Pr7X3AWw=="],
|
||||
|
||||
"@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@1.3.15", "", { "os": "win32", "cpu": "x64" }, "sha512-ZnDIc9Es8EF94MirPDN+hOMt7tkb8nMEbRJFKLMmNd0ElNPgsql+1cY5SqyGRH1hsKB87KfSUQlhFiKZvzbfIg=="],
|
||||
"@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-RG2FzmllBTUf4EksANlIvLckcBrLZEA0t13LIa6L213UZKQfEuDNHezqESgoVhJMg2S/tWauitATOCFgZNSmjg=="],
|
||||
|
||||
"@rspack/core": ["@rspack/core@1.3.15", "", { "dependencies": { "@module-federation/runtime-tools": "0.14.3", "@rspack/binding": "1.3.15", "@rspack/lite-tapable": "1.0.1" }, "peerDependencies": { "@swc/helpers": ">=0.5.1" }, "optionalPeers": ["@swc/helpers"] }, "sha512-QuElIC8jXSKWAp0LSx18pmbhA7NiA5HGoVYesmai90UVxz98tud0KpMxTVCg+0lrLrnKZfCWN9kwjCxM5pGnrA=="],
|
||||
|
||||
"@rspack/lite-tapable": ["@rspack/lite-tapable@1.0.1", "", {}, "sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w=="],
|
||||
|
||||
"@swc/core": ["@swc/core@1.12.1", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.1", "@swc/core-darwin-x64": "1.12.1", "@swc/core-linux-arm-gnueabihf": "1.12.1", "@swc/core-linux-arm64-gnu": "1.12.1", "@swc/core-linux-arm64-musl": "1.12.1", "@swc/core-linux-x64-gnu": "1.12.1", "@swc/core-linux-x64-musl": "1.12.1", "@swc/core-win32-arm64-msvc": "1.12.1", "@swc/core-win32-ia32-msvc": "1.12.1", "@swc/core-win32-x64-msvc": "1.12.1" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-aKXdDTqxTVFl/bKQZ3EQUjEMBEoF6JBv29moMZq0kbVO43na6u/u+3Vcbhbrh+A2N0X5OL4RaveuWfAjEgOmeA=="],
|
||||
|
||||
"@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-nUjWVcJ3YS2N40ZbKwYO2RJ4+o2tWYRzNOcIQp05FqW0+aoUCVMdAUUzQinPDynfgwVshDAXCKemY8X7nN5MaA=="],
|
||||
|
||||
"@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-OGm4a4d3OeJn+tRt8H/eiHgTFrJbS6r8mi/Ob65tAEXZGHN900T2kR7c5ALr0V2hBOQ8BfhexwPoQlGQP/B95w=="],
|
||||
|
||||
"@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.1", "", { "os": "linux", "cpu": "arm" }, "sha512-76YeeQKyK0EtNkQiNBZ0nbVGooPf9IucY0WqVXVpaU4wuG7ZyLEE2ZAIgXafIuzODGQoLfetue7I8boMxh1/MA=="],
|
||||
|
||||
"@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-BxJDIJPq1+aCh9UsaSAN6wo3tuln8UhNXruOrzTI8/ElIig/3sAueDM6Eq7GvZSGGSA7ljhNATMJ0elD7lFatQ=="],
|
||||
|
||||
"@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-NhLdbffSXvY0/FwUSAl4hKBlpe5GHQGXK8DxTo3HHjLsD9sCPYieo3vG0NQoUYAy4ZUY1WeGjyxeq4qZddJzEQ=="],
|
||||
|
||||
"@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.1", "", { "os": "linux", "cpu": "x64" }, "sha512-CrYnV8SZIgArQ9LKH0xEF95PKXzX9WkRSc5j55arOSBeDCeDUQk1Bg/iKdnDiuj5HC1hZpvzwMzSBJjv+Z70jA=="],
|
||||
|
||||
"@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.1", "", { "os": "linux", "cpu": "x64" }, "sha512-BQMl3d0HaGB0/h2xcKlGtjk/cGRn2tnbsaChAKcjFdCepblKBCz1pgO/mL7w5iXq3s57wMDUn++71/a5RAkZOA=="],
|
||||
|
||||
"@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-b7NeGnpqTfmIGtUqXBl0KqoSmOnH64nRZoT5l4BAGdvwY7nxitWR94CqZuwyLPty/bLywmyDA9uO12Kvgb3+gg=="],
|
||||
|
||||
"@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-iU/29X2D7cHBp1to62cUg/5Xk8K+lyOJiKIGGW5rdzTW/c2zz3d/ehgpzVP/rqC4NVr88MXspqHU4il5gmDajw=="],
|
||||
|
||||
"@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.1", "", { "os": "win32", "cpu": "x64" }, "sha512-+Zh+JKDwiFqV5N9yAd2DhYVGPORGh9cfenu1ptr9yge+eHAf7vZJcC3rnj6QMR1QJh0Y5VC9+YBjRFjZVA7XDw=="],
|
||||
"@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.4", "", { "os": "win32", "cpu": "x64" }, "sha512-oRHKnZlR83zaMeVUCmHENa4j5uNRAWbmEpjYbzRcfC45LPFNWKGWGAGERLx0u87XMUtTGqnVYxnBTHN/rzDHOw=="],
|
||||
|
||||
"@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="],
|
||||
|
||||
@@ -340,8 +299,6 @@
|
||||
|
||||
"@types/wicg-file-system-access": ["@types/wicg-file-system-access@2023.10.6", "", {}, "sha512-YO/183gNRzZFSdKu+ikkD7ambAj4PhgjFAF2A/Mw/7wroSF6ne8r804RkpZzqrJ/F6DO2/IYlQF/ULOZ/bhKyA=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.34.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.34.1", "@typescript-eslint/types": "8.34.1", "@typescript-eslint/typescript-estree": "8.34.1", "@typescript-eslint/visitor-keys": "8.34.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA=="],
|
||||
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.34.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.34.1", "@typescript-eslint/types": "^8.34.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA=="],
|
||||
@@ -490,7 +447,7 @@
|
||||
|
||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001723", "", {}, "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw=="],
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001724", "", {}, "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA=="],
|
||||
|
||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
@@ -590,7 +547,7 @@
|
||||
|
||||
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.168", "", {}, "sha512-RUNQmFLNIWVW6+z32EJQ5+qx8ci6RGvdtDC0Ls+F89wz6I2AthpXF0w0DIrn2jpLX0/PU9ZCo+Qp7bg/EckJmA=="],
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.171", "", {}, "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
@@ -1254,7 +1211,7 @@
|
||||
|
||||
"tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
|
||||
|
||||
"terser": ["terser@5.42.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ=="],
|
||||
"terser": ["terser@5.43.1", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg=="],
|
||||
|
||||
"terser-webpack-plugin": ["terser-webpack-plugin@5.3.14", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", "serialize-javascript": "^6.0.2", "terser": "^5.31.1" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw=="],
|
||||
|
||||
@@ -1264,7 +1221,7 @@
|
||||
|
||||
"tgui": ["tgui@workspace:packages/tgui"],
|
||||
|
||||
"tgui-core": ["tgui-core@4.3.0", "", { "dependencies": { "@floating-ui/react": "^0.27.12", "@nozbe/microfuzz": "^1.0.0" }, "peerDependencies": { "react": "^19.1.0", "react-dom": "^19.1.0" } }, "sha512-sH6FV3J3civqT2rS0Sr5jVW5uqB2BRgNTy5un6OKE/fxsGrglv+9wHyKJQcXNMvtLlPiFVaZvfyyPuAniAkrfw=="],
|
||||
"tgui-core": ["tgui-core@4.3.1", "", { "dependencies": { "@floating-ui/react": "^0.27.12", "@nozbe/microfuzz": "^1.0.0" }, "peerDependencies": { "react": "^19.1.0", "react-dom": "^19.1.0" } }, "sha512-9rHtBQyzDnMR/C09e+WqeMfCulbAX7eY/girN0o+K7ViZArx+s5SaISwwA8bPrUTzsS/JkwwV5bI/x5C5lnZGw=="],
|
||||
|
||||
"tgui-dev-server": ["tgui-dev-server@workspace:packages/tgui-dev-server"],
|
||||
|
||||
@@ -1368,7 +1325,7 @@
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="],
|
||||
"ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
|
||||
|
||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||
|
||||
@@ -1470,8 +1427,6 @@
|
||||
|
||||
"webpack/webpack-sources": ["webpack-sources@3.3.2", "", {}, "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA=="],
|
||||
|
||||
"webpack-bundle-analyzer/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
|
||||
|
||||
"webpack-cli/@discoveryjs/json-ext": ["@discoveryjs/json-ext@0.6.3", "", {}, "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ=="],
|
||||
|
||||
"webpack-cli/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
"scripts": {
|
||||
"tgui:analyze": "webpack --analyze",
|
||||
"tgui:build": "webpack",
|
||||
"tgui:dev": "bun packages/tgui-dev-server/index.js",
|
||||
"tgui:dev": "bun packages/tgui-dev-server/index.ts",
|
||||
"tgui:lint": "eslint packages --ext .js,.cjs,.ts,.tsx",
|
||||
"tgui:prettier": "prettier --check .",
|
||||
"tgui:test": "bun test",
|
||||
|
||||
+20
-36
@@ -7,35 +7,33 @@
|
||||
import { exec } from 'node:child_process';
|
||||
import { promisify } from 'node:util';
|
||||
|
||||
import axios, { isAxiosError } from 'axios';
|
||||
import axios, { AxiosInstance, AxiosResponse } from 'axios';
|
||||
|
||||
import { createLogger } from './logging.js';
|
||||
import { createLogger } from './logging';
|
||||
|
||||
type Entry = {
|
||||
addr: string;
|
||||
pid: number;
|
||||
};
|
||||
|
||||
const logger = createLogger('dreamseeker');
|
||||
|
||||
const instanceByPid = new Map();
|
||||
|
||||
export class DreamSeeker {
|
||||
/**
|
||||
* @param {number} pid
|
||||
* @param {string} addr
|
||||
*/
|
||||
constructor(pid, addr) {
|
||||
/** @type {number} */
|
||||
public pid: number;
|
||||
public addr: string;
|
||||
public client: AxiosInstance;
|
||||
|
||||
constructor(pid: number, addr: string) {
|
||||
this.pid = pid;
|
||||
/** @type {string} */
|
||||
this.addr = addr;
|
||||
/** @type {import('axios').AxiosInstance} */
|
||||
this.client = axios.create({
|
||||
baseURL: `http://${addr}`,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} params
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
topic(params = {}) {
|
||||
topic(params: Record<string, any> = {}): Promise<AxiosResponse> {
|
||||
const query = Object.keys(params)
|
||||
.map(
|
||||
(key) =>
|
||||
@@ -45,26 +43,12 @@ export class DreamSeeker {
|
||||
logger.log(
|
||||
`topic call at ${this.client.defaults.baseURL}/dummy.htm?${query}`,
|
||||
);
|
||||
return this.client.get('/dummy.htm?' + query).catch((e) => {
|
||||
if (isAxiosError(e) && e.code === 'ECONNREFUSED') {
|
||||
// Client exited, remove from list
|
||||
instanceByPid.delete(this.pid);
|
||||
logger.log(`client disconnected`);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
return this.client.get('/dummy.htm?' + query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number[]} pids
|
||||
* @returns {Promise<DreamSeeker[]>}
|
||||
*/
|
||||
static async getInstancesByPids(pids) {
|
||||
/** @type {DreamSeeker[]} */
|
||||
const instances = [];
|
||||
/** @type {number[]} */
|
||||
const pidsToResolve = [];
|
||||
static async getInstancesByPids(pids: number[]): Promise<DreamSeeker[]> {
|
||||
const instances: DreamSeeker[] = [];
|
||||
const pidsToResolve: number[] = [];
|
||||
|
||||
for (let pid of pids) {
|
||||
const instance = instanceByPid.get(pid);
|
||||
@@ -89,7 +73,7 @@ export class DreamSeeker {
|
||||
|
||||
// Line format:
|
||||
// proto addr mask mode pid
|
||||
const entries = [];
|
||||
const entries: Entry[] = [];
|
||||
const lines = stdout.split('\r\n');
|
||||
|
||||
for (let line of lines) {
|
||||
@@ -97,7 +81,7 @@ export class DreamSeeker {
|
||||
if (!words || words.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const entry = {
|
||||
const entry: Entry = {
|
||||
addr: words[1],
|
||||
pid: parseInt(words[4], 10),
|
||||
};
|
||||
@@ -126,6 +110,6 @@ export class DreamSeeker {
|
||||
}
|
||||
}
|
||||
|
||||
function plural(word, n) {
|
||||
function plural(word: string, n: number): string {
|
||||
return n !== 1 ? word + 's' : word;
|
||||
}
|
||||
@@ -4,16 +4,17 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { reloadByondCache } from './reloader.js';
|
||||
import { createCompiler } from './webpack.js';
|
||||
import fs from 'node:fs';
|
||||
|
||||
import { reloadByondCache } from './reloader';
|
||||
import { createCompiler } from './webpack';
|
||||
|
||||
const noTmp = process.argv.includes('--no-tmp');
|
||||
const reloadOnce = process.argv.includes('--reload');
|
||||
|
||||
async function setupServer() {
|
||||
const compiler = await createCompiler({
|
||||
useTmpFolder: !noTmp,
|
||||
});
|
||||
fs.mkdirSync('./public/.tmp', { recursive: true });
|
||||
|
||||
const compiler = await createCompiler({ mode: 'development', hot: true });
|
||||
|
||||
// Reload cache once
|
||||
if (reloadOnce) {
|
||||
@@ -1,169 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
let socket;
|
||||
const queue = [];
|
||||
const subscribers = [];
|
||||
|
||||
function ensureConnection() {
|
||||
if (process.env.NODE_ENV === 'production') return;
|
||||
|
||||
if (socket && socket.readyState !== WebSocket.CLOSED) return;
|
||||
|
||||
if (!window.WebSocket) return;
|
||||
|
||||
const DEV_SERVER_IP = process.env.DEV_SERVER_IP || '127.0.0.1';
|
||||
|
||||
socket = new WebSocket(`ws://${DEV_SERVER_IP}:3000`);
|
||||
|
||||
socket.onopen = () => {
|
||||
// Empty the message queue
|
||||
while (queue.length !== 0) {
|
||||
const msg = queue.shift();
|
||||
socket.send(msg);
|
||||
}
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
const msg = JSON.parse(event.data);
|
||||
for (let subscriber of subscribers) {
|
||||
subscriber(msg);
|
||||
}
|
||||
};
|
||||
|
||||
window.onunload = () => socket?.close();
|
||||
}
|
||||
|
||||
export function subscribe(fn) {
|
||||
subscribers.push(fn);
|
||||
}
|
||||
|
||||
function primitiveReviver(value) {
|
||||
if (typeof value === 'number' && !Number.isFinite(value)) {
|
||||
return {
|
||||
__number__: String(value),
|
||||
};
|
||||
}
|
||||
if (typeof value === 'undefined') {
|
||||
return {
|
||||
__undefined__: true,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* A json serializer which handles circular references and other junk.
|
||||
*/
|
||||
function serializeObject(obj) {
|
||||
let refs = [];
|
||||
|
||||
function objectReviver(key, value) {
|
||||
if (typeof value !== 'object') {
|
||||
return primitiveReviver(value);
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
return value;
|
||||
}
|
||||
// Circular reference
|
||||
if (refs.includes(value)) {
|
||||
return '[circular ref]';
|
||||
}
|
||||
refs.push(value);
|
||||
// Error object
|
||||
const isError =
|
||||
value instanceof Error ||
|
||||
(value.code && value.message && value.message.includes('Error'));
|
||||
if (isError) {
|
||||
return {
|
||||
__error__: true,
|
||||
string: String(value),
|
||||
stack: value.stack,
|
||||
};
|
||||
}
|
||||
// Array
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(primitiveReviver);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const json = JSON.stringify(obj, objectReviver);
|
||||
refs = null;
|
||||
return json;
|
||||
}
|
||||
|
||||
export function sendMessage(msg) {
|
||||
if (process.env.NODE_ENV === 'production') return;
|
||||
|
||||
const json = serializeObject(msg);
|
||||
// Send message using WebSocket
|
||||
if (!window.WebSocket) return;
|
||||
|
||||
ensureConnection();
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(json);
|
||||
} else {
|
||||
// Keep only 100 latest messages in the queue
|
||||
if (queue.length > 100) {
|
||||
queue.shift();
|
||||
}
|
||||
queue.push(json);
|
||||
}
|
||||
}
|
||||
|
||||
export function sendLogEntry(level, ns, ...args) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
try {
|
||||
sendMessage({
|
||||
type: 'log',
|
||||
payload: {
|
||||
level,
|
||||
ns: ns || 'client',
|
||||
args,
|
||||
},
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
}
|
||||
|
||||
export function setupHotReloading() {
|
||||
if (
|
||||
process.env.NODE_ENV === 'production' ||
|
||||
!process.env.WEBPACK_HMR_ENABLED ||
|
||||
!window.WebSocket
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (!import.meta.webpackHot) return;
|
||||
|
||||
ensureConnection();
|
||||
sendLogEntry(0, null, 'setting up hot reloading');
|
||||
subscribe(({ type }) => {
|
||||
sendLogEntry(0, null, 'received', type);
|
||||
if (type !== 'hotUpdate') return;
|
||||
|
||||
const status = import.meta.webpackHot.status();
|
||||
if (status !== 'idle') {
|
||||
sendLogEntry(0, null, 'hot reload status:', status);
|
||||
return;
|
||||
}
|
||||
|
||||
import.meta.webpackHot
|
||||
.check({
|
||||
ignoreUnaccepted: true,
|
||||
ignoreDeclined: true,
|
||||
ignoreErrored: true,
|
||||
})
|
||||
.then((modules) => {
|
||||
sendLogEntry(0, null, 'outdated modules', modules);
|
||||
})
|
||||
.catch((err) => {
|
||||
sendLogEntry(0, null, 'reload error', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
type Messenger = (msg: any) => void;
|
||||
|
||||
let socket: WebSocket | undefined;
|
||||
const queue: string[] = [];
|
||||
const subscribers: Messenger[] = [];
|
||||
|
||||
function ensureConnection() {
|
||||
if (socket) return;
|
||||
console.log('Creating a connection');
|
||||
|
||||
socket = new WebSocket('ws://127.0.0.1:3000');
|
||||
|
||||
socket.onopen = () => {
|
||||
// Empty the message queue
|
||||
while (queue.length !== 0) {
|
||||
const msg = queue.shift();
|
||||
if (msg) {
|
||||
socket?.send(msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
const msg = JSON.parse(event.data);
|
||||
for (let subscriber of subscribers) {
|
||||
subscriber(msg);
|
||||
}
|
||||
};
|
||||
|
||||
socket.onerror = (err) => {
|
||||
console.log('WebSocket error:', err);
|
||||
};
|
||||
|
||||
window.addEventListener('unload', () => socket?.close());
|
||||
}
|
||||
|
||||
function primitiveReviver(value: unknown): any {
|
||||
if (typeof value === 'number' && !Number.isFinite(value)) {
|
||||
return {
|
||||
__number__: String(value),
|
||||
};
|
||||
}
|
||||
if (typeof value === 'undefined') {
|
||||
return {
|
||||
__undefined__: true,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function sendLogEntry(
|
||||
level: number,
|
||||
ns: string | null = 'client',
|
||||
...args: any[]
|
||||
): void {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
try {
|
||||
sendMessage({
|
||||
type: 'log',
|
||||
payload: {
|
||||
level,
|
||||
ns,
|
||||
args,
|
||||
},
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
}
|
||||
|
||||
export function sendMessage(msg: Record<string, any>): void {
|
||||
if (process.env.NODE_ENV === 'development' && socket) {
|
||||
const json = serializeObject(msg);
|
||||
// Send message using WebSocket
|
||||
if (!window.WebSocket) return;
|
||||
|
||||
ensureConnection();
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(json);
|
||||
} else {
|
||||
// Keep only 100 latest messages in the queue
|
||||
if (queue.length > 100) {
|
||||
queue.shift();
|
||||
}
|
||||
queue.push(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A json serializer which handles circular references and other junk. */
|
||||
function serializeObject(obj: Record<string, any>): string {
|
||||
let refs: string[] = [];
|
||||
|
||||
function objectReviver(key: string, value: any) {
|
||||
if (typeof value !== 'object') {
|
||||
return primitiveReviver(value);
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
return value;
|
||||
}
|
||||
// Circular reference
|
||||
if (refs.includes(value)) {
|
||||
return '[circular ref]';
|
||||
}
|
||||
refs.push(value);
|
||||
// Error object
|
||||
const isError =
|
||||
value instanceof Error ||
|
||||
(value.code && value.message && value.message.includes('Error'));
|
||||
if (isError) {
|
||||
return {
|
||||
__error__: true,
|
||||
string: String(value),
|
||||
stack: value.stack,
|
||||
};
|
||||
}
|
||||
// Array
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(primitiveReviver);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const json = JSON.stringify(obj, objectReviver);
|
||||
refs = [];
|
||||
return json;
|
||||
}
|
||||
|
||||
export function setupHotReloading(): void {
|
||||
if (process.env.NODE_ENV === 'development' && window.WebSocket) {
|
||||
console.log('Setting up hot reloading...');
|
||||
if (socket) {
|
||||
socket.close();
|
||||
socket = undefined;
|
||||
subscribers.length = 0;
|
||||
}
|
||||
ensureConnection();
|
||||
subscribe(({ type }) => {
|
||||
if (type !== 'hotUpdate') return;
|
||||
|
||||
const status = import.meta.webpackHot?.status();
|
||||
if (status === 'ready') {
|
||||
import.meta.webpackHot
|
||||
?.apply({
|
||||
ignoreUnaccepted: true,
|
||||
ignoreDeclined: true,
|
||||
ignoreErrored: true,
|
||||
})
|
||||
.then((modules) => {
|
||||
console.log('outdated modules:', modules);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(import.meta.webpackHot?.status());
|
||||
console.error('Hot reload error:', err);
|
||||
});
|
||||
} else if (status === 'idle') {
|
||||
import.meta.webpackHot
|
||||
?.check(true)
|
||||
.then((updatedModules) => {
|
||||
console.log('Updated modules:', updatedModules);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Hot reload error:', err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function subscribe(fn: Messenger): void {
|
||||
subscribers.push(fn);
|
||||
}
|
||||
+27
-26
@@ -1,45 +1,46 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import { basename } from 'node:path';
|
||||
import path from 'node:path';
|
||||
|
||||
import { SourceMapConsumer } from 'source-map';
|
||||
import { parse as parseStackTrace } from 'stacktrace-parser';
|
||||
|
||||
import { createLogger } from '../logging.js';
|
||||
import { resolveGlob } from '../util.js';
|
||||
import { createLogger } from '../logging';
|
||||
import { resolveGlob } from '../util';
|
||||
|
||||
type SourceMap = {
|
||||
file: string;
|
||||
consumer: SourceMapConsumer;
|
||||
};
|
||||
|
||||
const logger = createLogger('retrace');
|
||||
|
||||
const sourceMaps = [];
|
||||
const sourceMaps: SourceMap[] = [];
|
||||
|
||||
export async function loadSourceMaps(bundleDir) {
|
||||
export async function loadSourceMaps(bundleDir: string): Promise<void> {
|
||||
// Destroy and garbage collect consumers
|
||||
while (sourceMaps.length !== 0) {
|
||||
const { consumer } = sourceMaps.shift();
|
||||
consumer.destroy();
|
||||
const map = sourceMaps.shift();
|
||||
if (!map?.consumer) continue;
|
||||
map.consumer.destroy();
|
||||
}
|
||||
|
||||
// Load new sourcemaps
|
||||
const paths = await resolveGlob(bundleDir, '*.map');
|
||||
for (let path of paths) {
|
||||
const files = await resolveGlob(bundleDir, '*.map');
|
||||
for (let file of files) {
|
||||
try {
|
||||
const file = basename(path).replace('.map', '');
|
||||
const consumer = await new SourceMapConsumer(
|
||||
JSON.parse(fs.readFileSync(path, 'utf8')),
|
||||
);
|
||||
const loc = path.resolve(bundleDir, file);
|
||||
const parsed = await Bun.file(loc).json();
|
||||
const consumer = await new SourceMapConsumer(parsed);
|
||||
|
||||
sourceMaps.push({ file, consumer });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
logger.log(`loaded ${sourceMaps.length} source maps`);
|
||||
}
|
||||
|
||||
export function retrace(stack) {
|
||||
export function retrace(stack: string): string | undefined {
|
||||
if (typeof stack !== 'string') {
|
||||
logger.log('ERROR: Stack is not a string!', stack);
|
||||
return stack;
|
||||
@@ -52,7 +53,7 @@ export function retrace(stack) {
|
||||
}
|
||||
// Find the correct source map
|
||||
const sourceMap = sourceMaps.find((sourceMap) => {
|
||||
return frame.file.includes(sourceMap.file);
|
||||
return frame.file!.includes(sourceMap.file);
|
||||
});
|
||||
if (!sourceMap) {
|
||||
return frame;
|
||||
@@ -60,9 +61,8 @@ export function retrace(stack) {
|
||||
// Map the frame
|
||||
const { consumer } = sourceMap;
|
||||
const mappedFrame = consumer.originalPositionFor({
|
||||
source: basename(frame.file),
|
||||
line: frame.lineNumber,
|
||||
column: frame.column,
|
||||
line: frame.lineNumber || 0,
|
||||
column: frame.column || 0,
|
||||
});
|
||||
return {
|
||||
...frame,
|
||||
@@ -78,10 +78,11 @@ export function retrace(stack) {
|
||||
return ` at ${methodName}`;
|
||||
}
|
||||
const compactPath = file
|
||||
.replace(/^webpack:\/\/\/?/, './')
|
||||
.replace(/^rspack:\/\/\/?/, './')
|
||||
.replace(/.*node_modules\//, '');
|
||||
return ` at ${methodName} (${compactPath}:${lineNumber})`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
return header + '\n' + mappedStack;
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { inspect } from 'node:util';
|
||||
|
||||
import * as WebSocket from 'ws';
|
||||
|
||||
import { createLogger, directLog } from '../logging.js';
|
||||
import { loadSourceMaps, retrace } from './retrace.js';
|
||||
|
||||
const logger = createLogger('link');
|
||||
|
||||
const DEBUG = process.argv.includes('--debug');
|
||||
|
||||
export { loadSourceMaps };
|
||||
|
||||
export function setupLink() {
|
||||
return new LinkServer();
|
||||
}
|
||||
|
||||
class LinkServer {
|
||||
constructor() {
|
||||
logger.log('setting up');
|
||||
/** @type {WebSocket.Server | null} */
|
||||
this.wss = null;
|
||||
this.setupWebSocketLink();
|
||||
}
|
||||
|
||||
// WebSocket-based client link
|
||||
setupWebSocketLink() {
|
||||
const port = 3000;
|
||||
this.wss = new WebSocket.WebSocketServer({ port });
|
||||
|
||||
this.wss.on('connection', (ws) => {
|
||||
logger.log('client connected');
|
||||
ws.on('message', (json) => {
|
||||
const msg = deserializeObject(json);
|
||||
this.handleLinkMessage(ws, msg);
|
||||
});
|
||||
ws.on('close', () => {
|
||||
logger.log('client disconnected');
|
||||
});
|
||||
});
|
||||
logger.log(`listening on port ${port} (WebSocket)`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {WebSocket.Client} ws
|
||||
* @param {WebSocket.MessageEvent} msg
|
||||
*/
|
||||
handleLinkMessage(ws, msg) {
|
||||
const { type, payload } = msg;
|
||||
if (type === 'log') {
|
||||
const { level, ns, args } = payload;
|
||||
// Skip debug messages
|
||||
if (level <= 0 && !DEBUG) {
|
||||
return;
|
||||
}
|
||||
|
||||
directLog(
|
||||
ns,
|
||||
...args.map((arg) => {
|
||||
if (typeof arg === 'object') {
|
||||
return inspect(arg, {
|
||||
depth: Infinity,
|
||||
colors: true,
|
||||
compact: 8,
|
||||
});
|
||||
}
|
||||
return arg;
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (type === 'relay') {
|
||||
if (!this.wss) {
|
||||
return;
|
||||
}
|
||||
for (let client of this.wss.clients) {
|
||||
if (client === ws) {
|
||||
continue;
|
||||
}
|
||||
this.sendMessage(client, msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
logger.log('unhandled message', msg);
|
||||
}
|
||||
|
||||
sendMessage(ws, msg) {
|
||||
ws.send(JSON.stringify(msg));
|
||||
}
|
||||
|
||||
broadcastMessage(msg) {
|
||||
if (!this.wss) {
|
||||
return;
|
||||
}
|
||||
const clients = [...this.wss.clients];
|
||||
if (clients.length === 0) {
|
||||
return;
|
||||
}
|
||||
logger.log(`broadcasting ${msg.type} to ${clients.length} clients`);
|
||||
for (let client of clients) {
|
||||
const json = JSON.stringify(msg);
|
||||
client.send(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deserializeObject(str) {
|
||||
return JSON.parse(str, (key, value) => {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (value.__undefined__) {
|
||||
// NOTE: You should not rely on deserialized object's undefined,
|
||||
// this is purely for inspection purposes.
|
||||
return {
|
||||
[inspect.custom]: () => undefined,
|
||||
};
|
||||
}
|
||||
if (value.__number__) {
|
||||
return parseFloat(value.__number__);
|
||||
}
|
||||
if (value.__error__) {
|
||||
if (!value.stack) {
|
||||
return value.string;
|
||||
}
|
||||
return retrace(value.stack);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { inspect } from 'node:util';
|
||||
|
||||
import { ServerWebSocket } from 'bun';
|
||||
|
||||
import { createLogger, directLog } from '../logging';
|
||||
import { retrace } from './retrace';
|
||||
|
||||
let server: Bun.Server;
|
||||
const logger = createLogger('link');
|
||||
const DEBUG = process.argv.includes('--debug');
|
||||
|
||||
export function setupLink() {
|
||||
server = Bun.serve({
|
||||
fetch: upgradeServer,
|
||||
development: true,
|
||||
hostname: '127.0.0.1',
|
||||
port: 3000,
|
||||
websocket: {
|
||||
open(ws) {
|
||||
ws.subscribe('link');
|
||||
logger.log('client connected');
|
||||
},
|
||||
close(ws) {
|
||||
ws.unsubscribe('link');
|
||||
logger.log('client disconnected');
|
||||
},
|
||||
message: handleLinkMessage,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function broadcastMessage(msg: Record<string, any>): void {
|
||||
const subscribers = server.subscriberCount('link');
|
||||
if (subscribers === 0) return;
|
||||
|
||||
server?.publish('link', JSON.stringify(msg));
|
||||
}
|
||||
|
||||
function deserializeObject(str: any): any {
|
||||
return JSON.parse(str, (_key: string, value: any) => {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (value.__undefined__) {
|
||||
// NOTE: You should not rely on deserialized object's undefined,
|
||||
// this is purely for inspection purposes.
|
||||
return {
|
||||
[inspect.custom]: () => undefined,
|
||||
};
|
||||
}
|
||||
if (value.__number__) {
|
||||
return parseFloat(value.__number__);
|
||||
}
|
||||
if (value.__error__) {
|
||||
if (!value.stack) {
|
||||
return value.string;
|
||||
}
|
||||
return retrace(value.stack);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
function handleLinkMessage(
|
||||
_ws: ServerWebSocket<unknown>,
|
||||
message: string | Buffer<ArrayBufferLike>,
|
||||
): void {
|
||||
const deserializedMsg = deserializeObject(message);
|
||||
const { type, payload } = deserializedMsg;
|
||||
|
||||
if (type === 'log') {
|
||||
const { level, ns, args } = payload;
|
||||
// Skip debug messages
|
||||
if (level <= 0 && !DEBUG) return;
|
||||
|
||||
directLog(
|
||||
ns,
|
||||
...args.map((arg) => {
|
||||
if (typeof arg === 'object') {
|
||||
return inspect(arg, {
|
||||
depth: Infinity,
|
||||
colors: true,
|
||||
compact: 8,
|
||||
});
|
||||
}
|
||||
return arg;
|
||||
}),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
if (type === 'relay') {
|
||||
broadcastMessage(payload);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
logger.log('unhandled message', JSON.stringify(message));
|
||||
}
|
||||
|
||||
function upgradeServer(req: Request, srv: Bun.Server) {
|
||||
const client = crypto.randomUUID();
|
||||
|
||||
const upgraded = srv.upgrade(req, {
|
||||
data: {
|
||||
id: client,
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
});
|
||||
|
||||
if (upgraded) {
|
||||
return new Response('Ok');
|
||||
} else {
|
||||
return new Response('Upgrade failed', { status: 500 });
|
||||
}
|
||||
}
|
||||
+13
-15
@@ -4,9 +4,11 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
type Fn = (...args: any[]) => void;
|
||||
|
||||
const inception = Date.now();
|
||||
|
||||
// Runtime detection
|
||||
// Runtime detection. Yes, even in bun!
|
||||
const isNode = process?.release?.name === 'node';
|
||||
let isChrome = false;
|
||||
try {
|
||||
@@ -14,12 +16,12 @@ try {
|
||||
} catch {}
|
||||
|
||||
// Timestamping function
|
||||
function getTimestamp() {
|
||||
function getTimestamp(): string {
|
||||
const timestamp = String(Date.now() - inception)
|
||||
.padStart(4, '0')
|
||||
.padStart(7, ' ');
|
||||
const seconds = timestamp.substr(0, timestamp.length - 3);
|
||||
const millis = timestamp.substr(-3);
|
||||
const seconds = timestamp.slice(0, timestamp.length - 3);
|
||||
const millis = timestamp.slice(-3);
|
||||
|
||||
return `${seconds}.${millis}`;
|
||||
}
|
||||
@@ -32,7 +34,7 @@ const getPrefix = (() => {
|
||||
bright: '\x1b[37;1m',
|
||||
reset: '\x1b[0m',
|
||||
};
|
||||
return (ns) => [
|
||||
return (ns: string) => [
|
||||
`${ESC.dimmed}${getTimestamp()} ${ESC.bright}${ns}${ESC.reset}`,
|
||||
];
|
||||
}
|
||||
@@ -42,20 +44,18 @@ const getPrefix = (() => {
|
||||
dimmed: 'color: #888',
|
||||
bright: 'font-weight: bold',
|
||||
};
|
||||
return (ns) => [
|
||||
return (ns: string) => [
|
||||
`%c${getTimestamp()}%c ${ns}`,
|
||||
styles.dimmed,
|
||||
styles.bright,
|
||||
];
|
||||
}
|
||||
|
||||
return (ns) => [`${getTimestamp()} ${ns}`];
|
||||
return (ns: string) => [`${getTimestamp()} ${ns}`];
|
||||
})();
|
||||
|
||||
/**
|
||||
* Creates a logger object.
|
||||
*/
|
||||
export function createLogger(ns) {
|
||||
/** Creates a logger object. */
|
||||
export function createLogger(ns: string): Record<string, Fn> {
|
||||
return {
|
||||
log: (...args) => console.log(...getPrefix(ns), ...args),
|
||||
trace: (...args) => console.trace(...getPrefix(ns), ...args),
|
||||
@@ -66,9 +66,7 @@ export function createLogger(ns) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly log with chosen namespace.
|
||||
*/
|
||||
export function directLog(ns, ...args) {
|
||||
/** Explicitly log with chosen namespace. */
|
||||
export function directLog(ns: string, ...args: any[]): void {
|
||||
console.log(...getPrefix(ns), ...args);
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "tgui-dev-server",
|
||||
"version": "6.0.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@rspack/core": "^1.3.7",
|
||||
"axios": "^1.8.4",
|
||||
"axios": "^1.10.0",
|
||||
"source-map": "^0.7.4",
|
||||
"stacktrace-parser": "^0.1.11",
|
||||
"ws": "^8.18.1"
|
||||
"stacktrace-parser": "^0.1.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ws": "^8.18.1"
|
||||
}
|
||||
"private": true,
|
||||
"type": "module"
|
||||
}
|
||||
|
||||
+27
-21
@@ -4,14 +4,13 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import { basename } from 'node:path';
|
||||
import path from 'node:path';
|
||||
|
||||
import { DreamSeeker } from './dreamseeker.js';
|
||||
import { createLogger } from './logging.js';
|
||||
import { resolveGlob, resolvePath } from './util.js';
|
||||
import { regQuery } from './winreg.js';
|
||||
import { DreamSeeker } from './dreamseeker';
|
||||
import { createLogger } from './logging';
|
||||
import { resolveGlob, resolvePath } from './util';
|
||||
import { regQuery } from './winreg';
|
||||
|
||||
const logger = createLogger('reloader');
|
||||
|
||||
@@ -32,18 +31,20 @@ const SEARCH_LOCATIONS = [
|
||||
`/mnt/c/Users/*/*/BYOND/cache`,
|
||||
];
|
||||
|
||||
let cacheRoot;
|
||||
let cacheRoot: string;
|
||||
|
||||
export async function findCacheRoot() {
|
||||
export async function findCacheRoot(): Promise<string | undefined> {
|
||||
if (cacheRoot) {
|
||||
return cacheRoot;
|
||||
}
|
||||
logger.log('looking for byond cache');
|
||||
// Find BYOND cache folders
|
||||
|
||||
for (let pattern of SEARCH_LOCATIONS) {
|
||||
if (!pattern) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const paths = await resolveGlob(pattern);
|
||||
if (paths.length > 0) {
|
||||
cacheRoot = paths[0];
|
||||
@@ -51,30 +52,30 @@ export async function findCacheRoot() {
|
||||
return cacheRoot;
|
||||
}
|
||||
}
|
||||
|
||||
// Query the Windows Registry
|
||||
if (process.platform === 'win32') {
|
||||
logger.log('querying windows registry');
|
||||
let userpath = await regQuery('HKCU\\Software\\Dantom\\BYOND', 'userpath');
|
||||
if (userpath) {
|
||||
cacheRoot = userpath.replace(/\\$/, '').replace(/\\/g, '/') + '/cache';
|
||||
onCacheRootFound(cacheRoot);
|
||||
await onCacheRootFound(cacheRoot);
|
||||
return cacheRoot;
|
||||
}
|
||||
}
|
||||
logger.log('found no cache directories');
|
||||
}
|
||||
|
||||
function onCacheRootFound(cacheRoot) {
|
||||
async function onCacheRootFound(cacheRoot: string): Promise<void> {
|
||||
logger.log(`found cache at '${cacheRoot}'`);
|
||||
// Plant a dummy browser window file, we'll be using this to avoid world topic. For byond 514.
|
||||
fs.closeSync(fs.openSync(cacheRoot + '/dummy.htm', 'w'));
|
||||
await Bun.write(cacheRoot + '/dummy.htm', '');
|
||||
}
|
||||
|
||||
export async function reloadByondCache(bundleDir) {
|
||||
export async function reloadByondCache(bundleDir: string): Promise<void> {
|
||||
const cacheRoot = await findCacheRoot();
|
||||
if (!cacheRoot) {
|
||||
return;
|
||||
}
|
||||
if (!cacheRoot) return;
|
||||
|
||||
// Find tmp folders in cache
|
||||
const cacheDirs = await resolveGlob(cacheRoot, 'tmp*');
|
||||
if (cacheDirs.length === 0) {
|
||||
@@ -89,20 +90,25 @@ export async function reloadByondCache(bundleDir) {
|
||||
const dssPromise = DreamSeeker.getInstancesByPids(pids);
|
||||
// Copy assets
|
||||
const assets = await resolveGlob(bundleDir, bundleGlob);
|
||||
|
||||
for (let cacheDir of cacheDirs) {
|
||||
// Clear garbage
|
||||
const garbage = await resolveGlob(cacheDir, bundleGlob);
|
||||
for (let file of garbage) {
|
||||
await Bun.file(file).delete();
|
||||
}
|
||||
|
||||
try {
|
||||
// Plant a dummy browser window file, we'll be using this to avoid world topic. For byond 515-516.
|
||||
fs.closeSync(fs.openSync(cacheDir + '/dummy.htm', 'w'));
|
||||
await Bun.write(cacheDir + '/dummy.htm', '');
|
||||
|
||||
for (let file of garbage) {
|
||||
fs.unlinkSync(file);
|
||||
}
|
||||
// Copy assets
|
||||
for (let asset of assets) {
|
||||
const destination = resolvePath(cacheDir, basename(asset));
|
||||
fs.writeFileSync(destination, fs.readFileSync(asset));
|
||||
const destination = resolvePath(cacheDir, path.basename(asset));
|
||||
const input = Bun.file(asset);
|
||||
const output = Bun.file(destination);
|
||||
|
||||
await Bun.write(output, input);
|
||||
}
|
||||
logger.log(`copied ${assets.length} files to '${cacheDir}'`);
|
||||
} catch (err) {
|
||||
@@ -10,14 +10,11 @@ import { Glob } from 'bun';
|
||||
|
||||
export const resolvePath = path.resolve;
|
||||
|
||||
/**
|
||||
* Combines path.resolve with glob patterns.
|
||||
*
|
||||
* @param {string} directory - The directory to resolve the glob pattern against.
|
||||
* @param {string} [pattern='*'] - The glob pattern to match files against. Defaults to '*', which matches all files in the directory.
|
||||
* @returns {Promise<string[]>} - A promise that resolves to an array of matched file paths.
|
||||
*/
|
||||
export async function resolveGlob(directory, pattern = '*') {
|
||||
/** Combines path.resolve with glob patterns. */
|
||||
export async function resolveGlob(
|
||||
directory: string,
|
||||
pattern = '*',
|
||||
): Promise<string[]> {
|
||||
// If no pattern is supplied, just return the directory if it exists
|
||||
if (pattern === '*') {
|
||||
try {
|
||||
@@ -33,8 +30,7 @@ export async function resolveGlob(directory, pattern = '*') {
|
||||
|
||||
// Otherwise, use glob logic
|
||||
const glob = new Glob(pattern);
|
||||
/** @type string[] */
|
||||
const results = [];
|
||||
const results: string[] = [];
|
||||
|
||||
for await (const match of glob.scan({ onlyFiles: false, cwd: directory })) {
|
||||
results.push(path.resolve(directory, match));
|
||||
+30
-20
@@ -4,70 +4,80 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import { createRequire } from 'module';
|
||||
import { dirname } from 'path';
|
||||
|
||||
import { loadSourceMaps, setupLink } from './link/server.js';
|
||||
import { loadSourceMaps } from './link/retrace';
|
||||
import { broadcastMessage, setupLink } from './link/server';
|
||||
import { createLogger } from './logging.js';
|
||||
import { reloadByondCache } from './reloader.js';
|
||||
import { resolveGlob } from './util.js';
|
||||
import { resolveGlob } from './util';
|
||||
|
||||
const logger = createLogger('webpack');
|
||||
|
||||
/**
|
||||
* @param {any} config
|
||||
* @return {WebpackCompiler}
|
||||
*/
|
||||
export const createCompiler = async (options) => {
|
||||
export async function createCompiler(
|
||||
options: Record<string, any>,
|
||||
): Promise<WebpackCompiler> {
|
||||
const compiler = new WebpackCompiler();
|
||||
await compiler.setup(options);
|
||||
|
||||
return compiler;
|
||||
};
|
||||
}
|
||||
|
||||
type WebpackImport = typeof import('webpack');
|
||||
|
||||
class WebpackCompiler {
|
||||
async setup(options) {
|
||||
public webpack: WebpackImport;
|
||||
public config: Record<string, any>;
|
||||
public bundleDir: string;
|
||||
|
||||
async setup(options: Record<string, any>): Promise<void> {
|
||||
// Create a require context that is relative to project root
|
||||
// and retrieve all necessary dependencies.
|
||||
const requireFromRoot = createRequire(dirname(import.meta.url) + '/../..');
|
||||
const webpack = await requireFromRoot('webpack');
|
||||
const requireFromRoot = createRequire(import.meta.dirname + '/../../..');
|
||||
const webpack: WebpackImport = await requireFromRoot('webpack');
|
||||
|
||||
const createConfig = await requireFromRoot('./webpack.config.js');
|
||||
const config = createConfig({}, options);
|
||||
|
||||
// Inject the HMR plugin into the config if we're using it
|
||||
if (options.hot) {
|
||||
config.plugins.push(new webpack.HotModuleReplacementPlugin());
|
||||
}
|
||||
|
||||
this.webpack = webpack;
|
||||
this.config = config;
|
||||
this.bundleDir = config.output.path;
|
||||
}
|
||||
|
||||
async watch() {
|
||||
async watch(): Promise<void> {
|
||||
logger.log('setting up');
|
||||
// Setup link
|
||||
const link = setupLink();
|
||||
// Instantiate the compiler
|
||||
const compiler = this.webpack.webpack(this.config);
|
||||
|
||||
// Clear garbage before compiling
|
||||
compiler.hooks.watchRun.tapPromise('tgui-dev-server', async () => {
|
||||
const files = await resolveGlob(this.bundleDir, './*.hot-update.*');
|
||||
const files = await resolveGlob(this.bundleDir, '*.hot-update.*');
|
||||
logger.log(`clearing garbage (${files.length} files)`);
|
||||
for (let file of files) {
|
||||
fs.unlinkSync(file);
|
||||
for (const file of files) {
|
||||
await Bun.file(file).delete();
|
||||
}
|
||||
logger.log('compiling');
|
||||
});
|
||||
|
||||
// Start reloading when it's finished
|
||||
compiler.hooks.done.tap('tgui-dev-server', async (stats) => {
|
||||
compiler.hooks.done.tap('tgui-dev-server', async () => {
|
||||
// Load source maps
|
||||
await loadSourceMaps(this.bundleDir);
|
||||
// Reload cache
|
||||
await reloadByondCache(this.bundleDir);
|
||||
// Notify all clients that update has happened
|
||||
link.broadcastMessage({
|
||||
broadcastMessage({
|
||||
type: 'hotUpdate',
|
||||
});
|
||||
});
|
||||
|
||||
// Start watching
|
||||
logger.log('watching for changes');
|
||||
compiler.watch({}, (err, stats) => {
|
||||
@@ -76,7 +86,7 @@ class WebpackCompiler {
|
||||
return;
|
||||
}
|
||||
stats
|
||||
.toString(this.config.devServer.stats)
|
||||
?.toString(this.config.devServer?.stats)
|
||||
.split('\n')
|
||||
.forEach((line) => logger.log(line));
|
||||
});
|
||||
@@ -9,17 +9,15 @@
|
||||
import { exec } from 'node:child_process';
|
||||
import { promisify } from 'node:util';
|
||||
|
||||
import { createLogger } from './logging.js';
|
||||
import { createLogger } from './logging';
|
||||
|
||||
const logger = createLogger('winreg');
|
||||
|
||||
/**
|
||||
* Query a registry key.
|
||||
* @param {string} path
|
||||
* @param {string} key
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
export async function regQuery(path, key) {
|
||||
/** Query a registry key. */
|
||||
export async function regQuery(
|
||||
path: string,
|
||||
key: string,
|
||||
): Promise<string | undefined> {
|
||||
if (process.platform !== 'win32') {
|
||||
return;
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import { captureExternalLinks } from 'tgui/links';
|
||||
import { render } from 'tgui/renderer';
|
||||
import { configureStore } from 'tgui/store';
|
||||
import { setupGlobalEvents } from 'tgui-core/events';
|
||||
import { setupHotReloading } from 'tgui-dev-server/link/client.mjs';
|
||||
import { setupHotReloading } from 'tgui-dev-server/link/client';
|
||||
|
||||
import { audioMiddleware, audioReducer } from './audio';
|
||||
import { chatMiddleware, chatReducer } from './chat';
|
||||
|
||||
@@ -42,7 +42,7 @@ export function debugMiddleware(store) {
|
||||
}
|
||||
|
||||
export function relayMiddleware(store) {
|
||||
const devServer = require('tgui-dev-server/link/client.mjs');
|
||||
const devServer = require('tgui-dev-server/link/client.ts');
|
||||
const externalBrowser = location.search === '?external';
|
||||
if (externalBrowser) {
|
||||
devServer.subscribe((msg) => {
|
||||
|
||||
@@ -11,7 +11,7 @@ import './styles/themes/clockwork.scss'; // SKYRAT EDIT ADDITION
|
||||
import { perf } from 'common/perf';
|
||||
import { setupGlobalEvents } from 'tgui-core/events';
|
||||
import { setupHotKeys } from 'tgui-core/hotkeys';
|
||||
import { setupHotReloading } from 'tgui-dev-server/link/client.mjs';
|
||||
import { setupHotReloading } from 'tgui-dev-server/link/client';
|
||||
|
||||
import { App } from './App';
|
||||
import { setGlobalStore } from './backend';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { sendLogEntry } from 'tgui-dev-server/link/client.mjs';
|
||||
import { sendLogEntry } from 'tgui-dev-server/link/client';
|
||||
|
||||
const LEVEL_DEBUG = 0;
|
||||
const LEVEL_LOG = 1;
|
||||
|
||||
+15
-33
@@ -26,7 +26,7 @@ const createStats = (verbose) => ({
|
||||
module.exports = (env = {}, argv) => {
|
||||
const mode = argv.mode || 'production';
|
||||
const config = {
|
||||
mode: mode === 'production' ? 'production' : 'development',
|
||||
mode,
|
||||
context: path.resolve(__dirname),
|
||||
target: ['web', 'browserslist:edge>=123'],
|
||||
entry: {
|
||||
@@ -35,9 +35,10 @@ module.exports = (env = {}, argv) => {
|
||||
'tgui-say': ['./packages/tgui-say'],
|
||||
},
|
||||
output: {
|
||||
path: argv.useTmpFolder
|
||||
? path.resolve(__dirname, './public/.tmp')
|
||||
: path.resolve(__dirname, './public'),
|
||||
path:
|
||||
mode !== 'production'
|
||||
? path.resolve(__dirname, './public/.tmp')
|
||||
: path.resolve(__dirname, './public'),
|
||||
filename: '[name].bundle.js',
|
||||
chunkFilename: '[name].bundle.js',
|
||||
chunkLoadTimeout: 15000,
|
||||
@@ -69,23 +70,12 @@ module.exports = (env = {}, argv) => {
|
||||
{
|
||||
test: /\.(s)?css$/,
|
||||
use: [
|
||||
{
|
||||
loader: ExtractCssPlugin.loader,
|
||||
options: {
|
||||
esModule: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
esModule: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('sass-loader'),
|
||||
},
|
||||
ExtractCssPlugin.loader,
|
||||
require.resolve('css-loader'),
|
||||
require.resolve('sass-loader'),
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
test: /\.(cur|png|jpg)$/,
|
||||
type: 'asset/resource',
|
||||
@@ -124,9 +114,7 @@ module.exports = (env = {}, argv) => {
|
||||
stats: createStats(true),
|
||||
plugins: [
|
||||
new webpack.EnvironmentPlugin({
|
||||
NODE_ENV: env.NODE_ENV || mode,
|
||||
WEBPACK_HMR_ENABLED: env.WEBPACK_HMR_ENABLED || argv.hot || false,
|
||||
DEV_SERVER_IP: env.DEV_SERVER_IP || null,
|
||||
NODE_ENV: mode,
|
||||
}),
|
||||
new ExtractCssPlugin({
|
||||
filename: '[name].bundle.css',
|
||||
@@ -144,22 +132,16 @@ module.exports = (env = {}, argv) => {
|
||||
legalComments: 'none',
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
// Development build specific options
|
||||
if (mode !== 'production') {
|
||||
config.devtool = 'cheap-module-source-map';
|
||||
}
|
||||
|
||||
// Development server specific options
|
||||
if (argv.devServer) {
|
||||
} else {
|
||||
config.devServer = {
|
||||
clientLogLevel: 'silent',
|
||||
hot: true,
|
||||
noInfo: false,
|
||||
progress: false,
|
||||
quiet: false,
|
||||
noInfo: false,
|
||||
clientLogLevel: 'silent',
|
||||
stats: createStats(false),
|
||||
};
|
||||
config.devtool = 'cheap-module-source-map';
|
||||
}
|
||||
|
||||
return config;
|
||||
|
||||
Reference in New Issue
Block a user