├── config ├── empty.js ├── webpack.config.prod.js ├── metadata.js ├── webpack.config.dev.js └── webpack.config.base.js ├── docs ├── user_info_collection.md └── screenshot │ ├── iqiyi.png │ └── bilibili.png ├── src ├── lib │ ├── jquery.d.ts │ ├── axios-userscript-adapter.d.ts │ ├── vue.d.ts │ ├── callback.d.ts │ ├── responses.d.ts │ └── globals.d.ts ├── js │ ├── externals.js │ ├── vars.ts │ ├── index.js │ ├── website.ts │ ├── utils.ts │ └── App.vue ├── playerUrl │ └── index.js └── style │ └── less │ ├── bilibili.less │ └── iqiyi.less ├── renovate.json ├── postcss.config.js ├── .editorconfig ├── .gitignore ├── .gitattributes ├── tsconfig.json ├── readme.md ├── .github └── workflows │ └── ci.yaml └── package.json /config/empty.js: -------------------------------------------------------------------------------- 1 | window.TM_ENV = 'dev' 2 | -------------------------------------------------------------------------------- /docs/user_info_collection.md: -------------------------------------------------------------------------------- 1 | 现在除了查询用户当前观看动画在bgm.tv上的对应条目外没有收集任何数据 2 | -------------------------------------------------------------------------------- /src/lib/jquery.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'jquery' { 2 | // export = $; 3 | export default $ 4 | } 5 | -------------------------------------------------------------------------------- /docs/screenshot/iqiyi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trim21-archive/bgm-tv-auto-tracker/HEAD/docs/screenshot/iqiyi.png -------------------------------------------------------------------------------- /docs/screenshot/bilibili.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trim21-archive/bgm-tv-auto-tracker/HEAD/docs/screenshot/bilibili.png -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "schedule:monthly", 4 | "github>Trim21/renovate-config", 5 | "config:js-app" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | cssnano: { 4 | zindex: false, 5 | autoprefixer: false 6 | }, 7 | autoprefixer: {} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/axios-userscript-adapter.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'axios-userscript-adapter' { 2 | import { AxiosAdapter } from 'axios' 3 | const adapter: AxiosAdapter 4 | export default adapter 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | env 4 | __pycache__ 5 | tmp 6 | node_modules 7 | .pytest_cache 8 | htmlcov 9 | .coverage 10 | bangumi-data 11 | .env 12 | tmp.py 13 | dist/bgm-tv-auto-tracker.dev.user.js 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Ensure all relevant files use LF. 5 | *.css eol=lf 6 | *.scss eol=lf 7 | *.js eol=lf 8 | *.json eol=lf 9 | -------------------------------------------------------------------------------- /src/lib/vue.d.ts: -------------------------------------------------------------------------------- 1 | import { BgmApi } from '@/js/utils' 2 | import { AbstractWebsite } from '@/js/website' 3 | 4 | declare module 'vue/types/vue' { 5 | interface Vue { 6 | $website: AbstractWebsite, 7 | $bgmApi: BgmApi, 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/callback.d.ts: -------------------------------------------------------------------------------- 1 | declare class InitResult { 2 | episodeIndex: number | string 3 | episodeID: string 4 | bangumiID: string 5 | title: string 6 | episodeStartWith: number 7 | } 8 | 9 | declare interface EpisodeChangeCallback { 10 | (info: any): void; 11 | } 12 | -------------------------------------------------------------------------------- /src/js/externals.js: -------------------------------------------------------------------------------- 1 | /* 2 | * this js module is a wrapper that don't let ts-loader 3 | * call `module.var` with `module.default.var` in compiled file 4 | * But `axios.default` is `axios` itself, so no need to wrap axios. 5 | * */ 6 | import Vue from 'vue' 7 | import $ from 'jquery' 8 | import adapter from 'axios-userscript-adapter' 9 | 10 | export { Vue, $, adapter } 11 | -------------------------------------------------------------------------------- /config/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge') 2 | 3 | const UserScriptMetaDataPlugin = require('userscript-metadata-webpack-plugin') 4 | const metadata = require('./metadata') 5 | 6 | const webpackConfig = require('./webpack.config.base') 7 | const cfg = merge({}, webpackConfig, { 8 | output: { 9 | filename: 'bgm-tv-auto-tracker.prod.user.js' 10 | }, 11 | plugins: [ 12 | new UserScriptMetaDataPlugin({ 13 | metadata 14 | }) 15 | ] 16 | }) 17 | 18 | module.exports = cfg 19 | -------------------------------------------------------------------------------- /src/lib/responses.d.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios' 2 | 3 | declare interface QuerySubjectIDResponse extends AxiosResponse { 4 | data: { 5 | subject_id: number, 6 | } 7 | } 8 | 9 | declare interface SubjectResponse extends AxiosResponse { 10 | data: { 11 | rating: { 12 | score: number, 13 | }, 14 | name_cn: string, 15 | name: string, 16 | } 17 | } 18 | 19 | declare interface AuthResponse { 20 | access_token: string 21 | expires_in: number 22 | token_type: string 23 | scope?: string 24 | user_id: number 25 | refresh_token: string 26 | auth_time: string 27 | } 28 | 29 | declare interface BaseResponse extends AxiosResponse { 30 | data: T 31 | } 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./build/", 4 | "noImplicitAny": true, 5 | "strict": false, 6 | "types": [ 7 | "axios", 8 | "jquery", 9 | "vue" 10 | ], 11 | "sourceMap": true, 12 | "module": "commonjs", 13 | "target": "es5", 14 | "allowSyntheticDefaultImports": true, 15 | "allowJs": true, 16 | "lib": [ 17 | "dom", 18 | "es6" 19 | ], 20 | "baseUrl": ".", 21 | "paths": { 22 | "@/*": [ 23 | "./src/*" 24 | ] 25 | } 26 | }, 27 | "include": [ 28 | "src/**/*.ts", 29 | "src/**/*.vue", 30 | "tests/**/*.ts" 31 | ], 32 | "exclude": [ 33 | "node_modules", 34 | "**/*.spec.ts" 35 | ] 36 | 37 | } 38 | -------------------------------------------------------------------------------- /config/metadata.js: -------------------------------------------------------------------------------- 1 | const pkg = require('../package.json') 2 | 3 | module.exports = { 4 | name: 'Bgm.tv auto tracker', 5 | namespace: 'https://trim21.me/', 6 | version: pkg.version, 7 | author: pkg.author, 8 | source: pkg.repository.url, 9 | license: 'MIT', 10 | match: [ 11 | 'https://www.bilibili.com/bangumi/play/*', 12 | 'http*://www.iqiyi.com/*_*.html*', 13 | 'https://www.trim21.cn/bgm-tv-auto-tracker/api.v1/oauth_callback*', 14 | ], 15 | grant: [ 16 | 'GM_addStyle', 17 | 'GM_setValue', 18 | 'GM_getValue', 19 | 'GM_openInTab', 20 | 'GM_addStyle', 21 | 'GM_xmlhttpRequest', 22 | 'unsafeWindow' 23 | ], 24 | connect: [ 25 | '127.0.0.1', 26 | 'api.bgm.tv', 27 | 'www.trim21.cn', 28 | ], 29 | 'run-at': 'document-idle' 30 | } 31 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # bgm.tv auto tracker 2 | 3 | ## 介绍 4 | 5 | 在b站和爱奇艺看番剧的同时一键在[bgm.tv](https://bgm.tv/)上标记已经看过的集数. 6 | 7 | ## 使用 8 | 9 | 首先安装[Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) 10 | 11 | 然后安装此脚本 12 | 13 | https://greasyfork.org/zh-CN/scripts/369643-bgm-tv-auto-tracker 14 | 15 | 去[https://bgm.tv/oauth/authorize](https://www.trim21.cn/bgm-tv-auto-tracker/api.v1/auth) 16 | 进行授权, 授权后即可正常使用。 17 | 18 | 效果图 19 | 20 | ![bilibili](docs/screenshot/bilibili.png) 21 | ![iqiyi](docs/screenshot/iqiyi.png) 22 | 23 | ## 开发 24 | 25 | 现已支持bilibili和iqiyi 26 | 27 | 欢迎贡献代码 28 | 29 | api server见https://www.trim21.cn/ 30 | 31 | 用户脚本使用TypeScript和JavaScript 32 | 33 | ```bash 34 | npm i #安装依赖 35 | npm run dev # 检测文件变动,自动重新编译 36 | ``` 37 | 38 | 入口是`src/js/index.js` 39 | 40 | 使用`webpack`打包 41 | 42 | ## 已知问题 43 | 44 | 在同一集内重复点击`看到本集`会报错 这是api的设定 45 | 46 | 如果bgm的某个番没有对应条目(比如柯南的900+集),`看过本集会报错` 47 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '**' 7 | - '*' 8 | push: 9 | branches: 10 | - '*' 11 | - '**' 12 | - '!renovate/**' 13 | tags: 14 | - '.*' 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-16.04 19 | 20 | strategy: 21 | matrix: 22 | node: [ '12', '13' ] 23 | 24 | name: Node ${{ matrix.node }} 25 | 26 | steps: 27 | - uses: actions/checkout@v1 28 | 29 | - name: Setup node 30 | uses: actions/setup-node@v1 31 | with: 32 | node-version: ${{ matrix.node }} 33 | 34 | - uses: actions/cache@v1 35 | id: cache 36 | with: 37 | path: ./node_modules 38 | key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/package-lock.json') }} 39 | 40 | - name: Install Dependencies 41 | if: steps.cache.outputs.cache-hit != 'true' 42 | run: npm ci 43 | 44 | # - run: npm run test:unit 45 | - run: npm run lint 46 | 47 | - run: npm run build 48 | -------------------------------------------------------------------------------- /src/lib/globals.d.ts: -------------------------------------------------------------------------------- 1 | interface BiliWindow extends Window { 2 | __INITIAL_STATE__: { 3 | mediaInfo: { 4 | ssType: number 5 | id: number 6 | title: string 7 | }, 8 | epList: Array<{ 9 | index: string 10 | i: number 11 | title: string 12 | }> 13 | epInfo: { 14 | index: string 15 | ep_id: string 16 | } 17 | pubInfo: {} 18 | }, 19 | player: { 20 | getDuration (): number, 21 | getCurrentTime (): number, 22 | } 23 | } 24 | 25 | declare class iQiyiVideoInfo { 26 | currentTime: number 27 | totalDuration: number 28 | } 29 | 30 | interface IqiyiWindow extends Window { 31 | player: { 32 | getPlayInfo (callback: Function): void, 33 | }, 34 | Q: { 35 | PageInfo: { 36 | playPageInfo: { 37 | categoryName: string 38 | } 39 | } 40 | } 41 | // _player: { 42 | // getPlayerInfo (f: Function): IqiyiWindow; 43 | // } 44 | } 45 | 46 | declare var unsafeWindow: BiliWindow | IqiyiWindow 47 | declare var GM_setValue: Function 48 | declare var GM_getValue: Function 49 | declare var GM_openInTab: Function 50 | declare var GM_info: { 51 | version: string, 52 | script: { version: string }, 53 | } 54 | -------------------------------------------------------------------------------- /src/js/vars.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface Window { 3 | TM_ENV?: string; 4 | } 5 | } 6 | 7 | /* eslint-disable no-undef, camelcase */ 8 | let gmUnsafeWindow = unsafeWindow 9 | let gmSetValue = GM_setValue 10 | let gmGetValue = GM_getValue 11 | let gmOpenInTab = GM_openInTab 12 | let gmInfo = GM_info 13 | /* eslint-enable no-undef, camelcase */ 14 | 15 | const URLS = { 16 | apiServerURL: 'https://bangumi-auto-tracker.trim21.cn', 17 | apiBgmUrl: 'https://api.bgm.tv', 18 | refreshTokenPath: '/bgm-tv-auto-tracker/api.v1/refresh', 19 | newApiServer: 'https://www.trim21.cn', 20 | authURL: 'https://www.trim21.cn/bgm-tv-auto-tracker/api.v1/auth', 21 | callBackUrl: 'https://www.trim21.cn/bgm-tv-auto-tracker/api.v1/oauth_callback', 22 | 23 | } 24 | 25 | if (window.TM_ENV === 'dev') { 26 | // URLS.newApiServer = 'http://127.0.0.1:8000/' 27 | // URLS.authURL = 'http://127.0.0.1:8000/bgm-tv-auto-tracker/api.v1/auth' 28 | // URLS.callBackUrl = 'http://127.0.0.1:8000/bgm-tv-auto-tracker/api.v1/oauth_callback' 29 | } 30 | 31 | const WEBSITE = { 32 | bilibili: 'bilibili', 33 | iqiyi: 'iqiyi', 34 | } 35 | 36 | export { 37 | WEBSITE, 38 | gmSetValue, 39 | gmGetValue, 40 | gmOpenInTab, 41 | gmUnsafeWindow, 42 | URLS, 43 | gmInfo 44 | } 45 | -------------------------------------------------------------------------------- /config/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge') 2 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 3 | const metadata = require('./metadata') 4 | const path = require('path') 5 | const UserScriptMetaDataPlugin = require('userscript-metadata-webpack-plugin') 6 | const LiveReloadPlugin = require('webpack-livereload-plugin') 7 | 8 | const webpackConfig = require('./webpack.config.base') 9 | 10 | if (Object.prototype.hasOwnProperty.call(metadata, 'require')) { 11 | metadata.require.push('file://' + path.resolve(__dirname, '../dist', 'bgm-tv-auto-tracker.prod.user.js')) 12 | } else { 13 | metadata.require = ['file://' + path.resolve(__dirname, '../dist', 'bgm-tv-auto-tracker.prod.user.js')] 14 | } 15 | 16 | metadata.name = 'dev ' + metadata.name 17 | const cfg = merge(webpackConfig, { 18 | entry: { 19 | prod: webpackConfig.entry, 20 | dev: path.resolve(__dirname, './empty.js'), 21 | }, 22 | optimization: { 23 | minimize: false, 24 | }, 25 | output: { 26 | filename: 'bgm-tv-auto-tracker.[name].user.js', 27 | path: path.resolve(__dirname, '../dist'), 28 | }, 29 | devtool: 'inline-source-map', 30 | watch: true, 31 | watchOptions: { 32 | ignored: /node_modules/ 33 | }, 34 | plugins: [ 35 | new LiveReloadPlugin({ 36 | delay: 500, 37 | }), 38 | new UserScriptMetaDataPlugin({ 39 | metadata 40 | }) 41 | ] 42 | }) 43 | 44 | if (process.env.npm_config_report) { 45 | cfg.plugins.push(new BundleAnalyzerPlugin()) 46 | } 47 | 48 | module.exports = cfg 49 | -------------------------------------------------------------------------------- /src/playerUrl/index.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Show Player Url On Bgm.tv 3 | // @namespace https://trim21.me/ 4 | // @version 0.0.2 5 | // @author Trim21 6 | // @source https://github.com/Trim21/bgm-tv-auto-tracker 7 | // @description show link of video website 8 | // @license MIT 9 | // @include /https?:\/\/(bgm\.tv|bangumi\.tv|chii\.in)\/subject/.*/ 10 | // @match https?://bgm.tv/subject/* 11 | // @match https?://bangumi.tv/subject/* 12 | // @match https?://chii.in/subject/* 13 | // @grant GM_addStyle 14 | // @grant GM_setValue 15 | // @grant GM_getValue 16 | // @grant GM_openInTab 17 | // @grant GM_addStyle 18 | // @grant GM_xmlhttpRequest 19 | // @grant unsafeWindow 20 | // @connect www.trim21.cn 21 | // @run-at document-idle 22 | // ==/UserScript== 23 | 24 | /* global $, GM_xmlhttpRequest */ 25 | (function () { 26 | 'use strict' 27 | if (document.location.pathname.startsWith('/subject/')) { 28 | const subjectID = /\/subject\/(\d+).*/g.exec(document.location.pathname)[1] 29 | GM_xmlhttpRequest({ 30 | method: 'GET', 31 | url: `https://www.trim21.cn/bgm.tv/api.v1/subject/player/${subjectID}`, 32 | onload: function (response) { 33 | const data = JSON.parse(response.responseText) 34 | const container = $('h1.nameSingle') 35 | for (const episode of data) { 36 | container.append(` ${episode.website}`) 37 | } 38 | } 39 | }) 40 | console.log(document.location) 41 | } 42 | 43 | // if (document.location.pathname.startsWith('/ep/')) { 44 | // const epID = /\/ep\/(\d+).*/g.exec(document.location.pathname)[1] 45 | // GM_xmlhttpRequest({ 46 | // method: 'GET', 47 | // url: `https://www.trim21.cn/bgm.tv/api.v0/ep/player/${epID}`, 48 | // onload: function (response) { 49 | // const data = JSON.parse(response.responseText) 50 | // $('#columnEpA .epDesc').append('
') 51 | // const container = $('#trim21-player-url-details') 52 | // for (const episode of data) { 53 | // container.append(`
  • ${episode.website}
  • `) 54 | // } 55 | // } 56 | // }) 57 | // console.log(document.location) 58 | // } 59 | })() 60 | -------------------------------------------------------------------------------- /src/style/less/bilibili.less: -------------------------------------------------------------------------------- 1 | 2 | #bangumi_detail .bangumi-info.clearfix .info-right .info-title.clearfix a h2 { 3 | width: 380px; 4 | } 5 | 6 | @media screen and (max-width: 1400px) { 7 | .arc-toolbar .block { 8 | padding: 0 12px; 9 | margin-left: -12px; 10 | } 11 | 12 | .video-toolbar-module .btn-item { 13 | padding: 0 0 0 60px !important; 14 | margin-left: -12px; 15 | } 16 | 17 | #bangumi_detail .bangumi-info.clearfix .info-right .info-title.clearfix a h2 { 18 | width: 200px !important; 19 | } 20 | } 21 | 22 | .bilibili { 23 | &#bgm_tv_tracker { 24 | display: inline-block; 25 | position: relative; 26 | float: left; 27 | margin-right: 20px; 28 | } 29 | 30 | .bgm_tv_tracker_radius { 31 | border-radius: 4px; 32 | border: 1px solid #e5e9ef; 33 | } 34 | 35 | .bgm_tv_tracker_btn.bgm_tv_tracker:hover { 36 | color: #00A1D6; 37 | border: 1px solid #00A1D6; 38 | } 39 | 40 | .bgm_tv_tracker_btn.bgm_tv_tracker { 41 | user-select: none; 42 | color: #6d757a; 43 | float: left; 44 | cursor: pointer; 45 | font-size: 14px; 46 | height: 28px; 47 | line-height: 28px; 48 | text-align: center; 49 | width: 100px !important; 50 | transition: all .1s ease-in; 51 | 52 | } 53 | 54 | .bgm_tv_tracker_info { 55 | padding: 8px; 56 | margin-top: 5px; 57 | background: #fff; 58 | border-radius: 0 0 4px 4px; 59 | border: 1px solid #e5e9ef; 60 | box-shadow: rgba(0, 0, 0, .16) 0 2px 4px; 61 | cursor: default; 62 | height: auto; 63 | left: -1px; 64 | line-height: normal; 65 | opacity: 0; 66 | pointer-events: none; 67 | position: absolute; 68 | text-align: left; 69 | top: 70px; 70 | white-space: normal; 71 | width: 300px; 72 | z-index: 1000; 73 | } 74 | 75 | .bgm_tv_tracker_info * { 76 | max-width: 100%; 77 | } 78 | 79 | .bgm_tv_tracker_info { 80 | opacity: 1; 81 | pointer-events: auto; 82 | top: 100%; 83 | } 84 | 85 | .bgm_tv_tracker_info button { 86 | padding: 4px 6px; 87 | line-height: 14px; 88 | display: inline-block; 89 | margin: 4px; 90 | border: 2px solid white; 91 | } 92 | 93 | .bgm_tv_tracker_info button:active { 94 | background: white; 95 | } 96 | 97 | .bgm_tv_tracker_info button:hover { 98 | border: 2px solid #99bdf7; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bgm-tv-auto-tracker", 3 | "description": "auto tracker your bangumi progress", 4 | "version": "1.0.10", 5 | "author": { 6 | "name": "Trim21", 7 | "email": "trim21me@gmail.com" 8 | }, 9 | "scripts": { 10 | "postversion": "npm run build && git add .", 11 | "anylize": "cross-env npm_config_report=true npm run build", 12 | "build": "cross-env NODE_ENV=production webpack --mode production --config config/webpack.config.prod.js", 13 | "dev": "webpack --mode development --config config/webpack.config.dev.js", 14 | "lint": "eslint -c package.json src" 15 | }, 16 | "browserslist": [ 17 | "last 2 version", 18 | "> 1%" 19 | ], 20 | "eslintIgnore": [ 21 | "dist/*.js" 22 | ], 23 | "eslintConfig": { 24 | "extends": "standard", 25 | "rules": { 26 | "comma-dangle": [ 27 | 0 28 | ], 29 | "standard/no-callback-literal": [ 30 | 0 31 | ], 32 | "prefer-promise-reject-errors": [ 33 | 0 34 | ] 35 | } 36 | }, 37 | "repository": { 38 | "type": "git", 39 | "url": "https://github.com/Trim21/bilibili-bangumi-tv-auto-tracker" 40 | }, 41 | "private": true, 42 | "devDependencies": { 43 | "@types/jquery": "3.3.33", 44 | "autoprefixer": "9.7.4", 45 | "cross-env": "6.0.3", 46 | "css-loader": "3.4.2", 47 | "cssnano": "4.1.10", 48 | "eslint": "6.8.0", 49 | "eslint-config-standard": "14.1.0", 50 | "eslint-loader": "3.0.3", 51 | "eslint-plugin-import": "2.20.1", 52 | "eslint-plugin-node": "11.0.0", 53 | "eslint-plugin-promise": "4.2.1", 54 | "eslint-plugin-standard": "4.0.1", 55 | "hoek": "6.1.3", 56 | "html-loader": "0.5.5", 57 | "postcss-loader": "3.0.0", 58 | "style-loader": "1.1.3", 59 | "to-string-loader": "1.1.6", 60 | "less": "3.11.1", 61 | "less-loader": "5.0.0", 62 | "ts-loader": "6.2.1", 63 | "typescript": "3.8.3", 64 | "userscript-metadata-webpack-plugin": "0.0.3", 65 | "vue-loader": "15.9.0", 66 | "vue-style-loader": "4.1.2", 67 | "vue-template-compiler": "2.6.11", 68 | "webpack": "4.41.6", 69 | "webpack-bundle-analyzer": "3.6.0", 70 | "webpack-cli": "3.3.11", 71 | "webpack-livereload-plugin": "2.2.0", 72 | "webpack-merge": "4.2.2", 73 | "wrapper-webpack-plugin": "2.1.0" 74 | }, 75 | "dependencies": { 76 | "axios": "0.19.0", 77 | "axios-userscript-adapter": "0.0.4", 78 | "jquery": "3.4.1", 79 | "vue": "2.6.11" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /config/webpack.config.base.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const VueLoaderPlugin = require('vue-loader/lib/plugin') 4 | const production = process.env.NODE_ENV === 'production' 5 | const pkg = require('../package.json') 6 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 7 | 8 | const webpackConfig = { 9 | resolve: { 10 | extensions: ['.js', '.vue', '.ts', '.less'], 11 | alias: { '@': path.resolve(__dirname, '../src/') }, 12 | }, 13 | optimization: { 14 | minimize: true, 15 | removeEmptyChunks: true, 16 | chunkIds: 'named', 17 | removeAvailableModules: false, 18 | mergeDuplicateChunks: false, 19 | }, 20 | entry: './src/js/index.js', 21 | output: { 22 | path: path.resolve(__dirname, '../dist') 23 | }, 24 | externals: { 25 | // jquery: '$', 26 | // vue: 'Vue', 27 | // axios: 'axios', 28 | // 'axios-userscript-adapter': 'axiosGmxhrAdapter', 29 | }, 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.ts?$/, 34 | loader: 'ts-loader', 35 | exclude: /node_modules/, 36 | options: { 37 | appendTsSuffixTo: [/\.vue$/] 38 | }, 39 | }, 40 | { 41 | test: /\.js$/, 42 | exclude: /node_modules/ 43 | }, 44 | { 45 | test: /\.less$/, 46 | loader: [ 47 | 'style-loader', 48 | 'css-loader', 49 | 'postcss-loader', 50 | 'less-loader', // 将 Less 编译为 CSS 51 | ] 52 | }, 53 | { 54 | test: /\.css$/, 55 | loader: [ 56 | 'style-loader', 57 | 'css-loader', 58 | 'postcss-loader' 59 | ] 60 | }, 61 | { 62 | test: /\.vue$/, 63 | loader: 'vue-loader', 64 | options: { 65 | loaders: { 66 | scss: 'vue-style-loader!css-loader!postcss-loader!sass-loader', // 549 | --------------------------------------------------------------------------------