diff --git a/.vscode/settings.json b/.vscode/settings.json
index 2cd5270f..3ae5ff72 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -2,7 +2,7 @@
"editor.formatOnSave": true,
"vetur.validation.template": false,
"editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
+ "source.fixAll.eslint": "explicit"
},
"eslint.validate": ["javascript", "vue", "typescript"]
}
diff --git a/README.md b/README.md
index 57a56689..e322aaed 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,10 @@
# Stylebot
-![CI](https://github.com/ankit/stylebot/workflows/CI/badge.svg)
+![Build](https://github.com/ankit/stylebot/actions/workflows/build.yml/badge.svg)
[![Kofi](https://badgen.net/badge/icon/kofi?icon=kofi&label)](https://ko-fi.com/stylebot)
[![Chrome Webstore Version](https://img.shields.io/chrome-web-store/v/oiaejidbmkiecgbjeifoejpgmdaleoha)](https://chrome.google.com/webstore/detail/stylebot/oiaejidbmkiecgbjeifoejpgmdaleoha)
[![Chrome Users](https://badgen.net/chrome-web-store/users/oiaejidbmkiecgbjeifoejpgmdaleoha)](https://chrome.google.com/webstore/detail/stylebot/oiaejidbmkiecgbjeifoejpgmdaleoha)
[![Webstore Rating](https://img.shields.io/chrome-web-store/stars/oiaejidbmkiecgbjeifoejpgmdaleoha)](https://chrome.google.com/webstore/detail/stylebot/oiaejidbmkiecgbjeifoejpgmdaleoha)
-[![Follow on Twitter](https://badgen.net/twitter/follow/ahujaankit)](https://twitter.com/ahujaankit)
![License](https://img.shields.io/github/license/ankit/stylebot)
Stylebot is a browser extension that lets you change the appearance of the web instantly.
@@ -50,8 +49,7 @@ If you would like to add a new feature to Stylebot or f
- Open `chrome://extensions` page.
- Disable the official Stylebot version.
- Enable the Developer mode.
-- Click Load unpacked extension button
-- Navigate to the project's `dist/` folder
+- Load unpacked `dist/` as extension
### Google Drive Sync
diff --git a/package.json b/package.json
index 27de0991..3d8228dd 100644
--- a/package.json
+++ b/package.json
@@ -59,7 +59,7 @@
"@babel/core": "^7.10.1",
"@babel/plugin-proposal-optional-chaining": "^7.10.1",
"@babel/preset-env": "^7.10.1",
- "@types/chrome": "^0.0.114",
+ "@types/chrome": "^0.0.193",
"@types/dedent": "^0.7.0",
"@types/jest": "^26.0.4",
"@types/lodash": "^4.14.157",
@@ -75,7 +75,6 @@
"bootstrap-vue": "^2.15.0",
"copy-webpack-plugin": "^6.0.1",
"cross-env": "^7.0.2",
- "crx-hotreload": "^1.0.4",
"css-loader": "^3.5.3",
"cssnano": "^4.1.10",
"date-fns": "^2.16.1",
@@ -87,7 +86,6 @@
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-vue": "^6.2.2",
"fibers": "^5.0.0",
- "fork-ts-checker-webpack-plugin": "^5.0.7",
"hotkeys-js": "^3.8.1",
"husky": "^4.2.5",
"jest": "^26.1.0",
diff --git a/src/background/cache.ts b/src/background/cache.ts
deleted file mode 100644
index 6848e9c8..00000000
--- a/src/background/cache.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { defaultOptions } from '@stylebot/settings';
-
-import BackgroundPageStyles from './styles';
-import BackgroundPageOptions from './options';
-
-const init = async (): Promise<{
- styles: BackgroundPageStyles;
- options: BackgroundPageOptions;
-}> => {
- return new Promise(resolve => {
- chrome.storage.local.get(['options', 'styles'], items => {
- let styles: BackgroundPageStyles;
-
- if (items['styles']) {
- styles = new BackgroundPageStyles(items['styles']);
- } else {
- styles = new BackgroundPageStyles({});
- }
-
- let options: BackgroundPageOptions;
-
- if (items['options']) {
- options = new BackgroundPageOptions(items['options']);
- } else {
- options = new BackgroundPageOptions(defaultOptions);
- }
-
- resolve({ styles, options });
- });
- });
-};
-
-export default { init };
diff --git a/src/background/contextmenu.ts b/src/background/contextmenu.ts
index 414367eb..8ab0b318 100644
--- a/src/background/contextmenu.ts
+++ b/src/background/contextmenu.ts
@@ -4,54 +4,32 @@ import { OpenStylebotFromContextMenu } from '@stylebot/types';
import BackgroundPageUtils from './utils';
const CONTEXT_MENU_ID = 'stylebot-contextmenu';
-
-const StyleElementContextMenu = () => {
- chrome.contextMenus.create({
- contexts: ['all'],
- title: t('style_element'),
- parentId: CONTEXT_MENU_ID,
-
- onclick: (_info: chrome.contextMenus.OnClickData, tab: chrome.tabs.Tab) => {
- if (tab.id) {
- const message: OpenStylebotFromContextMenu = {
- name: 'OpenStylebotFromContextMenu',
- };
-
- chrome.tabs.sendMessage(tab.id, message);
- }
- },
- });
-};
-
-const ParentContextMenu = () => {
- chrome.contextMenus.create({
- id: CONTEXT_MENU_ID,
- title: 'Stylebot',
- contexts: ['all'],
- });
-};
-
-const ViewOptionsContextMenu = () => {
- chrome.contextMenus.create({
- contexts: ['all'],
- title: t('view_options'),
- parentId: CONTEXT_MENU_ID,
- onclick: () => {
- chrome.tabs.create({
- active: true,
- url: 'options/index.html',
- });
- },
- });
-};
+const VIEW_OPTIONS_MENU_ITEM_ID = 'view-options';
+const STYLE_ELEMENT_MENU_ITEM_ID = 'style-element';
const ContextMenu = {
init(): void {
this.remove();
- ParentContextMenu();
- StyleElementContextMenu();
- ViewOptionsContextMenu();
+ chrome.contextMenus.create({
+ id: CONTEXT_MENU_ID,
+ title: 'Stylebot',
+ contexts: ['all'],
+ });
+
+ chrome.contextMenus.create({
+ contexts: ['all'],
+ title: t('style_element'),
+ parentId: CONTEXT_MENU_ID,
+ id: STYLE_ELEMENT_MENU_ITEM_ID,
+ });
+
+ chrome.contextMenus.create({
+ contexts: ['all'],
+ title: t('view_options'),
+ parentId: CONTEXT_MENU_ID,
+ id: VIEW_OPTIONS_MENU_ITEM_ID,
+ });
},
update(tab: chrome.tabs.Tab): void {
@@ -64,13 +42,15 @@ const ContextMenu = {
chrome.contextMenus.update(CONTEXT_MENU_ID, {
documentUrlPatterns: [''],
});
- } else {
- // If it isn't a valid url, hide the contextMenu
- // Set the document pattern to foo/*random*
- chrome.contextMenus.update(CONTEXT_MENU_ID, {
- documentUrlPatterns: ['http://foo/' + Math.random()],
- });
+
+ return;
}
+
+ // If it isn't a valid url, hide the contextMenu
+ // Set the document pattern to foo/*random*
+ chrome.contextMenus.update(CONTEXT_MENU_ID, {
+ documentUrlPatterns: ['http://foo/' + Math.random()],
+ });
},
remove(): void {
@@ -78,4 +58,27 @@ const ContextMenu = {
},
};
+chrome.contextMenus.onClicked.addListener((info, tab) => {
+ switch (info.menuItemId) {
+ case STYLE_ELEMENT_MENU_ITEM_ID:
+ if (tab?.id) {
+ const message: OpenStylebotFromContextMenu = {
+ name: 'OpenStylebotFromContextMenu',
+ };
+
+ chrome.tabs.sendMessage(tab.id, message);
+ }
+
+ break;
+
+ case VIEW_OPTIONS_MENU_ITEM_ID:
+ chrome.tabs.create({
+ active: true,
+ url: 'options/index.html',
+ });
+
+ break;
+ }
+});
+
export default ContextMenu;
diff --git a/src/background/index.ts b/src/background/index.ts
index 742f156d..2cbba670 100644
--- a/src/background/index.ts
+++ b/src/background/index.ts
@@ -1,40 +1,19 @@
-import 'crx-hotreload';
+import './listeners';
-import Cache from './cache';
-import Listeners from './listeners';
import ContextMenu from './contextmenu';
import DefaultShortcutUpdate from './default-shortcut-update';
import StylesMetadataUpdate from './styles-metadata-update';
import StylesModifiedTimeUpdate from './styles-modified-time-update';
-import { setNotification } from '@stylebot/utils';
-
(async () => {
await DefaultShortcutUpdate();
await StylesMetadataUpdate();
await StylesModifiedTimeUpdate();
-
- const { styles, options } = await Cache.init();
-
- if (options.get('contextMenu')) {
- ContextMenu.init();
- }
-
- Listeners.init(styles, options);
-
- chrome.browserAction.setBadgeBackgroundColor({
- color: '#555',
- });
})();
-chrome.runtime.onInstalled.addListener(async ({ reason }) => {
- if (reason === 'install') {
- chrome.tabs.create({
- url: 'https://stylebot.dev/help'
- });
-
- setNotification('release/3.1', true);
- }
+chrome.runtime.setUninstallURL('https://stylebot.dev/goodbye');
+chrome.action.setBadgeBackgroundColor({
+ color: '#555',
});
-chrome.runtime.setUninstallURL('https://stylebot.dev/goodbye');
+ContextMenu.init();
diff --git a/src/background/listeners.ts b/src/background/listeners.ts
index 7f7028a3..d8277389 100644
--- a/src/background/listeners.ts
+++ b/src/background/listeners.ts
@@ -1,5 +1,4 @@
import ContextMenu from './contextmenu';
-import BackgroundPageStyles from './styles';
import {
GetCommands,
@@ -24,126 +23,136 @@ import {
RunGoogleDriveSync,
} from './messages';
+import { get as getOption } from './options';
+
import {
TabUpdated,
BackgroundPageMessage,
BackgroundPageMessageResponse,
} from '@stylebot/types';
-import BackgroundPageOptions from './options';
+import { setNotification } from '@stylebot/utils';
/**
- * Initialize listeners for the background page
+ * Open Help page on installation
*/
-const init = (
- styles: BackgroundPageStyles,
- options: BackgroundPageOptions
-): void => {
- chrome.runtime.onMessage.addListener(
- (
- message: BackgroundPageMessage,
- sender: chrome.runtime.MessageSender,
- sendResponse: (response: BackgroundPageMessageResponse) => void
- ) => {
- switch (message.name) {
- case 'GetCommands':
- GetCommands(sendResponse);
- break;
- case 'SetCommands':
- SetCommands(message);
- break;
-
- case 'GetOption':
- GetOption(message, options, sendResponse);
- break;
- case 'SetOption':
- SetOption(message, options);
- break;
- case 'GetAllOptions':
- GetAllOptions(options, sendResponse);
- break;
- case 'OpenOptionsPage':
- OpenOptionsPage();
- break;
- case 'OpenDonatePage':
- OpenDonatePage();
- break;
-
- case 'SetStyle':
- SetStyle(message, styles);
- break;
- case 'MoveStyle':
- MoveStyle(message, styles);
- break;
- case 'GetAllStyles':
- GetAllStyles(styles, sendResponse);
- break;
- case 'SetAllStyles':
- SetAllStyles(message, styles);
- break;
- case 'GetStylesForPage':
- GetStylesForPage(message, styles, sender, sendResponse);
- break;
- case 'GetStylesForIframe':
- GetStylesForIframe(message, styles, sendResponse);
- break;
- case 'EnableStyle':
- EnableStyle(message, styles);
- break;
- case 'DisableStyle':
- DisableStyle(message, styles);
- break;
-
- case 'SetReadability':
- SetReadability(message, styles);
- break;
- case 'GetReadabilitySettings':
- GetReadabilitySettings(sendResponse);
- break;
- case 'SetReadabilitySettings':
- SetReadabilitySettings(message);
- break;
-
- case 'GetImportCss':
- GetImportCss(message, styles, sendResponse);
- break;
- case 'RunGoogleDriveSync':
- RunGoogleDriveSync(message, styles, sendResponse);
- break;
- }
-
- return true;
- }
- );
-
- /**
- * Listen when an existing tab is updated
- * and update the context-menu and browser-action
- */
- chrome.tabs.onUpdated.addListener((tabId, _, tab) => {
- if (tab.status === 'complete') {
- if (options.get('contextMenu')) {
- ContextMenu.update(tab);
- }
- }
+chrome.runtime.onInstalled.addListener(async ({ reason }) => {
+ if (reason === 'install') {
+ chrome.tabs.create({
+ url: 'https://stylebot.dev/help',
+ });
+
+ setNotification('release/3.1', true);
+ }
+});
+
+/**
+ * When an existing tab is updated, refresh the context-menu and action.
+ */
+chrome.tabs.onUpdated.addListener(async (tabId, _, tab) => {
+ const option = await getOption('contextMenu');
+
+ if (option && tab.status === 'complete') {
+ ContextMenu.update(tab);
const message: TabUpdated = {
name: 'TabUpdated',
};
- chrome.tabs.sendMessage(tabId, message);
- });
-
- /**
- * Listen when a tab is activated to update the context-menu
- */
- chrome.tabs.onActivated.addListener(activeInfo => {
- if (options.get('contextMenu')) {
- chrome.tabs.get(activeInfo.tabId, tab => {
- ContextMenu.update(tab);
- });
+ if (!tab.url?.includes('chrome-extension://')) {
+ chrome.tabs.sendMessage(tabId, message);
+ }
+ }
+});
+
+/**
+ * Listen when a tab is activated to refresh the context-menu.
+ */
+chrome.tabs.onActivated.addListener(async activeInfo => {
+ const option = await getOption('contextMenu');
+
+ if (option) {
+ chrome.tabs.get(activeInfo.tabId, tab => {
+ ContextMenu.update(tab);
+ });
+ }
+});
+
+chrome.runtime.onMessage.addListener(
+ (
+ message: BackgroundPageMessage,
+ sender: chrome.runtime.MessageSender,
+ sendResponse: (response: BackgroundPageMessageResponse) => void
+ ) => {
+ switch (message.name) {
+ case 'GetCommands':
+ GetCommands(sendResponse);
+ break;
+ case 'SetCommands':
+ SetCommands(message);
+ break;
+
+ case 'GetOption':
+ GetOption(message, sendResponse);
+ break;
+ case 'SetOption':
+ SetOption(message);
+ break;
+ case 'GetAllOptions':
+ GetAllOptions(sendResponse);
+ break;
+
+ case 'OpenOptionsPage':
+ OpenOptionsPage();
+ break;
+ case 'OpenDonatePage':
+ OpenDonatePage();
+ break;
+
+ case 'SetStyle':
+ SetStyle(message);
+ break;
+ case 'MoveStyle':
+ MoveStyle(message);
+ break;
+ case 'GetAllStyles':
+ GetAllStyles(sendResponse);
+ break;
+ case 'SetAllStyles':
+ SetAllStyles(message);
+ break;
+ case 'GetStylesForPage':
+ GetStylesForPage(message, sender, sendResponse);
+ break;
+ case 'GetStylesForIframe':
+ GetStylesForIframe(message, sendResponse);
+ break;
+ case 'EnableStyle':
+ EnableStyle(message);
+ break;
+ case 'DisableStyle':
+ DisableStyle(message);
+ break;
+
+ case 'SetReadability':
+ SetReadability(message);
+ break;
+ case 'GetReadabilitySettings':
+ GetReadabilitySettings(sendResponse);
+ break;
+ case 'SetReadabilitySettings':
+ SetReadabilitySettings(message);
+ break;
+
+ case 'GetImportCss':
+ GetImportCss(message, sendResponse);
+ break;
+
+ case 'RunGoogleDriveSync':
+ RunGoogleDriveSync(message, sendResponse);
+ break;
}
- });
-};
-export default { init };
+ return true;
+ }
+);
diff --git a/src/background/messages.ts b/src/background/messages.ts
index 75036d44..c118efc9 100644
--- a/src/background/messages.ts
+++ b/src/background/messages.ts
@@ -1,5 +1,22 @@
-import BackgroundPageStyles from './styles';
-import BackgroundPageOptions from './options';
+import {
+ set,
+ disable,
+ enable,
+ getAll,
+ setAll,
+ move,
+ getStylesForPage,
+ updateIcon,
+ setReadability,
+ getImportCss,
+ applyStylesToAllTabs,
+} from './styles';
+
+import {
+ get as getOption,
+ getAll as getAllOptions,
+ set as setOption,
+} from './options';
import {
GetOption as GetOptionType,
@@ -34,87 +51,80 @@ import {
import { get as getCommands, set as setCommands } from './commands';
-export const DisableStyle = (
- message: DisableStyleType,
- styles: BackgroundPageStyles
-): void => {
- styles.disable(message.url);
+export const DisableStyle = async (
+ message: DisableStyleType
+): Promise => {
+ await disable(message.url);
+ return applyStylesToAllTabs();
};
-export const EnableStyle = (
- message: EnableStyleType,
- styles: BackgroundPageStyles
-): void => {
- styles.enable(message.url);
+export const EnableStyle = async (message: EnableStyleType): Promise => {
+ await enable(message.url);
+ return applyStylesToAllTabs();
};
-export const SetStyle = (
- message: SetStyleType,
- styles: BackgroundPageStyles
-): void => {
- styles.set(message.url, message.css, message.readability);
-};
+export const SetStyle = (message: SetStyleType): Promise =>
+ set(message.url, message.css, message.readability);
-export const GetAllStyles = (
- styles: BackgroundPageStyles,
+export const GetAllStyles = async (
sendResponse: (response: GetAllStylesResponse) => void
-): void => {
- sendResponse(styles.getAll());
+): Promise => {
+ const styles = await getAll();
+ sendResponse(styles);
};
-export const SetAllStyles = (
- message: SetAllStylesType,
- styles: BackgroundPageStyles
-): void => {
- styles.setAll(message.styles, message.shouldPersist);
+export const SetAllStyles = async (
+ message: SetAllStylesType
+): Promise => {
+ await setAll(message.styles);
+ return applyStylesToAllTabs();
};
-export const GetStylesForIframe = (
+export const GetStylesForIframe = async (
message: GetStylesForIframeType,
- styles: BackgroundPageStyles,
sendResponse: (response: GetStylesForPageResponse) => void
-): void => {
- sendResponse(styles.getStylesForPage(message.url, message.important));
+): Promise => {
+ const styles = await getAll();
+ const pageStyles = getStylesForPage(message.url, styles, message.important);
+
+ sendResponse(pageStyles);
};
-export const GetStylesForPage = (
+export const GetStylesForPage = async (
message: GetStylesForPageType,
- styles: BackgroundPageStyles,
sender: chrome.runtime.MessageSender,
sendResponse: (response: GetStylesForPageResponse) => void
-): void => {
+): Promise => {
const tab = sender.tab || message.tab;
if (!tab || !tab.url) {
return;
}
- const response = styles.getStylesForPage(tab.url, message.important);
- styles.updateIcon(tab, response.styles, response.defaultStyle);
+ const styles = await getAll();
+ const response = getStylesForPage(tab.url, styles, message.important);
+ updateIcon(tab, response.styles, response.defaultStyle);
sendResponse(response);
};
-export const MoveStyle = (
- message: MoveStyleType,
- styles: BackgroundPageStyles
-): void => {
- styles.move(message.sourceUrl, message.destinationUrl);
+export const MoveStyle = (message: MoveStyleType): void => {
+ move(message.sourceUrl, message.destinationUrl);
};
-export const GetOption = (
+export const GetOption = async (
message: GetOptionType,
- options: BackgroundPageOptions,
sendResponse: (response: GetOptionResponse) => void
-): void => {
- sendResponse(options.get(message.optionName));
+): Promise => {
+ const option = await getOption(message.optionName);
+ sendResponse(option);
};
-export const GetAllOptions = (
- options: BackgroundPageOptions,
+export const GetAllOptions = async (
sendResponse: (response: GetAllOptionsResponse) => void
-): void => {
- sendResponse(options.getAll());
+): Promise => {
+ const options = await getAllOptions();
+ sendResponse(options);
};
export const OpenOptionsPage = (): void => {
@@ -125,11 +135,8 @@ export const OpenDonatePage = (): void => {
chrome.tabs.create({ url: 'https://ko-fi.com/stylebot' });
};
-export const SetOption = (
- message: SetOptionType,
- options: BackgroundPageOptions
-): void => {
- options.set(message.option.name, message.option.value);
+export const SetOption = (message: SetOptionType): void => {
+ setOption(message.option.name, message.option.value);
};
export const GetCommands = async (
@@ -143,11 +150,8 @@ export const SetCommands = (message: SetCommandsType): void => {
setCommands(message.value);
};
-export const SetReadability = (
- message: SetReadabilityType,
- styles: BackgroundPageStyles
-): void => {
- styles.setReadability(message.url, message.value);
+export const SetReadability = (message: SetReadabilityType): void => {
+ setReadability(message.url, message.value);
};
export const GetReadabilitySettings = async (
@@ -165,18 +169,17 @@ export const SetReadabilitySettings = (
export const GetImportCss = async (
message: GetImportCssType,
- styles: BackgroundPageStyles,
+
sendResponse: (response: GetImportCssResponse) => void
): Promise => {
- const css = await styles.getImportCss(message.url);
+ const css = await getImportCss(message.url);
sendResponse(css);
};
export const RunGoogleDriveSync = async (
_message: RunGoogleDriveSyncType,
- styles: BackgroundPageStyles,
sendResponse: (response: RunGoogleDriveSyncResponse) => void
): Promise => {
- await runGoogleDriveSync(styles);
+ await runGoogleDriveSync();
sendResponse();
};
diff --git a/src/background/options.ts b/src/background/options.ts
index 5ba603f8..46e086ff 100644
--- a/src/background/options.ts
+++ b/src/background/options.ts
@@ -1,41 +1,34 @@
-import ContextMenu from './contextmenu';
import { StylebotOptions } from '@stylebot/types';
+import { defaultOptions } from '@stylebot/settings';
-class BackgroundPageOptions {
- options: StylebotOptions;
-
- constructor(options: StylebotOptions) {
- this.options = options;
- }
-
- get(name: keyof StylebotOptions): StylebotOptions[keyof StylebotOptions] {
- return this.options[name];
- }
-
- getAll(): StylebotOptions {
- return this.options;
- }
-
- set(
- name: keyof StylebotOptions,
- value: StylebotOptions[keyof StylebotOptions]
- ): void {
- this.options = {
- ...this.options,
- [name]: value,
- };
-
- chrome.storage.local.set({ options: this.options });
-
- // If the option was contextMenu, update it
- if (name === 'contextMenu') {
- if (value === false) {
- ContextMenu.remove();
+export const getAll = (): Promise =>
+ new Promise(resolve => {
+ chrome.storage.local.get('options', items => {
+ if (items['options']) {
+ resolve(items['options']);
} else {
- ContextMenu.init();
+ resolve(defaultOptions);
}
- }
- }
-}
-
-export default BackgroundPageOptions;
+ });
+ });
+
+export const get = async (
+ name: keyof StylebotOptions
+): Promise => {
+ const options = await getAll();
+ return options[name];
+};
+
+export const set = async (
+ name: keyof StylebotOptions,
+ value: StylebotOptions[keyof StylebotOptions]
+): Promise => {
+ let options = await getAll();
+
+ options = {
+ ...options,
+ [name]: value,
+ };
+
+ chrome.storage.local.set({ options });
+};
diff --git a/src/background/styles.ts b/src/background/styles.ts
index c515fb53..734eaf32 100644
--- a/src/background/styles.ts
+++ b/src/background/styles.ts
@@ -2,6 +2,7 @@ import * as postcss from 'postcss';
import { getCurrentTimestamp } from '@stylebot/utils';
import { appendImportantToDeclarations } from '@stylebot/css';
+
import {
Style,
StyleMap,
@@ -11,211 +12,208 @@ import {
import BackgroundPageUtils from './utils';
-class BackgroundPageStyles {
- styles: StyleMap;
-
- constructor(styles: StyleMap) {
- this.styles = styles;
- }
-
- persistStorage(): void {
- chrome.storage.local.set({
- styles: this.styles,
-
- 'styles-metadata': {
- modifiedTime: getCurrentTimestamp(),
- },
+export const updateIcon = (
+ tab: chrome.tabs.Tab,
+ styles: Array
\ No newline at end of file
+
diff --git a/src/readability/reader.ts b/src/readability/reader.ts
index 7f86e891..59acf976 100644
--- a/src/readability/reader.ts
+++ b/src/readability/reader.ts
@@ -10,7 +10,7 @@ import { ReadabilityArticle } from '@stylebot/types';
import { cacheDocument } from './cache';
const initCss = async (root: ShadowRoot): Promise => {
- const cssUrl = chrome.extension.getURL('readability/index.css');
+ const cssUrl = chrome.runtime.getURL('readability/index.css');
return new Promise(resolve => {
fetch(cssUrl, { method: 'GET' })
diff --git a/src/sync/google-drive/sync.ts b/src/sync/google-drive/sync.ts
index f6e53280..9c0f68cf 100644
--- a/src/sync/google-drive/sync.ts
+++ b/src/sync/google-drive/sync.ts
@@ -15,7 +15,10 @@ import {
downloadSyncFile,
writeSyncFile,
} from './sync-file';
-import BackgroundPageStyles from 'background/styles';
+import {
+ setAll as setAllStyles,
+ getAll as getAllStyles,
+} from '../../background/styles';
const getStylesBlob = (styles: StyleMap) =>
new Blob([JSON.stringify(styles)], { type: 'application/json' });
@@ -43,10 +46,9 @@ const writeToRemote = async (
*/
const writeToLocal = async (
syncMetadata: GoogleDriveSyncMetadata,
- styles: StyleMap,
- backgroundPageStyles: BackgroundPageStyles
+ styles: StyleMap
) => {
- backgroundPageStyles.setAll(styles, false);
+ await setAllStyles(styles);
return setGoogleDriveSyncMetadata({
...syncMetadata,
@@ -59,14 +61,13 @@ const writeToLocal = async (
*/
const merge = async (
accessToken: string,
- syncMetadata: GoogleDriveSyncMetadata,
- backgroundPageStyles: BackgroundPageStyles
+ syncMetadata: GoogleDriveSyncMetadata
) => {
- const localStyles = backgroundPageStyles.getAll();
+ const localStyles = await getAllStyles();
const remoteStyles = await downloadSyncFile(accessToken, syncMetadata.id);
const mergedStyles = mergeStyles(localStyles, remoteStyles);
- await writeToLocal(syncMetadata, mergedStyles, backgroundPageStyles);
+ await writeToLocal(syncMetadata, mergedStyles);
await writeToRemote(accessToken, syncMetadata, mergedStyles);
};
@@ -79,9 +80,8 @@ const merge = async (
* - Else, write remote styles to local
* 4) If local styles' modified timestamp > remote sync timestamp, write local styles to remote.
*/
-export const runGoogleDriveSync = async (
- backgroundPageStyles: BackgroundPageStyles
-): Promise => {
+export const runGoogleDriveSync = async (): Promise => {
+ const styles = await getAllStyles();
const accessToken = await getAccessToken();
const remoteSyncMetadata = await getSyncFileMetadata(accessToken);
@@ -90,7 +90,7 @@ export const runGoogleDriveSync = async (
if (!remoteSyncMetadata) {
console.debug('did not find remote sync file, updating remote...');
- const blob = getStylesBlob(backgroundPageStyles.getAll());
+ const blob = getStylesBlob(styles);
const remoteSyncMetadata = await writeSyncFile(accessToken, blob);
return setGoogleDriveSyncMetadata(remoteSyncMetadata);
}
@@ -99,7 +99,7 @@ export const runGoogleDriveSync = async (
if (!localSyncMetadata) {
console.debug('no local sync metadata found. merging local and remote...');
- return merge(accessToken, remoteSyncMetadata, backgroundPageStyles);
+ return merge(accessToken, remoteSyncMetadata);
}
const localStylesMetadata = await getLocalStylesMetadata();
@@ -122,7 +122,7 @@ export const runGoogleDriveSync = async (
'both local and remote were updated since last sync, merging local and remote...'
);
- return merge(accessToken, remoteSyncMetadata, backgroundPageStyles);
+ return merge(accessToken, remoteSyncMetadata);
}
console.debug('remote was updated since last sync, updating local...');
@@ -131,17 +131,13 @@ export const runGoogleDriveSync = async (
remoteSyncMetadata.id
);
- return writeToLocal(remoteSyncMetadata, remoteStyles, backgroundPageStyles);
+ return writeToLocal(remoteSyncMetadata, remoteStyles);
}
// check if local styles were modified v/s remote
if (compareAsc(localStylesModifiedTime, remoteSyncTime) > 0) {
console.debug('local was updated since last sync, updating remote...');
- return writeToRemote(
- accessToken,
- remoteSyncMetadata,
- backgroundPageStyles.getAll()
- );
+ return writeToRemote(accessToken, remoteSyncMetadata, styles);
}
return setGoogleDriveSyncMetadata({
diff --git a/webpack.config.js b/webpack.config.js
index 707882a9..839c2511 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -8,7 +8,6 @@ const { VueLoaderPlugin } = require('vue-loader');
const CopyPlugin = require('copy-webpack-plugin');
const ExtensionReloader = require('webpack-extension-reloader');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
-const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
@@ -21,6 +20,7 @@ const config = {
stats: 'errors-only',
mode: process.env.NODE_ENV,
context: `${__dirname}/src`,
+ devtool: 'inline-source-map',
optimization: {
minimize: process.env.NODE_ENV === 'production',
@@ -37,17 +37,6 @@ const config = {
],
},
- entry: {
- 'sync/index': './sync/index.ts',
- 'popup/index': './popup/index.ts',
- 'editor/index': './editor/index.ts',
- 'options/index': './options/index.ts',
- 'background/index': './background/index.ts',
- 'inject-css/index': './inject-css/index.ts',
- 'monaco-editor/iframe/index': './monaco-editor/iframe/index.ts',
- 'readability/index': './readability/index.ts',
- },
-
output: {
publicPath: '/',
filename: '[name].js',
@@ -126,18 +115,7 @@ const config = {
plugins: [
new ProgressBarPlugin(),
- new webpack.DefinePlugin({
- global: 'window',
- }),
new VueLoaderPlugin(),
- new ForkTsCheckerWebpackPlugin({
- typescript: {
- configFile: '../tsconfig.json',
- extensions: {
- vue: true,
- },
- },
- }),
new MiniCssExtractPlugin({
filename: '[name].css',
}),
@@ -223,8 +201,9 @@ const config = {
let jsonContent = JSON.parse(content);
if (config.mode === 'development') {
- jsonContent['content_security_policy'] =
- "script-src 'self' 'unsafe-eval'; object-src 'self'";
+ jsonContent['content_security_policy'] = {
+ extension_page: "script-src 'self'; object-src 'self'",
+ };
}
if (process.env.BROWSER === 'firefox') {
@@ -268,4 +247,29 @@ function transformHtml(content) {
});
}
-module.exports = config;
+const backgroundPageConfig = {
+ ...config,
+ entry: {
+ 'background/index': './background/index.ts',
+ },
+ plugins: [
+ new webpack.DefinePlugin({
+ global: 'this',
+ }),
+ ],
+};
+
+const clientConfig = {
+ ...config,
+ entry: {
+ 'sync/index': './sync/index.ts',
+ 'popup/index': './popup/index.ts',
+ 'editor/index': './editor/index.ts',
+ 'options/index': './options/index.ts',
+ 'inject-css/index': './inject-css/index.ts',
+ 'monaco-editor/iframe/index': './monaco-editor/iframe/index.ts',
+ 'readability/index': './readability/index.ts',
+ },
+};
+
+module.exports = [backgroundPageConfig, clientConfig];
diff --git a/yarn.lock b/yarn.lock
index 1d720331..91506aa4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,7 +2,7 @@
# yarn lockfile v1
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.8.3":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
@@ -1165,11 +1165,6 @@
dependencies:
defer-to-connect "^1.0.1"
-"@types/anymatch@*":
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
- integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==
-
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7":
version "7.1.9"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d"
@@ -1203,10 +1198,10 @@
dependencies:
"@babel/types" "^7.3.0"
-"@types/chrome@^0.0.114":
- version "0.0.114"
- resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.114.tgz#8ceb33fa261f4b9e307fa7344ba8182d8d410d4e"
- integrity sha512-i7qRr74IrxHtbnrZSKUuP5Uvd5EOKwlwJq/yp7+yTPihOXnPhNQO4Z5bqb1XTnrjdbUKEJicaVVbhcgtRijmLA==
+"@types/chrome@^0.0.193":
+ version "0.0.193"
+ resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.193.tgz#cd0dc5033f27a243d228aebe566c3ec19ef17e36"
+ integrity sha512-R8C84oqvk8A8C8G1viBd8qLpDr86Y/jwD+KLgzUekbIT9RGds6a9GnlQyg8P7ltnGogTMHkiEQK0ZlcrvTeo3Q==
dependencies:
"@types/filesystem" "*"
"@types/har-format" "*"
@@ -1319,9 +1314,9 @@
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
"@types/source-list-map@*":
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
- integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.6.tgz#164e169dd061795b50b83c19e4d3be09f8d3a454"
+ integrity sha512-5JcVt1u5HDmlXkwOD2nslZVllBBc7HDuOICfiZah2Z0is8M8g+ddAEawbmd3VjedfDHBzxCaXLs07QEmb7y54g==
"@types/stack-utils@^1.0.1":
version "1.0.1"
@@ -1338,10 +1333,10 @@
resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1"
integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==
-"@types/tapable@*":
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74"
- integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==
+"@types/tapable@^1":
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.12.tgz#bc2cab12e87978eee89fb21576b670350d6d86ab"
+ integrity sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==
"@types/tinycolor2@^1.4.2":
version "1.4.2"
@@ -1349,40 +1344,40 @@
integrity sha512-PeHg/AtdW6aaIO2a+98Xj7rWY4KC1E6yOy7AFknJQ7VXUGNrMlyxDFxJo7HqLtjQms/ZhhQX52mLVW/EX3JGOw==
"@types/uglify-js@*":
- version "3.9.3"
- resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.9.3.tgz#d94ed608e295bc5424c9600e6b8565407b6b4b6b"
- integrity sha512-KswB5C7Kwduwjj04Ykz+AjvPcfgv/37Za24O2EDzYNbwyzOo8+ydtvzUfZ5UMguiVu29Gx44l1A6VsPPcmYu9w==
+ version "3.17.5"
+ resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.5.tgz#905ce03a3cbbf2e31cbefcbc68d15497ee2e17df"
+ integrity sha512-TU+fZFBTBcXj/GpDpDaBmgWk/gn96kMZ+uocaFUlV2f8a6WdMzzI44QBCmGcCiYR0Y6ZlNRiyUyKKt5nl/lbzQ==
dependencies:
source-map "^0.6.1"
"@types/webpack-sources@*":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-1.4.0.tgz#e58f1f05f87d39a5c64cf85705bdbdbb94d4d57e"
- integrity sha512-c88dKrpSle9BtTqR6ifdaxu1Lvjsl3C5OsfvuUbUwdXymshv1TkufUAXBajCCUM/f/TmnkZC/Esb03MinzSiXQ==
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.3.tgz#b667bd13e9fa15a9c26603dce502c7985418c3d8"
+ integrity sha512-4nZOdMwSPHZ4pTEZzSp0AsTM4K7Qmu40UKW4tJDiOVs20UzYF9l+qUe4s0ftfN0pin06n+5cWWDJXH+sbhAiDw==
dependencies:
"@types/node" "*"
"@types/source-list-map" "*"
source-map "^0.7.3"
"@types/webpack-sources@^0.1.5":
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.8.tgz#078d75410435993ec8a0a2855e88706f3f751f81"
- integrity sha512-JHB2/xZlXOjzjBB6fMOpH1eQAfsrpqVVIbneE0Rok16WXwFaznaI5vfg75U5WgGJm7V9W1c4xeRQDjX/zwvghA==
+ version "0.1.12"
+ resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.12.tgz#9beb82c5dc5483c0fb947da1723f4044b07f6204"
+ integrity sha512-+vRVqE3LzMLLVPgZHUeI8k1YmvgEky+MOir5fQhKvFxpB8uZ0CFnGqxkRAmf8jvNhUBQzhuGZpIMNWZDeEyDIA==
dependencies:
"@types/node" "*"
"@types/source-list-map" "*"
source-map "^0.6.1"
"@types/webpack@^4.39.8":
- version "4.41.21"
- resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.21.tgz#cc685b332c33f153bb2f5fc1fa3ac8adeb592dee"
- integrity sha512-2j9WVnNrr/8PLAB5csW44xzQSJwS26aOnICsP3pSGCEdsu6KYtfQ6QJsVUKHWRnm1bL7HziJsfh5fHqth87yKA==
+ version "4.41.38"
+ resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.38.tgz#5a40ac81bdd052bf405e8bdcf3e1236f6db6dc26"
+ integrity sha512-oOW7E931XJU1mVfCnxCVgv8GLFL768pDO5u2Gzk82i8yTIgX6i7cntyZOkZYb/JtYM8252SN9bQp9tgkVDSsRw==
dependencies:
- "@types/anymatch" "*"
"@types/node" "*"
- "@types/tapable" "*"
+ "@types/tapable" "^1"
"@types/uglify-js" "*"
"@types/webpack-sources" "*"
+ anymatch "^3.0.0"
source-map "^0.6.0"
"@types/yargs-parser@*":
@@ -1903,6 +1898,14 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
+anymatch@^3.0.0:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
+ integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
+ dependencies:
+ normalize-path "^3.0.0"
+ picomatch "^2.0.4"
+
anymatch@^3.0.3, anymatch@~3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
@@ -3383,11 +3386,6 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2:
shebang-command "^2.0.0"
which "^2.0.1"
-crx-hotreload@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/crx-hotreload/-/crx-hotreload-1.0.4.tgz#227b1a8aa2ddfc5c29075ad2ee3e3bc37f7a239b"
- integrity sha512-3mJMMbjoLhHmhNubyIPKmgLvr9V6UZ/yKaZW1WKb2n7yHs1R10VVCAUgQ00amR6fTmM/aiEvZHx+jzhSSf0CGA==
-
crypto-browserify@^3.11.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
@@ -4857,22 +4855,6 @@ forever-agent@~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
-fork-ts-checker-webpack-plugin@^5.0.7:
- version "5.0.7"
- resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.0.7.tgz#a462bd863bbd654bad14a25518006ab99b7b13a0"
- integrity sha512-X7zHKTx4is5YSKa9bDiXGzv8v5K3bsUrZlZgr3F8DeCVsb3oik/+0Mo+K138Sdjh4mpzoHcuUgsrIgZeLIXovw==
- dependencies:
- "@babel/code-frame" "^7.8.3"
- chalk "^2.4.1"
- cosmiconfig "^6.0.0"
- deepmerge "^4.2.2"
- fs-extra "^9.0.0"
- memfs "^3.1.2"
- minimatch "^3.0.4"
- schema-utils "1.0.0"
- semver "^5.6.0"
- tapable "^1.0.0"
-
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@@ -4902,7 +4884,7 @@ fs-constants@^1.0.0:
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
-fs-extra@9.0.1, fs-extra@^9.0.0:
+fs-extra@9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.1.tgz#910da0062437ba4c39fedd863f1675ccfefcb9fc"
integrity sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==
@@ -4937,11 +4919,6 @@ fs-minipass@^2.0.0:
dependencies:
minipass "^3.0.0"
-fs-monkey@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.1.tgz#4a82f36944365e619f4454d9fff106553067b781"
- integrity sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==
-
fs-write-stream-atomic@^1.0.8:
version "1.0.10"
resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
@@ -7064,13 +7041,6 @@ mem@^5.0.0:
mimic-fn "^2.1.0"
p-is-promise "^2.1.0"
-memfs@^3.1.2:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.2.0.tgz#f9438e622b5acd1daa8a4ae160c496fdd1325b26"
- integrity sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==
- dependencies:
- fs-monkey "1.0.1"
-
memory-fs@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@@ -9191,7 +9161,7 @@ saxes@^5.0.0:
dependencies:
xmlchars "^2.2.0"
-schema-utils@1.0.0, schema-utils@^1.0.0:
+schema-utils@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
@@ -10923,11 +10893,16 @@ write@1.0.3:
dependencies:
mkdirp "^0.5.1"
-ws@7.3.1, ws@^7.2.0, ws@^7.2.3:
+ws@7.3.1, ws@^7.2.3:
version "7.3.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
+ws@^7.2.0:
+ version "7.5.9"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
+ integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
+
xdg-basedir@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"