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/README.md b/README.md index bfbcbd1..9a835a1 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,7 @@ It uses GitHub's Gist records to store browser bookmarks for safe and secure use * [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 +* [Other browsers based on the Chromium kernel](https://chrome.google.com/webstore/detail/bookmarkhub-sync-bookmark/fohimdklhhcpcnpmmichieidclgfdmol) ## Usage diff --git a/README_cn.md b/README_cn.md index 3d40414..c0dff1e 100644 --- a/README_cn.md +++ b/README_cn.md @@ -59,8 +59,7 @@ BookmarkHub 是一款浏览器插件,可以在不同浏览器之间同步你 * [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 内核的浏览器 +* [其他基于 Chromium 内核的浏览器](https://chrome.google.com/webstore/detail/bookmarkhub-sync-bookmark/fohimdklhhcpcnpmmichieidclgfdmol) ## 使用方法 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..b456e02 --- /dev/null +++ b/src/entrypoints/popup/index.html @@ -0,0 +1,12 @@ + + + + + + + + +
+ + + diff --git a/src/views/popup/popup.css b/src/entrypoints/popup/popup.css similarity index 94% rename from src/views/popup/popup.css rename to src/entrypoints/popup/popup.css index 131da1a..3dd6472 100644 --- a/src/views/popup/popup.css +++ b/src/entrypoints/popup/popup.css @@ -23,7 +23,7 @@ .dropdown-item, .dropdown-item-text { font-size: 12px !important; - padding: .25rem 1rem !important; + padding: .5rem 1rem !important; vertical-align: middle !important; } 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 cfa7587..0000000 Binary files a/src/icons/icon128.png and /dev/null differ diff --git a/src/icons/icon16.png b/src/icons/icon16.png deleted file mode 100644 index d73809e..0000000 Binary files a/src/icons/icon16.png and /dev/null differ diff --git a/src/icons/icon19.png b/src/icons/icon19.png deleted file mode 100644 index 995bd77..0000000 Binary files a/src/icons/icon19.png and /dev/null differ diff --git a/src/icons/icon48.png b/src/icons/icon48.png deleted file mode 100644 index 986a1bd..0000000 Binary files a/src/icons/icon48.png and /dev/null differ diff --git a/src/icons/icon64.png b/src/icons/icon64.png deleted file mode 100644 index 636e8a3..0000000 Binary files a/src/icons/icon64.png and /dev/null differ diff --git a/src/index.css b/src/index.css deleted file mode 100644 index ec2585e..0000000 --- a/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/src/index.tsx b/src/index.tsx deleted file mode 100644 index ef2edf8..0000000 --- a/src/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; - -ReactDOM.render( - - - , - 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/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..58785cf 100644 --- a/src/utils/http.ts +++ b/src/utils/http.ts @@ -1,40 +1,29 @@ -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', + timeout:60000, + retry:1, + 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