Skip to content

Commit

Permalink
Update xtension from screenshot improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
justiceo committed Feb 3, 2025
1 parent 2165981 commit ed4eb2c
Show file tree
Hide file tree
Showing 16 changed files with 394 additions and 249 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"url": "https://github.com/essentialkit/xtension"
},
"engines": {
"npm": ">=9.6.7",
"node": ">=18.17.1"
"npm": ">=10.9.0",
"node": ">=22.12.0"
},
"keywords": [
"chrome",
Expand Down
Binary file added src/assets/marquee1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/marquee2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/marquee3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/tile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/background-script/app-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

3 changes: 2 additions & 1 deletion src/background-script/service-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import "./context-menus";
import "./icon-updater";
import "./feedback-checker";
import { getOrCreateSessionId } from "../utils/session-id";
import "./app-worker";

// All service-worker messages should go through this function.
const onMessage = (
message: any,
sender: chrome.runtime.MessageSender,
callback: (response?: any) => void,
callback: (response?: any) => void
) => {
// Check if the message is from this extension.
if (!sender.id || sender.id !== chrome.i18n.getMessage("@@extension_id")) {
Expand Down
7 changes: 5 additions & 2 deletions src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"default_locale": "en",
"author": "Justice Ogbonna",
"permissions": ["contextMenus", "storage", "cookies", "tabs"],
"host_permissions": ["*://*/*"],
"optional_permissions": ["unlimitedStorage"],
"host_permissions": ["<all_urls>"],
"action": {
"default_icon": {
"16": "assets/logo-16x16.png",
Expand All @@ -36,6 +37,8 @@
"48": "assets/logo-48x48.png",
"128": "assets/logo-128x128.png"
},
"__chrome__minimum_chrome_version": "102",
"__firefox__minimum_firefox_version": "50",
"side_panel": {
"default_path": "side-panel/side-panel.html"
},
Expand Down Expand Up @@ -71,7 +74,7 @@
"src/_locales": "_locales",
"src/popup/popup.html": "popup/popup.html",
"src/options-page/options.html": "options-page/options.html",
"src/welcome": "welcome",
"src/welcome/welcome.html": "welcome/welcome.html",
"src/side-panel/side-panel.html": "side-panel/side-panel.html"
}
}
1 change: 0 additions & 1 deletion src/messages_symlink.json

This file was deleted.

153 changes: 84 additions & 69 deletions tools/compat-checker.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from "fs";
import path from "path";
import { glob } from "glob";
import browserCompatData from "@mdn/browser-compat-data" assert { type: "json" };
import browserCompatData from "@mdn/browser-compat-data" with { type: "json" };

/**
* Analyzes the compatibility of a browser extension against
Expand All @@ -17,22 +17,32 @@ import browserCompatData from "@mdn/browser-compat-data" assert { type: "json" }
* All browsers have automatic updates, so in no time, the minimum supported version will be reaching >95% of users.
*/
export class BrowserCompatibilityAnalyzer {
constructor(extensionDirectory) {
compatOverrides = {
"chrome.runtime.setUninstallURL": 41, // Limits URL to 255 characters.
"chrome.storage.session": 102, // Limits session storage to 1MB.
};

constructor(extensionDirectory, browser = "chrome") {
this.extensionDirectory = extensionDirectory;
this.browser = browser;
this.chromeApi = browserCompatData.webextensions.api;
this.browsers = this.loadManifestBrowserInfo();
this.minBrowserVersion = this.getMinBrowserVersion(
extensionDirectory,
browser
);
this.files = this.getJavaScriptFiles();
}

loadManifestBrowserInfo() {
getMinBrowserVersion(outDir, browser) {
try {
const manifestPath = path.join(this.extensionDirectory, "manifest.json");
const manifestPath = path.join(outDir, "manifest.json");
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
// TODO: Update this table with browsers from args and support prefix like __chrome__.
return {
chrome: manifest.minimum_chrome_version,
firefox: manifest.minimum_firefox_version,
};
const minVersion = manifest[`minimum_${browser}_version`];
if (minVersion) {
return parseInt(minVersion, 10);
} else {
return 0; // All browser versions are supported.
}
} catch (error) {
console.error("Error loading or parsing manifest.json:", error);
return {};
Expand All @@ -48,41 +58,43 @@ export class BrowserCompatibilityAnalyzer {

for (const file of this.files) {
const code = await this.readFile(
path.join(this.extensionDirectory, file),
path.join(this.extensionDirectory, file)
);
if (code) {
const apiCalls = this.findChromeAPICalls(code);
this.checkCompatibility(apiCalls, file, violations);
try {
this.checkCompatibility(apiCalls, file, violations, this.browser);
} catch (error) {
throw Error(`Error checking compatibility for ${file}:${error}`);
}
}
}

Object.keys(violations).map((violationKey) => {
const violation = violations[violationKey];
console.log(
`[${violation.browser}]: ${violation.api} requires version ${violation.required_version} or later.`,
);
const violationsArr = Object.values(violations);
// Find violation with max required version
const violation = violationsArr.reduce(
(max, v) => (max.required_version > v.required_version ? max : v),
violationsArr[0]
);

let notes = "";
if (violation.support_info) {
if (Array.isArray(violation.support_info)) {
notes = violation.support_info[0].notes;
} else {
notes = violation.support_info.notes;
}
}
if (notes) {
console.log("Notes:", notes);
}
console.debug("Support info:", violation.support_info)
if (violation) {
console.error(
`[${violation.api}] requires version ${violation.required_version} or later.`
);
console.debug("Support info:", violation.support_info);

console.log(`Found in ${violation.occurrences.length} places:`);
const occurences =
" " +
violation.occurrences
.map((occurrence) => occurrence.location + "\t" + occurrence.match)
.map((occurrence) => occurrence.location)
.join("\n ");
console.log(occurences, "\n");
});

throw Error(
`[${violation.api}] requires version ${violation.required_version} or later.`
);
}
}

async readFile(file) {
Expand Down Expand Up @@ -123,49 +135,52 @@ export class BrowserCompatibilityAnalyzer {
}
data = data[symbol];
}
return data.__compat;
return data ? data.__compat : null;
}

checkCompatibility(apiCalls, file, violations) {
checkCompatibility(apiCalls, file, violations, browser) {
apiCalls.forEach((call) => {
const apiData = this.getCompatData(call.symbol);
if (apiData) {
for (const browser in this.browsers) {
const version = parseInt(this.browsers[browser], 10);
const supportInfos = apiData.support[browser];
if (!supportInfos) continue;
let latestVersion, latestSupportInfo;
if (Array.isArray(supportInfos)) {
latestVersion = Math.max(...supportInfos.map((s) => s.version_added));
latestSupportInfo = supportInfos.find((s) => s.version_added === "" + latestVersion);
} else {
latestVersion = supportInfos.version_added;
latestSupportInfo = supportInfos;
}
let versionAdded = latestSupportInfo.version_added;

if (versionAdded === true) continue;
versionAdded = parseInt(versionAdded, 10);
if (isNaN(versionAdded)) continue;
const supported = versionAdded <= version;

if (!supported) {
const violationKey = `${browser}:${call.symbol}`;
if (!violations[violationKey]) {
violations[violationKey] = {
browser,
api: call.symbol,
required_version: versionAdded,
support_info: supportInfos,
occurrences: [],
};
}
violations[violationKey].occurrences.push({
location: `${file}:${call.line}`,
match: call.match,
});
}
if (!apiData) {
return;
}
const supportInfos = apiData.support[browser];
if (!supportInfos) return;
let latestVersion, latestSupportInfo;
if (Array.isArray(supportInfos)) {
latestVersion = Math.max(...supportInfos.map((s) => s.version_added));
latestSupportInfo = supportInfos.find(
(s) => s.version_added === "" + latestVersion
);
} else {
latestVersion = supportInfos.version_added;
latestSupportInfo = supportInfos;
}
let versionAdded = latestSupportInfo.version_added;

if (versionAdded === true) return;
versionAdded = parseInt(versionAdded, 10);
if (isNaN(versionAdded)) return;
const overriddenVersion = this.compatOverrides[call.symbol];
const supported = overriddenVersion
? overriddenVersion <= this.minBrowserVersion
: versionAdded <= this.minBrowserVersion;

if (!supported) {
const violationKey = `${browser}:${call.symbol}`;
if (!violations[violationKey]) {
violations[violationKey] = {
browser,
api: call.symbol,
required_version: versionAdded,
support_info: supportInfos,
occurrences: [],
};
}
violations[violationKey].occurrences.push({
location: `${file}:${call.line}`,
match: call.match,
});
}
});
}
Expand Down
73 changes: 47 additions & 26 deletions tools/esbuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import esbuild from "esbuild";
import Jasmine from "jasmine";
import puppeteer from "puppeteer";
import { parse } from "./parse.js";
import manifest from "../src/manifest.json" assert { type: "json" };
import manifest from "../src/manifest.json" with { type: "json" };
import { Translator } from "./translator.js";
import { ImageResizer } from "./image-resizer.js";
import { ManifestValidator } from "./manifest-validator.js";
Expand Down Expand Up @@ -78,8 +78,16 @@ class Build {
new ChromeExtensionDownloader().download(args.url, args.dest);
break;
case "compat":
new BrowserCompatibilityAnalyzer(args.src ?? this.outDir).analyze();
new BrowserCompatibilityAnalyzer(
args.src ?? this.outDir,
this.browser
).analyze();
break;
case "validate":
new ManifestValidator(
this.srcDir ?? this.outDir,
this.browser
).runAllValidations();
case "publish":
new StorePublisher().publishItemPublic("random_id");
break;
Expand Down Expand Up @@ -107,28 +115,24 @@ class Build {
}

// Bundle scripts.
bundleScripts() {
return esbuild
.build({
entryPoints: manifest.__entryPoints,
bundle: true,
minify: this.isProd,
sourcemap: !this.isProd,
loader: {
".txt.html": "text",
".txt.css": "text",
".file.css": "file",
".woff2": "dataurl",
},
banner: {
js: `var IS_DEV_BUILD=${!this.isProd};`,
},
outdir: this.outDir,
target: ["chrome107"], // https://en.wikipedia.org/wiki/Google_Chrome_version_history
})
.catch((err) => {
console.error(err);
});
async bundleScripts() {
await esbuild.build({
entryPoints: manifest.__entryPoints,
bundle: true,
minify: this.isProd,
sourcemap: !this.isProd,
loader: {
".txt.html": "text",
".txt.css": "text",
".file.css": "file",
".woff2": "dataurl",
},
banner: {
js: `var IS_DEV_BUILD=${!this.isProd};`,
},
outdir: this.outDir,
target: ["chrome107"], // https://en.wikipedia.org/wiki/Google_Chrome_version_history
});
}

async watch() {
Expand Down Expand Up @@ -277,8 +281,25 @@ class Build {

// Package extension.
async packageExtension() {
await this.buildExtension();
new ManifestValidator(this.outDir).runAllValidations();
try {
await this.buildExtension();
} catch (e) {
console.error("Build failed: ", e.message);
return;
}

try {
await new ManifestValidator(
this.outDir,
this.browser
).runAllValidations();
} catch (e) {
console.error(
"Aborting packaging due to validation errors: \n",
e.message
);
return;
}
return new Promise((resolve, reject) => {
// Step into the directory to zip to avoid including directory in zip (for firefox).
exec(`cd ${this.outDir} && zip -r archive .`, (error, stdout, stderr) => {
Expand Down
Loading

0 comments on commit ed4eb2c

Please sign in to comment.