fix(desktop): make get-windows optional dep so Linux build doesn't break (#85377)
(cherry picked from commit f25e3467e6a28911067b416bf6647c8c0b3254a6)
This commit is contained in:
23
apps/desktop/electron/get-windows.d.ts
vendored
Normal file
23
apps/desktop/electron/get-windows.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// Type declarations for the get-windows optionalDependency.
|
||||
//
|
||||
// get-windows ships no bundled types and is an optionalDependency: on Linux
|
||||
// `npm ci` skips it when its node-pre-gyp install script fails (no Linux
|
||||
// prebuilt; the node-gyp fallback needs `gyp` in the active Python), so the
|
||||
// package is legitimately absent from node_modules on Linux builds. Declaring
|
||||
// the module here keeps the typecheck independent of whether the package
|
||||
// installed — the runtime import in window-below.ts degrades to null when it
|
||||
// is absent.
|
||||
|
||||
declare module 'get-windows' {
|
||||
export interface GetWindowsWindow {
|
||||
bounds?: { height?: number; width?: number; x?: number; y?: number }
|
||||
id?: number
|
||||
owner?: { name?: string; processId?: number }
|
||||
title?: string
|
||||
}
|
||||
|
||||
export function openWindows(options?: {
|
||||
accessibilityPermission?: boolean
|
||||
screenRecordingPermission?: boolean
|
||||
}): Promise<GetWindowsWindow[]>
|
||||
}
|
||||
@@ -122,10 +122,15 @@ type GetWindowsModule = {
|
||||
>
|
||||
}
|
||||
|
||||
let getWindowsModule: Promise<GetWindowsModule> | null = null
|
||||
let getWindowsModule: Promise<GetWindowsModule | null> | null = null
|
||||
|
||||
const loadGetWindows = (): Promise<GetWindowsModule> => {
|
||||
getWindowsModule ??= import('get-windows')
|
||||
const loadGetWindows = (): Promise<GetWindowsModule | null> => {
|
||||
// get-windows is an optionalDependency: on Linux `npm ci` skips it when its
|
||||
// node-pre-gyp install script fails (no Linux prebuilt, and the node-gyp
|
||||
// fallback needs `gyp` in the active Python). A missing module is therefore
|
||||
// a normal state, so the lazy import resolves to null instead of rejecting;
|
||||
// enumeration then degrades to the failure note instead of an uncaught error.
|
||||
getWindowsModule ??= import('get-windows').catch(() => null)
|
||||
|
||||
return getWindowsModule
|
||||
}
|
||||
@@ -143,7 +148,13 @@ async function enumerateViaGetWindows(titlesAvailable: boolean): Promise<Enumera
|
||||
let raw
|
||||
|
||||
try {
|
||||
const { openWindows } = await loadGetWindows()
|
||||
const getWindows = await loadGetWindows()
|
||||
|
||||
if (!getWindows) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { openWindows } = getWindows
|
||||
raw = await openWindows(
|
||||
process.platform === 'darwin'
|
||||
? { accessibilityPermission: false, screenRecordingPermission: titlesAvailable }
|
||||
|
||||
@@ -114,7 +114,6 @@
|
||||
"emojibase-data": "16.0.3",
|
||||
"fflate": "0.8.3",
|
||||
"frimousse": "0.3.0",
|
||||
"get-windows": "9.3.0",
|
||||
"hast-util-from-html-isomorphic": "2.0.0",
|
||||
"hast-util-to-text": "4.0.2",
|
||||
"ignore": "7.0.6",
|
||||
@@ -145,6 +144,9 @@
|
||||
"vfile": "6.0.3",
|
||||
"web-haptics": "0.0.6"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"get-windows": "9.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron/rebuild": "4.2.0",
|
||||
"@playwright/test": "1.58.2",
|
||||
|
||||
@@ -407,12 +407,20 @@ export function openWindowsSync() {
|
||||
`
|
||||
|
||||
function resolveGetWindowsRoot() {
|
||||
// get-windows' exports map doesn't expose ./package.json; resolve the entry
|
||||
// (index.js sits at the package root) and take its directory.
|
||||
const entryPath = require.resolve('get-windows', {
|
||||
paths: [projectRoot]
|
||||
})
|
||||
return dirname(entryPath)
|
||||
// get-windows is an optionalDependency (its node-pre-gyp install script has
|
||||
// no Linux prebuilt and the node-gyp fallback needs `gyp` in the active
|
||||
// Python, so `npm ci` may skip it entirely on Linux). Return null when it is
|
||||
// absent; the caller decides whether that is fatal per platform.
|
||||
try {
|
||||
// get-windows' exports map doesn't expose ./package.json; resolve the entry
|
||||
// (index.js sits at the package root) and take its directory.
|
||||
const entryPath = require.resolve('get-windows', {
|
||||
paths: [projectRoot]
|
||||
})
|
||||
return dirname(entryPath)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -537,9 +545,32 @@ function rebuildGetWindowsViaNpm() {
|
||||
}
|
||||
}
|
||||
|
||||
export function stageGetWindows({ platform = process.platform } = {}) {
|
||||
const srcRoot = resolveGetWindowsRoot()
|
||||
export function stageGetWindows(
|
||||
{ platform = process.platform, resolveRoot = resolveGetWindowsRoot } = {}
|
||||
) {
|
||||
const srcRoot = resolveRoot()
|
||||
const destRoot = resolve(projectRoot, 'dist/node_modules/get-windows')
|
||||
|
||||
if (!srcRoot) {
|
||||
// get-windows is an optionalDependency: `npm ci` on Linux skips it when the
|
||||
// install script fails (no Linux prebuilt, node-gyp needs `gyp` in the
|
||||
// active Python), so absence here is a normal state, not a broken checkout.
|
||||
// On Linux it only backs read_window_below over X11 (lib/linux.js shells
|
||||
// out to xprop); skipping it degrades that tool to Hyprland IPC or an
|
||||
// "unavailable" note instead of failing the whole desktop build. On
|
||||
// darwin/win32 the native payload is required, so fail loudly.
|
||||
if (platform === 'linux') {
|
||||
console.warn(
|
||||
'[stage-native-deps] get-windows not installed (optional dep skipped on ' +
|
||||
'Linux); read_window_below will be unavailable in this build'
|
||||
)
|
||||
return undefined
|
||||
}
|
||||
throw new Error(
|
||||
`[stage-native-deps] get-windows is not installed; cannot stage its ${platform} native payload`
|
||||
)
|
||||
}
|
||||
|
||||
// Only a win32 host can produce the win32 binding, so a cross-platform pack
|
||||
// has nothing to gain from the rebuild.
|
||||
const rebuild =
|
||||
|
||||
@@ -6,6 +6,7 @@ import { pathToFileURL } from 'node:url'
|
||||
import { test } from 'vitest'
|
||||
|
||||
import {
|
||||
stageGetWindows,
|
||||
stageGetWindowsInto,
|
||||
stageNodePtyInto,
|
||||
classifyNativeBinary
|
||||
@@ -519,3 +520,29 @@ test('darwin staging ships the Swift helper executable and the rewritten windows
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
// ─── stageGetWindows (optionalDependency gate) ──────────────────────
|
||||
//
|
||||
// get-windows is an optionalDependency: on Linux its node-pre-gyp install
|
||||
// script fails (no Linux prebuilt; the node-gyp fallback needs `gyp` in the
|
||||
// active Python), so npm skips the package and `npm ci` must still succeed.
|
||||
// The staging step mirrors that: absent package on Linux is a skip, on
|
||||
// darwin/win32 it is a hard failure because the native payload is required.
|
||||
|
||||
test('linux staging skips when get-windows is absent (optional dep skipped by npm)', () => {
|
||||
assert.equal(stageGetWindows({ platform: 'linux', resolveRoot: () => null }), undefined)
|
||||
})
|
||||
|
||||
test('darwin staging fails when get-windows is absent', () => {
|
||||
assert.throws(
|
||||
() => stageGetWindows({ platform: 'darwin', resolveRoot: () => null }),
|
||||
/get-windows is not installed/
|
||||
)
|
||||
})
|
||||
|
||||
test('win32 staging fails when get-windows is absent', () => {
|
||||
assert.throws(
|
||||
() => stageGetWindows({ platform: 'win32', resolveRoot: () => null }),
|
||||
/get-windows is not installed/
|
||||
)
|
||||
})
|
||||
|
||||
5
package-lock.json
generated
5
package-lock.json
generated
@@ -108,7 +108,6 @@
|
||||
"emojibase-data": "16.0.3",
|
||||
"fflate": "0.8.3",
|
||||
"frimousse": "0.3.0",
|
||||
"get-windows": "9.3.0",
|
||||
"hast-util-from-html-isomorphic": "2.0.0",
|
||||
"hast-util-to-text": "4.0.2",
|
||||
"ignore": "7.0.6",
|
||||
@@ -167,6 +166,9 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.22.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"get-windows": "9.3.0"
|
||||
}
|
||||
},
|
||||
"apps/desktop/node_modules/@electron/get": {
|
||||
@@ -11004,6 +11006,7 @@
|
||||
"integrity": "sha512-DrOfQSmIcsFax28FfSUjbLTfeOkAG7yeh6NCb/9zzRkDuClXaqYqHuQBPUqcqd1uYS70ygYERSooacMAwvbyVw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=18.18"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user