Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions conf/tsconfig.content_scripts.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"module": "es2022",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noEmitOnError": true,
"alwaysStrict": true,
"noImplicitAny": true,
"strictNullChecks": true,
Expand Down
2 changes: 2 additions & 0 deletions conf/tsconfig.streams.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"allowJs": true,
"alwaysStrict": true,
"noImplicitAny": true,
"noFallthroughCasesInSwitch": true,
"noEmitOnError": true,
"strictNullChecks": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
Expand Down
2 changes: 2 additions & 0 deletions conf/tsconfig.test.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"lib": ["es6", "dom"],
"alwaysStrict": true,
"noImplicitAny": true,
"noFallthroughCasesInSwitch": true,
"noEmitOnError": true,
"strictNullChecks": true,
"strictFunctionTypes": false,
"allowSyntheticDefaultImports": true,
Expand Down
2 changes: 2 additions & 0 deletions conf/tsconfig.tooling.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"lib": ["es6", "dom"],
"alwaysStrict": true,
"noImplicitAny": true,
"noFallthroughCasesInSwitch": true,
"noEmitOnError": true,
"strictNullChecks": true,
"module": "commonjs",
"sourceMap": false,
Expand Down
105 changes: 44 additions & 61 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Importing necessary ESLint plugins
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import tseslint from 'typescript-eslint';
import noOnlyTestsPlugin from 'eslint-plugin-no-only-tests';
import headerPlugin from '@tony.ganchev/eslint-plugin-header';
Expand All @@ -10,13 +11,12 @@ import eslintConfigPrettier from 'eslint-config-prettier';
import pluginJs from '@eslint/js';
import globals from 'globals';

const mergeConfigs = (configs, key) => configs.reduce((acc, cfg) => ({ ...acc, ...(cfg[key] || {}) }), {});
const tsconfigRootDir = path.dirname(fileURLToPath(import.meta.url));
const mergeRules = configs => Object.assign({}, ...configs.map(cfg => cfg.rules));
const { strictTypeChecked, stylisticTypeChecked } = tseslint.configs;

const strictTypeCheckedPlugins = mergeConfigs(strictTypeChecked, 'plugins');
const strictTypeCheckedRules = mergeConfigs(strictTypeChecked, 'rules');
const stylisticPlugins = mergeConfigs(stylisticTypeChecked, 'plugins');
const stylisticRules = mergeConfigs(stylisticTypeChecked, 'rules');
const strictTypeCheckedRules = mergeRules(strictTypeChecked);
const stylisticRules = mergeRules(stylisticTypeChecked);

const jsConfigRules = {
complexity: 'off',
Expand Down Expand Up @@ -66,27 +66,32 @@ const jsConfigRules = {
destructuring: 'all',
},
],
'prefer-promise-reject-errors': 'off',
radix: 'off',
'require-atomic-updates': 0,
'sort-imports': 'off',
'local-rules/standard-loops': 'error',
};

const basePlugins = {
header: headerPlugin,
jsdoc: jsdocPlugin,
'prefer-arrow': preferArrowPlugin,
'no-null': noNullPlugin,
'local-rules': localRulesPlugin,
};

const commonConfig = {
plugins: {
'@typescript-eslint': tseslint.plugin,
'no-only-tests': noOnlyTestsPlugin,
header: headerPlugin,
jsdoc: jsdocPlugin,
'prefer-arrow': preferArrowPlugin,
'no-null': noNullPlugin,
'local-rules': localRulesPlugin,
...strictTypeCheckedPlugins,
...stylisticPlugins,
...basePlugins,
},
languageOptions: {
parser: tseslint.parser,
parserOptions: {
project: true,
tsconfigRootDir,
},
},
rules: {
Expand Down Expand Up @@ -172,6 +177,21 @@ const commonConfig = {
},
};

const tsProjectLanguageOptions = project => ({
...commonConfig.languageOptions,
parserOptions: {
project,
tsconfigRootDir,
},
});

const tsProjectConfig = (files, project, rules = {}) => ({
...commonConfig,
files,
languageOptions: tsProjectLanguageOptions(project),
...(Object.keys(rules).length ? { rules: { ...commonConfig.rules, ...rules } } : {}),
});

export default [
{
ignores: [
Expand All @@ -188,51 +208,18 @@ export default [
},
pluginJs.configs.recommended,
eslintConfigPrettier,
{
...commonConfig,
files: ['extension/**/*.ts'],
languageOptions: {
...commonConfig.languageOptions,
parserOptions: {
project: './tsconfig.json',
},
},
},
{
...commonConfig,
files: ['tooling/**/*.ts'],
languageOptions: {
...commonConfig.languageOptions,
parserOptions: {
project: './conf/tsconfig.tooling.json',
},
},
},
{
...commonConfig,
files: ['test/**/*.ts'],
languageOptions: {
...commonConfig.languageOptions,
parserOptions: {
project: './conf/tsconfig.test.eslint.json',
},
},
rules: {
...commonConfig.rules,
'@typescript-eslint/no-unused-expressions': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
},
},
tsProjectConfig(['extension/**/*.ts'], './tsconfig.json'),
tsProjectConfig(['tooling/**/*.ts'], './conf/tsconfig.tooling.json'),
tsProjectConfig(['test/**/*.ts'], './conf/tsconfig.test.eslint.json', {
'@typescript-eslint/no-unused-expressions': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
}),
...tseslint.config({
files: ['extension/js/content_scripts/webmail/**/*.ts'],
languageOptions: {
parserOptions: {
project: './conf/tsconfig.content_scripts.json',
},
},
languageOptions: tsProjectLanguageOptions('./conf/tsconfig.content_scripts.json'),
}),
{
files: ['scripts/**/*.js'],
Expand All @@ -245,11 +232,7 @@ export default [
},
},
plugins: {
header: headerPlugin,
jsdoc: jsdocPlugin,
'prefer-arrow': preferArrowPlugin,
'no-null': noNullPlugin,
'local-rules': localRulesPlugin,
...basePlugins,
},
rules: { ...pluginJs.configs.recommended.rules, ...jsConfigRules },
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ export class GoogleOAuth extends OAuth {
// temporary use jquery for upload requests https://github.com/FlowCrypt/flowcrypt-browser/issues/5612
if (req.progress?.upload) {
// eslint-disable-next-line @typescript-eslint/no-deprecated
return (await Api.ajaxWithJquery(req, 'json')) as RT;
return await Api.ajaxWithJquery(req, 'json');
} else {
return (await Api.ajax(req, 'json')) as RT;
return await Api.ajax(req, 'json');
}
};

Expand Down
2 changes: 1 addition & 1 deletion extension/js/common/api/key-server/attester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export class Attester extends Api {
};

private jsonPost = async <RT>(path: string, values: Dict<Serializable>, hdrs?: Dict<string>): Promise<RT> => {
return (await Api.apiCall(ATTESTER_API_HOST, path, { data: values, fmt: 'JSON' }, undefined, { 'api-version': '3', ...(hdrs ?? {}) }, 'json')) as RT;
return await Api.apiCall(ATTESTER_API_HOST, path, { data: values, fmt: 'JSON' }, undefined, { 'api-version': '3', ...(hdrs ?? {}) }, 'json');
};

private pubCall = async (resource: string, data?: string, hdrs?: Dict<string>): Promise<string> => {
Expand Down
2 changes: 1 addition & 1 deletion extension/js/common/api/shared/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ export class Api {
headers?: AjaxHeaders,
resFmt?: T
): Promise<FetchResult<T, RT>> {
progress = progress || ({} as ProgressCbs);
progress = progress || {};
let formattedData: FormData | string | undefined;
let dataPart: AjaxParams = { method: 'GET' };
if (values) {
Expand Down
9 changes: 5 additions & 4 deletions extension/js/common/browser/browser-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { Dict } from '../core/common.js';
import { FlatTypes } from '../platform/store/abstract-store.js';
import { CatchHelper } from '../platform/catch-helper.js';
import { Catch } from '../platform/catch.js';

export class BrowserExtension {
// todo - move extension-specific common.js code here
Expand All @@ -14,14 +15,14 @@ export class BrowserExtension {
try {
bugReport.error = JSON.stringify(error, undefined, 2);
} catch (e) {
bugReport.error_as_string = String(error);
bugReport.error_serialization_error = String(e);
bugReport.error_as_string = Catch.stringify(error);
bugReport.error_serialization_error = Catch.stringify(e);
}
try {
bugReport.details = JSON.stringify(details, undefined, 2);
} catch (e) {
bugReport.details_as_string = String(details as unknown);
bugReport.details_serialization_error = String(e);
bugReport.details_as_string = Catch.stringify(details);
bugReport.details_serialization_error = Catch.stringify(e);
}
let result = '';
for (const k of Object.keys(bugReport)) {
Expand Down
32 changes: 31 additions & 1 deletion extension/js/common/core/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,38 @@ export class Str {
return list.map(x => Str.formatEmailWithOptionalNameEx(x, forceBrackets)).join(', ');
};

public static stringify = (obj: unknown, space?: number): string => {
if (typeof obj === 'object') {
try {
return JSON.stringify(obj, undefined, space) ?? '';
} catch {
return '[unstringifiable object]';
}
}
if (typeof obj === 'function') {
return `[function ${obj.name || 'anonymous'}]`;
}
if (typeof obj === 'undefined') {
return '';
}
if (typeof obj === 'symbol') {
return obj.description ?? '';
}
switch (typeof obj) {
case 'string':
return obj;
case 'number':
return obj.toString();
case 'boolean':
return obj ? 'true' : 'false';
case 'bigint':
return obj.toString();
}
return '';
};

public static prettyPrint = (obj: unknown) => {
return typeof obj === 'object' ? JSON.stringify(obj, undefined, 2).replace(/ /g, '&nbsp;').replace(/\n/g, '<br />') : String(obj as unknown);
return Str.stringify(obj, 2).replace(/ /g, '&nbsp;').replace(/\n/g, '<br />');
};

public static normalizeSpaces = (str: string) => {
Expand Down
6 changes: 3 additions & 3 deletions extension/js/common/downloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class Downloader {
resolve({ result: download });
})
.catch((e: unknown) => {
reject(e as Error);
reject(e instanceof Error ? e : new Error(Catch.stringify(e)));
});
});
};
Expand Down Expand Up @@ -87,7 +87,7 @@ export class Downloader {
resolve(msg.raw || '');
})
.catch((e: unknown) => {
reject(e as Error);
reject(e instanceof Error ? e : new Error(Catch.stringify(e)));
});
});
};
Expand All @@ -108,7 +108,7 @@ export class Downloader {
resolve(msg);
})
.catch((e: unknown) => {
reject(e as Error);
reject(e instanceof Error ? e : new Error(Catch.stringify(e)));
});
});
};
Expand Down
2 changes: 1 addition & 1 deletion extension/js/common/message-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ export class MessageRenderer {
resolve(await this.processedMessages.await(msgId, processed));
})
.catch((e: unknown) => {
reject(e as Error);
reject(e instanceof Error ? e : new Error(Catch.stringify(e)));
});
});
};
Expand Down
14 changes: 5 additions & 9 deletions extension/js/common/platform/catch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import type { ExternalService as IExternalService } from '../api/account-servers/external-service.js';
import { Env } from '../browser/env.js';
import { Url } from '../core/common.js';
import { Str, Url } from '../core/common.js';
import { FLAVOR, VERSION } from '../core/const.js';
import { CatchHelper } from './catch-helper.js';
import { ErrorReport, UnreportableError } from './error-report.js';
Expand Down Expand Up @@ -49,14 +49,10 @@ export class Catch {
if (e instanceof Error) {
return `[typeof:Error:${e.name}] ${e.message}\n\n${e.stack}`;
}
if (typeof e === 'string') {
return `[typeof:string] ${e}`;
}
try {
return `[typeof:${typeof e}:${String(e)}] ${JSON.stringify(e)}`;
} catch {
return `[unstringifiable typeof:${typeof e}:${String(e)}]`;
if (typeof e === 'object') {
return `[typeof:object:${Object.prototype.toString.call(e)}] ${Str.stringify(e)}`;
}
return `[typeof:${typeof e}] ${Str.stringify(e)}`;
}

public static hasStack(e: unknown): e is ObjWithStack {
Expand Down Expand Up @@ -315,7 +311,7 @@ export class Catch {
private static formExceptionFromThrown(thrown: unknown, errMsg?: string, url?: string, line?: number, col?: number, isManuallyCalled?: boolean): Error {
let exception: Error;
if (typeof thrown !== 'object') {
exception = new Error(`THROWN_NON_OBJECT[${typeof thrown}]: ${String(thrown as unknown)}`);
exception = new Error(`THROWN_NON_OBJECT[${typeof thrown}]: ${Catch.stringify(thrown)}`);
} else if (errMsg && url && typeof line !== 'undefined' && !col && !thrown && !isManuallyCalled) {
exception = new Error(`LIMITED_ERROR: ${errMsg}`);
} else if (thrown instanceof Error) {
Expand Down
Loading
Loading