mirror of
https://github.com/JosunLP/BrowserExtensionTemplate.git
synced 2025-12-15 01:40:06 +00:00
Compare commits
26 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11ecc63799 | ||
|
|
78e6cf87fe | ||
|
|
482151f980 | ||
|
|
28f710f9f5 | ||
|
|
8f99895c88 | ||
|
|
41094cf6a9 | ||
|
|
1723196db6 | ||
|
|
977ce6719f | ||
|
|
b225218e94 | ||
|
|
693a8da648 | ||
|
|
95121af7c3 | ||
|
|
741de12320 | ||
|
|
66fd16659f | ||
|
|
2319a1fd9b | ||
|
|
43b2d8c82b | ||
|
|
69b5363c88 | ||
|
|
241d3d02c6 | ||
|
|
2b9595746b | ||
|
|
f754b0a5ed | ||
|
|
b32594b1d1 | ||
|
|
1d3600de32 | ||
|
|
9145b60898 | ||
|
|
69a4cc6907 | ||
|
|
ab222a60e3 | ||
|
|
a85764891d | ||
|
|
1c906cc63c |
32 changed files with 1651 additions and 847 deletions
35
.editorconfig
Normal file
35
.editorconfig
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{js,ts,svg,md}]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
[*.{ts,js}]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[package.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.{sass,scss}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.{css,less}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.{json,yml,yaml}]
|
||||||
|
indent_style = space
|
||||||
31
.eslintrc.json
Normal file
31
.eslintrc.json
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2022": true,
|
||||||
|
"webextensions": true
|
||||||
|
},
|
||||||
|
"extends": ["eslint:recommended", "@typescript-eslint/recommended"],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": ["@typescript-eslint"],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"argsIgnorePattern": "^_",
|
||||||
|
"varsIgnorePattern": "^_"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "warn",
|
||||||
|
"@typescript-eslint/no-explicit-any": "error",
|
||||||
|
"@typescript-eslint/prefer-const": "error",
|
||||||
|
"@typescript-eslint/no-var-requires": "error",
|
||||||
|
"prefer-const": "error",
|
||||||
|
"no-var": "error",
|
||||||
|
"no-console": "warn"
|
||||||
|
},
|
||||||
|
"ignorePatterns": ["dist/", "node_modules/", "*.js"]
|
||||||
|
}
|
||||||
71
.github/workflows/codeql-analysis.yml
vendored
Normal file
71
.github/workflows/codeql-analysis.yml
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
pull_request:
|
||||||
|
# The branches below must be a subset of the branches above
|
||||||
|
branches: ["main"]
|
||||||
|
schedule:
|
||||||
|
- cron: "37 13 * * 4"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: ["javascript"]
|
||||||
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
|
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
|
||||||
|
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||||
|
# queries: security-extended,security-and-quality
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
|
|
||||||
|
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||||
|
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||||
|
|
||||||
|
# - run: |
|
||||||
|
# echo "Run, Build Application using script"
|
||||||
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -688,5 +688,7 @@ FodyWeavers.xsd
|
||||||
dist
|
dist
|
||||||
|
|
||||||
tools/syncConfig.js
|
tools/syncConfig.js
|
||||||
tools/deploy.js
|
tools/parse.js
|
||||||
tools/v2.js
|
tools/v2.js
|
||||||
|
tools/clean.js
|
||||||
|
package-lock.json
|
||||||
|
|
|
||||||
8
.prettierignore
Normal file
8
.prettierignore
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.sass
|
||||||
|
*.scss
|
||||||
19
.prettierrc.json
Normal file
19
.prettierrc.json
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 100,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"endOfLine": "auto",
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.json",
|
||||||
|
"options": {
|
||||||
|
"parser": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022 Jonas Pfalzgraf
|
Copyright (c) 2024 Jonas Pfalzgraf
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
215
README.md
215
README.md
|
|
@ -4,35 +4,222 @@
|
||||||
[](https://github.com/JosunLP/BrowserExtensionTemplate/network)
|
[](https://github.com/JosunLP/BrowserExtensionTemplate/network)
|
||||||
[](https://github.com/JosunLP/BrowserExtensionTemplate/stargazers)
|
[](https://github.com/JosunLP/BrowserExtensionTemplate/stargazers)
|
||||||
[](https://github.com/JosunLP/BrowserExtensionTemplate)
|
[](https://github.com/JosunLP/BrowserExtensionTemplate)
|
||||||
[](https://twitter.com/intent/tweet?text=Look+what+i+found+on+GitHub+%23Developer%2C+%23SoftwareDeveloper%3A&url=https%3A%2F%2Fgithub.com%2FJosunLP%2FBrowserExtensionTemplate)
|
|
||||||
[](https://www.codefactor.io/repository/github/josunlp/browserextensiontemplate)
|
[](https://www.codefactor.io/repository/github/josunlp/browserextensiontemplate)
|
||||||
[](https://snyk.io/test/github/JosunLP/BrowserExtensionTemplate)
|
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
A basic template based on SASS and TypeScript to create browser extensions without directly relying on a larger framework.
|
A modern, production-ready template for building browser extensions using TypeScript, SASS, and Vite. This template provides a solid foundation with best practices, type safety, and modern development tools.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- 🚀 **Modern Tech Stack**: TypeScript, SASS, Vite, Bootstrap
|
||||||
|
- 🛡️ **Type Safety**: Strict TypeScript configuration with comprehensive error checking
|
||||||
|
- 🔧 **Development Tools**: ESLint, Prettier, automated workflows
|
||||||
|
- 🎯 **Cross-Browser**: Supports both Chrome (Manifest v3) and Firefox (Manifest v2)
|
||||||
|
- 📦 **Component System**: Reusable UI components with type safety
|
||||||
|
- 💾 **Session Management**: Robust localStorage-based session handling
|
||||||
|
- 🛠️ **Build System**: Optimized Vite configuration with code splitting
|
||||||
|
- 🎨 **Modern CSS**: CSS Custom Properties with SASS preprocessing
|
||||||
|
- 🔒 **Security**: Content Security Policy and secure coding practices
|
||||||
|
- ⚡ **Error Handling**: Comprehensive error boundary system
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
You can download the source code from [GitHub](https://github.com/JosunLP/BrowserExtensionTemplate). Just copy it in your project and run `npm install` to install the dependencies.
|
### Quick Start
|
||||||
The basic configuration, wich will sync with `npm run sync` with the `package.json` file and the `manifest.json` file, is in `app.config.json`.
|
|
||||||
Alternatively, you can fork the project and run `npm install` in the forked project.
|
```bash
|
||||||
|
git clone https://github.com/JosunLP/BrowserExtensionTemplate.git
|
||||||
|
cd BrowserExtensionTemplate
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Start development mode with auto-rebuild
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Type checking
|
||||||
|
npm run type-check
|
||||||
|
|
||||||
|
# Linting and formatting
|
||||||
|
npm run validate
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Your sourcecode can be written in the `src` folder. The `public` folder contains static files like images, html and the manifest.json.
|
### Project Structure
|
||||||
With the `npm run deploy-v3` command you can deploy the extension to the dist folder, ready to be published to the chrome web store.
|
|
||||||
With the `npm run deploy-v2` command you can deploy the extension to the dist folder, ready to be published to the firefox web store.
|
|
||||||
This is necessary because the firefox web store needs the `manifest.json` file to be present in the version v2.
|
|
||||||
|
|
||||||
## License
|
```bash
|
||||||
|
src/
|
||||||
|
├── classes/ # Core classes (Session, ErrorBoundary)
|
||||||
|
├── components/ # Reusable UI components
|
||||||
|
├── sass/ # SASS styles with CSS custom properties
|
||||||
|
├── types/ # TypeScript type definitions
|
||||||
|
├── app.ts # Popup entry point
|
||||||
|
├── settings.ts # Options page entry point
|
||||||
|
└── background.ts # Background service worker
|
||||||
|
|
||||||
This project is licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
public/
|
||||||
|
├── icons/ # Extension icons
|
||||||
|
├── manifest.json # Extension manifest
|
||||||
|
├── popup.html # Popup HTML template
|
||||||
|
└── options.html # Options page HTML template
|
||||||
|
|
||||||
|
tools/ # Build and automation scripts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
The main configuration is in `app.config.json`. This file is automatically synchronized with `package.json` and `manifest.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"AppData": {
|
||||||
|
"id": "your_extension_id",
|
||||||
|
"name": "Your Extension Name",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Your extension description"
|
||||||
|
},
|
||||||
|
"htmlTemplatePairs": [
|
||||||
|
{
|
||||||
|
"key": "{{PLACEHOLDER}}",
|
||||||
|
"value": "Replacement Value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Development
|
||||||
|
npm run dev # Start development with watch mode
|
||||||
|
npm run sync # Sync configuration files
|
||||||
|
|
||||||
|
# Production
|
||||||
|
npm run deploy-v3 # Build for Chrome (Manifest v3)
|
||||||
|
npm run deploy-v2 # Build for Firefox (Manifest v2)
|
||||||
|
|
||||||
|
# Quality Assurance
|
||||||
|
npm run validate # Type check + lint
|
||||||
|
npm run lint # ESLint with auto-fix
|
||||||
|
npm run format # Prettier formatting
|
||||||
|
|
||||||
|
# Utilities
|
||||||
|
npm run clean # Clean dist folder
|
||||||
|
npm run build-tooling # Compile TypeScript tools
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Workflow
|
||||||
|
|
||||||
|
1. **Configure your extension** in `app.config.json`
|
||||||
|
2. **Run sync** to update all config files: `npm run sync`
|
||||||
|
3. **Start development**: `npm run dev`
|
||||||
|
4. **Write your code** in the `src/` directory
|
||||||
|
5. **Build for production**: `npm run deploy-v3` or `npm run deploy-v2`
|
||||||
|
6. **Load the extension** from the `dist/` folder in your browser
|
||||||
|
|
||||||
|
### Session Management
|
||||||
|
|
||||||
|
The template includes a robust session management system:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Session } from './classes/session';
|
||||||
|
|
||||||
|
// Get session instance (async)
|
||||||
|
const session = await Session.getInstance();
|
||||||
|
|
||||||
|
// Save data
|
||||||
|
session.contentTest = 'New value';
|
||||||
|
await session.save();
|
||||||
|
|
||||||
|
// Reset session
|
||||||
|
await Session.reset();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
Built-in error boundary system:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ErrorBoundary } from './classes/errorBoundary';
|
||||||
|
|
||||||
|
const errorBoundary = ErrorBoundary.getInstance();
|
||||||
|
|
||||||
|
// Wrap async functions
|
||||||
|
const safeAsyncFunction = errorBoundary.wrapAsync(asyncFunction);
|
||||||
|
|
||||||
|
// Add custom error handlers
|
||||||
|
errorBoundary.addErrorHandler(error => {
|
||||||
|
console.log('Custom error handling:', error);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Component System
|
||||||
|
|
||||||
|
Type-safe, reusable components:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { BasicButton } from './components/button';
|
||||||
|
|
||||||
|
// Create button
|
||||||
|
const button = new BasicButton('primary', 'Click me', 'my-button');
|
||||||
|
|
||||||
|
// Render as HTML string
|
||||||
|
const htmlString = button.render();
|
||||||
|
|
||||||
|
// Or create as DOM element
|
||||||
|
const buttonElement = button.createElement();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Browser Compatibility
|
||||||
|
|
||||||
|
- **Chrome**: Manifest v3 (recommended)
|
||||||
|
- **Firefox**: Manifest v2 (automatically converted)
|
||||||
|
- **Edge**: Manifest v3 compatible
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
This project is open source. Feel free to fork and contribute!
|
1. Fork the repository
|
||||||
|
2. Create a feature branch: `git checkout -b feature/amazing-feature`
|
||||||
|
3. Make your changes and ensure tests pass: `npm run validate`
|
||||||
|
4. Commit your changes: `git commit -m 'Add amazing feature'`
|
||||||
|
5. Push to the branch: `git push origin feature/amazing-feature`
|
||||||
|
6. Open a Pull Request
|
||||||
|
|
||||||
|
## Development Guidelines
|
||||||
|
|
||||||
|
- Follow TypeScript best practices
|
||||||
|
- Use meaningful variable and function names
|
||||||
|
- Add proper error handling
|
||||||
|
- Write self-documenting code
|
||||||
|
- Follow the established project structure
|
||||||
|
- Run `npm run validate` before committing
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||
|
|
||||||
Jonas Pfalzgraf
|
**_Jonas Pfalzgraf_**
|
||||||
|
|
||||||
|
- Email: <info@josunlp.de>
|
||||||
|
- GitHub: [@JosunLP](https://github.com/JosunLP)
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
### v0.0.1 (Current)
|
||||||
|
|
||||||
|
- ✨ Modern TypeScript setup with strict type checking
|
||||||
|
- 🛡️ Comprehensive error handling system
|
||||||
|
- 🎨 CSS Custom Properties with SASS
|
||||||
|
- 🔧 ESLint and Prettier configuration
|
||||||
|
- 📦 Optimized Vite build system
|
||||||
|
- 🚀 Cross-browser compatibility (Chrome/Firefox)
|
||||||
|
- 💾 Robust session management
|
||||||
|
- 🎯 Component-based architecture
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
{
|
{
|
||||||
"AppData": {
|
"AppData": {
|
||||||
"id": "browser_extension_template",
|
"id": "browser_extension_template",
|
||||||
"name": "Browser Extension Template",
|
"name": "Browser Extension Template",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "A basic template based on SASS and TypeScript to create browser extensions without directly relying on a larger framework.",
|
"description": "A basic template based on SASS and TypeScript to create browser extensions without directly relying on a larger framework.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+ssh://git@github.com:JosunLP/BrowserExtensionTemplate.git"
|
"url": "git+ssh://git@github.com:JosunLP/BrowserExtensionTemplate.git"
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"homepage": "https://github.com/JosunLP/BrowserExtensionTemplate",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/JosunLP/BrowserExtensionTemplate/issues"
|
|
||||||
},
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Jonas Pfalzgraf",
|
|
||||||
"email": "info@josunlp.de"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"htmlTemplatePairs": [
|
"license": "MIT",
|
||||||
{
|
"homepage": "https://github.com/JosunLP/BrowserExtensionTemplate",
|
||||||
"key": "{{BET}}",
|
"bugs": {
|
||||||
"value": "Browser Extension Template"
|
"url": "https://github.com/JosunLP/BrowserExtensionTemplate/issues"
|
||||||
}
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jonas Pfalzgraf",
|
||||||
|
"email": "info@josunlp.de"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"htmlTemplatePairs": [
|
||||||
|
{
|
||||||
|
"key": "{{BET}}",
|
||||||
|
"value": "Browser Extension Template"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
||||||
55
eslint.config.js
Normal file
55
eslint.config.js
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
||||||
|
import tsParser from '@typescript-eslint/parser';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['dist/', 'node_modules/', '**/*.js'],
|
||||||
|
},
|
||||||
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['src/**/*.ts'],
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
globals: {
|
||||||
|
chrome: 'readonly',
|
||||||
|
browser: 'readonly',
|
||||||
|
console: 'readonly',
|
||||||
|
document: 'readonly',
|
||||||
|
window: 'readonly',
|
||||||
|
localStorage: 'readonly',
|
||||||
|
sessionStorage: 'readonly',
|
||||||
|
HTMLElement: 'readonly',
|
||||||
|
HTMLDivElement: 'readonly',
|
||||||
|
HTMLButtonElement: 'readonly',
|
||||||
|
HTMLInputElement: 'readonly',
|
||||||
|
setTimeout: 'readonly',
|
||||||
|
clearTimeout: 'readonly',
|
||||||
|
crypto: 'readonly',
|
||||||
|
Error: 'readonly',
|
||||||
|
JSON: 'readonly',
|
||||||
|
Date: 'readonly',
|
||||||
|
String: 'readonly',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': tsPlugin,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...tsPlugin.configs.recommended.rules,
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@typescript-eslint/no-explicit-any': 'error',
|
||||||
|
'prefer-const': 'error',
|
||||||
|
'no-var': 'error',
|
||||||
|
'no-console': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
459
package-lock.json
generated
459
package-lock.json
generated
|
|
@ -1,459 +0,0 @@
|
||||||
{
|
|
||||||
"name": "browser_extension_template",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 2,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "browser_extension_template",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"friendly-helper": "^1.7.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "^18.7.4",
|
|
||||||
"sass": "^1.39.0",
|
|
||||||
"typescript": "^4.2.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/aes-js": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/aes-js/-/aes-js-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw=="
|
|
||||||
},
|
|
||||||
"node_modules/@types/node": {
|
|
||||||
"version": "18.7.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.4.tgz",
|
|
||||||
"integrity": "sha512-RzRcw8c0B8LzryWOR4Wj7YOTFXvdYKwvrb6xQQyuDfnlTxwYXGCV5RZ/TEbq5L5kn+w3rliHAUyRcG1RtbmTFg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/aes-js": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ=="
|
|
||||||
},
|
|
||||||
"node_modules/anymatch": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"normalize-path": "^3.0.0",
|
|
||||||
"picomatch": "^2.0.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/binary-extensions": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/braces": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"fill-range": "^7.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/chokidar": {
|
|
||||||
"version": "3.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
|
||||||
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"anymatch": "~3.1.2",
|
|
||||||
"braces": "~3.0.2",
|
|
||||||
"glob-parent": "~5.1.2",
|
|
||||||
"is-binary-path": "~2.1.0",
|
|
||||||
"is-glob": "~4.0.1",
|
|
||||||
"normalize-path": "~3.0.0",
|
|
||||||
"readdirp": "~3.6.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8.10.0"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"fsevents": "~2.3.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fill-range": {
|
|
||||||
"version": "7.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
|
||||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"to-regex-range": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/friendly-helper": {
|
|
||||||
"version": "1.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/friendly-helper/-/friendly-helper-1.7.1.tgz",
|
|
||||||
"integrity": "sha512-6X9baO2FZ/EReoJBNOP55fOO21Bu0Lnkr6cM97OHXPBgn+V0LF3rS1Nmxeb/WcQGomSkAO3kBi73HDcrEpUCiA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/aes-js": "^3.1.1",
|
|
||||||
"aes-js": "^3.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fsevents": {
|
|
||||||
"version": "2.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/glob-parent": {
|
|
||||||
"version": "5.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
|
||||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"is-glob": "^4.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/immutable": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/is-binary-path": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"binary-extensions": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-extglob": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-glob": {
|
|
||||||
"version": "4.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
|
||||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"is-extglob": "^2.1.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-number": {
|
|
||||||
"version": "7.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
|
||||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.12.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/normalize-path": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/picomatch": {
|
|
||||||
"version": "2.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.6"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/readdirp": {
|
|
||||||
"version": "3.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
|
||||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"picomatch": "^2.2.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sass": {
|
|
||||||
"version": "1.54.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz",
|
|
||||||
"integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"chokidar": ">=3.0.0 <4.0.0",
|
|
||||||
"immutable": "^4.0.0",
|
|
||||||
"source-map-js": ">=0.6.2 <2.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"sass": "sass.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/source-map-js": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/to-regex-range": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"is-number": "^7.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/typescript": {
|
|
||||||
"version": "4.7.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
|
||||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"tsc": "bin/tsc",
|
|
||||||
"tsserver": "bin/tsserver"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.2.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@types/aes-js": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/aes-js/-/aes-js-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw=="
|
|
||||||
},
|
|
||||||
"@types/node": {
|
|
||||||
"version": "18.7.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.4.tgz",
|
|
||||||
"integrity": "sha512-RzRcw8c0B8LzryWOR4Wj7YOTFXvdYKwvrb6xQQyuDfnlTxwYXGCV5RZ/TEbq5L5kn+w3rliHAUyRcG1RtbmTFg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"aes-js": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ=="
|
|
||||||
},
|
|
||||||
"anymatch": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"normalize-path": "^3.0.0",
|
|
||||||
"picomatch": "^2.0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"binary-extensions": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"braces": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"fill-range": "^7.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"chokidar": {
|
|
||||||
"version": "3.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
|
||||||
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"anymatch": "~3.1.2",
|
|
||||||
"braces": "~3.0.2",
|
|
||||||
"fsevents": "~2.3.2",
|
|
||||||
"glob-parent": "~5.1.2",
|
|
||||||
"is-binary-path": "~2.1.0",
|
|
||||||
"is-glob": "~4.0.1",
|
|
||||||
"normalize-path": "~3.0.0",
|
|
||||||
"readdirp": "~3.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fill-range": {
|
|
||||||
"version": "7.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
|
||||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"to-regex-range": "^5.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"friendly-helper": {
|
|
||||||
"version": "1.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/friendly-helper/-/friendly-helper-1.7.1.tgz",
|
|
||||||
"integrity": "sha512-6X9baO2FZ/EReoJBNOP55fOO21Bu0Lnkr6cM97OHXPBgn+V0LF3rS1Nmxeb/WcQGomSkAO3kBi73HDcrEpUCiA==",
|
|
||||||
"requires": {
|
|
||||||
"@types/aes-js": "^3.1.1",
|
|
||||||
"aes-js": "^3.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fsevents": {
|
|
||||||
"version": "2.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
|
||||||
"dev": true,
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"glob-parent": {
|
|
||||||
"version": "5.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
|
||||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"is-glob": "^4.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"immutable": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"is-binary-path": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"binary-extensions": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"is-extglob": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"is-glob": {
|
|
||||||
"version": "4.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
|
||||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"is-extglob": "^2.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"is-number": {
|
|
||||||
"version": "7.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
|
||||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"normalize-path": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"picomatch": {
|
|
||||||
"version": "2.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"readdirp": {
|
|
||||||
"version": "3.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
|
||||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"picomatch": "^2.2.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sass": {
|
|
||||||
"version": "1.54.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz",
|
|
||||||
"integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"chokidar": ">=3.0.0 <4.0.0",
|
|
||||||
"immutable": "^4.0.0",
|
|
||||||
"source-map-js": ">=0.6.2 <2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"source-map-js": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"to-regex-range": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"is-number": "^7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"typescript": {
|
|
||||||
"version": "4.7.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
|
||||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
46
package.json
46
package.json
|
|
@ -4,28 +4,44 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"deploy-v3": "npm run build-tooling && npm run sync && node ./tools/deploy.js && npm run build-js && npm run build-css",
|
"deploy-v3": "npm run clean && npm run build-tooling && npm run sync && npm run build && npm run parse",
|
||||||
"deploy-v2": "npm run deploy-v3 && node ./tools/v2.js",
|
"deploy-v2": "npm run deploy-v3 && node ./tools/v2.js",
|
||||||
"build-js": "tsc -p tsconfig.json",
|
"build": "vite build",
|
||||||
"build-css": "sass ./src/sass/:./dist/css/",
|
"build-tooling": "tsc --project ./tooling.tsconfig.json",
|
||||||
"build-tooling": "tsc ./tools/v2.ts --target esnext --module esnext --lib ESNext && tsc ./tools/syncConfig.ts --target esnext --module esnext --lib ESNext && tsc ./tools/deploy.ts --target esnext --module esnext --lib ESNext",
|
"watch": "vite build --watch",
|
||||||
"watch-ts": "tsc -w -p tsconfig.json",
|
"sync": "npm run build-tooling && node ./tools/syncConfig.js",
|
||||||
"watch-sass": "sass --watch ./src/sass/:./dist/css/",
|
"parse": "node ./tools/parse.js",
|
||||||
"sync": "npm run build-tooling && node ./tools/syncConfig.js"
|
"clean": "npx rimraf ./dist/",
|
||||||
|
"dev": "npm run sync && npm run watch",
|
||||||
|
"lint": "eslint src/**/*.ts --fix",
|
||||||
|
"format": "prettier --write src/**/*.{ts,json}",
|
||||||
|
"format:ts": "prettier --write src/**/*.ts",
|
||||||
|
"format:json": "prettier --write src/**/*.json",
|
||||||
|
"type-check": "tsc --noEmit",
|
||||||
|
"validate": "npm run type-check && npm run lint",
|
||||||
|
"prepare": "npm run validate && npm run build-tooling"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^18.7.4",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"sass": "^1.39.0",
|
"@eslint/js": "^9.30.1",
|
||||||
"typescript": "^4.2.4"
|
"@types/chrome": "^0.0.268",
|
||||||
|
"@types/node": "^20.13.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.36.0",
|
||||||
|
"@typescript-eslint/parser": "^8.36.0",
|
||||||
|
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||||
|
"eslint": "^9.30.1",
|
||||||
|
"prettier": "^3.6.2",
|
||||||
|
"rimraf": "^5.0.10",
|
||||||
|
"sass": "^1.77.4",
|
||||||
|
"typescript": "^5.8.3",
|
||||||
|
"vite": "^7.0.4",
|
||||||
|
"vite-tsconfig-paths": "^4.3.2"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"> 1%",
|
"> 1%",
|
||||||
"last 2 versions",
|
"last 2 versions",
|
||||||
"not dead"
|
"not dead"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
|
||||||
"friendly-helper": "^1.7.1"
|
|
||||||
},
|
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Jonas Pfalzgraf",
|
"name": "Jonas Pfalzgraf",
|
||||||
|
|
@ -41,5 +57,9 @@
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/JosunLP/BrowserExtensionTemplate/issues"
|
"url": "https://github.com/JosunLP/BrowserExtensionTemplate/issues"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@webcomponents/custom-elements": "^1.6.0",
|
||||||
|
"bootstrap": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14,19 +14,28 @@
|
||||||
"default_title": "BrowserExtensionTemplate",
|
"default_title": "BrowserExtensionTemplate",
|
||||||
"default_popup": "popup.html"
|
"default_popup": "popup.html"
|
||||||
},
|
},
|
||||||
|
"options_ui": {
|
||||||
|
"page": "options.html",
|
||||||
|
"open_in_tab": false
|
||||||
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
|
"storage",
|
||||||
"notifications"
|
"notifications"
|
||||||
],
|
],
|
||||||
"background": {
|
"background": {
|
||||||
"service_worker": "js/background.js"
|
"service_worker": "background.js"
|
||||||
},
|
},
|
||||||
"commands": {
|
"content_security_policy": {
|
||||||
"_execute_browser_action": {
|
"extension_pages": "script-src 'self'; object-src 'self'; style-src 'self' 'unsafe-inline';"
|
||||||
"suggested_key": {
|
},
|
||||||
"default": "Ctrl+Shift+F",
|
"web_accessible_resources": [
|
||||||
"mac": "MacCtrl+Shift+F"
|
{
|
||||||
},
|
"resources": [
|
||||||
"description": "Opens popup.html"
|
"icons/*.png"
|
||||||
|
],
|
||||||
|
"matches": [
|
||||||
|
"<all_urls>"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
25
public/options.html
Normal file
25
public/options.html
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="./favicon.ico" />
|
||||||
|
<title>{{BET}} Options</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong
|
||||||
|
>We're sorry but {{BET}} doesn't work properly without JavaScript enabled. Please enable it
|
||||||
|
to continue.</strong
|
||||||
|
>
|
||||||
|
</noscript>
|
||||||
|
<div id="app">
|
||||||
|
<img src="./icons/icon128.png" alt="Logo" class="logo" />
|
||||||
|
<h1>Settings</h1>
|
||||||
|
<div id="settings"></div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<script type="module" src="./settings.js"></script>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -1,26 +1,25 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<link rel="icon" href="./favicon.ico" />
|
||||||
<link rel="icon" href="./favicon.ico">
|
|
||||||
<title>{{BET}}</title>
|
<title>{{BET}}</title>
|
||||||
<link rel="stylesheet" href="css/app.css">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
<strong>We're sorry but {{BET}} doesn't work properly without JavaScript enabled. Please enable it to
|
<strong
|
||||||
continue.</strong>
|
>We're sorry but {{BET}} doesn't work properly without JavaScript enabled. Please enable it
|
||||||
|
to continue.</strong
|
||||||
|
>
|
||||||
</noscript>
|
</noscript>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<img src="./icons/icon128.png" class="logo" />
|
<img src="./icons/icon128.png" alt="Logo" class="logo" />
|
||||||
<h1>{{BET}}</h1>
|
<h1>{{BET}}</h1>
|
||||||
<div id="content">
|
<div id="content"></div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<script type="module" src="./js/app.js"></script>
|
<script type="module" src="./app.js"></script>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
64
src/app.ts
64
src/app.ts
|
|
@ -1,24 +1,60 @@
|
||||||
|
import { Session } from './classes/session';
|
||||||
|
import './sass/app.sass';
|
||||||
|
|
||||||
class App {
|
class App {
|
||||||
|
private static readonly CONTENT_ENTRY = 'content';
|
||||||
|
private session: Session | null = null;
|
||||||
|
|
||||||
private static contentEntry: string = "content"
|
constructor() {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
private async init(): Promise<void> {
|
||||||
this.drawData()
|
try {
|
||||||
this.main()
|
this.session = await Session.getInstance();
|
||||||
|
await this.drawData();
|
||||||
|
await this.main();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize app:', error);
|
||||||
|
this.handleError('Failed to initialize application');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async main(): Promise<void> {
|
||||||
|
console.log('Hello World');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async drawData(): Promise<void> {
|
||||||
|
if (!this.session) {
|
||||||
|
throw new Error('Session not initialized');
|
||||||
}
|
}
|
||||||
|
|
||||||
async main(): Promise<void> {
|
const contentRoot = document.getElementById(App.CONTENT_ENTRY) as HTMLDivElement | null;
|
||||||
|
if (!contentRoot) {
|
||||||
|
throw new Error(`Element with id '${App.CONTENT_ENTRY}' not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async drawData(): Promise<void> {
|
const body = document.createElement('div');
|
||||||
const contentRoot = <HTMLDivElement>document.getElementById(App.contentEntry)
|
body.className = 'app-content';
|
||||||
const body = document.createElement("div")
|
|
||||||
const title = document.createElement("h1")
|
const title = document.createElement('h1');
|
||||||
title.innerText = "Hello World"
|
title.innerText = 'Hello World';
|
||||||
body.appendChild(title)
|
|
||||||
contentRoot.appendChild(body)
|
const text = document.createElement('p');
|
||||||
|
text.innerText = this.session.contentTest;
|
||||||
|
|
||||||
|
body.appendChild(title);
|
||||||
|
body.appendChild(text);
|
||||||
|
contentRoot.appendChild(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleError(message: string): void {
|
||||||
|
console.error(message);
|
||||||
|
const contentRoot = document.getElementById(App.CONTENT_ENTRY);
|
||||||
|
if (contentRoot) {
|
||||||
|
contentRoot.innerHTML = `<div class="error-message">${message}</div>`;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new App();
|
new App();
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,82 @@
|
||||||
|
interface ExtensionMessage {
|
||||||
class Background {
|
type: string;
|
||||||
|
payload?: unknown;
|
||||||
constructor() {
|
|
||||||
this.main();
|
|
||||||
}
|
|
||||||
|
|
||||||
async main(): Promise<void> {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new Background();
|
class Background {
|
||||||
|
constructor() {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async init(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.setupEventListeners();
|
||||||
|
await this.main();
|
||||||
|
console.log('Background service worker initialized');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize background service worker:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setupEventListeners(): Promise<void> {
|
||||||
|
// Install event
|
||||||
|
chrome.runtime.onInstalled.addListener(details => {
|
||||||
|
console.log('Extension installed:', details.reason);
|
||||||
|
this.handleInstall(details.reason);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Message handling
|
||||||
|
chrome.runtime.onMessage.addListener((message: ExtensionMessage, sender, sendResponse) => {
|
||||||
|
this.handleMessage(message, sender)
|
||||||
|
.then(response => sendResponse(response))
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error handling message:', error);
|
||||||
|
sendResponse({ error: error.message });
|
||||||
|
});
|
||||||
|
return true; // Indicates we will send a response asynchronously
|
||||||
|
});
|
||||||
|
|
||||||
|
// Startup event
|
||||||
|
chrome.runtime.onStartup.addListener(() => {
|
||||||
|
console.log('Extension started');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleInstall(reason: string): Promise<void> {
|
||||||
|
if (reason === 'install') {
|
||||||
|
// First time installation
|
||||||
|
console.log('Extension installed for the first time');
|
||||||
|
} else if (reason === 'update') {
|
||||||
|
// Extension updated
|
||||||
|
console.log('Extension updated');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleMessage(
|
||||||
|
message: ExtensionMessage,
|
||||||
|
sender: chrome.runtime.MessageSender
|
||||||
|
): Promise<unknown> {
|
||||||
|
console.log('Received message:', message, 'from:', sender);
|
||||||
|
|
||||||
|
switch (message.type) {
|
||||||
|
case 'ping':
|
||||||
|
return { type: 'pong', timestamp: Date.now() };
|
||||||
|
|
||||||
|
case 'getVersion':
|
||||||
|
return {
|
||||||
|
type: 'version',
|
||||||
|
version: chrome.runtime.getManifest().version,
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown message type: ${message.type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async main(): Promise<void> {
|
||||||
|
// Main background logic can be implemented here
|
||||||
|
// This method is called after initialization
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Background();
|
||||||
|
|
|
||||||
99
src/classes/errorBoundary.ts
Normal file
99
src/classes/errorBoundary.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
export class ErrorBoundary {
|
||||||
|
private static instance: ErrorBoundary;
|
||||||
|
private errorHandlers: Array<(error: Error) => void> = [];
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
this.setupGlobalErrorHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(): ErrorBoundary {
|
||||||
|
if (!ErrorBoundary.instance) {
|
||||||
|
ErrorBoundary.instance = new ErrorBoundary();
|
||||||
|
}
|
||||||
|
return ErrorBoundary.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupGlobalErrorHandlers(): void {
|
||||||
|
// Handle uncaught errors
|
||||||
|
window.addEventListener('error', event => {
|
||||||
|
this.handleError(new Error(event.message), {
|
||||||
|
filename: event.filename,
|
||||||
|
lineno: event.lineno,
|
||||||
|
colno: event.colno,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle unhandled promise rejections
|
||||||
|
window.addEventListener('unhandledrejection', event => {
|
||||||
|
this.handleError(
|
||||||
|
event.reason instanceof Error ? event.reason : new Error(String(event.reason)),
|
||||||
|
{ type: 'unhandledrejection' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public addErrorHandler(handler: (error: Error) => void): void {
|
||||||
|
this.errorHandlers.push(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeErrorHandler(handler: (error: Error) => void): void {
|
||||||
|
const index = this.errorHandlers.indexOf(handler);
|
||||||
|
if (index > -1) {
|
||||||
|
this.errorHandlers.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public handleError(error: Error, context?: Record<string, unknown>): void {
|
||||||
|
console.error('Error caught by ErrorBoundary:', error, context);
|
||||||
|
|
||||||
|
// Call all registered error handlers
|
||||||
|
this.errorHandlers.forEach(handler => {
|
||||||
|
try {
|
||||||
|
handler(error);
|
||||||
|
} catch (handlerError) {
|
||||||
|
console.error('Error in error handler:', handlerError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send to background script if available
|
||||||
|
if (chrome.runtime) {
|
||||||
|
chrome.runtime
|
||||||
|
.sendMessage({
|
||||||
|
type: 'error',
|
||||||
|
payload: {
|
||||||
|
message: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
context,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// Ignore errors when sending to background
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public wrapAsync<T extends unknown[], R>(
|
||||||
|
fn: (...args: T) => Promise<R>
|
||||||
|
): (...args: T) => Promise<R> {
|
||||||
|
return async (...args: T): Promise<R> => {
|
||||||
|
try {
|
||||||
|
return await fn(...args);
|
||||||
|
} catch (error) {
|
||||||
|
this.handleError(error instanceof Error ? error : new Error(String(error)));
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public wrapSync<T extends unknown[], R>(fn: (...args: T) => R): (...args: T) => R {
|
||||||
|
return (...args: T): R => {
|
||||||
|
try {
|
||||||
|
return fn(...args);
|
||||||
|
} catch (error) {
|
||||||
|
this.handleError(error instanceof Error ? error : new Error(String(error)));
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
109
src/classes/session.ts
Normal file
109
src/classes/session.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
interface SessionData {
|
||||||
|
sessionId: string;
|
||||||
|
contentTest: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StorageService {
|
||||||
|
save(key: string, data: unknown): Promise<void>;
|
||||||
|
load<T>(key: string): Promise<T | null>;
|
||||||
|
remove(key: string): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalStorageService implements StorageService {
|
||||||
|
async save(key: string, data: unknown): Promise<void> {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(key, JSON.stringify(data));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save to localStorage:', error);
|
||||||
|
throw new Error('Storage operation failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async load<T>(key: string): Promise<T | null> {
|
||||||
|
try {
|
||||||
|
const item = localStorage.getItem(key);
|
||||||
|
return item ? (JSON.parse(item) as T) : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load from localStorage:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(key: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to remove from localStorage:', error);
|
||||||
|
throw new Error('Storage operation failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Session implements SessionData {
|
||||||
|
private static instance: Session | null = null;
|
||||||
|
private static readonly STORAGE_KEY = 'browser_extension_session';
|
||||||
|
private static readonly storageService: StorageService = new LocalStorageService();
|
||||||
|
|
||||||
|
public readonly sessionId: string;
|
||||||
|
public contentTest: string;
|
||||||
|
|
||||||
|
private constructor(data?: Partial<SessionData>) {
|
||||||
|
this.sessionId = data?.sessionId ?? crypto.randomUUID();
|
||||||
|
this.contentTest = data?.contentTest ?? 'This is a simple example of a web application';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async getInstance(): Promise<Session> {
|
||||||
|
if (!Session.instance) {
|
||||||
|
await Session.loadOrCreate();
|
||||||
|
}
|
||||||
|
return Session.instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async loadOrCreate(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const savedData = await Session.storageService.load<SessionData>(Session.STORAGE_KEY);
|
||||||
|
Session.instance = new Session(savedData ?? undefined);
|
||||||
|
await Session.instance.save();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load session, creating new one:', error);
|
||||||
|
Session.instance = new Session();
|
||||||
|
await Session.instance.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async save(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const data: SessionData = {
|
||||||
|
sessionId: this.sessionId,
|
||||||
|
contentTest: this.contentTest,
|
||||||
|
};
|
||||||
|
await Session.storageService.save(Session.STORAGE_KEY, data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save session:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async reset(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await Session.storageService.remove(Session.STORAGE_KEY);
|
||||||
|
Session.instance = new Session();
|
||||||
|
await Session.instance.save();
|
||||||
|
|
||||||
|
// Reload page only if we're in a browser environment
|
||||||
|
if (typeof window !== 'undefined' && window.location) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to reset session:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON(): SessionData {
|
||||||
|
return {
|
||||||
|
sessionId: this.sessionId,
|
||||||
|
contentTest: this.contentTest,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
96
src/components/button.ts
Normal file
96
src/components/button.ts
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
import { customButton } from '../types/buttonType';
|
||||||
|
|
||||||
|
export interface ButtonConfig {
|
||||||
|
type: customButton;
|
||||||
|
text: string;
|
||||||
|
id?: string | undefined;
|
||||||
|
className?: string | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
onClick?: (() => void) | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BasicButton {
|
||||||
|
private readonly config: ButtonConfig;
|
||||||
|
|
||||||
|
constructor(type: customButton, text: string, id?: string, className?: string) {
|
||||||
|
this.config = {
|
||||||
|
type,
|
||||||
|
text,
|
||||||
|
id,
|
||||||
|
className,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): string {
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.type = 'button';
|
||||||
|
button.textContent = this.config.text;
|
||||||
|
button.className = this.getBootstrapClass();
|
||||||
|
|
||||||
|
if (this.config.id) {
|
||||||
|
button.id = this.config.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.className) {
|
||||||
|
button.className += ` ${this.config.className}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.disabled) {
|
||||||
|
button.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return button.outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createElement(): HTMLButtonElement {
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.type = 'button';
|
||||||
|
button.textContent = this.config.text;
|
||||||
|
button.className = this.getBootstrapClass();
|
||||||
|
|
||||||
|
if (this.config.id) {
|
||||||
|
button.id = this.config.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.className) {
|
||||||
|
button.className += ` ${this.config.className}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.disabled) {
|
||||||
|
button.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.onClick) {
|
||||||
|
button.addEventListener('click', this.config.onClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getBootstrapClass(): string {
|
||||||
|
const typeMap: Record<customButton, string> = {
|
||||||
|
neutral: 'btn btn-secondary',
|
||||||
|
primary: 'btn btn-primary',
|
||||||
|
secondary: 'btn btn-secondary',
|
||||||
|
success: 'btn btn-success',
|
||||||
|
danger: 'btn btn-danger',
|
||||||
|
warning: 'btn btn-warning',
|
||||||
|
info: 'btn btn-info',
|
||||||
|
light: 'btn btn-light',
|
||||||
|
dark: 'btn btn-dark',
|
||||||
|
};
|
||||||
|
|
||||||
|
return typeMap[this.config.type] ?? 'btn btn-primary';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static create(config: ButtonConfig): BasicButton {
|
||||||
|
const button = new BasicButton(config.type, config.text, config.id, config.className);
|
||||||
|
if (config.disabled !== undefined) {
|
||||||
|
button.config.disabled = config.disabled;
|
||||||
|
}
|
||||||
|
if (config.onClick !== undefined) {
|
||||||
|
button.config.onClick = config.onClick;
|
||||||
|
}
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,84 +1,93 @@
|
||||||
@import "root"
|
@import "root"
|
||||||
|
|
||||||
@mixin respond-to($media)
|
@mixin respond-to($media)
|
||||||
@if $media == handhelds
|
@if $media == handhelds
|
||||||
@media only screen and (max-device-width: 40rem)
|
@media only screen and (max-device-width: 40rem)
|
||||||
@content
|
@content
|
||||||
|
|
||||||
@else if $media == medium-screens
|
@else if $media == medium-screens
|
||||||
@media only screen and (min-device-width: 40rem)
|
@media only screen and (min-device-width: 40rem)
|
||||||
@content
|
@content
|
||||||
|
|
||||||
@else if $media == wide-screens
|
@else if $media == wide-screens
|
||||||
@media only screen and (min-width: 1000px)
|
@media only screen and (min-width: 1000px)
|
||||||
@content
|
@content
|
||||||
|
|
||||||
@mixin partialButton
|
@mixin partialButton
|
||||||
button
|
width: 5rem !important
|
||||||
width: 100% !important
|
height: 2rem !important
|
||||||
height: 100% !important
|
text-align: center !important
|
||||||
text-align: center !important
|
margin: 0.5rem !important
|
||||||
margin: 0 !important
|
border-color: $seccond-color !important
|
||||||
|
border-radius: 0.5rem !important
|
||||||
|
|
||||||
@include respond-to(handhelds)
|
@include respond-to(handhelds)
|
||||||
font-size: 3rem
|
font-size: 3rem
|
||||||
|
|
||||||
@include respond-to(medium-screens)
|
@include respond-to(medium-screens)
|
||||||
font-size: 1.5rem
|
font-size: 1.5rem
|
||||||
|
|
||||||
@mixin hoverMe
|
@mixin hoverMe
|
||||||
&:hover
|
&:hover
|
||||||
button
|
button
|
||||||
color: grey !important
|
color: grey !important
|
||||||
|
|
||||||
@mixin shadow
|
@mixin shadow
|
||||||
box-shadow: 0px 0px 30px silver
|
box-shadow: 0px 0px 30px silver
|
||||||
|
|
||||||
@mixin noselect
|
@mixin noselect
|
||||||
-webkit-touch-callout: none
|
-webkit-touch-callout: none
|
||||||
-webkit-user-select: none
|
-webkit-user-select: none
|
||||||
-khtml-user-select: none
|
-khtml-user-select: none
|
||||||
-moz-user-select: none
|
-moz-user-select: none
|
||||||
-ms-user-select: none
|
-ms-user-select: none
|
||||||
user-select: none
|
user-select: none
|
||||||
pointer-events: none
|
pointer-events: none
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
margin-left: 2rem
|
||||||
|
margin-right: 2rem
|
||||||
|
margin-bottom: 0.5rem
|
||||||
|
flex-wrap: wrap
|
||||||
|
justify-content: center
|
||||||
|
display: flex
|
||||||
|
|
||||||
@mixin formBasic
|
@mixin formBasic
|
||||||
display: block
|
display: block
|
||||||
padding: 2rem
|
padding: 2rem
|
||||||
background: $background-color-content
|
background: $background-color-content
|
||||||
border-radius: 0.7rem
|
border-radius: 0.7rem
|
||||||
min-height: 20rem
|
min-height: 20rem
|
||||||
margin: auto
|
margin: auto
|
||||||
margin-top: 2rem
|
margin-top: 2rem
|
||||||
margin-bottom: 2rem
|
margin-bottom: 2rem
|
||||||
@include shadow
|
@include shadow
|
||||||
|
|
||||||
@include respond-to(handhelds)
|
@include respond-to(handhelds)
|
||||||
font-size: 2.5em
|
font-size: 2.5em
|
||||||
margin-left: -0.8em
|
margin-left: -0.8em
|
||||||
margin-right: -0.8em
|
margin-right: -0.8em
|
||||||
border-radius: 0
|
border-radius: 0
|
||||||
|
|
||||||
@include respond-to(medium-screens)
|
@include respond-to(medium-screens)
|
||||||
max-width: 40rem
|
max-width: 40rem
|
||||||
|
|
||||||
@include respond-to(wide-screens)
|
@include respond-to(wide-screens)
|
||||||
max-width: 40rem
|
max-width: 40rem
|
||||||
|
|
||||||
input
|
input
|
||||||
@include respond-to(handhelds)
|
@include respond-to(handhelds)
|
||||||
font-size: 3rem
|
font-size: 3rem
|
||||||
border-radius: 0.5rem
|
border-radius: 0.5rem
|
||||||
|
|
||||||
.check
|
.check
|
||||||
position: static
|
position: static
|
||||||
@include respond-to(handhelds)
|
@include respond-to(handhelds)
|
||||||
width: 2rem !important
|
width: 2rem !important
|
||||||
height: 2rem !important
|
height: 2rem !important
|
||||||
|
|
||||||
button
|
button
|
||||||
@include respond-to(handhelds)
|
@include respond-to(handhelds)
|
||||||
font-size: 3rem
|
font-size: 3rem
|
||||||
padding: 1rem
|
padding: 1rem
|
||||||
border-radius: 1rem
|
border-radius: 1rem
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,64 @@
|
||||||
$main-font: 'Ubuntu', 'Staatliches'
|
// CSS Custom Properties for theming support
|
||||||
$main-font-color: white
|
:root
|
||||||
$main-font-color-hover: lightgrey
|
--main-font: 'Ubuntu', 'Segoe UI', 'Roboto', sans-serif
|
||||||
$main-uschrift-font: 'Ubuntu', Arial
|
--main-font-color: #ffffff
|
||||||
$seccond-color: lightgrey
|
--main-font-color-hover: #f8f9fa
|
||||||
$background-color: rgb(119, 178, 255)
|
--main-font-color-focus: #e9ecef
|
||||||
$background-color-content: rgb(198, 223, 255)
|
--main-font-color-disabled: #6c757d
|
||||||
$logo-image: url('../icons/icon128.png')
|
--main-font-color-active: #f8f9fa
|
||||||
|
|
||||||
|
--primary-color: #007bff
|
||||||
|
--primary-color-hover: #0056b3
|
||||||
|
--primary-color-focus: #004085
|
||||||
|
--primary-color-disabled: #6c757d
|
||||||
|
--primary-color-active: #004085
|
||||||
|
|
||||||
|
--secondary-color: #6c757d
|
||||||
|
--secondary-color-hover: #545b62
|
||||||
|
--secondary-color-focus: #4e555b
|
||||||
|
--secondary-color-disabled: #adb5bd
|
||||||
|
--secondary-color-active: #4e555b
|
||||||
|
|
||||||
|
--background-color: #77B2FF
|
||||||
|
--background-color-content: #c6dfff
|
||||||
|
--background-color-content-hover: #b3d7ff
|
||||||
|
--background-color-content-focus: #9fcdff
|
||||||
|
--background-color-content-active: #8cc4ff
|
||||||
|
--background-color-content-disabled: #e9ecef
|
||||||
|
|
||||||
|
--shadow-color: rgba(0, 0, 0, 0.1)
|
||||||
|
--border-radius: 0.375rem
|
||||||
|
--transition-duration: 0.15s
|
||||||
|
--font-size-base: 1rem
|
||||||
|
--line-height-base: 1.5
|
||||||
|
|
||||||
|
// SASS Variables (for backwards compatibility)
|
||||||
|
$main-font: var(--main-font)
|
||||||
|
$main-font-color: var(--main-font-color)
|
||||||
|
$main-font-color-hover: var(--main-font-color-hover)
|
||||||
|
$main-font-color-focus: var(--main-font-color-focus)
|
||||||
|
$main-font-color-disabled: var(--main-font-color-disabled)
|
||||||
|
$main-font-color-active: var(--main-font-color-active)
|
||||||
|
|
||||||
|
$primary-color: var(--primary-color)
|
||||||
|
$primary-color-hover: var(--primary-color-hover)
|
||||||
|
$primary-color-focus: var(--primary-color-focus)
|
||||||
|
$primary-color-disabled: var(--primary-color-disabled)
|
||||||
|
$primary-color-active: var(--primary-color-active)
|
||||||
|
|
||||||
|
$secondary-color: var(--secondary-color)
|
||||||
|
$secondary-color-hover: var(--secondary-color-hover)
|
||||||
|
$secondary-color-focus: var(--secondary-color-focus)
|
||||||
|
$secondary-color-disabled: var(--secondary-color-disabled)
|
||||||
|
$secondary-color-active: var(--secondary-color-active)
|
||||||
|
|
||||||
|
$background-color: var(--background-color)
|
||||||
|
$background-color-content: var(--background-color-content)
|
||||||
|
$background-color-content-hover: var(--background-color-content-hover)
|
||||||
|
$background-color-content-focus: var(--background-color-content-focus)
|
||||||
|
$background-color-content-active: var(--background-color-content-active)
|
||||||
|
$background-color-content-disabled: var(--background-color-content-disabled)
|
||||||
|
|
||||||
|
$shadow-color: var(--shadow-color)
|
||||||
|
$border-radius: var(--border-radius)
|
||||||
|
$transition-duration: var(--transition-duration)
|
||||||
|
|
|
||||||
|
|
@ -2,40 +2,50 @@
|
||||||
@import 'mixin'
|
@import 'mixin'
|
||||||
@import 'content'
|
@import 'content'
|
||||||
|
|
||||||
|
// Bootstrap with legacy import (suppressed warnings via Vite config)
|
||||||
|
@import "../../node_modules/bootstrap/scss/bootstrap"
|
||||||
|
|
||||||
body
|
body
|
||||||
height: 30rem
|
height: 30rem
|
||||||
width: 30rem
|
width: 30rem
|
||||||
background-color: $background-color
|
background-color: $background-color
|
||||||
text-align: center
|
text-align: center
|
||||||
|
margin: auto
|
||||||
|
padding: 1rem
|
||||||
|
color: $main-font-color
|
||||||
|
|
||||||
|
p
|
||||||
|
font-size: 1rem
|
||||||
|
font-weight: bold
|
||||||
margin: auto
|
margin: auto
|
||||||
padding: auto
|
padding: auto
|
||||||
color: $main-font-color
|
color: $main-font-color
|
||||||
|
text-align: center
|
||||||
@include partialButton
|
font-family: 'Roboto', sans-serif
|
||||||
|
|
||||||
h1, h2
|
h1, h2
|
||||||
@include noselect
|
@include noselect
|
||||||
|
|
||||||
form
|
form
|
||||||
@include formBasic
|
@include formBasic
|
||||||
|
|
||||||
.logo
|
.logo
|
||||||
width: 5rem
|
width: 5rem
|
||||||
height: auto
|
height: auto
|
||||||
padding-top: 2rem
|
padding-top: 2rem
|
||||||
@include noselect
|
@include noselect
|
||||||
|
|
||||||
svg
|
svg
|
||||||
@include noselect
|
@include noselect
|
||||||
|
|
||||||
table
|
table
|
||||||
color: $main-font-color !important
|
color: $main-font-color !important
|
||||||
|
|
||||||
th
|
th
|
||||||
@include noselect
|
@include noselect
|
||||||
|
|
||||||
a
|
a
|
||||||
color: $main-font-color
|
color: $main-font-color
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
color: $background-color-content
|
color: $background-color-content
|
||||||
|
|
|
||||||
120
src/settings.ts
Normal file
120
src/settings.ts
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
import { Session } from './classes/session';
|
||||||
|
import { BasicButton } from './components/button';
|
||||||
|
import './sass/app.sass';
|
||||||
|
|
||||||
|
class Settings {
|
||||||
|
private session: Session | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async init(): Promise<void> {
|
||||||
|
try {
|
||||||
|
this.session = await Session.getInstance();
|
||||||
|
await this.renderSettings();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize settings:', error);
|
||||||
|
this.handleError('Failed to load settings');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async renderSettings(): Promise<void> {
|
||||||
|
if (!this.session) {
|
||||||
|
throw new Error('Session not initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsElement = document.getElementById('settings') as HTMLDivElement | null;
|
||||||
|
if (!settingsElement) {
|
||||||
|
throw new Error('Settings element not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveButton = new BasicButton('success', 'Save', 'saveSettings').render();
|
||||||
|
|
||||||
|
settingsElement.innerHTML = `
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="contentTest">Content Test</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control text-input"
|
||||||
|
id="contentTest"
|
||||||
|
placeholder="Enter content test"
|
||||||
|
value="${this.escapeHtml(this.session.contentTest)}"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
${saveButton}
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.attachEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
private attachEventListeners(): void {
|
||||||
|
const saveButton = document.getElementById('saveSettings') as HTMLButtonElement | null;
|
||||||
|
const contentInput = document.getElementById('contentTest') as HTMLInputElement | null;
|
||||||
|
|
||||||
|
if (!saveButton || !contentInput) {
|
||||||
|
console.error('Required elements not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveButton.addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
await this.saveSettings(contentInput.value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save settings:', error);
|
||||||
|
this.showNotification('Failed to save settings', 'error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async saveSettings(contentTest: string): Promise<void> {
|
||||||
|
if (!this.session) {
|
||||||
|
throw new Error('Session not initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.session.contentTest = contentTest;
|
||||||
|
await this.session.save();
|
||||||
|
this.showNotification('Settings saved successfully!', 'success');
|
||||||
|
}
|
||||||
|
|
||||||
|
private escapeHtml(text: string): string {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = text;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
private showNotification(message: string, type: 'success' | 'error'): void {
|
||||||
|
// Simple notification - could be enhanced with a proper notification system
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.className = `notification notification-${type}`;
|
||||||
|
notification.textContent = message;
|
||||||
|
notification.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: white;
|
||||||
|
background-color: ${type === 'success' ? '#28a745' : '#dc3545'};
|
||||||
|
z-index: 1000;
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (notification.parentNode) {
|
||||||
|
notification.parentNode.removeChild(notification);
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleError(message: string): void {
|
||||||
|
console.error(message);
|
||||||
|
const settingsElement = document.getElementById('settings');
|
||||||
|
if (settingsElement) {
|
||||||
|
settingsElement.innerHTML = `<div class="error-message">${message}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Settings();
|
||||||
10
src/types/buttonType.ts
Normal file
10
src/types/buttonType.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
export type customButton =
|
||||||
|
| 'neutral'
|
||||||
|
| 'primary'
|
||||||
|
| 'secondary'
|
||||||
|
| 'success'
|
||||||
|
| 'danger'
|
||||||
|
| 'warning'
|
||||||
|
| 'info'
|
||||||
|
| 'light'
|
||||||
|
| 'dark';
|
||||||
31
tooling.tsconfig.json
Normal file
31
tooling.tsconfig.json
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"lib": [
|
||||||
|
"ESNext"
|
||||||
|
],
|
||||||
|
"module": "ESNext",
|
||||||
|
"outDir": "./tools/",
|
||||||
|
"rootDir": "./tools/",
|
||||||
|
"removeComments": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"types": [
|
||||||
|
"node",
|
||||||
|
"chrome"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./tools/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"./src/*.ts",
|
||||||
|
"./src/**/*.ts",
|
||||||
|
"./dist/*.ts",
|
||||||
|
"./dist/*.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
const appConfig = JSON.parse(fs.readFileSync('./app.config.json', 'utf8'));
|
|
||||||
const DEPLOY_ENTRY = "./public/";
|
|
||||||
const DEPLOY_TARGET = "./dist/";
|
|
||||||
|
|
||||||
function deleteFolderRecursive(path: string) {
|
|
||||||
if (fs.existsSync(path)) {
|
|
||||||
fs.readdirSync(path).forEach(function (file: string) {
|
|
||||||
const curPath = path + "/" + file;
|
|
||||||
if (fs.lstatSync(curPath).isDirectory()) {
|
|
||||||
deleteFolderRecursive(curPath);
|
|
||||||
} else {
|
|
||||||
fs.unlinkSync(curPath);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fs.rmdirSync(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function findHtmlFilesRecursive(source: string): string[] {
|
|
||||||
let files: string[] = [];
|
|
||||||
const dir = fs.readdirSync(source);
|
|
||||||
dir.forEach(function (file: string) {
|
|
||||||
const sourceFile = path.join(source, file);
|
|
||||||
const stat = fs.lstatSync(sourceFile);
|
|
||||||
if (stat.isDirectory()) {
|
|
||||||
files = files.concat(findHtmlFilesRecursive(sourceFile));
|
|
||||||
} else {
|
|
||||||
if (path.extname(sourceFile) == '.html') {
|
|
||||||
files.push(sourceFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceKeywordsInHtmlFile(file: string) {
|
|
||||||
const content = fs.readFileSync(file, 'utf8');
|
|
||||||
const pairs = appConfig.htmlTemplatePairs;
|
|
||||||
pairs.forEach(function (pair: object) {
|
|
||||||
// @ts-ignore
|
|
||||||
content = content.replaceAll(pair.key, pair.value);
|
|
||||||
});
|
|
||||||
file = file.replace("public\\", DEPLOY_TARGET);
|
|
||||||
fs.writeFileSync(file, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildHtmlFiles(source: string) {
|
|
||||||
const files = findHtmlFilesRecursive(source);
|
|
||||||
files.forEach(function (file: string) {
|
|
||||||
replaceKeywordsInHtmlFile(file);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function mkdirSync(path: string) {
|
|
||||||
try {
|
|
||||||
fs.mkdirSync(path);
|
|
||||||
} catch (e: any) {
|
|
||||||
if (e.code != 'EEXIST') throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyFiles(source: string, target: string) {
|
|
||||||
const files = fs.readdirSync(source);
|
|
||||||
files.forEach(function (file: string) {
|
|
||||||
const sourceFile = path.join(source, file);
|
|
||||||
const targetFile = path.join(target, file);
|
|
||||||
const stat = fs.lstatSync(sourceFile);
|
|
||||||
if (stat.isDirectory()) {
|
|
||||||
mkdirSync(targetFile);
|
|
||||||
copyFiles(sourceFile, targetFile);
|
|
||||||
} else {
|
|
||||||
fs.writeFileSync(targetFile, fs.readFileSync(sourceFile));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteFolderRecursive(DEPLOY_TARGET);
|
|
||||||
mkdirSync(DEPLOY_TARGET);
|
|
||||||
copyFiles(DEPLOY_ENTRY, DEPLOY_TARGET);
|
|
||||||
buildHtmlFiles(DEPLOY_ENTRY);
|
|
||||||
|
|
||||||
console.log("Deployed to " + DEPLOY_TARGET);
|
|
||||||
72
tools/parse.ts
Normal file
72
tools/parse.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
const appConfig = JSON.parse(fs.readFileSync('./app.config.json', 'utf8'));
|
||||||
|
const DEPLOY_TARGET = './dist/';
|
||||||
|
|
||||||
|
function findCssFileNames(source: string): string[] {
|
||||||
|
let files: string[] = [];
|
||||||
|
const dir = fs.readdirSync(source);
|
||||||
|
dir.forEach(function (file: string) {
|
||||||
|
const sourceFile = path.join(source, file);
|
||||||
|
const stat = fs.lstatSync(sourceFile);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
files = files.concat(findCssFileNames(sourceFile));
|
||||||
|
} else {
|
||||||
|
if (path.extname(sourceFile) === '.css') {
|
||||||
|
files.push(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findHtmlFilesRecursive(source: string): string[] {
|
||||||
|
let files: string[] = [];
|
||||||
|
const dir = fs.readdirSync(source);
|
||||||
|
dir.forEach(function (file: string) {
|
||||||
|
const sourceFile = path.join(source, file);
|
||||||
|
const stat = fs.lstatSync(sourceFile);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
files = files.concat(findHtmlFilesRecursive(sourceFile));
|
||||||
|
} else {
|
||||||
|
if (path.extname(sourceFile) == '.html') {
|
||||||
|
files.push(sourceFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceKeywordsInHtmlFile(file: string) {
|
||||||
|
let content = fs.readFileSync(file, 'utf8');
|
||||||
|
const pairs: { key: string; value: string }[] = appConfig.htmlTemplatePairs;
|
||||||
|
pairs.forEach(function (pair: { key: string; value: string }) {
|
||||||
|
//@ts-ignore
|
||||||
|
content = content.replaceAll(pair.key, pair.value);
|
||||||
|
});
|
||||||
|
file = file.replace('public\\', DEPLOY_TARGET);
|
||||||
|
fs.writeFileSync(file, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildHtmlFiles(source: string) {
|
||||||
|
const files = findHtmlFilesRecursive(source);
|
||||||
|
files.forEach(function (file: string) {
|
||||||
|
replaceKeywordsInHtmlFile(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findCssFileNames(DEPLOY_TARGET).forEach((file: string) => {
|
||||||
|
const files = findHtmlFilesRecursive(DEPLOY_TARGET);
|
||||||
|
files.forEach(function (htmlFile: string) {
|
||||||
|
let content = fs.readFileSync(htmlFile, 'utf8');
|
||||||
|
content = content.replace(
|
||||||
|
'</head>',
|
||||||
|
`<link rel="stylesheet" href="./assets/${file}">\n</head>`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(htmlFile, content);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
buildHtmlFiles(DEPLOY_TARGET);
|
||||||
|
|
||||||
|
console.log('Parsed Files: ', findHtmlFilesRecursive(DEPLOY_TARGET));
|
||||||
|
|
@ -1,22 +1,151 @@
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
||||||
const appConfig = JSON.parse(fs.readFileSync('./app.config.json', 'utf8'));
|
interface AppConfig {
|
||||||
const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
AppData: {
|
||||||
const manifest = JSON.parse(fs.readFileSync('./public/manifest.json', 'utf8'));
|
id: string;
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
description: string;
|
||||||
|
repository: {
|
||||||
|
type: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
license: string;
|
||||||
|
homepage: string;
|
||||||
|
bugs: {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
authors: Array<{
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
htmlTemplatePairs: Array<{
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
pkg.version = appConfig.AppData.version;
|
interface PackageJson {
|
||||||
pkg.name = appConfig.AppData.id;
|
version: string;
|
||||||
pkg.authors = appConfig.AppData.authors;
|
name: string;
|
||||||
pkg.description = appConfig.AppData.description;
|
authors: Array<{
|
||||||
pkg.homepage = appConfig.AppData.homepage;
|
name: string;
|
||||||
pkg.license = appConfig.AppData.license;
|
email: string;
|
||||||
pkg.repository = appConfig.AppData.repository;
|
}>;
|
||||||
pkg.bugs = appConfig.AppData.bugs;
|
description: string;
|
||||||
|
homepage: string;
|
||||||
|
license: string;
|
||||||
|
repository: {
|
||||||
|
type: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
bugs: {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
manifest.version = appConfig.AppData.version;
|
interface ManifestJson {
|
||||||
manifest.name = appConfig.AppData.name;
|
version: string;
|
||||||
manifest.description = appConfig.AppData.description;
|
name: string;
|
||||||
manifest.homepage_url = appConfig.AppData.homepage;
|
description: string;
|
||||||
|
homepage_url: string;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2));
|
class ConfigSyncer {
|
||||||
fs.writeFileSync('./public/manifest.json', JSON.stringify(manifest, null, 2));
|
private readonly appConfigPath = './app.config.json';
|
||||||
|
private readonly packageJsonPath = './package.json';
|
||||||
|
private readonly manifestJsonPath = './public/manifest.json';
|
||||||
|
|
||||||
|
public async sync(): Promise<void> {
|
||||||
|
try {
|
||||||
|
console.log('Starting configuration synchronization...');
|
||||||
|
|
||||||
|
const appConfig = await this.loadAppConfig();
|
||||||
|
const packageJson = await this.loadPackageJson();
|
||||||
|
const manifestJson = await this.loadManifestJson();
|
||||||
|
|
||||||
|
this.updatePackageJson(packageJson, appConfig.AppData);
|
||||||
|
this.updateManifestJson(manifestJson, appConfig.AppData);
|
||||||
|
|
||||||
|
await this.savePackageJson(packageJson);
|
||||||
|
await this.saveManifestJson(manifestJson);
|
||||||
|
|
||||||
|
console.log('Configuration synchronization completed successfully!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to sync configuration:', error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadAppConfig(): Promise<AppConfig> {
|
||||||
|
try {
|
||||||
|
const content = await fs.promises.readFile(this.appConfigPath, 'utf8');
|
||||||
|
return JSON.parse(content) as AppConfig;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to load app config: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadPackageJson(): Promise<PackageJson> {
|
||||||
|
try {
|
||||||
|
const content = await fs.promises.readFile(this.packageJsonPath, 'utf8');
|
||||||
|
return JSON.parse(content) as PackageJson;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to load package.json: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadManifestJson(): Promise<ManifestJson> {
|
||||||
|
try {
|
||||||
|
const content = await fs.promises.readFile(this.manifestJsonPath, 'utf8');
|
||||||
|
return JSON.parse(content) as ManifestJson;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to load manifest.json: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updatePackageJson(packageJson: PackageJson, appData: AppConfig['AppData']): void {
|
||||||
|
packageJson.version = appData.version;
|
||||||
|
packageJson.name = appData.id;
|
||||||
|
packageJson.authors = appData.authors;
|
||||||
|
packageJson.description = appData.description;
|
||||||
|
packageJson.homepage = appData.homepage;
|
||||||
|
packageJson.license = appData.license;
|
||||||
|
packageJson.repository = appData.repository;
|
||||||
|
packageJson.bugs = appData.bugs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateManifestJson(manifestJson: ManifestJson, appData: AppConfig['AppData']): void {
|
||||||
|
manifestJson.version = appData.version;
|
||||||
|
manifestJson.name = appData.name;
|
||||||
|
manifestJson.description = appData.description;
|
||||||
|
manifestJson.homepage_url = appData.homepage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async savePackageJson(packageJson: PackageJson): Promise<void> {
|
||||||
|
try {
|
||||||
|
const content = JSON.stringify(packageJson, null, 2);
|
||||||
|
await fs.promises.writeFile(this.packageJsonPath, content, 'utf8');
|
||||||
|
console.log('✓ package.json updated');
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to save package.json: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async saveManifestJson(manifestJson: ManifestJson): Promise<void> {
|
||||||
|
try {
|
||||||
|
const content = JSON.stringify(manifestJson, null, 2);
|
||||||
|
await fs.promises.writeFile(this.manifestJsonPath, content, 'utf8');
|
||||||
|
console.log('✓ manifest.json updated');
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to save manifest.json: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the sync process
|
||||||
|
const syncer = new ConfigSyncer();
|
||||||
|
syncer.sync().catch(console.error);
|
||||||
|
|
|
||||||
45
tools/v2.ts
45
tools/v2.ts
|
|
@ -1,45 +1,46 @@
|
||||||
import * as fs from 'fs';
|
// @ts-ignore
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
const manifest = JSON.parse(fs.readFileSync('./dist/manifest.json', 'utf8'));
|
const manifest = JSON.parse(fs.readFileSync('./dist/manifest.json', 'utf8'));
|
||||||
|
|
||||||
manifest.manifest_version = 2
|
manifest.manifest_version = 2;
|
||||||
|
|
||||||
manifest.background.scripts = []
|
manifest.background.scripts = [];
|
||||||
|
|
||||||
manifest.background.scripts.push(manifest.background.service_worker)
|
manifest.background.scripts.push(manifest.background.service_worker);
|
||||||
delete manifest.background.type
|
delete manifest.background.type;
|
||||||
delete manifest.background.service_worker
|
delete manifest.background.service_worker;
|
||||||
manifest.background.persistent = true
|
manifest.background.persistent = true;
|
||||||
if (manifest.host_permissions) {
|
if (manifest.host_permissions) {
|
||||||
manifest.permissions.push(manifest.host_permissions)
|
manifest.permissions.push(manifest.host_permissions);
|
||||||
}
|
}
|
||||||
if (manifest.optional_host_permissions) {
|
if (manifest.optional_host_permissions) {
|
||||||
manifest.permissions.push(manifest.optional_host_permissions)
|
manifest.permissions.push(manifest.optional_host_permissions);
|
||||||
}
|
}
|
||||||
delete manifest.host_permissions
|
delete manifest.host_permissions;
|
||||||
delete manifest.optional_host_permissions
|
delete manifest.optional_host_permissions;
|
||||||
|
|
||||||
let newContentSecurityPolicy = ""
|
let newContentSecurityPolicy = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const policy of manifest.content_security_policy) {
|
for (const policy of manifest.content_security_policy) {
|
||||||
newContentSecurityPolicy += policy.key + "'" + policy.value + "'" + " "
|
newContentSecurityPolicy += policy.key + "'" + policy.value + "'" + ' ';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
newContentSecurityPolicy = "default-src 'self'"
|
newContentSecurityPolicy = "default-src 'self'";
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest.content_security_policy = newContentSecurityPolicy
|
manifest.content_security_policy = newContentSecurityPolicy;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
manifest.web_accessible_resources = manifest.web_accessible_resources.resources
|
manifest.web_accessible_resources = manifest.web_accessible_resources.resources;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
manifest.web_accessible_resources = []
|
manifest.web_accessible_resources = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manifest.action) {
|
if (manifest.action) {
|
||||||
manifest.browser_action = manifest.action
|
manifest.browser_action = manifest.action;
|
||||||
}
|
}
|
||||||
delete manifest.action
|
delete manifest.action;
|
||||||
|
|
||||||
fs.writeFileSync('./dist/manifest.json', JSON.stringify(manifest, null, 2));
|
fs.writeFileSync('./dist/manifest.json', JSON.stringify(manifest, null, 2));
|
||||||
|
|
|
||||||
114
tsconfig.json
114
tsconfig.json
|
|
@ -1,81 +1,49 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
/* Language and Environment */
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
/* Basic Options */
|
/* Type Checking */
|
||||||
// "incremental": true, /* Enable incremental compilation */
|
"strict": true,
|
||||||
"target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
"noImplicitAny": true,
|
||||||
"module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
"strictNullChecks": true,
|
||||||
// "lib": ["ESNext"], /* Specify library files to be included in the compilation. */
|
"strictFunctionTypes": true,
|
||||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
"strictBindCallApply": true,
|
||||||
// "checkJs": true, /* Report errors in .js files. */
|
"strictPropertyInitialization": true,
|
||||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|
"noImplicitThis": true,
|
||||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
"alwaysStrict": true,
|
||||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
"noUnusedLocals": true,
|
||||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
"noUnusedParameters": true,
|
||||||
// "outFile": "./public/js/app.js", /* Concatenate and emit output to single file. */
|
"exactOptionalPropertyTypes": true,
|
||||||
"outDir": "./dist/js/", /* Redirect output structure to the directory. */
|
"noImplicitReturns": true,
|
||||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
"noFallthroughCasesInSwitch": true,
|
||||||
// "composite": true, /* Enable project compilation */
|
"noUncheckedIndexedAccess": true,
|
||||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
"noImplicitOverride": true,
|
||||||
// "removeComments": true, /* Do not emit comments to output. */
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
// "noEmit": true, /* Do not emit outputs. */
|
|
||||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
|
||||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
|
||||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
|
||||||
"moduleResolution": "node",
|
|
||||||
/* Strict Type-Checking Options */
|
|
||||||
"strict": true, /* Enable all strict type-checking options. */
|
|
||||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
|
||||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
|
||||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
|
||||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
|
||||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
|
||||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
|
||||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
|
||||||
|
|
||||||
/* Additional Checks */
|
/* Emit */
|
||||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
"declaration": true,
|
||||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
"declarationMap": true,
|
||||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
"sourceMap": true,
|
||||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
"outDir": "./dist",
|
||||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
"removeComments": false,
|
||||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
|
"importHelpers": true,
|
||||||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
|
||||||
|
|
||||||
/* Module Resolution Options */
|
/* Interop Constraints */
|
||||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
"esModuleInterop": true,
|
||||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
"allowSyntheticDefaultImports": true,
|
||||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
"forceConsistentCasingInFileNames": true,
|
||||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
|
||||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
|
||||||
// "types": [], /* Type declaration files to be included in compilation. */
|
|
||||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
|
||||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
|
||||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
|
||||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
|
||||||
|
|
||||||
/* Source Map Options */
|
/* Completeness */
|
||||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
"skipLibCheck": true
|
||||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
|
||||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
|
||||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
|
||||||
|
|
||||||
/* Experimental Options */
|
|
||||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
|
||||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
|
||||||
|
|
||||||
/* Advanced Options */
|
|
||||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
|
||||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src/**/*.ts"],
|
||||||
"./src/**/*.ts",
|
"exclude": ["node_modules", "dist", "tools/*.js"]
|
||||||
"./src/*.ts"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"./tools/*.ts",
|
|
||||||
"./dist/*.ts",
|
|
||||||
"./dist/*.js"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
71
vite.config.ts
Normal file
71
vite.config.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { resolve } from 'path';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
app: resolve(__dirname, 'src/app.ts'),
|
||||||
|
settings: resolve(__dirname, 'src/settings.ts'),
|
||||||
|
background: resolve(__dirname, 'src/background.ts'),
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
entryFileNames: '[name].js',
|
||||||
|
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||||
|
assetFileNames: assetInfo => {
|
||||||
|
if (assetInfo.name?.endsWith('.css')) {
|
||||||
|
return 'assets/[name]-[hash][extname]';
|
||||||
|
}
|
||||||
|
return 'assets/[name]-[hash][extname]';
|
||||||
|
},
|
||||||
|
dir: resolve(__dirname, 'dist'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sourcemap: true,
|
||||||
|
target: 'es2022',
|
||||||
|
minify: 'esbuild',
|
||||||
|
reportCompressedSize: false,
|
||||||
|
chunkSizeWarningLimit: 1000,
|
||||||
|
},
|
||||||
|
plugins: [tsconfigPaths()],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.tsx', '.js', '.jsx', '.scss', '.sass'],
|
||||||
|
alias: {
|
||||||
|
'@': resolve(__dirname, './src'),
|
||||||
|
'@components': resolve(__dirname, './src/components'),
|
||||||
|
'@classes': resolve(__dirname, './src/classes'),
|
||||||
|
'@types': resolve(__dirname, './src/types'),
|
||||||
|
'@sass': resolve(__dirname, './src/sass'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
esbuild: {
|
||||||
|
target: 'es2022',
|
||||||
|
include: /.*\.tsx?$/,
|
||||||
|
exclude: [/node_modules/, /dist/],
|
||||||
|
legalComments: 'none',
|
||||||
|
},
|
||||||
|
define: {
|
||||||
|
__DEV__: JSON.stringify(process.env.NODE_ENV === 'development'),
|
||||||
|
__VERSION__: JSON.stringify(process.env.npm_package_version || '0.0.1'),
|
||||||
|
},
|
||||||
|
css: {
|
||||||
|
preprocessorOptions: {
|
||||||
|
sass: {
|
||||||
|
additionalData: `@import "@sass/_root.sass"\n@import "@sass/_mixin.sass"\n`,
|
||||||
|
quietDeps: true,
|
||||||
|
verbose: false,
|
||||||
|
charset: false,
|
||||||
|
silenceDeprecations: [
|
||||||
|
'import',
|
||||||
|
'global-builtin',
|
||||||
|
'color-functions',
|
||||||
|
'legacy-js-api',
|
||||||
|
'mixed-decls',
|
||||||
|
'slash-div',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
devSourcemap: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue