From 9b37d396a760fd984ecb6ba1e07e85c811282b95 Mon Sep 17 00:00:00 2001 From: Ryan Thaut Date: Mon, 27 May 2024 20:30:25 -0600 Subject: [PATCH] Chore: Code Linting & Formatting (#412) * (WIP) update+configure prettier, eslint, and plugins/configs * code linting changes * remove webextension-toolbox.config file --- .babelrc | 5 ++ .editorconfig | 3 + .eslintrc.json | 8 +++ app/scripts/background.js | 8 +-- app/scripts/background/commands.js | 8 +-- app/scripts/background/menus.js | 12 ++-- app/scripts/background/popout.js | 74 ++++++++++---------- app/scripts/background/runtime.js | 6 +- app/scripts/background/tabs.js | 26 +++---- app/scripts/background/webRequest.js | 8 +-- app/scripts/content.js | 12 ++-- app/scripts/content/YouTubeCustomControls.js | 59 ++++++++-------- app/scripts/content/YouTubePopoutPlayer.js | 16 ++--- app/scripts/helpers/options.js | 6 +- app/scripts/helpers/utils.js | 4 +- app/scripts/helpers/youtube.js | 8 +-- app/scripts/options/stores/optionsStore.js | 32 +++------ package.json | 17 +++-- prettier.config.js | 31 ++++++++ webextension-toolbox.config.js | 47 ------------- 20 files changed, 191 insertions(+), 199 deletions(-) create mode 100644 .babelrc create mode 100644 .eslintrc.json create mode 100644 prettier.config.js delete mode 100644 webextension-toolbox.config.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..1d31ebc --- /dev/null +++ b/.babelrc @@ -0,0 +1,5 @@ +{ + "presets": [ + "@babel/preset-react" + ] +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index 1c86c69..66c9b28 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,3 +15,6 @@ insert_final_newline = false [*.{pem,pub}] insert_final_newline = false + +[*.css] +tab_width = 4 diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..4fe2f84 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,8 @@ +{ + "root": true, + "extends": [ + "prettier" + ], + "plugins": [], + "rules": {} +} \ No newline at end of file diff --git a/app/scripts/background.js b/app/scripts/background.js index 7be5117..4b00b76 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -8,8 +8,8 @@ import { OnBeforeSendHeaders, OnSendHeaders, } from "./background/webRequest"; -import { IsVideoURL } from "./helpers/youtube"; import Options from "./helpers/options"; +import { IsVideoURL } from "./helpers/youtube"; browser.browserAction.onClicked.addListener(() => { if (browser.runtime.openOptionsPage) { @@ -18,7 +18,7 @@ browser.browserAction.onClicked.addListener(() => { browser.management .getSelf() .then(({ optionsUrl: url }) => - browser.windows.create({ url, type: "popup" }) + browser.windows.create({ url, type: "popup" }), ); } }); @@ -32,13 +32,13 @@ browser.runtime.onMessage.addListener(OnRuntimeMessage); browser.webRequest.onBeforeSendHeaders.addListener( OnBeforeSendHeaders, GetFilter("onBeforeSendHeaders"), - GetExtraInfoSpec("onBeforeSendHeaders") + GetExtraInfoSpec("onBeforeSendHeaders"), ); browser.webRequest.onSendHeaders.addListener( OnSendHeaders, GetFilter("onSendHeaders"), - GetExtraInfoSpec("onSendHeaders") + GetExtraInfoSpec("onSendHeaders"), ); browser.tabs.onUpdated.addListener(async (tabId, changeInfo) => { diff --git a/app/scripts/background/commands.js b/app/scripts/background/commands.js index a594714..b1212cd 100644 --- a/app/scripts/background/commands.js +++ b/app/scripts/background/commands.js @@ -22,7 +22,7 @@ export const OnCommandEventHandler = async (command) => { case "open-popout-force-close-command": console.log( - "[Background] Instructing active tab to open popout player and close original tab" + "[Background] Instructing active tab to open popout player and close original tab", ); SendMessageToActiveTab({ action: "open-popout-via-command", @@ -35,7 +35,7 @@ export const OnCommandEventHandler = async (command) => { case "open-popout-no-close-command": console.log( - "[Background] Instructing active tab to open popout player without closing original tab" + "[Background] Instructing active tab to open popout player without closing original tab", ); SendMessageToActiveTab({ action: "open-popout-via-command", @@ -48,7 +48,7 @@ export const OnCommandEventHandler = async (command) => { case "rotate-video-left": console.log( - "[Background] Instructing active tab to rotate video to the left (by -90 degrees)" + "[Background] Instructing active tab to rotate video to the left (by -90 degrees)", ); SendMessageToActiveTab({ action: "rotate-video-player", @@ -60,7 +60,7 @@ export const OnCommandEventHandler = async (command) => { case "rotate-video-right": console.log( - "[Background] Instructing active tab to rotate video to the right (by +90 degrees)" + "[Background] Instructing active tab to rotate video to the right (by +90 degrees)", ); SendMessageToActiveTab({ action: "rotate-video-player", diff --git a/app/scripts/background/menus.js b/app/scripts/background/menus.js index a874bcc..7531527 100644 --- a/app/scripts/background/menus.js +++ b/app/scripts/background/menus.js @@ -1,11 +1,9 @@ -import { OpenPopoutBackgroundHelper } from "./popout"; - import { - YOUTUBE_VIDEO_URL_PATTERNS, YOUTUBE_PLAYLIST_URL_PATTERNS, + YOUTUBE_VIDEO_URL_PATTERNS, } from "../helpers/constants"; - import Options from "../helpers/options"; +import { OpenPopoutBackgroundHelper } from "./popout"; export const GetMenus = async () => { const menus = [ @@ -27,12 +25,12 @@ export const GetMenus = async () => { const showRotationMenus = await Options.GetLocalOption( "behavior", - "showRotationMenus" + "showRotationMenus", ); if (showRotationMenus !== false) { menus.push({ title: browser.i18n.getMessage( - "LinkContextMenuEntry_OpenVideoRotateLeft_Text" + "LinkContextMenuEntry_OpenVideoRotateLeft_Text", ), contexts: ["link"], targetUrlPatterns: YOUTUBE_VIDEO_URL_PATTERNS, @@ -41,7 +39,7 @@ export const GetMenus = async () => { }); menus.push({ title: browser.i18n.getMessage( - "LinkContextMenuEntry_OpenVideoRotateRight_Text" + "LinkContextMenuEntry_OpenVideoRotateRight_Text", ), contexts: ["link"], targetUrlPatterns: YOUTUBE_VIDEO_URL_PATTERNS, diff --git a/app/scripts/background/popout.js b/app/scripts/background/popout.js index 24cda85..f9ab52a 100644 --- a/app/scripts/background/popout.js +++ b/app/scripts/background/popout.js @@ -1,19 +1,19 @@ import { - START_THRESHOLD, OPTION_DEFAULTS, + START_THRESHOLD, YOUTUBE_EMBED_URL, YOUTUBE_NOCOOKIE_EMBED_URL, } from "../helpers/constants"; import Options from "../helpers/options"; import { GetDimensionForScreenPercentage, IsFirefox } from "../helpers/utils"; -import { GetVideoIDFromURL, GetPlaylistIDFromURL } from "../helpers/youtube"; +import { GetPlaylistIDFromURL, GetVideoIDFromURL } from "../helpers/youtube"; +import { ShowBasicNotification } from "./notifications"; import { AddContextualIdentityToDataObject, CloseTab, GetActiveTab, GetPopoutPlayerTabs, } from "./tabs"; -import { ShowBasicNotification } from "./notifications"; const WIDTH_PADDING = 16; // TODO: find a way to calculate this (or make it configurable) const HEIGHT_PADDING = 40; // TODO: find a way to calculate this (or make it configurable) @@ -32,7 +32,7 @@ export const OpenPopoutBackgroundHelper = async ( tabId = -1, allowCloseTab = true, allowCloseTabOnAnyDomain = false, - rotation = 0 + rotation = 0, ) => { console.log("[Background] OpenPopoutBackgroundHelper()", { url, @@ -137,7 +137,7 @@ export const OpenPopoutPlayer = async ({ default: console.warn( '[Background] OpenPopoutPlayer() :: Invalid value for "behavior.controls" option', - behavior.controls + behavior.controls, ); // use values for "standard" configuration params.controls = 1; @@ -147,7 +147,7 @@ export const OpenPopoutPlayer = async ({ if (time <= START_THRESHOLD) { console.info( - "[Background] OpenPopoutPlayer() :: Popout video will start from beginning" + "[Background] OpenPopoutPlayer() :: Popout video will start from beginning", ); time = 0; } @@ -159,7 +159,7 @@ export const OpenPopoutPlayer = async ({ // as a workaround, get the video IDs from the DOM and make a manual playlist from them console.log( - "Watch Later playlist detected; attempting to convert to manual playlist" + "Watch Later playlist detected; attempting to convert to manual playlist", ); try { @@ -204,22 +204,22 @@ export const OpenPopoutPlayer = async ({ const reuseExistingWindowsTabs = await Options.GetLocalOption( "behavior", - "reuseWindowsTabs" + "reuseWindowsTabs", ); if (reuseExistingWindowsTabs) { const tabs = await GetPopoutPlayerTabs(originTabId); if (tabs.length < 1) { console.log( - "[Background] OpenPopoutPlayer() :: No existing popout player tabs found" + "[Background] OpenPopoutPlayer() :: No existing popout player tabs found", ); } else { console.log( "[Background] OpenPopoutPlayer() :: Re-using existing popout player tab(s)", - tabs + tabs, ); result = await Promise.all( - tabs.map((tab) => browser.tabs.update(tab.id, { url })) + tabs.map((tab) => browser.tabs.update(tab.id, { url })), ); console.log("[Background] OpenPopoutPlayer() :: Return", result); return result; @@ -228,7 +228,7 @@ export const OpenPopoutPlayer = async ({ const openInBackground = await Options.GetLocalOption( "advanced", - "background" + "background", ); switch (behavior.target.toLowerCase()) { @@ -243,7 +243,7 @@ export const OpenPopoutPlayer = async ({ openInBackground, originTabId, originalVideoWidth, - originalVideoHeight + originalVideoHeight, ); break; } @@ -262,7 +262,7 @@ export const OpenPopoutPlayer = async ({ export const OpenPopoutPlayerInTab = async ( url, active = true, - originTabId = -1 + originTabId = -1, ) => { console.log("[Background] OpenPopoutPlayerInTab()", url, active, originTabId); @@ -271,12 +271,12 @@ export const OpenPopoutPlayerInTab = async ( url, active, }, - originTabId + originTabId, ); console.log( "[Background] OpenPopoutPlayerInTab() :: Creating tab", - createData + createData, ); const tab = await browser.tabs.create(createData); @@ -298,7 +298,7 @@ export const OpenPopoutPlayerInWindow = async ( openInBackground = false, originTabId = -1, originalVideoWidth = OPTION_DEFAULTS.size.width, - originalVideoHeight = OPTION_DEFAULTS.size.height + originalVideoHeight = OPTION_DEFAULTS.size.height, ) => { console.log( "[Background] OpenPopoutPlayerInWindow()", @@ -306,12 +306,12 @@ export const OpenPopoutPlayerInWindow = async ( openInBackground, originTabId, originalVideoWidth, - originalVideoHeight + originalVideoHeight, ); const dimensions = await GetDimensionsForPopoutPlayerWindow( originalVideoWidth, - originalVideoHeight + originalVideoHeight, ); const createData = await AddContextualIdentityToDataObject( @@ -321,7 +321,7 @@ export const OpenPopoutPlayerInWindow = async ( type: "popup", ...dimensions, }, - originTabId + originTabId, ); const isFirefox = await IsFirefox(); @@ -332,7 +332,7 @@ export const OpenPopoutPlayerInWindow = async ( console.log( "[Background] OpenPopoutPlayerInWindow() :: Creating popout player window", - createData + createData, ); let window = await browser.windows.create(createData); @@ -343,14 +343,14 @@ export const OpenPopoutPlayerInWindow = async ( if (!isNaN(position?.top) && !isNaN(position?.left)) { console.log( "[Background] OpenPopoutPlayerInWindow() :: Positioning popout player window", - position + position, ); window = await browser.windows.update(window.id, position); } if ((await Options.GetLocalOption("size", "mode")) === "maximized") { console.log( - "[Background] OpenPopoutPlayerInWindow() :: Maximizing popout player window" + "[Background] OpenPopoutPlayerInWindow() :: Maximizing popout player window", ); window = await browser.windows.update(window.id, { state: "maximized", @@ -359,7 +359,7 @@ export const OpenPopoutPlayerInWindow = async ( if (!isNaN(originTabId) && parseInt(originTabId, 10) > 0) { // try to move the original window back to the foreground console.log( - "[Background] OpenPopoutPlayerInWindow() :: Moving original window to foreground" + "[Background] OpenPopoutPlayerInWindow() :: Moving original window to foreground", ); const { windowId: originWindowId } = await browser.tabs.get(originTabId); await browser.windows.update(originWindowId, { @@ -369,7 +369,7 @@ export const OpenPopoutPlayerInWindow = async ( // fallback: minimize the popout player window console.warn( "[Background] OpenPopoutPlayerInWindow() :: Missing/Invalid ID for original tab", - originTabId + originTabId, ); window = await browser.windows.update(window.id, { focused: false, @@ -418,12 +418,12 @@ export const GetUrlForPopoutPlayer = async (id = null, params = null) => { */ export const GetDimensionsForPopoutPlayerWindow = async ( originalVideoWidth, - originalVideoHeight + originalVideoHeight, ) => { console.log( "[Background] GetDimensionsForPopoutPlayerWindow()", originalVideoWidth, - originalVideoHeight + originalVideoHeight, ); let width = originalVideoWidth ?? OPTION_DEFAULTS.size.width; @@ -432,7 +432,7 @@ export const GetDimensionsForPopoutPlayerWindow = async ( const size = await Options.GetLocalOptionsForDomain("size"); console.log( "[Background] GetDimensionsForPopoutPlayerWindow() :: Size options", - size + size, ); if (size.mode.toLowerCase() !== "current") { @@ -450,7 +450,7 @@ export const GetDimensionsForPopoutPlayerWindow = async ( default: console.warn( '[Background] OpenPopoutPlayerInWindow() :: Invalid value for "size.units" option', - size.units + size.units, ); // do nothing; use the original video player's dimensions instead break; @@ -478,12 +478,12 @@ export const GetPositionForPopoutPlayerWindow = async () => { const position = await Options.GetLocalOptionsForDomain("position"); console.log( "[Background] GetDimensionsForPopoutPlayerWindow() :: Position options", - position + position, ); if (position.mode.toLowerCase() === "auto") { console.log( - '[Background] GetDimensionsForPopoutPlayerWindow() :: Position mode is "auto" - returning empty position values' + '[Background] GetDimensionsForPopoutPlayerWindow() :: Position mode is "auto" - returning empty position values', ); return { top: null, @@ -513,7 +513,7 @@ export const StoreDimensionsAndPosition = async ({ const target = await Options.GetLocalOption("behavior", "target"); if (target.toLowerCase() !== "window") { console.log( - '[Background] GetDimensionsForPopoutPlayerWindow() :: Behavior target is not "window" - skipping dimensions and position storage' + '[Background] GetDimensionsForPopoutPlayerWindow() :: Behavior target is not "window" - skipping dimensions and position storage', ); return; } @@ -525,7 +525,7 @@ export const StoreDimensionsAndPosition = async ({ { sizeMode, positionMode, - } + }, ); if (sizeMode.toLowerCase() === "previous") { @@ -536,13 +536,13 @@ export const StoreDimensionsAndPosition = async ({ }; console.log( "[Background] StoreDimensionsAndPosition() :: Saving size", - size + size, ); Options.SetLocalOptionsForDomain("size", size); } else { console.warn( "[Background] StoreDimensionsAndPosition() :: Missing or invalid dimensions values", - dimensions + dimensions, ); } } @@ -551,13 +551,13 @@ export const StoreDimensionsAndPosition = async ({ if (!isNaN(position?.top) && !isNaN(position?.left)) { console.log( "[Background] StoreDimensionsAndPosition() :: Saving Position", - position + position, ); Options.SetLocalOptionsForDomain("position", position); } else { console.warn( "[Background] StoreDimensionsAndPosition() :: Missing or invalid position values", - position + position, ); } } diff --git a/app/scripts/background/runtime.js b/app/scripts/background/runtime.js index ac3d287..db003b8 100644 --- a/app/scripts/background/runtime.js +++ b/app/scripts/background/runtime.js @@ -7,12 +7,12 @@ export const OnInstalled = async (details) => { if (details.reason === "install") { console.log( - "[Background] Extension Installed :: Initializing defaults for all options" + "[Background] Extension Installed :: Initializing defaults for all options", ); Options.InitLocalStorageDefaults(); } else if (details.reason === "update") { console.log( - "[Background] Extension Updated :: Initializing defaults for new options" + "[Background] Extension Updated :: Initializing defaults for new options", ); Options.InitLocalStorageDefaults(false); } @@ -42,7 +42,7 @@ export const OnRuntimeMessage = async (message, sender) => { console.log( "[Background] Runtime Message :: Unhandled action", - message.action + message.action, ); return; } diff --git a/app/scripts/background/tabs.js b/app/scripts/background/tabs.js index 1f71dc2..c48cda8 100644 --- a/app/scripts/background/tabs.js +++ b/app/scripts/background/tabs.js @@ -20,7 +20,7 @@ export const CloseTab = async (tabId, enforceDomainRestriction = false) => { // if the "tabs" permission is not granted, the tab object does NOT include the `url` property // we cannot request that permission now, as we might be outside a synchronous user input handler console.error( - '[Background] CloseTab() :: Unable to determine if original window/tab should be closed (likely due to the "tabs" permission not being granted)' + '[Background] CloseTab() :: Unable to determine if original window/tab should be closed (likely due to the "tabs" permission not being granted)', ); return false; } @@ -30,7 +30,7 @@ export const CloseTab = async (tabId, enforceDomainRestriction = false) => { if (!YOUTUBE_DOMAINS.includes(domain)) { console.info( - "[Background] CloseTab() :: Original tab is NOT a YouTube domain" + "[Background] CloseTab() :: Original tab is NOT a YouTube domain", ); return false; } @@ -58,7 +58,7 @@ export const SendMessageToActiveTab = async (message) => { }); return await Promise.all( - tabs.map((tab) => browser.tabs.sendMessage(tab.id, message)) + tabs.map((tab) => browser.tabs.sendMessage(tab.id, message)), ); }; @@ -84,11 +84,11 @@ export const GetPopoutPlayerTabs = async (originTabId = -1) => await AddContextualIdentityToDataObject( { url: [YOUTUBE_EMBED_URL, YOUTUBE_NOCOOKIE_EMBED_URL].map( - (url) => url + "*?*popout=1*" + (url) => url + "*?*popout=1*", ), }, - originTabId - ) + originTabId, + ), ); /** @@ -108,14 +108,14 @@ export const GetCookieStoreIDForTab = async (tabId) => { if (!Object.prototype.hasOwnProperty.call(tab, "cookieStoreId")) { console.warn( - "[Background] GetCookieStoreIDForTab() :: Tab data is missing cookieStoreId property" + "[Background] GetCookieStoreIDForTab() :: Tab data is missing cookieStoreId property", ); return null; } console.log( "[Background] GetCookieStoreIDForTab() :: Return", - tab.cookieStoreId + tab.cookieStoreId, ); return tab.cookieStoreId; }; @@ -128,13 +128,13 @@ export const GetCookieStoreIDForTab = async (tabId) => { */ export const AddContextualIdentityToDataObject = async ( data = {}, - originTabId = -1 + originTabId = -1, ) => { try { const isFirefox = await IsFirefox(); const useContextualIdentity = await Options.GetLocalOption( "advanced", - "contextualIdentity" + "contextualIdentity", ); if (isFirefox && useContextualIdentity) { @@ -143,18 +143,18 @@ export const AddContextualIdentityToDataObject = async ( data.cookieStoreId = cookieStoreId; console.log( "[Background] AddContextualIdentityToObjectData() :: Added cookie store ID to window/tab data object", - data + data, ); } else { console.warn( - "[Background] AddContextualIdentityToObjectData() :: Failed to get cookie store ID from original tab" + "[Background] AddContextualIdentityToObjectData() :: Failed to get cookie store ID from original tab", ); } } } catch (error) { console.error( "Failed to add contextual identity to window/tab data object", - error + error, ); } diff --git a/app/scripts/background/webRequest.js b/app/scripts/background/webRequest.js index 439ed8d..4d2cf93 100644 --- a/app/scripts/background/webRequest.js +++ b/app/scripts/background/webRequest.js @@ -43,7 +43,7 @@ export const GetExtraInfoSpec = (eventName) => { if ( Object.prototype.hasOwnProperty.call( chrome?.webRequest?.OnBeforeSendHeadersOptions, - "EXTRA_HEADERS" + "EXTRA_HEADERS", ) ) { extraInfoSpec.push("extraHeaders"); @@ -78,14 +78,14 @@ export const OnBeforeSendHeaders = (details) => { const HasHeader = (name) => requestHeaders.some( - (header) => header.name.toLowerCase() === name.toLowerCase() + (header) => header.name.toLowerCase() === name.toLowerCase(), ); const AddHeader = (header) => { if (!HasHeader(header.name)) { console.log( `[Background] OnBeforeSendHeaders() :: Setting "${header.name}" header`, - header + header, ); requestHeaders.push(header); } @@ -95,7 +95,7 @@ export const OnBeforeSendHeaders = (details) => { if (parseInt(url.searchParams.get("popout"), 10) === 1) { console.log( "[Background] OnBeforeSendHeaders() :: Request is for popout player", - url + url, ); // the `Referer` header is required to avoid the "Video unavailable" error in the popout player diff --git a/app/scripts/content.js b/app/scripts/content.js index d739395..b3441d6 100644 --- a/app/scripts/content.js +++ b/app/scripts/content.js @@ -1,10 +1,10 @@ +import { InsertControlsAndWatch } from "./content/YouTubeCustomControls"; import { GetVideoPlayerInfo, OpenPopoutForPageVideo, - RotateVideoPlayer, PauseVideoPlayer, + RotateVideoPlayer, } from "./content/YouTubePopoutPlayer"; -import { InsertControlsAndWatch } from "./content/YouTubeCustomControls"; import { debounce, IsPopoutPlayer } from "./helpers/utils"; import { GetPlaylistVideoIDsFromDOM } from "./helpers/youtube"; @@ -24,7 +24,7 @@ export const CloseTab = async (enforceDomainRestriction = true) => { if (response !== undefined) { console.log( '[Content] YouTubePopoutPlayer CloseTab() :: Action "close-tab" response', - response + response, ); } } catch (error) { @@ -66,7 +66,7 @@ const OnRuntimeMessage = async (message, sender) => { } console.log( - "[Content] YouTubePopoutPlayer Runtime Message :: Unhandled Action" + "[Content] YouTubePopoutPlayer Runtime Message :: Unhandled Action", ); return; } @@ -82,7 +82,7 @@ const RegisterEventListeners = () => { "resize", debounce(() => { SendWindowDimensionsAndPosition("popout-resized"); - }, 400) + }, 400), ); // alternative to window close event listener (as recommended by MDN), @@ -129,7 +129,7 @@ const SendWindowDimensionsAndPosition = async (action) => { if (query.has("rotation")) { document.documentElement.setAttribute( "data-ytp-rotation", - parseInt(query.get("rotation"), 10) + parseInt(query.get("rotation"), 10), ); } } diff --git a/app/scripts/content/YouTubeCustomControls.js b/app/scripts/content/YouTubeCustomControls.js index 46c1749..5c43de5 100644 --- a/app/scripts/content/YouTubeCustomControls.js +++ b/app/scripts/content/YouTubeCustomControls.js @@ -1,11 +1,10 @@ +import { CloseTab } from "../content"; +import Options from "../helpers/options"; +import { IsPopoutPlayer } from "../helpers/utils"; import { OpenPopoutForPageVideo, RotateVideoPlayer, } from "./YouTubePopoutPlayer"; -import { CloseTab } from "../content"; - -import Options from "../helpers/options"; -import { IsPopoutPlayer } from "../helpers/utils"; /** * Click event handler for the context menu entry and the controls button @@ -53,7 +52,7 @@ export const WatchForPageChanges = () => { case "ytp-popup ytp-contextmenu": console.log( "[YouTubeCustomControls] WatchForPageChanges() :: Mutation observed for context menu", - node + node, ); InsertPopoutEntryIntoContextMenu(); break; @@ -61,7 +60,7 @@ export const WatchForPageChanges = () => { case "ytp-right-controls": console.log( "[YouTubeCustomControls] WatchForPageChanges() :: Mutation observed for player controls", - node + node, ); await InsertPopoutButtonIntoPlayerControls(); await InsertRotationButtonsIntoPlayerControls(); @@ -109,7 +108,7 @@ const InsertPopoutEntryIntoContextMenu = () => { const menuItemLabel = document.createElement("div"); menuItemLabel.className = "ytp-menuitem-label"; menuItemLabel.innerText = browser.i18n.getMessage( - "ContextMenuEntryLabel_PopoutPlayer" + "ContextMenuEntryLabel_PopoutPlayer", ); const menuItemContent = document.createElement("div"); @@ -121,7 +120,7 @@ const InsertPopoutEntryIntoContextMenu = () => { menuItem.addEventListener( "click", OpenPopoutPlayerControlsClickEventHandler, - false + false, ); const menu = contextmenu @@ -143,7 +142,7 @@ const InsertPopoutEntryIntoContextMenu = () => { } catch (error) { console.error( "Failed to insert popout player entry into context menu", - error + error, ); return false; } @@ -166,7 +165,7 @@ const InsertPopoutButtonIntoPlayerControls = async () => { } catch (error) { console.error( `Failed to get "behavior.controls" option from local storage`, - error + error, ); } } @@ -182,13 +181,13 @@ const InsertPopoutButtonIntoPlayerControls = async () => { if (playerButton) { console.warn( "#popout-player-control-button already exists", - playerButton + playerButton, ); return false; } const fullScreenButton = controls.getElementsByClassName( - "ytp-fullscreen-button" + "ytp-fullscreen-button", )[0]; if (!fullScreenButton) { console.warn("Missing player controls full screen button"); @@ -199,32 +198,32 @@ const InsertPopoutButtonIntoPlayerControls = async () => { playerButton.className = ["ytp-popout-button", "ytp-button"].join(" "); playerButton.setAttribute( "aria-label", - browser.i18n.getMessage("PlayerControlsButtonTitle_PopoutPlayer") + browser.i18n.getMessage("PlayerControlsButtonTitle_PopoutPlayer"), ); playerButton.setAttribute( "title", - browser.i18n.getMessage("PlayerControlsButtonTitle_PopoutPlayer") + browser.i18n.getMessage("PlayerControlsButtonTitle_PopoutPlayer"), ); playerButton.id = "popout-player-control-button"; playerButton.appendChild(GetPopoutIconSVG("button", true)); playerButton.addEventListener( "click", OpenPopoutPlayerControlsClickEventHandler, - false + false, ); // TODO: this doesn't work (`fullScreenButton["onmouseover"]` is null); need another way to implement the fancy tooltip playerButton.addEventListener( "mouseover", fullScreenButton["onmouseover"], - false + false, ); controls.insertBefore(playerButton, fullScreenButton); } catch (error) { console.error( "Failed to insert popout player button into player controls", - error + error, ); return false; } @@ -235,7 +234,7 @@ const InsertPopoutButtonIntoPlayerControls = async () => { const InsertRotationButtonsIntoPlayerControls = async () => { if (!IsPopoutPlayer(window.location)) { console.info( - "Video player rotation controls are currently only supported within the popout player" + "Video player rotation controls are currently only supported within the popout player", ); return false; } @@ -243,7 +242,7 @@ const InsertRotationButtonsIntoPlayerControls = async () => { try { const showRotationButtons = await Options.GetLocalOption( "behavior", - "showRotationButtons" + "showRotationButtons", ); if (showRotationButtons === false) { console.info(`Options "behavior.showRotationButtons" is disabled`); @@ -252,7 +251,7 @@ const InsertRotationButtonsIntoPlayerControls = async () => { } catch (error) { console.error( `Failed to get "behavior.showRotationButtons" option from local storage`, - error + error, ); } @@ -266,12 +265,12 @@ const InsertRotationButtonsIntoPlayerControls = async () => { const directions = ["left", "right"].reverse(); for (const direction of directions) { let button = controls.querySelector( - "#ytp-rotate-" + direction + "-button" + "#ytp-rotate-" + direction + "-button", ); if (button) { console.warn( "#ytp-rotate-" + direction + "-button already exists", - button + button, ); continue; } @@ -284,11 +283,15 @@ const InsertRotationButtonsIntoPlayerControls = async () => { ].join(" "); button.setAttribute( "aria-label", - browser.i18n.getMessage("PopoutPlayerControls_RotateVideo_" + direction) + browser.i18n.getMessage( + "PopoutPlayerControls_RotateVideo_" + direction, + ), ); button.setAttribute( "title", - browser.i18n.getMessage("PopoutPlayerControls_RotateVideo_" + direction) + browser.i18n.getMessage( + "PopoutPlayerControls_RotateVideo_" + direction, + ), ); button.id = "ytp-rotate-" + direction + "-button"; button.appendChild(GetRotateIconSVG(direction, true)); @@ -305,7 +308,7 @@ const InsertRotationButtonsIntoPlayerControls = async () => { break; } }, - false + false, ); controls.insertBefore(button, controls.querySelector("button")); @@ -313,7 +316,7 @@ const InsertRotationButtonsIntoPlayerControls = async () => { } catch (error) { console.error( "Failed to insert video rotation buttons into player controls", - error + error, ); return false; } @@ -361,7 +364,7 @@ const GetPopoutIconSVG = (type, shadow = false) => { useElement.setAttributeNS( "http://www.w3.org/1999/xlink", "href", - "#" + pathElement.id + "#" + pathElement.id, ); iconSVG.appendChild(useElement); @@ -415,7 +418,7 @@ const GetRotateIconSVG = (direction, shadow = false) => { useElement.setAttributeNS( "http://www.w3.org/1999/xlink", "href", - "#" + pathElement.id + "#" + pathElement.id, ); iconSVG.appendChild(useElement); diff --git a/app/scripts/content/YouTubePopoutPlayer.js b/app/scripts/content/YouTubePopoutPlayer.js index 12b4e70..1a054da 100644 --- a/app/scripts/content/YouTubePopoutPlayer.js +++ b/app/scripts/content/YouTubePopoutPlayer.js @@ -1,5 +1,5 @@ import { IsVisible } from "../helpers/utils"; -import { GetVideoIDFromURL, GetPlaylistIDFromURL } from "../helpers/youtube"; +import { GetPlaylistIDFromURL, GetVideoIDFromURL } from "../helpers/youtube"; /** * Gets the video element for the primary video player on a YouTube video page @@ -9,21 +9,21 @@ export const GetPageVideo = () => { console.log("[Content] YouTubePopoutPlayer GetPageVideo()"); const videos = document.querySelectorAll( - "#shorts-player video, #movie_player video, #player video" + "#shorts-player video, #movie_player video, #player video", ); for (const video of videos) { if (IsVisible(video)) { console.log( "[Content] YouTubePopoutPlayer GetPageVideo() :: Return", - video + video, ); return video; } } console.warn( - "[Content] YouTubePopoutPlayer GetPageVideo() :: Failed to Get Video Player" + "[Content] YouTubePopoutPlayer GetPageVideo() :: Failed to Get Video Player", ); return undefined; }; @@ -45,7 +45,7 @@ export const GetVideoPlayerInfo = () => { console.log( "[Content] YouTubePopoutPlayer GetVideoPlayerInfo() :: Return", - info + info, ); return info; }; @@ -58,7 +58,7 @@ export const GetVideoPlayerInfo = () => { export const OpenPopoutFromContentScript = async (data) => { console.log( "[Content] YouTubePopoutPlayer OpenPopoutFromContentScript()", - data + data, ); try { @@ -70,7 +70,7 @@ export const OpenPopoutFromContentScript = async (data) => { if (response !== undefined) { console.log( '[Content] YouTubePopoutPlayer OpenPopoutFromContentScript() :: Action "open-popout" response', - response + response, ); } @@ -78,7 +78,7 @@ export const OpenPopoutFromContentScript = async (data) => { } catch (error) { console.error( "[Content] YouTubePopoutPlayer OpenPopoutFromContentScript() :: Error", - error + error, ); } diff --git a/app/scripts/helpers/options.js b/app/scripts/helpers/options.js index b2d6304..dcb6a68 100644 --- a/app/scripts/helpers/options.js +++ b/app/scripts/helpers/options.js @@ -21,7 +21,7 @@ const Options = (() => { // console.log("Options.InitDefaults()"); const defaults = this.ConvertForStorage( - Object.assign({}, OPTION_DEFAULTS) + Object.assign({}, OPTION_DEFAULTS), ); if (reset) { @@ -156,7 +156,7 @@ const Options = (() => { // console.log("Options.SetLocalOption()", domain, name); const option = this.ConvertForStorage( - Object.assign({}, { [domain]: { [name]: value } }) + Object.assign({}, { [domain]: { [name]: value } }), ); await browser.storage.local.set(option); @@ -199,7 +199,7 @@ const Options = (() => { // console.log("Options.SetLocalOptionsForDomain()", domain, options); options = this.ConvertForStorage( - Object.assign({}, { [domain]: options }) + Object.assign({}, { [domain]: options }), ); await browser.storage.local.set(options); diff --git a/app/scripts/helpers/utils.js b/app/scripts/helpers/utils.js index ccabde7..12bf946 100644 --- a/app/scripts/helpers/utils.js +++ b/app/scripts/helpers/utils.js @@ -76,7 +76,7 @@ export const IsPopoutPlayer = (location) => { export const TitleCase = (string) => string.replace( /\b\w+/g, - (txt) => txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase() + (txt) => txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase(), ); /** @@ -135,5 +135,5 @@ export const IsVisible = (elem) => elem.offsetWidth > 0 && elem.offsetHeight > 0 && !["none", "hidden"].includes( - getComputedStyle(elem).getPropertyValue("display") + getComputedStyle(elem).getPropertyValue("display"), ); diff --git a/app/scripts/helpers/youtube.js b/app/scripts/helpers/youtube.js index 2ab1fd4..95166d0 100644 --- a/app/scripts/helpers/youtube.js +++ b/app/scripts/helpers/youtube.js @@ -66,12 +66,12 @@ export const GetPlaylistVideoIDsFromDOM = () => { [ 'ytd-playlist-panel-renderer a[href*="v="]', 'ytd-playlist-video-list-renderer a[href*="v="]', - ].join(",") - ) + ].join(","), + ), ) .map((elem) => GetParamFromURL("v", elem.getAttribute("href"))) - .filter(Boolean) - ) + .filter(Boolean), + ), ); // console.log("Return", IDs); diff --git a/app/scripts/options/stores/optionsStore.js b/app/scripts/options/stores/optionsStore.js index a63e73b..c2c0f54 100644 --- a/app/scripts/options/stores/optionsStore.js +++ b/app/scripts/options/stores/optionsStore.js @@ -9,22 +9,12 @@ export const createOptionsStore = (type) => { const options = Options.ConvertForStorage(OPTION_DEFAULTS); // NOTES: - // 1.) Currently options do not have getters, though that could be a useful/necessary enhancement in the future. - // Instead, options are read directly from state as direct properties on the state object (per Zustand standards). - // This requires: - // a) Setting the initial values in state synchronously (by using hard-coded defaults) - // b) Loading the values from extension storage (async) and setting them into state when the store is first created - // The major drawback to this approach is that IF the options are set/changed from outside of this store, - // the React App will become out of sync until a manual page reload occurs. - // This would be an issue when the Options page is open AND extension storage is updated from some other source, - // like the background script setting/resetting options programmatically, - // or in the future if we make the synchronize the options between browsers - // 2.) Do NOT manipulate state without also updating the extension storage (use `setOption()` or `setOptions()`). - // Anything that functions as a setter for a value in state needs to use `browser.storage[type].set`, - // otherwise the change won't actually go into storage and thus won't be used by the extension's scripts. - // 3.) This does NOT use Zustand's "persist" middleware (which can be pointed to extension storage quite easily), - // as that would persist the entire state as a single object in one entry within extension storage, - // rather than persisting each property of state into its own individual storage entry, which is what we need. + // 1.) Currently options do not have getters, though that could be a useful/necessary enhancement in the future. Instead, options are read directly from state as direct properties on the state object (per Zustand standards). This requires: + // a.) Setting the initial values in state synchronously (by using hard-coded defaults) + // b.) Loading the values from extension storage (async) and setting them into state when the store is first created + // The major drawback to this approach is that IF the options are set/changed from outside of this store, the React App will become out of sync until a manual page reload occurs. This would be an issue when the Options page is open AND extension storage is updated from some other source, like the background script setting/resetting options programmatically, or in the future if we make the synchronize the options between browsers + // 2.) Do NOT manipulate state without also updating the extension storage (use `setOption()` or `setOptions()`). Anything that functions as a setter for a value in state needs to use `browser.storage[type].set`, otherwise the change won't actually go into storage and thus won't be used by the extension's scripts. + // 3.) This does NOT use Zustand's "persist" middleware (which can be pointed to extension storage quite easily), as that would persist the entire state as a single object in one entry within extension storage, rather than persisting each property of state into its own individual storage entry, which is what we need. const store = create((set) => { /** @@ -94,12 +84,11 @@ export const useOptionsForDomain = (domain) => { Object.fromEntries( Object.entries(state) .filter(([key]) => key.startsWith(domain + ".")) - .map(([key, value]) => [key.replace(domain + ".", ""), value]) - ) + .map(([key, value]) => [key.replace(domain + ".", ""), value]), + ), ); - // set note #2 above - these wrapper methods must use the custom methods from the created store/state, - // rather than the `useOptionsStore.setState()` method (which would NOT commit the changes to extension storage). + // set note #2 above - these wrapper methods must use the custom methods from the created store/state, rather than the `useOptionsStore.setState()` method (which would NOT commit the changes to extension storage). const setOptionInState = useOptionsStore((state) => state.setOption); const setOptionsInState = useOptionsStore((state) => state.setOptions); const setOptionForDomain = (name, value) => @@ -123,8 +112,7 @@ export const useOption = (domain, name) => { const value = useOptionsStore((state) => state[`${domain}.${name}`]); const setOption = useOptionsStore((state) => state.setOption); - // set note #2 above - this wrapper method must use the custom method from the created store/state, - // rather than the `useOptionsStore.setState()` method (which would NOT commit the changes to extension storage). + // set note #2 above - this wrapper method must use the custom method from the created store/state, rather than the `useOptionsStore.setState()` method (which would NOT commit the changes to extension storage). const setValue = (value) => setOption(domain, name, value); return [value, setValue]; diff --git a/package.json b/package.json index 967d39c..167c60e 100644 --- a/package.json +++ b/package.json @@ -45,16 +45,19 @@ "zustand": "^4.3.6" }, "devDependencies": { - "@welldone-software/why-did-you-render": "^7.0.1", - "browser-sync": "^2.29.1", + "@babel/preset-react": "^7.23.3", + "@ianvs/prettier-plugin-sort-imports": "^4.1.1", + "@webextension-toolbox/webextension-toolbox": "^6.2.0", + "browser-sync": "^3.0.2", "cross-env": "^7.0.3", - "eslint": "^8.36.0", - "eslint-plugin-react": "^7.32.2", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.2", + "eslint-plugin-react": "^7.33.2", "npm-run-all": "^4.1.5", - "prettier": "^2.8.7", + "prettier": "^3.1.1", "svg2png": "^4.1.1", "trash-cli": "^5.0.0", - "web-ext": "^7.6.0", - "webextension-toolbox": "github:rthaut/webextension-toolbox#browserslist-string-vendor-version" + "web-ext": "^7.9.0" } } diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..9348bbb --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,31 @@ +/** @type {import('prettier').Config} */ +module.exports = { + endOfLine: "lf", + semi: true, + singleQuote: false, + tabWidth: 2, + trailingComma: "all", + importOrder: [ + "^(react/(.*)$)|^(react$)", + "", + "", + "^types$", + "^@/types/(.*)$", + "^@/config/(.*)$", + "^@/lib/(.*)$", + "^@/hooks/(.*)$", + "^@/components/ui/(.*)$", + "^@/components/(.*)$", + "^@/styles/(.*)$", + "^@/app/(.*)$", + "", + "^[./]", + ], + importOrderSeparation: true, + importOrderSortSpecifiers: true, + importOrderBuiltinModulesToTop: true, + importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], + importOrderMergeDuplicateImports: true, + importOrderCombineTypeAndValueImports: true, + plugins: ["@ianvs/prettier-plugin-sort-imports"], +} diff --git a/webextension-toolbox.config.js b/webextension-toolbox.config.js deleted file mode 100644 index 9ddd76a..0000000 --- a/webextension-toolbox.config.js +++ /dev/null @@ -1,47 +0,0 @@ -/* global require */ -const TerserPlugin = require("terser-webpack-plugin"); - -/* global module */ -module.exports = { - webpack: (config, { dev }) => { - config.optimization = { - ...config.optimization, - minimizer: [ - new TerserPlugin({ - terserOptions: { - compress: { - pure_funcs: [ - // drop all console functions except errors - "console.assert", - "console.clear", - "console.count", - "console.countReset", - "console.debug", - "console.dir", - "console.dirxml", - //"console.error", - //"console.exception", - "console.group", - "console.groupCollapsed", - "console.groupEnd", - "console.info", - "console.log", - "console.profile", - "console.profileEnd", - "console.table", - "console.time", - "console.timeEnd", - "console.timeLog", - "console.timeStamp", - "console.trace", - "console.warn", - ], - }, - }, - }), - ], - }; - - return config; - }, -};