extension: Lint after TypeScript migration
This commit is contained in:
parent
cfc57a2e1d
commit
488e4421e9
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"webextensions": true
|
||||
},
|
||||
"globals": {
|
||||
"__webpack_public_path__": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"webextensions": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-non-null-assertion": "off"
|
||||
}
|
||||
}
|
|
@ -1,10 +1,15 @@
|
|||
import * as utils from "./utils";
|
||||
|
||||
type WebResponseHeadersDetails = chrome.webRequest.WebResponseHeadersDetails | browser.webRequest._OnHeadersReceivedDetails;
|
||||
|
||||
function isSwf(details: WebResponseHeadersDetails) {
|
||||
function isSwf(
|
||||
details:
|
||||
| chrome.webRequest.WebResponseHeadersDetails
|
||||
| browser.webRequest._OnHeadersReceivedDetails
|
||||
) {
|
||||
// TypeScript doesn't compile without this explicit type delaration.
|
||||
const headers: (chrome.webRequest.HttpHeader | browser.webRequest._HttpHeaders)[] = details.responseHeaders!;
|
||||
const headers: (
|
||||
| chrome.webRequest.HttpHeader
|
||||
| browser.webRequest._HttpHeaders
|
||||
)[] = details.responseHeaders!;
|
||||
const typeHeader = headers.find(
|
||||
({ name }) => name.toLowerCase() === "content-type"
|
||||
);
|
||||
|
@ -12,8 +17,8 @@ function isSwf(details: WebResponseHeadersDetails) {
|
|||
return false;
|
||||
}
|
||||
|
||||
const mime = typeHeader.value!
|
||||
.toLowerCase()
|
||||
const mime = typeHeader
|
||||
.value!.toLowerCase()
|
||||
.match(/^\s*(.*?)\s*(?:;.*)?$/)![1];
|
||||
|
||||
// Some sites (e.g. swfchan.net) might (wrongly?) send octet-stream, so check file extension too.
|
||||
|
@ -26,7 +31,11 @@ function isSwf(details: WebResponseHeadersDetails) {
|
|||
return mime === "application/x-shockwave-flash";
|
||||
}
|
||||
|
||||
function onHeadersReceived(details: WebResponseHeadersDetails) {
|
||||
function onHeadersReceived(
|
||||
details:
|
||||
| chrome.webRequest.WebResponseHeadersDetails
|
||||
| browser.webRequest._OnHeadersReceivedDetails
|
||||
) {
|
||||
if (isSwf(details)) {
|
||||
const baseUrl = utils.runtime.getURL("player.html");
|
||||
return { redirectUrl: `${baseUrl}?url=${details.url}` };
|
||||
|
@ -57,7 +66,7 @@ function disable() {
|
|||
enable();
|
||||
}
|
||||
|
||||
utils.storage.onChanged.addListener((changes: any, namespace: string) => {
|
||||
utils.storage.onChanged.addListener((changes, namespace) => {
|
||||
if (
|
||||
namespace === "sync" &&
|
||||
Object.prototype.hasOwnProperty.call(changes, "ruffleEnable")
|
||||
|
|
|
@ -5,12 +5,15 @@ function camelize(s: string) {
|
|||
}
|
||||
|
||||
export interface Options {
|
||||
ruffleEnable: boolean,
|
||||
ignoreOptout: boolean,
|
||||
ruffleEnable: boolean;
|
||||
ignoreOptout: boolean;
|
||||
}
|
||||
|
||||
function getBooleanElements() {
|
||||
const elements: Record<string, any> = {};
|
||||
const elements: Record<
|
||||
string,
|
||||
{ option: Element; checkbox: HTMLInputElement; label: HTMLLabelElement }
|
||||
> = {};
|
||||
for (const option of document.getElementsByClassName("option")) {
|
||||
const [checkbox] = option.getElementsByTagName("input");
|
||||
if (checkbox.type !== "checkbox") {
|
||||
|
@ -23,7 +26,9 @@ function getBooleanElements() {
|
|||
return elements;
|
||||
}
|
||||
|
||||
export async function bindBooleanOptions(onChange?: (options: Options) => void) {
|
||||
export async function bindBooleanOptions(
|
||||
onChange?: (options: Options) => void
|
||||
): Promise<void> {
|
||||
const elements = getBooleanElements();
|
||||
|
||||
// Bind initial values.
|
||||
|
@ -36,7 +41,7 @@ export async function bindBooleanOptions(onChange?: (options: Options) => void)
|
|||
// TODO: click/change/input?
|
||||
checkbox.addEventListener("click", () => {
|
||||
const value = checkbox.checked;
|
||||
options[key] = value;
|
||||
options[key as keyof Options] = value;
|
||||
utils.storage.sync.set({ [key]: value });
|
||||
});
|
||||
|
||||
|
@ -50,7 +55,7 @@ export async function bindBooleanOptions(onChange?: (options: Options) => void)
|
|||
}
|
||||
|
||||
// Listen for future changes.
|
||||
utils.storage.onChanged.addListener((changes: Object, namespace: string) => {
|
||||
utils.storage.onChanged.addListener((changes, namespace) => {
|
||||
if (namespace !== "sync") {
|
||||
return;
|
||||
}
|
||||
|
@ -60,7 +65,7 @@ export async function bindBooleanOptions(onChange?: (options: Options) => void)
|
|||
continue;
|
||||
}
|
||||
elements[key].checkbox.checked = option.newValue;
|
||||
options[key] = option.newValue;
|
||||
options[key as keyof Options] = option.newValue;
|
||||
}
|
||||
|
||||
if (onChange) {
|
||||
|
|
|
@ -20,7 +20,10 @@
|
|||
|
||||
import * as utils from "./utils";
|
||||
|
||||
const pendingMessages: ({ resolve(value: any): void, reject(reason?: any): void } | null)[] = [];
|
||||
const pendingMessages: ({
|
||||
resolve(value: unknown): void;
|
||||
reject(reason?: unknown): void;
|
||||
} | null)[] = [];
|
||||
const uniqueMessageSuffix = Math.floor(Math.random() * 100000000000);
|
||||
|
||||
/**
|
||||
|
@ -28,7 +31,7 @@ const uniqueMessageSuffix = Math.floor(Math.random() * 100000000000);
|
|||
* @param {*} data - JSON-serializable data to send to main world.
|
||||
* @returns {Promise<*>} JSON-serializable response from main world.
|
||||
*/
|
||||
function sendMessageToPage(data: any): Promise<any> {
|
||||
function sendMessageToPage(data: unknown): Promise<unknown> {
|
||||
const message = {
|
||||
type: `FROM_RUFFLE${uniqueMessageSuffix}`,
|
||||
index: pendingMessages.length,
|
||||
|
@ -87,6 +90,12 @@ function checkPageOptout(): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Document {
|
||||
xmlVersion: string | null;
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const options = await utils.getOptions(["ruffleEnable", "ignoreOptout"]);
|
||||
const pageOptout = checkPageOptout();
|
||||
|
@ -96,7 +105,7 @@ function checkPageOptout(): boolean {
|
|||
!window.RufflePlayer &&
|
||||
(options.ignoreOptout || !pageOptout);
|
||||
|
||||
utils.runtime.onMessage.addListener((message: any, _sender: any, sendResponse: (options: Object) => void) => {
|
||||
utils.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (shouldLoad) {
|
||||
sendMessageToPage(message).then((response) => {
|
||||
sendResponse({
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
declare let __webpack_public_path__: string;
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
import { PublicAPI, SourceAPI, publicPath, Letterbox, LogLevel } from "ruffle-core";
|
||||
import {
|
||||
PublicAPI,
|
||||
SourceAPI,
|
||||
publicPath,
|
||||
Letterbox,
|
||||
LogLevel,
|
||||
} from "ruffle-core";
|
||||
|
||||
const api = PublicAPI.negotiate(
|
||||
window.RufflePlayer!,
|
||||
"local",
|
||||
new SourceAPI("local"),
|
||||
new SourceAPI("local")
|
||||
);
|
||||
window.RufflePlayer = api;
|
||||
__webpack_public_path__ = publicPath(api.config, "local");
|
||||
|
|
|
@ -22,7 +22,9 @@ const STATUS_COLORS = {
|
|||
"status_result_disabled": "gray",
|
||||
};
|
||||
|
||||
async function queryTabStatus(listener: (status: keyof typeof STATUS_COLORS) => void) {
|
||||
async function queryTabStatus(
|
||||
listener: (status: keyof typeof STATUS_COLORS) => void
|
||||
) {
|
||||
listener("status_init");
|
||||
|
||||
let tabs: chrome.tabs.Tab[] | browser.tabs.Tab[];
|
||||
|
@ -77,7 +79,8 @@ async function queryTabStatus(listener: (status: keyof typeof STATUS_COLORS) =>
|
|||
optionsChanged();
|
||||
}
|
||||
|
||||
function objectsEqual<T extends Record<string, any>>(x: T, y: T) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function objectsEqual(x: any, y: any) {
|
||||
for (const [key, value] of Object.entries(x)) {
|
||||
if (y[key] !== value) {
|
||||
return false;
|
||||
|
@ -115,14 +118,20 @@ window.addEventListener("DOMContentLoaded", () => {
|
|||
optionsChanged();
|
||||
});
|
||||
|
||||
statusIndicator = document.getElementById("status-indicator") as HTMLDivElement;
|
||||
statusIndicator = document.getElementById(
|
||||
"status-indicator"
|
||||
) as HTMLDivElement;
|
||||
statusText = document.getElementById("status-text") as HTMLSpanElement;
|
||||
|
||||
const optionsButton = document.getElementById("options-button") as HTMLButtonElement;
|
||||
const optionsButton = document.getElementById(
|
||||
"options-button"
|
||||
) as HTMLButtonElement;
|
||||
optionsButton.textContent = utils.i18n.getMessage("open_settings_page");
|
||||
optionsButton.addEventListener("click", () => utils.openOptionsPage());
|
||||
|
||||
reloadButton = document.getElementById("reload-button") as HTMLButtonElement;
|
||||
reloadButton = document.getElementById(
|
||||
"reload-button"
|
||||
) as HTMLButtonElement;
|
||||
reloadButton.textContent = utils.i18n.getMessage("action_reload");
|
||||
reloadButton.addEventListener("click", async () => {
|
||||
await utils.tabs.reload(activeTab.id!);
|
||||
|
|
|
@ -3,7 +3,7 @@ import { PublicAPI, SourceAPI, publicPath } from "ruffle-core";
|
|||
const api = PublicAPI.negotiate(
|
||||
window.RufflePlayer!,
|
||||
"extension",
|
||||
new SourceAPI("extension"),
|
||||
new SourceAPI("extension")
|
||||
);
|
||||
window.RufflePlayer = api;
|
||||
__webpack_public_path__ = publicPath(api.config, "extension");
|
||||
|
@ -34,7 +34,11 @@ if (uniqueMessageSuffix) {
|
|||
const { type, index, data } = event.data;
|
||||
if (type === `FROM_RUFFLE${uniqueMessageSuffix}`) {
|
||||
// Ping back.
|
||||
const message = { type: `TO_RUFFLE${uniqueMessageSuffix}`, index, data };
|
||||
const message = {
|
||||
type: `TO_RUFFLE${uniqueMessageSuffix}`,
|
||||
index,
|
||||
data,
|
||||
};
|
||||
window.postMessage(message, "*");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,19 +1,66 @@
|
|||
import { Options } from "./common";
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
ruffleEnable: true,
|
||||
ignoreOptout: false,
|
||||
};
|
||||
|
||||
export let i18n: any;
|
||||
export let storage: any;
|
||||
export let tabs: {
|
||||
reload(tabId: number): Promise<void>,
|
||||
query(query: chrome.tabs.QueryInfo & browser.tabs._QueryQueryInfo): Promise<chrome.tabs.Tab[] | browser.tabs.Tab[]>,
|
||||
sendMessage(tabId: number, message: any, options?: chrome.tabs.MessageSendOptions & browser.tabs._SendMessageOptions): Promise<any>,
|
||||
export let i18n: {
|
||||
getMessage(name: string): string;
|
||||
};
|
||||
export let runtime: any;
|
||||
export let openOptionsPage: any;
|
||||
export let storage: {
|
||||
local: {
|
||||
get(keys: string[]): Promise<Record<string, unknown>>;
|
||||
remove(keys: string[]): Promise<void>;
|
||||
set(items: Record<string, unknown>): Promise<void>;
|
||||
};
|
||||
sync: {
|
||||
get(keys: string[]): Promise<Record<string, unknown>>;
|
||||
remove(keys: string[]): Promise<void>;
|
||||
set(items: Record<string, unknown>): Promise<void>;
|
||||
};
|
||||
onChanged: {
|
||||
addListener(
|
||||
listener: (
|
||||
changes:
|
||||
| Record<string, chrome.storage.StorageChange>
|
||||
| Record<string, browser.storage.StorageChange>,
|
||||
areaName: string
|
||||
) => void
|
||||
): void;
|
||||
};
|
||||
};
|
||||
export let tabs: {
|
||||
reload(tabId: number): Promise<void>;
|
||||
query(
|
||||
query: chrome.tabs.QueryInfo & browser.tabs._QueryQueryInfo
|
||||
): Promise<chrome.tabs.Tab[] | browser.tabs.Tab[]>;
|
||||
sendMessage(
|
||||
tabId: number,
|
||||
message: unknown,
|
||||
options?: chrome.tabs.MessageSendOptions &
|
||||
browser.tabs._SendMessageOptions
|
||||
): Promise<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
};
|
||||
export let runtime: {
|
||||
onMessage: {
|
||||
addListener(
|
||||
listener: (
|
||||
message: unknown,
|
||||
sender:
|
||||
| chrome.runtime.MessageSender
|
||||
| browser.runtime.MessageSender,
|
||||
sendResponse: (response?: unknown) => void
|
||||
) => void
|
||||
): void;
|
||||
};
|
||||
getURL(path: string): string;
|
||||
};
|
||||
export let openOptionsPage: () => Promise<void>;
|
||||
|
||||
function promisify<T>(func: (callback: (result?: T) => void) => void): Promise<T> {
|
||||
function promisify<T>(
|
||||
func: (callback: (result?: T) => void) => void
|
||||
): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
func((result) => {
|
||||
const error = chrome.runtime.lastError;
|
||||
|
@ -37,38 +84,59 @@ if (typeof chrome !== "undefined") {
|
|||
promisify((cb) => chrome.storage.local.get(keys, cb)),
|
||||
remove: (keys: string[]) =>
|
||||
promisify((cb) => chrome.storage.local.remove(keys, cb)),
|
||||
set: (items: object) =>
|
||||
set: (items: Record<string, unknown>) =>
|
||||
promisify((cb) => chrome.storage.local.set(items, cb)),
|
||||
},
|
||||
sync: {
|
||||
get: (keys: string[]) => promisify((cb) => chrome.storage.sync.get(keys, cb)),
|
||||
get: (keys: string[]) =>
|
||||
promisify((cb) => chrome.storage.sync.get(keys, cb)),
|
||||
remove: (keys: string[]) =>
|
||||
promisify((cb) => chrome.storage.sync.remove(keys, cb)),
|
||||
set: (items: object) =>
|
||||
set: (items: Record<string, unknown>) =>
|
||||
promisify((cb) => chrome.storage.sync.set(items, cb)),
|
||||
},
|
||||
onChanged: {
|
||||
addListener: (listener: (changes: chrome.storage.StorageChange, areaName: string) => void) =>
|
||||
chrome.storage.onChanged.addListener(listener),
|
||||
addListener: (
|
||||
listener: (
|
||||
changes: Record<string, chrome.storage.StorageChange>,
|
||||
areaName: string
|
||||
) => void
|
||||
) => chrome.storage.onChanged.addListener(listener),
|
||||
},
|
||||
};
|
||||
|
||||
tabs = {
|
||||
reload: (tabId: number) => promisify((cb) => chrome.tabs.reload(tabId, undefined, cb)),
|
||||
query: (query: chrome.tabs.QueryInfo) => promisify((cb) => chrome.tabs.query(query, cb)),
|
||||
sendMessage: (tabId: number, message: any, options: chrome.tabs.MessageSendOptions) =>
|
||||
promisify((cb) => chrome.tabs.sendMessage(tabId, message, options, cb)),
|
||||
reload: (tabId: number) =>
|
||||
promisify((cb) => chrome.tabs.reload(tabId, undefined, cb)),
|
||||
query: (query: chrome.tabs.QueryInfo) =>
|
||||
promisify((cb) => chrome.tabs.query(query, cb)),
|
||||
sendMessage: (
|
||||
tabId: number,
|
||||
message: unknown,
|
||||
options: chrome.tabs.MessageSendOptions
|
||||
) =>
|
||||
promisify((cb) =>
|
||||
chrome.tabs.sendMessage(tabId, message, options, cb)
|
||||
),
|
||||
};
|
||||
|
||||
runtime = {
|
||||
onMessage: {
|
||||
addListener: (listener: (message: any, sender: chrome.runtime.MessageSender, sendResponse: (response?: any) => void) => void) =>
|
||||
chrome.runtime.onMessage.addListener(listener),
|
||||
addListener: (
|
||||
listener: (
|
||||
message: unknown,
|
||||
sender: chrome.runtime.MessageSender,
|
||||
sendResponse: (response?: unknown) => void
|
||||
) => void
|
||||
) => chrome.runtime.onMessage.addListener(listener),
|
||||
},
|
||||
getURL: (path: string) => chrome.runtime.getURL(path),
|
||||
};
|
||||
|
||||
openOptionsPage = () => chrome.tabs.create({ url: "/options.html" });
|
||||
openOptionsPage = () =>
|
||||
promisify((cb: () => void) =>
|
||||
chrome.tabs.create({ url: "/options.html" }, cb)
|
||||
);
|
||||
} else if (typeof browser !== "undefined") {
|
||||
i18n = {
|
||||
getMessage: (name: string) => browser.i18n.getMessage(name),
|
||||
|
@ -78,30 +146,45 @@ if (typeof chrome !== "undefined") {
|
|||
local: {
|
||||
get: (keys: string[]) => browser.storage.local.get(keys),
|
||||
remove: (keys: string[]) => browser.storage.local.remove(keys),
|
||||
set: (items: Record<string, any>) => browser.storage.local.set(items),
|
||||
set: (items: Record<string, unknown>) =>
|
||||
browser.storage.local.set(items),
|
||||
},
|
||||
sync: {
|
||||
get: (keys: string[]) => browser.storage.sync.get(keys),
|
||||
remove: (keys: string[]) => browser.storage.sync.remove(keys),
|
||||
set: (items: Record<string, any>) => browser.storage.sync.set(items),
|
||||
set: (items: Record<string, unknown>) =>
|
||||
browser.storage.sync.set(items),
|
||||
},
|
||||
onChanged: {
|
||||
addListener: (listener: (changes: Record<string, browser.storage.StorageChange>, areaName: string) => void) =>
|
||||
browser.storage.onChanged.addListener(listener),
|
||||
addListener: (
|
||||
listener: (
|
||||
changes: Record<string, browser.storage.StorageChange>,
|
||||
areaName: string
|
||||
) => void
|
||||
) => browser.storage.onChanged.addListener(listener),
|
||||
},
|
||||
};
|
||||
|
||||
tabs = {
|
||||
reload: (tabId: number) => browser.tabs.reload(tabId),
|
||||
query: (query: browser.tabs._QueryQueryInfo) => browser.tabs.query(query),
|
||||
sendMessage: (tabId: number, message: any, options: browser.tabs._SendMessageOptions) =>
|
||||
browser.tabs.sendMessage(tabId, message, options),
|
||||
query: (query: browser.tabs._QueryQueryInfo) =>
|
||||
browser.tabs.query(query),
|
||||
sendMessage: (
|
||||
tabId: number,
|
||||
message: unknown,
|
||||
options: browser.tabs._SendMessageOptions
|
||||
) => browser.tabs.sendMessage(tabId, message, options),
|
||||
};
|
||||
|
||||
runtime = {
|
||||
onMessage: {
|
||||
addListener: (listener: (message: any, sender: browser.runtime.MessageSender, sendResponse: (response?: any) => void) => boolean | Promise<any> | void) =>
|
||||
browser.runtime.onMessage.addListener(listener),
|
||||
addListener: (
|
||||
listener: (
|
||||
message: unknown,
|
||||
sender: browser.runtime.MessageSender,
|
||||
sendResponse: (response?: unknown) => void
|
||||
) => boolean | Promise<unknown> | void
|
||||
) => browser.runtime.onMessage.addListener(listener),
|
||||
},
|
||||
getURL: (path: string) => browser.runtime.getURL(path),
|
||||
};
|
||||
|
@ -111,7 +194,7 @@ if (typeof chrome !== "undefined") {
|
|||
throw new Error("Extension API not found.");
|
||||
}
|
||||
|
||||
export async function getOptions(keys: string[]) {
|
||||
export async function getOptions(keys: string[]): Promise<Options> {
|
||||
const options = await storage.sync.get(keys);
|
||||
|
||||
// Copy over default options if they don't exist yet.
|
||||
|
|
Loading…
Reference in New Issue