Skip to content
Open
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
30 changes: 30 additions & 0 deletions src/sync/paths.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,36 @@
expect(paths.configDir).toBe('C:\\Users\\Test\\AppData\\Roaming');
expect(paths.dataDir).toBe('C:\\Users\\Test\\AppData\\Local');
});

it('respects XDG env vars on windows', () => {
const env = {
USERPROFILE: 'C:\\Users\\Test',
APPDATA: 'C:\\Users\\Test\\AppData\\Roaming',
LOCALAPPDATA: 'C:\\Users\\Test\\AppData\\Local',
XDG_CONFIG_HOME: 'C:\\Users\\Test\\.config',
XDG_DATA_HOME: 'C:\\Users\\Test\\.local\\share',
XDG_STATE_HOME: 'C:\\Users\\Test\\.local\\state',
} as NodeJS.ProcessEnv;
const paths = resolveXdgPaths(env, 'win32');

expect(paths.configDir).toBe('C:\\Users\\Test\\.config');
expect(paths.dataDir).toBe('C:\\Users\\Test\\.local\\share');
expect(paths.stateDir).toBe('C:\\Users\\Test\\.local\\state');
});

it('prefers XDG on windows when only XDG_CONFIG_HOME is set', () => {
const env = {
USERPROFILE: 'C:\\Users\\Test',
APPDATA: 'C:\\Users\\Test\\AppData\\Roaming',
LOCALAPPDATA: 'C:\\Users\\Test\\AppData\\Local',
XDG_CONFIG_HOME: 'C:\\Users\\Test\\.config',
} as NodeJS.ProcessEnv;
const paths = resolveXdgPaths(env, 'win32');

expect(paths.configDir).toBe('C:\\Users\\Test\\.config');
expect(paths.dataDir).toBe('C:\\Users\\Test\\.local\\share');

Check failure on line 53 in src/sync/paths.test.ts

View workflow job for this annotation

GitHub Actions / Check

error: expect(received).toBe(expected)

Expected: "C:\Users\Test\.local\share" Received: "C:\Users\Test/.local/share" at <anonymous> (/home/runner/work/opencode-synced/opencode-synced/src/sync/paths.test.ts:53:27)
expect(paths.stateDir).toBe('C:\\Users\\Test\\.local\\state');
});
});

describe('resolveSyncLocations', () => {
Expand Down
19 changes: 18 additions & 1 deletion src/sync/paths.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import crypto from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';

import type { SyncConfig } from './config.js';
Expand Down Expand Up @@ -83,9 +84,25 @@ export function resolveXdgPaths(
}

if (platform === 'win32') {
// On Windows, prefer XDG environment variables when explicitly set.
// Some tools (including opencode itself) use XDG-style paths on Windows
// (e.g. ~/.config, ~/.local/share, ~/.local/state) instead of AppData.
// If XDG vars are set, honor them. Otherwise, detect whether opencode
// is using XDG layout by checking for ~/.config/opencode, and fall back
// to standard Windows paths only if XDG paths don't exist.
const xdgConfigDir = env.XDG_CONFIG_HOME ?? path.join(homeDir, '.config');
const xdgDataDir = env.XDG_DATA_HOME ?? path.join(homeDir, '.local', 'share');
const xdgStateDir = env.XDG_STATE_HOME ?? path.join(homeDir, '.local', 'state');

const hasXdgEnv = Boolean(env.XDG_CONFIG_HOME ?? env.XDG_DATA_HOME ?? env.XDG_STATE_HOME);
const xdgConfigExists = fs.existsSync(path.join(xdgConfigDir, 'opencode'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of fs.existsSync is a synchronous file system operation. While it might be acceptable for path resolution during application startup, synchronous I/O operations can block the Node.js event loop, potentially leading to performance bottlenecks or unresponsiveness, especially in larger applications or under heavy load. Consider using an asynchronous alternative like fs.promises.access or fs.promises.stat if the resolveXdgPaths function could be refactored to be asynchronous. If not, ensure this synchronous call's impact is understood and deemed acceptable for the current use case.


if (hasXdgEnv || xdgConfigExists) {
return { homeDir, configDir: xdgConfigDir, dataDir: xdgDataDir, stateDir: xdgStateDir };
}

const configDir = env.APPDATA ?? path.join(homeDir, 'AppData', 'Roaming');
const dataDir = env.LOCALAPPDATA ?? path.join(homeDir, 'AppData', 'Local');
// Windows doesn't have XDG_STATE_HOME equivalent, use LOCALAPPDATA
const stateDir = env.LOCALAPPDATA ?? path.join(homeDir, 'AppData', 'Local');
return { homeDir, configDir, dataDir, stateDir };
}
Expand Down
Loading