diff --git a/browser/manifest.json b/browser/manifest.json index 357312b09..3463e46cf 100644 --- a/browser/manifest.json +++ b/browser/manifest.json @@ -36,7 +36,7 @@ "web_accessible_resources": [ { - "resources": ["dist/*", "third-party/*"], + "resources": ["dist/*", "vendor/*"], "matches": ["*://*.discord.com/*"] } ], diff --git a/browser/monaco.ts b/browser/monaco.ts index ead061d65..dc243df7d 100644 --- a/browser/monaco.ts +++ b/browser/monaco.ts @@ -15,7 +15,7 @@ declare global { const getTheme: () => string; } -const BASE = "/dist/monaco/vs"; +const BASE = "/vendor/monaco/vs"; self.MonacoEnvironment = { getWorkerUrl(_moduleId: unknown, label: string) { diff --git a/browser/monacoWin.html b/browser/monacoWin.html index a55b0e547..12523d455 100644 --- a/browser/monacoWin.html +++ b/browser/monacoWin.html @@ -24,12 +24,12 @@ diff --git a/eslint.config.mjs b/eslint.config.mjs index 67327b938..d59c37532 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -134,7 +134,7 @@ export default tseslint.config( "no-unsafe-optional-chaining": "error", "no-useless-backreference": "error", "use-isnan": "error", - "prefer-const": "error", + "prefer-const": ["error", { destructuring: "all" }], "prefer-spread": "error", // Plugin Rules diff --git a/package.json b/package.json index dca52a16f..872d7ce03 100644 --- a/package.json +++ b/package.json @@ -30,13 +30,12 @@ "lint": "eslint", "lint-styles": "stylelint \"src/**/*.css\" --ignore-pattern src/userplugins", "lint:fix": "pnpm lint --fix", - "test": "pnpm buildStandalone && pnpm lint && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson", + "test": "pnpm buildStandalone && pnpm testTsc && pnpm lint && pnpm lint-styles && pnpm generatePluginJson", "testWeb": "pnpm lint && pnpm buildWeb && pnpm testTsc", "testTsc": "tsc --noEmit" }, "dependencies": { "@intrnl/xxhash64": "^0.1.2", - "@sapphi-red/web-noise-suppressor": "0.3.5", "@vap/core": "0.0.12", "@vap/shiki": "0.10.5", "fflate": "^0.8.2", @@ -56,7 +55,7 @@ "@types/yazl": "^2.4.5", "diff": "^7.0.0", "discord-types": "^1.3.26", - "esbuild": "^0.15.18", + "esbuild": "^0.25.0", "eslint": "^9.17.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-path-alias": "2.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a49df467b..169d76fcf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,9 +19,6 @@ importers: '@intrnl/xxhash64': specifier: ^0.1.2 version: 0.1.2 - '@sapphi-red/web-noise-suppressor': - specifier: 0.3.5 - version: 0.3.5 '@vap/core': specifier: 0.0.12 version: 0.0.12 @@ -75,8 +72,8 @@ importers: specifier: ^1.3.26 version: 1.3.26 esbuild: - specifier: ^0.15.18 - version: 0.15.18 + specifier: ^0.25.0 + version: 0.25.0 eslint: specifier: ^9.17.0 version: 9.17.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4) @@ -229,6 +226,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.17.19': resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} engines: {node: '>=12'} @@ -241,10 +244,10 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.15.18': - resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} - engines: {node: '>=12'} - cpu: [arm] + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + engines: {node: '>=18'} + cpu: [arm64] os: [android] '@esbuild/android-arm@0.17.19': @@ -259,6 +262,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.17.19': resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} engines: {node: '>=12'} @@ -271,6 +280,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.17.19': resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} engines: {node: '>=12'} @@ -283,6 +298,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.17.19': resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} engines: {node: '>=12'} @@ -295,6 +316,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.17.19': resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} engines: {node: '>=12'} @@ -307,6 +334,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.17.19': resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} engines: {node: '>=12'} @@ -319,6 +352,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.17.19': resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} engines: {node: '>=12'} @@ -331,6 +370,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.17.19': resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} engines: {node: '>=12'} @@ -343,6 +388,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.17.19': resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} engines: {node: '>=12'} @@ -355,10 +406,10 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.15.18': - resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} - engines: {node: '>=12'} - cpu: [loong64] + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + engines: {node: '>=18'} + cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.17.19': @@ -373,6 +424,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.17.19': resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} engines: {node: '>=12'} @@ -385,6 +442,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.17.19': resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} engines: {node: '>=12'} @@ -397,6 +460,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.17.19': resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} engines: {node: '>=12'} @@ -409,6 +478,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.17.19': resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} engines: {node: '>=12'} @@ -421,6 +496,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.17.19': resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} engines: {node: '>=12'} @@ -433,6 +514,18 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.0': + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.17.19': resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} engines: {node: '>=12'} @@ -445,12 +538,24 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.0': + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.23.1': resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.17.19': resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} engines: {node: '>=12'} @@ -463,6 +568,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.0': + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.17.19': resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} engines: {node: '>=12'} @@ -475,6 +586,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.17.19': resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} engines: {node: '>=12'} @@ -487,6 +604,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.17.19': resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} engines: {node: '>=12'} @@ -499,6 +622,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.17.19': resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} engines: {node: '>=12'} @@ -511,6 +640,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.1': resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -609,9 +744,6 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@sapphi-red/web-noise-suppressor@0.3.5': - resolution: {integrity: sha512-jh3+V9yM+zxLriQexoGm0GatoPaJWjs6ypFIbFYwQp+AoUb55eUXrjKtKQyuC5zShzzeAQUl0M5JzqB7SSrsRA==} - '@stylistic/eslint-plugin@2.12.1': resolution: {integrity: sha512-fubZKIHSPuo07FgRTn6S4Nl0uXPRPYVNpyZzIDGfp7Fny6JjNus6kReLD7NI380JXi4HtUTSOZ34LBuNPO1XLQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1205,131 +1337,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild-android-64@0.15.18: - resolution: {integrity: sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - esbuild-android-arm64@0.15.18: - resolution: {integrity: sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - esbuild-darwin-64@0.15.18: - resolution: {integrity: sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - esbuild-darwin-arm64@0.15.18: - resolution: {integrity: sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - esbuild-freebsd-64@0.15.18: - resolution: {integrity: sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - esbuild-freebsd-arm64@0.15.18: - resolution: {integrity: sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - esbuild-linux-32@0.15.18: - resolution: {integrity: sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - esbuild-linux-64@0.15.18: - resolution: {integrity: sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - esbuild-linux-arm64@0.15.18: - resolution: {integrity: sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - esbuild-linux-arm@0.15.18: - resolution: {integrity: sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - esbuild-linux-mips64le@0.15.18: - resolution: {integrity: sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - esbuild-linux-ppc64le@0.15.18: - resolution: {integrity: sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - esbuild-linux-riscv64@0.15.18: - resolution: {integrity: sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - esbuild-linux-s390x@0.15.18: - resolution: {integrity: sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - esbuild-netbsd-64@0.15.18: - resolution: {integrity: sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - esbuild-openbsd-64@0.15.18: - resolution: {integrity: sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - esbuild-sunos-64@0.15.18: - resolution: {integrity: sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - esbuild-windows-32@0.15.18: - resolution: {integrity: sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - esbuild-windows-64@0.15.18: - resolution: {integrity: sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - esbuild-windows-arm64@0.15.18: - resolution: {integrity: sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - esbuild@0.15.18: - resolution: {integrity: sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==} - engines: {node: '>=12'} - hasBin: true - esbuild@0.17.19: resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} engines: {node: '>=12'} @@ -1340,6 +1347,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -2889,13 +2901,16 @@ snapshots: '@esbuild/aix-ppc64@0.23.1': optional: true + '@esbuild/aix-ppc64@0.25.0': + optional: true + '@esbuild/android-arm64@0.17.19': optional: true '@esbuild/android-arm64@0.23.1': optional: true - '@esbuild/android-arm@0.15.18': + '@esbuild/android-arm64@0.25.0': optional: true '@esbuild/android-arm@0.17.19': @@ -2904,55 +2919,79 @@ snapshots: '@esbuild/android-arm@0.23.1': optional: true + '@esbuild/android-arm@0.25.0': + optional: true + '@esbuild/android-x64@0.17.19': optional: true '@esbuild/android-x64@0.23.1': optional: true + '@esbuild/android-x64@0.25.0': + optional: true + '@esbuild/darwin-arm64@0.17.19': optional: true '@esbuild/darwin-arm64@0.23.1': optional: true + '@esbuild/darwin-arm64@0.25.0': + optional: true + '@esbuild/darwin-x64@0.17.19': optional: true '@esbuild/darwin-x64@0.23.1': optional: true + '@esbuild/darwin-x64@0.25.0': + optional: true + '@esbuild/freebsd-arm64@0.17.19': optional: true '@esbuild/freebsd-arm64@0.23.1': optional: true + '@esbuild/freebsd-arm64@0.25.0': + optional: true + '@esbuild/freebsd-x64@0.17.19': optional: true '@esbuild/freebsd-x64@0.23.1': optional: true + '@esbuild/freebsd-x64@0.25.0': + optional: true + '@esbuild/linux-arm64@0.17.19': optional: true '@esbuild/linux-arm64@0.23.1': optional: true + '@esbuild/linux-arm64@0.25.0': + optional: true + '@esbuild/linux-arm@0.17.19': optional: true '@esbuild/linux-arm@0.23.1': optional: true + '@esbuild/linux-arm@0.25.0': + optional: true + '@esbuild/linux-ia32@0.17.19': optional: true '@esbuild/linux-ia32@0.23.1': optional: true - '@esbuild/linux-loong64@0.15.18': + '@esbuild/linux-ia32@0.25.0': optional: true '@esbuild/linux-loong64@0.17.19': @@ -2961,75 +3000,117 @@ snapshots: '@esbuild/linux-loong64@0.23.1': optional: true + '@esbuild/linux-loong64@0.25.0': + optional: true + '@esbuild/linux-mips64el@0.17.19': optional: true '@esbuild/linux-mips64el@0.23.1': optional: true + '@esbuild/linux-mips64el@0.25.0': + optional: true + '@esbuild/linux-ppc64@0.17.19': optional: true '@esbuild/linux-ppc64@0.23.1': optional: true + '@esbuild/linux-ppc64@0.25.0': + optional: true + '@esbuild/linux-riscv64@0.17.19': optional: true '@esbuild/linux-riscv64@0.23.1': optional: true + '@esbuild/linux-riscv64@0.25.0': + optional: true + '@esbuild/linux-s390x@0.17.19': optional: true '@esbuild/linux-s390x@0.23.1': optional: true + '@esbuild/linux-s390x@0.25.0': + optional: true + '@esbuild/linux-x64@0.17.19': optional: true '@esbuild/linux-x64@0.23.1': optional: true + '@esbuild/linux-x64@0.25.0': + optional: true + + '@esbuild/netbsd-arm64@0.25.0': + optional: true + '@esbuild/netbsd-x64@0.17.19': optional: true '@esbuild/netbsd-x64@0.23.1': optional: true + '@esbuild/netbsd-x64@0.25.0': + optional: true + '@esbuild/openbsd-arm64@0.23.1': optional: true + '@esbuild/openbsd-arm64@0.25.0': + optional: true + '@esbuild/openbsd-x64@0.17.19': optional: true '@esbuild/openbsd-x64@0.23.1': optional: true + '@esbuild/openbsd-x64@0.25.0': + optional: true + '@esbuild/sunos-x64@0.17.19': optional: true '@esbuild/sunos-x64@0.23.1': optional: true + '@esbuild/sunos-x64@0.25.0': + optional: true + '@esbuild/win32-arm64@0.17.19': optional: true '@esbuild/win32-arm64@0.23.1': optional: true + '@esbuild/win32-arm64@0.25.0': + optional: true + '@esbuild/win32-ia32@0.17.19': optional: true '@esbuild/win32-ia32@0.23.1': optional: true + '@esbuild/win32-ia32@0.25.0': + optional: true + '@esbuild/win32-x64@0.17.19': optional: true '@esbuild/win32-x64@0.23.1': optional: true + '@esbuild/win32-x64@0.25.0': + optional: true + '@eslint-community/eslint-utils@4.4.1(eslint@9.17.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4))': dependencies: eslint: 9.17.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4) @@ -3135,8 +3216,6 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@sapphi-red/web-noise-suppressor@0.3.5': {} - '@stylistic/eslint-plugin@2.12.1(eslint@9.17.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4))(typescript@5.7.2)': dependencies: '@typescript-eslint/utils': 8.18.1(eslint@9.17.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4))(typescript@5.7.2) @@ -3936,91 +4015,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild-android-64@0.15.18: - optional: true - - esbuild-android-arm64@0.15.18: - optional: true - - esbuild-darwin-64@0.15.18: - optional: true - - esbuild-darwin-arm64@0.15.18: - optional: true - - esbuild-freebsd-64@0.15.18: - optional: true - - esbuild-freebsd-arm64@0.15.18: - optional: true - - esbuild-linux-32@0.15.18: - optional: true - - esbuild-linux-64@0.15.18: - optional: true - - esbuild-linux-arm64@0.15.18: - optional: true - - esbuild-linux-arm@0.15.18: - optional: true - - esbuild-linux-mips64le@0.15.18: - optional: true - - esbuild-linux-ppc64le@0.15.18: - optional: true - - esbuild-linux-riscv64@0.15.18: - optional: true - - esbuild-linux-s390x@0.15.18: - optional: true - - esbuild-netbsd-64@0.15.18: - optional: true - - esbuild-openbsd-64@0.15.18: - optional: true - - esbuild-sunos-64@0.15.18: - optional: true - - esbuild-windows-32@0.15.18: - optional: true - - esbuild-windows-64@0.15.18: - optional: true - - esbuild-windows-arm64@0.15.18: - optional: true - - esbuild@0.15.18: - optionalDependencies: - '@esbuild/android-arm': 0.15.18 - '@esbuild/linux-loong64': 0.15.18 - esbuild-android-64: 0.15.18 - esbuild-android-arm64: 0.15.18 - esbuild-darwin-64: 0.15.18 - esbuild-darwin-arm64: 0.15.18 - esbuild-freebsd-64: 0.15.18 - esbuild-freebsd-arm64: 0.15.18 - esbuild-linux-32: 0.15.18 - esbuild-linux-64: 0.15.18 - esbuild-linux-arm: 0.15.18 - esbuild-linux-arm64: 0.15.18 - esbuild-linux-mips64le: 0.15.18 - esbuild-linux-ppc64le: 0.15.18 - esbuild-linux-riscv64: 0.15.18 - esbuild-linux-s390x: 0.15.18 - esbuild-netbsd-64: 0.15.18 - esbuild-openbsd-64: 0.15.18 - esbuild-sunos-64: 0.15.18 - esbuild-windows-32: 0.15.18 - esbuild-windows-64: 0.15.18 - esbuild-windows-arm64: 0.15.18 - esbuild@0.17.19: optionalDependencies: '@esbuild/android-arm': 0.17.19 @@ -4073,6 +4067,34 @@ snapshots: '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 + esbuild@0.25.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index 623f9f940..9c2b49708 100755 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -17,38 +17,41 @@ * along with this program. If not, see . */ -import esbuild from "esbuild"; +// @ts-check + import { readdir } from "fs/promises"; import { join } from "path"; -import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, commonRendererPlugins, watch } from "./common.mjs"; +import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, commonRendererPlugins, watch, buildOrWatchAll, stringifyValues } from "./common.mjs"; -const defines = { +const defines = stringifyValues({ IS_STANDALONE, IS_DEV, IS_REPORTER, IS_UPDATER_DISABLED, IS_WEB: false, IS_EXTENSION: false, - VERSION: JSON.stringify(VERSION), + VERSION, BUILD_TIMESTAMP -}; +}); -if (defines.IS_STANDALONE === false) +if (defines.IS_STANDALONE === "false") { // If this is a local build (not standalone), optimize // for the specific platform we're on defines["process.platform"] = JSON.stringify(process.platform); +} /** - * @type {esbuild.BuildOptions} + * @type {import("esbuild").BuildOptions} */ const nodeCommonOpts = { ...commonOpts, + define: defines, format: "cjs", platform: "node", target: ["esnext"], - external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external], - define: defines + // @ts-ignore this is never undefined + external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external] }; const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`; @@ -102,25 +105,27 @@ const globNativesPlugin = { } }; -await Promise.all([ +/** @type {import("esbuild").BuildOptions[]} */ +const buildConfigs = ([ // Discord Desktop main & renderer & preload - esbuild.build({ + { ...nodeCommonOpts, entryPoints: ["src/main/index.ts"], outfile: "dist/patcher.js", footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") }, sourcemap, - define: { - ...defines, - IS_DISCORD_DESKTOP: true, - IS_VESKTOP: false - }, plugins: [ + // @ts-ignore this is never undefined ...nodeCommonOpts.plugins, globNativesPlugin - ] - }), - esbuild.build({ + ], + define: { + ...defines, + IS_DISCORD_DESKTOP: "true", + IS_VESKTOP: "false" + } + }, + { ...commonOpts, entryPoints: ["src/Vencord.ts"], outfile: "dist/renderer.js", @@ -135,11 +140,11 @@ await Promise.all([ ], define: { ...defines, - IS_DISCORD_DESKTOP: true, - IS_VESKTOP: false + IS_DISCORD_DESKTOP: "true", + IS_VESKTOP: "false" } - }), - esbuild.build({ + }, + { ...nodeCommonOpts, entryPoints: ["src/preload.ts"], outfile: "dist/preload.js", @@ -147,29 +152,29 @@ await Promise.all([ sourcemap, define: { ...defines, - IS_DISCORD_DESKTOP: true, - IS_VESKTOP: false + IS_DISCORD_DESKTOP: "true", + IS_VESKTOP: "false" } - }), + }, // Vencord Desktop main & renderer & preload - esbuild.build({ + { ...nodeCommonOpts, entryPoints: ["src/main/index.ts"], outfile: "dist/vencordDesktopMain.js", footer: { js: "//# sourceURL=VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") }, sourcemap, - define: { - ...defines, - IS_DISCORD_DESKTOP: false, - IS_VESKTOP: true - }, plugins: [ ...nodeCommonOpts.plugins, globNativesPlugin - ] - }), - esbuild.build({ + ], + define: { + ...defines, + IS_DISCORD_DESKTOP: "false", + IS_VESKTOP: "true" + } + }, + { ...commonOpts, entryPoints: ["src/Vencord.ts"], outfile: "dist/vencordDesktopRenderer.js", @@ -184,11 +189,11 @@ await Promise.all([ ], define: { ...defines, - IS_DISCORD_DESKTOP: false, - IS_VESKTOP: true + IS_DISCORD_DESKTOP: "false", + IS_VESKTOP: "true" } - }), - esbuild.build({ + }, + { ...nodeCommonOpts, entryPoints: ["src/preload.ts"], outfile: "dist/vencordDesktopPreload.js", @@ -196,14 +201,10 @@ await Promise.all([ sourcemap, define: { ...defines, - IS_DISCORD_DESKTOP: false, - IS_VESKTOP: true + IS_DISCORD_DESKTOP: "false", + IS_VESKTOP: "true" } - }), -]).catch(err => { - console.error("Build failed"); - console.error(err.message); - // make ci fail - if (!commonOpts.watch) - process.exitCode = 1; -}); + } +]); + +await buildOrWatchAll(buildConfigs); diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index deab86610..33168ff97 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -17,29 +17,30 @@ * along with this program. If not, see . */ -import esbuild from "esbuild"; +// @ts-check + import { readFileSync } from "fs"; import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises"; import { join } from "path"; import Zip from "zip-local"; -import { BUILD_TIMESTAMP, commonOpts, globPlugins, IS_DEV, IS_REPORTER, VERSION, commonRendererPlugins } from "./common.mjs"; +import { BUILD_TIMESTAMP, commonOpts, globPlugins, IS_DEV, IS_REPORTER, VERSION, commonRendererPlugins, buildOrWatchAll, stringifyValues } from "./common.mjs"; /** - * @type {esbuild.BuildOptions} + * @type {import("esbuild").BuildOptions} */ const commonOptions = { ...commonOpts, entryPoints: ["browser/Vencord.ts"], - globalName: "Vencord", format: "iife", + globalName: "Vencord", external: ["~plugins", "~git-hash", "/assets/*"], + target: ["esnext"], plugins: [ globPlugins("web"), ...commonRendererPlugins ], - target: ["esnext"], - define: { + define: stringifyValues({ IS_WEB: true, IS_EXTENSION: false, IS_STANDALONE: true, @@ -48,9 +49,9 @@ const commonOptions = { IS_DISCORD_DESKTOP: false, IS_VESKTOP: false, IS_UPDATER_DISABLED: true, - VERSION: JSON.stringify(VERSION), + VERSION, BUILD_TIMESTAMP - } + }) }; const MonacoWorkerEntryPoints = [ @@ -58,70 +59,59 @@ const MonacoWorkerEntryPoints = [ "vs/editor/editor.worker.js" ]; -const RnNoiseFiles = [ - "dist/rnnoise.wasm", - "dist/rnnoise_simd.wasm", - "dist/rnnoise/workletProcessor.js", - "LICENSE" +/** @type {import("esbuild").BuildOptions[]} */ +const buildConfigs = [ + { + entryPoints: MonacoWorkerEntryPoints.map(entry => `node_modules/monaco-editor/esm/${entry}`), + bundle: true, + minify: true, + format: "iife", + outbase: "node_modules/monaco-editor/esm/", + outdir: "dist/vendor/monaco" + }, + { + entryPoints: ["browser/monaco.ts"], + bundle: true, + minify: true, + format: "iife", + outfile: "dist/vendor/monaco/index.js", + loader: { + ".ttf": "file" + } + }, + { + ...commonOptions, + outfile: "dist/browser.js", + footer: { js: "//# sourceURL=VencordWeb" } + }, + { + ...commonOptions, + outfile: "dist/extension.js", + define: { + ...commonOptions.define, + IS_EXTENSION: "true" + }, + footer: { js: "//# sourceURL=VencordWeb" } + }, + { + ...commonOptions, + inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])], + define: { + ...commonOptions.define, + window: "unsafeWindow", + }, + outfile: "dist/Vencord.user.js", + banner: { + js: readFileSync("browser/userscript.meta.js", "utf-8").replace("%version%", `${VERSION}.${new Date().getTime()}`) + }, + footer: { + // UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local + js: "Object.defineProperty(unsafeWindow,'Vencord',{get:()=>Vencord});" + } + } ]; -await Promise.all( - [ - esbuild.build({ - entryPoints: MonacoWorkerEntryPoints.map(entry => `node_modules/monaco-editor/esm/${entry}`), - bundle: true, - minify: true, - format: "iife", - outbase: "node_modules/monaco-editor/esm/", - outdir: "dist/monaco" - }), - esbuild.build({ - entryPoints: ["browser/monaco.ts"], - bundle: true, - minify: true, - format: "iife", - outfile: "dist/monaco/index.js", - loader: { - ".ttf": "file" - } - }), - esbuild.build({ - ...commonOptions, - outfile: "dist/browser.js", - footer: { js: "//# sourceURL=VencordWeb" } - }), - esbuild.build({ - ...commonOptions, - outfile: "dist/extension.js", - define: { - ...commonOptions?.define, - IS_EXTENSION: true, - }, - footer: { js: "//# sourceURL=VencordWeb" } - }), - esbuild.build({ - ...commonOptions, - inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])], - define: { - ...(commonOptions?.define), - window: "unsafeWindow", - }, - outfile: "dist/Vencord.user.js", - banner: { - js: readFileSync("browser/userscript.meta.js", "utf-8").replace("%version%", `${VERSION}.${new Date().getTime()}`) - }, - footer: { - // UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local - js: "Object.defineProperty(unsafeWindow,'Vencord',{get:()=>Vencord});" - } - }) - ] -).catch(err => { - console.error("Build failed"); - console.error(err.message); - if (!commonOpts.watch) - process.exit(1); -});; +await buildOrWatchAll(buildConfigs); /** * @type {(dir: string) => Promise} @@ -155,16 +145,13 @@ async function buildExtension(target, files) { const entries = { "dist/Vencord.js": await readFile("dist/extension.js"), "dist/Vencord.css": await readFile("dist/extension.css"), - ...await loadDir("dist/monaco"), - ...Object.fromEntries(await Promise.all(RnNoiseFiles.map(async file => - [`third-party/rnnoise/${file.replace(/^dist\//, "")}`, await readFile(`node_modules/@sapphi-red/web-noise-suppressor/${file}`)] - ))), + ...await loadDir("dist/vendor/monaco", "dist/"), ...Object.fromEntries(await Promise.all(files.map(async f => { let content = await readFile(join("browser", f)); if (f.startsWith("manifest")) { const json = JSON.parse(content.toString("utf-8")); json.version = VERSION; - content = new TextEncoder().encode(JSON.stringify(json)); + content = Buffer.from(new TextEncoder().encode(JSON.stringify(json))); } return [ @@ -210,7 +197,6 @@ if (!process.argv.includes("--skip-extension")) { Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip"); console.info("Packed Firefox Extension written to dist/extension-firefox.zip"); - } else { await appendCssRuntime; } diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 5e71c8c5f..920e59267 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -16,11 +16,13 @@ * along with this program. If not, see . */ +// @ts-check + import "../suppressExperimentalWarnings.js"; import "../checkNodeVersion.js"; import { exec, execSync } from "child_process"; -import esbuild from "esbuild"; +import esbuild, { build, context } from "esbuild"; import { constants as FsConstants, readFileSync } from "fs"; import { access, readdir, readFile } from "fs/promises"; import { minify as minifyHtml } from "html-minifier-terser"; @@ -31,7 +33,7 @@ import { getPluginTarget } from "../utils.mjs"; import { builtinModules } from "module"; /** @type {import("../../package.json")} */ -const PackageJSON = JSON.parse(readFileSync("package.json")); +const PackageJSON = JSON.parse(readFileSync("package.json", "utf-8")); export const VERSION = PackageJSON.version; // https://reproducible-builds.org/docs/source-date-epoch/ @@ -54,6 +56,34 @@ export const banner = { `.trim() }; +/** + * JSON.stringify all values in an object + * @type {(obj: Record) => Record} + */ +export function stringifyValues(obj) { + for (const key in obj) { + obj[key] = JSON.stringify(obj[key]); + } + return obj; +} + +/** + * @param {import("esbuild").BuildOptions[]} buildConfigs + */ +export async function buildOrWatchAll(buildConfigs) { + if (watch) { + await Promise.all(buildConfigs.map(cfg => + context(cfg).then(ctx => ctx.watch()) + )); + } else { + await Promise.all(buildConfigs.map(cfg => build(cfg))) + .catch(error => { + console.error(error.message); + process.exit(1); // exit immediately to skip the rest of the builds + }); + } +} + const PluginDefinitionNameMatcher = /definePlugin\(\{\s*(["'])?name\1:\s*(["'`])(.+?)\2/; /** * @param {string} base @@ -311,18 +341,16 @@ export const banImportPlugin = (filter, message) => ({ export const commonOpts = { logLevel: "info", bundle: true, - watch, minify: !watch && !IS_REPORTER, - sourcemap: watch ? "inline" : "", + sourcemap: watch ? "inline" : "external", legalComments: "linked", banner, plugins: [fileUrlPlugin, gitHashPlugin, gitRemotePlugin, stylePlugin], external: ["~plugins", "~git-hash", "~git-remote", "/assets/*"], inject: ["./scripts/build/inject/react.mjs"], + jsx: "transform", jsxFactory: "VencordCreateElement", - jsxFragment: "VencordFragment", - // Work around https://github.com/evanw/esbuild/issues/2460 - tsconfig: "./scripts/build/tsconfig.esbuild.json" + jsxFragment: "VencordFragment" }; const escapedBuiltinModules = builtinModules @@ -335,5 +363,6 @@ export const commonRendererPlugins = [ banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"), banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"), banImportPlugin(/^ts-pattern$/, "Cannot import from ts-pattern. match and P should be imported from @webpack/common"), + // @ts-ignore this is never undefined ...commonOpts.plugins ]; diff --git a/scripts/build/tsconfig.esbuild.json b/scripts/build/tsconfig.esbuild.json deleted file mode 100644 index e3e28a14d..000000000 --- a/scripts/build/tsconfig.esbuild.json +++ /dev/null @@ -1,7 +0,0 @@ -// Work around https://github.com/evanw/esbuild/issues/2460 -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "jsx": "react" - } -} diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 5cab1b46e..7bfda763b 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -16,11 +16,7 @@ * along with this program. If not, see . */ -/* eslint-disable no-fallthrough */ - -// eslint-disable-next-line spaced-comment /// -// eslint-disable-next-line spaced-comment /// import { createHmac } from "crypto"; diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts index 212078553..427acd11a 100644 --- a/src/debug/loadLazyChunks.ts +++ b/src/debug/loadLazyChunks.ts @@ -8,7 +8,7 @@ import { Logger } from "@utils/Logger"; import { canonicalizeMatch } from "@utils/patches"; import * as Webpack from "@webpack"; import { wreq } from "@webpack"; -import { AnyModuleFactory, ModuleFactory } from "webpack"; +import { AnyModuleFactory, ModuleFactory } from "@webpack/wreq.d"; export async function loadLazyChunks() { const LazyChunkLoaderLogger = new Logger("LazyChunkLoader"); @@ -140,8 +140,8 @@ export async function loadLazyChunks() { } Webpack.factoryListeners.add(factoryListener); - for (const factoryId in wreq.m) { - factoryListener(wreq.m[factoryId]); + for (const moduleId in wreq.m) { + factoryListener(wreq.m[moduleId]); } await chunksSearchingDone; diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 8d4194bc4..7a14609e1 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -6,9 +6,9 @@ import { Logger } from "@utils/Logger"; import * as Webpack from "@webpack"; -import { addPatch, patches } from "plugins"; -import { getBuildNumber } from "webpack/patchWebpack"; +import { getBuildNumber, patchTimings } from "@webpack/patcher"; +import { addPatch, patches } from "../plugins"; import { loadLazyChunks } from "./loadLazyChunks"; async function runReporter() { @@ -51,7 +51,7 @@ async function runReporter() { } } - for (const [plugin, moduleId, match, totalTime] of Vencord.WebpackPatcher.patchTimings) { + for (const [plugin, moduleId, match, totalTime] of patchTimings) { if (totalTime > 5) { new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`); } diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index 58d8d42a3..30920a067 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType, StartAt } from "@utils/types"; -import { WebpackRequire } from "webpack"; +import { WebpackRequire } from "@webpack/wreq.d"; const settings = definePluginSettings({ disableAnalytics: { diff --git a/src/plugins/index.ts b/src/plugins/index.ts index e1899b743..4a2688681 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -31,6 +31,7 @@ import { Logger } from "@utils/Logger"; import { canonicalizeFind, canonicalizeReplacement } from "@utils/patches"; import { Patch, Plugin, PluginDef, ReporterTestable, StartAt } from "@utils/types"; import { FluxDispatcher } from "@webpack/common"; +import { patches } from "@webpack/patcher"; import { FluxEvents } from "@webpack/types"; import Plugins from "~plugins"; @@ -41,7 +42,7 @@ const logger = new Logger("PluginManager", "#a6d189"); export const PMLogger = logger; export const plugins = Plugins; -export const patches = [] as Patch[]; +export { patches }; /** Whether we have subscribed to flux events of all the enabled plugins when FluxDispatcher was ready */ let enabledPluginsSubscribedFlux = false; diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 870362373..14c1888c9 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -9,32 +9,23 @@ import { makeLazy } from "@utils/lazy"; import { Logger } from "@utils/Logger"; import { interpolateIfDefined } from "@utils/misc"; import { canonicalizeReplacement } from "@utils/patches"; -import { PatchReplacement } from "@utils/types"; +import { Patch, PatchReplacement } from "@utils/types"; import { traceFunctionWithResults } from "../debug/Tracer"; -import { patches } from "../plugins"; -import { _initWebpack, _shouldIgnoreModule, AnyModuleFactory, AnyWebpackRequire, factoryListeners, findModuleId, MaybeWrappedModuleFactory, ModuleExports, moduleListeners, waitForSubscriptions, WebpackRequire, WrappedModuleFactory, wreq } from "."; +import { _initWebpack, _shouldIgnoreModule, factoryListeners, findModuleId, moduleListeners, waitForSubscriptions, wreq } from "./webpack"; +import { AnyModuleFactory, AnyWebpackRequire, MaybePatchedModuleFactory, ModuleExports, PatchedModuleFactory, WebpackRequire } from "./wreq.d"; + +export const patches = [] as Patch[]; export const SYM_ORIGINAL_FACTORY = Symbol("WebpackPatcher.originalFactory"); export const SYM_PATCHED_SOURCE = Symbol("WebpackPatcher.patchedSource"); export const SYM_PATCHED_BY = Symbol("WebpackPatcher.patchedBy"); -/** A set with all the Webpack instances */ export const allWebpackInstances = new Set(); -export const patchTimings = [] as Array<[plugin: string, moduleId: PropertyKey, match: string | RegExp, totalTime: number]>; -const logger = new Logger("WebpackInterceptor", "#8caaee"); -/** Whether we tried to fallback to factory WebpackRequire, or disabled patches */ -let wreqFallbackApplied = false; -/** Whether we should be patching factories. - * - * This should be disabled if we start searching for the module to get the build number, and then resumed once it's done. - * */ -let shouldPatchFactories = true; +export const patchTimings = [] as Array<[plugin: string, moduleId: PropertyKey, match: PatchReplacement["match"], totalTime: number]>; export const getBuildNumber = makeLazy(() => { try { - shouldPatchFactories = false; - try { if (wreq.m[128014]?.toString().includes("Trying to open a changelog for an invalid build number")) { const hardcodedGetBuildNumber = wreq(128014).b as () => number; @@ -59,13 +50,23 @@ export const getBuildNumber = makeLazy(() => { return typeof buildNumber === "number" ? buildNumber : -1; } catch { return -1; - } finally { - shouldPatchFactories = true; } }); -type Define = typeof Reflect.defineProperty; -const define: Define = (target, p, attributes) => { +export function getFactoryPatchedSource(moduleId: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) { + return webpackRequire.m[moduleId]?.[SYM_PATCHED_SOURCE]; +} + +export function getFactoryPatchedBy(moduleId: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) { + return webpackRequire.m[moduleId]?.[SYM_PATCHED_BY]; +} + +const logger = new Logger("WebpackInterceptor", "#8caaee"); + +/** Whether we tried to fallback to the WebpackRequire of the factory, or disabled patches */ +let wreqFallbackApplied = false; + +const define: typeof Reflect.defineProperty = (target, p, attributes) => { if (Object.hasOwn(attributes, "value")) { attributes.writable = true; } @@ -77,22 +78,17 @@ const define: Define = (target, p, attributes) => { }); }; -export function getOriginalFactory(id: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) { - const moduleFactory = webpackRequire.m[id]; - return (moduleFactory?.[SYM_ORIGINAL_FACTORY] ?? moduleFactory) as AnyModuleFactory | undefined; -} +// wreq.m is the Webpack object containing module factories. It is pre-populated with factories, and is also populated via webpackGlobal.push +// We use this setter to intercept when wreq.m is defined and apply patching to its factories. -export function getFactoryPatchedSource(id: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) { - return webpackRequire.m[id]?.[SYM_PATCHED_SOURCE]; -} +// Factories can be patched in two ways. Eagerly or lazily. +// If we are patching eagerly, pre-populated factories are patched immediately and new factories are patched when set. +// Else, we only patch them when called. -export function getFactoryPatchedBy(id: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) { - return webpackRequire.m[id]?.[SYM_PATCHED_BY]; -} +// Factories are always wrapped in a proxy, which allows us to intercept the call to them, patch if they werent eagerly patched, +// and call them with our wrapper which notifies our listeners. -// wreq.m is the Webpack object containing module factories. It is pre-populated with module factories, and is also populated via webpackGlobal.push -// We use this setter to intercept when wreq.m is defined and apply the patching in its module factories. -// We wrap wreq.m with our proxy, which is responsible for patching the module factories when they are set, or defining getters for the patched versions. +// wreq.m is also wrapped in a proxy to intercept when new factories are set, patch them eargely, if enabled, and wrap them in the factory proxy. // If this is the main Webpack, we also set up the internal references to WebpackRequire. define(Function.prototype, "m", { @@ -131,13 +127,17 @@ define(Function.prototype, "m", { const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "e"), 0); // Patch the pre-populated factories - for (const id in originalModules) { - if (updateExistingFactory(originalModules, id, originalModules[id], true)) { + for (const moduleId in originalModules) { + const originalFactory = originalModules[moduleId]; + + if (updateExistingFactory(originalModules, moduleId, originalFactory, originalModules, true)) { continue; } - notifyFactoryListeners(originalModules[id]); - defineModulesFactoryGetter(id, Settings.eagerPatches && shouldPatchFactories ? wrapAndPatchFactory(id, originalModules[id]) : originalModules[id]); + notifyFactoryListeners(moduleId, originalFactory); + + const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(moduleId, originalFactory) : originalFactory, moduleFactoryHandler); + define(originalModules, moduleId, { value: proxiedFactory }); } define(originalModules, Symbol.toStringTag, { @@ -145,7 +145,6 @@ define(Function.prototype, "m", { enumerable: false }); - // The proxy responsible for patching the module factories when they are set, or defining getters for the patched versions const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler); /* If Webpack ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype @@ -156,6 +155,7 @@ define(Function.prototype, "m", { } }); +// The proxy for patching eagerly and/or wrapping factories in their proxy. const moduleFactoriesHandler: ProxyHandler = { /* If Webpack ever decides to set module factories using the variable of the modules object directly instead of wreq.m, we need to switch the proxy to the prototype @@ -172,57 +172,96 @@ const moduleFactoriesHandler: ProxyHandler = { }, */ - // The set trap for patching or defining getters for the module factories when new module factories are loaded set(target, p, newValue, receiver) { - if (updateExistingFactory(target, p, newValue)) { + if (updateExistingFactory(target, p, newValue, receiver)) { return true; } - notifyFactoryListeners(newValue); - defineModulesFactoryGetter(p, Settings.eagerPatches && shouldPatchFactories ? wrapAndPatchFactory(p, newValue) : newValue); + notifyFactoryListeners(p, newValue); - return true; + const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(p, newValue) : newValue, moduleFactoryHandler); + return Reflect.set(target, p, proxiedFactory, receiver); + } +}; + +// The proxy for patching lazily and/or running factories with our wrapper. +const moduleFactoryHandler: ProxyHandler = { + apply(target, thisArg: unknown, argArray: Parameters) { + // SAFETY: Factories have `name` as their key in the module factories object, and that is always their module id + const moduleId = target.name; + + // SYM_ORIGINAL_FACTORY means the factory has already been patched + if (target[SYM_ORIGINAL_FACTORY] != null) { + return runFactoryWithWrap(moduleId, target as PatchedModuleFactory, thisArg, argArray); + } + + const patchedFactory = patchFactory(moduleId, target); + return runFactoryWithWrap(moduleId, patchedFactory, thisArg, argArray); + }, + + get(target, p, receiver) { + if (target[SYM_ORIGINAL_FACTORY] != null && (p === SYM_PATCHED_SOURCE || p === SYM_PATCHED_BY)) { + return Reflect.get(target[SYM_ORIGINAL_FACTORY], p, target[SYM_ORIGINAL_FACTORY]); + } + + const v = Reflect.get(target, p, receiver); + + // Make proxied factories `toString` return their original factory `toString` + if (p === "toString") { + return v.bind(target[SYM_ORIGINAL_FACTORY] ?? target); + } + + return v; } }; /** * Update a factory that exists in any Webpack instance with a new original factory. * - * @target The module factories where this new original factory is being set - * @param id The id of the module + * @param moduleFactoriesTarget The module factories where this new original factory is being set + * @param moduleId The id of the module * @param newFactory The new original factory + * @param receiver The receiver of the new factory * @param ignoreExistingInTarget Whether to ignore checking if the factory already exists in the moduleFactoriesTarget * @returns Whether the original factory was updated, or false if it doesn't exist in any Webpack instance */ -function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], id: PropertyKey, newFactory: AnyModuleFactory, ignoreExistingInTarget: boolean = false) { +function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget: boolean = false) { let existingFactory: TypedPropertyDescriptor | undefined; let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined; for (const wreq of allWebpackInstances) { - if (ignoreExistingInTarget && wreq.m === moduleFactoriesTarget) continue; + if (ignoreExistingInTarget && wreq.m === moduleFactoriesTarget) { + continue; + } - if (Object.hasOwn(wreq.m, id)) { - existingFactory = Reflect.getOwnPropertyDescriptor(wreq.m, id); + if (Object.hasOwn(wreq.m, moduleId)) { + existingFactory = Reflect.getOwnPropertyDescriptor(wreq.m, moduleId); moduleFactoriesWithFactory = wreq.m; break; } } if (existingFactory != null) { - // If existingFactory exists in any Webpack instance, it's either wrapped in defineModuleFactoryGetter, or it has already been required. - // So define the descriptor of it on this current Webpack instance (if it doesn't exist already), call Reflect.set with the new original, - // and let the correct logic apply (normal set, or defineModuleFactoryGetter setter) - + // If existingFactory exists in any Webpack instance, it's either wrapped in our proxy, or it has already been required. + // In the case it is wrapped in our proxy, we need the Webpack instance with this new original factory to also have our proxy. + // So, define the descriptor of the existing factory on it. if (moduleFactoriesWithFactory !== moduleFactoriesTarget) { - Reflect.defineProperty(moduleFactoriesTarget, id, existingFactory); + Reflect.defineProperty(receiver, moduleId, existingFactory); } - // Persist patched source and patched by in the new original factory, if the patched one has already been required - if (IS_DEV && existingFactory.value != null) { - newFactory[SYM_PATCHED_SOURCE] = existingFactory.value[SYM_PATCHED_SOURCE]; - newFactory[SYM_PATCHED_BY] = existingFactory.value[SYM_PATCHED_BY]; + const existingFactoryValue = moduleFactoriesWithFactory![moduleId]; + + // Update with the new original factory, if it does have a current original factory + if (existingFactoryValue[SYM_ORIGINAL_FACTORY] != null) { + existingFactoryValue[SYM_ORIGINAL_FACTORY] = newFactory; } - return Reflect.set(moduleFactoriesTarget, id, newFactory, moduleFactoriesTarget); + // Persist patched source and patched by in the new original factory + if (IS_DEV) { + newFactory[SYM_PATCHED_SOURCE] = existingFactoryValue[SYM_PATCHED_SOURCE]; + newFactory[SYM_PATCHED_BY] = existingFactoryValue[SYM_PATCHED_BY]; + } + + return true; } return false; @@ -231,12 +270,13 @@ function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], id /** * Notify all factory listeners. * + * @param moduleId The id of the module * @param factory The original factory to notify for */ -function notifyFactoryListeners(factory: AnyModuleFactory) { +function notifyFactoryListeners(moduleId: PropertyKey, factory: AnyModuleFactory) { for (const factoryListener of factoryListeners) { try { - factoryListener(factory); + factoryListener(factory, moduleId); } catch (err) { logger.error("Error in Webpack factory listener:\n", err, factoryListener); } @@ -244,190 +284,138 @@ function notifyFactoryListeners(factory: AnyModuleFactory) { } /** - * Define the getter for returning the patched version of the module factory. + * Run a (possibly) patched module factory with a wrapper which notifies our listeners. * - * If eagerPatches is enabled, the factory argument should already be the patched version, else it will be the original - * and only be patched when accessed for the first time. - * - * @param id The id of the module - * @param factory The original or patched module factory + * @param moduleId The id of the module + * @param patchedFactory The (possibly) patched module factory + * @param thisArg The `value` of the call to the factory + * @param argArray The arguments of the call to the factory */ -function defineModulesFactoryGetter(id: PropertyKey, factory: MaybeWrappedModuleFactory) { - const descriptor: PropertyDescriptor = { - get() { - // SYM_ORIGINAL_FACTORY means the factory is already patched - if (!shouldPatchFactories || factory[SYM_ORIGINAL_FACTORY] != null) { - return factory; - } +function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModuleFactory, thisArg: unknown, argArray: Parameters) { + const originalFactory = patchedFactory[SYM_ORIGINAL_FACTORY]; - return (factory = wrapAndPatchFactory(id, factory)); - }, - set(newFactory: MaybeWrappedModuleFactory) { - if (IS_DEV) { - newFactory[SYM_PATCHED_SOURCE] = factory[SYM_PATCHED_SOURCE]; - newFactory[SYM_PATCHED_BY] = factory[SYM_PATCHED_BY]; - } - - if (factory[SYM_ORIGINAL_FACTORY] != null) { - factory.toString = newFactory.toString.bind(newFactory); - factory[SYM_ORIGINAL_FACTORY] = newFactory; - } else { - factory = newFactory; - } - } - }; - - // Define the getter in all the module factories objects. Patches are only executed once, so make sure all module factories object - // have the patched version - for (const wreq of allWebpackInstances) { - define(wreq.m, id, descriptor); + if (patchedFactory === originalFactory) { + // @ts-expect-error Clear up ORIGINAL_FACTORY if the factory did not have any patch applied + delete patchedFactory[SYM_ORIGINAL_FACTORY]; } -} -/** - * Wraps and patches a module factory. - * - * @param id The id of the module - * @param factory The original or patched module factory - * @returns The wrapper for the patched module factory - */ -function wrapAndPatchFactory(id: PropertyKey, originalFactory: AnyModuleFactory) { - const [patchedFactory, patchedSource, patchedBy] = patchFactory(id, originalFactory); + // Restore the original factory in all the module factories objects, discarding our proxy and allowing it to be garbage collected + for (const wreq of allWebpackInstances) { + define(wreq.m, moduleId, { value: originalFactory }); + } - const wrappedFactory: WrappedModuleFactory = function (...args) { - // Restore the original factory in all the module factories objects. We want to make sure the original factory is restored properly, no matter what is the Webpack instance - for (const wreq of allWebpackInstances) { - define(wreq.m, id, { value: wrappedFactory[SYM_ORIGINAL_FACTORY] }); - } + let [module, exports, require] = argArray; - // eslint-disable-next-line prefer-const - let [module, exports, require] = args; + if (wreq == null) { + if (!wreqFallbackApplied) { + wreqFallbackApplied = true; - if (wreq == null) { - if (!wreqFallbackApplied) { - wreqFallbackApplied = true; + // Make sure the require argument is actually the WebpackRequire function + if (typeof require === "function" && require.m != null) { + const { stack } = new Error(); + const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1]; - // Make sure the require argument is actually the WebpackRequire function - if (typeof require === "function" && require.m != null) { - const { stack } = new Error(); - const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1]; + logger.warn( + "WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called wrapped module factory (" + + `id: ${String(moduleId)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` + + ")" + ); - logger.warn( - "WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called patched module factory (" + - `id: ${String(id)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` + - ")" - ); - - _initWebpack(require as WebpackRequire); - } else if (IS_DEV) { - logger.error("WebpackRequire was not initialized, running modules without patches instead."); - return wrappedFactory[SYM_ORIGINAL_FACTORY].apply(this, args); - } + // Could technically be wrong, but it's better than nothing + _initWebpack(require as WebpackRequire); } else if (IS_DEV) { - return wrappedFactory[SYM_ORIGINAL_FACTORY].apply(this, args); + logger.error("WebpackRequire was not initialized, running modules without patches instead."); + return originalFactory.apply(thisArg, argArray); } + } else if (IS_DEV) { + return originalFactory.apply(thisArg, argArray); + } + } + + let factoryReturn: unknown; + try { + factoryReturn = patchedFactory.apply(thisArg, argArray); + } catch (err) { + // Just re-throw Discord errors + if (patchedFactory === originalFactory) { + throw err; } - let factoryReturn: unknown; - try { - // Call the patched factory - factoryReturn = patchedFactory.apply(this, args); - } catch (err) { - // Just re-throw Discord errors - if (patchedFactory === wrappedFactory[SYM_ORIGINAL_FACTORY]) { - throw err; + logger.error("Error in patched module factory:\n", err); + return originalFactory.apply(thisArg, argArray); + } + + exports = module.exports; + if (exports == null) { + return factoryReturn; + } + + if (typeof require === "function") { + const shouldIgnoreModule = _shouldIgnoreModule(exports); + + if (shouldIgnoreModule) { + if (require.c != null) { + Object.defineProperty(require.c, moduleId, { + value: require.c[moduleId], + enumerable: false, + configurable: true, + writable: true + }); } - logger.error("Error in patched module factory:\n", err); - return wrappedFactory[SYM_ORIGINAL_FACTORY].apply(this, args); - } - - exports = module.exports; - if (exports == null) { return factoryReturn; } - - if (typeof require === "function") { - const shouldIgnoreModule = _shouldIgnoreModule(exports); - - if (shouldIgnoreModule) { - if (require.c != null) { - Object.defineProperty(require.c, id, { - value: require.c[id], - enumerable: false, - configurable: true, - writable: true - }); - } - - return factoryReturn; - } - } - - for (const callback of moduleListeners) { - try { - callback(exports, id); - } catch (err) { - logger.error("Error in Webpack module listener:\n", err, callback); - } - } - - for (const [filter, callback] of waitForSubscriptions) { - try { - if (filter(exports)) { - waitForSubscriptions.delete(filter); - callback(exports, id); - continue; - } - - if (typeof exports !== "object") { - continue; - } - - for (const exportKey in exports) { - const exportValue = exports[exportKey]; - - if (exportValue != null && filter(exportValue)) { - waitForSubscriptions.delete(filter); - callback(exportValue, id); - break; - } - } - } catch (err) { - logger.error("Error while firing callback for Webpack waitFor subscription:\n", err, filter, callback); - } - } - - return factoryReturn; - }; - - wrappedFactory.toString = originalFactory.toString.bind(originalFactory); - wrappedFactory[SYM_ORIGINAL_FACTORY] = originalFactory; - - if (IS_DEV && patchedFactory !== originalFactory) { - wrappedFactory[SYM_PATCHED_SOURCE] = patchedSource; - wrappedFactory[SYM_PATCHED_BY] = patchedBy; - originalFactory[SYM_PATCHED_SOURCE] = patchedSource; - originalFactory[SYM_PATCHED_BY] = patchedBy; } - // @ts-expect-error Allow GC to get into action, if possible - originalFactory = undefined; - return wrappedFactory; + for (const callback of moduleListeners) { + try { + callback(exports, moduleId); + } catch (err) { + logger.error("Error in Webpack module listener:\n", err, callback); + } + } + + for (const [filter, callback] of waitForSubscriptions) { + try { + if (filter(exports)) { + waitForSubscriptions.delete(filter); + callback(exports, moduleId); + continue; + } + + if (typeof exports !== "object") { + continue; + } + + for (const exportKey in exports) { + const exportValue = exports[exportKey]; + + if (exportValue != null && filter(exportValue)) { + waitForSubscriptions.delete(filter); + callback(exportValue, moduleId); + break; + } + } + } catch (err) { + logger.error("Error while firing callback for Webpack waitFor subscription:\n", err, filter, callback); + } + } + + return factoryReturn; } /** * Patches a module factory. * - * @param id The id of the module - * @param factory The original module factory - * @returns The patched module factory, the patched source of it, and the plugins that patched it + * @param moduleId The id of the module + * @param originalFactory The original module factory + * @returns The patched module factory */ -function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFactory: AnyModuleFactory, patchedSource: string, patchedBy: Set] { +function patchFactory(moduleId: PropertyKey, originalFactory: AnyModuleFactory): PatchedModuleFactory { // 0, prefix to turn it into an expression: 0,function(){} would be invalid syntax without the 0, - let code: string = "0," + String(factory); + let code: string = "0," + String(originalFactory); let patchedSource = code; - let patchedFactory = factory; + let patchedFactory = originalFactory; const patchedBy = new Set(); @@ -442,8 +430,8 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto continue; } - // Reporter eagerly patches and cannot retrieve the build number because this code runs before the module for it is loaded - const buildNumber = IS_REPORTER ? -1 : getBuildNumber(); + // Eager patches cannot retrieve the build number because this code runs before the module for it is loaded + const buildNumber = Settings.eagerPatches ? -1 : getBuildNumber(); const shouldCheckBuildNumber = !Settings.eagerPatches && buildNumber !== -1; if ( @@ -463,7 +451,7 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto }); const previousCode = code; - const previousFactory = factory; + const previousFactory = originalFactory; let markedAsPatched = false; // We change all patch.replacement to array in plugins/index @@ -482,18 +470,18 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto } const lastCode = code; - const lastFactory = factory; + const lastFactory = originalFactory; try { const [newCode, totalTime] = executePatch(replacement.match, replacement.replace as string); if (IS_REPORTER) { - patchTimings.push([patch.plugin, id, replacement.match, totalTime]); + patchTimings.push([patch.plugin, moduleId, replacement.match, totalTime]); } if (newCode === code) { if (!patch.noWarn) { - logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${String(id)}): ${replacement.match}`); + logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${String(moduleId)}): ${replacement.match}`); if (IS_DEV) { logger.debug("Function Source:\n", code); } @@ -515,7 +503,7 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto } code = newCode; - patchedSource = `// Webpack Module ${String(id)} - Patched by ${[...patchedBy, patch.plugin].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${String(id)}`; + patchedSource = `// Webpack Module ${String(moduleId)} - Patched by ${[...patchedBy, patch.plugin].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${String(moduleId)}`; patchedFactory = (0, eval)(patchedSource); if (!patchedBy.has(patch.plugin)) { @@ -523,7 +511,7 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto markedAsPatched = true; } } catch (err) { - logger.error(`Patch by ${patch.plugin} errored (Module id is ${String(id)}): ${replacement.match}\n`, err); + logger.error(`Patch by ${patch.plugin} errored (Module id is ${String(moduleId)}): ${replacement.match}\n`, err); if (IS_DEV) { diffErroredPatch(code, lastCode, lastCode.match(replacement.match)!); @@ -550,7 +538,14 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto } } - return [patchedFactory, patchedSource, patchedBy]; + patchedFactory[SYM_ORIGINAL_FACTORY] = originalFactory; + + if (IS_DEV && patchedFactory !== originalFactory) { + originalFactory[SYM_PATCHED_SOURCE] = patchedSource; + originalFactory[SYM_PATCHED_BY] = patchedBy; + } + + return patchedFactory as PatchedModuleFactory; } function diffErroredPatch(code: string, lastCode: string, match: RegExpMatchArray) { diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 8d5b3c688..09c04a2a1 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -90,7 +90,7 @@ export const filters = { }; export type CallbackFn = (module: ModuleExports, id: PropertyKey) => void; -export type FactoryListernFn = (factory: AnyModuleFactory) => void; +export type FactoryListernFn = (factory: AnyModuleFactory, moduleId: PropertyKey) => void; export const waitForSubscriptions = new Map(); export const moduleListeners = new Set(); diff --git a/src/webpack/wreq.d.ts b/src/webpack/wreq.d.ts index dbc451054..b9f5353f3 100644 --- a/src/webpack/wreq.d.ts +++ b/src/webpack/wreq.d.ts @@ -200,12 +200,10 @@ export type AnyModuleFactory = ((this: ModuleExports, module: Module, exports: M [SYM_PATCHED_BY]?: Set; }; -export type WrappedModuleFactory = AnyModuleFactory & { +export type PatchedModuleFactory = AnyModuleFactory & { [SYM_ORIGINAL_FACTORY]: AnyModuleFactory; [SYM_PATCHED_SOURCE]?: string; [SYM_PATCHED_BY]?: Set; }; -export type MaybeWrappedModuleFactory = AnyModuleFactory | WrappedModuleFactory; - -export type WrappedModuleFactories = Record; +export type MaybePatchedModuleFactory = PatchedModuleFactory | AnyModuleFactory; diff --git a/tsconfig.json b/tsconfig.json index db6d0918d..d2a42bd57 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,7 +29,9 @@ "@shared/*": ["./shared/*"], "@webpack/types": ["./webpack/common/types"], "@webpack/common": ["./webpack/common"], - "@webpack": ["./webpack/webpack"] + "@webpack": ["./webpack/webpack"], + "@webpack/patcher": ["./webpack/patchWebpack"], + "@webpack/wreq.d": ["./webpack/wreq.d"], }, "plugins": [