refac: improve multi browser support

This commit is contained in:
sethburkart123
2024-08-29 16:28:56 +10:00
parent f996e4bf19
commit 125ebfbaea
26 changed files with 519 additions and 133 deletions
+16
View File
@@ -0,0 +1,16 @@
import fs from "fs";
import mime from "mime-types";
export const base64Loader = {
name: "base64-loader",
transform(_: any, id: string) {
const [filePath, query] = id.split("?");
if (query !== "base64") return null;
const data = fs.readFileSync(filePath, { encoding: 'base64' });
const mimeType = mime.lookup(filePath);
const dataURL = `data:${mimeType};base64,${data}`;
return `export default '${dataURL}';`;
},
};
+33
View File
@@ -0,0 +1,33 @@
import type { Browser, BuildTarget, Manifest } from './types'
import type { AnyCase } from './utils'
/**
*
*
* @export
* @param {Manifest} manifest
* @param {AnyCase<Browser>} browser
* @return {*} {@link BuildTarget}
*/
export function createManifest(
manifest: Manifest,
browser: AnyCase<Browser>,
): BuildTarget {
return {
manifest,
browser,
}
}
/**
* create a base Manifest to inherit from
* type Manifest = chrome.runtime.ManifestV3
*
* use as shared base to extend inBrowser manifests
*
* @export
* @param {Manifest} manifest
* @return {*} {@link Manifest}
*/
export function createManifestBase(manifest: Manifest): Manifest {
return manifest
}
+90
View File
@@ -0,0 +1,90 @@
const glob = require('glob');
const semver = require('semver');
const { execSync } = require('child_process');
const path = require('path');
function getLatestVersion(files) {
console.log('Files passed to getLatestVersion:', files);
const versions = files.map(file => {
const match = file.match(/@(\d+\.\d+\.\d+)-/);
console.log('Matching file:', file, 'Version found:', match ? match[1] : 'None');
return match ? match[1] : null;
}).filter(Boolean);
console.log('Extracted versions:', versions);
const latestVersion = semver.maxSatisfying(versions, '*');
console.log('Latest version:', latestVersion);
return latestVersion;
}
function getLatestFiles(browser) {
const pattern = `dist/betterseqtaplus@*-*${browser}.zip`;
console.log('Glob pattern:', pattern);
const files = glob.sync(pattern);
console.log('Files found for browser', browser, ':', files);
const latestVersion = getLatestVersion(files);
const latestFile = files.find(file => file.includes(latestVersion));
console.log('Latest file for browser', browser, ':', latestFile);
return latestFile;
}
function zipSources() {
const zipFileName = `dist/betterseqtaplus@latest-sources.zip`;
const excludePatterns = [
'node_modules',
'dist',
'.env*',
'.git',
'.github',
'.vscode',
'LICENSE',
'package.json'
].map(pattern => `-x!${pattern}`).join(' ');
const zipCommand = `7z a ${zipFileName} . ${excludePatterns}`;
console.log('Zipping project sources with command:', zipCommand);
execSync(zipCommand, { stdio: 'inherit' });
return zipFileName;
}
function runPublishCommand(browsers) {
const chromeZip = browsers.includes('chrome') ? getLatestFiles('chrome') : null;
const firefoxZip = browsers.includes('firefox') ? getLatestFiles('firefox') : null;
const firefoxSourcesZip = browsers.includes('firefox') ? zipSources() : null;
console.log('Chrome zip:', chromeZip);
console.log('Firefox zip:', firefoxZip);
console.log('Firefox sources zip:', firefoxSourcesZip);
if (browsers.length === 0) {
console.log('No browsers specified. Exiting.');
process.exit(0);
}
if ((browsers.includes('chrome') && !chromeZip) || (browsers.includes('firefox') && (!firefoxZip || !firefoxSourcesZip))) {
console.error('Could not find required zip files for specified browsers.');
process.exit(1);
}
let command = 'publish-extension';
if (chromeZip) {
command += ` --chrome-zip ${chromeZip}`;
}
if (firefoxZip && firefoxSourcesZip) {
command += ` --firefox-zip ${firefoxZip} --firefox-sources-zip ${firefoxSourcesZip}`;
}
console.log('Running command:', command);
execSync(command, { stdio: 'inherit' });
}
// Parse command-line arguments
const args = process.argv.slice(2);
const browserIndex = args.indexOf('--b');
const browsers = browserIndex !== -1 ? args.slice(browserIndex + 1) : [];
runPublishCommand(browsers);
+104
View File
@@ -0,0 +1,104 @@
import type { ManifestV3Export } from '@crxjs/vite-plugin'
import { type AnyCase, createEnum } from './utils'
export const FrameworkEnum = {
React: 'React',
Vanilla: 'Vanilla',
Preact: 'Preact',
Lit: 'Lit',
Svelte: 'Svelte',
Vue: 'Vue',
} as const
export const BrowserEnum = {
Chrome: 'Chrome',
Brave: 'Brave',
Opera: 'Opera',
Edge: 'Edge',
Firefox: 'Firefox',
Safari: 'Safari',
} as const
const LanguageEnum = {
TypeScript: 'TypeScript',
JavaScript: 'JavaScript',
} as const
export const StyleEnum = {
Tailwind: 'Tailwind',
} as const
export const PackageManagerEnum = {
Bun: 'Bun',
PnPm: 'PnPm',
Npm: 'Npm',
Yarn: 'Yarn',
} as const
// see: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/firefox-webext-browser/index.d.ts
export type BrowserSpecificSettings = {
browser_specific_settings?: {
gecko?: {
id: string
strict_min_version?: string
strict_max_version?: string
}
}
}
export type Manifest = ManifestV3Export
export type ManifestIcons = chrome.runtime.ManifestIcons
export type ManifestBackground = chrome.runtime.ManifestV3['background']
export type ManifestContentScripts =
chrome.runtime.ManifestV3['content_scripts']
export type ManifestWebAccessibleResources =
chrome.runtime.ManifestV3['web_accessible_resources']
export type ManifestCommands = chrome.runtime.ManifestV3['commands']
export type ManifestAction = chrome.runtime.ManifestV3['action']
export type ManifestPermissions = chrome.runtime.ManifestV3['permissions']
export type ManifestOptionsUI = chrome.runtime.ManifestV3['options_ui']
export type ManifestURLOverrides =
chrome.runtime.ManifestV3['chrome_url_overrides']
export type BrowserName<T extends string> = Capitalize<T> | Lowercase<T>
export type BrowserEnumType<T extends string> = {
[browser in BrowserName<T>]: BrowserName<T>
}
export type BuildMode = AnyCase<Browser>
export type BuildTarget = {
manifest: Manifest
browser: AnyCase<Browser>
}
export type BuildConfig = {
command?: 'build' | 'serve'
mode?: AnyCase<Browser> | string | undefined
}
export interface Repository {
type: string
url?: string
bugs?: Bugs
}
export interface Bugs {
url?: string
email?: string
}
export type Browser = (typeof BrowserEnum)[keyof typeof BrowserEnum]
export const Browser: AnyCase<Browser> = createEnum(BrowserEnum)
export type PackageManager =
(typeof PackageManagerEnum)[keyof typeof PackageManagerEnum]
export const PackageManager: AnyCase<PackageManager> =
createEnum(PackageManagerEnum)
export type Framework = (typeof FrameworkEnum)[keyof typeof FrameworkEnum]
export const Framework: AnyCase<Framework> = createEnum(FrameworkEnum)
export type Style = (typeof StyleEnum)[keyof typeof StyleEnum]
export const Style: AnyCase<Style> = createEnum(StyleEnum)
export type Language = (typeof LanguageEnum)[keyof typeof LanguageEnum]
export const Language: AnyCase<Language> = createEnum(LanguageEnum)
+21
View File
@@ -0,0 +1,21 @@
export type ObjectValues<T> = T[keyof T]
export function createEnum<T extends Record<string, string>>(enumObj: T) {
return Object.values(enumObj) as unknown as ObjectValues<T>
}
export type AnyCase<T extends string> =
| Uppercase<T>
| Lowercase<T>
| Capitalize<T>
| Uncapitalize<T>
export type AnyCaseLanguage<T extends string, K extends string> =
| Uppercase<T | K>
| Lowercase<T | K>
| Capitalize<T | K>
| Uncapitalize<T | K>
export type OptionalKeys<T> = {
[K in keyof T as undefined extends T[K] ? K : never]: T[K]
}