Add linting packages (#581)

This commit is contained in:
Leendert de Borst
2025-03-07 00:49:47 +01:00
parent 4c45047d23
commit 09bc4286d9
8 changed files with 3445 additions and 48 deletions

View File

@@ -0,0 +1,129 @@
import js from "@eslint/js";
import tsParser from "@typescript-eslint/parser";
import tsPlugin from "@typescript-eslint/eslint-plugin";
import reactPlugin from "eslint-plugin-react";
import reactHooksPlugin from "eslint-plugin-react-hooks";
import importPlugin from "eslint-plugin-import";
import jsdocPlugin from "eslint-plugin-jsdoc";
import globals from 'globals';
export default [
{
ignores: [
"dist/**",
"node_modules/**",
]
},
js.configs.recommended,
{
files: ["src/**/*.{ts,tsx}"],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: "latest",
sourceType: "module",
project: "./tsconfig.json",
tsconfigRootDir: ".",
},
},
plugins: {
"@typescript-eslint": tsPlugin,
"react": reactPlugin,
"react-hooks": reactHooksPlugin,
"import": importPlugin,
"jsdoc": jsdocPlugin,
},
rules: {
...tsPlugin.configs.recommended.rules,
...reactPlugin.configs.recommended.rules,
...reactHooksPlugin.configs.recommended.rules,
"curly": ["error", "all"],
"brace-style": ["error", "1tbs", { "allowSingleLine": false }],
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/prefer-nullish-coalescing": ["error", {
"ignoreTernaryTests": false,
"ignoreConditionalTests": false,
"ignoreMixedLogicalExpressions": false
}],
"react/react-in-jsx-scope": "off",
"react/no-unused-prop-types": "error",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": ["error", {
"vars": "all",
"args": "after-used",
"ignoreRestSiblings": true,
"varsIgnorePattern": "^_",
"argsIgnorePattern": "^_"
}],
"indent": ["error", 2, {
"SwitchCase": 1,
"VariableDeclarator": 1,
"outerIIFEBody": 1,
"MemberExpression": 1,
"FunctionDeclaration": { "parameters": 1, "body": 1 },
"FunctionExpression": { "parameters": 1, "body": 1 },
"CallExpression": { "arguments": 1 },
"ArrayExpression": 1,
"ObjectExpression": 1,
"ImportDeclaration": 1,
"flatTernaryExpressions": false,
"ignoreComments": false
}],
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1, "maxBOF": 0 }],
"no-console": ["error", { allow: ["warn", "error", "info", "debug"] }],
"jsdoc/require-jsdoc": ["error", {
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
"ClassDeclaration": true,
"ArrowFunctionExpression": true,
"FunctionExpression": true
}
}],
"jsdoc/require-description": ["error", {
"contexts": [
"FunctionDeclaration",
"MethodDefinition",
"ClassDeclaration",
"ArrowFunctionExpression",
"FunctionExpression"
]
}],
"spaced-comment": ["error", "always"],
"multiline-comment-style": ["error", "starred-block"],
"@typescript-eslint/explicit-member-accessibility": ["error"],
"@typescript-eslint/explicit-function-return-type": ["error"],
"@typescript-eslint/typedef": ["error"],
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "interface",
"format": ["PascalCase"],
"prefix": ["I"]
},
{
"selector": "class",
"format": ["PascalCase"]
}
],
"react-hooks/exhaustive-deps": "warn",
"react/jsx-no-constructed-context-values": "error",
},
settings: {
react: {
version: "detect",
},
},
},
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
NodeJS: true,
chrome: 'readonly',
}
}
}
];

View File

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,9 @@
"build:firefox": "wxt build -b firefox",
"test": "vitest",
"test:coverage": "vitest run --coverage",
"lint": "eslint src",
"lint:custom": "eslint",
"lint:fix": "eslint src --fix",
"zip": "wxt zip",
"zip:firefox": "wxt zip -b firefox",
"compile": "tsc --noEmit",
@@ -19,6 +22,7 @@
"dependencies": {
"argon2-browser": "^1.18.0",
"buffer": "^6.0.3",
"globals": "^16.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-router-dom": "^7.1.4",
@@ -29,11 +33,20 @@
},
"devDependencies": {
"@types/chrome": "^0.0.280",
"@types/react": "^19.0.1",
"@types/react-dom": "^19.0.2",
"@types/jsdom": "^21.1.7",
"@types/react": "^19.0.7",
"@types/react-dom": "^19.0.3",
"@types/sql.js": "^1.4.9",
"@typescript-eslint/eslint-plugin": "^8.21.0",
"@typescript-eslint/parser": "^8.21.0",
"@vitest/coverage-v8": "^3.0.8",
"@wxt-dev/module-react": "^1.1.2",
"autoprefixer": "^10.4.20",
"eslint": "^9.19.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsdoc": "^50.6.3",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.1.0",
"jsdom": "^26.0.0",
"postcss": "^8.5.1",
"tailwindcss": "^3.4.17",

View File

@@ -5,6 +5,9 @@ import { handleClearVault, handleCreateIdentity, handleGetCredentials, handleGet
import { handleOpenPopup, handlePopupWithCredential } from './background/PopupMessageHandler';
export default defineBackground({
/**
*
*/
main() {
// Set up context menus
setupContextMenus();

View File

@@ -16,7 +16,7 @@ import { DefaultEmailDomainResponse as messageDefaultEmailDomainResponse } from
*/
export async function handleStoreVault(
message: any,
) : Promise<messageBoolResponse> {
) : Promise<messageBoolResponse> {
try {
const vaultResponse = message.vaultResponse as VaultResponse;
const encryptedVaultBlob = vaultResponse.vault.blob;
@@ -41,7 +41,7 @@ export async function handleStoreVault(
* Sync the vault with the server to check if a newer vault is available. If so, the vault will be updated.
*/
export async function handleSyncVault(
) : Promise<messageBoolResponse> {
) : Promise<messageBoolResponse> {
const webApi = new WebApiService(() => {});
const statusResponse = await webApi.getStatus();
const statusError = webApi.validateStatusResponse(statusResponse);
@@ -70,7 +70,7 @@ export async function handleSyncVault(
* Get the vault from browser storage.
*/
export async function handleGetVault(
) : Promise<messageVaultResponse> {
) : Promise<messageVaultResponse> {
try {
const encryptedVault = await storage.getItem('session:encryptedVault') as string;
const derivedKey = await storage.getItem('session:derivedKey') as string;
@@ -105,7 +105,7 @@ export async function handleGetVault(
* Clear the vault from browser storage.
*/
export function handleClearVault(
) : messageBoolResponse {
) : messageBoolResponse {
storage.removeItems([
'session:encryptedVault',
'session:derivedKey',
@@ -121,7 +121,7 @@ export function handleClearVault(
* Get all credentials.
*/
export async function handleGetCredentials(
) : Promise<messageCredentialsResponse> {
) : Promise<messageCredentialsResponse> {
const derivedKey = await storage.getItem('session:derivedKey') as string;
if (!derivedKey) {
@@ -143,7 +143,7 @@ export async function handleGetCredentials(
*/
export async function handleCreateIdentity(
message: any,
) : Promise<messageBoolResponse> {
) : Promise<messageBoolResponse> {
const derivedKey = await storage.getItem('session:derivedKey') as string;
if (!derivedKey) {
@@ -193,7 +193,7 @@ export async function getEmailAddressesForVault(
* Get default email domain for a vault.
*/
export function handleGetDefaultEmailDomain(
) : Promise<messageDefaultEmailDomainResponse> {
) : Promise<messageDefaultEmailDomainResponse> {
return (async () => {
try {
const privateEmailDomains = await storage.getItem('session:privateEmailDomains') as string[];
@@ -245,7 +245,7 @@ export function handleGetDefaultEmailDomain(
* Get the derived key for the encrypted vault.
*/
export async function handleGetDerivedKey(
) : Promise<string> {
) : Promise<string> {
const derivedKey = await storage.getItem('session:derivedKey') as string;
return derivedKey;
}

View File

@@ -5,6 +5,9 @@ import { onMessage } from "webext-bridge/content-script";
export default defineContentScript({
matches: ['<all_urls>'],
/**
*
*/
main(ctx) {
if (ctx.isInvalid) {
return;
@@ -24,7 +27,9 @@ export default defineContentScript({
!target.dataset.aliasvaultIgnore) {
const formDetector = new FormDetector(document, target);
if (!formDetector.containsLoginForm()) return;
if (!formDetector.containsLoginForm()) {
return;
}
injectIcon(target);

View File

@@ -420,42 +420,42 @@ export function createAutofillPopup(input: HTMLInputElement, credentials: Creden
const uniqueCredentials = Array.from(new Map(response.credentials.map(cred => [cred.Id, cred])).values());
let filteredCredentials;
if (searchTerm === '') {
// If search is empty, use original URL-based filtering
filteredCredentials = filterCredentials(
uniqueCredentials,
window.location.href,
document.title
).sort((a, b) => {
// First compare by service name
const serviceNameComparison = (a.ServiceName ?? '').localeCompare(b.ServiceName ?? '');
if (serviceNameComparison !== 0) {
return serviceNameComparison;
}
if (searchTerm === '') {
// If search is empty, use original URL-based filtering
filteredCredentials = filterCredentials(
uniqueCredentials,
window.location.href,
document.title
).sort((a, b) => {
// First compare by service name
const serviceNameComparison = (a.ServiceName ?? '').localeCompare(b.ServiceName ?? '');
if (serviceNameComparison !== 0) {
return serviceNameComparison;
}
// If service names are equal, compare by username/nickname
return (a.Username ?? '').localeCompare(b.Username ?? '');
});
} else {
// Otherwise filter based on search term
filteredCredentials = uniqueCredentials.filter(cred =>
cred.ServiceName.toLowerCase().includes(searchTerm) ||
// If service names are equal, compare by username/nickname
return (a.Username ?? '').localeCompare(b.Username ?? '');
});
} else {
// Otherwise filter based on search term
filteredCredentials = uniqueCredentials.filter(cred =>
cred.ServiceName.toLowerCase().includes(searchTerm) ||
cred.Username.toLowerCase().includes(searchTerm) ||
cred.Email.toLowerCase().includes(searchTerm) ||
cred.ServiceUrl?.toLowerCase().includes(searchTerm)
).sort((a, b) => {
// First compare by service name
const serviceNameComparison = (a.ServiceName ?? '').localeCompare(b.ServiceName ?? '');
if (serviceNameComparison !== 0) {
return serviceNameComparison;
}
).sort((a, b) => {
// First compare by service name
const serviceNameComparison = (a.ServiceName ?? '').localeCompare(b.ServiceName ?? '');
if (serviceNameComparison !== 0) {
return serviceNameComparison;
}
// If service names are equal, compare by username/nickname
return (a.Username ?? '').localeCompare(b.Username ?? '');
});
}
// If service names are equal, compare by username/nickname
return (a.Username ?? '').localeCompare(b.Username ?? '');
});
}
// Update popup content with filtered results
// Update popup content with filtered results
updatePopupContent(filteredCredentials, credentialList, input);
}
});

View File

@@ -20,6 +20,9 @@ const AuthSettings: React.FC = () => {
const [customClientUrl, setCustomClientUrl] = useState<string>('');
useEffect(() => {
/**
*
*/
const loadStoredSettings = async () => {
const apiUrl = await storage.getItem('local:apiUrl') as string;
const clientUrl = await storage.getItem('local:clientUrl') as string;