This commit is contained in:
mrbunker
2022-10-03 14:40:29 +08:00
commit 6e271c3d6a
13 changed files with 1234 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>jop</title>
<!-- /index.html 必须存在, 否则 vite 无法注入 hmr 代码, plugin-monkey 以及其他插件 也无法注入相关辅助代码 -->
<!-- /index.html must exist, if not, vite will not inject hmr code, plugin-monkey and others will not inject theirs related auxiliary code -->
</head>
</html>

15
package.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "userjs-jop",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build"
},
"devDependencies": {
"typescript": "^4.8.4",
"vite": "^3.1.4",
"vite-plugin-monkey": "^2.5.1"
}
}

537
pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,537 @@
lockfileVersion: 5.4
specifiers:
typescript: ^4.8.4
vite: ^3.1.4
vite-plugin-monkey: ^2.5.1
devDependencies:
typescript: 4.8.4
vite: 3.1.4
vite-plugin-monkey: 2.5.1_vite@3.1.4
packages:
/@esbuild/android-arm/0.15.10:
resolution: {integrity: sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64/0.15.10:
resolution: {integrity: sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/address/1.2.1:
resolution: {integrity: sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==}
engines: {node: '>= 10.0.0'}
dev: true
/cross-spawn/7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
dependencies:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
dev: true
/debug/2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.0.0
dev: true
/define-lazy-prop/2.0.0:
resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
engines: {node: '>=8'}
dev: true
/detect-port/1.3.0:
resolution: {integrity: sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==}
engines: {node: '>= 4.2.1'}
hasBin: true
dependencies:
address: 1.2.1
debug: 2.6.9
transitivePeerDependencies:
- supports-color
dev: true
/dom-serializer/2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
entities: 4.4.0
dev: true
/domelementtype/2.3.0:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
dev: true
/domhandler/5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
dependencies:
domelementtype: 2.3.0
dev: true
/domutils/3.0.1:
resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==}
dependencies:
dom-serializer: 2.0.0
domelementtype: 2.3.0
domhandler: 5.0.3
dev: true
/entities/4.4.0:
resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==}
engines: {node: '>=0.12'}
dev: true
/esbuild-android-64/0.15.10:
resolution: {integrity: sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-android-arm64/0.15.10:
resolution: {integrity: sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-64/0.15.10:
resolution: {integrity: sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-arm64/0.15.10:
resolution: {integrity: sha512-M1t5+Kj4IgSbYmunf2BB6EKLkWUq+XlqaFRiGOk8bmBapu9bCDrxjf4kUnWn59Dka3I27EiuHBKd1rSO4osLFQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-64/0.15.10:
resolution: {integrity: sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-arm64/0.15.10:
resolution: {integrity: sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-32/0.15.10:
resolution: {integrity: sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-64/0.15.10:
resolution: {integrity: sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm/0.15.10:
resolution: {integrity: sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm64/0.15.10:
resolution: {integrity: sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-mips64le/0.15.10:
resolution: {integrity: sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-ppc64le/0.15.10:
resolution: {integrity: sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-riscv64/0.15.10:
resolution: {integrity: sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-s390x/0.15.10:
resolution: {integrity: sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-netbsd-64/0.15.10:
resolution: {integrity: sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-openbsd-64/0.15.10:
resolution: {integrity: sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-sunos-64/0.15.10:
resolution: {integrity: sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-32/0.15.10:
resolution: {integrity: sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-64/0.15.10:
resolution: {integrity: sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-arm64/0.15.10:
resolution: {integrity: sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild/0.15.10:
resolution: {integrity: sha512-N7wBhfJ/E5fzn/SpNgX+oW2RLRjwaL8Y0ezqNqhjD6w0H2p0rDuEz2FKZqpqLnO8DCaWumKe8dsC/ljvVSSxng==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.15.10
'@esbuild/linux-loong64': 0.15.10
esbuild-android-64: 0.15.10
esbuild-android-arm64: 0.15.10
esbuild-darwin-64: 0.15.10
esbuild-darwin-arm64: 0.15.10
esbuild-freebsd-64: 0.15.10
esbuild-freebsd-arm64: 0.15.10
esbuild-linux-32: 0.15.10
esbuild-linux-64: 0.15.10
esbuild-linux-arm: 0.15.10
esbuild-linux-arm64: 0.15.10
esbuild-linux-mips64le: 0.15.10
esbuild-linux-ppc64le: 0.15.10
esbuild-linux-riscv64: 0.15.10
esbuild-linux-s390x: 0.15.10
esbuild-netbsd-64: 0.15.10
esbuild-openbsd-64: 0.15.10
esbuild-sunos-64: 0.15.10
esbuild-windows-32: 0.15.10
esbuild-windows-64: 0.15.10
esbuild-windows-arm64: 0.15.10
dev: true
/fsevents/2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
/function-bind/1.1.1:
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
dev: true
/has/1.0.3:
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
engines: {node: '>= 0.4.0'}
dependencies:
function-bind: 1.1.1
dev: true
/htmlparser2/8.0.1:
resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
domutils: 3.0.1
entities: 4.4.0
dev: true
/is-core-module/2.10.0:
resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==}
dependencies:
has: 1.0.3
dev: true
/is-docker/2.2.1:
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
engines: {node: '>=8'}
hasBin: true
dev: true
/is-wsl/2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
dependencies:
is-docker: 2.2.1
dev: true
/isexe/2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
/mrmime/1.0.1:
resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==}
engines: {node: '>=10'}
dev: true
/ms/2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
dev: true
/nanoid/3.3.4:
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
dev: true
/node-fetch/2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
dependencies:
whatwg-url: 5.0.0
dev: true
/open/8.4.0:
resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==}
engines: {node: '>=12'}
dependencies:
define-lazy-prop: 2.0.0
is-docker: 2.2.1
is-wsl: 2.2.0
dev: true
/path-key/3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
dev: true
/path-parse/1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true
/picocolors/1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
dev: true
/postcss/8.4.17:
resolution: {integrity: sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.4
picocolors: 1.0.0
source-map-js: 1.0.2
dev: true
/resolve/1.22.1:
resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
hasBin: true
dependencies:
is-core-module: 2.10.0
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
dev: true
/rollup/2.78.1:
resolution: {integrity: sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==}
engines: {node: '>=10.0.0'}
hasBin: true
optionalDependencies:
fsevents: 2.3.2
dev: true
/shebang-command/2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
dependencies:
shebang-regex: 3.0.0
dev: true
/shebang-regex/3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
dev: true
/source-map-js/1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
dev: true
/supports-preserve-symlinks-flag/1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
dev: true
/tr46/0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
dev: true
/typescript/4.8.4:
resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/vite-plugin-monkey/2.5.1_vite@3.1.4:
resolution: {integrity: sha512-18eKt5++5eRF7rn2nXm7GjHECxSvBh36OiYnYR2vpRYIw+052fEAg7XvAAGCY1H3jb+1ox6ZOkmMvTPNubRaQg==}
engines: {node: ^14.18 || >= 16, pnpm: '>=6'}
peerDependencies:
vite: ^2.0.0 || ^3.0.0
dependencies:
cross-spawn: 7.0.3
detect-port: 1.3.0
htmlparser2: 8.0.1
mrmime: 1.0.1
node-fetch: 2.6.7
open: 8.4.0
picocolors: 1.0.0
vite: 3.1.4
transitivePeerDependencies:
- encoding
- supports-color
dev: true
/vite/3.1.4:
resolution: {integrity: sha512-JoQI08aBjY9lycL7jcEq4p9o1xUjq5aRvdH4KWaXtkSx7e7RpAh9D3IjzDWRD4Fg44LS3oDAIOG/Kq1L+82psA==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
less: '*'
sass: '*'
stylus: '*'
terser: ^5.4.0
peerDependenciesMeta:
less:
optional: true
sass:
optional: true
stylus:
optional: true
terser:
optional: true
dependencies:
esbuild: 0.15.10
postcss: 8.4.17
resolve: 1.22.1
rollup: 2.78.1
optionalDependencies:
fsevents: 2.3.2
dev: true
/webidl-conversions/3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
dev: true
/whatwg-url/5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
dev: true
/which/2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
hasBin: true
dependencies:
isexe: 2.0.0
dev: true

48
src/createNode.ts Normal file
View File

@@ -0,0 +1,48 @@
export function createPanel() {
const parentNode = document.querySelector("body");
const panelNode = document.createElement("div");
parentNode && parentNode.appendChild(panelNode);
panelNode.classList.add("jop-panel");
panelNode.addEventListener("click", () => {
const class1 = panelNode.classList[1];
if (class1 === undefined) {
panelNode.classList.add("jop-panel_open");
} else {
panelNode.classList.remove("jop-panel_open");
}
});
return panelNode;
}
export function createButtonNode(panelNode: HTMLDivElement, siteName: string, siteUrl: string) {
const button = document.createElement("a");
button.setAttribute("target", "_blank");
button.classList.add("jop-button");
button.innerHTML = siteName;
/** 先给上默认链接 */
button.href = siteUrl;
button.addEventListener("click", (e) => e.stopPropagation());
panelNode.appendChild(button);
/** 加载动画 */
const loadAni = document.createElement("span");
loadAni.classList.add("jop-button-loading");
button.appendChild(loadAni);
/** 设置按钮状态样式 */ const setButtonStatus = (
targetLink: string,
color: "red" | "green",
hasLeakage = false,
hasSubtitle = false,
) => {
button.href = targetLink;
button.classList.add(`jop-button_${color}`);
loadAni.classList.remove("jop-button-loading");
hasLeakage && button.classList.add("jop-button_leakage");
hasSubtitle && button.classList.add("jop-button_subtitle");
};
return { button, setButtonStatus };
}

42
src/index.ts Normal file
View File

@@ -0,0 +1,42 @@
import { createButtonNode, createPanel } from "./createNode";
import { matchList } from "./matchList";
import { siteList } from "./siteList";
import { xhr } from "./xhr";
import type { Cms } from "./matchList";
import "./style.css";
function getCode(cmsName: Cms["name"]) {
if (cmsName === "javdb") {
return document.querySelector<HTMLElement>(`[data-clipboard-text]`)?.dataset
.clipboardText as string;
} else if (cmsName === "javbus") {
return document
.querySelector<HTMLElement>(`span[style="color:#CC0000;"]`)
?.innerText.replace("复制", "") as string;
} else {
return document.querySelector<HTMLElement>(`#video_id td.text`)?.innerHTML as string;
}
}
export async function main(HOSTNAME: string) {
/** 当前 macth 站点对象 */
const cms = matchList.find((item) => item.hostname === HOSTNAME) as Cms;
const CODE = getCode(cms.name);
const panel = createPanel();
/** 禁用部分 */
const envSiteList = siteList.filter((item) => item.disable !== cms.name);
envSiteList.forEach(async (siteItem) => {
const siteUrl = siteItem.url.replace("{{code}}", CODE);
const { setButtonStatus } = createButtonNode(panel, siteItem.name, siteUrl);
const { isSuccess, hasLeakage, hasSubtitle, targetLink } = await xhr(siteItem, siteUrl, CODE);
setButtonStatus(targetLink, isSuccess ? "green" : "red", hasLeakage, hasSubtitle);
});
}
main(window.location.hostname);

26
src/matchList.ts Normal file
View File

@@ -0,0 +1,26 @@
export type Cms = {
name: string;
enable: boolean;
hostname: string;
panelParentQueryStr: string;
};
export const matchList: Cms[] = [
{
name: "javdb",
enable: true,
hostname: "javdb.com",
panelParentQueryStr: "video-meta-panel",
},
{
name: "javbus",
enable: true,
hostname: "www.javbus.com",
panelParentQueryStr: "div.row.movie",
},
{
name: "javlib",
enable: true,
hostname: "www.javlibrary.com",
panelParentQueryStr: "#video_jacket_info",
},
];

200
src/siteList.ts Normal file
View File

@@ -0,0 +1,200 @@
export interface DomQuery_parser {
/** 部分网站搜索页结果第一个是广告,所以要加一个 index */
listIndex?: number;
/** code 是用空格分割的 */
spaceCode?: boolean;
linkQuery: string;
titleQuery: string;
/** 确定返回颜色,暂未启用 */
certainColor?: string;
}
export interface DomQuery_get {
/** 收录视频,但是未提供在线播放资源 */
videoQuery?: string;
subQuery?: string;
leakQuery?: string;
}
export interface SiteItem_get {
name: string;
disable?: string;
hostname: string;
url: string;
fetcher: "get";
domQuery: DomQuery_get;
codeFormater?: (arg0: string) => string;
method?: Function;
}
export interface SiteItem_parser {
name: string;
disable?: string;
hostname: string;
url: string;
fetcher: "parser";
domQuery: DomQuery_parser;
codeFormater?: (arg0: string) => string;
method?: Function;
}
export type SiteItem = SiteItem_get | SiteItem_parser;
const print = (name: string) => {
console.log(name);
};
/** 网站列表 */
export const siteList: SiteItem[] = [
{
name: "Jable",
hostname: "jable.tv",
url: "https://jable.tv/videos/{{code}}/",
fetcher: "get",
domQuery: { subQuery: ".header-right>h6" },
method: print,
},
{
name: "MISSAV",
hostname: "missav.com",
url: "https://missav.com/{{code}}/",
fetcher: "get",
domQuery: {
/** 标签区的第一个一般是字幕标签 */
subQuery: '.space-y-2 a.text-nord13[href="https://missav.com/chinese-subtitle"]',
/** videoPage 有个跳转按钮 */
leakQuery: ".order-first div.rounded-md a[href]:last-child",
},
method: print,
},
{
name: "NETFLAV",
hostname: "netflav.com",
url: "https://netflav.com/search?type=title&keyword={{code}}",
fetcher: "parser",
domQuery: { linkQuery: ".grid_cell>a", titleQuery: ".grid_cell>a>.grid_title" },
method: print,
},
{
name: "Avgle",
hostname: "avgle.com",
url: "https://avgle.com/search/videos?search_query={{code}}&search_type=videos",
fetcher: "parser",
domQuery: {
linkQuery: ".container>.row .row .well>a[href]",
titleQuery: ".container>.row .row .well .video-title",
},
method: print,
},
{
name: "JAVHHH",
hostname: "javhhh.com",
url: "https://javhhh.com/v/?wd={{code}}",
fetcher: "parser",
domQuery: {
linkQuery: ".typelist>.i-container>a[href]",
titleQuery: ".typelist>.i-container>a[href]",
},
method: print,
},
{
name: "BestJP",
hostname: "bestjavporn.com",
url: "https://www3.bestjavporn.com/search/{{code}}",
fetcher: "parser",
domQuery: { linkQuery: "article.thumb-block>a", titleQuery: "article.thumb-block>a" },
method: print,
},
{
name: "JAVMENU",
hostname: "javmenu.com",
url: "https://javmenu.com/{{code}}",
fetcher: "get",
domQuery: {
videoQuery: "a.nav-link[aria-controls='pills-0']",
},
method: print,
},
{
name: "Jav.Guru",
hostname: "jav.guru",
url: "https://jav.guru/?s={{code}}",
fetcher: "parser",
domQuery: { linkQuery: ".imgg>a[href]", titleQuery: ".inside-article>.grid1 a[title]" },
method: print,
},
{
name: "JAVMOST",
hostname: "javmost.cx",
url: "https://javmost.cx/search/{{code}}/",
fetcher: "parser",
domQuery: {
linkQuery: "#content .card a#MyImage",
titleQuery: "#content .card-block .card-title",
},
method: print,
},
{
name: "JAVFC2",
hostname: "javfc2.net",
url: "https://javfc2.net/?s={{code}}",
fetcher: "parser",
domQuery: {
linkQuery: "article.loop-video>a[href]",
titleQuery: "article.loop-video .entry-header",
},
method: print,
},
{
name: "baihuse",
hostname: "paipancon.com",
url: "https://paipancon.com/search/{{code}}",
fetcher: "parser",
domQuery: {
linkQuery: "div.col>div.card>a[href]",
// 然而这个不是 title是图片这个站居然 title 里不包含 code反而图片包含
titleQuery: "div.card img.card-img-top",
},
method: print,
},
{
name: "GGJAV",
hostname: "ggjav.com",
url: "https://ggjav.com/main/search?string={{code}}",
fetcher: "parser",
domQuery: {
listIndex: 1,
spaceCode: true,
titleQuery: "div.columns.large-3.medium-6.small-12.item.float-left>div.item_title>a.gray_a",
linkQuery: "div.columns.large-3.medium-6.small-12.item.float-left>div.item_title>a.gray_a",
},
method: print,
},
{
name: "AV01",
hostname: "av01.tv",
url: "https://www.av01.tv/search/videos?search_query={{code}}",
fetcher: "parser",
domQuery: { linkQuery: "div[id].well-sm>a", titleQuery: ".video-views>.pull-left" },
method: print,
},
{
name: "JavBus",
disable: "javbus",
hostname: "javbus.com",
url: "https://javbus.com/{{code}}",
fetcher: "get",
domQuery: {},
method: print,
},
// {
// name: "JavDB",
// disable:"javdb",
// hostname: "javbus.com",
// url: "https://javbus.com/{{code}}",
// fetcher: "get",
// domQuery: {},
// method: print,
// },
];

101
src/style.css Normal file
View File

@@ -0,0 +1,101 @@
.jop-panel {
box-sizing: border-box;
position: fixed;
top: 37.5%;
right: -305px;
width: 330px;
height: 400px;
padding: 35px 35px 35px 60px;
border-radius: 15px 0 0 15px;
background-color: white;
box-shadow: rgb(0 0 0 / 26%) 3px 0px 8px;
transition: right 200ms ease-in-out;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 10px 0;
}
.jop-panel_open {
right: 0;
}
.jop-button {
display: inline-flex;
-webkit-box-align: center;
align-items: center;
-webkit-box-pack: center;
justify-content: center;
position: relative;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
background-color: transparent;
outline: 0px;
cursor: pointer;
user-select: none;
vertical-align: middle;
appearance: none;
text-decoration: none;
font-family: Roboto, Helvetica, Arial, sans-serif;
font-weight: 500;
font-size: 14px;
line-height: 1.75;
letter-spacing: 0.02857em;
min-width: 110px;
padding: 5px 15px;
border-radius: 8px;
transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
border-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
color: #606266;
border: 1px solid #dcdfe6;
}
.jop-button:visited {
color: #606266;
}
.jop-button:hover {
text-decoration: none;
color: #409eff;
border: 1px solid #c6e2ff;
background-color: #ecf5ff;
}
.jop-button-loading {
position: absolute;
left: 5px;
width: 8px;
height: 8px;
border: 1px solid #dcdfe6;
border-top-color: transparent;
border-radius: 100%;
animation: btnLoading infinite 0.75s linear;
}
.jop-button_green {
color: #67c23a !important;
background-color: #f0f9eb;
border: 1px solid #b3e19d;
}
.jop-button_green:hover {
color: white !important;
background-color: #67c23a;
}
.jop-button_red {
color: #f56c6c !important;
background-color: #fef0f0;
border: 1px solid #fab6b6;
}
.jop-button_red:hover {
color: white !important;
background-color: #f56c6c;
}
@keyframes btnLoading {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}

35
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1,35 @@
/// <reference types="vite/client" />
/**
* alias of vite-plugin-monkey/dist/client
*/
declare module '$' {
export * from 'vite-plugin-monkey/dist/client';
}
// if set mountGmApi=true
// type MonkeyWindow = import('vite-plugin-monkey/dist/client').MonkeyWindow;
// declare const unsafeWindow: MonkeyWindow['unsafeWindow'];
// declare const GM_addStyle: MonkeyWindow['GM_addStyle'];
// declare const GM_addElement: MonkeyWindow['GM_addElement'];
// declare const GM_deleteValue: MonkeyWindow['GM_deleteValue'];
// declare const GM_listValues: MonkeyWindow['GM_listValues'];
// declare const GM_addValueChangeListener: MonkeyWindow['GM_addValueChangeListener'];
// declare const GM_removeValueChangeListener: MonkeyWindow['GM_removeValueChangeListener'];
// declare const GM_setValue: MonkeyWindow['GM_setValue'];
// declare const GM_getValue: MonkeyWindow['GM_getValue'];
// declare const GM_log: MonkeyWindow['GM_log'];
// declare const GM_getResourceText: MonkeyWindow['GM_getResourceText'];
// declare const GM_getResourceURL: MonkeyWindow['GM_getResourceURL'];
// declare const GM_registerMenuCommand: MonkeyWindow['GM_registerMenuCommand'];
// declare const GM_unregisterMenuCommand: MonkeyWindow['GM_unregisterMenuCommand'];
// declare const GM_openInTab: MonkeyWindow['GM_openInTab'];
// declare const GM_xmlhttpRequest: MonkeyWindow['GM_xmlhttpRequest'];
// declare const GM_download: MonkeyWindow['GM_download'];
// declare const GM_getTab: MonkeyWindow['GM_getTab'];
// declare const GM_saveTab: MonkeyWindow['GM_saveTab'];
// declare const GM_getTabs: MonkeyWindow['GM_getTabs'];
// declare const GM_notification: MonkeyWindow['GM_notification'];
// declare const GM_setClipboard: MonkeyWindow['GM_setClipboard'];
// declare const GM_info: MonkeyWindow['GM_info'];
// declare const GM_cookie: MonkeyWindow['GM_cookie'];

129
src/xhr.ts Normal file
View File

@@ -0,0 +1,129 @@
import { GM_xmlhttpRequest } from "$";
import type { DomQuery_get, DomQuery_parser, SiteItem } from "./siteList";
/** 针对视频播放页进行解析,寻找字幕等信息 */
function videoPageParser(responseText: string, { subQuery, leakQuery, videoQuery }: DomQuery_get) {
const doc = new DOMParser().parseFromString(responseText, "text/html");
const subNode = subQuery ? doc.querySelector<HTMLElement>(subQuery) : "";
const subNodeText = subNode ? subNode.innerHTML : "";
const leakNode = leakQuery ? doc.querySelector<HTMLElement>(leakQuery) : null;
// 部分网站收录视频,但是未提供播放资源,所以需要使用 videoQuery 进一步检测是否存在在线播放
/** videoQuery 为 undefine 时,不需要查找 video */
const videoNode = videoQuery ? doc.querySelector<HTMLElement>(videoQuery) : true;
return {
isSuccess: !!videoNode,
hasSubtitle: subNodeText.includes("字幕") || subNodeText.includes("subtitle"),
hasLeakage: !!leakNode,
};
}
/** 针对 fetcher==="parser" 时的搜索结果页进行解析,寻找是否存在视频资源。
* linkQuery、titleQuery 都是必须,
* linkQuery 有结果且 titleQuery 结果包含 code返回 isSuccess。
* 再检查下 title 中是否含有字幕信息等
*/
function serachPageParser(
responseText: string,
{ linkQuery, titleQuery, listIndex = 0, spaceCode = false }: DomQuery_parser,
siteHostName: string,
CODE: string,
) {
const doc = new DOMParser().parseFromString(responseText, "text/html");
const linkNode = linkQuery ? doc.querySelectorAll<HTMLAnchorElement>(linkQuery)[listIndex] : null;
const titleNode = titleQuery ? doc.querySelectorAll(titleQuery)[listIndex] : null;
const titleNodeText = titleNode ? titleNode?.outerHTML : "";
function query() {
/** 空格版本的 code */
const envCodeWithSpace = spaceCode ? CODE.replace("-", " ") : CODE;
const condition =
linkNode &&
titleNode &&
(titleNodeText.includes(envCodeWithSpace) || titleNodeText.includes(CODE));
if (condition) {
return {
isSuccess: true,
targetLink: linkNode.href.replace(linkNode.hostname, siteHostName),
hasLeakage: titleNodeText.includes("无码") || titleNodeText.includes("Uncensored"),
hasSubtitle: titleNodeText.includes("字幕") || titleNodeText.includes("subtitle"),
};
} else {
return { targetLink: "", isSuccess: false };
}
}
return query();
}
export async function xhr(siteItem: SiteItem, siteUrl: string, CODE: string) {
const xhrPromise: Promise<{
isSuccess: boolean;
targetLink: string;
name: string;
hasSubtitle?: boolean;
hasLeakage?: boolean;
msg: string;
}> = new Promise((resolve) => {
GM_xmlhttpRequest({
method: "GET",
url: siteUrl,
onload: (response) => {
if (siteItem.fetcher === "get") {
// 直接 get 网页,且 get 结果为 404大概是对应网站没有资源
if (response.status === 404) {
resolve({
isSuccess: false,
targetLink: siteUrl,
name: siteItem.name,
msg: "应该是没有资源",
});
}
// 直接 get 网页,成功,需要进一步解析 videoPage获取字幕等信息
else {
const { hasSubtitle, hasLeakage, isSuccess } = videoPageParser(
response.responseText,
siteItem.domQuery,
);
resolve({
isSuccess,
targetLink: siteUrl,
name: siteItem.name,
hasSubtitle,
hasLeakage,
msg: "[get],存在资源",
});
}
}
// 需要解析 searchPage
else if (siteItem.fetcher === "parser") {
const { targetLink, isSuccess, hasLeakage, hasSubtitle } = serachPageParser(
response.responseText,
siteItem.domQuery,
siteItem.hostname,
CODE,
);
resolve({
name: siteItem.name,
isSuccess,
targetLink: isSuccess ? targetLink : siteUrl,
hasSubtitle,
hasLeakage,
msg: "[parser]存在资源",
});
}
},
onerror: (error) => {
resolve({
isSuccess: false,
targetLink: siteUrl,
name: siteItem.name,
msg: error.error,
});
},
});
});
return xhrPromise;
}

20
tsconfig.json Normal file
View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ESNext", "DOM"],
"moduleResolution": "Node",
"strict": true,
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"skipLibCheck": true
},
"include": ["src"]
}

44
vite.config.ts Normal file
View File

@@ -0,0 +1,44 @@
import { defineConfig } from "vite";
import monkey, { MonkeyUserScript } from "vite-plugin-monkey";
const UserscriptConfig: MonkeyUserScript = {
author: "mission522",
version: "1.0.0",
license: "MIT",
name: "JAV 添加跳转在线观看 三合一",
match: ["*://*.javdb.com/*", "*://*.javbus.com/*", "*://*.javlibrary.com/*"],
include: /^https:\/\/(\w*\.)?javdb(\d)*\.com.*$/,
icon: "https://javdb.com/favicon-32x32.png",
namespace: "https://greasyfork.org/users/58790",
description:
"在 JavDB、JavBus、JavLibrart 网站的影片详情页添加跳转在线播放按钮,并在按钮上标注是否存在可播放资源,资源是否为「字幕」、「无码」等信息。",
connect: [
"jable.tv",
"missav.com",
"javhhh.com",
"netflav.com",
"avgle.com",
"bestjavporn.com",
"jav.guru",
"javmost.cx",
"hpjav.tv",
"av01.tv",
"javbus.com",
"javmenu.com",
"javfc2.net",
"paipancon.com",
"ggjav.com",
],
};
export default defineConfig({
plugins: [
monkey({
entry: "src/index.ts",
build: { fileName: "jop.user.js" },
userscript: {
...UserscriptConfig,
},
}),
],
});