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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
packageNameFromAbsolutePath,
|
|
4
|
+
parseWorkspacePatterns,
|
|
5
|
+
type WorkspacePackageEntry,
|
|
6
|
+
} from "./workspace-discovery";
|
|
7
|
+
|
|
8
|
+
describe("workspace-discovery", () => {
|
|
9
|
+
test("parseWorkspacePatterns handles array format", () => {
|
|
10
|
+
expect(parseWorkspacePatterns(["apps/*", "packages/*"])).toEqual(["apps/*", "packages/*"]);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("parseWorkspacePatterns handles object format", () => {
|
|
14
|
+
expect(parseWorkspacePatterns({ packages: ["tools/*"] })).toEqual(["tools/*"]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("parseWorkspacePatterns returns empty for missing workspaces", () => {
|
|
18
|
+
expect(parseWorkspacePatterns(undefined)).toEqual([]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("packageNameFromAbsolutePath resolves workspace package from path", () => {
|
|
22
|
+
const workspacePackages: WorkspacePackageEntry[] = [
|
|
23
|
+
{ relativePath: "packages/ui", name: "@packages/ui" },
|
|
24
|
+
{ relativePath: "apps/api", name: "@apps/api" },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
expect(
|
|
28
|
+
packageNameFromAbsolutePath(
|
|
29
|
+
"/workspace/packages/ui/src/index.ts",
|
|
30
|
+
"/workspace",
|
|
31
|
+
workspacePackages,
|
|
32
|
+
),
|
|
33
|
+
).toBe("@packages/ui");
|
|
34
|
+
|
|
35
|
+
expect(
|
|
36
|
+
packageNameFromAbsolutePath(
|
|
37
|
+
"/workspace/apps/api/Dockerfile",
|
|
38
|
+
"/workspace",
|
|
39
|
+
workspacePackages,
|
|
40
|
+
),
|
|
41
|
+
).toBe("@apps/api");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("packageNameFromAbsolutePath returns null for paths outside workspace", () => {
|
|
45
|
+
expect(
|
|
46
|
+
packageNameFromAbsolutePath("/other/path", "/workspace", [
|
|
47
|
+
{ relativePath: "packages/ui", name: "@packages/ui" },
|
|
48
|
+
]),
|
|
49
|
+
).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import type { PackageJson } from "./package.types";
|
|
4
|
+
|
|
5
|
+
export interface WorkspacePackageEntry {
|
|
6
|
+
readonly relativePath: string;
|
|
7
|
+
readonly name: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Scoped workspace package: `@scope/name` → filesystem path `scope/name` */
|
|
11
|
+
export function getWorkspacePackagePath(packageName: string): string | null {
|
|
12
|
+
if (!packageName.startsWith("@") || !packageName.includes("/")) return null;
|
|
13
|
+
return packageName.slice(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Last path segment of a scoped package (e.g. `@packages/ui` → `ui`) for service name matching */
|
|
17
|
+
export function stripWorkspaceScope(packageName: string): string {
|
|
18
|
+
const workspacePath = getWorkspacePackagePath(packageName);
|
|
19
|
+
if (workspacePath === null) return packageName;
|
|
20
|
+
const slashIndex = workspacePath.lastIndexOf("/");
|
|
21
|
+
return slashIndex === -1 ? workspacePath : workspacePath.slice(slashIndex + 1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function parseWorkspacePatterns(workspaces: PackageJson["workspaces"]): string[] {
|
|
25
|
+
if (!workspaces) return [];
|
|
26
|
+
if (Array.isArray(workspaces)) return workspaces;
|
|
27
|
+
return workspaces.packages ?? [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function findWorkspaceRootSync(startDir: string = process.cwd()): string {
|
|
31
|
+
let workspaceRoot = startDir;
|
|
32
|
+
while (workspaceRoot !== dirname(workspaceRoot)) {
|
|
33
|
+
if (existsSync(join(workspaceRoot, "package.json"))) return workspaceRoot;
|
|
34
|
+
workspaceRoot = dirname(workspaceRoot);
|
|
35
|
+
}
|
|
36
|
+
return startDir;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function expandWorkspacePatterns(workspaceRoot: string, patterns: string[]): string[] {
|
|
40
|
+
const paths = new Set<string>();
|
|
41
|
+
for (const pattern of patterns) {
|
|
42
|
+
for (const path of expandWorkspacePattern(workspaceRoot, pattern)) {
|
|
43
|
+
paths.add(path);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return [...paths];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function expandWorkspacePattern(workspaceRoot: string, pattern: string): string[] {
|
|
50
|
+
const normalized = pattern.replace(/\\/g, "/");
|
|
51
|
+
if (!normalized.includes("*")) return [normalized];
|
|
52
|
+
|
|
53
|
+
const parts = normalized.split("/");
|
|
54
|
+
const starIndex = parts.findIndex((part) => part === "*");
|
|
55
|
+
if (starIndex === -1) return [normalized];
|
|
56
|
+
|
|
57
|
+
const baseParts = parts.slice(0, starIndex);
|
|
58
|
+
const suffixParts = parts.slice(starIndex + 1);
|
|
59
|
+
const baseDir = join(workspaceRoot, ...baseParts);
|
|
60
|
+
if (!existsSync(baseDir)) return [];
|
|
61
|
+
|
|
62
|
+
return readdirSync(baseDir, { withFileTypes: true })
|
|
63
|
+
.filter((entry) => entry.isDirectory())
|
|
64
|
+
.flatMap((entry) => {
|
|
65
|
+
const childPattern = [...baseParts, entry.name, ...suffixParts].join("/");
|
|
66
|
+
return expandWorkspacePattern(workspaceRoot, childPattern);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function discoverWorkspacePackagesSync(workspaceRoot: string): WorkspacePackageEntry[] {
|
|
71
|
+
const rootPackageJsonPath = join(workspaceRoot, "package.json");
|
|
72
|
+
if (!existsSync(rootPackageJsonPath)) return [];
|
|
73
|
+
|
|
74
|
+
const rootPackageJson = JSON.parse(readFileSync(rootPackageJsonPath, "utf-8")) as PackageJson;
|
|
75
|
+
const patterns = parseWorkspacePatterns(rootPackageJson.workspaces);
|
|
76
|
+
if (patterns.length === 0) return [];
|
|
77
|
+
|
|
78
|
+
const entries: WorkspacePackageEntry[] = [];
|
|
79
|
+
for (const relativePath of expandWorkspacePatterns(workspaceRoot, patterns)) {
|
|
80
|
+
const packageJsonPath = join(workspaceRoot, relativePath, "package.json");
|
|
81
|
+
if (!existsSync(packageJsonPath)) continue;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as PackageJson;
|
|
85
|
+
if (packageJson.name) {
|
|
86
|
+
entries.push({ relativePath, name: packageJson.name });
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
// skip invalid package.json
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return entries;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function discoverWorkspacePackageNamesSync(workspaceRoot: string): string[] {
|
|
97
|
+
return discoverWorkspacePackagesSync(workspaceRoot).map((entry) => entry.name);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function packageNameFromAbsolutePath(
|
|
101
|
+
absolutePath: string,
|
|
102
|
+
workspaceRoot: string,
|
|
103
|
+
workspacePackages: readonly WorkspacePackageEntry[],
|
|
104
|
+
): string | null {
|
|
105
|
+
const normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
106
|
+
const normalizedRoot = workspaceRoot.replace(/\\/g, "/").replace(/\/$/, "");
|
|
107
|
+
|
|
108
|
+
if (!normalizedPath.startsWith(normalizedRoot)) return null;
|
|
109
|
+
|
|
110
|
+
const relativePath = normalizedPath.slice(normalizedRoot.length).replace(/^\//, "");
|
|
111
|
+
const sortedEntries = [...workspacePackages].sort(
|
|
112
|
+
(a, b) => b.relativePath.length - a.relativePath.length,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
for (const entry of sortedEntries) {
|
|
116
|
+
if (relativePath === entry.relativePath || relativePath.startsWith(`${entry.relativePath}/`)) {
|
|
117
|
+
return entry.name;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function discoverWorkspacePackagesAsync(
|
|
125
|
+
workspaceRoot: string,
|
|
126
|
+
readDirectory: (dirPath: string) => Promise<string[]>,
|
|
127
|
+
canAccessFile: (filePath: string) => Promise<boolean>,
|
|
128
|
+
readFileAsText: (filePath: string) => Promise<string>,
|
|
129
|
+
rootPackageJson: PackageJson,
|
|
130
|
+
): Promise<WorkspacePackageEntry[]> {
|
|
131
|
+
const patterns = parseWorkspacePatterns(rootPackageJson.workspaces);
|
|
132
|
+
if (patterns.length === 0) return [];
|
|
133
|
+
|
|
134
|
+
const entries: WorkspacePackageEntry[] = [];
|
|
135
|
+
for (const relativePath of await expandWorkspacePatternsAsync(
|
|
136
|
+
workspaceRoot,
|
|
137
|
+
patterns,
|
|
138
|
+
readDirectory,
|
|
139
|
+
)) {
|
|
140
|
+
const packageJsonPath = join(workspaceRoot, relativePath, "package.json");
|
|
141
|
+
try {
|
|
142
|
+
const exists = await canAccessFile(packageJsonPath);
|
|
143
|
+
if (!exists) continue;
|
|
144
|
+
|
|
145
|
+
const content = await readFileAsText(packageJsonPath);
|
|
146
|
+
const packageJson = JSON.parse(content) as PackageJson;
|
|
147
|
+
if (packageJson.name) {
|
|
148
|
+
entries.push({ relativePath, name: packageJson.name });
|
|
149
|
+
}
|
|
150
|
+
} catch {
|
|
151
|
+
// skip invalid package.json
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return entries;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function discoverWorkspacePackageNamesAsync(
|
|
159
|
+
workspaceRoot: string,
|
|
160
|
+
readDirectory: (dirPath: string) => Promise<string[]>,
|
|
161
|
+
canAccessFile: (filePath: string) => Promise<boolean>,
|
|
162
|
+
readFileAsText: (filePath: string) => Promise<string>,
|
|
163
|
+
rootPackageJson: PackageJson,
|
|
164
|
+
): Promise<string[]> {
|
|
165
|
+
const entries = await discoverWorkspacePackagesAsync(
|
|
166
|
+
workspaceRoot,
|
|
167
|
+
readDirectory,
|
|
168
|
+
canAccessFile,
|
|
169
|
+
readFileAsText,
|
|
170
|
+
rootPackageJson,
|
|
171
|
+
);
|
|
172
|
+
return entries.map((entry) => entry.name);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function expandWorkspacePatternsAsync(
|
|
176
|
+
workspaceRoot: string,
|
|
177
|
+
patterns: string[],
|
|
178
|
+
readDirectory: (dirPath: string) => Promise<string[]>,
|
|
179
|
+
): Promise<string[]> {
|
|
180
|
+
const paths = new Set<string>();
|
|
181
|
+
for (const pattern of patterns) {
|
|
182
|
+
for (const path of await expandWorkspacePatternAsync(workspaceRoot, pattern, readDirectory)) {
|
|
183
|
+
paths.add(path);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return [...paths];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function expandWorkspacePatternAsync(
|
|
190
|
+
workspaceRoot: string,
|
|
191
|
+
pattern: string,
|
|
192
|
+
readDirectory: (dirPath: string) => Promise<string[]>,
|
|
193
|
+
): Promise<string[]> {
|
|
194
|
+
const normalized = pattern.replace(/\\/g, "/");
|
|
195
|
+
if (!normalized.includes("*")) return [normalized];
|
|
196
|
+
|
|
197
|
+
const parts = normalized.split("/");
|
|
198
|
+
const starIndex = parts.findIndex((part) => part === "*");
|
|
199
|
+
if (starIndex === -1) return [normalized];
|
|
200
|
+
|
|
201
|
+
const baseParts = parts.slice(0, starIndex);
|
|
202
|
+
const suffixParts = parts.slice(starIndex + 1);
|
|
203
|
+
const baseDir = join(workspaceRoot, ...baseParts);
|
|
204
|
+
|
|
205
|
+
let entries: string[] = [];
|
|
206
|
+
try {
|
|
207
|
+
entries = await readDirectory(baseDir);
|
|
208
|
+
} catch {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const results: string[] = [];
|
|
213
|
+
for (const entry of entries) {
|
|
214
|
+
const childPattern = [...baseParts, entry, ...suffixParts].join("/");
|
|
215
|
+
const expanded = await expandWorkspacePatternAsync(workspaceRoot, childPattern, readDirectory);
|
|
216
|
+
results.push(...expanded);
|
|
217
|
+
}
|
|
218
|
+
return results;
|
|
219
|
+
}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { entitiesShell } from "../entities.shell";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
discoverWorkspacePackagesAsync,
|
|
6
|
+
EntityPackage,
|
|
7
|
+
packageNameFromAbsolutePath,
|
|
8
|
+
type TsConfig,
|
|
9
|
+
type TsConfigPaths,
|
|
10
|
+
type WorkspacePackageEntry,
|
|
11
|
+
} from "../package";
|
|
12
|
+
import { packagesShell } from "../package/package.shell";
|
|
5
13
|
|
|
6
14
|
export class EntityDependencyAnalyzer {
|
|
7
15
|
private readonly package: EntityPackage;
|
|
@@ -16,28 +24,48 @@ export class EntityDependencyAnalyzer {
|
|
|
16
24
|
*/
|
|
17
25
|
async getPackageDependenciesAtRef(reference: string): Promise<string[]> {
|
|
18
26
|
try {
|
|
19
|
-
// Get all internal packages in the monorepo
|
|
20
27
|
const allPackages = await EntityPackage.getAllPackages();
|
|
28
|
+
const internalPackages = new Set(allPackages);
|
|
29
|
+
const workspaceContext = await this.getWorkspaceContext();
|
|
21
30
|
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
const packageJsonDeps = await this.getPackageJsonDependencies(reference, internalPackages);
|
|
32
|
+
const tsconfigDeps = await this.getTsConfigDependencies(
|
|
33
|
+
reference,
|
|
34
|
+
internalPackages,
|
|
35
|
+
workspaceContext,
|
|
36
|
+
);
|
|
27
37
|
|
|
28
|
-
// Combine and filter for internal dependencies only
|
|
29
38
|
const allDeps = [...new Set([...packageJsonDeps, ...tsconfigDeps])];
|
|
30
|
-
|
|
31
|
-
return allDeps.filter((dep) => allPackages.includes(dep));
|
|
39
|
+
return allDeps.filter((dep) => internalPackages.has(dep));
|
|
32
40
|
} catch {
|
|
33
41
|
return [];
|
|
34
42
|
}
|
|
35
43
|
}
|
|
36
44
|
|
|
45
|
+
private async getWorkspaceContext(): Promise<{
|
|
46
|
+
readonly workspaceRoot: string;
|
|
47
|
+
readonly workspacePackages: WorkspacePackageEntry[];
|
|
48
|
+
}> {
|
|
49
|
+
const workspaceRoot = await packagesShell.getWorkspaceRoot();
|
|
50
|
+
const rootPackageJson = packagesShell.readJsonFile(`${workspaceRoot}/package.json`);
|
|
51
|
+
const workspacePackages = await discoverWorkspacePackagesAsync(
|
|
52
|
+
workspaceRoot,
|
|
53
|
+
packagesShell.readDirectory,
|
|
54
|
+
packagesShell.canAccessFile,
|
|
55
|
+
packagesShell.readFileAsText,
|
|
56
|
+
rootPackageJson,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return { workspaceRoot, workspacePackages };
|
|
60
|
+
}
|
|
61
|
+
|
|
37
62
|
/**
|
|
38
63
|
* Get package.json dependencies for a package at a specific reference
|
|
39
64
|
*/
|
|
40
|
-
private async getPackageJsonDependencies(
|
|
65
|
+
private async getPackageJsonDependencies(
|
|
66
|
+
reference: string,
|
|
67
|
+
internalPackages: ReadonlySet<string>,
|
|
68
|
+
): Promise<string[]> {
|
|
41
69
|
try {
|
|
42
70
|
const result = await entitiesShell.gitShowPackageJsonAtTag(
|
|
43
71
|
reference,
|
|
@@ -61,8 +89,7 @@ export class EntityDependencyAnalyzer {
|
|
|
61
89
|
: []),
|
|
62
90
|
];
|
|
63
91
|
|
|
64
|
-
|
|
65
|
-
return deps.filter((dep) => dep.startsWith("@repo/")).map((dep) => dep.replace("@repo/", ""));
|
|
92
|
+
return deps.filter((dep) => internalPackages.has(dep));
|
|
66
93
|
} catch {
|
|
67
94
|
return [];
|
|
68
95
|
}
|
|
@@ -71,7 +98,14 @@ export class EntityDependencyAnalyzer {
|
|
|
71
98
|
/**
|
|
72
99
|
* Get tsconfig dependencies by resolving paths to actual internal packages
|
|
73
100
|
*/
|
|
74
|
-
private async getTsConfigDependencies(
|
|
101
|
+
private async getTsConfigDependencies(
|
|
102
|
+
reference: string,
|
|
103
|
+
internalPackages: ReadonlySet<string>,
|
|
104
|
+
workspaceContext: {
|
|
105
|
+
readonly workspaceRoot: string;
|
|
106
|
+
readonly workspacePackages: WorkspacePackageEntry[];
|
|
107
|
+
},
|
|
108
|
+
): Promise<string[]> {
|
|
75
109
|
try {
|
|
76
110
|
const packagePath = this.package.getPath();
|
|
77
111
|
const tsconfigPaths = await this.getTsConfigPaths(reference);
|
|
@@ -79,16 +113,18 @@ export class EntityDependencyAnalyzer {
|
|
|
79
113
|
const deps: string[] = [];
|
|
80
114
|
|
|
81
115
|
for (const [alias, paths] of Object.entries(tsconfigPaths)) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
deps.push(alias.replace("@repo/", ""));
|
|
116
|
+
if (internalPackages.has(alias)) {
|
|
117
|
+
deps.push(alias);
|
|
85
118
|
}
|
|
86
119
|
|
|
87
|
-
// Check if paths point to internal packages
|
|
88
120
|
if (Array.isArray(paths)) {
|
|
89
121
|
for (const path of paths) {
|
|
90
122
|
const resolvedPath = resolve(packagePath, path);
|
|
91
|
-
const internalPackage = this.findInternalPackageFromPath(
|
|
123
|
+
const internalPackage = this.findInternalPackageFromPath(
|
|
124
|
+
resolvedPath,
|
|
125
|
+
workspaceContext.workspaceRoot,
|
|
126
|
+
workspaceContext.workspacePackages,
|
|
127
|
+
);
|
|
92
128
|
if (internalPackage) {
|
|
93
129
|
deps.push(internalPackage);
|
|
94
130
|
}
|
|
@@ -102,23 +138,12 @@ export class EntityDependencyAnalyzer {
|
|
|
102
138
|
}
|
|
103
139
|
}
|
|
104
140
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const appsMatch = absolutePath.match(/\/apps\/([^/]+)/);
|
|
112
|
-
|
|
113
|
-
if (packagesMatch) {
|
|
114
|
-
return `@repo/${packagesMatch[1]}`;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (appsMatch) {
|
|
118
|
-
return appsMatch[1];
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return null;
|
|
141
|
+
private findInternalPackageFromPath(
|
|
142
|
+
absolutePath: string,
|
|
143
|
+
workspaceRoot: string,
|
|
144
|
+
workspacePackages: readonly WorkspacePackageEntry[],
|
|
145
|
+
): string | null {
|
|
146
|
+
return packageNameFromAbsolutePath(absolutePath, workspaceRoot, workspacePackages);
|
|
122
147
|
}
|
|
123
148
|
|
|
124
149
|
/**
|
|
@@ -142,14 +167,12 @@ export class EntityDependencyAnalyzer {
|
|
|
142
167
|
try {
|
|
143
168
|
const result = await entitiesShell.gitShow(`${reference}:${this.package.getTsconfigPath()}`);
|
|
144
169
|
if (result.exitCode !== 0) {
|
|
145
|
-
// If tsconfig.json doesn't exist at this reference, return empty config
|
|
146
170
|
return {};
|
|
147
171
|
}
|
|
148
172
|
|
|
149
173
|
const content = result.text();
|
|
150
174
|
return JSON.parse(content);
|
|
151
175
|
} catch {
|
|
152
|
-
// If parsing fails, return empty config
|
|
153
176
|
return {};
|
|
154
177
|
}
|
|
155
178
|
}
|
|
@@ -171,10 +194,8 @@ export class EntityDependencyAnalyzer {
|
|
|
171
194
|
const extendedContent = readFileSync(extendedPath, "utf-8");
|
|
172
195
|
const extendedConfig: TsConfig = JSON.parse(extendedContent);
|
|
173
196
|
|
|
174
|
-
// Recursively resolve extended configs
|
|
175
197
|
const resolvedExtended = await this.resolveExtendedTsConfig(extendedConfig, packagePath);
|
|
176
198
|
|
|
177
|
-
// Merge configurations
|
|
178
199
|
return {
|
|
179
200
|
...resolvedExtended,
|
|
180
201
|
...config,
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { EntityPackage } from "../package";
|
|
3
|
+
import { installPackagesShellTestMock } from "../package/package-test-mock";
|
|
3
4
|
import { EntityPackageCommits } from "./package-commits";
|
|
4
5
|
|
|
5
6
|
describe("EntityPackageCommits", () => {
|
|
7
|
+
let restorePackagesShellMock: () => void;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
restorePackagesShellMock = installPackagesShellTestMock();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
restorePackagesShellMock();
|
|
15
|
+
});
|
|
16
|
+
|
|
6
17
|
test("should create instance", () => {
|
|
7
|
-
const packageInstance = new EntityPackage("api");
|
|
18
|
+
const packageInstance = new EntityPackage("@apps/api");
|
|
8
19
|
const commitPackage = new EntityPackageCommits(packageInstance);
|
|
9
20
|
expect(commitPackage).toBeDefined();
|
|
10
21
|
});
|
|
@@ -16,9 +27,8 @@ describe("EntityPackageCommits", () => {
|
|
|
16
27
|
});
|
|
17
28
|
|
|
18
29
|
test("should have main method", () => {
|
|
19
|
-
const packageInstance = new EntityPackage("api");
|
|
30
|
+
const packageInstance = new EntityPackage("@apps/api");
|
|
20
31
|
const commitPackage = new EntityPackageCommits(packageInstance);
|
|
21
|
-
// Check that the main method exists
|
|
22
32
|
expect(typeof commitPackage.getCommitsInRange).toBe("function");
|
|
23
33
|
});
|
|
24
34
|
|
|
@@ -26,16 +36,14 @@ describe("EntityPackageCommits", () => {
|
|
|
26
36
|
test("should return empty array when git operations fail", async () => {
|
|
27
37
|
const packageInstance = new EntityPackage("root");
|
|
28
38
|
const commitPackage = new EntityPackageCommits(packageInstance);
|
|
29
|
-
// Test with invalid range to trigger error handling
|
|
30
39
|
const result = await commitPackage.getCommitsInRange("invalid", "invalid");
|
|
31
40
|
expect(Array.isArray(result)).toBe(true);
|
|
32
41
|
expect(result.length).toBe(0);
|
|
33
42
|
});
|
|
34
43
|
|
|
35
44
|
test("should handle package-specific commits", async () => {
|
|
36
|
-
const packageInstance = new EntityPackage("api");
|
|
45
|
+
const packageInstance = new EntityPackage("@apps/api");
|
|
37
46
|
const commitPackage = new EntityPackageCommits(packageInstance);
|
|
38
|
-
// Test with invalid range to trigger error handling
|
|
39
47
|
const result = await commitPackage.getCommitsInRange("invalid", "invalid");
|
|
40
48
|
expect(Array.isArray(result)).toBe(true);
|
|
41
49
|
expect(result.length).toBe(0);
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { EntityPackage } from "../package";
|
|
3
|
+
import { installPackagesShellTestMock } from "../package/package-test-mock";
|
|
3
4
|
import { EntityPackageTags } from "./package-tags";
|
|
4
5
|
|
|
5
6
|
describe("EntityPackageTags", () => {
|
|
7
|
+
let restorePackagesShellMock: () => void;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
restorePackagesShellMock = installPackagesShellTestMock();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
restorePackagesShellMock();
|
|
15
|
+
});
|
|
16
|
+
|
|
6
17
|
test("should create instance", () => {
|
|
7
|
-
const packageInstance = new EntityPackage("api");
|
|
18
|
+
const packageInstance = new EntityPackage("@apps/api");
|
|
8
19
|
const tagPackage = new EntityPackageTags(packageInstance);
|
|
9
20
|
expect(tagPackage).toBeDefined();
|
|
10
21
|
});
|
|
@@ -25,49 +36,45 @@ describe("EntityPackageTags", () => {
|
|
|
25
36
|
});
|
|
26
37
|
|
|
27
38
|
test("should detect tag prefix for package-specific tags", () => {
|
|
28
|
-
const packageInstance = new EntityPackage("api");
|
|
39
|
+
const packageInstance = new EntityPackage("@apps/api");
|
|
29
40
|
const tagPackage = new EntityPackageTags(packageInstance);
|
|
30
41
|
|
|
31
|
-
expect(tagPackage.detectTagPrefix("api-v1.0.0")).toBe("api-v");
|
|
32
|
-
expect(tagPackage.detectTagPrefix("intershell-v2.1.3")).toBe("intershell-v");
|
|
33
|
-
expect(tagPackage.detectTagPrefix("ui-v0.0.1")).toBe("ui-v");
|
|
42
|
+
expect(tagPackage.detectTagPrefix("apps/api-v1.0.0")).toBe("apps/api-v");
|
|
43
|
+
expect(tagPackage.detectTagPrefix("packages/intershell-v2.1.3")).toBe("packages/intershell-v");
|
|
44
|
+
expect(tagPackage.detectTagPrefix("packages/ui-v0.0.1")).toBe("packages/ui-v");
|
|
34
45
|
});
|
|
35
46
|
|
|
36
47
|
test("should return undefined for invalid tag formats", () => {
|
|
37
|
-
const packageInstance = new EntityPackage("api");
|
|
48
|
+
const packageInstance = new EntityPackage("@apps/api");
|
|
38
49
|
const tagPackage = new EntityPackageTags(packageInstance);
|
|
39
50
|
|
|
40
51
|
expect(tagPackage.detectTagPrefix("1.0.0")).toBeUndefined();
|
|
41
|
-
expect(tagPackage.detectTagPrefix("invalid")).toBeUndefined();
|
|
52
|
+
expect(tagPackage.detectTagPrefix("invalid-tag")).toBeUndefined();
|
|
42
53
|
expect(tagPackage.detectTagPrefix("")).toBeUndefined();
|
|
43
54
|
});
|
|
44
55
|
|
|
45
56
|
test("should compare versions correctly", () => {
|
|
46
|
-
const packageInstance = new EntityPackage("api");
|
|
57
|
+
const packageInstance = new EntityPackage("@apps/api");
|
|
47
58
|
const tagPackage = new EntityPackageTags(packageInstance);
|
|
48
59
|
|
|
49
|
-
expect(tagPackage.compareVersions("1.0.0", "1.0.1")).toBe(-1);
|
|
50
|
-
expect(tagPackage.compareVersions("1.0.1", "1.0.0")).toBe(1);
|
|
51
60
|
expect(tagPackage.compareVersions("1.0.0", "1.0.0")).toBe(0);
|
|
52
|
-
expect(tagPackage.compareVersions("
|
|
53
|
-
expect(tagPackage.compareVersions("1.0.0", "
|
|
61
|
+
expect(tagPackage.compareVersions("1.1.0", "1.0.0")).toBe(1);
|
|
62
|
+
expect(tagPackage.compareVersions("1.0.0", "1.1.0")).toBe(-1);
|
|
54
63
|
});
|
|
55
64
|
|
|
56
65
|
test("should handle version comparison with different lengths", () => {
|
|
57
|
-
const packageInstance = new EntityPackage("api");
|
|
66
|
+
const packageInstance = new EntityPackage("@apps/api");
|
|
58
67
|
const tagPackage = new EntityPackageTags(packageInstance);
|
|
59
68
|
|
|
69
|
+
expect(tagPackage.compareVersions("1.0.0.1", "1.0.0")).toBe(1);
|
|
60
70
|
expect(tagPackage.compareVersions("1.0", "1.0.0")).toBe(0);
|
|
61
|
-
expect(tagPackage.compareVersions("1.0.0", "1.0")).toBe(0);
|
|
62
|
-
expect(tagPackage.compareVersions("1", "1.0.0")).toBe(0);
|
|
63
71
|
});
|
|
64
72
|
|
|
65
73
|
test("should handle version comparison edge cases", () => {
|
|
66
|
-
const packageInstance = new EntityPackage("api");
|
|
74
|
+
const packageInstance = new EntityPackage("@apps/api");
|
|
67
75
|
const tagPackage = new EntityPackageTags(packageInstance);
|
|
68
76
|
|
|
69
|
-
expect(tagPackage.compareVersions("0.0.
|
|
70
|
-
expect(tagPackage.compareVersions("0.
|
|
71
|
-
expect(tagPackage.compareVersions("1.0.0", "0.9.9")).toBe(1);
|
|
77
|
+
expect(tagPackage.compareVersions("0.0.1", "0.0.0")).toBe(1);
|
|
78
|
+
expect(tagPackage.compareVersions("10.0.0", "9.0.0")).toBe(1);
|
|
72
79
|
});
|
|
73
80
|
});
|
|
@@ -188,7 +188,7 @@ export class EntityPackageTags {
|
|
|
188
188
|
packageName = matchingPackage;
|
|
189
189
|
} else {
|
|
190
190
|
throw new Error(
|
|
191
|
-
`Invalid tag prefix format: "${prefix}". Expected format: v (root) or
|
|
191
|
+
`Invalid tag prefix format: "${prefix}". Expected format: v (root) or <scope>/<name>-v (e.g., apps/api-v, packages/intershell-v)`,
|
|
192
192
|
);
|
|
193
193
|
}
|
|
194
194
|
|