Skip to content

Commit c56281f

Browse files
use version instead of string
1 parent c0d1088 commit c56281f

File tree

9 files changed

+73
-79
lines changed

9 files changed

+73
-79
lines changed

editor/src/ctx.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,8 @@ export class Ctx {
286286
const parsed = vscode.Uri.parse(params.uri).fsPath;
287287
const packageFolder = path.dirname(path.dirname(parsed));
288288

289-
const packageManager = new PackageManager(packageFolder, undefined, session.accessToken,
290-
this.get4DVersion().toString(false).replace("R", "."));
289+
const packageManager = new PackageManager(packageFolder, session.accessToken,
290+
this.get4DVersion().toString(false), undefined);
291291
await packageManager.initialize();
292292
let options: FetchOptions = {};
293293
packageManager.fetch(options).then(() => {

packageManager/src/PackageManager.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { GitHubDependency } from './dependency/GithubDependency';
99
// Cross-platform test paths
1010
const TEST_PROJECT_PATH = path.resolve('/tmp/test-project');
1111
const TEST_CACHE_PATH = path.resolve('/tmp/test-cache');
12-
12+
const TEST_IDE_VERSION = "21.2.0";
1313
// Mock dependencies
1414
vi.mock('./config/ConfigReader');
1515
vi.mock('./cache/CacheManager');
@@ -70,7 +70,7 @@ describe('PackageManager', () => {
7070
checkOutdated: vi.fn()
7171
}) as unknown as GitHubDependency);
7272

73-
packageManager = new PackageManager(TEST_PROJECT_PATH);
73+
packageManager = new PackageManager(TEST_PROJECT_PATH, TEST_IDE_VERSION);
7474
});
7575

7676
describe('fetchRecursively', () => {
@@ -100,7 +100,7 @@ describe('PackageManager', () => {
100100
compare: vi.fn()
101101
}) as unknown as GitHubDependency);
102102

103-
packageManager = new PackageManager(TEST_PROJECT_PATH);
103+
packageManager = new PackageManager(TEST_PROJECT_PATH, TEST_IDE_VERSION);
104104
await packageManager.initialize();
105105
const result = await packageManager.fetch();
106106

@@ -136,7 +136,7 @@ describe('PackageManager', () => {
136136
} as unknown as GitHubDependency;
137137
});
138138

139-
packageManager = new PackageManager(TEST_PROJECT_PATH);
139+
packageManager = new PackageManager(TEST_PROJECT_PATH, TEST_IDE_VERSION);
140140
await packageManager.initialize();
141141
const result = await packageManager.fetch();
142142

@@ -188,7 +188,7 @@ describe('PackageManager', () => {
188188
} as unknown as GitHubDependency;
189189
});
190190

191-
packageManager = new PackageManager(TEST_PROJECT_PATH);
191+
packageManager = new PackageManager(TEST_PROJECT_PATH, TEST_IDE_VERSION);
192192
await packageManager.initialize();
193193
const result = await packageManager.fetch();
194194

@@ -217,7 +217,7 @@ describe('PackageManager', () => {
217217
} as unknown as GitHubDependency;
218218
});
219219

220-
packageManager = new PackageManager(TEST_PROJECT_PATH);
220+
packageManager = new PackageManager(TEST_PROJECT_PATH, TEST_IDE_VERSION);
221221
await packageManager.initialize();
222222
const result = await packageManager.fetch({ filter: ['dep1'] });
223223

@@ -253,7 +253,7 @@ describe('PackageManager', () => {
253253
} as unknown as GitHubDependency;
254254
});
255255

256-
packageManager = new PackageManager(TEST_PROJECT_PATH);
256+
packageManager = new PackageManager(TEST_PROJECT_PATH, TEST_IDE_VERSION);
257257
await packageManager.initialize();
258258
await packageManager.fetch();
259259

packageManager/src/PackageManager.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
FetchResult
1313
} from './types';
1414
import { GithubFetcher } from './dependency/GithubFetcher';
15+
import { Version } from './version/Version';
1516

1617
/**
1718
* Main package manager orchestrator
@@ -25,9 +26,9 @@ export class PackageManager {
2526
private dependencies: DependenciesFile | null = null;
2627
private lock: LockFile | null = null;
2728
private reconciled: Map<string, GitHubDependency> = new Map();
28-
private ideVersion: string | null = null;
29+
private ideVersion: Version;
2930

30-
constructor(projectPath: string, cacheFolder?: string, authToken?: string, ideVersion?: string) {
31+
constructor(projectPath: string, ideVersion: string, authToken?: string, cacheFolder?: string) {
3132
// Validate inputs
3233
if (!projectPath || typeof projectPath !== 'string') {
3334
throw new Error('Project path is required and must be a string');
@@ -42,7 +43,7 @@ export class PackageManager {
4243
this.configReader = new ConfigReader(projectPath);
4344
this.cacheManager = new CacheManager(cacheFolder);
4445
this.fetcher = new GithubFetcher(authToken);
45-
this.ideVersion = ideVersion || null;
46+
this.ideVersion = new Version(ideVersion);
4647
}
4748

4849
/**
@@ -199,7 +200,7 @@ export class PackageManager {
199200

200201
const lockEntry = this.lock!.dependencies[name];
201202
const fetched = await dep.fetch(
202-
this.ideVersion || '',
203+
this.ideVersion,
203204
this.environment!,
204205
lockEntry,
205206
this.fetcher!,
@@ -333,7 +334,7 @@ export class PackageManager {
333334
if (lockEntry) {
334335
await dep.checkOutdated(
335336
this.fetcher,
336-
this.ideVersion || '',
337+
this.ideVersion,
337338
lockEntry,
338339
this.cacheManager
339340
);

packageManager/src/dependency/Dependency.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Fetcher } from "./Fetcher";
33
import { Range } from "../version/Range";
44
import { assert } from "console";
55
import { Package } from "./Package";
6+
import { Version } from "../version/Version";
67

78
export class Dependency {
89

@@ -129,7 +130,7 @@ export class Dependency {
129130
*/
130131
protected async resolveVersion(
131132
fetcher: Fetcher,
132-
ideVersion: string
133+
ideVersion: Version
133134
): Promise<string | null> {
134135

135136
const owner = this._owner;
@@ -146,7 +147,7 @@ export class Dependency {
146147

147148
// If "4d" keyword, match IDE version
148149
if (this._version.toLowerCase() === '4d') {
149-
const range = new Range(`^${ideVersion}`);
150+
const range = new Range(this._version, ideVersion);
150151
return await this.resolveRange(fetcher, owner, repo, range);
151152

152153
}

packageManager/src/dependency/GithubDependency.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as os from 'os';
1010
import * as path from 'path';
1111
import * as fs from 'fs/promises';
1212
import { Dependency } from './Dependency';
13+
import { Version } from '../version/Version';
1314

1415
/**
1516
* GitHub-based dependency implementation
@@ -34,7 +35,7 @@ export class GitHubDependency extends Dependency {
3435
* Fetch this dependency
3536
*/
3637
async fetch(
37-
ideVersion: string,
38+
ideVersion: Version,
3839
env: Environment,
3940
lock: LockEntry,
4041
fetcher: Fetcher,
@@ -132,7 +133,7 @@ export class GitHubDependency extends Dependency {
132133
*/
133134
async checkOutdated(
134135
fetcher: Fetcher,
135-
ideVersion: string,
136+
ideVersion: Version,
136137
lock: LockEntry,
137138
cacheManager: CacheManager
138139
): Promise<void> {
@@ -146,9 +147,7 @@ export class GitHubDependency extends Dependency {
146147

147148
// Get current range
148149
const rangeSpec = this.version || 'latest';
149-
const range = rangeSpec.toLowerCase() === '4d'
150-
? new Range(`^${ideVersion}`)
151-
: new Range(rangeSpec);
150+
const range = new Range(rangeSpec, ideVersion)
152151

153152
// Get available versions
154153
const releases = await fetcher.getReleases(owner, repo);

packageManager/src/version/Range.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ export class Range {
99
private range: semver.Range;
1010
private rangeSpec: string;
1111

12-
constructor(rangeSpec: string, ideVersion?: string) {
12+
constructor(rangeSpec: string, ideVersion?: Version) {
1313
this.rangeSpec = rangeSpec;
1414

1515
// Handle special keywords
1616
if (rangeSpec === 'latest' || rangeSpec === '*' || !rangeSpec) {
1717
this.range = new semver.Range('*');
1818
} else if (rangeSpec.toLowerCase() === '4d') {
1919
// Match IDE version
20-
const version = ideVersion || this.getIDEVersion();
20+
const version = ideVersion?.toIDEString();
2121
this.range = new semver.Range(`^${version}`);
2222
} else {
2323
// Parse as semver range
@@ -119,23 +119,12 @@ export class Range {
119119
return this.rangeSpec;
120120
}
121121

122-
/**
123-
* Get IDE version from environment or use default
124-
*/
125-
private getIDEVersion(): string {
126-
// Check environment variable
127-
if (process.env.IDE_VERSION) {
128-
return process.env.IDE_VERSION;
129-
}
130122

131-
// Default to 21.2.0 (2-digit year format)
132-
return '21.2.0';
133-
}
134123

135124
/**
136125
* Static factory method
137126
*/
138-
static parse(rangeSpec: string, ideVersion?: string): Range | null {
127+
static parse(rangeSpec: string, ideVersion?: Version): Range | null {
139128
try {
140129
return new Range(rangeSpec, ideVersion);
141130
} catch {
@@ -153,23 +142,23 @@ export class Range {
153142
/**
154143
* Check if a version satisfies a range (static helper)
155144
*/
156-
static satisfies(version: string, rangeSpec: string, ideVersion?: string): boolean {
145+
static satisfies(version: string, rangeSpec: string, ideVersion?: Version): boolean {
157146
const range = new Range(rangeSpec, ideVersion);
158147
return range.satisfiedBy(version);
159148
}
160149

161150
/**
162151
* Find max satisfying version (static helper)
163152
*/
164-
static maxSatisfying(versions: string[], rangeSpec: string, ideVersion?: string): string | null {
153+
static maxSatisfying(versions: string[], rangeSpec: string, ideVersion?: Version): string | null {
165154
const range = new Range(rangeSpec, ideVersion);
166155
return range.maxSatisfying(versions);
167156
}
168157

169158
/**
170159
* Find min satisfying version (static helper)
171160
*/
172-
static minSatisfying(versions: string[], rangeSpec: string, ideVersion?: string): string | null {
161+
static minSatisfying(versions: string[], rangeSpec: string, ideVersion?: Version): string | null {
173162
const range = new Range(rangeSpec, ideVersion);
174163
return range.minSatisfying(versions);
175164
}

packageManager/src/version/Version.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { VersionInfo } from '../types';
88
export class Version {
99
private semver: semver.SemVer;
1010
public isR: boolean = false;
11+
public isMain: boolean = false;
1112
public raw: string;
1213

1314
constructor(versionString: string) {
@@ -36,6 +37,7 @@ export class Version {
3637
throw new Error(`Invalid version: ${versionString}`);
3738
}
3839
this.semver = parsed;
40+
this.isMain = this.semver.major == 0;
3941
}
4042

4143
/**
@@ -127,7 +129,7 @@ export class Version {
127129
* Convert to 4D IDE version string (e.g., "21.2")
128130
*/
129131
toIDEString(): string {
130-
return `${this.major}.${this.minor}`;
132+
return `${this.major}.${this.minor}.${this.patch}`;
131133
}
132134

133135
/**

packageManager/test/github-dependency.test.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { DependencySpec, LockEntry, Environment } from '../src/types';
44
import type { Fetcher } from '../src/dependency/Fetcher';
55
import type { CacheManager } from '../src/cache/CacheManager';
66
import * as fs from 'fs/promises';
7+
import { Version } from '../src/version/Version';
78

89
describe('GitHubDependency', () => {
910

@@ -157,7 +158,7 @@ describe('GitHubDependency', () => {
157158
const spec: DependencySpec = { github: 'invalid', version: '^1.0.0' };
158159
const dep = new GitHubDependency(spec, true);
159160

160-
const result = await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager);
161+
const result = await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager);
161162

162163
expect(result).toBe(false);
163164
expect(mockFetcher.downloadReleaseAsset).not.toHaveBeenCalled();
@@ -172,7 +173,7 @@ describe('GitHubDependency', () => {
172173
{ id: 1, tag_name: 'v1.0.0', name: 'v1.0.0', draft: false, prerelease: false },
173174
]);
174175

175-
const result = await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager);
176+
const result = await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager);
176177

177178
expect(result).toBe(false);
178179
expect(lock.update?.errors).toBeDefined();
@@ -188,7 +189,7 @@ describe('GitHubDependency', () => {
188189
getListDependencies: vi.fn().mockResolvedValue({ dependencies: {} }),
189190
});
190191

191-
const result = await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager, false);
192+
const result = await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager, false);
192193

193194
expect(result).toBe(false); // Skipped, already cached
194195
expect(lock.found).toBe(true);
@@ -203,7 +204,7 @@ describe('GitHubDependency', () => {
203204
// Mock getPackage to return null (not cached)
204205
vi.spyOn(dep, 'getPackage' as any).mockResolvedValue(null);
205206

206-
const result = await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager);
207+
const result = await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager);
207208

208209
expect(result).toBe(true);
209210
expect(mockFetcher.downloadReleaseAsset).toHaveBeenCalledWith('owner', 'repo', 'v1.1.0');
@@ -223,7 +224,7 @@ describe('GitHubDependency', () => {
223224
getListDependencies: vi.fn().mockResolvedValue({ dependencies: {} }),
224225
});
225226

226-
const result = await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager, true);
227+
const result = await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager, true);
227228

228229
expect(result).toBe(true);
229230
expect(mockFetcher.downloadReleaseAsset).toHaveBeenCalled();
@@ -236,7 +237,7 @@ describe('GitHubDependency', () => {
236237

237238
vi.spyOn(dep, 'getPackage' as any).mockResolvedValue(null);
238239

239-
await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager);
240+
await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager);
240241

241242
expect(lock.htmlURL).toBe('https://github.com/owner/repo/releases/tag/v1.1.0');
242243
expect(lock.archiveURL).toBe('https://github.com/owner/repo/releases/download/v1.1.0/repo.zip');
@@ -250,7 +251,7 @@ describe('GitHubDependency', () => {
250251

251252
mockEnv.github.htmlURL = 'https://github.mycompany.com';
252253

253-
await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager);
254+
await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager);
254255

255256
expect(lock.htmlURL).toBe('https://github.mycompany.com/owner/repo/releases/tag/v1.1.0');
256257
});
@@ -262,7 +263,7 @@ describe('GitHubDependency', () => {
262263
vi.spyOn(dep, 'getPackage' as any).mockResolvedValue(null);
263264
mockFetcher.downloadReleaseAsset = vi.fn().mockRejectedValue(new Error('Network error'));
264265

265-
const result = await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager);
266+
const result = await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager);
266267

267268
expect(result).toBe(false);
268269
expect(lock.found).toBe(false);
@@ -276,7 +277,7 @@ describe('GitHubDependency', () => {
276277
vi.spyOn(dep, 'getPackage' as any).mockResolvedValue(null);
277278
mockCacheManager.extractArchive = vi.fn().mockRejectedValue(new Error('Extraction failed'));
278279

279-
const result = await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager);
280+
const result = await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager);
280281

281282
expect(result).toBe(false);
282283
expect(lock.found).toBe(false);
@@ -295,7 +296,7 @@ describe('GitHubDependency', () => {
295296
}),
296297
});
297298

298-
await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager, false);
299+
await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager, false);
299300

300301
expect(lock.dependencies).toEqual({
301302
'subDep': { github: 'other/subdep', version: '^1.0.0' }
@@ -308,7 +309,7 @@ describe('GitHubDependency', () => {
308309

309310
vi.spyOn(dep, 'getPackage' as any).mockResolvedValue(null);
310311

311-
await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager);
312+
await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager);
312313

313314
expect(lock.tag).toBe('v2.0.0-beta');
314315
expect(mockFetcher.getReleases).not.toHaveBeenCalled();
@@ -322,7 +323,7 @@ describe('GitHubDependency', () => {
322323
vi.spyOn(dep, 'getPackage' as any).mockResolvedValue(null);
323324
mockFetcher.getLatestRelease = vi.fn().mockResolvedValue({ tag_name: 'v3.0.0' });
324325

325-
await dep.fetch('20.0.0', mockEnv, lock, mockFetcher, mockCacheManager);
326+
await dep.fetch(new Version('20.0.0'), mockEnv, lock, mockFetcher, mockCacheManager);
326327

327328
expect(lock.tag).toBe('v3.0.0');
328329
expect(mockFetcher.getLatestRelease).toHaveBeenCalledWith('owner', 'repo');

0 commit comments

Comments
 (0)