feat(desktop): add React DevTools extension for Electron dev mode
Downloads and loads React DevTools Chrome extension using Electron 41's session.extensions API directly, avoiding the deprecated session.loadExtension used by electron-devtools-installer. Extension is cached in userData after first download. Only loaded when !app.isPackaged.
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
"electron": "41.0.3",
|
||||
"electron-builder": "26.8.1",
|
||||
"typescript": "5.9.3",
|
||||
"unzip-crx-3": "^0.2.0",
|
||||
"wait-on": "8.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import path from "node:path";
|
||||
import { existsSync } from "node:fs";
|
||||
import { mkdir, writeFile, unlink } from "node:fs/promises";
|
||||
import { app, session, net } from "electron";
|
||||
|
||||
const REACT_DEVTOOLS_EXTENSION_ID = "fmkadmapgofadopljbjfkapdkoienihi";
|
||||
|
||||
export async function loadReactDevTools(): Promise<void> {
|
||||
const extensionsDir = path.join(app.getPath("userData"), "extensions");
|
||||
const extensionPath = path.join(extensionsDir, REACT_DEVTOOLS_EXTENSION_ID);
|
||||
|
||||
if (!existsSync(extensionPath)) {
|
||||
await mkdir(extensionsDir, { recursive: true });
|
||||
const crxUrl = `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${REACT_DEVTOOLS_EXTENSION_ID}%26uc&prodversion=${process.versions.chrome}`;
|
||||
const crxPath = `${extensionPath}.crx`;
|
||||
|
||||
const buffer = await new Promise<Buffer>((resolve, reject) => {
|
||||
const request = net.request(crxUrl);
|
||||
request.on("response", (response) => {
|
||||
const chunks: Buffer[] = [];
|
||||
response.on("data", (chunk) => chunks.push(chunk));
|
||||
response.on("end", () => resolve(Buffer.concat(chunks)));
|
||||
response.on("error", reject);
|
||||
});
|
||||
request.on("error", reject);
|
||||
request.end();
|
||||
});
|
||||
|
||||
await writeFile(crxPath, buffer);
|
||||
const unzipCrx = (await import("unzip-crx-3")).default;
|
||||
await unzipCrx(crxPath, extensionPath);
|
||||
await unlink(crxPath);
|
||||
}
|
||||
|
||||
try {
|
||||
const ext = await session.defaultSession.extensions.loadExtension(extensionPath, {
|
||||
allowFileAccess: true,
|
||||
});
|
||||
console.log(`[DevTools] Loaded: ${ext.name}`);
|
||||
} catch (err) {
|
||||
console.warn("[DevTools] Failed to load React DevTools:", err);
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,8 @@ async function createMainWindow(): Promise<void> {
|
||||
});
|
||||
|
||||
if (!app.isPackaged) {
|
||||
const { loadReactDevTools } = await import("./features/react-devtools.js");
|
||||
await loadReactDevTools();
|
||||
await mainWindow.loadURL(DEV_SERVER_URL);
|
||||
mainWindow.webContents.openDevTools({ mode: "detach" });
|
||||
return;
|
||||
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
declare module "unzip-crx-3" {
|
||||
function unzipCrx(crxPath: string, outputDir: string): Promise<void>;
|
||||
export default unzipCrx;
|
||||
}
|
||||
Reference in New Issue
Block a user