-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathpackage-paths.ts
More file actions
133 lines (122 loc) · 4.76 KB
/
package-paths.ts
File metadata and controls
133 lines (122 loc) · 4.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/**
* Utilities for resolving filesystem paths relative to the server package root.
*
* The server can run from three different directory layouts:
*
* 1. **Source** (dev): `server/src/lib/` → packageRoot = `server/`
* 2. **Bundle in monorepo** (dev/CI): `server/dist/` → packageRoot = `server/`
* 3. **Bundle via npm** (production): `<pkg>/dist/` → packageRoot = `<pkg>/`
*
* In all three cases, the bundled QL tool query packs live at
* `<packageRoot>/ql/<language>/tools/src/`.
*
* The "workspace root" (monorepo root) is one level above packageRoot when
* running from the monorepo checkout, and the packageRoot itself when running
* from an npm install (no parent monorepo).
*/
import { dirname, resolve } from 'path';
import { existsSync, readFileSync } from 'fs';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
/**
* Detect whether the current __dirname looks like source code (`src/lib` or
* `src/utils`) vs a bundled flat output directory (`dist/`).
*
* Uses a tail-of-path check so that unrelated `/src/` segments earlier in the
* install path (e.g. `~/src/project/node_modules/.../dist`) don't cause a
* false positive.
*/
function isRunningFromSource(dir: string): boolean {
const normalized = dir.replace(/\\/g, '/');
return /\/src(\/[^/]+)?$/.test(normalized);
}
/**
* Get the server package root directory.
*
* - From source (`server/src/utils/`): up 2 levels → `server/`
* - From bundle (`server/dist/` or `<pkg>/dist/`): up 1 level → package root
*/
export function getPackageRootDir(currentDir: string = __dirname): string {
return isRunningFromSource(currentDir)
? resolve(currentDir, '..', '..') // src/utils → server/
: resolve(currentDir, '..'); // dist/ → package root
}
/**
* Get the workspace root directory (monorepo root when applicable).
*
* If a `package.json` with `workspaces` exists one level above the package
* root, we're in a monorepo and that parent is the workspace root. Otherwise,
* the packageRoot itself is the workspace root (npm install scenario).
*/
export function getWorkspaceRootDir(packageRoot?: string): string {
const pkgRoot = packageRoot ?? getPackageRootDir();
const parentDir = resolve(pkgRoot, '..');
// In the monorepo, the parent directory contains a package.json with workspaces
try {
const parentPkgPath = resolve(parentDir, 'package.json');
if (existsSync(parentPkgPath)) {
const parentPkg = JSON.parse(readFileSync(parentPkgPath, 'utf8'));
if (parentPkg.workspaces) {
return parentDir;
}
}
} catch {
// Not in a monorepo — fall through
}
return pkgRoot;
}
/**
* Resolve the path to a tool query pack's source directory.
*
* @param language - CodeQL language identifier (e.g., "javascript", "cpp")
* @param packageRoot - Override the package root (for testing)
* @returns Absolute path to `ql/<language>/tools/src/`
*/
export function resolveToolQueryPackPath(language: string, packageRoot?: string): string {
const pkgRoot = packageRoot ?? getPackageRootDir();
return resolve(pkgRoot, 'ql', language, 'tools', 'src');
}
/**
* Read the package version from the nearest package.json.
*
* Cached at first call so the file is read at most once per process.
*/
let _cachedVersion: string | undefined;
export function getPackageVersion(): string {
if (_cachedVersion !== undefined) return _cachedVersion;
try {
const pkgPath = resolve(getPackageRootDir(), 'package.json');
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
_cachedVersion = pkg.version ?? '0.0.0';
} catch {
_cachedVersion = '0.0.0';
}
return _cachedVersion as string;
}
/**
* Get the effective workspace directory for resolving user-supplied relative
* paths (test directories, database paths, pack dirs, etc.).
*
* In a monorepo checkout the workspace root is the monorepo parent. In an
* npm-installed layout, `workspaceRootDir` falls back to `packageRootDir`
* which may be read-only and is not the user's project. In that case we
* fall back to `process.cwd()` so that relative paths resolve against the
* directory the user actually invoked the server from.
*
* Override with `CODEQL_MCP_WORKSPACE` for deterministic behavior.
*/
export function getUserWorkspaceDir(): string {
if (process.env.CODEQL_MCP_WORKSPACE) {
return process.env.CODEQL_MCP_WORKSPACE;
}
// When workspaceRootDir === packageRootDir we are NOT in a monorepo
// (npm-installed), so fall back to process.cwd().
if (workspaceRootDir === packageRootDir) {
return process.cwd();
}
return workspaceRootDir;
}
// Pre-computed values for use throughout the server
export const packageRootDir = getPackageRootDir();
export const workspaceRootDir = getWorkspaceRootDir(packageRootDir);