diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index ea94015..7f3552f 100644 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -10,19 +10,15 @@ set -e dist="dist" -# 清除先前构建 -if [ -d "$dist" ]; then - rm -rf "$dist" -fi -mkdir $dist - # 获取数据 pnpm run data:info pnpm run data:chart -# 复制兼容性网页 -mkdir $dist/dist -mv $dist/*.json $dist/xpi $dist/dist -f +# 复制兼容性文件 +if [ ! -d "$dist/dist" ]; then + mkdir $dist/dist +fi +cp $dist/*.json $dist/dist cp .github/scripts/index.html $dist cp .github/scripts/_redirects $dist touch $dist/.nojekyll diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 23dce55..4206184 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,14 @@ jobs: - name: Install deps run: pnpm install + - name: Cache dist + uses: actions/cache@v3 + env: + cache-name: cache-dist + with: + path: dist + key: ${{ env.cache-name }}-${{ hashFiles('dist/plugins.json') }} + - name: Build env: GITHUB_TOKEN: ${{ secrets.PAT_GITHUB_TOKEN_NORTHWORD_GHOST }} diff --git a/eslint.config.js b/eslint.config.js index f4705e3..3b614a0 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,9 +1,3 @@ import antfu from '@antfu/eslint-config' -export default antfu({ - javascript: { - overrides: { - 'no-console': 'off', - }, - }, -}) +export default antfu() diff --git a/package.json b/package.json index cd7a862..9bc096a 100644 --- a/package.json +++ b/package.json @@ -31,23 +31,23 @@ "@antfu/eslint-config": "^2.26.0", "@highcharts/dashboards": "2.3.0", "@types/adm-zip": "0.5.5", + "@types/fs-extra": "^11.0.4", "@types/node": "20.16.2", - "@types/xml2js": "0.4.14", - "@vitalets/google-translate-api": "9.2.0", "adm-zip": "0.5.16", + "consola": "^3.2.3", "cross-env": "7.0.3", + "es-toolkit": "^1.17.0", "eslint": "9.9.1", "franc-min": "6.2.0", - "google-translate-api-x": "10.7.1", + "fs-extra": "^11.2.0", + "globby": "^14.0.2", "highcharts": "11.4.8", "husky": "9.1.5", "jsonc": "2.0.0", "lint-staged": "15.2.9", - "npm-run-all2": "6.2.2", "octokit": "4.0.2", "tsx": "4.19.0", - "typescript": "5.5.4", - "xml2js": "0.6.2" + "typescript": "5.5.4" }, "lint-staged": { "*.*": "eslint --fix" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 688a273..b0765af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,30 +17,36 @@ importers: '@types/adm-zip': specifier: 0.5.5 version: 0.5.5 + '@types/fs-extra': + specifier: ^11.0.4 + version: 11.0.4 '@types/node': specifier: 20.16.2 version: 20.16.2 - '@types/xml2js': - specifier: 0.4.14 - version: 0.4.14 - '@vitalets/google-translate-api': - specifier: 9.2.0 - version: 9.2.0 adm-zip: specifier: 0.5.16 version: 0.5.16 + consola: + specifier: ^3.2.3 + version: 3.2.3 cross-env: specifier: 7.0.3 version: 7.0.3 + es-toolkit: + specifier: ^1.17.0 + version: 1.17.0 eslint: specifier: 9.9.1 version: 9.9.1 franc-min: specifier: 6.2.0 version: 6.2.0 - google-translate-api-x: - specifier: 10.7.1 - version: 10.7.1 + fs-extra: + specifier: ^11.2.0 + version: 11.2.0 + globby: + specifier: ^14.0.2 + version: 14.0.2 highcharts: specifier: 11.4.8 version: 11.4.8 @@ -53,9 +59,6 @@ importers: lint-staged: specifier: 15.2.9 version: 15.2.9 - npm-run-all2: - specifier: 6.2.2 - version: 6.2.2 octokit: specifier: 4.0.2 version: 4.0.2 @@ -65,9 +68,6 @@ importers: typescript: specifier: 5.5.4 version: 5.5.4 - xml2js: - specifier: 0.6.2 - version: 0.6.2 packages: @@ -479,6 +479,10 @@ packages: resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@sindresorhus/merge-streams@2.3.0': + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + '@stylistic/eslint-plugin@2.7.2': resolution: {integrity: sha512-3DVLU5HEuk2pQoBmXJlzvrxbKNpu2mJ0SRqz5O/CJjyNCr12ZiPcYMEtuArTyPOk5i7bsAU44nywh1rGfe3gKQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -500,12 +504,15 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - '@types/http-errors@1.8.2': - resolution: {integrity: sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==} + '@types/fs-extra@11.0.4': + resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/jsonfile@6.1.4': + resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + '@types/mdast@3.0.15': resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} @@ -518,9 +525,6 @@ packages: '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - '@types/xml2js@0.4.14': - resolution: {integrity: sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==} - '@typescript-eslint/eslint-plugin@8.3.0': resolution: {integrity: sha512-FLAIn63G5KH+adZosDYiutqkOkYEx0nvcwNNfJAf+c7Ae/H35qWwTYvPZUKFj5AS+WfHG/WJJfWnDnyNUlp8UA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -582,10 +586,6 @@ packages: resolution: {integrity: sha512-RmZwrTbQ9QveF15m/Cl28n0LXD6ea2CjkhH5rQ55ewz3H24w+AMCJHPVYaZ8/0HoG8Z3cLLFFycRXxeO2tz9FA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitalets/google-translate-api@9.2.0': - resolution: {integrity: sha512-w98IPWGuexlGmh8Y19AxF6cgWT0U5JLevVNDKEuFpTWtBC9z3YtDWKTDxF3nPP1k9bWicuB1V7I7YfHoZiDScw==} - engines: {node: '>=14'} - '@vitest/eslint-plugin@1.1.0': resolution: {integrity: sha512-Ur80Y27Wbw8gFHJ3cv6vypcjXmrx6QHfw+q435h6Q2L+tf+h4Xf5pJTCL4YU/Jps9EVeggQxS85OcUZU7sdXRw==} peerDependencies: @@ -776,6 +776,10 @@ packages: confbox@0.1.7: resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + consola@3.2.3: + resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + engines: {node: ^14.18.0 || >=16.10.0} + core-js-compat@3.38.1: resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==} @@ -813,10 +817,6 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -848,6 +848,9 @@ packages: es-module-lexer@1.5.4: resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + es-toolkit@1.17.0: + resolution: {integrity: sha512-aJvpNxK7d+I+Rt9tmdwzelxTe4EwtxX1Kv0xv6ZTRWJBpMCxe0vxTLLW4STz6pHYjtyTTrEkonNXLaBsYHg2Yw==} + esbuild@0.23.1: resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} engines: {node: '>=18'} @@ -1108,6 +1111,10 @@ packages: franc-min@6.2.0: resolution: {integrity: sha512-1uDIEUSlUZgvJa2AKYR/dmJC66v/PvGQ9mWfI9nOr/kPpMFyvswK0gPXOwpYJYiYD008PpHLkGfG58SPjQJFxw==} + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1154,9 +1161,9 @@ packages: resolution: {integrity: sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==} engines: {node: '>=18'} - google-translate-api-x@10.7.1: - resolution: {integrity: sha512-OdZDS6jRWzn1woOk62aOKQ5OyVaJSA+eyc6CktOWxo36IWfstOjwG/dkvnGl3Z2Sbpmk1A+jc2WwrBiRjqaY2A==} - engines: {node: '>=14.0.0'} + globby@14.0.2: + resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} + engines: {node: '>=18'} graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -1182,10 +1189,6 @@ packages: hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} @@ -1211,9 +1214,6 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - is-alphabetical@1.0.4: resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} @@ -1305,10 +1305,6 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-parse-even-better-errors@3.0.2: - resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -1323,6 +1319,9 @@ packages: resolution: {integrity: sha512-B281bLCT2TRMQa+AQUQY5AGcqSOXBOKaYGP4wDzoA/+QswUfN8sODektbPEs9Baq7LGKun5jQbNFpzwGuVYKhw==} engines: {node: '>=8'} + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1380,10 +1379,6 @@ packages: mdast-util-to-string@2.0.0: resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} - memorystream@0.3.1: - resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} - engines: {node: '>= 0.10.0'} - merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -1455,30 +1450,12 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - npm-normalize-package-bin@3.0.1: - resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-run-all2@6.2.2: - resolution: {integrity: sha512-Q+alQAGIW7ZhKcxLt8GcSi3h3ryheD6xnmXahkMRVM5LYmajcUrSITm8h+OPC9RYWMV2GR0Q1ntTUCfxaNoOJw==} - engines: {node: ^14.18.0 || ^16.13.0 || >=18.0.0, npm: '>= 8'} - hasBin: true - npm-run-path@5.3.0: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1563,6 +1540,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-type@5.0.0: + resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} + engines: {node: '>=12'} + pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -1608,10 +1589,6 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - read-package-json-fast@3.0.2: - resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -1665,9 +1642,6 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - sax@1.4.1: - resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} - scslre@0.3.0: resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} engines: {node: ^14.0.0 || >=16.0.0} @@ -1681,9 +1655,6 @@ packages: engines: {node: '>=10'} hasBin: true - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1692,9 +1663,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -1702,6 +1670,10 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + slashes@3.0.12: resolution: {integrity: sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==} @@ -1735,10 +1707,6 @@ packages: stable-hash@0.0.4: resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -1813,17 +1781,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - toml-eslint-parser@0.10.0: resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - trigram-utils@2.0.1: resolution: {integrity: sha512-nfWIXHEaB+HdyslAfMxSqWKDdmqY9I32jS7GnqpdWQnLH89r6A5sdk3fDVYqGAZ0CrT8ovAFSAo6HRiWcWNIGQ==} @@ -1868,6 +1829,10 @@ packages: undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + unist-util-stringify-position@2.0.3: resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} @@ -1877,6 +1842,10 @@ packages: universal-user-agent@7.0.2: resolution: {integrity: sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + update-browserslist-db@1.1.0: resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} hasBin: true @@ -1898,12 +1867,6 @@ packages: peerDependencies: eslint: '>=6.0.0' - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1925,14 +1888,6 @@ packages: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} - xml2js@0.6.2: - resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} - engines: {node: '>=4.0.0'} - - xmlbuilder@11.0.1: - resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} - engines: {node: '>=4.0'} - y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -2346,6 +2301,8 @@ snapshots: '@pkgr/core@0.1.1': {} + '@sindresorhus/merge-streams@2.3.0': {} + '@stylistic/eslint-plugin@2.7.2(eslint@9.9.1)(typescript@5.5.4)': dependencies: '@types/eslint': 9.6.1 @@ -2377,10 +2334,17 @@ snapshots: '@types/estree@1.0.5': {} - '@types/http-errors@1.8.2': {} + '@types/fs-extra@11.0.4': + dependencies: + '@types/jsonfile': 6.1.4 + '@types/node': 20.16.2 '@types/json-schema@7.0.15': {} + '@types/jsonfile@6.1.4': + dependencies: + '@types/node': 20.16.2 + '@types/mdast@3.0.15': dependencies: '@types/unist': 2.0.11 @@ -2393,10 +2357,6 @@ snapshots: '@types/unist@2.0.11': {} - '@types/xml2js@0.4.14': - dependencies: - '@types/node': 20.16.2 - '@typescript-eslint/eslint-plugin@8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.9.1)(typescript@5.5.4))(eslint@9.9.1)(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.11.0 @@ -2480,14 +2440,6 @@ snapshots: '@typescript-eslint/types': 8.3.0 eslint-visitor-keys: 3.4.3 - '@vitalets/google-translate-api@9.2.0': - dependencies: - '@types/http-errors': 1.8.2 - http-errors: 2.0.0 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - '@vitest/eslint-plugin@1.1.0(@typescript-eslint/utils@8.3.0(eslint@9.9.1)(typescript@5.5.4))(eslint@9.9.1)(typescript@5.5.4)': dependencies: eslint: 9.9.1 @@ -2662,6 +2614,8 @@ snapshots: confbox@0.1.7: {} + consola@3.2.3: {} + core-js-compat@3.38.1: dependencies: browserslist: 4.23.3 @@ -2688,8 +2642,6 @@ snapshots: deep-is@0.1.4: {} - depd@2.0.0: {} - doctrine@3.0.0: dependencies: esutils: 2.0.3 @@ -2715,6 +2667,8 @@ snapshots: es-module-lexer@1.5.4: {} + es-toolkit@1.17.0: {} + esbuild@0.23.1: optionalDependencies: '@esbuild/aix-ppc64': 0.23.1 @@ -3093,6 +3047,12 @@ snapshots: dependencies: trigram-utils: 2.0.1 + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + fsevents@2.3.3: optional: true @@ -3128,7 +3088,14 @@ snapshots: globals@15.9.0: {} - google-translate-api-x@10.7.1: {} + globby@14.0.2: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.2 + ignore: 5.3.2 + path-type: 5.0.0 + slash: 5.1.0 + unicorn-magic: 0.1.0 graceful-fs@4.2.11: {} @@ -3146,14 +3113,6 @@ snapshots: hosted-git-info@2.8.9: {} - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - human-signals@5.0.0: {} husky@9.1.5: {} @@ -3169,8 +3128,6 @@ snapshots: indent-string@4.0.0: {} - inherits@2.0.4: {} - is-alphabetical@1.0.4: {} is-alphanumerical@1.0.4: @@ -3234,8 +3191,6 @@ snapshots: json-parse-even-better-errors@2.3.1: {} - json-parse-even-better-errors@3.0.2: {} - json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -3256,6 +3211,12 @@ snapshots: strip-bom: 4.0.0 strip-json-comments: 3.1.1 + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -3336,8 +3297,6 @@ snapshots: mdast-util-to-string@2.0.0: {} - memorystream@0.3.1: {} - merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -3402,10 +3361,6 @@ snapshots: natural-compare@1.4.0: {} - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - node-releases@2.0.18: {} normalize-package-data@2.5.0: @@ -3415,18 +3370,6 @@ snapshots: semver: 5.7.2 validate-npm-package-license: 3.0.4 - npm-normalize-package-bin@3.0.1: {} - - npm-run-all2@6.2.2: - dependencies: - ansi-styles: 6.2.1 - cross-spawn: 7.0.3 - memorystream: 0.3.1 - minimatch: 9.0.5 - pidtree: 0.6.0 - read-package-json-fast: 3.0.2 - shell-quote: 1.8.1 - npm-run-path@5.3.0: dependencies: path-key: 4.0.0 @@ -3525,6 +3468,8 @@ snapshots: path-parse@1.0.7: {} + path-type@5.0.0: {} + pathe@1.1.2: {} picocolors@1.0.1: {} @@ -3560,11 +3505,6 @@ snapshots: queue-microtask@1.2.3: {} - read-package-json-fast@3.0.2: - dependencies: - json-parse-even-better-errors: 3.0.2 - npm-normalize-package-bin: 3.0.1 - read-pkg-up@7.0.1: dependencies: find-up: 4.1.0 @@ -3618,8 +3558,6 @@ snapshots: dependencies: queue-microtask: 1.2.3 - sax@1.4.1: {} - scslre@0.3.0: dependencies: '@eslint-community/regexpp': 4.11.0 @@ -3630,20 +3568,18 @@ snapshots: semver@7.6.3: {} - setprototypeof@1.2.0: {} - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} - shell-quote@1.8.1: {} - signal-exit@4.1.0: {} sisteransi@1.0.5: {} + slash@5.1.0: {} + slashes@3.0.12: {} slice-ansi@5.0.0: @@ -3679,8 +3615,6 @@ snapshots: stable-hash@0.0.4: {} - statuses@2.0.1: {} - string-argv@0.3.2: {} string-width@4.2.3: @@ -3744,14 +3678,10 @@ snapshots: dependencies: is-number: 7.0.0 - toidentifier@1.0.1: {} - toml-eslint-parser@0.10.0: dependencies: eslint-visitor-keys: 3.4.3 - tr46@0.0.3: {} - trigram-utils@2.0.1: dependencies: collapse-white-space: 2.1.0 @@ -3786,6 +3716,8 @@ snapshots: undici-types@6.19.8: {} + unicorn-magic@0.1.0: {} + unist-util-stringify-position@2.0.3: dependencies: '@types/unist': 2.0.11 @@ -3794,6 +3726,8 @@ snapshots: universal-user-agent@7.0.2: {} + universalify@2.0.1: {} + update-browserslist-db@1.1.0(browserslist@4.23.3): dependencies: browserslist: 4.23.3 @@ -3824,13 +3758,6 @@ snapshots: transitivePeerDependencies: - supports-color - webidl-conversions@3.0.1: {} - - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - which@2.0.2: dependencies: isexe: 2.0.0 @@ -3851,13 +3778,6 @@ snapshots: xml-name-validator@4.0.0: {} - xml2js@0.6.2: - dependencies: - sax: 1.4.1 - xmlbuilder: 11.0.1 - - xmlbuilder@11.0.1: {} - y18n@5.0.8: {} yaml-eslint-parser@1.2.3: diff --git a/src/deprecated.ts b/src/deprecated.ts index 7725c32..5c7447f 100644 --- a/src/deprecated.ts +++ b/src/deprecated.ts @@ -1,4 +1,4 @@ -import type { PluginInfoBase } from '../types/index.js' +import type { PluginInfoBase } from './types.js' /** * 废弃的插件列表 diff --git a/src/get-plugins-info.ts b/src/get-plugins-info.ts deleted file mode 100644 index 0b8a781..0000000 --- a/src/get-plugins-info.ts +++ /dev/null @@ -1,175 +0,0 @@ -import fs from 'node:fs' -import { Buffer } from 'node:buffer' -import { franc } from 'franc-min' -// import translate from "google-translate-api-x"; -import AdmZip from 'adm-zip' -import { jsonc } from 'jsonc' -import type { PluginInfo, PluginInfoBase } from '../types/index.js' -import { writeFile } from './utils.js' -import { dist, octokit } from './index.js' - -const XpiIds: number[] = [] -// todo: 缓存XPI;将backend的dist单独保存,分离前后端工作流,对PR启用工作流和预览 - -export function fetchPlugins(plugins: PluginInfoBase[]) { - return Promise.all(plugins.map(fetchPlugin)) -} - -async function fetchPlugin(pluginBase: PluginInfoBase): Promise { - const plugin = pluginBase as PluginInfo - const repoParts = pluginBase.repo.split('/') - const owner = repoParts[0] - const repo = repoParts[1] - - // 仓库信息:插件简介 - await octokit.rest.repos.get({ owner, repo }).then((resp) => { - let desc = '' - if (resp.data.description) { - if (franc(resp.data.description) === 'cmn') { - desc = resp.data.description - } - else { - desc = resp.data.description - // 翻译 - // console.log("需要翻译"); - // await translate(resp.data.description, { to: "zh-CN" }) - // .then((res) => { - // console.log(res.text); - // desc = res.text; - // }) - // .catch((e) => { - // console.log(e); - // desc = resp.data.description; - // }); - } - } - - plugin.description = desc - plugin.stars = resp.data.stargazers_count - plugin.watchers = resp.data.subscribers_count - }) - - // 作者信息 - await octokit.rest.users.getByUsername({ username: owner }).then( - resp => - (plugin.author = { - name: resp.data.name || owner, - url: resp.data.blog || resp.data.html_url, - avatar: resp.data.avatar_url, - }), - ) - - // 发行版 - for (const release of plugin.releases) { - async function getRelease() { - if (release.tagName === 'latest') { - const resp = await octokit.rest.repos.getLatestRelease({ owner, repo }) - return resp.data - } - else if (release.tagName === 'pre') { - const resp = await octokit.rest.repos.listReleases({ owner, repo }) - return resp.data.filter(item => item.prerelease)[0] - } - else { - const resp = await octokit.rest.repos.getReleaseByTag({ - owner, - repo, - tag: release.tagName, - }) - return resp.data - } - } - - await getRelease().then(async (resp) => { - release.tagName = resp.tag_name - - const asset = resp.assets - .filter(asset => asset.content_type === 'application/x-xpinstall') - .sort((a, b) => { - const dateA = new Date(a.updated_at) - const dateB = new Date(b.updated_at) - // 使用时间戳进行比较 - return dateB.getTime() - dateA.getTime() - })[0] - - if (!asset) { - console.log(` ${plugin.name} ${release.tagName} 不存在 XPI`) - // throw new Error(`${plugin.name} ${release.tagName} 不存在 XPI`); - return - } - - if (!fs.existsSync(`${dist}/xpi/${asset.id}.xpi`)) { - await octokit.rest.repos - .getReleaseAsset({ - owner, - repo, - asset_id: asset.id, - headers: { - Accept: 'application/octet-stream', - }, - }) - .then((resp) => { - writeFile( - `${dist}/xpi/${asset.id}.xpi`, - Buffer.from(resp.data as unknown as ArrayBuffer), - ) - }) - } - - release.assetId = asset.id - XpiIds.push(asset.id) - release.releaseDate = asset.updated_at - release.downloadCount = asset.download_count - release.xpiDownloadUrl = { - github: asset.browser_download_url, - gitee: `https://gitee.com/northword/zotero-plugins/raw/gh-pages/dist/xpi/${release.assetId}.xpi`, - ghProxy: `https://ghproxy.com/?q=${encodeURI(asset.browser_download_url)}`, - jsdeliver: `https://cdn.jsdelivr.net/gh/northword/zotero-plugins@gh-pages/dist/xpi/${release.assetId}.xpi`, - kgithub: asset.browser_download_url.replace( - 'github.com', - 'kkgithub.com', - ), - } - }) - - // if (plugin.releases.length > 1 && release.targetZoteroVersion === "6") - // continue; - - // 拆 XPI 包 - const zip = new AdmZip(`${dist}/xpi/${release.assetId}.xpi`) - const zipEntries = zip.getEntries() - const zipEntryNames = zipEntries.map(zipEntrie => zipEntrie.entryName) - if (!zipEntryNames.includes('manifest.json')) - throw new Error('Bad XPI file') - - const fileData = zip - .getEntry('manifest.json')! - .getData() - .toString('utf8') - const manifestData = jsonc.parse(fileData) - - plugin.name - = release.targetZoteroVersion === '7' - ? manifestData.name - : plugin.name ?? manifestData.name ?? repo - if (plugin.name === '__MSG_name__') { - const locale = ['zh-CN', 'zh', manifestData.default_locale] - .map(e => `_locales/${e}/messages.json`) - .find(e => !!zipEntryNames.includes(e)) - if (locale) { - const message = zip.getEntry(locale)!.getData().toString('utf8') - const messageData = jsonc.parse(message) - plugin.name = messageData.name.message - } - else { - plugin.name = repo - } - } - release.id = manifestData.applications.zotero.id - release.xpiVersion = manifestData.version || '' - plugin.description = plugin.description || manifestData.description || '' - // todo: 适配多语言,当值为 `__MSG_description__` 是前往 i18n 目录获取 - } - console.info(`${plugin.name} 处理完成`) - return plugin -} diff --git a/src/charts.ts b/src/handler/charts-data.ts similarity index 98% rename from src/charts.ts rename to src/handler/charts-data.ts index 57f473e..cce4036 100644 --- a/src/charts.ts +++ b/src/handler/charts-data.ts @@ -1,4 +1,3 @@ -import { createRequire } from 'node:module' import { env } from 'node:process' import type { Board } from '@highcharts/dashboards' import type { @@ -11,14 +10,14 @@ import type { SeriesSplineOptions, SeriesWordcloudOptions, } from 'highcharts' +import consola from 'consola' +import fs from 'fs-extra' +import type { PluginInfo } from '../types.js' +import { octokit } from '../utils/index.js' -import type { PluginInfo } from '../types/index.js' -import { octokit } from './index.js' - -const require = createRequire(import.meta.url) const pluginMap: { [name: string]: PluginMapInfo } = env.NODE_ENV === 'development' - ? require('../dist/charts-debug.json') + ? fs.readJSONSync('./dist/charts-debug.json') : {} interface PluginMapInfo { @@ -75,7 +74,7 @@ async function fetchInfo(plugin: PluginInfo) { pluginMap[plugin.name].totalDownloads = pluginMap[ plugin.name ].releases!.reduce((sum, release) => sum + release.downloadCount, 0) - console.info(plugin.name, 'done') + consola.info(plugin.name, 'done') } async function getIssues(owner: string, repo: string) { diff --git a/src/handler/file-cache.ts b/src/handler/file-cache.ts new file mode 100644 index 0000000..fc225aa --- /dev/null +++ b/src/handler/file-cache.ts @@ -0,0 +1,28 @@ +import process from 'node:process' +import { globbySync } from 'globby' +import { consola } from 'consola' +import fs from 'fs-extra' +import { difference } from 'es-toolkit' +import type { PluginInfo } from '../types.js' + +export function cleanAssets() { + const currentAssets = globbySync('dist/xpi/*.xpi') + .map(p => p.replace('dist/xpi/', '').replace('.xpi', '')) + + const plugins = fs.readJsonSync('dist/plugins.json') as PluginInfo[] + const keepAssets = plugins + .map(p => p.releases) + .flat() + .map(r => String(r.assetId)) + + const diff = difference(currentAssets, keepAssets) + + if (process.env.NODE_ENV !== 'development') { + diff.forEach((id) => { + fs.rmSync(`dist/xpi/${id}.xpi`) + }) + } + else { + consola.info('Clean assets skiped.', diff) + } +} diff --git a/src/handler/plugins-data.ts b/src/handler/plugins-data.ts new file mode 100644 index 0000000..b931f9f --- /dev/null +++ b/src/handler/plugins-data.ts @@ -0,0 +1,138 @@ +import fs from 'fs-extra' +import AdmZip from 'adm-zip' +import { jsonc } from 'jsonc' +import { consola } from 'consola' +import type { PluginInfo, PluginInfoBase, ReleaseInfo, ReleaseInfoBase } from '../types.js' +import { getRelease, getReleaseAssetBuffer, octokit, translateString } from '../utils/index.js' +import { dist } from '../index.js' + +export function fetchPlugins(plugins: PluginInfoBase[]) { + return Promise.all(plugins.map(fetchPlugin)) +} + +async function fetchPlugin(pluginBase: PluginInfoBase): Promise { + const plugin = { ...pluginBase } as PluginInfo + const [owner, repo] = pluginBase.repo.split('/') + + // 仓库信息:插件简介 + await octokit.rest.repos.get({ owner, repo }).then((resp) => { + plugin.description = translateString(resp.data.description) + plugin.stars = resp.data.stargazers_count + plugin.watchers = resp.data.subscribers_count + }) + + // 作者信息 + await octokit.rest.users.getByUsername({ username: owner }).then(resp => + plugin.author = { + name: resp.data.name || owner, + url: resp.data.blog || resp.data.html_url, + avatar: resp.data.avatar_url, + }, + ) + + // 发行版 + for (const release of plugin.releases) { + const releaseDist = await parseRelease(owner, repo, release) + Object.assign(release, releaseDist) + + const xpiData = parseXPI(`${dist}/xpi/${release.assetId}.xpi`) + plugin.name ??= xpiData.name + plugin.description ??= xpiData.description + release.id = xpiData.id + release.xpiVersion = xpiData.xpiVersion + } + consola.info(`${owner}/${repo} done`) + return plugin +} + +async function parseRelease(owner: string, repo: string, releaseBase: ReleaseInfoBase): Promise { + const release = { ...releaseBase } as ReleaseInfo + + const resp = await getRelease(owner, repo, releaseBase.tagName) + release.tagName = resp.tag_name + + const asset = resp.assets + .filter(asset => asset.content_type === 'application/x-xpinstall') + .sort((a, b) => + new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(), + )[0] + + if (!asset) { + throw new Error(` ${owner}/${repo} ${release.tagName} 不存在 XPI`) + } + + if (!fs.existsSync(`${dist}/xpi/${asset.id}.xpi`)) { + const buffer = await getReleaseAssetBuffer(owner, repo, asset.id) + fs.outputFileSync(`${dist}/xpi/${asset.id}.xpi`, buffer) + consola.log(` Write ${asset.name} ${asset.id}`) + } + + release.assetId = asset.id + release.releaseDate = asset.updated_at + release.downloadCount = asset.download_count + release.xpiDownloadUrl = { + github: asset.browser_download_url, + gitee: `https://gitee.com/northword/zotero-plugins/raw/gh-pages/xpi/${release.assetId}.xpi`, + ghProxy: `https://ghproxy.com/?q=${encodeURI(asset.browser_download_url)}`, + jsdeliver: `https://cdn.jsdelivr.net/gh/northword/zotero-plugins@gh-pages/xpi/${release.assetId}.xpi`, + kgithub: asset.browser_download_url.replace( + 'github.com', + 'kkgithub.com', + ), + } + return release +} + +function parseXPI(filePath: string): { + name: string + description: string + id: string + xpiVersion: string +} { + if (!fs.existsSync(filePath)) + throw new Error(`${filePath} do not exist.`) + + const zip = new AdmZip(filePath) + const zipEntries = zip.getEntries() + const zipEntryNames = zipEntries.map(zipEntrie => zipEntrie.entryName) + if (!zipEntryNames.includes('manifest.json')) + throw new Error('Bad XPI file') + + const fileData = zip + .getEntry('manifest.json')! + .getData() + .toString('utf8') + const manifestData = jsonc.parse(fileData) + + function getManifestValueLocal(key: string) { + const locale = ['zh-CN', 'zh', manifestData.default_locale] + .map(e => `_locales/${e}/messages.json`) + .find(e => !!zipEntryNames.includes(e)) + if (locale) { + const message = zip.getEntry(locale)!.getData().toString('utf8') + const messageData = jsonc.parse(message) + return messageData[key].message + } + } + + // Plugin Name + let name = manifestData.name + if (name === '__MSG_name__') { + name = getManifestValueLocal('name') + } + + // todo: 适配多语言,当值为 `__MSG_description__` 是前往 i18n 目录获取 + let description = manifestData.description + if (description === '__MSG_description') + description = getManifestValueLocal('description') + + const id = manifestData.applications.zotero.id + const xpiVersion = manifestData.version + + return { + name, + description, + id, + xpiVersion, + } +} diff --git a/src/index.ts b/src/index.ts index c8e4ebf..c1455ea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,86 +1,64 @@ import { argv, env, exit } from 'node:process' -import { Octokit } from 'octokit' -import getChartOptions from './charts.js' -import { fetchPlugins } from './get-plugins-info.js' +import fs from 'fs-extra' +import { consola } from 'consola' +import getChartOptions from './handler/charts-data.js' +import { fetchPlugins } from './handler/plugins-data.js' import plugins from './plugins.js' -import { readFile, writeFile } from './utils.js' -// import { renderMarkdown } from "./render-markdown"; +import { checkRateLimit } from './utils/index.js' +import { cleanAssets } from './handler/file-cache.js' if (!env.GITHUB_TOKEN) throw new Error('GITHUB_TOKEN 未设置') export const dist = './dist' -export const octokit = new Octokit({ - auth: env.GITHUB_TOKEN, -}) -export function args() { - return <'fetchPlugins' | 'md' | 'charts'>argv.slice(2)[0] +async function handlePluginsData() { + const pluginsInfoDist = await fetchPlugins(plugins) + fs.outputJSONSync(`${dist}/plugins-debug.json`, pluginsInfoDist, { spaces: 2 }) + fs.outputJSONSync(`${dist}/plugins.json`, pluginsInfoDist) + + cleanAssets() + + const shields = { + lastUpdate: new Date().toLocaleString('zh-CN'), + } + fs.outputJSONSync(`${dist}/shields.json`, shields) +} + +async function handleChartsData() { + const pluginsInfoDist = fs.readJSONSync(`${dist}/plugins.json`) + const chartOptions = await getChartOptions(pluginsInfoDist) + fs.outputJSONSync(`${dist}/charts.json`, chartOptions, { spaces: env.NODE_ENV === 'development' ? 2 : 0 }) } -async function main(mode: 'fetchPlugins' | 'charts' | string) { - const quotaStart = (await octokit.rest.rateLimit.get()).data.rate - console.log(quotaStart) +async function main() { + const mode: 'fetchPlugins' | 'charts' | string = argv.slice(2)[0] + const quotaStart = await checkRateLimit() if (quotaStart.remaining < 1500) { - console.log( - `TOKEN 余量不足, ${new Date(quotaStart.reset).toLocaleTimeString()}后重试`, - ) + consola.error(`TOKEN 余量不足, ${new Date(quotaStart.reset).toLocaleTimeString()}后重试`) exit(1) } - console.log('开始处理') - switch (mode) { - case 'fetchPlugins': - { - const pluginsInfoDist = await fetchPlugins(plugins) - if (!env.CI) { - writeFile( - `${dist}/plugins-debug.json`, - JSON.stringify(plugins, null, 2), - ) - } - writeFile(`${dist}/plugins.json`, JSON.stringify(pluginsInfoDist)) + consola.log('开始处理') + fs.ensureDir(dist) - const shields = { - lastUpdate: new Date().toLocaleString('zh-CN'), - } - writeFile(`${dist}/shields.json`, JSON.stringify(shields, null, 2)) - } - break - case 'md': { - // console.log("处理 Markdown"); - // const pluginsInfoDist = readFile(`${dist}/plugins.json`); - // const markdownContent = await renderMarkdown(pluginsInfoDist); - // writeFile(`${dist}/plugins.md`, markdownContent); - break - } - case 'charts': - { - const pluginsInfoDist = readFile(`${dist}/plugins.json`) - const chartOptions = await getChartOptions(pluginsInfoDist) - writeFile( - `${dist}/charts.json`, - JSON.stringify( - chartOptions, - null, - env.NODE_ENV === 'development' ? 2 : 0, - ), - ) - } - break - default: - console.log('No arg, exit.') - break + if (mode === 'fetchPlugins') { + await handlePluginsData() + } + else if (mode === 'charts') { + await handleChartsData() + } + else { + consola.error('No arg, exit.') } - console.log('完成') - const quotaEnd = (await octokit.rest.rateLimit.get()).data.rate - console.log(quotaEnd) - console.log(`共计请求 ${quotaStart.remaining - quotaEnd.remaining} 次`) + consola.log('完成') + const quotaEnd = await checkRateLimit() + consola.log(`共计请求 ${quotaStart.remaining - quotaEnd.remaining} 次`) } -main(args()).catch((err) => { - console.log(err) +main().catch((err) => { + consola.error(err) exit(1) }) diff --git a/src/plugins.ts b/src/plugins.ts index 9070f69..d3789ae 100644 --- a/src/plugins.ts +++ b/src/plugins.ts @@ -1,5 +1,5 @@ import { env } from 'node:process' -import type { PluginInfoBase } from '../types/index.js' +import type { PluginInfoBase } from './types.js' /** * 插件列表 diff --git a/types/index.d.ts b/src/types.ts similarity index 100% rename from types/index.d.ts rename to src/types.ts diff --git a/src/utils.ts b/src/utils.ts deleted file mode 100644 index 20dd9ab..0000000 --- a/src/utils.ts +++ /dev/null @@ -1,63 +0,0 @@ -import fs from 'node:fs' -import path from 'node:path' - -// 读写 -export function readFile(filePath: string) { - // 读取文件 - const data = fs.readFileSync(filePath, { encoding: 'utf8' }) - return JSON.parse(data) -} - -export function writeFile( - filePath: string, - data: string | NodeJS.ArrayBufferView, -) { - // 获取目标目录路径 - const dirPath = path.dirname(filePath) - - // 检查目录是否存在,如果不存在则创建目录 - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }) - } - - // 写入文件 - fs.writeFileSync(filePath, data, 'utf8') - console.log(` ${filePath} 写入完成`) -} - -export function copyFileSync(source: string, target: string) { - let targetFile = target - - // If target is a directory, a new file with the same name will be created - if (fs.existsSync(target)) { - if (fs.lstatSync(target).isDirectory()) { - targetFile = path.join(target, path.basename(source)) - } - } - - fs.writeFileSync(targetFile, fs.readFileSync(source)) -} - -export function copyFolderRecursiveSync(source: string, target: string) { - let files = [] - - // Check if folder needs to be created or integrated - const targetFolder = path.join(target) - if (!fs.existsSync(targetFolder)) { - fs.mkdirSync(targetFolder) - } - - // Copy - if (fs.lstatSync(source).isDirectory()) { - files = fs.readdirSync(source) - files.forEach((file) => { - const curSource = path.join(source, file) - if (fs.lstatSync(curSource).isDirectory()) { - copyFolderRecursiveSync(curSource, targetFolder) - } - else { - copyFileSync(curSource, targetFolder) - } - }) - } -} diff --git a/src/utils/github.ts b/src/utils/github.ts new file mode 100644 index 0000000..9eb30c1 --- /dev/null +++ b/src/utils/github.ts @@ -0,0 +1,44 @@ +import process from 'node:process' +import { Buffer } from 'node:buffer' +import { Octokit } from 'octokit' + +export const octokit = new Octokit({ + auth: process.env.GITHUB_TOKEN, +}) + +export async function checkRateLimit() { + const { data } = await octokit.rest.rateLimit.get() + return data.rate +} + +export async function getRelease(owner: string, repo: string, tagName: string) { + if (tagName === 'latest') { + const resp = await octokit.rest.repos.getLatestRelease({ owner, repo }) + return resp.data + } + else if (tagName === 'pre') { + const resp = await octokit.rest.repos.listReleases({ owner, repo }) + return resp.data.filter(item => item.prerelease && item.tag_name !== 'release')[0] + } + else { + const resp = await octokit.rest.repos.getReleaseByTag({ + owner, + repo, + tag: tagName, + }) + return resp.data + } +} + +export async function getReleaseAssetBuffer(owner: string, repo: string, asset_id: number) { + const resp = await octokit.rest.repos + .getReleaseAsset({ + owner, + repo, + asset_id, + headers: { + Accept: 'application/octet-stream', + }, + }) + return Buffer.from(resp.data as unknown as ArrayBuffer) +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..4a29b0c --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from './github.js' +export * from './string.js' diff --git a/src/utils/string.ts b/src/utils/string.ts new file mode 100644 index 0000000..565ee2f --- /dev/null +++ b/src/utils/string.ts @@ -0,0 +1,26 @@ +import { franc } from 'franc-min' + +export * from './github.js' + +export function translateString(string: string | null) { + if (!string) + return '' + + if (franc(string) === 'cmn') { + return string + } + else { + return string + // 翻译 + // consola.log("需要翻译"); + // await translate(resp.data.description, { to: "zh-CN" }) + // .then((res) => { + // consola.log(res.text); + // desc = res.text; + // }) + // .catch((e) => { + // consola.log(e); + // desc = resp.data.description; + // }); + } +}