Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
16 changes: 11 additions & 5 deletions src/editor-launch.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,16 @@ const REFUSAL_REASONS = {
// editor that has since been uninstalled, is refused rather than resolved
// through an environment that is not there.
//
// The spawn options are the ones main.js holds everywhere else it starts a
// child: no shell, hidden on Windows, detached with no stdio so the editor
// outlives the app and cannot block on a pipe nobody reads.
// The spawn options: never a shell, and detached with no stdio so the editor
// outlives the app and cannot block on a pipe nobody reads. `detached` is
// unconditional here, unlike the runners in main.js which set it only off
// Windows — they are killed as a process group, and this child is released
// rather than ever signalled.
//
// No `windowsHide`. That flag fills STARTUPINFO with "start hidden", and a GUI
// application that honors it — VS Code does — launches with its window
// invisible while the spawn still reports success (#181). It is for children
// the app runs to collect their output; here the window is the point.
async function openSiteInEditor(sitePath, editorPath, {
sites,
platform,
Expand Down Expand Up @@ -321,8 +328,7 @@ async function openSiteInEditor(sitePath, editorPath, {
child = spawn(command, args, {
detached: true,
stdio: 'ignore',
shell: false,
windowsHide: true
shell: false
});
} catch (e) {
// A synchronous throw is the argument-shape failure only. The one that
Expand Down
28 changes: 25 additions & 3 deletions test/editor-launch.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -390,20 +390,42 @@ test('a refusal is logged on one bounded line', async () => {
assert.ok(description.length <= 121);
});

test('the child is detached, shell-free and hidden, and its handle released', async () => {
test('the child is detached and shell-free, and its handle released', async () => {
const { calls, options } = launchDeps();

await openSiteInEditor(SITE, EDITOR, options);

assert.deepEqual(calls[0].options, {
detached: true,
stdio: 'ignore',
shell: false,
windowsHide: true
shell: false
});
assert.equal(calls[0].unrefed, true);
});

// `windowsHide` fills the new process's STARTUPINFO with "start hidden", and a
// GUI application that honors it — VS Code does — launches with its window
// invisible while the spawn still reports success, so the app said ok and the
// contributor saw nothing (#181). The flag exists to suppress console windows
// the app's own tooling creates; the editor's window is the point of the click.
test('the editor is not asked to start hidden on Windows', async () => {
const winEditor = 'C:\\Users\\dev\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe';
const winSite = 'C:\\Users\\dev\\Desktop\\wp';
const fs = fakeFs({ [winEditor]: 'exe' });
const { calls, spawn } = recordingSpawn();

const result = await openSiteInEditor(winSite, winEditor, {
sites: [winSite],
platform: 'win32',
statPath: fs.statPath,
spawn
});

assert.deepEqual(result, { ok: true });
assert.ok(!('windowsHide' in calls[0].options),
'a GUI editor must not inherit a hidden first window');
});

test('a spawn that throws is reported, not raised at the window', async () => {
const { options } = launchDeps({
spawn: () => { throw new TypeError('args must be an array'); }
Expand Down
Loading