This commit is contained in:
Lalle 2023-05-10 20:46:33 +02:00
parent ec2284bfb9
commit 17389af48c
No known key found for this signature in database
GPG Key ID: A6583D207A8F6B0D
17 changed files with 1464 additions and 282 deletions

43
.eslintrc.json Normal file
View File

@ -0,0 +1,43 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended"
],
"overrides": [
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react",
"@typescript-eslint"
],
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"double"
],
"semi": [
"error",
"never"
]
}
}

131
.gitignore vendored
View File

@ -1,3 +1,134 @@
**/dist **/dist
**/node_modules **/node_modules
.DS_Store .DS_Store
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

View File

@ -6,6 +6,7 @@
"description": "A @samrum/vite-plugin-web-extension web extension", "description": "A @samrum/vite-plugin-web-extension web extension",
"scripts": { "scripts": {
"build": "vite build", "build": "vite build",
"lint": "eslint --ext .ts,.tsx src --fix",
"watch": "vite build --watch --mode development --minify false", "watch": "vite build --watch --mode development --minify false",
"dev": "vite", "dev": "vite",
"serve:firefox": "web-ext run --start-url \"about:debugging#/runtime/this-firefox\" --source-dir ./dist/", "serve:firefox": "web-ext run --start-url \"about:debugging#/runtime/this-firefox\" --source-dir ./dist/",
@ -18,12 +19,22 @@
"@types/react": "^18.0.28", "@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",
"@types/webextension-polyfill": "^0.10.0", "@types/webextension-polyfill": "^0.10.0",
"@typescript-eslint/eslint-plugin": "^5.59.5",
"@typescript-eslint/parser": "^5.59.5",
"@vitejs/plugin-react": "^4.0.0", "@vitejs/plugin-react": "^4.0.0",
"eslint": "^8.40.0",
"eslint-plugin-react": "^7.32.2",
"typescript": "^5.0.4", "typescript": "^5.0.4",
"vite": "~4.3.1", "vite": "~4.3.1",
"web-ext": "^7.6.1" "web-ext": "^7.6.1"
}, },
"dependencies": { "dependencies": {
"eslint-config-standard-with-typescript": "^34.0.1",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"imageboard": "^0.6.25",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"webextension-polyfill": "^0.10.0" "webextension-polyfill": "^0.10.0"

1204
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,23 @@
import React, { useState } from "react"; import "./PageContent.css"
import "./PageContent.css";
import logo from "~/assets/logo.svg"; import React, { useState } from "react"
import logo from "~/assets/logo.svg"
function PageContent(props: { children: React.ReactNode }) { function PageContent(props: { children: React.ReactNode }) {
const imageUrl = new URL(logo, import.meta.url).href; const imageUrl = new URL(logo, import.meta.url).href
const [count, setCount] = useState(0); const [count, setCount] = useState(0)
return ( return (
<div> <div>
<img src={imageUrl} height="45" alt="" /> <img src={imageUrl} height="45" alt="" />
<h1>{props.children}</h1> <h1>{props.children}</h1>
<button type="button" onClick={() => setCount((count) => count + 1)}> <button type="button" onClick={() => setCount((count) => count + 1)}>
Clicks: {count} Clicks: {count}
</button> </button>
</div> </div>
); )
} }
export default PageContent; export default PageContent

View File

@ -1,5 +1,5 @@
import browser from "webextension-polyfill"; import browser from "webextension-polyfill"
browser.runtime.onInstalled.addListener(() => { browser.runtime.onInstalled.addListener(() => {
console.log("Extension installed"); console.log("Extension installed")
}); })

View File

@ -1 +1 @@
import "./main"; import "./main"

View File

@ -1 +1 @@
import "./main"; import "./main"

View File

@ -1,14 +1,15 @@
import logo from "~/assets/logo.svg"; import React from "react"
import "./App.css"; import logo from "~/assets/logo.svg"
import "./App.css"
function App() { function App() {
const logoImageUrl = new URL(logo, import.meta.url).href; const logoImageUrl = new URL(logo, import.meta.url).href
return ( return (
<div className="logo"> <div className="logo">
<img src={logoImageUrl} height="50" alt="" /> <img src={logoImageUrl} height="50" alt="" />
</div> </div>
); )
} }
export default App; export default App

View File

@ -1,13 +1,13 @@
import "../../enableDevHmr"; import "../../enableDevHmr"
import React from "react"; import React from "react"
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client"
import renderContent from "../renderContent"; import renderContent from "../renderContent"
import App from "./App"; import App from "./App"
renderContent(import.meta.PLUGIN_WEB_EXT_CHUNK_CSS_PATHS, (appRoot) => { renderContent(import.meta.PLUGIN_WEB_EXT_CHUNK_CSS_PATHS, (appRoot) => {
ReactDOM.createRoot(appRoot).render( ReactDOM.createRoot(appRoot).render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode> </React.StrictMode>
); )
}); })

View File

@ -1,32 +1,32 @@
import browser from "webextension-polyfill"; import browser from "webextension-polyfill"
export default async function renderContent( export default async function renderContent(
cssPaths: string[], cssPaths: string[],
render: (appRoot: HTMLElement) => void render: (appRoot: HTMLElement) => void
) { ) {
const appContainer = document.createElement("div"); const appContainer = document.createElement("div")
const shadowRoot = appContainer.attachShadow({ const shadowRoot = appContainer.attachShadow({
mode: import.meta.env.MODE === "development" ? "open" : "closed", mode: import.meta.env.MODE === "development" ? "open" : "closed",
}); })
const appRoot = document.createElement("div"); const appRoot = document.createElement("div")
if (import.meta.hot) { if (import.meta.hot) {
const { addViteStyleTarget } = await import( const { addViteStyleTarget } = await import(
"@samrum/vite-plugin-web-extension/client" "@samrum/vite-plugin-web-extension/client"
); )
await addViteStyleTarget(shadowRoot); await addViteStyleTarget(shadowRoot)
} else { } else {
cssPaths.forEach((cssPath: string) => { cssPaths.forEach((cssPath: string) => {
const styleEl = document.createElement("link"); const styleEl = document.createElement("link")
styleEl.setAttribute("rel", "stylesheet"); styleEl.setAttribute("rel", "stylesheet")
styleEl.setAttribute("href", browser.runtime.getURL(cssPath)); styleEl.setAttribute("href", browser.runtime.getURL(cssPath))
shadowRoot.appendChild(styleEl); shadowRoot.appendChild(styleEl)
}); })
} }
shadowRoot.appendChild(appRoot); shadowRoot.appendChild(appRoot)
document.body.appendChild(appContainer); document.body.appendChild(appContainer)
render(appRoot); render(appRoot)
} }

View File

@ -1,9 +1,11 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-empty-function */
// @ts-nocheck // @ts-nocheck
import RefreshRuntime from "/@react-refresh"; import RefreshRuntime from "/@react-refresh"
if (import.meta.hot) { if (import.meta.hot) {
RefreshRuntime.injectIntoGlobalHook(window); RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {}; window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type; window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true; window.__vite_plugin_react_preamble_installed__ = true
} }

View File

@ -1,12 +1,13 @@
import PageContent from "~/components/PageContent"; import PageContent from "~/components/PageContent"
import "./App.css"; import React from "react"
import "./App.css"
function App() { function App() {
return ( return (
<main> <main>
<PageContent>Options</PageContent> <PageContent>Options</PageContent>
</main> </main>
); )
} }
export default App; export default App

View File

@ -1,10 +1,10 @@
import "../enableDevHmr"; import "../enableDevHmr"
import React from "react"; import React from "react"
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client"
import App from "./App"; import App from "./App"
ReactDOM.createRoot(document.getElementById("app") as HTMLElement).render( ReactDOM.createRoot(document.getElementById("app") as HTMLElement).render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode> </React.StrictMode>
); )

View File

@ -1,12 +1,13 @@
import PageContent from "~/components/PageContent"; import PageContent from "~/components/PageContent"
import "./App.css"; import React from "react"
import "./App.css"
function App() { function App() {
return ( return (
<main> <main>
<PageContent>Popup</PageContent> <PageContent>Popup</PageContent>
</main> </main>
); )
} }
export default App; export default App

View File

@ -1,10 +1,10 @@
import "../enableDevHmr"; import "../enableDevHmr"
import React from "react"; import React from "react"
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client"
import App from "./App"; import App from "./App"
ReactDOM.createRoot(document.getElementById("app") as HTMLElement).render( ReactDOM.createRoot(document.getElementById("app") as HTMLElement).render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode> </React.StrictMode>
); )

View File

@ -1,89 +1,89 @@
import pkg from "../package.json"; import pkg from "../package.json"
const sharedManifest = { const sharedManifest = {
content_scripts: [ content_scripts: [
{ {
js: ["src/entries/contentScript/primary/main.tsx"], js: ["src/entries/contentScript/primary/main.tsx"],
matches: ["*://*/*"], matches: ["*://*/*"],
}, },
], ],
icons: { icons: {
16: "icons/16.png", 16: "icons/16.png",
19: "icons/19.png", 19: "icons/19.png",
32: "icons/32.png", 32: "icons/32.png",
38: "icons/38.png", 38: "icons/38.png",
48: "icons/48.png", 48: "icons/48.png",
64: "icons/64.png", 64: "icons/64.png",
96: "icons/96.png", 96: "icons/96.png",
128: "icons/128.png", 128: "icons/128.png",
256: "icons/256.png", 256: "icons/256.png",
512: "icons/512.png", 512: "icons/512.png",
}, },
options_ui: { options_ui: {
page: "src/entries/options/index.html", page: "src/entries/options/index.html",
open_in_tab: true, open_in_tab: true,
}, },
permissions: [], permissions: [],
}; }
const browserAction = { const browserAction = {
default_icon: { default_icon: {
16: "icons/16.png", 16: "icons/16.png",
19: "icons/19.png", 19: "icons/19.png",
32: "icons/32.png", 32: "icons/32.png",
38: "icons/38.png", 38: "icons/38.png",
}, },
default_popup: "src/entries/popup/index.html", default_popup: "src/entries/popup/index.html",
}; }
const ManifestV2 = { const ManifestV2 = {
...sharedManifest, ...sharedManifest,
background: { background: {
scripts: ["src/entries/background/script.ts"], scripts: ["src/entries/background/script.ts"],
persistent: true, persistent: true,
}, },
browser_action: browserAction, browser_action: browserAction,
options_ui: { options_ui: {
...sharedManifest.options_ui, ...sharedManifest.options_ui,
chrome_style: false, chrome_style: false,
}, },
permissions: [...sharedManifest.permissions, "*://*/*"], permissions: [...sharedManifest.permissions, "*://*/*"],
}; }
const ManifestV3 = { const ManifestV3 = {
...sharedManifest, ...sharedManifest,
action: browserAction, action: browserAction,
background: { background: {
service_worker: "src/entries/background/serviceWorker.ts", service_worker: "src/entries/background/serviceWorker.ts",
}, },
host_permissions: ["*://*/*"], host_permissions: ["*://*/*"],
}; }
export function getManifest(manifestVersion: number): chrome.runtime.ManifestV2 | chrome.runtime.ManifestV3 { export function getManifest(manifestVersion: number): chrome.runtime.ManifestV2 | chrome.runtime.ManifestV3 {
const manifest = { const manifest = {
author: pkg.author, author: pkg.author,
description: pkg.description, description: pkg.description,
name: pkg.displayName ?? pkg.name, name: pkg.displayName ?? pkg.name,
version: pkg.version, version: pkg.version,
}; }
if (manifestVersion === 2) { if (manifestVersion === 2) {
return { return {
...manifest, ...manifest,
...ManifestV2, ...ManifestV2,
manifest_version: manifestVersion, manifest_version: manifestVersion,
}; }
} }
if (manifestVersion === 3) { if (manifestVersion === 3) {
return { return {
...manifest, ...manifest,
...ManifestV3, ...ManifestV3,
manifest_version: manifestVersion, manifest_version: manifestVersion,
}; }
} }
throw new Error( throw new Error(
`Missing manifest definition for manifestVersion ${manifestVersion}` `Missing manifest definition for manifestVersion ${manifestVersion}`
); )
} }