From 1cf870d0271ae90179c7e64b8ef28c64d1fb834b Mon Sep 17 00:00:00 2001 From: dudor Date: Wed, 18 Dec 2024 07:12:22 +0800 Subject: [PATCH 1/3] mv3 --- .gitignore | 43 +- LICENSE | 201 - README.md | 99 +- README_cn.md | 96 - config/env.js | 106 - config/getHttpsConfig.js | 66 - config/jest/babelTransform.js | 29 - config/jest/cssTransform.js | 14 - config/jest/fileTransform.js | 40 - config/modules.js | 134 - config/paths.js | 75 - config/pnpTs.js | 35 - config/webpack.config.js | 761 - config/webpackDevServer.config.js | 130 - images/1.png | Bin 181449 -> 0 bytes images/2.png | Bin 113377 -> 0 bytes images/3.gif | Bin 1483670 -> 0 bytes package.json | 205 +- pnpm-lock.yaml | 5032 +++++++ public/icons/icon128.png | Bin 4268 -> 0 bytes public/icons/icon32.png | Bin 4233 -> 0 bytes public/manifest.json | 36 - scripts/build.js | 214 - scripts/start.js | 166 - scripts/test.js | 53 - src/App.css | 38 - src/App.test.tsx | 9 - src/App.tsx | 26 - src/assets/react.svg | 1 + src/entrypoints/background.ts | 383 + src/entrypoints/options/index.html | 17 + .../options/options.css | 0 .../options/options.tsx | 12 +- src/entrypoints/popup/index.html | 13 + src/{views => entrypoints}/popup/popup.css | 0 src/{views => entrypoints}/popup/popup.tsx | 12 +- src/icons/icon128.png | Bin 4268 -> 0 bytes src/icons/icon16.png | Bin 416 -> 0 bytes src/icons/icon19.png | Bin 469 -> 0 bytes src/icons/icon48.png | Bin 1208 -> 0 bytes src/icons/icon64.png | Bin 1666 -> 0 bytes src/index.css | 13 - src/index.tsx | 17 - src/logo.svg | 1 - .../public}/_locales/en/messages.json | 0 .../public}/_locales/zh_CN/messages.json | 0 {images => src/public/icon}/icon128.png | Bin {public/icons => src/public/icon}/icon16.png | Bin {public/icons => src/public/icon}/icon19.png | Bin {public/icons => src/public/icon}/icon48.png | Bin {public/icons => src/public/icon}/icon64.png | Bin src/public/wxt.svg | 15 + src/react-app-env.d.ts | 71 - src/reportWebVitals.ts | 15 - src/setupTests.ts | 5 - src/utils/http.ts | 57 +- src/utils/optionsStorage.ts | 1 - src/utils/services.ts | 18 +- src/utils/setting.ts | 25 + src/views/background/background.html | 41 - src/views/background/background.tsx | 361 - src/views/options/options.html | 41 - src/views/popup/popup.html | 41 - tools.js | 71 - tsconfig.json | 25 +- wxt.config.ts | 18 + yarn.lock | 11567 ---------------- 67 files changed, 5615 insertions(+), 14834 deletions(-) delete mode 100644 LICENSE delete mode 100644 README_cn.md delete mode 100644 config/env.js delete mode 100644 config/getHttpsConfig.js delete mode 100644 config/jest/babelTransform.js delete mode 100644 config/jest/cssTransform.js delete mode 100644 config/jest/fileTransform.js delete mode 100644 config/modules.js delete mode 100644 config/paths.js delete mode 100644 config/pnpTs.js delete mode 100644 config/webpack.config.js delete mode 100644 config/webpackDevServer.config.js delete mode 100644 images/1.png delete mode 100644 images/2.png delete mode 100644 images/3.gif create mode 100644 pnpm-lock.yaml delete mode 100644 public/icons/icon128.png delete mode 100644 public/icons/icon32.png delete mode 100644 public/manifest.json delete mode 100644 scripts/build.js delete mode 100644 scripts/start.js delete mode 100644 scripts/test.js delete mode 100644 src/App.css delete mode 100644 src/App.test.tsx delete mode 100644 src/App.tsx create mode 100644 src/assets/react.svg create mode 100644 src/entrypoints/background.ts create mode 100644 src/entrypoints/options/index.html rename src/{views => entrypoints}/options/options.css (100%) rename src/{views => entrypoints}/options/options.tsx (95%) create mode 100644 src/entrypoints/popup/index.html rename src/{views => entrypoints}/popup/popup.css (100%) rename src/{views => entrypoints}/popup/popup.tsx (94%) delete mode 100644 src/icons/icon128.png delete mode 100644 src/icons/icon16.png delete mode 100644 src/icons/icon19.png delete mode 100644 src/icons/icon48.png delete mode 100644 src/icons/icon64.png delete mode 100644 src/index.css delete mode 100644 src/index.tsx delete mode 100644 src/logo.svg rename {public => src/public}/_locales/en/messages.json (100%) rename {public => src/public}/_locales/zh_CN/messages.json (100%) rename {images => src/public/icon}/icon128.png (100%) rename {public/icons => src/public/icon}/icon16.png (100%) rename {public/icons => src/public/icon}/icon19.png (100%) rename {public/icons => src/public/icon}/icon48.png (100%) rename {public/icons => src/public/icon}/icon64.png (100%) create mode 100644 src/public/wxt.svg delete mode 100644 src/react-app-env.d.ts delete mode 100644 src/reportWebVitals.ts delete mode 100644 src/setupTests.ts delete mode 100644 src/views/background/background.html delete mode 100644 src/views/background/background.tsx delete mode 100644 src/views/options/options.html delete mode 100644 src/views/popup/popup.html delete mode 100644 tools.js create mode 100644 wxt.config.ts delete mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore index 4d29575..a256953 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,26 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - +# Logs +logs +*.log npm-debug.log* yarn-debug.log* yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.output +stats.html +stats-*.json +.wxt +web-ext.config.ts + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md index bfbcbd1..78cf194 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,3 @@ +# WXT + React - -
-

- - BookmarkHub - - -

BookmarkHub

-

- BookmarkHub is a browser plug-in that can synchronize your bookmarks between different browsers. -
- Feedback - · - 简体中文 - · - English -

-

- - -
-

Table of Contents

-
    -
  1. About The Project
  2. -
  3. Features
  4. -
  5. Installation
  6. -
  7. Usage
  8. -
  9. Roadmap
  10. -
  11. License
  12. -
  13. Contact
  14. -
-
- - -## About The Project - -BookmarkHub is a browser plug-in that can synchronize your bookmarks between different browsers. - -For major browsers such as Chrome, Firefox, Microsoft Edge, and more. - -It uses GitHub's Gist records to store browser bookmarks for safe and secure use. - -![BookmarkHub](images/3.gif) - -![BookmarkHub](images/1.png) - -![BookmarkHub](images/2.png) - -## Features -* No registration required, just use the Token and Gist of your GitHub account -* Easy to upload and download bookmarks with one click -* Clear all local bookmarks with one click -* Support cross-machine and cross-browser synchronization of bookmarks -* Support to display the number of local and remote bookmarks - - -## Installation -> This plug-in requires bookmarks to be stored in Gist, so make sure you have a GitHub account or register your GitHub account over the network. -* [Chrome](https://chrome.google.com/webstore/detail/bookmarkhub-sync-bookmark/fohimdklhhcpcnpmmichieidclgfdmol) -* [Firefox](https://addons.mozilla.org/en/firefox/addon/BookmarkHub/) -* [Microsoft Edge](https://microsoftedge.microsoft.com/addons/detail/BookmarkHub/fdnmfpogadcljhecfhdikdecbkggfmgk) -* Opera -* Other browsers based on the Chromium kernel - - -## Usage - -1. [Login](https://github.com/login) GitHub,If you don't have an account, please [click here to register](https://github.com/join)。 -2. [Create a token that manages the gist](https://github.com/settings/tokens/new)。 -3. [Create a secret gist](https://gist.github.com)。__Note: If it's a public gist, your bookmarks can be searched by others。__ -4. Download BookmarkHub in the browser store, click the plug-in's settings button, fill in the token and gist ID in the pop-up settings window, and you can upload the download bookmark。 - - -## Roadmap - -- [ ] Automatically sync bookmarks -- [ ] Support webdav protocol -- [ ] Mobile app -- [ ] Import and Export -- [ ] Share bookmarks - - -## License - -See `LICENSE` for more information. - - - - -## Contact - -dudor - -Project Link: [https://github.com/dudor/BookmarkHub](https://github.com/dudor/BookmarkHub) - - - +This template should help get you started developing with React in WXT. diff --git a/README_cn.md b/README_cn.md deleted file mode 100644 index 3d40414..0000000 --- a/README_cn.md +++ /dev/null @@ -1,96 +0,0 @@ - - -
-

- - BookmarkHub - - -

BookmarkHub

-

- BookmarkHub 是一款浏览器插件,可以在不同浏览器之间同步你的书签。 -
- 反馈问题 - · - 简体中文 - · - English -

-

- - -
-

目录

-
    -
  1. 关于
  2. -
  3. 功能
  4. -
  5. 下载安装
  6. -
  7. 使用方法
  8. -
  9. 待实现的功能
  10. -
  11. License
  12. -
  13. Contact
  14. -
-
- - -## 关于 - -BookmarkHub 是一款浏览器插件,可以在不同浏览器之间同步你的书签。 - -适用于各大主流浏览器,如 Chrome、Firefox、Microsoft Edge 等。 - -它使用 GitHub 的 Gist 记录来存储浏览器的书签,可以放心安全的使用。 - -![BookmarkHub](images/3.gif) - -![BookmarkHub](images/1.png) - -![BookmarkHub](images/2.png) - -## 功能 -* 不需要注册特殊账号,只需要用你的 GitHub 账号的Token和Gist -* 一键上传下载书签 -* 一键清空本地所有书签 -* 支持跨电脑跨浏览器同步书签 -* 支持显示本地和远程书签的数量 - -## 下载安装 -> 本插件需要把书签存储到 Gist 中,所以请确保有 GitHub 账号或可以通过网络注册 GitHub 账号。 -* [Chrome 浏览器](https://chrome.google.com/webstore/detail/bookmarkhub-sync-bookmark/fohimdklhhcpcnpmmichieidclgfdmol) -* [Firefox 浏览器](https://addons.mozilla.org/zh-CN/firefox/addon/BookmarkHub/) -* [Microsoft Edge 浏览器](https://microsoftedge.microsoft.com/addons/detail/BookmarkHub/fdnmfpogadcljhecfhdikdecbkggfmgk) -* Opera 浏览器 -* 其他基于 Chromium 内核的浏览器 - - -## 使用方法 - -1. [登陆](https://github.com/login) GitHub,如果没有账号请点此[注册](https://github.com/join)。 -2. [创建一个可以管理 gist 的 token](https://github.com/settings/tokens/new)。 -3. [创建一个私有的 gist](https://gist.github.com)。__注意:如果是公开的 gist,你的书签是可以被他人搜索到的。__ -4. 在浏览器的应用商店下载 BookmarkHub,点击插件的设置按钮,在弹出的设置窗口填入 token 和 gist ID,然后你就可以上传下载书签了。 - - -## 待实现的功能 - -- [ ] 自动同步书签 -- [ ] 支持 webdav 协议 -- [ ] 移动端 -- [ ] 导入导出 -- [ ] 分享书签 - - - -## License - -See `LICENSE` for more information. - - -## Contact - -dudor - -Project Link: [https://github.com/dudor/BookmarkHub](https://github.com/dudor/BookmarkHub) - - - diff --git a/config/env.js b/config/env.js deleted file mode 100644 index 3d1411b..0000000 --- a/config/env.js +++ /dev/null @@ -1,106 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const paths = require('./paths'); - -// Make sure that including paths.js after env.js will read .env variables. -delete require.cache[require.resolve('./paths')]; - -const NODE_ENV = process.env.NODE_ENV; -if (!NODE_ENV) { - throw new Error( - 'The NODE_ENV environment variable is required but was not specified.' - ); -} - -// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use -const dotenvFiles = [ - `${paths.dotenv}.${NODE_ENV}.local`, - // Don't include `.env.local` for `test` environment - // since normally you expect tests to produce the same - // results for everyone - NODE_ENV !== 'test' && `${paths.dotenv}.local`, - `${paths.dotenv}.${NODE_ENV}`, - paths.dotenv, -].filter(Boolean); - -// Load environment variables from .env* files. Suppress warnings using silent -// if this file is missing. dotenv will never modify any environment variables -// that have already been set. Variable expansion is supported in .env files. -// https://github.com/motdotla/dotenv -// https://github.com/motdotla/dotenv-expand -dotenvFiles.forEach(dotenvFile => { - if (fs.existsSync(dotenvFile)) { - require('dotenv-expand')( - require('dotenv').config({ - path: dotenvFile, - }) - ); - } -}); - -// We support resolving modules according to `NODE_PATH`. -// This lets you use absolute paths in imports inside large monorepos: -// https://github.com/facebook/create-react-app/issues/253. -// It works similar to `NODE_PATH` in Node itself: -// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders -// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. -// Otherwise, we risk importing Node.js core modules into an app instead of webpack shims. -// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421 -// We also resolve them to make sure all tools using them work consistently. -const appDirectory = fs.realpathSync(process.cwd()); -process.env.NODE_PATH = (process.env.NODE_PATH || '') - .split(path.delimiter) - .filter(folder => folder && !path.isAbsolute(folder)) - .map(folder => path.resolve(appDirectory, folder)) - .join(path.delimiter); - -// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be -// injected into the application via DefinePlugin in webpack configuration. -const REACT_APP = /^REACT_APP_/i; - -function getClientEnvironment(publicUrl) { - const raw = Object.keys(process.env) - .filter(key => REACT_APP.test(key)) - .reduce( - (env, key) => { - env[key] = process.env[key]; - return env; - }, - { - // Useful for determining whether we’re running in production mode. - // Most importantly, it switches React into the correct mode. - NODE_ENV: process.env.NODE_ENV || 'development', - // Useful for resolving the correct path to static assets in `public`. - // For example, . - // This should only be used as an escape hatch. Normally you would put - // images into the `src` and `import` them in code to get their paths. - PUBLIC_URL: publicUrl, - // We support configuring the sockjs pathname during development. - // These settings let a developer run multiple simultaneous projects. - // They are used as the connection `hostname`, `pathname` and `port` - // in webpackHotDevClient. They are used as the `sockHost`, `sockPath` - // and `sockPort` options in webpack-dev-server. - WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST, - WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH, - WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT, - // Whether or not react-refresh is enabled. - // react-refresh is not 100% stable at this time, - // which is why it's disabled by default. - // It is defined here so it is available in the webpackHotDevClient. - FAST_REFRESH: process.env.FAST_REFRESH !== 'false', - } - ); - // Stringify all values so we can feed into webpack DefinePlugin - const stringified = { - 'process.env': Object.keys(raw).reduce((env, key) => { - env[key] = JSON.stringify(raw[key]); - return env; - }, {}), - }; - - return { raw, stringified }; -} - -module.exports = getClientEnvironment; diff --git a/config/getHttpsConfig.js b/config/getHttpsConfig.js deleted file mode 100644 index 013d493..0000000 --- a/config/getHttpsConfig.js +++ /dev/null @@ -1,66 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const crypto = require('crypto'); -const chalk = require('react-dev-utils/chalk'); -const paths = require('./paths'); - -// Ensure the certificate and key provided are valid and if not -// throw an easy to debug error -function validateKeyAndCerts({ cert, key, keyFile, crtFile }) { - let encrypted; - try { - // publicEncrypt will throw an error with an invalid cert - encrypted = crypto.publicEncrypt(cert, Buffer.from('test')); - } catch (err) { - throw new Error( - `The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}` - ); - } - - try { - // privateDecrypt will throw an error with an invalid key - crypto.privateDecrypt(key, encrypted); - } catch (err) { - throw new Error( - `The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${ - err.message - }` - ); - } -} - -// Read file and throw an error if it doesn't exist -function readEnvFile(file, type) { - if (!fs.existsSync(file)) { - throw new Error( - `You specified ${chalk.cyan( - type - )} in your env, but the file "${chalk.yellow(file)}" can't be found.` - ); - } - return fs.readFileSync(file); -} - -// Get the https config -// Return cert files if provided in env, otherwise just true or false -function getHttpsConfig() { - const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env; - const isHttps = HTTPS === 'true'; - - if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) { - const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE); - const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE); - const config = { - cert: readEnvFile(crtFile, 'SSL_CRT_FILE'), - key: readEnvFile(keyFile, 'SSL_KEY_FILE'), - }; - - validateKeyAndCerts({ ...config, keyFile, crtFile }); - return config; - } - return isHttps; -} - -module.exports = getHttpsConfig; diff --git a/config/jest/babelTransform.js b/config/jest/babelTransform.js deleted file mode 100644 index dabf5a8..0000000 --- a/config/jest/babelTransform.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -const babelJest = require('babel-jest'); - -const hasJsxRuntime = (() => { - if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { - return false; - } - - try { - require.resolve('react/jsx-runtime'); - return true; - } catch (e) { - return false; - } -})(); - -module.exports = babelJest.createTransformer({ - presets: [ - [ - require.resolve('babel-preset-react-app'), - { - runtime: hasJsxRuntime ? 'automatic' : 'classic', - }, - ], - ], - babelrc: false, - configFile: false, -}); diff --git a/config/jest/cssTransform.js b/config/jest/cssTransform.js deleted file mode 100644 index 8f65114..0000000 --- a/config/jest/cssTransform.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -// This is a custom Jest transformer turning style imports into empty objects. -// http://facebook.github.io/jest/docs/en/webpack.html - -module.exports = { - process() { - return 'module.exports = {};'; - }, - getCacheKey() { - // The output is always the same. - return 'cssTransform'; - }, -}; diff --git a/config/jest/fileTransform.js b/config/jest/fileTransform.js deleted file mode 100644 index aab6761..0000000 --- a/config/jest/fileTransform.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -const path = require('path'); -const camelcase = require('camelcase'); - -// This is a custom Jest transformer turning file imports into filenames. -// http://facebook.github.io/jest/docs/en/webpack.html - -module.exports = { - process(src, filename) { - const assetFilename = JSON.stringify(path.basename(filename)); - - if (filename.match(/\.svg$/)) { - // Based on how SVGR generates a component name: - // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 - const pascalCaseFilename = camelcase(path.parse(filename).name, { - pascalCase: true, - }); - const componentName = `Svg${pascalCaseFilename}`; - return `const React = require('react'); - module.exports = { - __esModule: true, - default: ${assetFilename}, - ReactComponent: React.forwardRef(function ${componentName}(props, ref) { - return { - $$typeof: Symbol.for('react.element'), - type: 'svg', - ref: ref, - key: null, - props: Object.assign({}, props, { - children: ${assetFilename} - }) - }; - }), - };`; - } - - return `module.exports = ${assetFilename};`; - }, -}; diff --git a/config/modules.js b/config/modules.js deleted file mode 100644 index d63e41d..0000000 --- a/config/modules.js +++ /dev/null @@ -1,134 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const paths = require('./paths'); -const chalk = require('react-dev-utils/chalk'); -const resolve = require('resolve'); - -/** - * Get additional module paths based on the baseUrl of a compilerOptions object. - * - * @param {Object} options - */ -function getAdditionalModulePaths(options = {}) { - const baseUrl = options.baseUrl; - - if (!baseUrl) { - return ''; - } - - const baseUrlResolved = path.resolve(paths.appPath, baseUrl); - - // We don't need to do anything if `baseUrl` is set to `node_modules`. This is - // the default behavior. - if (path.relative(paths.appNodeModules, baseUrlResolved) === '') { - return null; - } - - // Allow the user set the `baseUrl` to `appSrc`. - if (path.relative(paths.appSrc, baseUrlResolved) === '') { - return [paths.appSrc]; - } - - // If the path is equal to the root directory we ignore it here. - // We don't want to allow importing from the root directly as source files are - // not transpiled outside of `src`. We do allow importing them with the - // absolute path (e.g. `src/Components/Button.js`) but we set that up with - // an alias. - if (path.relative(paths.appPath, baseUrlResolved) === '') { - return null; - } - - // Otherwise, throw an error. - throw new Error( - chalk.red.bold( - "Your project's `baseUrl` can only be set to `src` or `node_modules`." + - ' Create React App does not support other values at this time.' - ) - ); -} - -/** - * Get webpack aliases based on the baseUrl of a compilerOptions object. - * - * @param {*} options - */ -function getWebpackAliases(options = {}) { - const baseUrl = options.baseUrl; - - if (!baseUrl) { - return {}; - } - - const baseUrlResolved = path.resolve(paths.appPath, baseUrl); - - if (path.relative(paths.appPath, baseUrlResolved) === '') { - return { - src: paths.appSrc, - }; - } -} - -/** - * Get jest aliases based on the baseUrl of a compilerOptions object. - * - * @param {*} options - */ -function getJestAliases(options = {}) { - const baseUrl = options.baseUrl; - - if (!baseUrl) { - return {}; - } - - const baseUrlResolved = path.resolve(paths.appPath, baseUrl); - - if (path.relative(paths.appPath, baseUrlResolved) === '') { - return { - '^src/(.*)$': '/src/$1', - }; - } -} - -function getModules() { - // Check if TypeScript is setup - const hasTsConfig = fs.existsSync(paths.appTsConfig); - const hasJsConfig = fs.existsSync(paths.appJsConfig); - - if (hasTsConfig && hasJsConfig) { - throw new Error( - 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.' - ); - } - - let config; - - // If there's a tsconfig.json we assume it's a - // TypeScript project and set up the config - // based on tsconfig.json - if (hasTsConfig) { - const ts = require(resolve.sync('typescript', { - basedir: paths.appNodeModules, - })); - config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config; - // Otherwise we'll check if there is jsconfig.json - // for non TS projects. - } else if (hasJsConfig) { - config = require(paths.appJsConfig); - } - - config = config || {}; - const options = config.compilerOptions || {}; - - const additionalModulePaths = getAdditionalModulePaths(options); - - return { - additionalModulePaths: additionalModulePaths, - webpackAliases: getWebpackAliases(options), - jestAliases: getJestAliases(options), - hasTsConfig, - }; -} - -module.exports = getModules(); diff --git a/config/paths.js b/config/paths.js deleted file mode 100644 index ea402d8..0000000 --- a/config/paths.js +++ /dev/null @@ -1,75 +0,0 @@ -'use strict'; - -const path = require('path'); -const fs = require('fs'); -const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath'); - -// Make sure any symlinks in the project folder are resolved: -// https://github.com/facebook/create-react-app/issues/637 -const appDirectory = fs.realpathSync(process.cwd()); -const resolveApp = relativePath => path.resolve(appDirectory, relativePath); - -// We use `PUBLIC_URL` environment variable or "homepage" field to infer -// "public path" at which the app is served. -// webpack needs to know it to put the right + + diff --git a/src/views/options/options.css b/src/entrypoints/options/options.css similarity index 100% rename from src/views/options/options.css rename to src/entrypoints/options/options.css diff --git a/src/views/options/options.tsx b/src/entrypoints/options/options.tsx similarity index 95% rename from src/views/options/options.tsx rename to src/entrypoints/options/options.tsx index 0787df0..0853656 100644 --- a/src/views/options/options.tsx +++ b/src/entrypoints/options/options.tsx @@ -1,8 +1,7 @@ import React, { useState, useEffect } from 'react' -import ReactDOM from 'react-dom' +import ReactDOM from 'react-dom/client'; import { Container, Form, Button, Col, Row, InputGroup } from 'react-bootstrap'; import { useForm } from "react-hook-form"; -import { browser } from "webextension-polyfill-ts"; import 'bootstrap/dist/css/bootstrap.min.css'; import './options.css' import optionsStorage from '../../utils/optionsStorage' @@ -61,9 +60,10 @@ const Popup: React.FC = () => { ) } -ReactDOM.render( + +ReactDOM.createRoot(document.getElementById('root')!).render( - + , - document.getElementById('root') -) \ No newline at end of file + ); + \ No newline at end of file diff --git a/src/entrypoints/popup/index.html b/src/entrypoints/popup/index.html new file mode 100644 index 0000000..96e80e2 --- /dev/null +++ b/src/entrypoints/popup/index.html @@ -0,0 +1,13 @@ + + + + + + BookmarkHub,sync bookmarks across different browsers + + + +
+ + + diff --git a/src/views/popup/popup.css b/src/entrypoints/popup/popup.css similarity index 100% rename from src/views/popup/popup.css rename to src/entrypoints/popup/popup.css diff --git a/src/views/popup/popup.tsx b/src/entrypoints/popup/popup.tsx similarity index 94% rename from src/views/popup/popup.tsx rename to src/entrypoints/popup/popup.tsx index 662b24d..a2143ab 100644 --- a/src/views/popup/popup.tsx +++ b/src/entrypoints/popup/popup.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect } from 'react' -import ReactDOM from 'react-dom' -import { browser } from "webextension-polyfill-ts"; +import ReactDOM from 'react-dom/client'; import { Dropdown, Badge } from 'react-bootstrap'; import { IconContext } from 'react-icons' import { @@ -20,7 +19,7 @@ const Popup: React.FC = () => { browser.runtime.sendMessage({ name: elem.name }) .then((res) => { elem.removeAttribute('disabled'); - console.log("msg", res) + console.log("msg", Date.now()) }) .catch(c => { console.log("error", c) @@ -53,12 +52,11 @@ const Popup: React.FC = () => { ) } -ReactDOM.render( + +ReactDOM.createRoot(document.getElementById('root')!).render( , - document.getElementById('root') -) - +); diff --git a/src/icons/icon128.png b/src/icons/icon128.png deleted file mode 100644 index cfa7587e8de34db1c1abc476ac343757c9b95624..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4268 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 LNkvXXu0mjfqvh87 diff --git a/src/icons/icon48.png b/src/icons/icon48.png deleted file mode 100644 index 986a1bdb65369dc6c6519ecb6dcadafa40db1a27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1208 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 - - , - document.getElementById('root') -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/src/logo.svg b/src/logo.svg deleted file mode 100644 index 9dfc1c0..0000000 --- a/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/_locales/en/messages.json b/src/public/_locales/en/messages.json similarity index 100% rename from public/_locales/en/messages.json rename to src/public/_locales/en/messages.json diff --git a/public/_locales/zh_CN/messages.json b/src/public/_locales/zh_CN/messages.json similarity index 100% rename from public/_locales/zh_CN/messages.json rename to src/public/_locales/zh_CN/messages.json diff --git a/images/icon128.png b/src/public/icon/icon128.png similarity index 100% rename from images/icon128.png rename to src/public/icon/icon128.png diff --git a/public/icons/icon16.png b/src/public/icon/icon16.png similarity index 100% rename from public/icons/icon16.png rename to src/public/icon/icon16.png diff --git a/public/icons/icon19.png b/src/public/icon/icon19.png similarity index 100% rename from public/icons/icon19.png rename to src/public/icon/icon19.png diff --git a/public/icons/icon48.png b/src/public/icon/icon48.png similarity index 100% rename from public/icons/icon48.png rename to src/public/icon/icon48.png diff --git a/public/icons/icon64.png b/src/public/icon/icon64.png similarity index 100% rename from public/icons/icon64.png rename to src/public/icon/icon64.png diff --git a/src/public/wxt.svg b/src/public/wxt.svg new file mode 100644 index 0000000..0e76320 --- /dev/null +++ b/src/public/wxt.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts deleted file mode 100644 index 624c875..0000000 --- a/src/react-app-env.d.ts +++ /dev/null @@ -1,71 +0,0 @@ -/// -/// -/// - -declare namespace NodeJS { - interface ProcessEnv { - readonly NODE_ENV: 'development' | 'production' | 'test'; - readonly PUBLIC_URL: string; - } -} - -declare module '*.avif' { - const src: string; - export default src; -} - -declare module '*.bmp' { - const src: string; - export default src; -} - -declare module '*.gif' { - const src: string; - export default src; -} - -declare module '*.jpg' { - const src: string; - export default src; -} - -declare module '*.jpeg' { - const src: string; - export default src; -} - -declare module '*.png' { - const src: string; - export default src; -} - -declare module '*.webp' { - const src: string; - export default src; -} - -declare module '*.svg' { - import * as React from 'react'; - - export const ReactComponent: React.FunctionComponent & { title?: string }>; - - const src: string; - export default src; -} - -declare module '*.module.css' { - const classes: { readonly [key: string]: string }; - export default classes; -} - -declare module '*.module.scss' { - const classes: { readonly [key: string]: string }; - export default classes; -} - -declare module '*.module.sass' { - const classes: { readonly [key: string]: string }; - export default classes; -} diff --git a/src/reportWebVitals.ts b/src/reportWebVitals.ts deleted file mode 100644 index 49a2a16..0000000 --- a/src/reportWebVitals.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ReportHandler } from 'web-vitals'; - -const reportWebVitals = (onPerfEntry?: ReportHandler) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/src/setupTests.ts b/src/setupTests.ts deleted file mode 100644 index 8f2609b..0000000 --- a/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; diff --git a/src/utils/http.ts b/src/utils/http.ts index 62e3249..728eb55 100644 --- a/src/utils/http.ts +++ b/src/utils/http.ts @@ -1,40 +1,27 @@ -import axios from 'axios' +import ky from 'ky' import { Setting } from './setting' -let axiosObj = axios.create({ - timeout: 60000, -}); -axiosObj.interceptors.request.use(async cfg => { - let setting = await Setting.build(); - cfg.baseURL = setting.githubURL; - cfg.headers.post['Content-Type'] = "application/json;charset=UTF-8"; - cfg.headers.common['Authorization'] = `token ${setting.githubToken}`; - cfg.headers.common['Accept'] = `application/vnd.github.v3+json`; - return cfg; -}, - error => { - console.error(error) - return Promise.reject(error); - }) +// async function get(url:string){ +// return ky.get(url,null); +// } -axiosObj.interceptors.response.use(function (response) { - // Any status code that lie within the range of 2xx cause this function to trigger - // Do something with response data - if (response.status === 200) { - return Promise.resolve(response) - } - else { - console.warn('response', response); - return Promise.reject(response) - } -}, function (error) { - // Any status codes that falls outside the range of 2xx cause this function to trigger - // Do something with response error - console.error('error', error) - if (error.response) { - error = { ...error, ...error.response.data } +// async function patch(url:string,data:any){ +// return ky.patch(url,) +// } + + +export const http = ky.create({ + prefixUrl: 'https://api.github.com', + hooks: { + beforeRequest: [ + async request => { + let setting = await Setting.build(); + request.headers.set('Authorization', `Bearer ${setting.githubToken}`); + request.headers.set('Content-Type', `application/json;charset=utf-8`); + request.headers.set('X-GitHub-Api-Version', `2022-11-28`); + request.headers.set('Accept', `application/vnd.github+json`); + request.headers.set('cache','no-store'); + } + ] } - return Promise.reject(error); }); - -export default axiosObj \ No newline at end of file diff --git a/src/utils/optionsStorage.ts b/src/utils/optionsStorage.ts index 80f05a0..fa1b1f8 100644 --- a/src/utils/optionsStorage.ts +++ b/src/utils/optionsStorage.ts @@ -1,5 +1,4 @@ import OptionsSync from 'webext-options-sync'; -import { SettingBase } from './setting' /* global OptionsSync */ export default new OptionsSync({ diff --git a/src/utils/services.ts b/src/utils/services.ts index 1787436..af3a1d5 100644 --- a/src/utils/services.ts +++ b/src/utils/services.ts @@ -1,27 +1,29 @@ import { Setting } from './setting' -import axios from './http' +import { http } from './http' class BookmarkService { async get() { let setting = await Setting.build(); - let resp = await axios.get(`/gists/${setting.gistID}`) - if (resp && resp.data) { - let filenames = Object.keys(resp.data.files); + let resp = await http.get(`gists/${setting.gistID}`).json() as any + if (resp?.files) { + let filenames = Object.keys(resp.files); if (filenames.indexOf(setting.gistFileName) !== -1) { - let gistFile = resp.data.files[setting.gistFileName] + let gistFile = resp.files[setting.gistFileName] if (gistFile.truncated) { - return axios.get(gistFile.raw_url, { responseType: 'blob' }).then(resp => resp.data.text()) + const txt = http.get(gistFile.raw_url).text(); + return txt; } else { return gistFile.content } } } + return resp; } async getAllGist() { - return axios.get('/gists').then(resp => resp.data) + return http.get('gists').json(); } async update(data: any) { let setting = await Setting.build(); - return axios.patch(`/gists/${setting.gistID}`, data).then(resp => resp.data) + return http.patch(`gists/${setting.gistID}`, { json: data }).json(); } } diff --git a/src/utils/setting.ts b/src/utils/setting.ts index 62f0426..171a861 100644 --- a/src/utils/setting.ts +++ b/src/utils/setting.ts @@ -21,3 +21,28 @@ export class Setting extends SettingBase { return setting; } } + + + + +// export class SettingBase { +// constructor() { } +// [key: string]: string | number | boolean; +// githubToken: string = ''; +// gistID: string = ''; +// gistFileName: string = 'BookmarkHub'; +// enableNotify: boolean = true; +// githubURL: string = 'https://api.github.com'; +// } +// export class Setting extends SettingBase { +// private constructor() { super() } +// static async build() { +// let options =new Setting(); +// let setting = new Setting(); +// setting.gistID = options.gistID; +// setting.gistFileName = options.gistFileName; +// setting.githubToken = options.githubToken; +// setting.enableNotify = options.enableNotify; +// return setting; +// } +// } diff --git a/src/views/background/background.html b/src/views/background/background.html deleted file mode 100644 index b284e38..0000000 --- a/src/views/background/background.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - BookmarkHub - sync bookmarks - - - - -
- - - - \ No newline at end of file diff --git a/src/views/background/background.tsx b/src/views/background/background.tsx deleted file mode 100644 index cab4fd6..0000000 --- a/src/views/background/background.tsx +++ /dev/null @@ -1,361 +0,0 @@ -import { browser, Bookmarks } from "webextension-polyfill-ts"; -import BookmarkService from '../../utils/services' -import { Setting } from '../../utils/setting' -import iconLogo from '../../icons/icon128.png' -import { OperType, BookmarkInfo, SyncDataInfo, RootBookmarksType, BrowserType } from '../../utils/models' -let curOperType = OperType.NONE; -let curBrowserType = BrowserType.CHROME; -browser.runtime.onMessage.addListener(async (msg, sender) => { - if (msg.name === 'upload') { - curOperType = OperType.SYNC - await uploadBookmarks(); - curOperType = OperType.NONE - browser.browserAction.setBadgeText({ text: "" }); - refreshLocalCount(); - } - if (msg.name === 'download') { - curOperType = OperType.SYNC - await downloadBookmarks(); - curOperType = OperType.NONE - browser.browserAction.setBadgeText({ text: "" }); - refreshLocalCount(); - } - if (msg.name === 'removeAll') { - curOperType = OperType.REMOVE - await clearBookmarkTree(); - curOperType = OperType.NONE - browser.browserAction.setBadgeText({ text: "" }); - refreshLocalCount(); - } - if (msg.name === 'setting') { - await browser.runtime.openOptionsPage(); - } - return true; -}); -browser.bookmarks.onCreated.addListener((id, info) => { - if (curOperType === OperType.NONE) { - // console.log("onCreated", id, info) - browser.browserAction.setBadgeText({ text: "!" }); - browser.browserAction.setBadgeBackgroundColor({ color: "#F00" }); - refreshLocalCount(); - } -}); -browser.bookmarks.onChanged.addListener((id, info) => { - if (curOperType === OperType.NONE) { - // console.log("onChanged", id, info) - browser.browserAction.setBadgeText({ text: "!" }); - browser.browserAction.setBadgeBackgroundColor({ color: "#F00" }); - } -}) -browser.bookmarks.onMoved.addListener((id, info) => { - if (curOperType === OperType.NONE) { - // console.log("onMoved", id, info) - browser.browserAction.setBadgeText({ text: "!" }); - browser.browserAction.setBadgeBackgroundColor({ color: "#F00" }); - } -}) -browser.bookmarks.onRemoved.addListener((id, info) => { - if (curOperType === OperType.NONE) { - // console.log("onRemoved", id, info) - browser.browserAction.setBadgeText({ text: "!" }); - browser.browserAction.setBadgeBackgroundColor({ color: "#F00" }); - refreshLocalCount(); - } -}) - -async function uploadBookmarks() { - try { - let setting = await Setting.build() - if (setting.githubToken == '') { - throw new Error("Gist Token Not Found"); - } - if (setting.gistID == '') { - throw new Error("Gist ID Not Found"); - } - if (setting.gistFileName == '') { - throw new Error("Gist File Not Found"); - } - let bookmarks = await getBookmarks(); - let syncdata = new SyncDataInfo(); - syncdata.version = browser.runtime.getManifest().version; - syncdata.createDate = Date.now(); - syncdata.bookmarks = formatBookmarks(bookmarks); - syncdata.browser = navigator.userAgent; - await BookmarkService.update(JSON.stringify({ - files: { - [setting.gistFileName]: { - content: JSON.stringify(syncdata) - } - }, - description: setting.gistFileName - })); - const count = getBookmarkCount(syncdata.bookmarks); - await browser.storage.local.set({ remoteCount: count }); - if (setting.enableNotify) { - await browser.notifications.create({ - type: "basic", - iconUrl: iconLogo, - title: browser.i18n.getMessage('uploadBookmarks'), - message: browser.i18n.getMessage('success') - }); - } - - } - catch (error) { - await browser.notifications.create({ - type: "basic", - iconUrl: iconLogo, - title: browser.i18n.getMessage('uploadBookmarks'), - message: `${browser.i18n.getMessage('error')}:${error.message}` - }); - } -} -async function downloadBookmarks() { - try { - let gist = await BookmarkService.get(); - let setting = await Setting.build() - if (gist) { - let syncdata: SyncDataInfo = JSON.parse(gist); - if (syncdata.bookmarks == undefined || syncdata.bookmarks.length == 0) { - if (setting.enableNotify) { - await browser.notifications.create({ - type: "basic", - iconUrl: iconLogo, - title: browser.i18n.getMessage('downloadBookmarks'), - message: `${browser.i18n.getMessage('error')}:Gist File ${setting.gistFileName} is NULL` - }); - } - return; - } - await clearBookmarkTree(); - await createBookmarkTree(syncdata.bookmarks); - const count = getBookmarkCount(syncdata.bookmarks); - await browser.storage.local.set({ remoteCount: count }); - if (setting.enableNotify) { - await browser.notifications.create({ - type: "basic", - iconUrl: iconLogo, - title: browser.i18n.getMessage('downloadBookmarks'), - message: browser.i18n.getMessage('success') - }); - } - } - else { - await browser.notifications.create({ - type: "basic", - iconUrl: iconLogo, - title: browser.i18n.getMessage('downloadBookmarks'), - message: `${browser.i18n.getMessage('error')}:Gist File ${setting.gistFileName} Not Found` - }); - } - } - catch (error) { - await browser.notifications.create({ - type: "basic", - iconUrl: iconLogo, - title: browser.i18n.getMessage('downloadBookmarks'), - message: `${browser.i18n.getMessage('error')}:${error.message}` - }); - } -} - -async function getBookmarks() { - let bookmarkTree: BookmarkInfo[] = await browser.bookmarks.getTree(); - if (bookmarkTree && bookmarkTree[0].id === "root________") { - curBrowserType = BrowserType.FIREFOX; - } - else { - curBrowserType = BrowserType.CHROME; - } - return bookmarkTree; -} - -async function clearBookmarkTree() { - try { - let setting = await Setting.build() - if (setting.githubToken == '') { - throw new Error("Gist Token Not Found"); - } - if (setting.gistID == '') { - throw new Error("Gist ID Not Found"); - } - if (setting.gistFileName == '') { - throw new Error("Gist File Not Found"); - } - let bookmarks = await getBookmarks(); - let tempNodes: BookmarkInfo[] = []; - bookmarks[0].children?.forEach(c => { - c.children?.forEach(d => { - tempNodes.push(d) - }) - }); - if (tempNodes.length > 0) { - for (let node of tempNodes) { - if (node.id) { - await browser.bookmarks.removeTree(node.id) - } - } - } - if (curOperType === OperType.REMOVE && setting.enableNotify) { - await browser.notifications.create({ - type: "basic", - iconUrl: iconLogo, - title: browser.i18n.getMessage('removeAllBookmarks'), - message: browser.i18n.getMessage('success') - }); - } - } - catch (error) { - await browser.notifications.create({ - type: "basic", - iconUrl: iconLogo, - title: browser.i18n.getMessage('removeAllBookmarks'), - message: `${browser.i18n.getMessage('error')}:${error.message}` - }); - } -} - -async function createBookmarkTree(bookmarkList: BookmarkInfo[] | undefined) { - if (bookmarkList == null) { - return; - } - for (let i = 0; i < bookmarkList.length; i++) { - let node = bookmarkList[i]; - if (node.title == RootBookmarksType.MenuFolder - || node.title == RootBookmarksType.MobileFolder - || node.title == RootBookmarksType.ToolbarFolder - || node.title == RootBookmarksType.UnfiledFolder) { - if (curBrowserType == BrowserType.FIREFOX) { - switch (node.title) { - case RootBookmarksType.MenuFolder: - node.children?.forEach(c => c.parentId = "menu________"); - break; - case RootBookmarksType.MobileFolder: - node.children?.forEach(c => c.parentId = "mobile______"); - break; - case RootBookmarksType.ToolbarFolder: - node.children?.forEach(c => c.parentId = "toolbar_____"); - break; - case RootBookmarksType.UnfiledFolder: - node.children?.forEach(c => c.parentId = "unfiled_____"); - break; - default: - node.children?.forEach(c => c.parentId = "unfiled_____"); - break; - } - } else { - switch (node.title) { - case RootBookmarksType.MobileFolder: - node.children?.forEach(c => c.parentId = "3"); - break; - case RootBookmarksType.ToolbarFolder: - node.children?.forEach(c => c.parentId = "1"); - break; - case RootBookmarksType.UnfiledFolder: - case RootBookmarksType.MenuFolder: - node.children?.forEach(c => c.parentId = "2"); - break; - default: - node.children?.forEach(c => c.parentId = "2"); - break; - } - } - await createBookmarkTree(node.children); - continue; - } - - let res: Bookmarks.BookmarkTreeNode = { id: '', title: '' }; - try { - /* 处理firefox中创建 chrome://chrome-urls/ 格式的书签会报错的问题 */ - res = await browser.bookmarks.create({ - parentId: node.parentId, - title: node.title, - url: node.url - }); - } catch (err) { - console.error(res, err); - } - if (res.id && node.children && node.children.length > 0) { - node.children.forEach(c => c.parentId = res.id); - await createBookmarkTree(node.children); - } - } -} - -function getBookmarkCount(bookmarkList: BookmarkInfo[] | undefined) { - let count = 0; - if (bookmarkList) { - bookmarkList.forEach(c => { - if (c.url) { - count = count + 1; - } - else { - count = count + getBookmarkCount(c.children); - } - }); - } - return count; -} - -async function refreshLocalCount() { - let bookmarkList = await getBookmarks(); - const count = getBookmarkCount(bookmarkList); - await browser.storage.local.set({ localCount: count }); -} - - -function formatBookmarks(bookmarks: BookmarkInfo[]): BookmarkInfo[] | undefined { - if (bookmarks[0].children) { - for (let a of bookmarks[0].children) { - switch (a.id) { - case "1": - case "toolbar_____": - a.title = RootBookmarksType.ToolbarFolder; - break; - case "menu________": - a.title = RootBookmarksType.MenuFolder; - break; - case "2": - case "unfiled_____": - a.title = RootBookmarksType.UnfiledFolder; - break; - case "3": - case "mobile______": - a.title = RootBookmarksType.MobileFolder; - break; - } - } - } - - let a = format(bookmarks[0]); - return a.children; -} - -function format(b: BookmarkInfo): BookmarkInfo { - b.dateAdded = undefined; - b.dateGroupModified = undefined; - b.id = undefined; - b.index = undefined; - b.parentId = undefined; - b.type = undefined; - b.unmodifiable = undefined; - if (b.children && b.children.length > 0) { - b.children?.map(c => format(c)) - } - return b; -} -///暂时不启用自动备份 -async function backupToLocalStorage(bookmarks: BookmarkInfo[]) { - try { - let syncdata = new SyncDataInfo(); - syncdata.version = browser.runtime.getManifest().version; - syncdata.createDate = Date.now(); - syncdata.bookmarks = formatBookmarks(bookmarks); - syncdata.browser = navigator.userAgent; - const keyname = 'BookmarkHub_backup_' + Date.now().toString(); - await browser.storage.local.set({ [keyname]: JSON.stringify(syncdata) }); - } - catch (error) { - console.error(error) - } -} \ No newline at end of file diff --git a/src/views/options/options.html b/src/views/options/options.html deleted file mode 100644 index b284e38..0000000 --- a/src/views/options/options.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - BookmarkHub - sync bookmarks - - - - -
- - - - \ No newline at end of file diff --git a/src/views/popup/popup.html b/src/views/popup/popup.html deleted file mode 100644 index b284e38..0000000 --- a/src/views/popup/popup.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - BookmarkHub - sync bookmarks - - - - -
- - - - \ No newline at end of file diff --git a/tools.js b/tools.js deleted file mode 100644 index 8ba3dac..0000000 --- a/tools.js +++ /dev/null @@ -1,71 +0,0 @@ -const path = require('path'); -const glob = require('glob'); -const paths = require('./config/paths'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); - -const allSitePath = (isEnvDevelopment) => { - let entryFiles = glob.sync(paths.appSrc + '/views/*'); - - let map = {}; - entryFiles.forEach((item) => { - let filename = item.substring(item.lastIndexOf('/') + 1); - let filePath = `${item}/${filename}.tsx`; - - map[filename] = [ - isEnvDevelopment && - require.resolve('react-dev-utils/webpackHotDevClient'), - filePath - ].filter(Boolean); - }); - - return map; -} - -const htmlPlugin = (isEnvProduction, isEnvDevelopment) => { - let fileNameLists = Object.keys( - allSitePath(isEnvDevelopment) - ); - - let arr = []; - fileNameLists.forEach(item => { - let filename = item.substring(item.lastIndexOf('/') + 1); - - arr.push( - // Generates an `index.html` file with the