intershell 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/compose/compose.d.ts.map +1 -1
- package/dist/compose/compose.js +2 -2
- package/dist/compose/compose.js.map +1 -1
- package/dist/compose/compose.test.js +33 -32
- package/dist/compose/compose.test.js.map +1 -1
- package/dist/intershell-config/intershell-config.d.ts.map +1 -1
- package/dist/intershell-config/intershell-config.default.js +1 -1
- package/dist/intershell-config/intershell-config.default.js.map +1 -1
- package/dist/intershell-config/intershell-config.js +5 -21
- package/dist/intershell-config/intershell-config.js.map +1 -1
- package/dist/package/index.d.ts +1 -0
- package/dist/package/index.d.ts.map +1 -1
- package/dist/package/index.js +1 -0
- package/dist/package/index.js.map +1 -1
- package/dist/package/package-test-mock.d.ts +2 -0
- package/dist/package/package-test-mock.d.ts.map +1 -0
- package/dist/package/package-test-mock.js +28 -0
- package/dist/package/package-test-mock.js.map +1 -0
- package/dist/package/package.d.ts +1 -1
- package/dist/package/package.d.ts.map +1 -1
- package/dist/package/package.js +14 -49
- package/dist/package/package.js.map +1 -1
- package/dist/package/package.test.js +93 -56
- package/dist/package/package.test.js.map +1 -1
- package/dist/package/package.types.d.ts +3 -1
- package/dist/package/package.types.d.ts.map +1 -1
- package/dist/package/workspace-discovery.d.ts +18 -0
- package/dist/package/workspace-discovery.d.ts.map +1 -0
- package/dist/package/workspace-discovery.js +166 -0
- package/dist/package/workspace-discovery.js.map +1 -0
- package/dist/package/workspace-discovery.test.d.ts +2 -0
- package/dist/package/workspace-discovery.test.d.ts.map +1 -0
- package/dist/package/workspace-discovery.test.js +27 -0
- package/dist/package/workspace-discovery.test.js.map +1 -0
- package/dist/package-commits/dependency-analyzer.d.ts +1 -3
- package/dist/package-commits/dependency-analyzer.d.ts.map +1 -1
- package/dist/package-commits/dependency-analyzer.js +21 -35
- package/dist/package-commits/dependency-analyzer.js.map +1 -1
- package/dist/package-commits/package-commits.test.js +12 -7
- package/dist/package-commits/package-commits.test.js.map +1 -1
- package/dist/package-tags/package-tags.js +1 -1
- package/dist/package-tags/package-tags.js.map +1 -1
- package/dist/package-tags/package-tags.test.js +24 -20
- package/dist/package-tags/package-tags.test.js.map +1 -1
- package/dist/package-version/package-version.test.js +43 -34
- package/dist/package-version/package-version.test.js.map +1 -1
- package/package.json +2 -2
- package/src/compose/compose.test.ts +36 -35
- package/src/compose/compose.ts +2 -4
- package/src/intershell-config/intershell-config.default.ts +1 -1
- package/src/intershell-config/intershell-config.ts +8 -23
- package/src/package/index.ts +1 -0
- package/src/package/package-test-mock.ts +33 -0
- package/src/package/package.test.ts +93 -56
- package/src/package/package.ts +27 -55
- package/src/package/package.types.ts +1 -1
- package/src/package/workspace-discovery.test.ts +51 -0
- package/src/package/workspace-discovery.ts +219 -0
- package/src/package-commits/dependency-analyzer.ts +61 -40
- package/src/package-commits/package-commits.test.ts +15 -7
- package/src/package-tags/package-tags.test.ts +27 -20
- package/src/package-tags/package-tags.ts +1 -1
- package/src/package-version/package-version.test.ts +48 -35
|
@@ -4,7 +4,7 @@ type CustomBunType = {
|
|
|
4
4
|
YAML: {
|
|
5
5
|
parse: (input: string) => Record<string, unknown>;
|
|
6
6
|
};
|
|
7
|
-
file: (path: string) => { text: () => string };
|
|
7
|
+
file: (path: string) => { text: () => Promise<string>; exists: () => Promise<boolean> };
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
describe("EntityCompose", () => {
|
|
@@ -55,13 +55,6 @@ describe("EntityCompose", () => {
|
|
|
55
55
|
originalBunFile = (Bun as unknown as CustomBunType).file;
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
// Restore mocks after each test
|
|
59
|
-
afterEach(() => {
|
|
60
|
-
// Restore original Bun methods
|
|
61
|
-
(Bun as unknown as CustomBunType).YAML = originalBunYaml;
|
|
62
|
-
(Bun as unknown as CustomBunType).file = originalBunFile;
|
|
63
|
-
});
|
|
64
|
-
|
|
65
58
|
// Store original methods to restore after tests
|
|
66
59
|
let originalEntityPackageGetAllPackages: () => Promise<string[]>;
|
|
67
60
|
let originalEntityAffectedGetAffectedPackages: (
|
|
@@ -104,13 +97,22 @@ describe("EntityCompose", () => {
|
|
|
104
97
|
}
|
|
105
98
|
};
|
|
106
99
|
|
|
100
|
+
afterEach(async () => {
|
|
101
|
+
(Bun as unknown as CustomBunType).YAML = originalBunYaml;
|
|
102
|
+
(Bun as unknown as CustomBunType).file = originalBunFile;
|
|
103
|
+
await cleanupMocks();
|
|
104
|
+
});
|
|
105
|
+
|
|
107
106
|
// Common mock setup for Bun
|
|
108
107
|
const setupBunMocks = (
|
|
109
108
|
mockYamlParse: ReturnType<typeof mock>,
|
|
110
109
|
mockFileText: ReturnType<typeof mock>,
|
|
111
110
|
) => {
|
|
112
111
|
(Bun as unknown as CustomBunType).YAML = { parse: mockYamlParse };
|
|
113
|
-
(Bun as unknown as CustomBunType).file = mock(() => ({
|
|
112
|
+
(Bun as unknown as CustomBunType).file = mock(() => ({
|
|
113
|
+
text: mockFileText,
|
|
114
|
+
exists: () => Promise.resolve(true),
|
|
115
|
+
}));
|
|
114
116
|
};
|
|
115
117
|
|
|
116
118
|
it("should handle core functionality - parsing, validation, and basic operations", async () => {
|
|
@@ -329,8 +331,8 @@ describe("EntityCompose", () => {
|
|
|
329
331
|
return defaultYaml;
|
|
330
332
|
});
|
|
331
333
|
|
|
332
|
-
|
|
333
|
-
(
|
|
334
|
+
const mockFileText = mock(() => Promise.resolve(""));
|
|
335
|
+
setupBunMocks(mockYamlParse, mockFileText);
|
|
334
336
|
|
|
335
337
|
const yamlInput = `
|
|
336
338
|
version: "3.9"
|
|
@@ -378,6 +380,28 @@ volumes:
|
|
|
378
380
|
it("should handle depends_on in object format (long syntax)", async () => {
|
|
379
381
|
await setupMocks();
|
|
380
382
|
|
|
383
|
+
const yamlInput = `
|
|
384
|
+
version: "3.8"
|
|
385
|
+
services:
|
|
386
|
+
app:
|
|
387
|
+
image: node:18
|
|
388
|
+
ports:
|
|
389
|
+
- "3000:3000"
|
|
390
|
+
depends_on:
|
|
391
|
+
postgres:
|
|
392
|
+
condition: service_healthy
|
|
393
|
+
redis:
|
|
394
|
+
condition: service_started
|
|
395
|
+
postgres:
|
|
396
|
+
image: postgres:13
|
|
397
|
+
ports:
|
|
398
|
+
- "5432:5432"
|
|
399
|
+
redis:
|
|
400
|
+
image: redis:alpine
|
|
401
|
+
ports:
|
|
402
|
+
- "6379:6379"
|
|
403
|
+
`;
|
|
404
|
+
|
|
381
405
|
const mockYamlParse = mock((input: string) => {
|
|
382
406
|
if (input.includes("service_healthy")) {
|
|
383
407
|
return {
|
|
@@ -411,35 +435,12 @@ volumes:
|
|
|
411
435
|
return defaultYaml;
|
|
412
436
|
});
|
|
413
437
|
|
|
414
|
-
const mockFileText = mock(() => Promise.resolve(
|
|
438
|
+
const mockFileText = mock(() => Promise.resolve(yamlInput));
|
|
415
439
|
setupBunMocks(mockYamlParse, mockFileText);
|
|
416
440
|
|
|
417
441
|
const { EntityCompose } = await import("./compose");
|
|
418
442
|
const entityCompose = new EntityCompose("docker-compose.yml");
|
|
419
443
|
|
|
420
|
-
// Test parsing with object format depends_on
|
|
421
|
-
const yamlInput = `
|
|
422
|
-
version: "3.8"
|
|
423
|
-
services:
|
|
424
|
-
app:
|
|
425
|
-
image: node:18
|
|
426
|
-
ports:
|
|
427
|
-
- "3000:3000"
|
|
428
|
-
depends_on:
|
|
429
|
-
postgres:
|
|
430
|
-
condition: service_healthy
|
|
431
|
-
redis:
|
|
432
|
-
condition: service_started
|
|
433
|
-
postgres:
|
|
434
|
-
image: postgres:13
|
|
435
|
-
ports:
|
|
436
|
-
- "5432:5432"
|
|
437
|
-
redis:
|
|
438
|
-
image: redis:alpine
|
|
439
|
-
ports:
|
|
440
|
-
- "6379:6379"
|
|
441
|
-
`;
|
|
442
|
-
|
|
443
444
|
const composeData = EntityCompose.parseDockerCompose(yamlInput);
|
|
444
445
|
expect(composeData.services.app.depends_on).toEqual(["postgres", "redis"]);
|
|
445
446
|
|
package/src/compose/compose.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EntityAffected } from "../affected";
|
|
2
|
-
import { EntityPackage } from "../package";
|
|
2
|
+
import { EntityPackage, stripWorkspaceScope } from "../package";
|
|
3
3
|
import type {
|
|
4
4
|
ComposeData,
|
|
5
5
|
ComposeValidationResult,
|
|
@@ -158,9 +158,7 @@ export class EntityCompose {
|
|
|
158
158
|
|
|
159
159
|
// Find services associated with affected packages
|
|
160
160
|
for (const service of services) {
|
|
161
|
-
const associatedPackage = allPackages.find(
|
|
162
|
-
(p) => p.replace(/^@repo\//, "") === service.name,
|
|
163
|
-
);
|
|
161
|
+
const associatedPackage = allPackages.find((p) => stripWorkspaceScope(p) === service.name);
|
|
164
162
|
|
|
165
163
|
if (keys.some((k: string) => k === associatedPackage)) {
|
|
166
164
|
affectedServices.add(service.name);
|
|
@@ -151,7 +151,7 @@ export const defaultConfig = {
|
|
|
151
151
|
},
|
|
152
152
|
staged: [
|
|
153
153
|
{
|
|
154
|
-
filePattern: [/coverage\/.*/, /dist\/.*/, /node_modules\/.*/,
|
|
154
|
+
filePattern: [/coverage\/.*/, /dist\/.*/, /node_modules\/.*/, /(^|\/)\.env$/, /\.act/],
|
|
155
155
|
description: "development files should not be manually committed",
|
|
156
156
|
},
|
|
157
157
|
{
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
discoverWorkspacePackageNamesSync,
|
|
5
|
+
findWorkspaceRootSync,
|
|
6
|
+
} from "../package/workspace-discovery";
|
|
3
7
|
import { defaultConfig } from "./intershell-config.default";
|
|
4
8
|
import type { CustomConfigJson, IConfig } from "./intershell-config.types";
|
|
5
9
|
|
|
@@ -96,29 +100,10 @@ class Config {
|
|
|
96
100
|
const packages: string[] = ["root"];
|
|
97
101
|
|
|
98
102
|
try {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// Check for apps directory
|
|
103
|
-
const appsPath = join(workspaceRoot, "apps");
|
|
104
|
-
if (existsSync(appsPath)) {
|
|
105
|
-
const apps = readdirSync(appsPath, { withFileTypes: true })
|
|
106
|
-
.filter((dirent) => dirent.isDirectory())
|
|
107
|
-
.map((dirent) => dirent.name);
|
|
108
|
-
packages.push(...apps);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Check for packages directory
|
|
112
|
-
const packagesPath = join(workspaceRoot, "packages");
|
|
113
|
-
if (existsSync(packagesPath)) {
|
|
114
|
-
const pkgs = readdirSync(packagesPath, { withFileTypes: true })
|
|
115
|
-
.filter((dirent) => dirent.isDirectory())
|
|
116
|
-
.map((dirent) => `@repo/${dirent.name}`);
|
|
117
|
-
packages.push(...pkgs);
|
|
118
|
-
}
|
|
103
|
+
const workspaceRoot = findWorkspaceRootSync();
|
|
104
|
+
packages.push(...discoverWorkspacePackageNamesSync(workspaceRoot));
|
|
119
105
|
} catch {
|
|
120
|
-
// If we can't read
|
|
121
|
-
packages.push("admin", "api", "storefront", "ui", "utils");
|
|
106
|
+
// If we can't read workspaces, fall back to root only
|
|
122
107
|
}
|
|
123
108
|
|
|
124
109
|
return packages;
|
package/src/package/index.ts
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { mock } from "bun:test";
|
|
2
|
+
import { packagesShell } from "./package.shell";
|
|
3
|
+
import type { PackageJson } from "./package.types";
|
|
4
|
+
|
|
5
|
+
const defaultRootPackageJson = (): PackageJson => ({
|
|
6
|
+
name: "root",
|
|
7
|
+
version: "1.0.0",
|
|
8
|
+
private: false,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const defaultAppsApiPackageJson = (): PackageJson => ({
|
|
12
|
+
name: "@apps/api",
|
|
13
|
+
version: "0.1.0",
|
|
14
|
+
private: false,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export function installPackagesShellTestMock(): () => void {
|
|
18
|
+
const originalReadJsonFile = packagesShell.readJsonFile;
|
|
19
|
+
|
|
20
|
+
packagesShell.readJsonFile = mock((filePath: string) => {
|
|
21
|
+
if (filePath === "./package.json") {
|
|
22
|
+
return defaultRootPackageJson();
|
|
23
|
+
}
|
|
24
|
+
if (filePath === "apps/api/package.json") {
|
|
25
|
+
return defaultAppsApiPackageJson();
|
|
26
|
+
}
|
|
27
|
+
throw new Error(`Unexpected package.json path: ${filePath}`);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return () => {
|
|
31
|
+
packagesShell.readJsonFile = originalReadJsonFile;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -226,13 +226,23 @@ describe("EntityPackage", () => {
|
|
|
226
226
|
expect(rootPackages.getPath()).toBe(".");
|
|
227
227
|
});
|
|
228
228
|
|
|
229
|
-
it("should return packages path for @
|
|
230
|
-
const
|
|
231
|
-
expect(
|
|
229
|
+
it("should return packages path for @packages packages", () => {
|
|
230
|
+
const packagesPackage = new EntityPackage("@packages/test-package");
|
|
231
|
+
expect(packagesPackage.getPath()).toBe("packages/test-package");
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
-
it("should return apps path for
|
|
235
|
-
|
|
234
|
+
it("should return apps path for @apps packages", () => {
|
|
235
|
+
const appsPackage = new EntityPackage("@apps/test-app");
|
|
236
|
+
expect(appsPackage.getPath()).toBe("apps/test-app");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("should return tools path for @tools packages", () => {
|
|
240
|
+
const toolsPackage = new EntityPackage("@tools/test-preset");
|
|
241
|
+
expect(toolsPackage.getPath()).toBe("tools/test-preset");
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should return package name as path for unscoped packages", () => {
|
|
245
|
+
expect(packages.getPath()).toBe("test-package");
|
|
236
246
|
});
|
|
237
247
|
});
|
|
238
248
|
|
|
@@ -242,7 +252,7 @@ describe("EntityPackage", () => {
|
|
|
242
252
|
});
|
|
243
253
|
|
|
244
254
|
it("should return correct package.json path", () => {
|
|
245
|
-
expect(packages.getJsonPath()).toBe("
|
|
255
|
+
expect(packages.getJsonPath()).toBe("test-package/package.json");
|
|
246
256
|
});
|
|
247
257
|
|
|
248
258
|
it("should return correct path for root package", () => {
|
|
@@ -250,9 +260,9 @@ describe("EntityPackage", () => {
|
|
|
250
260
|
expect(rootPackages.getJsonPath()).toBe("./package.json");
|
|
251
261
|
});
|
|
252
262
|
|
|
253
|
-
it("should return correct path for @
|
|
254
|
-
const
|
|
255
|
-
expect(
|
|
263
|
+
it("should return correct path for @packages package", () => {
|
|
264
|
+
const packagesPackage = new EntityPackage("@packages/test-package");
|
|
265
|
+
expect(packagesPackage.getJsonPath()).toBe("packages/test-package/package.json");
|
|
256
266
|
});
|
|
257
267
|
});
|
|
258
268
|
|
|
@@ -358,7 +368,7 @@ describe("EntityPackage", () => {
|
|
|
358
368
|
});
|
|
359
369
|
|
|
360
370
|
it("should return correct changelog path", () => {
|
|
361
|
-
expect(packages.getChangelogPath()).toBe("
|
|
371
|
+
expect(packages.getChangelogPath()).toBe("test-package/CHANGELOG.md");
|
|
362
372
|
});
|
|
363
373
|
});
|
|
364
374
|
|
|
@@ -540,14 +550,13 @@ describe("EntityPackage", () => {
|
|
|
540
550
|
expect(rootPackages.getTagSeriesName()).toBe("v");
|
|
541
551
|
});
|
|
542
552
|
|
|
543
|
-
it.skip("should return 'package-name-v' for @
|
|
544
|
-
// Mock packagesShell to return @repo package for this test
|
|
553
|
+
it.skip("should return 'packages/package-name-v' for @packages packages", () => {
|
|
545
554
|
mockPackagesShell.readJsonFile.mockImplementationOnce(() =>
|
|
546
|
-
mockPackageJson({ name: "@
|
|
555
|
+
mockPackageJson({ name: "@packages/package-name", private: false }),
|
|
547
556
|
);
|
|
548
557
|
|
|
549
|
-
const intershellPackages = new EntityPackage("@
|
|
550
|
-
expect(intershellPackages.getTagSeriesName()).toBe("package-name-v");
|
|
558
|
+
const intershellPackages = new EntityPackage("@packages/package-name");
|
|
559
|
+
expect(intershellPackages.getTagSeriesName()).toBe("packages/package-name-v");
|
|
551
560
|
});
|
|
552
561
|
|
|
553
562
|
it.skip("should return 'package-name-v' for regular packages", () => {
|
|
@@ -675,51 +684,79 @@ describe("EntityPackage", () => {
|
|
|
675
684
|
|
|
676
685
|
describe("getAllPackages", () => {
|
|
677
686
|
it("should return list of packages including root", async () => {
|
|
678
|
-
// Mock packagesShell methods for this test
|
|
679
687
|
mockPackagesShell.getWorkspaceRoot.mockResolvedValueOnce("/workspace");
|
|
680
|
-
mockPackagesShell.
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
.
|
|
691
|
-
.
|
|
692
|
-
.
|
|
688
|
+
mockPackagesShell.readJsonFile.mockImplementation((filePath: string) => {
|
|
689
|
+
if (filePath === "/workspace/package.json") {
|
|
690
|
+
return {
|
|
691
|
+
name: "root",
|
|
692
|
+
workspaces: ["apps/*", "packages/*", "tools/*"],
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
return mockPackageJson();
|
|
696
|
+
});
|
|
697
|
+
mockPackagesShell.readDirectory.mockImplementation((dirPath: string) => {
|
|
698
|
+
if (dirPath.endsWith("/apps")) return Promise.resolve(["test-app", "another-app"]);
|
|
699
|
+
if (dirPath.endsWith("/packages")) return Promise.resolve(["ui", "utils"]);
|
|
700
|
+
if (dirPath.endsWith("/tools")) return Promise.resolve(["typescript-config"]);
|
|
701
|
+
return Promise.resolve([]);
|
|
702
|
+
});
|
|
703
|
+
mockPackagesShell.canAccessFile.mockResolvedValue(true);
|
|
704
|
+
mockPackagesShell.readFileAsText.mockImplementation((filePath: string) => {
|
|
705
|
+
const packageNames: Record<string, string> = {
|
|
706
|
+
"/workspace/apps/test-app/package.json":
|
|
707
|
+
'{"name": "@apps/test-app", "version": "1.0.0"}',
|
|
708
|
+
"/workspace/apps/another-app/package.json":
|
|
709
|
+
'{"name": "@apps/another-app", "version": "1.0.0"}',
|
|
710
|
+
"/workspace/packages/ui/package.json": '{"name": "@packages/ui", "version": "1.0.0"}',
|
|
711
|
+
"/workspace/packages/utils/package.json":
|
|
712
|
+
'{"name": "@packages/utils", "version": "1.0.0"}',
|
|
713
|
+
"/workspace/tools/typescript-config/package.json":
|
|
714
|
+
'{"name": "@tools/typescript-config", "private": true}',
|
|
715
|
+
};
|
|
716
|
+
return Promise.resolve(packageNames[filePath] ?? "");
|
|
717
|
+
});
|
|
693
718
|
|
|
694
719
|
const result = await EntityPackage.getAllPackages();
|
|
695
720
|
|
|
696
721
|
expect(result).toContain("root");
|
|
697
|
-
expect(result).toContain("test-app");
|
|
698
|
-
expect(result).toContain("another-app");
|
|
699
|
-
expect(result).toContain("@
|
|
700
|
-
expect(result).toContain("@
|
|
722
|
+
expect(result).toContain("@apps/test-app");
|
|
723
|
+
expect(result).toContain("@apps/another-app");
|
|
724
|
+
expect(result).toContain("@packages/ui");
|
|
725
|
+
expect(result).toContain("@packages/utils");
|
|
726
|
+
expect(result).toContain("@tools/typescript-config");
|
|
701
727
|
});
|
|
702
728
|
|
|
703
|
-
it("should handle
|
|
704
|
-
// Mock packagesShell methods for this test
|
|
729
|
+
it("should handle missing workspaces field gracefully", async () => {
|
|
705
730
|
mockPackagesShell.getWorkspaceRoot.mockResolvedValueOnce("/workspace");
|
|
706
|
-
mockPackagesShell.
|
|
707
|
-
.
|
|
708
|
-
|
|
731
|
+
mockPackagesShell.readJsonFile.mockImplementation((filePath: string) => {
|
|
732
|
+
if (filePath === "/workspace/package.json") {
|
|
733
|
+
return { name: "root" };
|
|
734
|
+
}
|
|
735
|
+
return mockPackageJson();
|
|
736
|
+
});
|
|
709
737
|
|
|
710
738
|
const result = await EntityPackage.getAllPackages();
|
|
711
739
|
expect(result).toEqual(["root"]);
|
|
712
740
|
});
|
|
713
741
|
|
|
714
742
|
it("should filter out packages without valid package.json", async () => {
|
|
715
|
-
// Mock packagesShell methods for this test
|
|
716
743
|
mockPackagesShell.getWorkspaceRoot.mockResolvedValueOnce("/workspace");
|
|
717
|
-
mockPackagesShell.
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
744
|
+
mockPackagesShell.readJsonFile.mockImplementation((filePath: string) => {
|
|
745
|
+
if (filePath === "/workspace/package.json") {
|
|
746
|
+
return {
|
|
747
|
+
name: "root",
|
|
748
|
+
workspaces: ["apps/*", "packages/*", "tools/*"],
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
return mockPackageJson();
|
|
752
|
+
});
|
|
753
|
+
mockPackagesShell.readDirectory.mockImplementation((dirPath: string) => {
|
|
754
|
+
if (dirPath.endsWith("/apps")) return Promise.resolve(["invalid-app"]);
|
|
755
|
+
if (dirPath.endsWith("/packages")) return Promise.resolve(["invalid-pkg"]);
|
|
756
|
+
if (dirPath.endsWith("/tools")) return Promise.resolve(["invalid-tool"]);
|
|
757
|
+
return Promise.resolve([]);
|
|
758
|
+
});
|
|
759
|
+
mockPackagesShell.canAccessFile.mockResolvedValue(false);
|
|
723
760
|
|
|
724
761
|
const result = await EntityPackage.getAllPackages();
|
|
725
762
|
expect(result).toEqual(["root"]);
|
|
@@ -730,23 +767,23 @@ describe("EntityPackage", () => {
|
|
|
730
767
|
describe("edge cases and error handling", () => {
|
|
731
768
|
it("should handle package names with special characters", () => {
|
|
732
769
|
const specialPackages = new EntityPackage("test-package@1.0.0");
|
|
733
|
-
expect(specialPackages.getPath()).toBe("
|
|
770
|
+
expect(specialPackages.getPath()).toBe("test-package@1.0.0");
|
|
734
771
|
});
|
|
735
772
|
|
|
736
773
|
it("should handle empty package name", () => {
|
|
737
774
|
const emptyPackages = new EntityPackage("");
|
|
738
|
-
expect(emptyPackages.getPath()).toBe("
|
|
775
|
+
expect(emptyPackages.getPath()).toBe("");
|
|
739
776
|
});
|
|
740
777
|
|
|
741
778
|
it("should handle very long package names", () => {
|
|
742
779
|
const longName = "a".repeat(1000);
|
|
743
780
|
const longPackages = new EntityPackage(longName);
|
|
744
|
-
expect(longPackages.getPath()).toBe(
|
|
781
|
+
expect(longPackages.getPath()).toBe(longName);
|
|
745
782
|
});
|
|
746
783
|
|
|
747
784
|
it("should handle package names with spaces", () => {
|
|
748
785
|
const spacedPackages = new EntityPackage("test package");
|
|
749
|
-
expect(spacedPackages.getPath()).toBe("
|
|
786
|
+
expect(spacedPackages.getPath()).toBe("test package");
|
|
750
787
|
});
|
|
751
788
|
});
|
|
752
789
|
|
|
@@ -762,12 +799,12 @@ describe("EntityPackage", () => {
|
|
|
762
799
|
.mockResolvedValueOnce(true); // packages/ui/package.json
|
|
763
800
|
mockPackagesShell.readFileAsText
|
|
764
801
|
.mockResolvedValueOnce('{"name": "test-app", "version": "1.0.0", "private": false}')
|
|
765
|
-
.mockResolvedValueOnce('{"name": "@
|
|
802
|
+
.mockResolvedValueOnce('{"name": "@packages/ui", "version": "1.0.0", "private": false}');
|
|
766
803
|
|
|
767
804
|
const result = await EntityPackage.getVersionedPackages();
|
|
768
805
|
expect(Array.isArray(result)).toBe(true);
|
|
769
806
|
expect(result).toContain("test-app");
|
|
770
|
-
expect(result).toContain("@
|
|
807
|
+
expect(result).toContain("@packages/ui");
|
|
771
808
|
});
|
|
772
809
|
});
|
|
773
810
|
|
|
@@ -779,7 +816,7 @@ describe("EntityPackage", () => {
|
|
|
779
816
|
return { name: "test-app", version: "1.0.0", private: true };
|
|
780
817
|
}
|
|
781
818
|
if (path.includes("ui")) {
|
|
782
|
-
return { name: "@
|
|
819
|
+
return { name: "@packages/ui", version: "1.0.0", private: true };
|
|
783
820
|
}
|
|
784
821
|
// Default fallback
|
|
785
822
|
return mockPackageJson();
|
|
@@ -795,12 +832,12 @@ describe("EntityPackage", () => {
|
|
|
795
832
|
.mockResolvedValueOnce(true); // packages/ui/package.json
|
|
796
833
|
mockPackagesShell.readFileAsText
|
|
797
834
|
.mockResolvedValueOnce('{"name": "test-app", "version": "1.0.0", "private": true}')
|
|
798
|
-
.mockResolvedValueOnce('{"name": "@
|
|
835
|
+
.mockResolvedValueOnce('{"name": "@packages/ui", "version": "1.0.0", "private": true}');
|
|
799
836
|
|
|
800
837
|
const result = await EntityPackage.getUnversionedPackages();
|
|
801
838
|
expect(Array.isArray(result)).toBe(true);
|
|
802
839
|
expect(result).toContain("test-app");
|
|
803
|
-
expect(result).toContain("@
|
|
840
|
+
expect(result).toContain("@packages/ui");
|
|
804
841
|
});
|
|
805
842
|
});
|
|
806
843
|
|
|
@@ -816,7 +853,7 @@ describe("EntityPackage", () => {
|
|
|
816
853
|
.mockResolvedValueOnce(true); // packages/ui/package.json
|
|
817
854
|
mockPackagesShell.readFileAsText
|
|
818
855
|
.mockResolvedValueOnce('{"name": "test-app", "version": "1.0.0", "private": false}')
|
|
819
|
-
.mockResolvedValueOnce('{"name": "@
|
|
856
|
+
.mockResolvedValueOnce('{"name": "@packages/ui", "version": "1.0.0", "private": false}');
|
|
820
857
|
|
|
821
858
|
const result = await EntityPackage.validateAllPackages();
|
|
822
859
|
expect(Array.isArray(result)).toBe(true);
|
|
@@ -824,7 +861,7 @@ describe("EntityPackage", () => {
|
|
|
824
861
|
expect(result).toHaveLength(3);
|
|
825
862
|
expect(result).toContain("root: Consider adding a description to package.json");
|
|
826
863
|
expect(result).toContain("test-app: Consider adding a description to package.json");
|
|
827
|
-
expect(result).toContain("@
|
|
864
|
+
expect(result).toContain("@packages/ui: Consider adding a description to package.json");
|
|
828
865
|
});
|
|
829
866
|
});
|
|
830
867
|
});
|
package/src/package/package.ts
CHANGED
|
@@ -2,6 +2,12 @@ import { entitiesShell } from "../entities.shell";
|
|
|
2
2
|
import { entitiesConfig } from "../intershell-config/intershell-config";
|
|
3
3
|
import { packagesShell } from "./package.shell";
|
|
4
4
|
import type { PackageJson, TsConfig } from "./package.types";
|
|
5
|
+
import {
|
|
6
|
+
discoverWorkspacePackageNamesAsync,
|
|
7
|
+
discoverWorkspacePackagesSync,
|
|
8
|
+
findWorkspaceRootSync,
|
|
9
|
+
getWorkspacePackagePath,
|
|
10
|
+
} from "./workspace-discovery";
|
|
5
11
|
|
|
6
12
|
export class EntityPackage {
|
|
7
13
|
private readonly packageName: string;
|
|
@@ -20,9 +26,16 @@ export class EntityPackage {
|
|
|
20
26
|
|
|
21
27
|
getPath(): string {
|
|
22
28
|
if (this.packageName === "root") return ".";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
const workspacePath = getWorkspacePackagePath(this.packageName);
|
|
30
|
+
if (workspacePath !== null) return workspacePath;
|
|
31
|
+
|
|
32
|
+
const workspaceRoot = findWorkspaceRootSync();
|
|
33
|
+
const entry = discoverWorkspacePackagesSync(workspaceRoot).find(
|
|
34
|
+
(pkg) => pkg.name === this.packageName,
|
|
35
|
+
);
|
|
36
|
+
if (entry) return entry.relativePath;
|
|
37
|
+
|
|
38
|
+
return this.packageName;
|
|
26
39
|
}
|
|
27
40
|
|
|
28
41
|
getJsonPath(): string {
|
|
@@ -138,14 +151,13 @@ export class EntityPackage {
|
|
|
138
151
|
|
|
139
152
|
/**
|
|
140
153
|
* Gets the tag series name for this package
|
|
141
|
-
* @returns tag series prefix (e.g., 'v', 'intershell-v') or null if package shouldn't be versioned
|
|
154
|
+
* @returns tag series prefix (e.g., 'v', 'packages/intershell-v') or null if package shouldn't be versioned
|
|
142
155
|
*/
|
|
143
156
|
getTagSeriesName(): string | null {
|
|
144
157
|
if (!this.shouldVersion()) return null;
|
|
145
158
|
|
|
146
|
-
// Generate tag series name based on package name
|
|
147
159
|
if (this.packageName === "root") return "v";
|
|
148
|
-
return `${this.packageName.
|
|
160
|
+
return `${this.packageName.replaceAll("@", "")}-v`;
|
|
149
161
|
}
|
|
150
162
|
|
|
151
163
|
static getRepoUrl(): string {
|
|
@@ -156,58 +168,18 @@ export class EntityPackage {
|
|
|
156
168
|
}
|
|
157
169
|
static async getAllPackages(): Promise<string[]> {
|
|
158
170
|
const packages: string[] = ["root"];
|
|
159
|
-
|
|
160
|
-
// Get workspace root
|
|
161
171
|
const workspaceRoot = await packagesShell.getWorkspaceRoot();
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Read packages directory
|
|
173
|
-
let pkgs: string[] = [];
|
|
174
|
-
try {
|
|
175
|
-
const packagesPath = `${workspaceRoot}/packages`;
|
|
176
|
-
const packageNames = await packagesShell.readDirectory(packagesPath);
|
|
177
|
-
pkgs = packageNames.map((name) => `@repo/${name}`);
|
|
178
|
-
} catch {
|
|
179
|
-
// packages directory doesn't exist or can't be read
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Filter packages that have valid package.json files
|
|
183
|
-
const filteredPackages = await Promise.all(
|
|
184
|
-
[...apps, ...pkgs].map(async (pkg) => {
|
|
185
|
-
const packageInstance = new EntityPackage(pkg);
|
|
186
|
-
const packageJsonPath = packageInstance.getJsonPath();
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
const exists = await packagesShell.canAccessFile(packageJsonPath);
|
|
190
|
-
if (!exists) return null;
|
|
191
|
-
|
|
192
|
-
const packageJsonContent = await packagesShell.readFileAsText(packageJsonPath);
|
|
193
|
-
const packageJson = JSON.parse(packageJsonContent);
|
|
194
|
-
const name = packageJson.name;
|
|
195
|
-
|
|
196
|
-
if (!name) return null;
|
|
197
|
-
|
|
198
|
-
if (name !== pkg) {
|
|
199
|
-
throw new Error(`Package ${pkg} has a different name in package.json: ${name}`);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return name;
|
|
203
|
-
} catch {
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
}),
|
|
172
|
+
const rootPackageJson = packagesShell.readJsonFile(`${workspaceRoot}/package.json`);
|
|
173
|
+
|
|
174
|
+
const workspacePackages = await discoverWorkspacePackageNamesAsync(
|
|
175
|
+
workspaceRoot,
|
|
176
|
+
packagesShell.readDirectory,
|
|
177
|
+
packagesShell.canAccessFile,
|
|
178
|
+
packagesShell.readFileAsText,
|
|
179
|
+
rootPackageJson,
|
|
207
180
|
);
|
|
208
181
|
|
|
209
|
-
packages.push(...
|
|
210
|
-
|
|
182
|
+
packages.push(...workspacePackages);
|
|
211
183
|
return packages;
|
|
212
184
|
}
|
|
213
185
|
|