Compare commits

...

2 Commits

Author SHA1 Message Date
David Sanders
dcba53cdfe fix: align process.exit() behavior in main process with Node.js 2026-04-30 18:59:11 -07:00
Niklas Wenzel
aaf328930d docs: fix version of deprecation notice (#51406) 2026-04-30 16:14:15 -07:00
3 changed files with 131 additions and 15 deletions

View File

@@ -134,6 +134,12 @@ When a cookie is deleted, the change cause remains `explicit`.
When the cookie being set is identical to an existing one (same name, domain, path, and value, with no actual changes), the change cause is `inserted-no-change-overwrite`.
When the value of the cookie being set remains unchanged but some of its attributes are updated, such as the expiration attribute, the change cause will be `inserted-no-value-change-overwrite`.
### Deprecated: `showHiddenFiles` in Dialogs on Linux
This property will still be honored on macOS and Windows, but support on Linux
will be removed in Electron 42. GTK intends for this to be a user choice rather
than an app choice and has removed the API to do this programmatically.
## Planned Breaking API Changes (40.0)
### Deprecated: `clipboard` API access from renderer processes
@@ -147,12 +153,6 @@ your preload script and expose it using the [contextBridge](https://www.electron
Debug symbols for MacOS (dSYM) now use xz compression in order to handle larger file sizes. `dsym.zip` files are now
`dsym.tar.xz` files. End users using debug symbols may need to update their zip utilities.
### Deprecated: `showHiddenFiles` in Dialogs on Linux
This property will still be honored on macOS and Windows, but support on Linux
will be removed in Electron 42. GTK intends for this to be a user choice rather
than an app choice and has removed the API to do this programmatically.
## Planned Breaking API Changes (39.0)
### Deprecated: `--host-rules` command line switch

View File

@@ -72,8 +72,17 @@ if (process.platform === 'win32') {
}
}
// Map process.exit to app.exit, which quits gracefully.
process.exit = app.exit as () => never;
// Map process.exit to app.exit, which quits gracefully. When called without
// an explicit code, fall back to process.exitCode like Node.js does.
process.exit = ((code: number | string | undefined | null) => {
// Refs https://github.com/nodejs/node/blob/fc192ee030ee076b948ce7d9d72cba6c101989b8/lib/internal/process/per_thread.js#L229-L252
if (code !== undefined) {
// Node.js handles any string to number conversion here for us
process.exitCode = code;
}
app.exit(process.exitCode || 0);
}) as typeof process.exit;
// Load the RPC server.
require('@electron/internal/browser/rpc-server');

View File

@@ -17,7 +17,14 @@ import {
spawn
} from './lib/codesign-helpers';
import { withTempDirectory } from './lib/fs-helpers';
import { getRemoteContext, ifdescribe, ifit, itremote, useRemoteContext } from './lib/spec-helpers';
import {
getRemoteContext,
ifdescribe,
ifit,
itremote,
startRemoteControlApp,
useRemoteContext
} from './lib/spec-helpers';
const mainFixturesPath = path.resolve(__dirname, 'fixtures');
@@ -586,9 +593,115 @@ describe('node feature', () => {
});
});
describe('process.exit', () => {
it('exits with exit code zero when called without an argument', async () => {
const rc = await startRemoteControlApp();
rc.remotely(() => {
setImmediate(() => process.exit());
}).catch(() => {});
const [code] = await once(rc.process, 'exit');
expect(code).to.equal(0);
});
it('uses process.exitCode when called without an argument', async () => {
const rc = await startRemoteControlApp();
rc.remotely(() => {
process.exitCode = 42;
setImmediate(() => process.exit());
}).catch(() => {});
const [code] = await once(rc.process, 'exit');
expect(code).to.equal(42);
});
it('overrides process.exitCode when called with an argument', async () => {
const rc = await startRemoteControlApp();
rc.remotely(() => {
process.exitCode = 42;
setImmediate(() => process.exit(11));
}).catch(() => {});
const [code] = await once(rc.process, 'exit');
expect(code).to.equal(11);
});
it('can be called with a null argument', async () => {
const rc = await startRemoteControlApp();
rc.remotely(() => {
setImmediate(() => process.exit(null));
}).catch(() => {});
const [code] = await once(rc.process, 'exit');
expect(code).to.equal(0);
});
it('can be called with a number argument', async () => {
const rc = await startRemoteControlApp();
rc.remotely(() => {
setImmediate(() => process.exit(7));
}).catch(() => {});
const [code] = await once(rc.process, 'exit');
expect(code).to.equal(7);
});
it('throws with an invalid number argument', async () => {
const rc = await startRemoteControlApp();
let stdout = '';
rc.process.stdout!.on('data', (d) => {
stdout += d.toString();
});
rc.remotely(() => {
setImmediate(() => {
try {
process.exit(4.2);
} catch (err) {
console.log(err);
process.exit(99);
}
});
}).catch(() => {});
const [code] = await once(rc.process, 'exit');
expect(code).to.equal(99);
expect(stdout).to.match(
/RangeError \[ERR_OUT_OF_RANGE\]: The value of "code" is out of range. It must be an integer./
);
});
it('can be called with a string argument', async () => {
const rc = await startRemoteControlApp();
rc.remotely(() => {
setImmediate(() => process.exit('12'));
}).catch(() => {});
const [code] = await once(rc.process, 'exit');
expect(code).to.equal(12);
});
it('throws with an invalid string argument', async () => {
const rc = await startRemoteControlApp();
let stdout = '';
rc.process.stdout!.on('data', (d) => {
stdout += d.toString();
});
rc.remotely(() => {
setImmediate(() => {
try {
process.exit('invalid');
} catch (err) {
console.log(err);
process.exit(99);
}
});
}).catch(() => {});
const [code] = await once(rc.process, 'exit');
expect(code).to.equal(99);
expect(stdout).to.match(/TypeError \[ERR_INVALID_ARG_TYPE\]/);
});
});
describe('process.stdout', () => {
useRemoteContext();
it('is a real Node stream', () => {
expect((process.stdout as any)._type).to.not.be.undefined();
});
itremote('does not throw an exception when accessed', () => {
expect(() => process.stdout).to.not.throw();
});
@@ -923,12 +1036,6 @@ describe('node feature', () => {
});
});
describe('process.stdout', () => {
it('is a real Node stream', () => {
expect((process.stdout as any)._type).to.not.be.undefined();
});
});
describe('fs.readFile', () => {
it('can accept a FileHandle as the Path argument', async () => {
const filePathForHandle = path.resolve(mainFixturesPath, 'dogs-running.txt');