Migrate to Electron 42 and modernize tooling#1
Merged
Conversation
- Electron 23 -> 42; electron-builder 23 -> 26; electron-updater 5 -> 6;
electron-debug 3 -> 4; @electron/notarize 1 -> 3; @electron/rebuild 3 -> 4
- Webpack stack: webpack-dev-server 4 -> 5; css-loader 6 -> 7;
sass-loader 13 -> 16; @svgr/webpack 6 -> 8; css-minimizer 4 -> 7
- TypeScript 4.9 -> 5.7; prettier 2 -> 3; rimraf 4 -> 5
Add src/main/bootstrap.cjs so ts-node fires in dev. Electron 28+ loads
package.json::main via dynamic import(), which bypasses ts-node's CJS
require hook. The .cjs bootstrap registers ts-node and tsconfig-paths,
then requires main.ts.
Replace AppWindow's 0x0-size init guard with an explicit
initialSetupDone flag. Chromium 148 clamps BrowserWindow({width:0,
height:0}) to a non-zero minimum, so the old "size === 0" check never
fired and the window stayed clamped after startup.
Switch electron-debug to ESM-default import (v4 is pure ESM) and open
DevTools in a detached window.
Bundle VideoService.exe into packaged builds via extraResources so it
lands under process.resourcesPath.
Set package.json "type": "commonjs" to disable Node 22's ESM
auto-detection of typeless files. Migrate notarize.js to @electron/notarize
v3 API (drop appBundleId, require teamId), rimraf scripts to v5 named
rimrafSync export, and Sass map-get to the module-scoped map.get.
Add CLAUDE.md and a jest setup that stubs window.electron so the
existing App.test.tsx mounts cleanly inside jsdom.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR is a broad tooling and dependency modernization. It bumps Electron from 23 to 42 and updates the surrounding webpack/test/lint/prettier stack, then applies the minimum source/config changes needed to keep the app booting, packaging, and testing under the new versions. It also adds a jsdom Jest setup so the existing renderer smoke test mounts, and bundles VideoService.exe into packaged builds.
Changes:
- Major version bumps across Electron, electron-builder/updater/debug/rebuild/notarize, the webpack/css/sass/svgr stack, TypeScript 5.7, prettier 3, and rimraf 5, with corresponding API migrations (named
rimrafSync,@electron/notarizev3teamId, Sassmap.get, ESM-defaultelectron-debug). - New
src/main/bootstrap.cjsplus"type": "commonjs"andmainpointing at the bootstrap so ts-node hooks fire under Electron 28+'s dynamic-import main loader;start:mainno longer pre-registers ts-node on the CLI. AppWindow.updateSizeswitched from a0x0-size guard (broken by Chromium 148 clamping) to an explicitinitialSetupDonestatic flag; large prettier reformat (trailing commas, quotes, brace style) across renderer/main TS, plus rewrite ofVideoPreviewPanel.tsx; newCLAUDE.md,.erb/scripts/jest.setup.tsstubbingwindow.electron, andextraResourcesentry forVideoService.exe.
Reviewed changes
Copilot reviewed 21 out of 23 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Dependency bumps; switch main to bootstrap.cjs, add "type": "commonjs", register jest setup file, add VideoService.exe to extraResources, replace devEngines with engines. |
| src/main/bootstrap.cjs | New CJS entrypoint that registers ts-node + tsconfig-paths then loads main.ts. |
| src/main/main.ts | Use ESM-default electron-debug import with { devToolsMode: 'detach' }; prettier reformat. |
| src/main/RecordingChildProcess.ts | Prettier reformat; add no-console eslint disable for the debug log. |
| src/main/preload.ts | Prettier trailing-comma reformat. |
| src/renderer/ipc/AppWindow.ts | Replace 0×0-size init guard with initialSetupDone flag; reformat. |
| src/renderer/ipc/RecordingService.ts, HotKeyService.ts | Prettier trailing-comma reformat. |
| src/renderer/components/app/App.tsx | Prettier reformat (trailing commas only). |
| src/renderer/components/settingspanel/SettingsPanel.tsx | Prettier reformat. |
| src/renderer/components/videopreviewpanel/VideoPreviewPanel.tsx | Reformat + small refactor (destructuring, functional setState, type="button", jsx-a11y media disable comment); import ordering. |
| src/renderer/components/_Common.scss | Switch from deprecated map-get to sass:map's map.get; remove stray double semicolon. |
| src/common/IpcApi.ts, TelemetryService.ts | Prettier trailing-comma reformat. |
| src/tests/App.test.tsx | Fix import path to the actual renderer/components/app/App. |
| .erb/scripts/jest.setup.ts | New Jest setup stubbing window.electron IPC surface and navigator.mediaDevices for jsdom. |
| .erb/scripts/notarize.js | Migrate to @electron/notarize v3: drop appBundleId, require APPLE_TEAM_ID. |
| .erb/scripts/clean.js, delete-source-maps.js | Migrate to rimraf v5 named rimrafSync (with { glob: true } where globs are used). |
| .eslintignore | Ignore the entire .erb/ directory and .eslintrc.js; drops prior opt-in linting of .erb. |
| CLAUDE.md | New contributor guide for Claude Code with project context, commands, and architecture notes. |
| release/app/package-lock.json | Rename lockfile root from electron-react-boilerplate to desktop-recorder. |
Files not reviewed (1)
- release/app/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- .erb/scripts/notarize.js: defer @electron/notarize load to dynamic
import inside the darwin-only branch. v3 is pure ESM and the top-level
require() failed at module load on every platform, even though the
hook is gated to macOS.
- tsconfig.json: add skipLibCheck. react-inlinesvg v4's index.d.ts uses
both `export =` and named exports which tsc rejects; standard fix is
to skip type-checking third-party declaration files.
- .github/workflows/{test,publish}.yml: bump Node 16 -> 22 (engines now
requires >=20) and actions/{checkout,setup-node}@V3 -> v4. Add
APPLE_TEAM_ID to publish env (@electron/notarize v3 requires it).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add src/main/bootstrap.cjs so ts-node fires in dev. Electron 28+ loads package.json::main via dynamic import(), which bypasses ts-node's CJS require hook. The .cjs bootstrap registers ts-node and tsconfig-paths, then requires main.ts.
Replace AppWindow's 0x0-size init guard with an explicit initialSetupDone flag. Chromium 148 clamps BrowserWindow({width:0, height:0}) to a non-zero minimum, so the old "size === 0" check never fired and the window stayed clamped after startup.
Switch electron-debug to ESM-default import (v4 is pure ESM) and open DevTools in a detached window.
Bundle VideoService.exe into packaged builds via extraResources so it lands under process.resourcesPath.
Set package.json "type": "commonjs" to disable Node 22's ESM auto-detection of typeless files. Migrate notarize.js to @electron/notarize v3 API (drop appBundleId, require teamId), rimraf scripts to v5 named rimrafSync export, and Sass map-get to the module-scoped map.get.
Add CLAUDE.md and a jest setup that stubs window.electron so the existing App.test.tsx mounts cleanly inside jsdom.