diff --git a/package-lock.json b/package-lock.json index 76c306d95..a2d2cd609 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,7 @@ "@types/url-parse": "^1.4.11", "autoprefixer": "^10.4.21", "babel-loader": "^10.0.0", + "browserslist-to-esbuild": "^2.1.1", "com.foxdebug.acode.rk.exec.proot": "file:src/plugins/proot", "com.foxdebug.acode.rk.exec.terminal": "file:src/plugins/terminal", "cordova-android": "^14.0.1", @@ -71,8 +72,10 @@ "cordova-plugin-system": "file:src/plugins/system", "cordova-plugin-websocket": "file:src/plugins/websocket", "css-loader": "^7.1.2", + "esbuild": "^0.25.10", "mini-css-extract-plugin": "^2.9.3", "path-browserify": "^1.0.1", + "postcss": "^8.4.38", "postcss-loader": "^8.1.1", "prettier": "^3.6.2", "prettier-plugin-java": "^2.7.4", @@ -128,6 +131,7 @@ "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -1881,6 +1885,448 @@ "node": ">=14.17.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", @@ -2958,8 +3404,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/markdown-it": { "version": "14.1.2", @@ -2976,8 +3421,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/node": { "version": "24.2.1", @@ -3290,7 +3734,8 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", @@ -3321,6 +3766,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3354,6 +3800,7 @@ "version": "8.12.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -3734,6 +4181,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001733", "electron-to-chromium": "^1.5.199", @@ -3747,6 +4195,25 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/browserslist-to-esbuild": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/browserslist-to-esbuild/-/browserslist-to-esbuild-2.1.1.tgz", + "integrity": "sha512-KN+mty6C3e9AN8Z5dI1xeN15ExcRNeISoC3g7V0Kax/MMF9MSoYA2G7lkTTcVUFntiEjkpI0HNgqJC1NjdyNUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "meow": "^13.0.0" + }, + "bin": { + "browserslist-to-esbuild": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "browserslist": "*" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3836,6 +4303,7 @@ "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@chevrotain/cst-dts-gen": "11.0.3", "@chevrotain/gast": "11.0.3", @@ -4948,6 +5416,48 @@ "dev": true, "license": "MIT" }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -6016,6 +6526,7 @@ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "license": "MIT", + "peer": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -6067,6 +6578,19 @@ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "license": "MIT" @@ -6326,9 +6850,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -6336,6 +6860,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6883,9 +7408,9 @@ } }, "node_modules/postcss": { - "version": "8.4.33", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", - "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -6901,10 +7426,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", + "peer": true, "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -7055,6 +7582,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -7221,6 +7749,7 @@ "version": "6.12.6", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7731,7 +8260,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -8053,6 +8584,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8324,6 +8856,7 @@ "integrity": "sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -8373,6 +8906,7 @@ "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.6.1", "@webpack-cli/configtest": "^3.0.1", diff --git a/package.json b/package.json index f29c7648b..efc50f90d 100644 --- a/package.json +++ b/package.json @@ -80,8 +80,10 @@ "cordova-plugin-system": "file:src/plugins/system", "cordova-plugin-websocket": "file:src/plugins/websocket", "css-loader": "^7.1.2", + "esbuild": "^0.25.10", "mini-css-extract-plugin": "^2.9.3", "path-browserify": "^1.0.1", + "postcss": "^8.4.38", "postcss-loader": "^8.1.1", "prettier": "^3.6.2", "prettier-plugin-java": "^2.7.4", diff --git a/src/components/quickTools/footer.js b/src/components/quickTools/footer.js index d94d2411f..9f3af4bba 100644 --- a/src/components/quickTools/footer.js +++ b/src/components/quickTools/footer.js @@ -3,7 +3,7 @@ */ import settings from "lib/settings"; -import items, { ref } from "./items"; +import items from "./items"; /** * Create a row with common buttons diff --git a/src/lib/acode.js b/src/lib/acode.js index 8783d6d24..0e7939338 100644 --- a/src/lib/acode.js +++ b/src/lib/acode.js @@ -29,6 +29,11 @@ import EditorFile from "lib/editorFile"; import files from "lib/fileList"; import fileTypeHandler from "lib/fileTypeHandler"; import fonts from "lib/fonts"; +import { + LOADED_PLUGINS, + onPluginLoadCallback, + onPluginsLoadCompleteCallback, +} from "lib/loadPlugins"; import NotificationManager from "lib/notificationManager"; import openFolder, { addedFolder } from "lib/openFolder"; import projects from "lib/projects"; @@ -65,6 +70,23 @@ export default class Acode { }, }, ]; + #pluginWatchers = {}; + + /** + * Clear a plugin's broken mark (so it can be retried) + * @param {string} pluginId + */ + clearBrokenPluginMark(pluginId) { + try { + const broken = appSettings.value.pluginsBroken || {}; + if (broken[pluginId]) { + delete broken[pluginId]; + appSettings.update(false); + } + } catch (e) { + console.warn("Failed to clear broken plugin mark:", e); + } + } constructor() { const encodingsModule = { @@ -288,112 +310,111 @@ export default class Acode { */ installPlugin(pluginId, installerPluginName) { return new Promise((resolve, reject) => { - confirm( - strings.install, - `Do you want to install plugin '${pluginId}'${installerPluginName ? ` requested by ${installerPluginName}` : ""}?`, - ) - .then((confirmation) => { - if (!confirmation) { - reject(new Error("User cancelled installation")); + fsOperation(Url.join(PLUGIN_DIR, pluginId)) + .exists() + .then((isPluginExists) => { + if (isPluginExists) { + reject(new Error("Plugin already installed")); return; } - fsOperation(Url.join(PLUGIN_DIR, pluginId)) - .exists() - .then((isPluginExists) => { - if (isPluginExists) { - reject(new Error("Plugin already installed")); - return; - } - - let purchaseToken; - let product; - const pluginUrl = Url.join( - constants.API_BASE, - `plugin/${pluginId}`, - ); - fsOperation(pluginUrl) - .readFile("json") - .catch(() => { - reject(new Error("Failed to fetch plugin details")); - return null; - }) - .then((remotePlugin) => { - if (remotePlugin) { - const isPaid = remotePlugin.price > 0; - helpers - .promisify(iap.getProducts, [remotePlugin.sku]) - .then((products) => { - [product] = products; - if (product) { - return getPurchase(product.productId); - } - return null; - }) - .then((purchase) => { - purchaseToken = purchase?.purchaseToken; - - if (isPaid && !purchaseToken) { - if (!product) throw new Error("Product not found"); - return helpers.checkAPIStatus().then((apiStatus) => { - if (!apiStatus) { - alert(strings.error, strings.api_error); - return; - } - - iap.setPurchaseUpdatedListener( - ...purchaseListener(onpurchase, onerror), - ); - return helpers.promisify( - iap.purchase, - product.productId, - ); + confirm( + strings.install, + `Do you want to install plugin '${pluginId}'${installerPluginName ? ` requested by ${installerPluginName}` : ""}?`, + ).then((confirmation) => { + if (!confirmation) { + reject(new Error("User cancelled installation")); + return; + } + + let purchaseToken; + let product; + const pluginUrl = Url.join( + constants.API_BASE, + `plugin/${pluginId}`, + ); + fsOperation(pluginUrl) + .readFile("json") + .catch(() => { + reject(new Error("Failed to fetch plugin details")); + return null; + }) + .then((remotePlugin) => { + if (remotePlugin) { + const isPaid = remotePlugin.price > 0; + helpers + .promisify(iap.getProducts, [remotePlugin.sku]) + .then((products) => { + [product] = products; + if (product) { + return getPurchase(product.productId); + } + return null; + }) + .then((purchase) => { + purchaseToken = purchase?.purchaseToken; + + if (isPaid && !purchaseToken) { + if (!product) throw new Error("Product not found"); + return helpers.checkAPIStatus().then((apiStatus) => { + if (!apiStatus) { + alert(strings.error, strings.api_error); + return; + } + + iap.setPurchaseUpdatedListener( + ...purchaseListener(onpurchase, onerror), + ); + return helpers.promisify( + iap.purchase, + product.productId, + ); + }); + } + }) + .then(() => { + import("lib/installPlugin").then( + ({ default: installPlugin }) => { + installPlugin( + pluginId, + remotePlugin.name, + purchaseToken, + ).then(() => { + resolve(); }); - } - }) - .then(() => { - import("lib/installPlugin").then( - ({ default: installPlugin }) => { - installPlugin( - pluginId, - remotePlugin.name, - purchaseToken, - ).then(() => { - resolve(); - }); - }, - ); - }); - - async function onpurchase(e) { - const purchase = await getPurchase(product.productId); - await ajax.post( - Url.join(constants.API_BASE, "plugin/order"), - { - data: { - id: remotePlugin.id, - token: purchase?.purchaseToken, - package: BuildInfo.packageName, - }, }, ); - purchaseToken = purchase?.purchaseToken; - } + }); + + async function onpurchase(e) { + const purchase = await getPurchase(product.productId); + await ajax.post( + Url.join(constants.API_BASE, "plugin/order"), + { + data: { + id: remotePlugin.id, + token: purchase?.purchaseToken, + package: BuildInfo.packageName, + }, + }, + ); + purchaseToken = purchase?.purchaseToken; + } - async function onerror(error) { - throw error; - } + async function onerror(error) { + throw error; } - }); - - async function getPurchase(sku) { - const purchases = await helpers.promisify(iap.getPurchases); - const purchase = purchases.find((p) => - p.productIds.includes(sku), - ); - return purchase; - } - }); + } + }); + + async function getPurchase(sku) { + const purchases = await helpers.promisify(iap.getPurchases); + const purchase = purchases.find((p) => + p.productIds.includes(sku), + ); + return purchase; + } + }); }) .catch((error) => { reject(error); @@ -401,6 +422,32 @@ export default class Acode { }); } + [onPluginLoadCallback](pluginId) { + if (this.#pluginWatchers[pluginId]) { + this.#pluginWatchers[pluginId].resolve(); + delete this.#pluginWatchers[pluginId]; + } + } + + [onPluginsLoadCompleteCallback]() { + for (const key in this.#pluginWatchers) { + this.#pluginWatchers[key].reject(); + } + } + + waitForPlugin(pluginId) { + return new Promise((resolve, reject) => { + if (LOADED_PLUGINS.has(pluginId)) { + return resolve(true); + } + + this.#pluginWatchers[pluginId] = { + resolve, + reject, + }; + }); + } + get exitAppMessage() { const numFiles = editorManager.hasUnsavedFiles(); if (numFiles) { diff --git a/src/lib/loadPlugins.js b/src/lib/loadPlugins.js index 415a15944..af9d81226 100644 --- a/src/lib/loadPlugins.js +++ b/src/lib/loadPlugins.js @@ -24,11 +24,18 @@ const THEME_IDENTIFIERS = new Set([ "acode.plugin.extra_syntax_highlights", ]); +export const onPluginLoadCallback = Symbol("onPluginLoadCallback"); +export const onPluginsLoadCompleteCallback = Symbol( + "onPluginsLoadCompleteCallback", +); + +export const LOADED_PLUGINS = new Set(); +export const BROKEN_PLUGINS = new Map(); + export default async function loadPlugins(loadOnlyTheme = false) { const plugins = await fsOperation(PLUGIN_DIR).lsDir(); const results = []; const failedPlugins = []; - const loadedPlugins = new Set(); if (plugins.length > 0) { toast(strings["loading plugins"]); @@ -42,21 +49,29 @@ export default async function loadPlugins(loadOnlyTheme = false) { // Only load theme plugins matching current theme pluginsToLoad = plugins.filter((pluginDir) => { const pluginId = Url.basename(pluginDir.url); - return isThemePlugin(pluginId) && !loadedPlugins.has(pluginId); + // Skip already loaded and plugins that were previously marked broken + return ( + isThemePlugin(pluginId) && + !LOADED_PLUGINS.has(pluginId) && + !BROKEN_PLUGINS.has(pluginId) + ); }); } else { // Load non-theme plugins that aren't loaded yet and are enabled pluginsToLoad = plugins.filter((pluginDir) => { const pluginId = Url.basename(pluginDir.url); + // Skip theme plugins, already loaded, disabled or previously marked broken return ( !isThemePlugin(pluginId) && - !loadedPlugins.has(pluginId) && - enabledMap[pluginId] !== true + !LOADED_PLUGINS.has(pluginId) && + enabledMap[pluginId] !== true && + !BROKEN_PLUGINS.has(pluginId) ); }); } // Load plugins concurrently + const LOAD_TIMEOUT = 15000; // ms per plugin const loadPromises = pluginsToLoad.map(async (pluginDir) => { const pluginId = Url.basename(pluginDir.url); @@ -73,11 +88,33 @@ export default async function loadPlugins(loadOnlyTheme = false) { } try { - await loadPlugin(pluginId); - loadedPlugins.add(pluginId); + // ensure loadPlugin doesn't hang: timeout wrapper + await Promise.race([ + loadPlugin(pluginId), + new Promise((_, rej) => + setTimeout(() => rej(new Error("Plugin load timeout")), LOAD_TIMEOUT), + ), + ]); + LOADED_PLUGINS.add(pluginId); + + acode[onPluginLoadCallback](pluginId); + results.push(true); + // clear broken mark if present + if (BROKEN_PLUGINS.has(pluginId)) { + BROKEN_PLUGINS.delete(pluginId); + } } catch (error) { console.error(`Error loading plugin ${pluginId}:`, error); + // mark plugin as broken to avoid repeated attempts until user intervenes + try { + BROKEN_PLUGINS.set(pluginId, { + error: String(error.message || error), + timestamp: Date.now(), + }); + } catch (e) { + console.warn("Failed to mark plugin as broken:", e); + } failedPlugins.push(pluginId); results.push(false); } @@ -85,6 +122,8 @@ export default async function loadPlugins(loadOnlyTheme = false) { await Promise.allSettled(loadPromises); + acode[onPluginsLoadCompleteCallback](); + if (failedPlugins.length > 0) { setTimeout(() => { cleanupFailedPlugins(failedPlugins).catch((error) => { diff --git a/src/pages/sponsors/sponsors.js b/src/pages/sponsors/sponsors.js index b9e12f55c..c93db5919 100644 --- a/src/pages/sponsors/sponsors.js +++ b/src/pages/sponsors/sponsors.js @@ -207,7 +207,7 @@ function SponsorCard({ name, image, website, tier, tagline }) { function handleLinkClick(e) { const target = e.target.closest(".sponsor-card"); if (!target) return; - const { website } = target.dataset; + let { website } = target.dataset; if (!website) return; if (!website.startsWith("http")) { website = "http://" + website; diff --git a/src/sidebarApps/searchInFiles/index.js b/src/sidebarApps/searchInFiles/index.js index 5fc682911..c62ebe78f 100644 --- a/src/sidebarApps/searchInFiles/index.js +++ b/src/sidebarApps/searchInFiles/index.js @@ -529,7 +529,7 @@ function terminateWorker(initializeNewWorkers = true) { * @returns {Worker} A new Worker object that runs the code in 'searchInFilesWorker.build.js'. */ function getWorker() { - return new Worker(new URL("./worker.js", import.meta.url)); + return new Worker("./build/searchInFilesWorker.js"); } /** diff --git a/src/utils/keyboardEvent.js b/src/utils/keyboardEvent.js index 3eabdd098..0682dcdd3 100644 --- a/src/utils/keyboardEvent.js +++ b/src/utils/keyboardEvent.js @@ -37,7 +37,7 @@ const keys = { 46: "Delete", }; -const initKeyboardEventType = (function (event) { +let initKeyboardEventType = (function (event) { try { event.initKeyboardEvent( "keyup", // in DOMString typeArg diff --git a/utils/esbuild.js b/utils/esbuild.js new file mode 100644 index 000000000..ba6dde657 --- /dev/null +++ b/utils/esbuild.js @@ -0,0 +1,172 @@ +#!/usr/bin/env node +const path = require("node:path"); +const fs = require("node:fs/promises"); +const babel = require("@babel/core"); +const esbuild = require("esbuild"); +const sass = require("sass"); +const postcss = require("postcss"); +const tagLoader = require("html-tag-js/jsx/tag-loader"); + +const postcssConfig = require("../postcss.config.js"); + +const args = process.argv.slice(2); +const modeArgIndex = args.indexOf("--mode"); +const mode = + modeArgIndex > -1 && args[modeArgIndex + 1] + ? args[modeArgIndex + 1] + : "development"; +const isProd = mode === "production"; +const watch = args.includes("--watch"); + +const root = path.resolve(__dirname, ".."); +const outdir = path.join(root, "www", "build"); +const target = ["es5"]; + +async function ensureCleanOutdir() { + await fs.rm(outdir, { recursive: true, force: true }); + await fs.mkdir(outdir, { recursive: true }); +} + +async function processCssFile(filePath) { + const isSass = /\.(sa|sc)ss$/.test(filePath); + let css; + + if (isSass) { + const result = sass.compile(filePath, { + style: isProd ? "compressed" : "expanded", + loadPaths: [ + path.dirname(filePath), + path.join(root, "src"), + path.join(root, "node_modules"), + ], + }); + css = result.css; + } else { + css = await fs.readFile(filePath, "utf8"); + } + + const postcssPlugins = postcssConfig.plugins || []; + const processed = await postcss(postcssPlugins).process(css, { + from: filePath, + map: false, + }); + + return processed.css; +} + +const babelPlugin = { + name: "babel-transform", + setup(build) { + build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => { + const source = await fs.readFile(args.path, "utf8"); + const result = await babel.transformAsync(source, { + filename: args.path, + configFile: path.join(root, ".babelrc"), + babelrc: false, + sourceType: "unambiguous", + caller: { + name: "esbuild", + supportsStaticESM: true, + supportsDynamicImport: true, + }, + }); + const transformed = result && result.code ? result.code : source; + const contents = tagLoader(transformed); + return { contents, loader: "js" }; + }); + }, +}; + +const cssPlugin = { + name: "css-modules-as-text", + setup(build) { + build.onLoad({ filter: /\.(sa|sc|c)ss$/ }, async (args) => { + const isModule = /\.m\.(sa|sc|c)ss$/.test(args.path); + const contents = await processCssFile(args.path); + return { + contents, + loader: isModule ? "text" : "css", + }; + }); + }, +}; + +const nodeFallbackPlugin = { + name: "node-fallbacks", + setup(build) { + const emptyNamespace = "empty-module"; + + build.onResolve({ filter: /^path$/ }, () => ({ + path: require.resolve("path-browserify"), + })); + + build.onResolve({ filter: /^crypto$/ }, () => ({ + path: "crypto", + namespace: emptyNamespace, + })); + + build.onLoad({ filter: /.*/, namespace: emptyNamespace }, () => ({ + contents: "export default {};", + loader: "js", + })); + }, +}; + +async function run() { + await ensureCleanOutdir(); + + const buildOptions = { + absWorkingDir: root, + entryPoints: { + main: "./src/main.js", + console: "./src/lib/console.js", + searchInFilesWorker: "./src/sidebarApps/searchInFiles/worker.js", + }, + outdir, + entryNames: "[name]", + chunkNames: "[name].chunk", + assetNames: "[name][ext]", + publicPath: "/build/", + bundle: true, + format: "iife", + platform: "browser", + target, + minify: isProd, + define: { + "process.env.NODE_ENV": JSON.stringify(mode), + }, + nodePaths: [path.join(root, "src")], + loader: { + ".hbs": "text", + ".md": "text", + ".png": "file", + ".svg": "file", + ".jpg": "file", + ".jpeg": "file", + ".ico": "file", + ".ttf": "file", + ".woff2": "file", + ".webp": "file", + ".eot": "file", + ".woff": "file", + ".webm": "file", + ".mp4": "file", + ".wav": "file", + }, + plugins: [babelPlugin, cssPlugin, nodeFallbackPlugin], + }; + + if (watch) { + const ctx = await esbuild.context(buildOptions); + await ctx.watch(); + console.log("esbuild is watching for changes..."); + return; + } + + await esbuild.build(buildOptions); +} + +run().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/utils/scripts/build.sh b/utils/scripts/build.sh index 7b7e73b8f..b6b4433fd 100644 --- a/utils/scripts/build.sh +++ b/utils/scripts/build.sh @@ -80,7 +80,7 @@ RED='' NC='' script1="node ./utils/config.js $mode $app" -script2="webpack --progress --mode $webpackmode " +script2="node ./utils/esbuild.js --mode $webpackmode" # script3="node ./utils/loadStyles.js" echo "type : $packageType" diff --git a/utils/scripts/start.sh b/utils/scripts/start.sh index 9418aa48a..832ae6353 100644 --- a/utils/scripts/start.sh +++ b/utils/scripts/start.sh @@ -30,7 +30,7 @@ fi RED='' NC='' script1="node ./utils/config.js $mode $app" -script2="webpack --progress --mode $webpackmode " +script2="node ./utils/esbuild.js --mode $webpackmode" # script3="node ./utils/loadStyles.js" script4="cordova run $platform $cordovamode" eval " @@ -42,4 +42,4 @@ $script2&& # $script3; echo \"${RED}$script4${NC}\"; $script4 -" \ No newline at end of file +"