Add a bunch of files.
This commit is contained in:
parent
137b7a5f18
commit
ebb365223a
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Tildes Shepherd</title>
|
||||||
|
<link rel="shortcut icon" href="/tildes-shepherd.png" type="image/png">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="catppuccin">
|
||||||
|
<noscript>
|
||||||
|
This WebExtension doesn't work without JavaScript enabled, sorry! 😭
|
||||||
|
</noscript>
|
||||||
|
|
||||||
|
<script type="module" src="./setup.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,13 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 100 100">
|
||||||
|
<rect fill="#002b36" width="100" height="100"/>
|
||||||
|
<g>
|
||||||
|
<rect fill="#859900" width="12.5" height="12.5" x="50" y="75"/> -->
|
||||||
|
<rect fill="#2aa198" width="12.5" height="12.5" x="50" y="62.5"/>
|
||||||
|
<rect fill="#268bd2" width="12.5" height="12.5" x="37.5" y="50"/>
|
||||||
|
<rect fill="#6c71c4" width="12.5" height="12.5" x="25" y="37.5"/>
|
||||||
|
<rect fill="#d33682" width="12.5" height="12.5" x="25" y="25"/>
|
||||||
|
<rect fill="#dc322f" width="12.5" height="12.5" x="37.5" y="12.5"/>
|
||||||
|
<rect fill="#cb4b16" width="12.5" height="12.5" x="50" y="12.5"/>
|
||||||
|
<rect fill="#b58900" width="12.5" height="12.5" x="62.5" y="25"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 720 B |
|
@ -0,0 +1,27 @@
|
||||||
|
// The main entry point for the background script. Note that in Manifest V3 this
|
||||||
|
// is run in a service worker.
|
||||||
|
// https://developer.chrome.com/docs/extensions/migrating/to-service-workers/
|
||||||
|
|
||||||
|
import browser from "webextension-polyfill";
|
||||||
|
|
||||||
|
if ($browser === "firefox") {
|
||||||
|
browser.browserAction.onClicked.addListener(async () => {
|
||||||
|
await browser.runtime.openOptionsPage();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
browser.action.onClicked.addListener(async () => {
|
||||||
|
await browser.runtime.openOptionsPage();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.runtime.onInstalled.addListener(async () => {
|
||||||
|
if (!$dev) {
|
||||||
|
await browser.runtime.openOptionsPage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.runtime.onMessage.addListener(async (message) => {
|
||||||
|
if (message === "open-options-page") {
|
||||||
|
await browser.runtime.openOptionsPage();
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,120 @@
|
||||||
|
import path from "node:path";
|
||||||
|
import process from "node:process";
|
||||||
|
import fsp from "node:fs/promises";
|
||||||
|
import esbuild from "esbuild";
|
||||||
|
import copyPlugin from "esbuild-copy-static-files";
|
||||||
|
import {sassPlugin, type SassPluginOptions} from "esbuild-sass-plugin";
|
||||||
|
import cssnano from "cssnano";
|
||||||
|
import postcss from "postcss";
|
||||||
|
import {createManifest} from "./manifest.js";
|
||||||
|
import {createWebExtConfig} from "./web-ext.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an absolute path from a given relative one, using the directory
|
||||||
|
* this file is located in as the base.
|
||||||
|
*
|
||||||
|
* @param relative The relative path to make absolute.
|
||||||
|
* @returns The absolute path.
|
||||||
|
*/
|
||||||
|
function toAbsolutePath(relative: string): string {
|
||||||
|
return new URL(relative, import.meta.url).pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create variables based on the environment.
|
||||||
|
const browser = process.env.BROWSER ?? "firefox";
|
||||||
|
const dev = process.env.NODE_ENV === "development";
|
||||||
|
const test = process.env.TEST === "true";
|
||||||
|
const watch = process.env.WATCH === "true";
|
||||||
|
|
||||||
|
// Create absolute paths to various directories.
|
||||||
|
const buildDir = toAbsolutePath("../build");
|
||||||
|
const outDir = path.join(buildDir, browser);
|
||||||
|
const sourceDir = toAbsolutePath("../source");
|
||||||
|
|
||||||
|
// Ensure that the output directory exists.
|
||||||
|
await fsp.mkdir(outDir, {recursive: true});
|
||||||
|
|
||||||
|
// Write the WebExtension manifest file.
|
||||||
|
await fsp.writeFile(
|
||||||
|
path.join(outDir, "manifest.json"),
|
||||||
|
JSON.stringify(createManifest(browser)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write the web-ext configuration file.
|
||||||
|
await fsp.writeFile(
|
||||||
|
path.join(buildDir, `web-ext-${browser}.json`),
|
||||||
|
JSON.stringify(createWebExtConfig(browser, buildDir, dev, outDir)),
|
||||||
|
);
|
||||||
|
|
||||||
|
const cssProcessor = postcss([cssnano()]);
|
||||||
|
|
||||||
|
const createSassPlugin = (type: SassPluginOptions["type"]) => {
|
||||||
|
return sassPlugin({
|
||||||
|
type,
|
||||||
|
async transform(source) {
|
||||||
|
// In development, don't do any extra processing.
|
||||||
|
if (dev) {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
// But in production, run the CSS through PostCSS.
|
||||||
|
const {css} = await cssProcessor.process(source, {from: undefined});
|
||||||
|
return css;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const options: esbuild.BuildOptions = {
|
||||||
|
bundle: true,
|
||||||
|
// Define variables to be replaced in the code. Note that these are replaced
|
||||||
|
// "as is" and so we have to stringify them as JSON, otherwise a string won't
|
||||||
|
// have its quotes for example.
|
||||||
|
define: {
|
||||||
|
$browser: JSON.stringify(browser),
|
||||||
|
$dev: JSON.stringify(dev),
|
||||||
|
$test: JSON.stringify(test),
|
||||||
|
},
|
||||||
|
entryPoints: [
|
||||||
|
path.join(sourceDir, "background/setup.ts"),
|
||||||
|
path.join(sourceDir, "options/setup.tsx"),
|
||||||
|
path.join(sourceDir, "content-scripts/setup.ts"),
|
||||||
|
],
|
||||||
|
format: "esm",
|
||||||
|
logLevel: "info",
|
||||||
|
minify: !dev,
|
||||||
|
outdir: outDir,
|
||||||
|
plugins: [
|
||||||
|
// Copy all files from `source/assets/` to the output directory.
|
||||||
|
copyPlugin({src: path.join(sourceDir, "assets/"), dest: outDir}),
|
||||||
|
// Compile SCSS to CSS.
|
||||||
|
createSassPlugin("style"),
|
||||||
|
],
|
||||||
|
// Link sourcemaps in development but omit them in production.
|
||||||
|
sourcemap: dev ? "linked" : false,
|
||||||
|
// Currently code splitting can't be used because we use ES modules and
|
||||||
|
// Firefox doesn't run the background script with `type="module"`.
|
||||||
|
// Once Firefox properly supports Manifest V3 this should be possible though.
|
||||||
|
splitting: false,
|
||||||
|
// Target ES2022, and the first Chromium and Firefox releases from 2022.
|
||||||
|
target: ["es2022", "chrome97", "firefox102"],
|
||||||
|
treeShaking: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const contentStyleOptions: esbuild.BuildOptions = {
|
||||||
|
entryPoints: [path.join(sourceDir, "scss/shepherd/shepherd.scss")],
|
||||||
|
logLevel: options.logLevel,
|
||||||
|
minify: options.minify,
|
||||||
|
outfile: path.join(outDir, "css/shepherd.css"),
|
||||||
|
plugins: [createSassPlugin("css")],
|
||||||
|
sourcemap: options.sourcemap,
|
||||||
|
target: options.target,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (watch) {
|
||||||
|
const context = await esbuild.context(options);
|
||||||
|
const contentStyleContext = await esbuild.context(contentStyleOptions);
|
||||||
|
await Promise.all([context.watch(), contentStyleContext.watch()]);
|
||||||
|
} else {
|
||||||
|
await esbuild.build(options);
|
||||||
|
await esbuild.build(contentStyleOptions);
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
|
|
||||||
|
import {type Manifest} from "webextension-polyfill";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the WebExtension manifest based on the browser target.
|
||||||
|
*
|
||||||
|
* @param browser The browser target ("firefox" or "chromium").
|
||||||
|
* @returns The WebExtension manifest.
|
||||||
|
*/
|
||||||
|
export function createManifest(browser: string): Manifest.WebExtensionManifest {
|
||||||
|
const manifest: Manifest.WebExtensionManifest = {
|
||||||
|
manifest_version: Number.NaN,
|
||||||
|
name: "Tildes Shepherd",
|
||||||
|
version: "0.1.0",
|
||||||
|
permissions: ["storage"],
|
||||||
|
options_ui: {
|
||||||
|
page: "options/index.html",
|
||||||
|
open_in_tab: true,
|
||||||
|
},
|
||||||
|
content_scripts: [
|
||||||
|
{
|
||||||
|
css: ["css/shepherd.css"],
|
||||||
|
js: ["content-scripts/setup.js"],
|
||||||
|
matches: ["https://*.tildes.net/*"],
|
||||||
|
run_at: "document_start",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const icons: Manifest.IconPath = {
|
||||||
|
128: "tildes-shepherd.png",
|
||||||
|
};
|
||||||
|
|
||||||
|
const action: Manifest.ActionManifest = {
|
||||||
|
default_icon: icons,
|
||||||
|
};
|
||||||
|
|
||||||
|
const backgroundScript = "background/setup.js";
|
||||||
|
|
||||||
|
if (browser === "firefox") {
|
||||||
|
manifest.manifest_version = 2;
|
||||||
|
manifest.background = {
|
||||||
|
scripts: [backgroundScript],
|
||||||
|
};
|
||||||
|
manifest.browser_action = action;
|
||||||
|
manifest.browser_specific_settings = {
|
||||||
|
gecko: {
|
||||||
|
// TODO: Add the AMO ID once it has been published.
|
||||||
|
strict_min_version: "102.0",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (browser === "chromium") {
|
||||||
|
manifest.manifest_version = 3;
|
||||||
|
manifest.action = action;
|
||||||
|
manifest.background = {
|
||||||
|
service_worker: backgroundScript,
|
||||||
|
type: "module",
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unknown target browser: ${browser}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Number.isNaN(manifest.manifest_version)) {
|
||||||
|
throw new TypeError("Manifest version is NaN");
|
||||||
|
}
|
||||||
|
|
||||||
|
return manifest;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Type definitions for third-party packages.
|
||||||
|
|
||||||
|
declare module "esbuild-copy-static-files" {
|
||||||
|
import {type cpSync} from "node:fs";
|
||||||
|
import {type Plugin} from "esbuild";
|
||||||
|
|
||||||
|
type CopySyncParameters = Parameters<typeof cpSync>;
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
src?: CopySyncParameters[0];
|
||||||
|
dest?: CopySyncParameters[1];
|
||||||
|
} & CopySyncParameters[2];
|
||||||
|
|
||||||
|
export default function (options: Options): Plugin;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Export something so TypeScript doesn't see this file as an ambient module.
|
||||||
|
export {};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
const $browser: "chromium" | "firefox";
|
||||||
|
const $dev: boolean;
|
||||||
|
const $test: boolean;
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Barebones type definition for web-ext configuration.
|
||||||
|
*
|
||||||
|
* Since web-ext doesn't export any types this is done by ourselves. The keys
|
||||||
|
* mostly follow a camelCased version of the CLI options
|
||||||
|
* (ie. --start-url becomes startUrl).
|
||||||
|
*/
|
||||||
|
type WebExtConfig = {
|
||||||
|
artifactsDir: string;
|
||||||
|
sourceDir: string;
|
||||||
|
verbose?: boolean;
|
||||||
|
|
||||||
|
build: {
|
||||||
|
filename: string;
|
||||||
|
overwriteDest: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
run: {
|
||||||
|
browserConsole: boolean;
|
||||||
|
firefoxProfile: string;
|
||||||
|
keepProfileChanges: boolean;
|
||||||
|
profileCreateIfMissing: boolean;
|
||||||
|
startUrl: string[];
|
||||||
|
target: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the web-ext configuration.
|
||||||
|
*
|
||||||
|
* @param browser The browser target ("firefox" or "chromium").
|
||||||
|
* @param buildDir The path to the build directory.
|
||||||
|
* @param dev Is this for development or production.
|
||||||
|
* @param outDir The path to the output directory.
|
||||||
|
* @returns The configuration for web-ext.
|
||||||
|
*/
|
||||||
|
export function createWebExtConfig(
|
||||||
|
browser: string,
|
||||||
|
buildDir: string,
|
||||||
|
dev: boolean,
|
||||||
|
outDir: string,
|
||||||
|
): WebExtConfig {
|
||||||
|
const config: WebExtConfig = {
|
||||||
|
artifactsDir: path.join(buildDir, "artifacts"),
|
||||||
|
sourceDir: outDir,
|
||||||
|
|
||||||
|
build: {
|
||||||
|
filename: `{name}-{version}-${browser}.zip`,
|
||||||
|
overwriteDest: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
run: {
|
||||||
|
browserConsole: dev,
|
||||||
|
firefoxProfile: path.join(buildDir, "firefox-profile/"),
|
||||||
|
keepProfileChanges: true,
|
||||||
|
profileCreateIfMissing: true,
|
||||||
|
startUrl: [],
|
||||||
|
target: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (browser === "firefox") {
|
||||||
|
config.run.startUrl.push("about:debugging#/runtime/this-firefox");
|
||||||
|
config.run.target.push("firefox-desktop");
|
||||||
|
} else if (browser === "chromium") {
|
||||||
|
config.run.startUrl.push("chrome://extensions/");
|
||||||
|
config.run.target.push("chromium");
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unknown target browser: ${browser}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
Loading…
Reference in New Issue