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
38 changes: 36 additions & 2 deletions packages/lsp/src/server/dart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,47 @@ async function setupDartDependencies(platformId: PlatformId): Promise<string | u
}
}

const DART_PROBE_TIMEOUT_MS = 10_000

/**
* Find a system Dart that actually runs. A version-manager shim (e.g. mise) can resolve
* on PATH while exiting non-zero because no SDK version is configured.
*/
export async function findSystemDart(root: string): Promise<string | undefined> {
// Bun.which defaults to the PATH captured at startup; follow the current environment
const systemDart = Bun.which('dart', { PATH: process.env.PATH ?? '' })
if (!systemDart) {
return undefined
}

try {
// Run in the project root: version managers resolve the SDK per directory
const proc = Bun.spawn([systemDart, '--version'], {
cwd: root,
stdout: 'ignore',
stderr: 'ignore',
timeout: DART_PROBE_TIMEOUT_MS,
})
const exitCode = await proc.exited
if (exitCode !== 0) {
log.warn({ systemDart, exitCode, signal: proc.signalCode }, 'System Dart failed --version, trying auto-download')
return undefined
}
}
catch (err) {
log.warn({ systemDart, err }, 'Failed to spawn system Dart for version check, trying auto-download')
return undefined
}

return systemDart
}

export const DartServer: LSPServerInfo = {
id: 'dart',
extensions: ['.dart'],
root: nearestRoot(['pubspec.yaml', 'pubspec.lock']),
async spawn(root) {
// Try system dart first
const systemDart = Bun.which('dart')
const systemDart = await findSystemDart(root)
if (systemDart) {
try {
const proc = spawn(systemDart, ['language-server', '--client-id', 'dora.dart', '--client-version', '1.0'], {
Expand Down
43 changes: 43 additions & 0 deletions packages/lsp/test/unit/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
TypescriptServer,
VueServer,
} from '../../src/server'
import { findSystemDart } from '../../src/server/dart'

describe('LSP_SERVERS', () => {
test('contains expected servers', () => {
Expand Down Expand Up @@ -398,6 +399,48 @@ describe('DartServer', () => {
await fs.rm(tempDir, { recursive: true, force: true })
}
})

describe.skipIf(process.platform === 'win32')('findSystemDart', () => {
async function findSystemDartWith(dartScript: string, rootFiles: string[] = []): Promise<{ result: string | undefined, fakeDart: string }> {
const binDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dart-bin-'))
const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dart-root-'))
const savedPath = process.env.PATH
try {
const fakeDart = path.join(binDir, 'dart')
await fs.writeFile(fakeDart, dartScript, { mode: 0o755 })
Comment thread
amondnet marked this conversation as resolved.
for (const file of rootFiles) {
await fs.writeFile(path.join(rootDir, file), '')
}
process.env.PATH = binDir
return { result: await findSystemDart(rootDir), fakeDart }
}
finally {
process.env.PATH = savedPath
await fs.rm(binDir, { recursive: true, force: true })
await fs.rm(rootDir, { recursive: true, force: true })
}
}

test('ignores a dart on PATH that fails --version', async () => {
const { result } = await findSystemDartWith('#!/bin/sh\necho "No version is set for shim: dart" >&2\nexit 1\n')
expect(result).toBeUndefined()
})

test('ignores a dart on PATH that cannot be spawned', async () => {
const { result } = await findSystemDartWith('\x7FELF not a real binary')
expect(result).toBeUndefined()
})

test('returns a dart on PATH that runs --version', async () => {
const { result, fakeDart } = await findSystemDartWith('#!/bin/sh\nexit 0\n')
expect(result).toBe(fakeDart)
})

test('probes --version in the project root', async () => {
const { result, fakeDart } = await findSystemDartWith('#!/bin/sh\n[ -f .tool-versions ] || exit 1\n', ['.tool-versions'])
expect(result).toBe(fakeDart)
})
})
})

describe('VueServer', () => {
Expand Down
Loading