Refactor background page to use Typescript
Also, this CL adds clean-terminal-webpack-plugin to make it easier to
debug Typescript errors while developing the extension.
Bug: translateselectedtext:15
Change-Id: If9b97cb7508859e2e05f5dc82940808fd935bf1a
diff --git a/src/common/actionApi.js b/src/common/actionApi.ts
similarity index 100%
rename from src/common/actionApi.js
rename to src/common/actionApi.ts
diff --git a/src/common/consts.js b/src/common/consts.ts
similarity index 96%
rename from src/common/consts.js
rename to src/common/consts.ts
index 7b1ef78..44e6ae8 100644
--- a/src/common/consts.js
+++ b/src/common/consts.ts
@@ -1,4 +1,12 @@
-export const isoLangs = {
+interface IsoLang {
+ name: string;
+ nativeName: string;
+};
+interface IsoLangs {
+ [key: string]: IsoLang;
+};
+
+export const isoLangs: IsoLangs = {
'af': {'name': 'Afrikaans', 'nativeName': 'Afrikaans'},
'ak': {'name': 'Twi', 'nativeName': 'Akan'},
'am': {'name': 'Amharic', 'nativeName': 'አማርኛ'},
@@ -134,9 +142,13 @@
'zu': {'name': 'Zulu', 'nativeName': 'isiZulu'},
};
+interface LanguageDictionary {
+ [key: string]: string;
+};
+
// Some languages were incorrectly set. This map serves as a conversion between
// the previous wrong languages and the correct code.
-export const convertLanguages = {
+export const convertLanguages: LanguageDictionary = {
'jv': 'jw',
'zh': 'zh-CN',
};
diff --git a/src/common/i18n.js b/src/common/i18n.ts
similarity index 63%
rename from src/common/i18n.js
rename to src/common/i18n.ts
index 053ea9b..38bb0ca 100644
--- a/src/common/i18n.js
+++ b/src/common/i18n.ts
@@ -1,5 +1,3 @@
// Helper function which serves as a shorter alias to the chrome.i18n.getMessage
// method, specially useful inside lit templates.
-export function msg(...args) {
- return chrome.i18n.getMessage(...args);
-}
+export const msg = chrome.i18n.getMessage;
diff --git a/src/common/options.js b/src/common/options.ts
similarity index 70%
rename from src/common/options.js
rename to src/common/options.ts
index 8861881..6268f22 100644
--- a/src/common/options.js
+++ b/src/common/options.ts
@@ -1,6 +1,43 @@
-import {convertLanguages, isoLangs} from './consts.js';
+import {convertLanguages, isoLangs} from './consts';
-export const TAB_OPTIONS = [
+type TabOptionValue = ''|'yep'|'popup';
+type DeprecatedTabOptionValue = 'panel';
+type TabOptionValueIncludingDeprecated =
+ TabOptionValue|DeprecatedTabOptionValue;
+
+interface TabOption {
+ value: TabOptionValue;
+ labelMsg: string;
+ deprecatedValues: TabOptionValueIncludingDeprecated[];
+}
+
+interface TargetLangs {
+ [key: string]: string; // Here the key is a string with a number.
+}
+interface OptionsV0 {
+ translateinto: TargetLangs;
+ uniquetab: TabOptionValue;
+}
+
+interface LegacyLanguages {
+ [key: string]: string; // Here the key is a string with the language code.
+}
+/**
+ * Backwards-compatible interface for the information available in the sync
+ * storage area.
+ */
+interface LegacyOptions {
+ translateinto: TargetLangs;
+ languages: LegacyLanguages;
+ uniquetab: TabOptionValueIncludingDeprecated;
+}
+
+interface OptionsWrapper {
+ options: OptionsV0;
+ isFirstRun: boolean;
+}
+
+export const TAB_OPTIONS: TabOption[] = [
// Open in new tab for each translation
{
value: '',
@@ -24,22 +61,25 @@
// Class which can be used to retrieve the user options in order to act
// accordingly.
export default class Options {
- constructor(options, isFirstRun) {
+ _options: OptionsV0;
+ isFirstRun: boolean;
+
+ constructor(options: OptionsV0, isFirstRun: boolean) {
this._options = options;
this.isFirstRun = isFirstRun;
}
- get uniqueTab() {
+ get uniqueTab(): TabOptionValue {
return this._options.uniquetab;
}
- get targetLangs() {
+ get targetLangs(): TargetLangs {
return this._options.translateinto;
}
// Returns a promise that resolves in an instance of the Object class with the
// current options.
- static getOptions(readOnly = true) {
+ static getOptions(readOnly: boolean = true): Promise<Options> {
return Options.getOptionsRaw(readOnly).then(res => {
return new Options(res.options, res.isFirstRun);
});
@@ -53,20 +93,20 @@
//
// If the options needed to be normalized/created, they are also saved in the
// sync storage area.
- static getOptionsRaw(readOnly) {
+ static getOptionsRaw(readOnly: boolean): Promise<OptionsWrapper> {
return new Promise((res, rej) => {
- chrome.storage.sync.get(null, items => {
+ chrome.storage.sync.get(null, itemsAny => {
if (chrome.runtime.lastError) {
return rej(chrome.runtime.lastError);
}
+ let items = <LegacyOptions>itemsAny;
let didTranslateintoChange = false;
let didUniquetabChange = false;
- let returnObject = {};
// If the extension sync storage area is blank, set this as being the
// first run.
- returnObject.isFirstRun = Object.keys(items).length === 0;
+ let isFirstRun = Object.keys(items).length === 0;
// Create |translateinto| property if it doesn't exist.
if (items.translateinto === undefined) {
@@ -75,8 +115,9 @@
// Upgrade from a version previous to v0.7 if applicable, otherwise
// create the property with the default values.
if (items.languages !== undefined) {
+ let newTranslateinto: TargetLangs;
items.translateinto =
- Object.assign({}, Object.values(items.languages));
+ Object.assign(newTranslateinto, Object.values(items.languages));
} else {
let uiLocale = chrome.i18n.getMessage('@@ui_locale');
let defaultLang1 = uiLocale.replace('_', '-');
@@ -122,20 +163,22 @@
// value we use now.
// - If it is set to an incorrect value or it isn't set, change it to
// the default value.
+ let uniquetabNewValue: TabOptionValue;
let foundValue = false;
for (let opt of TAB_OPTIONS) {
if (opt.value == items?.uniquetab) {
+ uniquetabNewValue = opt.value;
foundValue = true;
break;
}
if (opt.deprecatedValues.includes(items?.uniquetab)) {
foundValue = true;
- items.uniquetab = opt.value;
+ uniquetabNewValue = opt.value;
break;
}
}
if (!foundValue) {
- items.uniquetab = 'popup';
+ uniquetabNewValue = 'popup';
didUniquetabChange = true;
}
@@ -145,17 +188,21 @@
chrome.storage.sync.remove('languages');
}
+ let returnObject: OptionsWrapper = {
+ isFirstRun,
+ options: {
+ translateinto: items.translateinto,
+ uniquetab: uniquetabNewValue,
+ }
+ };
+
// Save properties that have changed if we're not in read-only mode
if (!readOnly) {
if (didTranslateintoChange || didUniquetabChange) {
- chrome.storage.sync.set({
- translateinto: items.translateinto,
- uniquetab: items.uniquetab,
- });
+ chrome.storage.sync.set(returnObject.options);
}
}
- returnObject.options = items;
res(returnObject);
});
});
diff --git a/src/common/sessionStorage.js b/src/common/sessionStorage.js
deleted file mode 100644
index db46c84..0000000
--- a/src/common/sessionStorage.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// #!if ['chromium_mv3', 'edge_mv3'].includes(browser_target)
-import ExtSessionStorage from './sessionStorage_mv3.js'
-// #!else
-import ExtSessionStorage from './sessionStorage_mv2.js'
-// #!endif
-
-export default ExtSessionStorage;
diff --git a/src/common/sessionStorage.ts b/src/common/sessionStorage.ts
new file mode 100644
index 0000000..450e466
--- /dev/null
+++ b/src/common/sessionStorage.ts
@@ -0,0 +1,7 @@
+// #!if ['chromium_mv3', 'edge_mv3'].includes(browser_target)
+import ExtSessionStorage from './sessionStorage_mv3'
+// #!else
+import ExtSessionStorage from './sessionStorage_mv2'
+// #!endif
+
+export default ExtSessionStorage;
diff --git a/src/common/sessionStorage_mv2.js b/src/common/sessionStorage_mv2.ts
similarity index 80%
rename from src/common/sessionStorage_mv2.js
rename to src/common/sessionStorage_mv2.ts
index 4ad7daf..dda0ae1 100644
--- a/src/common/sessionStorage_mv2.js
+++ b/src/common/sessionStorage_mv2.ts
@@ -1,5 +1,11 @@
+declare global {
+ interface Window {
+ extCustomStorage: any;
+ }
+}
+
export default class ExtSessionStorage {
- static set(items) {
+ static set(items: any): Promise<void> {
return new Promise((res, rej) => {
if (window.extCustomStorage === undefined) window.extCustomStorage = {};
@@ -10,7 +16,7 @@
});
}
- static get(keys) {
+ static get(keys: string|Array<string>|undefined): Promise<any> {
return new Promise((res, rej) => {
if (window.extCustomStorage === undefined) window.extCustomStorage = {};
@@ -25,7 +31,7 @@
}
if (Array.isArray(keys)) {
- let returnObject = {};
+ let returnObject: any = {};
for (const key of keys) {
returnObject[key] = window.extCustomStorage[key];
}
diff --git a/src/common/sessionStorage_mv3.js b/src/common/sessionStorage_mv3.js
deleted file mode 100644
index 9def901..0000000
--- a/src/common/sessionStorage_mv3.js
+++ /dev/null
@@ -1,9 +0,0 @@
-export default class ExtSessionStorage {
- static set(items) {
- return chrome.storage.session.set(items);
- }
-
- static get(keys) {
- return chrome.storage.session.get(keys);
- }
-}
diff --git a/src/common/sessionStorage_mv3.ts b/src/common/sessionStorage_mv3.ts
new file mode 100644
index 0000000..befd2a8
--- /dev/null
+++ b/src/common/sessionStorage_mv3.ts
@@ -0,0 +1,9 @@
+export default class ExtSessionStorage {
+ static set(items: any): Promise<void> {
+ return chrome.storage.session.set(items);
+ }
+
+ static get(keys?: string|string[]|undefined): Promise<any> {
+ return chrome.storage.session.get(keys);
+ }
+}