Initial commit
This commit is contained in:
84
chrome-extension/manifest.ts
Normal file
84
chrome-extension/manifest.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import type { ManifestType } from '@extension/shared';
|
||||
|
||||
const packageJson = JSON.parse(readFileSync('./package.json', 'utf8'));
|
||||
|
||||
/**
|
||||
* @prop default_locale
|
||||
* if you want to support multiple languages, you can use the following reference
|
||||
* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Internationalization
|
||||
*
|
||||
* @prop browser_specific_settings
|
||||
* Must be unique to your extension to upload to addons.mozilla.org
|
||||
* (you can delete if you only want a chrome extension)
|
||||
*
|
||||
* @prop permissions
|
||||
* Firefox doesn't support sidePanel (It will be deleted in manifest parser)
|
||||
*
|
||||
* @prop content_scripts
|
||||
* css: ['content.css'], // public folder
|
||||
*/
|
||||
const manifest = {
|
||||
manifest_version: 3,
|
||||
default_locale: 'en',
|
||||
name: '__MSG_extensionName__',
|
||||
browser_specific_settings: {
|
||||
gecko: {
|
||||
id: 'example@example.com',
|
||||
strict_min_version: '109.0',
|
||||
},
|
||||
},
|
||||
version: packageJson.version,
|
||||
description: '__MSG_extensionDescription__',
|
||||
host_permissions: ['<all_urls>'],
|
||||
permissions: ['storage', 'scripting', 'tabs', 'notifications', 'sidePanel'],
|
||||
options_page: 'options/index.html',
|
||||
background: {
|
||||
service_worker: 'background.js',
|
||||
type: 'module',
|
||||
},
|
||||
action: {
|
||||
default_popup: 'popup/index.html',
|
||||
default_icon: 'icon-34.png',
|
||||
},
|
||||
chrome_url_overrides: {
|
||||
newtab: 'new-tab/index.html',
|
||||
},
|
||||
icons: {
|
||||
'128': 'icon-128.png',
|
||||
},
|
||||
content_scripts: [
|
||||
{
|
||||
matches: ['http://*/*', 'https://*/*', '<all_urls>'],
|
||||
js: ['content/all.iife.js'],
|
||||
},
|
||||
{
|
||||
matches: ['https://example.com/*'],
|
||||
js: ['content/example.iife.js'],
|
||||
},
|
||||
{
|
||||
matches: ['http://*/*', 'https://*/*', '<all_urls>'],
|
||||
js: ['content-ui/all.iife.js'],
|
||||
},
|
||||
{
|
||||
matches: ['https://example.com/*'],
|
||||
js: ['content-ui/example.iife.js'],
|
||||
},
|
||||
{
|
||||
matches: ['http://*/*', 'https://*/*', '<all_urls>'],
|
||||
css: ['content.css'],
|
||||
},
|
||||
],
|
||||
devtools_page: 'devtools/index.html',
|
||||
web_accessible_resources: [
|
||||
{
|
||||
resources: ['*.js', '*.css', '*.svg', 'icon-128.png', 'icon-34.png'],
|
||||
matches: ['*://*/*'],
|
||||
},
|
||||
],
|
||||
side_panel: {
|
||||
default_path: 'side-panel/index.html',
|
||||
},
|
||||
} satisfies ManifestType;
|
||||
|
||||
export default manifest;
|
||||
36
chrome-extension/package.json
Normal file
36
chrome-extension/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "chrome-extension",
|
||||
"version": "0.5.0",
|
||||
"description": "chrome extension - core settings",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"clean:node_modules": "pnpx rimraf node_modules",
|
||||
"clean:turbo": "rimraf .turbo",
|
||||
"clean": "pnpm clean:turbo && pnpm clean:node_modules",
|
||||
"ready": "tsc -b pre-build.tsconfig.json",
|
||||
"build": "vite build",
|
||||
"dev": "vite build --mode development",
|
||||
"test": "vitest run",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "pnpm lint --fix",
|
||||
"prettier": "prettier . --write --ignore-path ../.prettierignore",
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@extension/env": "workspace:*",
|
||||
"@extension/shared": "workspace:*",
|
||||
"@extension/storage": "workspace:*",
|
||||
"webextension-polyfill": "^0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@extension/dev-utils": "workspace:*",
|
||||
"@extension/hmr": "workspace:*",
|
||||
"@extension/tsconfig": "workspace:*",
|
||||
"@extension/vite-config": "workspace:*",
|
||||
"@laynezh/vite-plugin-lib-assets": "^1.1.0",
|
||||
"magic-string": "^0.30.17",
|
||||
"ts-loader": "^9.5.2"
|
||||
}
|
||||
}
|
||||
9
chrome-extension/pre-build.tsconfig.json
Normal file
9
chrome-extension/pre-build.tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./",
|
||||
"noEmit": false
|
||||
},
|
||||
"include": ["manifest.ts"],
|
||||
"exclude": [""]
|
||||
}
|
||||
0
chrome-extension/public/content.css
Normal file
0
chrome-extension/public/content.css
Normal file
BIN
chrome-extension/public/icon-128.png
Normal file
BIN
chrome-extension/public/icon-128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
chrome-extension/public/icon-34.png
Normal file
BIN
chrome-extension/public/icon-34.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
9
chrome-extension/src/background/index.ts
Normal file
9
chrome-extension/src/background/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import 'webextension-polyfill';
|
||||
import { exampleThemeStorage } from '@extension/storage';
|
||||
|
||||
exampleThemeStorage.get().then(theme => {
|
||||
console.log('theme', theme);
|
||||
});
|
||||
|
||||
console.log('Background loaded');
|
||||
console.log("Edit 'chrome-extension/src/background/index.ts' and save to reload.");
|
||||
10
chrome-extension/tsconfig.json
Normal file
10
chrome-extension/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "@extension/tsconfig/app",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@src/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src", "utils", "vite.config.mts", "manifest.ts", "../node_modules/@types"]
|
||||
}
|
||||
83
chrome-extension/utils/plugins/make-manifest-plugin.ts
Normal file
83
chrome-extension/utils/plugins/make-manifest-plugin.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { ManifestParser } from '@extension/dev-utils';
|
||||
import { IS_DEV, IS_FIREFOX } from '@extension/env';
|
||||
import { colorfulLog } from '@extension/shared';
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { platform } from 'node:process';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import type { ManifestType } from '@extension/shared';
|
||||
import type { PluginOption } from 'vite';
|
||||
|
||||
const manifestFile = resolve(import.meta.dirname, '..', '..', 'manifest.js');
|
||||
const refreshFilePath = resolve(
|
||||
import.meta.dirname,
|
||||
'..',
|
||||
'..',
|
||||
'..',
|
||||
'packages',
|
||||
'hmr',
|
||||
'dist',
|
||||
'lib',
|
||||
'injections',
|
||||
'refresh.js',
|
||||
);
|
||||
|
||||
const withHMRId = (code: string) => `(function() {let __HMR_ID = 'chrome-extension-hmr';${code}\n})();`;
|
||||
|
||||
const getManifestWithCacheBurst = async () => {
|
||||
const withCacheBurst = (path: string) => `${path}?${Date.now().toString()}`;
|
||||
|
||||
/**
|
||||
* In Windows, import() doesn't work without file:// protocol.
|
||||
* So, we need to convert path to file:// protocol. (url.pathToFileURL)
|
||||
*/
|
||||
if (platform === 'win32') {
|
||||
return (await import(withCacheBurst(pathToFileURL(manifestFile).href))).default;
|
||||
} else {
|
||||
return (await import(withCacheBurst(manifestFile))).default;
|
||||
}
|
||||
};
|
||||
|
||||
const addRefreshContentScript = (manifest: ManifestType) => {
|
||||
manifest.content_scripts = manifest.content_scripts || [];
|
||||
manifest.content_scripts.push({
|
||||
matches: ['http://*/*', 'https://*/*', '<all_urls>'],
|
||||
js: ['refresh.js'], // for public's HMR(refresh) support
|
||||
});
|
||||
};
|
||||
|
||||
export default (config: { outDir: string }): PluginOption => {
|
||||
const makeManifest = (manifest: ManifestType, to: string) => {
|
||||
if (!existsSync(to)) {
|
||||
mkdirSync(to);
|
||||
}
|
||||
|
||||
const manifestPath = resolve(to, 'manifest.json');
|
||||
|
||||
if (IS_DEV) {
|
||||
addRefreshContentScript(manifest);
|
||||
}
|
||||
|
||||
writeFileSync(manifestPath, ManifestParser.convertManifestToString(manifest, IS_FIREFOX));
|
||||
|
||||
const refreshFileString = readFileSync(refreshFilePath, 'utf-8');
|
||||
|
||||
if (IS_DEV) {
|
||||
writeFileSync(resolve(to, 'refresh.js'), withHMRId(refreshFileString));
|
||||
}
|
||||
|
||||
colorfulLog(`Manifest file copy complete: ${manifestPath}`, 'success');
|
||||
};
|
||||
|
||||
return {
|
||||
name: 'make-manifest',
|
||||
buildStart() {
|
||||
this.addWatchFile(manifestFile);
|
||||
},
|
||||
async writeBundle() {
|
||||
const outDir = config.outDir;
|
||||
const manifest = await getManifestWithCacheBurst();
|
||||
makeManifest(manifest, outDir);
|
||||
},
|
||||
};
|
||||
};
|
||||
52
chrome-extension/vite.config.mts
Normal file
52
chrome-extension/vite.config.mts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { resolve } from 'node:path';
|
||||
import { defineConfig, type PluginOption } from 'vite';
|
||||
import libAssetsPlugin from '@laynezh/vite-plugin-lib-assets';
|
||||
import makeManifestPlugin from './utils/plugins/make-manifest-plugin.js';
|
||||
import { watchPublicPlugin, watchRebuildPlugin } from '@extension/hmr';
|
||||
import { watchOption } from '@extension/vite-config';
|
||||
import env, { IS_DEV, IS_PROD } from '@extension/env';
|
||||
import { nodePolyfills } from 'vite-plugin-node-polyfills';
|
||||
|
||||
const rootDir = resolve(import.meta.dirname);
|
||||
const srcDir = resolve(rootDir, 'src');
|
||||
|
||||
const outDir = resolve(rootDir, '..', 'dist');
|
||||
export default defineConfig({
|
||||
define: {
|
||||
'process.env': env,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@root': rootDir,
|
||||
'@src': srcDir,
|
||||
'@assets': resolve(srcDir, 'assets'),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
libAssetsPlugin({
|
||||
outputPath: outDir,
|
||||
}) as PluginOption,
|
||||
watchPublicPlugin(),
|
||||
makeManifestPlugin({ outDir }),
|
||||
IS_DEV && watchRebuildPlugin({ reload: true, id: 'chrome-extension-hmr' }),
|
||||
nodePolyfills(),
|
||||
],
|
||||
publicDir: resolve(rootDir, 'public'),
|
||||
build: {
|
||||
lib: {
|
||||
name: 'BackgroundScript',
|
||||
fileName: 'background',
|
||||
formats: ['es'],
|
||||
entry: resolve(srcDir, 'background', 'index.ts'),
|
||||
},
|
||||
outDir,
|
||||
emptyOutDir: false,
|
||||
sourcemap: IS_DEV,
|
||||
minify: IS_PROD,
|
||||
reportCompressedSize: IS_PROD,
|
||||
watch: watchOption,
|
||||
rollupOptions: {
|
||||
external: ['chrome'],
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user