Skip to content

Commit

Permalink
feat: email indexation (#216)
Browse files Browse the repository at this point in the history
Co-authored-by: medlouraoui <med.louraoui@gmail.com>
  • Loading branch information
lsagetlethias and mehdilouraoui committed Sep 14, 2022
1 parent cc90c1d commit 04d0555
Show file tree
Hide file tree
Showing 113 changed files with 4,708 additions and 3,147 deletions.
11 changes: 10 additions & 1 deletion .eslintrc.js
@@ -1,4 +1,6 @@
const path = require("path");
const glob = require("glob");
const { getDeclaredTypesNames } = require("./scripts/ts-ast");

const tsconfigPath = path.resolve(__dirname, "./tsconfig.json");
const tsconfigRendererPath = path.resolve(
Expand All @@ -12,9 +14,15 @@ const tsconfigCommonPath = path.resolve(
);
const tsconfigScriptsPath = path.resolve(__dirname, "./scripts/tsconfig.json");

const typesFiles = glob.sync(path.resolve(__dirname, "./types/**/*.d.ts"));
const globalTypesNames = typesFiles
.flatMap(getDeclaredTypesNames)
.reduce((acc, name) => ({ ...acc, [name]: "readonly" }), {});

/** @type {import("eslint").Linter.Config} */
const typescriptConfig = {
extends: "@socialgouv/eslint-config-typescript",
globals: globalTypesNames,
parser: "@typescript-eslint/parser",
parserOptions: {
project: tsconfigPath,
Expand All @@ -23,6 +31,7 @@ const typescriptConfig = {
plugins: ["typescript-sort-keys"],
rules: {
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-invalid-void-type": ["off"],
"@typescript-eslint/no-misused-promises": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unused-vars": "off",
Expand Down Expand Up @@ -118,7 +127,7 @@ const defaultConfig = {
{
files: "src/**/*.ts*",
globals: {
__static: true,
__static: "readonly",
},
},
{
Expand Down
2 changes: 2 additions & 0 deletions global.d.ts
Expand Up @@ -19,6 +19,8 @@ declare module "*.png" {
}

declare module "source-map-support";
// eslint-disable-next-line @typescript-eslint/naming-convention
declare const __static: string;

declare namespace NodeJS {
interface ProcessEnv {
Expand Down
3 changes: 3 additions & 0 deletions jest.config.js
Expand Up @@ -36,6 +36,9 @@ module.exports = {
projects: [
{
displayName: "integration",
setupFilesAfterEnv: [
"<rootDir>/tests/integration/mock/electron.ts",
],
testMatch: [
"<rootDir>/tests/integration/**/?(*.)(spec|test).(ts|tsx)",
],
Expand Down
12 changes: 7 additions & 5 deletions package.json
Expand Up @@ -180,6 +180,8 @@
"stylelint-config-sass-guidelines": "^9.0.1",
"stylelint-config-standard": "^26.0.0",
"ts-jest": "^27.1.3",
"ts-node": "10.4.0",
"tsconfig-paths": "^4.1.0",
"typescript": "4.6.2",
"unzip-crx-3": "^0.2.0",
"webpack": "4"
Expand All @@ -190,26 +192,26 @@
"@nivo/circle-packing": "^0.79.0",
"@nivo/core": "^0.79.0",
"@nivo/generators": "^0.79.0",
"@sentry/electron": "^3.0.3",
"@socialgouv/archimail-pst-extractor": "^0.0.2",
"@sentry/electron": "^3.0.7",
"@socialgouv/archimail-pst-extractor": "^0.1.0",
"d3": "^7.3.0",
"electron-store": "^8.0.2",
"electron-updater": "^5.0.5",
"electron-util": "^0.17.2",
"fs-extra": "^10.1.0",
"i18next": "^21.8.10",
"jotai": "^1.7.2",
"fs-extra": "^10.1.0",
"json2csv": "^5.0.6",
"level": "^8.0.0",
"lodash": "^4.17.21",
"normalize.css": "^8.0.1",
"posthog-js": "^1.24.0",
"posthog-js": "~1.24.0",
"posthog-node": "^1.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-dropzone": "^12.0.4",
"react-i18next": "^11.17.2",
"source-map-support": "^0.5.21",
"ts-node": "10.4.0",
"uuid": "^8.3.2",
"xlsx": "^0.18.0",
"zustand": "^3.7.0"
Expand Down
29 changes: 29 additions & 0 deletions scripts/ts-ast.js
@@ -0,0 +1,29 @@
const ts = require("typescript");

/** @type {Map<string, ts.SourceFile>} */
const sourceFileCache = new Map();

/**
* @param {string} filePath
*/
module.exports.getDeclaredTypesNames = function (filePath) {
const sourceFile =
sourceFileCache.get(filePath) ??
ts.createProgram([filePath], { noEmit: true }).getSourceFile(filePath);

sourceFileCache.set(filePath, sourceFile);

const out = [];
ts.forEachChild(sourceFile, (node) => {
if (
ts.isInterfaceDeclaration(node) ||
ts.isTypeAliasDeclaration(node)
) {
if (ts.SyntaxKind.DeclareKeyword === node.modifiers?.[0].kind) {
out.push(node.name.getText(sourceFile));
}
}
});

return out;
};
1 change: 1 addition & 0 deletions socket.yml
@@ -0,0 +1 @@
enabled: false
88 changes: 73 additions & 15 deletions src/common/config.ts
@@ -1,30 +1,88 @@
import { app, ipcMain, ipcRenderer } from "electron";
import path from "path";
import { isMainThread, workerData } from "worker_threads";

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
export const IS_MAIN = (ipcMain && !ipcRenderer) as boolean;
export const IS_TEST = !!process.env.NODE_ENV?.startsWith("test");
export const IS_DEV = process.env.NODE_ENV !== "production" && !IS_TEST;
export const IS_E2E = !!process.env.E2E;
export const IS_MAC = process.platform === "darwin";
import { name } from "./utils/package";

export const IS_WORKER = !isMainThread;
export const WORKER_CONFIG_TOKEN = "__config" as const;

// electron is not available in Worker side so we fake it.
const { app, ipcMain, ipcRenderer } = IS_WORKER
? // eslint-disable-next-line @typescript-eslint/consistent-type-imports -- deal with it
({} as typeof import("electron"))
: // eslint-disable-next-line @typescript-eslint/no-require-imports -- yep, sad uh?
require("electron");

export interface WorkerConfig {
APP_CACHE: string;
IS_DEV: boolean;
IS_DIST_MODE: boolean;
IS_E2E: boolean;
IS_MAC: boolean;
IS_MAIN: boolean;
IS_PACKAGED: boolean;
IS_TEST: boolean;
STATIC_PATH: string;
}

const localWorkerConfig: Partial<WorkerConfig> = IS_WORKER
? workerData[WORKER_CONFIG_TOKEN]
: {};

export const IS_MAIN =
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
localWorkerConfig.IS_MAIN ?? ((ipcMain && !ipcRenderer) as boolean);
export const IS_TEST =
localWorkerConfig.IS_TEST ?? !!process.env.NODE_ENV?.startsWith("test");
export const IS_DEV =
localWorkerConfig.IS_DEV ??
(process.env.NODE_ENV !== "production" && !IS_TEST);
export const IS_E2E = localWorkerConfig.IS_E2E ?? !!process.env.E2E;
export const IS_MAC = localWorkerConfig.IS_MAC ?? process.platform === "darwin";
const IS_PACKAGE_EVENT = "config.IS_PACKAGED";
if (IS_MAIN) {
const APP_CACHE_EVENT = "config.APP_CACHE";
if (IS_MAIN && !IS_WORKER) {
ipcMain.on(IS_PACKAGE_EVENT, (event) => {
event.returnValue = app.isPackaged;
});
ipcMain.on(APP_CACHE_EVENT, (event) => {
event.returnValue = app.getPath("cache");
});
}

export const IS_PACKAGED = (): boolean => {
if (IS_WORKER) return localWorkerConfig.IS_PACKAGED!;
if (IS_MAIN) {
return app.isPackaged;
} else return ipcRenderer.sendSync(IS_PACKAGE_EVENT, []) as boolean;
} else return ipcRenderer.sendSync(IS_PACKAGE_EVENT) as boolean;
};

export const APP_CACHE = (): string => {
if (IS_WORKER) return localWorkerConfig.APP_CACHE!;
if (IS_MAIN) {
return path.resolve(app.getPath("cache"), name);
} else return ipcRenderer.sendSync(APP_CACHE_EVENT) as string;
};

export const IS_DIST_MODE =
!IS_PACKAGED() && !process.env.ELECTRON_WEBPACK_WDS_PORT;
localWorkerConfig.IS_DIST_MODE ??
(!IS_PACKAGED() && !process.env.ELECTRON_WEBPACK_WDS_PORT);

export const STATIC_PATH =
localWorkerConfig.STATIC_PATH ?? IS_PACKAGED()
? __static // prod
: !process.env.ELECTRON_WEBPACK_WDS_PORT
? path.resolve(__dirname, "../../static") // dist / e2e
: __static; // dev

export const STATIC_PATH = IS_PACKAGED()
? __static // prod
: !process.env.ELECTRON_WEBPACK_WDS_PORT
? path.resolve(__dirname, "../../static") // dist / e2e
: __static; // dev
export const workerConfig: WorkerConfig = {
APP_CACHE: APP_CACHE(),
IS_DEV,
IS_DIST_MODE,
IS_E2E,
IS_MAC,
IS_MAIN,
IS_PACKAGED: IS_PACKAGED(),
IS_TEST,
STATIC_PATH,
};
6 changes: 0 additions & 6 deletions src/common/constant/event.ts
@@ -1,7 +1 @@
export const PST_EXTRACT_EVENT = "pstExtractor.event.extract";
export const PST_PROGRESS_EVENT = "pstExtractor.event.progress";
export const PST_PROGRESS_SUBSCRIBE_EVENT =
"pstExtractor.event.progressSuscribe";
export const PST_STOP_EXTRACT_EVENT = "pstExtractor.event.stopExtract";

export const CONSOLE_LOG_EVENT = "archifiltre-mails-console.event.log";
4 changes: 2 additions & 2 deletions src/common/lib/ModuleManager.ts
Expand Up @@ -5,7 +5,7 @@ import { AppError } from "./error/AppError";
/**
* Load and init the given modules in the current process.
*/
export const loadModules = async (...mods: Module[]): Promise<void> => {
export const loadModules = async (...mods: Module[]): pvoid => {
await Promise.all(
mods.map(async (mod) => {
console.log(`<MODULE_LOADER> ${mod.constructor.name} loading !`);
Expand All @@ -24,7 +24,7 @@ export const loadModules = async (...mods: Module[]): Promise<void> => {
/**
* Unload and uninit the given modules in the current process.
*/
export const unloadModules = async (...mods: Module[]): Promise<void> => {
export const unloadModules = async (...mods: Module[]): pvoid => {
await Promise.all(
mods.map(async (mod) => {
console.warn(
Expand Down
2 changes: 1 addition & 1 deletion src/common/lib/event/PubSub.ts
Expand Up @@ -96,7 +96,7 @@ export class PubSub extends IsomorphicService {
/**
* @override
*/
public async uninit(): Promise<void> {
public async uninit(): pvoid {
this.unsubscribersInRenderer.forEach((unsubscribe, uuid) => {
console.log("[pubsub] call unsubscribe for ", uuid);
unsubscribe();
Expand Down
2 changes: 1 addition & 1 deletion src/common/lib/event/type.ts
@@ -1,4 +1,4 @@
import type { Any, VoidFunction } from "../../utils/type";
import type { VoidFunction } from "../../utils/type";

export interface Event<TState = unknown> {
namespace: string;
Expand Down
40 changes: 28 additions & 12 deletions src/common/lib/ipc/event.ts
@@ -1,7 +1,11 @@
/* eslint-disable @typescript-eslint/no-empty-interface */
import type { IpcMainEvent } from "electron";

import type { UnknownMapping } from "../../utils/type";
import type {
EveryFunction,
UnboxPromise,
UnknownMapping,
} from "../../utils/type";

/**
* Define an IPC config with arguments type and a return values type.
Expand All @@ -13,6 +17,14 @@ export interface IpcConfig<TArgs extends unknown[], TReturnValue> {
returnValue: TReturnValue;
}

/**
* Define an IPC config from arguements and return value of a given function.
*/
export type IpcConfigFromFunction<TFunction extends EveryFunction> =
TFunction extends (...args: infer TArgs) => infer TReturnValue
? IpcConfig<TArgs, UnboxPromise<TReturnValue>>
: never;

/**
* Define an asynchronous IPC config (e.g. `ipcRenderer.send()`) with arguments
* type, return values type, and an channel that will be asynchronously called back with those return values.
Expand All @@ -27,6 +39,11 @@ export interface DualIpcConfig<
replyKey: TReplyChannel;
}

export type DualIpcConfigExtractReply<T extends DualAsyncIpcKeys> = (
replyChannel: GetDualAsyncIpcConfig<T>["replyKey"],
...args: GetDualAsyncIpcConfig<T>["returnValue"]
) => void;

/**
* A default IPC config if you only need to declare an IPC channel for autocomplete.
*/
Expand Down Expand Up @@ -69,15 +86,12 @@ export type ReplyDualAsyncIpcKeys = {
[K in DualAsyncIpcKeys]: DualAsyncIpcMapping[K]["replyKey"];
}[DualAsyncIpcKeys];

export type GetSyncIpcConfig<T> = T extends SyncIpcKeys
? SyncIpcMapping[T]
: DefaultIpcConfig;
export type GetAsyncIpcConfig<T> = T extends AsyncIpcKeys
? AsyncIpcMapping[T]
: DefaultIpcConfig;
export type GetDualAsyncIpcConfig<T> = T extends DualAsyncIpcKeys
? DualAsyncIpcMapping[T]
: DefaultDualIpcConfig;
export type GetSyncIpcConfig<T extends SyncIpcKeys | UnknownMapping> =
T extends SyncIpcKeys ? SyncIpcMapping[T] : DefaultIpcConfig;
export type GetAsyncIpcConfig<T extends AsyncIpcKeys | UnknownMapping> =
T extends AsyncIpcKeys ? AsyncIpcMapping[T] : DefaultIpcConfig;
export type GetDualAsyncIpcConfig<T extends DualAsyncIpcKeys | UnknownMapping> =
T extends DualAsyncIpcKeys ? DualAsyncIpcMapping[T] : DefaultDualIpcConfig;
export type GetRepliedDualAsyncIpcConfig<T> = T extends ReplyDualAsyncIpcKeys
? {
[K in DualAsyncIpcKeys]: T extends DualAsyncIpcMapping[K]["replyKey"]
Expand All @@ -100,7 +114,8 @@ export type ReplyDualAsyncIpcChannel<
> = ReplyDualAsyncIpcKeys | T;

// -- augments
interface CustomSyncIpcMainEvent<T> extends IpcMainEvent {
interface CustomSyncIpcMainEvent<T extends SyncIpcKeys | UnknownMapping>
extends IpcMainEvent {
/**
* Sync event comming from `ipcRenderer.sendSync`. Use `ipcRenderer.send` to return a sync value.
* @deprecated
Expand All @@ -109,7 +124,8 @@ interface CustomSyncIpcMainEvent<T> extends IpcMainEvent {
returnValue: GetSyncIpcConfig<T>["returnValue"];
}

interface CustomAsyncIpcMainEvent<T> extends IpcMainEvent {
interface CustomAsyncIpcMainEvent<T extends DualAsyncIpcKeys | UnknownMapping>
extends IpcMainEvent {
reply: (
replyChannel: GetDualAsyncIpcConfig<T>["replyKey"],
...args: GetDualAsyncIpcConfig<T>["returnValue"]
Expand Down
11 changes: 11 additions & 0 deletions src/common/lib/ipc/index.ts
Expand Up @@ -62,6 +62,17 @@ interface IpcRenderer extends BaseIpcRenderer {
) => void
) => this;

removeAllListeners: <
T extends
| AsyncIpcKeys
| DualAsyncIpcKeys
| ReplyDualAsyncIpcKeys
| SyncIpcKeys
| UnknownMapping
>(
channel: T
) => this;

send: <T extends DualAsyncIpcKeys | UnknownMapping>(
channel: DualAsyncIpcChannel<T>,
...args: GetDualAsyncIpcConfig<T>["args"]
Expand Down
4 changes: 2 additions & 2 deletions src/common/modules/ContainerModule.ts
Expand Up @@ -69,7 +69,7 @@ const serviceMap = new Map<string, unknown>();
class ContainerModule extends IsomorphicModule {
private inited = false;

public async init(): Promise<void> {
public async init(): pvoid {
if (this.inited && IS_PACKAGED()) {
throw new ModuleError(
"ContainerModule has already been inited.",
Expand All @@ -94,7 +94,7 @@ class ContainerModule extends IsomorphicModule {
this.inited = true;
}

public async uninit(): Promise<void> {
public async uninit(): pvoid {
if (!this.inited && IS_PACKAGED()) {
throw new ModuleError("ContainerModule not yet inited.", this);
}
Expand Down

0 comments on commit 04d0555

Please sign in to comment.