Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move fiddle-core usage to main process #1452

Merged
merged 4 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
32 changes: 30 additions & 2 deletions src/ambient.d.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import { ReleaseInfo, SemVer } from '@electron/fiddle-core';
import * as MonacoType from 'monaco-editor';

import {
BisectRequest,
BlockableAccelerator,
DownloadVersionParams,
EditorValues,
FiddleEvent,
FileTransformOperation,
Files,
IPackageManager,
InstallState,
InstallStateEvent,
MessageOptions,
NodeTypes,
OutputEntry,
PMOperationOptions,
PackageJsonOptions,
ProgressObject,
ReleaseInfo,
RunResult,
RunnableVersion,
SelectedLocalVersion,
SemVer,
StartFiddleParams,
TestRequest,
Version,
} from './interfaces';
Expand All @@ -42,6 +47,14 @@ declare global {
listener: (commandId: string) => void,
options?: { signal: AbortSignal },
): void;
addEventListener(
type: 'fiddle-runner-output',
listener: (output: string) => void,
): void;
addEventListener(
type: 'fiddle-stopped',
listener: (code: number | null, signal: string | null) => void,
): void;
addEventListener(
type: 'load-example',
listener: (exampleInfo: { path: string; tag: string }) => void,
Expand Down Expand Up @@ -72,6 +85,14 @@ declare global {
type: 'toggle-monaco-option',
listener: (path: string) => void,
): void;
addEventListener(
type: 'version-download-progress',
listener: (version: string, progress: ProgressObject) => void,
): void;
addEventListener(
type: 'version-state-changed',
listener: (event: InstallStateEvent) => void,
): void;
addEventListener(
type: 'electron-types-changed',
listener: (types: string, version: string) => void,
Expand All @@ -81,7 +102,6 @@ declare global {
...names: Array<string>
): Promise<string>;
app: App;
appPaths: Record<string, string>;
arch: string;
blockAccelerators(acceleratorsToBlock: BlockableAccelerator[]): void;
cleanupDirectory(dir: string): Promise<boolean>;
Expand All @@ -91,6 +111,10 @@ declare global {
name?: string,
): Promise<LoadedFiddleTheme>;
deleteUserData(name: string): Promise<void>;
downloadVersion(
version: string,
opts?: Partial<DownloadVersionParams>,
): Promise<void>;
fetchVersions(): Promise<Version[]>;
getAvailableThemes(): Promise<Array<LoadedFiddleTheme>>;
getElectronTypes(ver: RunnableVersion): Promise<string | undefined>;
Expand All @@ -111,6 +135,7 @@ declare global {
getReleaseInfo(version: string): Promise<ReleaseInfo | undefined>;
getReleasedVersions(): Array<Version>;
getUsername(): string;
getVersionState(version: string): InstallState;
isDevMode: boolean;
isReleasedMajor(major: number): Promise<boolean>;
macTitlebarClicked(): void;
Expand All @@ -132,13 +157,16 @@ declare global {
readThemeFile(name?: string): Promise<LoadedFiddleTheme | null>;
reloadWindows(): void;
removeAllListeners(type: FiddleEvent): void;
removeVersion(version: string): Promise<void>;
saveFilesToTemp(files: Files): Promise<string>;
selectLocalVersion: () => Promise<SelectedLocalVersion | undefined>;
sendReady(): void;
setNativeTheme(theme: 'dark' | 'light' | 'system'): void;
setShowMeTemplate(template?: string): void;
showWarningDialog(messageOptions: MessageOptions): void;
showWindow(): void;
startFiddle(params: StartFiddleParams): Promise<void>;
stopFiddle(): void;
taskDone(result: RunResult): void;
themePath: string;
uncacheTypes(ver: RunnableVersion): Promise<void>;
Expand Down
27 changes: 26 additions & 1 deletion src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import type { Mirrors } from '@electron/fiddle-core';
export type {
InstallStateEvent,
ProgressObject,
ReleaseInfo,
SemVer,
} from '@electron/fiddle-core';

export type Files = Map<string, string>;

export type FileTransform = (files: Files) => Promise<Files>;
Expand Down Expand Up @@ -158,6 +166,8 @@ export type FiddleEvent =
| 'clear-console'
| 'electron-types-changed'
| 'execute-monaco-command'
| 'fiddle-runner-output'
| 'fiddle-stopped'
| 'load-example'
| 'load-gist'
| 'make-fiddle'
Expand All @@ -177,7 +187,9 @@ export type FiddleEvent =
| 'test-task'
| 'toggle-bisect'
| 'toggle-monaco-option'
| 'undo-in-editor';
| 'undo-in-editor'
| 'version-download-progress'
| 'version-state-changed';

export interface MessageOptions {
message: string;
Expand Down Expand Up @@ -251,3 +263,16 @@ export interface PackageJsonOptions {
includeElectron?: boolean;
includeDependencies?: boolean;
}

export interface StartFiddleParams {
localPath: string | undefined;
isValidBuild: boolean; // If the localPath is a valid Electron build
version: string; // The user selected version
dir: string;
options: string[];
env: { [x: string]: string | undefined };
}

export interface DownloadVersionParams {
mirror: Mirrors;
}
15 changes: 14 additions & 1 deletion src/ipc-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export enum IpcEvents {
BISECT_COMMANDS_TOGGLE = 'BISECT_COMMANDS_TOGGLE',
BEFORE_QUIT = 'BEFORE_QUIT',
CONFIRM_QUIT = 'CONFIRM_QUIT',
GET_APP_PATHS = 'GET_APP_PATHS',
SELECT_ALL_IN_EDITOR = 'SELECT_ALL_IN_EDITOR',
UNDO_IN_EDITOR = 'UNDO_IN_EDITOR',
REDO_IN_EDITOR = 'REDO_IN_EDITOR',
Expand Down Expand Up @@ -66,6 +65,15 @@ export enum IpcEvents {
SAVE_FILES_TO_TEMP = 'SAVE_FILES_TO_TEMP',
SAVED_LOCAL_FIDDLE = 'SAVED_LOCAL_FIDDLE',
GET_FILES = 'GET_FILES',
START_FIDDLE = 'START_FIDDLE',
STOP_FIDDLE = 'STOP_FIDDLE',
GET_VERSION_STATE = 'GET_VERSION_STATE',
DOWNLOAD_VERSION = 'DOWNLOAD_VERSION',
REMOVE_VERSION = 'REMOVE_VERSION',
FIDDLE_RUNNER_OUTPUT = 'FIDDLE_RUNNER_OUTPUT',
FIDDLE_STOPPED = 'FIDDLE_STOPPED',
VERSION_DOWNLOAD_PROGRESS = 'VERSION_DOWNLOAD_PROGRESS',
VERSION_STATE_CHANGED = 'VERSION_STATE_CHANGED',
}

export const ipcMainEvents = [
Expand Down Expand Up @@ -107,6 +115,11 @@ export const ipcMainEvents = [
IpcEvents.CLEANUP_DIRECTORY,
IpcEvents.DELETE_USER_DATA,
IpcEvents.SAVE_FILES_TO_TEMP,
IpcEvents.START_FIDDLE,
IpcEvents.STOP_FIDDLE,
IpcEvents.GET_VERSION_STATE,
IpcEvents.DOWNLOAD_VERSION,
IpcEvents.REMOVE_VERSION,
];

export const WEBCONTENTS_READY_FOR_IPC_SIGNAL =
Expand Down
11 changes: 11 additions & 0 deletions src/main/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
import * as path from 'node:path';

import { app } from 'electron';

export const STATIC_DIR = path.resolve(__dirname, '../static');

export const ELECTRON_DOWNLOAD_PATH = path.join(
app.getPath('userData'),
'electron-bin',
);
export const ELECTRON_INSTALL_PATH = path.join(
ELECTRON_DOWNLOAD_PATH,
'current',
);
163 changes: 163 additions & 0 deletions src/main/fiddle-core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { ChildProcess } from 'node:child_process';

import { ElectronVersions, Installer, Runner } from '@electron/fiddle-core';
import { BrowserWindow, IpcMainEvent, WebContents } from 'electron';

import { ELECTRON_DOWNLOAD_PATH, ELECTRON_INSTALL_PATH } from './constants';
import { ipcMainManager } from './ipc';
import {
DownloadVersionParams,
ProgressObject,
StartFiddleParams,
} from '../interfaces';
import { IpcEvents } from '../ipc-events';

let installer: Installer;
let runner: Runner;

// Keep track of which fiddle process belongs to which WebContents
const fiddleProcesses = new WeakMap<WebContents, ChildProcess>();

const downloadingVersions = new Map<string, Promise<any>>();
const removingVersions = new Map<string, Promise<void>>();

/**
* Start running an Electron fiddle.
*/
export async function startFiddle(
webContents: WebContents,
params: StartFiddleParams,
): Promise<void> {
const { dir, env, isValidBuild, localPath, options, version } = params;
const child = await runner.spawn(
isValidBuild && localPath ? Installer.getExecPath(localPath) : version,
dir,
{ args: options, cwd: dir, env },
);
fiddleProcesses.set(webContents, child);

const pushOutput = (data: string | Buffer) => {
ipcMainManager.send(
IpcEvents.FIDDLE_RUNNER_OUTPUT,
[data.toString()],
webContents,
);
};

child.stdout?.on('data', pushOutput);
child.stderr?.on('data', pushOutput);

child.on('close', async (code, signal) => {
fiddleProcesses.delete(webContents);

ipcMainManager.send(IpcEvents.FIDDLE_STOPPED, [code, signal], webContents);
});
}

/**
* Stop a currently running Electron fiddle.
*/
export function stopFiddle(webContents: WebContents): void {
const child = fiddleProcesses.get(webContents);
child?.kill();

if (child) {
// If the child process is still alive 1 second after we've
// attempted to kill it by normal means, kill it forcefully.
setTimeout(() => {
if (child.exitCode === null) {
child.kill('SIGKILL');
}
}, 1000);
}
}

export async function setupFiddleCore(versions: ElectronVersions) {
// For managing downloads and versions for electron
installer = new Installer({
electronDownloads: ELECTRON_DOWNLOAD_PATH,
electronInstall: ELECTRON_INSTALL_PATH,
});

// Broadcast state changes to all windows
installer.on('state-changed', (event) => {
for (const window of BrowserWindow.getAllWindows()) {
ipcMainManager.send(
IpcEvents.VERSION_STATE_CHANGED,
[event],
window.webContents,
);
}
});

runner = await Runner.create({ installer, versions });

ipcMainManager.on(
IpcEvents.GET_VERSION_STATE,
(event: IpcMainEvent, version: string) => {
event.returnValue = installer.state(version);
},
);
ipcMainManager.handle(
IpcEvents.DOWNLOAD_VERSION,
async (
event: IpcMainEvent,
version: string,
opts?: Partial<DownloadVersionParams>,
) => {
const webContents = event.sender;

if (removingVersions.has(version)) {
throw new Error('Version is being removed');
}

if (!downloadingVersions.has(version)) {
const promise = installer.ensureDownloaded(version, {
...opts,
progressCallback: (progress: ProgressObject) => {
ipcMainManager.send(
IpcEvents.VERSION_DOWNLOAD_PROGRESS,
[version, progress],
webContents,
);
},
});

downloadingVersions.set(version, promise);
}

try {
await downloadingVersions.get(version);
} finally {
downloadingVersions.delete(version);
}
},
);
ipcMainManager.handle(
IpcEvents.REMOVE_VERSION,
async (_: IpcMainEvent, version: string) => {
if (downloadingVersions.has(version)) {
throw new Error('Version is being downloaded');
}

if (!removingVersions.has(version)) {
removingVersions.set(version, installer.remove(version));
}

try {
await removingVersions.get(version);
} finally {
removingVersions.delete(version);
}
},
);
ipcMainManager.handle(
IpcEvents.START_FIDDLE,
async (event: IpcMainEvent, params: StartFiddleParams) => {
await startFiddle(event.sender, params);
},
);
ipcMainManager.on(IpcEvents.STOP_FIDDLE, (event: IpcMainEvent) => {
stopFiddle(event.sender);
});
}
2 changes: 2 additions & 0 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { setupContent } from './content';
import { setupDevTools } from './devtools';
import { setupDialogs } from './dialogs';
import { setupTypes } from './electron-types';
import { setupFiddleCore } from './fiddle-core';
import { onFirstRunMaybe } from './first-run';
import { ipcMainManager } from './ipc';
import { setupNpm } from './npm';
Expand Down Expand Up @@ -64,6 +65,7 @@ export async function onReady() {
setupGetProjectName();
setupGetUsername();
setupTypes(knownVersions);
await setupFiddleCore(knownVersions);

// Do this after setting everything up to ensure that
// any IPC listeners are set up before they're used
Expand Down
Loading