diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 5c6f66e..0000000 --- a/.editorconfig +++ /dev/null @@ -1,35 +0,0 @@ -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 diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 83d0a6a..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "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"] -} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index bfcc659..968170e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -12,60 +12,61 @@ 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" + 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 + 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 + 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 + 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. + # 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 - # 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 - # 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 - # â„šī¸ 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. - # 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 - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.gitignore b/.gitignore index 7a21a8d..e0400f6 100644 --- a/.gitignore +++ b/.gitignore @@ -688,7 +688,5 @@ FodyWeavers.xsd dist tools/syncConfig.js -tools/parse.js -tools/v2.js -tools/clean.js -package-lock.json +tools/deploy.js +tools/v2.js \ No newline at end of file diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 611cf4f..0000000 --- a/.prettierignore +++ /dev/null @@ -1,8 +0,0 @@ -node_modules/ -dist/ -*.log -.DS_Store -.vscode/ -.idea/ -*.sass -*.scss diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index aa3f49e..0000000 --- a/.prettierrc.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "semi": true, - "trailingComma": "es5", - "singleQuote": true, - "printWidth": 100, - "tabWidth": 2, - "useTabs": false, - "bracketSpacing": true, - "arrowParens": "avoid", - "endOfLine": "auto", - "overrides": [ - { - "files": "*.json", - "options": { - "parser": "json" - } - } - ] -} diff --git a/LICENSE b/LICENSE index 79f09ef..0404ba7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Jonas Pfalzgraf +Copyright (c) 2022 Jonas Pfalzgraf Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 7ed4257..d9c9633 100644 --- a/README.md +++ b/README.md @@ -4,222 +4,35 @@ [![GitHub forks](https://img.shields.io/github/forks/JosunLP/BrowserExtensionTemplate?style=for-the-badge)](https://github.com/JosunLP/BrowserExtensionTemplate/network) [![GitHub stars](https://img.shields.io/github/stars/JosunLP/BrowserExtensionTemplate?style=for-the-badge)](https://github.com/JosunLP/BrowserExtensionTemplate/stargazers) [![GitHub license](https://img.shields.io/github/license/JosunLP/BrowserExtensionTemplate?style=for-the-badge)](https://github.com/JosunLP/BrowserExtensionTemplate) +[![Twitter URL](https://img.shields.io/twitter/url?logo=twitter&style=for-the-badge&url=https%3A%2F%2Fgithub.com%2FJosunLP%2FBrowserExtensionTemplate)](https://twitter.com/intent/tweet?text=Look+what+i+found+on+GitHub+%23Developer%2C+%23SoftwareDeveloper%3A&url=https%3A%2F%2Fgithub.com%2FJosunLP%2FBrowserExtensionTemplate) [![CodeFactor](https://www.codefactor.io/repository/github/josunlp/browserextensiontemplate/badge?style=for-the-badge)](https://www.codefactor.io/repository/github/josunlp/browserextensiontemplate) +[![Known Vulnerabilities](https://snyk.io/test/github/JosunLP/BrowserExtensionTemplate/badge.svg?style=for-the-badge)](https://snyk.io/test/github/JosunLP/BrowserExtensionTemplate) ## Description -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 +A basic template based on SASS and TypeScript to create browser extensions without directly relying on a larger framework. ## Installation -### Quick Start - -```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 -``` +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. +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. ## Usage -### Project Structure - -```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 - -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 - -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 +Your sourcecode can be written in the `src` folder. The `public` folder contains static files like images, html and the manifest.json. +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 -This project is licensed under the [MIT License](https://opensource.org/licenses/MIT). +This project is licensed under the [MIT license](https://opensource.org/licenses/MIT). + +## Contributing + +This project is open source. Feel free to fork and contribute! ## Author -**_Jonas Pfalzgraf_** - -- Email: -- 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 +Jonas Pfalzgraf diff --git a/app.config.json b/app.config.json index 96a13dc..e5ceac0 100644 --- a/app.config.json +++ b/app.config.json @@ -1,29 +1,29 @@ { - "AppData": { - "id": "browser_extension_template", - "name": "Browser Extension Template", - "version": "0.0.1", - "description": "A basic template based on SASS and TypeScript to create browser extensions without directly relying on a larger framework.", - "repository": { - "type": "git", - "url": "git+ssh://git@github.com:JosunLP/BrowserExtensionTemplate.git" + "AppData": { + "id": "browser_extension_template", + "name": "Browser Extension Template", + "version": "0.0.1", + "description": "A basic template based on SASS and TypeScript to create browser extensions without directly relying on a larger framework.", + "repository": { + "type": "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" + } + ] }, - "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": [ + { + "key": "{{BET}}", + "value": "Browser Extension Template" + } ] - }, - "htmlTemplatePairs": [ - { - "key": "{{BET}}", - "value": "Browser Extension Template" - } - ] -} +} \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 0a5419c..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,55 +0,0 @@ -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', - }, - }, -]; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7fdbd2b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,418 @@ +{ + "name": "browser_extension_template", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "browser_extension_template", + "version": "0.0.1", + "license": "MIT", + "devDependencies": { + "@types/node": "^18.7.4", + "sass": "^1.39.0", + "typescript": "^4.2.4" + } + }, + "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/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/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/node": { + "version": "18.7.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.4.tgz", + "integrity": "sha512-RzRcw8c0B8LzryWOR4Wj7YOTFXvdYKwvrb6xQQyuDfnlTxwYXGCV5RZ/TEbq5L5kn+w3rliHAUyRcG1RtbmTFg==", + "dev": true + }, + "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" + } + }, + "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 + } + } +} diff --git a/package.json b/package.json index 1bfa990..67ccc8c 100644 --- a/package.json +++ b/package.json @@ -4,38 +4,19 @@ "private": true, "type": "module", "scripts": { - "deploy-v3": "npm run clean && npm run build-tooling && npm run sync && npm run build && npm run parse", + "deploy-v3": "npm run build-tooling && npm run sync && node ./tools/deploy.js && npm run build-js && npm run build-css", "deploy-v2": "npm run deploy-v3 && node ./tools/v2.js", - "build": "vite build", - "build-tooling": "tsc --project ./tooling.tsconfig.json", - "watch": "vite build --watch", - "sync": "npm run build-tooling && node ./tools/syncConfig.js", - "parse": "node ./tools/parse.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" + "build-js": "tsc -p tsconfig.json", + "build-css": "sass ./src/sass/:./dist/css/", + "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-ts": "tsc -w -p tsconfig.json", + "watch-sass": "sass --watch ./src/sass/:./dist/css/", + "sync": "npm run build-tooling && node ./tools/syncConfig.js" }, "devDependencies": { - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.30.1", - "@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" + "@types/node": "^18.7.4", + "sass": "^1.39.0", + "typescript": "^4.2.4" }, "browserslist": [ "> 1%", @@ -57,9 +38,5 @@ }, "bugs": { "url": "https://github.com/JosunLP/BrowserExtensionTemplate/issues" - }, - "dependencies": { - "@webcomponents/custom-elements": "^1.6.0", - "bootstrap": "^5.3.3" } } \ No newline at end of file diff --git a/public/manifest.json b/public/manifest.json index 6d17cfa..2f57490 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -15,27 +15,21 @@ "default_popup": "popup.html" }, "options_ui": { - "page": "options.html", - "open_in_tab": false + "page": "options.html" }, "permissions": [ - "storage", "notifications" ], "background": { - "service_worker": "background.js" + "service_worker": "js/background.js" }, - "content_security_policy": { - "extension_pages": "script-src 'self'; object-src 'self'; style-src 'self' 'unsafe-inline';" - }, - "web_accessible_resources": [ - { - "resources": [ - "icons/*.png" - ], - "matches": [ - "" - ] + "commands": { + "_execute_browser_action": { + "suggested_key": { + "default": "Ctrl+Shift+F", + "mac": "MacCtrl+Shift+F" + }, + "description": "Opens popup.html" } - ] + } } \ No newline at end of file diff --git a/public/options.html b/public/options.html index c9a0e6b..1cd3cda 100644 --- a/public/options.html +++ b/public/options.html @@ -1,25 +1,26 @@ - - + + - - - + + + + {{BET}} Options +
- +

Settings

-
+
+
- +
diff --git a/public/popup.html b/public/popup.html index a59f164..e467626 100644 --- a/public/popup.html +++ b/public/popup.html @@ -1,25 +1,26 @@ - - + + - - - + + + + {{BET}} +
- +

{{BET}}

-
+
+
- +
diff --git a/src/app.ts b/src/app.ts index 8157802..727e9f0 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,60 +1,30 @@ -import { Session } from './classes/session'; -import './sass/app.sass'; +import { Session } from "./classes/session.js" class App { - private static readonly CONTENT_ENTRY = 'content'; - private session: Session | null = null; - constructor() { - this.init(); - } + private static contentEntry: string = "content" - private async init(): Promise { - try { - 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 { - console.log('Hello World'); - } - - private async drawData(): Promise { - if (!this.session) { - throw new Error('Session not initialized'); + constructor() { + this.drawData() + this.main() } - const contentRoot = document.getElementById(App.CONTENT_ENTRY) as HTMLDivElement | null; - if (!contentRoot) { - throw new Error(`Element with id '${App.CONTENT_ENTRY}' not found`); + async main(): Promise { + console.log("Hello World") } - const body = document.createElement('div'); - body.className = 'app-content'; - - const title = document.createElement('h1'); - title.innerText = 'Hello World'; - - 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 = `
${message}
`; + async drawData(): Promise { + const session = Session.getInstance() + const contentRoot = document.getElementById(App.contentEntry) + const body = document.createElement("div") + const title = document.createElement("h1") + const text = document.createElement("p") + title.innerText = "Hello World" + text.innerText = session.contentTest + body.appendChild(title) + body.appendChild(text) + contentRoot.appendChild(body) } - } } -new App(); +new App(); \ No newline at end of file diff --git a/src/background.ts b/src/background.ts index 635339b..197f02f 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,82 +1,12 @@ -interface ExtensionMessage { - type: string; - payload?: unknown; -} - class Background { - constructor() { - this.init(); - } - private async init(): Promise { - try { - await this.setupEventListeners(); - await this.main(); - console.log('Background service worker initialized'); - } catch (error) { - console.error('Failed to initialize background service worker:', error); + constructor() { + this.main(); } - } - private async setupEventListeners(): Promise { - // Install event - chrome.runtime.onInstalled.addListener(details => { - console.log('Extension installed:', details.reason); - this.handleInstall(details.reason); - }); + async main(): Promise { - // 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 { - 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 { - 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 { - // Main background logic can be implemented here - // This method is called after initialization - } } -new Background(); +new Background(); \ No newline at end of file diff --git a/src/classes/errorBoundary.ts b/src/classes/errorBoundary.ts deleted file mode 100644 index 8e46dd2..0000000 --- a/src/classes/errorBoundary.ts +++ /dev/null @@ -1,99 +0,0 @@ -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): 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( - fn: (...args: T) => Promise - ): (...args: T) => Promise { - return async (...args: T): Promise => { - try { - return await fn(...args); - } catch (error) { - this.handleError(error instanceof Error ? error : new Error(String(error))); - throw error; - } - }; - } - - public wrapSync(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; - } - }; - } -} diff --git a/src/classes/session.ts b/src/classes/session.ts index 9c9c472..3443233 100644 --- a/src/classes/session.ts +++ b/src/classes/session.ts @@ -1,109 +1,55 @@ -interface SessionData { - sessionId: string; - contentTest: string; -} +export class Session { -interface StorageService { - save(key: string, data: unknown): Promise; - load(key: string): Promise; - remove(key: string): Promise; -} + private static instance: Session; -class LocalStorageService implements StorageService { - async save(key: string, data: unknown): Promise { - try { - localStorage.setItem(key, JSON.stringify(data)); - } catch (error) { - console.error('Failed to save to localStorage:', error); - throw new Error('Storage operation failed'); + private constructor() { } - } - async load(key: string): Promise { - 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; + static getInstance() { + if (!Session.instance && !Session.load()) { + Session.instance = new Session(); + } + if (!Session.instance && Session.load()) { + Session.instance = Session.load(); + } + Session.save(); + return Session.instance; } - } - async remove(key: string): Promise { - try { - localStorage.removeItem(key); - } catch (error) { - console.error('Failed to remove from localStorage:', error); - throw new Error('Storage operation failed'); + public static save() { + localStorage.setItem('session', JSON.stringify(this.instance)); } - } -} -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) { - this.sessionId = data?.sessionId ?? crypto.randomUUID(); - this.contentTest = data?.contentTest ?? 'This is a simple example of a web application'; - } - - public static async getInstance(): Promise { - if (!Session.instance) { - await Session.loadOrCreate(); + public static load(): Session | null { + const session = localStorage.getItem('session'); + if (session) { + const obj = JSON.parse(session); + const result = new Session(); + result.contentTest = obj.contentTest; + return result; + } + return null; } - return Session.instance!; - } - private static async loadOrCreate(): Promise { - try { - const savedData = await Session.storageService.load(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 static reloadSession() { + const session = localStorage.getItem('session'); + if (session) { + const obj = JSON.parse(session); + const result = new Session(); + result.contentTest = obj.contentTest; + Session.instance = result; + } } - } - public async save(): Promise { - 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 resetSession() { + localStorage.removeItem('session'); + sessionStorage.removeItem('session'); + this.instance = new Session(); + Session.save(); + location.reload(); } - } - public static async reset(): Promise { - try { - await Session.storageService.remove(Session.STORAGE_KEY); - Session.instance = new Session(); - await Session.instance.save(); + public readonly sessionId: string = crypto.randomUUID(); - // 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, - }; - } -} + public contentTest: string = 'This is a simple example of a web application'; +} \ No newline at end of file diff --git a/src/classes/settings.ts b/src/classes/settings.ts new file mode 100644 index 0000000..772b290 --- /dev/null +++ b/src/classes/settings.ts @@ -0,0 +1,29 @@ +import { Session } from "./session.js"; + +class Settings { + + private session = Session.getInstance(); + + constructor() { + this.renderSettings(); + } + + private async renderSettings(): Promise { + const settings = document.getElementById('settings'); + settings.innerHTML = ` +
+ + +
+ + `; + const saveSettings = document.getElementById('saveSettings'); + saveSettings.addEventListener('click', () => { + this.session.contentTest = (document.getElementById('contentTest')).value; + Session.save(); + Session.reloadSession(); + }); + } +} + +new Settings(); \ No newline at end of file diff --git a/src/components/button.ts b/src/components/button.ts deleted file mode 100644 index b023eaa..0000000 --- a/src/components/button.ts +++ /dev/null @@ -1,96 +0,0 @@ -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 = { - 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; - } -} diff --git a/src/sass/_mixin.sass b/src/sass/_mixin.sass index ddd1d41..41422c9 100644 --- a/src/sass/_mixin.sass +++ b/src/sass/_mixin.sass @@ -1,93 +1,86 @@ @import "root" @mixin respond-to($media) - @if $media == handhelds - @media only screen and (max-device-width: 40rem) - @content + @if $media == handhelds + @media only screen and (max-device-width: 40rem) + @content - @else if $media == medium-screens - @media only screen and (min-device-width: 40rem) - @content + @else if $media == medium-screens + @media only screen and (min-device-width: 40rem) + @content - @else if $media == wide-screens - @media only screen and (min-width: 1000px) - @content + @else if $media == wide-screens + @media only screen and (min-width: 1000px) + @content @mixin partialButton - width: 5rem !important - height: 2rem !important - text-align: center !important - margin: 0.5rem !important - border-color: $seccond-color !important - border-radius: 0.5rem !important + button + width: 5rem !important + height: 2rem !important + text-align: center !important + margin: 0.5rem !important + border-color: $seccond-color !important + border-radius: 0.5rem !important - @include respond-to(handhelds) - font-size: 3rem + @include respond-to(handhelds) + font-size: 3rem - @include respond-to(medium-screens) - font-size: 1.5rem + @include respond-to(medium-screens) + font-size: 1.5rem @mixin hoverMe - &:hover - button - color: grey !important + &:hover + button + color: grey !important @mixin shadow - box-shadow: 0px 0px 30px silver + box-shadow: 0px 0px 30px silver @mixin noselect - -webkit-touch-callout: none - -webkit-user-select: none - -khtml-user-select: none - -moz-user-select: none - -ms-user-select: none - user-select: none - pointer-events: none - -.form-group - margin-left: 2rem - margin-right: 2rem - margin-bottom: 0.5rem - flex-wrap: wrap - justify-content: center - display: flex + -webkit-touch-callout: none + -webkit-user-select: none + -khtml-user-select: none + -moz-user-select: none + -ms-user-select: none + user-select: none + pointer-events: none @mixin formBasic - display: block - padding: 2rem - background: $background-color-content - border-radius: 0.7rem - min-height: 20rem - margin: auto - margin-top: 2rem - margin-bottom: 2rem - @include shadow + display: block + padding: 2rem + background: $background-color-content + border-radius: 0.7rem + min-height: 20rem + margin: auto + margin-top: 2rem + margin-bottom: 2rem + @include shadow - @include respond-to(handhelds) - font-size: 2.5em - margin-left: -0.8em - margin-right: -0.8em - border-radius: 0 + @include respond-to(handhelds) + font-size: 2.5em + margin-left: -0.8em + margin-right: -0.8em + border-radius: 0 - @include respond-to(medium-screens) - max-width: 40rem + @include respond-to(medium-screens) + max-width: 40rem - @include respond-to(wide-screens) - max-width: 40rem + @include respond-to(wide-screens) + max-width: 40rem - input - @include respond-to(handhelds) - font-size: 3rem - border-radius: 0.5rem + input + @include respond-to(handhelds) + font-size: 3rem + border-radius: 0.5rem - .check - position: static - @include respond-to(handhelds) - width: 2rem !important - height: 2rem !important + .check + position: static + @include respond-to(handhelds) + width: 2rem !important + height: 2rem !important - button - @include respond-to(handhelds) - font-size: 3rem - padding: 1rem - border-radius: 1rem + button + @include respond-to(handhelds) + font-size: 3rem + padding: 1rem + border-radius: 1rem diff --git a/src/sass/_root.sass b/src/sass/_root.sass index e96d116..e3babc1 100644 --- a/src/sass/_root.sass +++ b/src/sass/_root.sass @@ -1,64 +1,8 @@ -// CSS Custom Properties for theming support -:root - --main-font: 'Ubuntu', 'Segoe UI', 'Roboto', sans-serif - --main-font-color: #ffffff - --main-font-color-hover: #f8f9fa - --main-font-color-focus: #e9ecef - --main-font-color-disabled: #6c757d - --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) +$main-font: 'Ubuntu', 'Staatliches' +$main-font-color: white +$main-font-color-hover: lightgrey +$main-uschrift-font: 'Ubuntu', Arial +$seccond-color: lightgrey +$background-color: #77B2FF +$background-color-content: rgb(198, 223, 255) +$logo-image: url('../icons/icon128.png') diff --git a/src/sass/app.sass b/src/sass/app.sass index c0c29d1..0b05a95 100644 --- a/src/sass/app.sass +++ b/src/sass/app.sass @@ -2,50 +2,40 @@ @import 'mixin' @import 'content' -// Bootstrap with legacy import (suppressed warnings via Vite config) -@import "../../node_modules/bootstrap/scss/bootstrap" - body - height: 30rem - width: 30rem - background-color: $background-color - text-align: center - margin: auto - padding: 1rem - color: $main-font-color - - p - font-size: 1rem - font-weight: bold + height: 30rem + width: 30rem + background-color: $background-color + text-align: center margin: auto padding: auto color: $main-font-color - text-align: center - font-family: 'Roboto', sans-serif + + @include partialButton h1, h2 - @include noselect - -form - @include formBasic - -.logo - width: 5rem - height: auto - padding-top: 2rem - @include noselect - -svg - @include noselect - -table - color: $main-font-color !important - - th @include noselect -a - color: $main-font-color +form + @include formBasic - &:hover - color: $background-color-content +.logo + width: 5rem + height: auto + padding-top: 2rem + @include noselect + +svg + @include noselect + +table + color: $main-font-color !important + + th + @include noselect + +a + color: $main-font-color + + &:hover + color: $background-color-content \ No newline at end of file diff --git a/src/settings.ts b/src/settings.ts deleted file mode 100644 index 0f08c73..0000000 --- a/src/settings.ts +++ /dev/null @@ -1,120 +0,0 @@ -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 { - 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 { - 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 = ` -
- - -
- ${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 { - 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 = `
${message}
`; - } - } -} - -new Settings(); diff --git a/src/types/buttonType.ts b/src/types/buttonType.ts deleted file mode 100644 index efdfd5f..0000000 --- a/src/types/buttonType.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type customButton = - | 'neutral' - | 'primary' - | 'secondary' - | 'success' - | 'danger' - | 'warning' - | 'info' - | 'light' - | 'dark'; diff --git a/tooling.tsconfig.json b/tooling.tsconfig.json deleted file mode 100644 index 72730da..0000000 --- a/tooling.tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "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" - ] -} diff --git a/tools/deploy.ts b/tools/deploy.ts new file mode 100644 index 0000000..a12bf54 --- /dev/null +++ b/tools/deploy.ts @@ -0,0 +1,84 @@ +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) { + let 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); \ No newline at end of file diff --git a/tools/parse.ts b/tools/parse.ts deleted file mode 100644 index de3f6a1..0000000 --- a/tools/parse.ts +++ /dev/null @@ -1,72 +0,0 @@ -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( - '', - `\n` - ); - fs.writeFileSync(htmlFile, content); - }); -}); - -buildHtmlFiles(DEPLOY_TARGET); - -console.log('Parsed Files: ', findHtmlFilesRecursive(DEPLOY_TARGET)); diff --git a/tools/syncConfig.ts b/tools/syncConfig.ts index 6c4bc41..22b800c 100644 --- a/tools/syncConfig.ts +++ b/tools/syncConfig.ts @@ -1,151 +1,22 @@ import * as fs from 'fs'; -interface AppConfig { - AppData: { - 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; - }>; -} +const appConfig = JSON.parse(fs.readFileSync('./app.config.json', 'utf8')); +const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8')); +const manifest = JSON.parse(fs.readFileSync('./public/manifest.json', 'utf8')); -interface PackageJson { - version: string; - name: string; - authors: Array<{ - name: string; - email: string; - }>; - description: string; - homepage: string; - license: string; - repository: { - type: string; - url: string; - }; - bugs: { - url: string; - }; - [key: string]: unknown; -} +pkg.version = appConfig.AppData.version; +pkg.name = appConfig.AppData.id; +pkg.authors = appConfig.AppData.authors; +pkg.description = appConfig.AppData.description; +pkg.homepage = appConfig.AppData.homepage; +pkg.license = appConfig.AppData.license; +pkg.repository = appConfig.AppData.repository; +pkg.bugs = appConfig.AppData.bugs; -interface ManifestJson { - version: string; - name: string; - description: string; - homepage_url: string; - [key: string]: unknown; -} +manifest.version = appConfig.AppData.version; +manifest.name = appConfig.AppData.name; +manifest.description = appConfig.AppData.description; +manifest.homepage_url = appConfig.AppData.homepage; -class ConfigSyncer { - private readonly appConfigPath = './app.config.json'; - private readonly packageJsonPath = './package.json'; - private readonly manifestJsonPath = './public/manifest.json'; - - public async sync(): Promise { - 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 { - 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 { - 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 { - 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 { - 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 { - 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); +fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2)); +fs.writeFileSync('./public/manifest.json', JSON.stringify(manifest, null, 2)); \ No newline at end of file diff --git a/tools/v2.ts b/tools/v2.ts index dcb4007..dd42ddb 100644 --- a/tools/v2.ts +++ b/tools/v2.ts @@ -1,46 +1,45 @@ -// @ts-ignore -const fs = require('fs'); +import * as fs from 'fs'; 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); -delete manifest.background.type; -delete manifest.background.service_worker; -manifest.background.persistent = true; +manifest.background.scripts.push(manifest.background.service_worker) +delete manifest.background.type +delete manifest.background.service_worker +manifest.background.persistent = true if (manifest.host_permissions) { - manifest.permissions.push(manifest.host_permissions); + manifest.permissions.push(manifest.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.optional_host_permissions; +delete manifest.host_permissions +delete manifest.optional_host_permissions -let newContentSecurityPolicy = ''; +let newContentSecurityPolicy = "" try { - for (const policy of manifest.content_security_policy) { - newContentSecurityPolicy += policy.key + "'" + policy.value + "'" + ' '; - } + for (const policy of manifest.content_security_policy) { + newContentSecurityPolicy += policy.key + "'" + policy.value + "'" + " " + } } catch (e) { - newContentSecurityPolicy = "default-src 'self'"; + newContentSecurityPolicy = "default-src 'self'" } -manifest.content_security_policy = newContentSecurityPolicy; +manifest.content_security_policy = newContentSecurityPolicy try { - manifest.web_accessible_resources = manifest.web_accessible_resources.resources; + manifest.web_accessible_resources = manifest.web_accessible_resources.resources } catch (e) { - manifest.web_accessible_resources = []; + manifest.web_accessible_resources = [] } 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)); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 96013de..fe9081d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,49 +1,81 @@ { "compilerOptions": { - /* Language and Environment */ - "target": "ES2022", - "module": "ESNext", - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, + /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Type Checking */ - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictBindCallApply": true, - "strictPropertyInitialization": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "exactOptionalPropertyTypes": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ + "module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": ["ESNext"], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./public/js/app.js", /* Concatenate and emit output to single file. */ + "outDir": "./dist/js/", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "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. */ - /* Emit */ - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "outDir": "./dist", - "removeComments": false, - "importHelpers": true, + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ - /* Interop Constraints */ - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "forceConsistentCasingInFileNames": true, + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "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. */ - /* Completeness */ - "skipLibCheck": true + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "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": ["src/**/*.ts"], - "exclude": ["node_modules", "dist", "tools/*.js"] + "include": [ + "./src/**/*.ts", + "./src/*.ts" + ], + "exclude": [ + "./tools/*.ts", + "./dist/*.ts", + "./dist/*.js" + ] } diff --git a/vite.config.ts b/vite.config.ts deleted file mode 100644 index fdf26ed..0000000 --- a/vite.config.ts +++ /dev/null @@ -1,71 +0,0 @@ -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, - }, -});