diff --git a/config/scripts/build-orcad.mjs b/config/scripts/build-orcad.mjs index 76b83c2445b..e5a8083426b 100644 --- a/config/scripts/build-orcad.mjs +++ b/config/scripts/build-orcad.mjs @@ -34,6 +34,7 @@ import { materializeWatcherPackage } from './orcad-watcher-package.mjs' import { stageOrcadWindowsProcessTree } from './orcad-windows-process-tree.mjs' import { stageOrcadWindowsCliLauncher } from './orcad-windows-cli-launcher.mjs' import { stageOrcadWindowsSecurity } from './orcad-windows-security.mjs' +import { stageOrcadLinearSdk } from './orcad-linear-sdk-package.mjs' import { ORCAD_EMOJI_SHORTCODE_DATASET, ORCAD_CLI_ENTRY_FILENAME, @@ -175,6 +176,7 @@ copyFileSync( createRequire(import.meta.url).resolve('emojibase-data/en/shortcodes/emojibase.json'), emojiDatasetOutput ) +await stageOrcadLinearSdk(ROOT, OUT_DIR) // The desktop template omits it: ~10 MB per target, and orcad already treats it as optional. if (existsSync(AGENT_BROWSER_SOURCE) && process.env.ORCAD_OMIT_AGENT_BROWSER !== '1') { copyFileSync(AGENT_BROWSER_SOURCE, AGENT_BROWSER_OUTPUT) diff --git a/config/scripts/orcad-linear-sdk-package.mjs b/config/scripts/orcad-linear-sdk-package.mjs new file mode 100644 index 00000000000..ea1e45901dd --- /dev/null +++ b/config/scripts/orcad-linear-sdk-package.mjs @@ -0,0 +1,25 @@ +import { build } from 'esbuild' +import { join } from 'node:path' +import { ORCAD_LINEAR_SDK_ENTRY } from '../../src/shared/orcad-artifacts.ts' + +/** + * @linear/sdk as one CommonJS file where orcad's lazy `require('@linear/sdk')` resolves it. + * Why bundled rather than copied: the package ships hashed chunk names that change every release. + */ +export async function stageOrcadLinearSdk(root, outputDir) { + await build({ + stdin: { + contents: "module.exports = require('@linear/sdk')", + resolveDir: root, + sourcefile: 'orcad-linear-sdk-entry.js' + }, + bundle: true, + platform: 'node', + target: 'node18', + format: 'cjs', + outfile: join(outputDir, ORCAD_LINEAR_SDK_ENTRY), + minify: true, + sourcemap: false, + logLevel: 'error' + }) +} diff --git a/config/scripts/orcad-linear-sdk-package.test.mjs b/config/scripts/orcad-linear-sdk-package.test.mjs new file mode 100644 index 00000000000..794138f4c9e --- /dev/null +++ b/config/scripts/orcad-linear-sdk-package.test.mjs @@ -0,0 +1,54 @@ +import { mkdtempSync, realpathSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterAll, beforeAll, expect, it } from 'vitest' +import { runProcessSync } from '@orca/process-host' +import { + ORCAD_ARTIFACTS, + ORCAD_LINEAR_SDK_ENTRY, + ORCAD_SERVER_ENTRY_FILENAME +} from '../../src/shared/orcad-artifacts.ts' +import { stageOrcadLinearSdk } from './orcad-linear-sdk-package.mjs' + +const root = join(import.meta.dirname, '..', '..') +// Outside the repo, so Node cannot fall back to the checkout's node_modules. +const deployment = realpathSync(mkdtempSync(join(tmpdir(), 'orcad-linear-sdk-'))) + +beforeAll(async () => { + await stageOrcadLinearSdk(root, deployment) +}, 60_000) + +afterAll(() => { + rmSync(deployment, { recursive: true, force: true }) +}) + +it('ships the SDK as a deployment artifact', () => { + expect(ORCAD_ARTIFACTS.map((artifact) => artifact.filename)).toContain(ORCAD_LINEAR_SDK_ENTRY) +}) + +it("resolves linear-sdk.ts's lazy require from the deployed server under plain Node", () => { + const server = join(deployment, ORCAD_SERVER_ENTRY_FILENAME) + writeFileSync( + server, + `const load = require('node:module').createRequire(__filename); + const sdk = load('@linear/sdk'); + console.log(JSON.stringify({ + resolved: load.resolve('@linear/sdk'), + client: typeof sdk.LinearClient, + authError: typeof sdk.AuthenticationLinearError + }));` + ) + const loaded = runProcessSync({ + program: process.execPath, + args: [server], + cwd: deployment, + env: { ORCA_BACKGROUND_LAUNCH: '1' }, + timeoutMs: 30_000 + }) + expect(loaded.code, loaded.stderr.slice(-2_000)).toBe(0) + expect(JSON.parse(loaded.stdout.trim())).toEqual({ + resolved: join(deployment, ORCAD_LINEAR_SDK_ENTRY), + client: 'function', + authError: 'function' + }) +}, 40_000) diff --git a/src/shared/orcad-artifacts.ts b/src/shared/orcad-artifacts.ts index a5487b9afd2..8d427b73495 100644 --- a/src/shared/orcad-artifacts.ts +++ b/src/shared/orcad-artifacts.ts @@ -103,6 +103,8 @@ export const ORCAD_PARCEL_WATCHER_ENTRY = 'node_modules/@parcel/watcher/index.js export const ORCAD_PARCEL_WATCHER_NATIVE = 'node_modules/@parcel/watcher/watcher.node' export const ORCAD_EMOJI_SHORTCODE_DATASET = 'node_modules/emojibase-data/en/shortcodes/emojibase.json' +// linear-sdk.ts loads it through createRequire, which the server bundle cannot follow. +export const ORCAD_LINEAR_SDK_ENTRY = 'node_modules/@linear/sdk/index.js' export const ORCAD_VERSION = '0.1.0' export const ORCAD_LAUNCHER_FILENAME = 'orcad.js' @@ -191,6 +193,7 @@ export const ORCAD_ARTIFACTS: readonly OrcadArtifact[] = [ { filename: ORCAD_PARCEL_WATCHER_ENTRY }, { filename: ORCAD_PARCEL_WATCHER_NATIVE }, { filename: ORCAD_EMOJI_SHORTCODE_DATASET }, + { filename: ORCAD_LINEAR_SDK_ENTRY }, ...ORCAD_NODE_PTY_JS_ARTIFACTS.map((filename) => ({ filename })), ...ORCAD_RIPGREP_LICENSE_ARTIFACTS.map((filename) => ({ filename })), ...ORCAD_NATIVE_CHAT_VISUALS_ARTIFACTS.map((filename) => ({ filename }))