bun-workspaces 1.8.2 → 1.9.0
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 +51 -13
- package/package.json +1 -1
- package/src/2392.mjs +184 -3
- package/src/5166.mjs +1 -0
- package/src/8529.mjs +10 -0
- package/src/affected/affectedBaseRef.mjs +12 -0
- package/src/affected/externalDependencyChanges.mjs +47 -0
- package/src/affected/fileAffectedWorkspaces.mjs +145 -53
- package/src/affected/gitAffectedFiles.mjs +44 -1
- package/src/affected/gitAffectedWorkspaces.mjs +73 -3
- package/src/affected/index.mjs +2 -0
- package/src/ai/mcp/serverState.mjs +1 -1
- package/src/cli/commands/commandHandlerUtils.mjs +12 -7
- package/src/cli/commands/commands.mjs +4 -1
- package/src/cli/commands/handleSimpleCommands.mjs +2 -2
- package/src/cli/commands/listAffected.mjs +184 -0
- package/src/cli/commands/runScript/handleRunAffected.mjs +99 -0
- package/src/cli/commands/runScript/handleRunScript.mjs +19 -202
- package/src/cli/commands/runScript/index.mjs +1 -0
- package/src/cli/commands/runScript/scriptRunFlow.mjs +213 -0
- package/src/cli/index.d.ts +749 -134
- package/src/config/public.d.ts +66 -2
- package/src/config/rootConfig/rootConfig.mjs +4 -0
- package/src/config/rootConfig/rootConfigSchema.mjs +3 -0
- package/src/config/workspaceConfig/mergeWorkspaceConfig.mjs +33 -19
- package/src/config/workspaceConfig/workspaceConfig.mjs +3 -0
- package/src/config/workspaceConfig/workspaceConfigSchema.mjs +26 -0
- package/src/index.d.ts +307 -5
- package/src/index.mjs +1 -0
- package/src/internal/bun/bunLock.mjs +33 -0
- package/src/internal/generated/aiDocs/docs.mjs +152 -3
- package/src/internal/generated/ajv/validateRootConfig.mjs +1 -1
- package/src/internal/generated/ajv/validateWorkspaceConfig.mjs +1 -1
- package/src/project/implementations/fileSystemProject/affectedWorkspaces.mjs +225 -0
- package/src/project/implementations/{fileSystemProject.mjs → fileSystemProject/fileSystemProject.mjs} +169 -12
- package/src/project/implementations/fileSystemProject/index.mjs +4 -0
- package/src/project/implementations/memoryProject.mjs +1 -0
- package/src/project/index.mjs +1 -1
- package/src/rslib-runtime.mjs +0 -31
- package/src/workspaces/applyWorkspacePatternConfigs.mjs +10 -1
- package/src/workspaces/dependencyGraph/resolveDependencies.mjs +68 -18
- package/src/workspaces/findWorkspaces.mjs +1 -0
- package/src/workspaces/workspace.mjs +8 -2
|
@@ -8,10 +8,15 @@ const GLOB_CHARACTER_REGEX = /[*?[{]/;
|
|
|
8
8
|
const toPosixPath = (filePath) => filePath.replaceAll("\\", "/");
|
|
9
9
|
const stripTrailingSlashes = (filePath) => filePath.replace(/\/+$/, "");
|
|
10
10
|
const stripLeadingSlashes = (filePath) => filePath.replace(/^\/+/, "");
|
|
11
|
+
const stripDotSlashSegments = (filePath) => {
|
|
12
|
+
let stripped = filePath;
|
|
13
|
+
while (stripped.startsWith("./")) stripped = stripped.slice(2);
|
|
14
|
+
return stripped === "." ? "" : stripped;
|
|
15
|
+
};
|
|
11
16
|
const normalizeChangedFilePath = ({ rootDirectory, filePath }) => {
|
|
12
17
|
const posixFilePath = toPosixPath(filePath);
|
|
13
18
|
if (!path.isAbsolute(filePath)) {
|
|
14
|
-
return posixFilePath;
|
|
19
|
+
return stripDotSlashSegments(posixFilePath);
|
|
15
20
|
}
|
|
16
21
|
const posixRoot = stripTrailingSlashes(toPosixPath(rootDirectory));
|
|
17
22
|
if (posixFilePath === posixRoot) {
|
|
@@ -160,12 +165,36 @@ const resolveInputWorkspaceDependencies = ({ workspaceInputs }) => {
|
|
|
160
165
|
}
|
|
161
166
|
return inputDependenciesByName;
|
|
162
167
|
};
|
|
168
|
+
const collectDirectEdges = ({
|
|
169
|
+
workspace,
|
|
170
|
+
inputDependenciesByName,
|
|
171
|
+
ignoreWorkspaceDependencies,
|
|
172
|
+
}) => {
|
|
173
|
+
const edges = [];
|
|
174
|
+
for (const dependencyName of inputDependenciesByName.get(workspace.name) ??
|
|
175
|
+
[]) {
|
|
176
|
+
edges.push({
|
|
177
|
+
dependencyName,
|
|
178
|
+
edgeSource: "input",
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
if (!ignoreWorkspaceDependencies) {
|
|
182
|
+
for (const dependencyName of workspace.dependencies) {
|
|
183
|
+
edges.push({
|
|
184
|
+
dependencyName,
|
|
185
|
+
edgeSource: "package",
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return edges;
|
|
190
|
+
};
|
|
163
191
|
const computeAffectedWorkspaceSet = ({
|
|
164
192
|
workspaceInputs,
|
|
165
193
|
workspaceByName,
|
|
166
194
|
changedFilesByName,
|
|
195
|
+
externalDepChangesByWorkspace,
|
|
167
196
|
inputDependenciesByName,
|
|
168
|
-
|
|
197
|
+
ignoreWorkspaceDependencies,
|
|
169
198
|
}) => {
|
|
170
199
|
const inputDependentsByName = new Map();
|
|
171
200
|
for (const [workspaceName, dependencyNames] of inputDependenciesByName) {
|
|
@@ -181,7 +210,11 @@ const computeAffectedWorkspaceSet = ({
|
|
|
181
210
|
const affected = new Set();
|
|
182
211
|
const queue = [];
|
|
183
212
|
for (const { workspace } of workspaceInputs) {
|
|
184
|
-
|
|
213
|
+
const hasChangedFiles =
|
|
214
|
+
(changedFilesByName.get(workspace.name)?.length ?? 0) > 0;
|
|
215
|
+
const hasExternalDepChanges =
|
|
216
|
+
(externalDepChangesByWorkspace.get(workspace.name)?.length ?? 0) > 0;
|
|
217
|
+
if (hasChangedFiles || hasExternalDepChanges) {
|
|
185
218
|
affected.add(workspace.name);
|
|
186
219
|
queue.push(workspace.name);
|
|
187
220
|
}
|
|
@@ -191,7 +224,7 @@ const computeAffectedWorkspaceSet = ({
|
|
|
191
224
|
const currentWorkspace = workspaceByName.get(currentName);
|
|
192
225
|
const dependents = [
|
|
193
226
|
...(inputDependentsByName.get(currentName) ?? []),
|
|
194
|
-
...(!
|
|
227
|
+
...(!ignoreWorkspaceDependencies && currentWorkspace
|
|
195
228
|
? currentWorkspace.dependents
|
|
196
229
|
: []),
|
|
197
230
|
];
|
|
@@ -204,66 +237,117 @@ const computeAffectedWorkspaceSet = ({
|
|
|
204
237
|
}
|
|
205
238
|
return affected;
|
|
206
239
|
};
|
|
240
|
+
/**
|
|
241
|
+
* Walk forward from `directDependencyName` through the affected dep graph,
|
|
242
|
+
* appending each next affected dep edge to the chain until we run out of
|
|
243
|
+
* affected dep edges to follow. Stops on:
|
|
244
|
+
* - no further affected dep edges,
|
|
245
|
+
* - revisiting a workspace already in the chain (cycle).
|
|
246
|
+
*
|
|
247
|
+
* Branching is broken deterministically by edge insertion order
|
|
248
|
+
* (input edges before package edges, declaration order within each).
|
|
249
|
+
*/ const extendChainThroughAffectedDeps = ({
|
|
250
|
+
startingWorkspaceName,
|
|
251
|
+
directDependencyName,
|
|
252
|
+
directEdgeSource,
|
|
253
|
+
workspaceByName,
|
|
254
|
+
inputDependenciesByName,
|
|
255
|
+
affectedSet,
|
|
256
|
+
ignoreWorkspaceDependencies,
|
|
257
|
+
}) => {
|
|
258
|
+
const chain = [
|
|
259
|
+
{
|
|
260
|
+
workspaceName: startingWorkspaceName,
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
workspaceName: directDependencyName,
|
|
264
|
+
edgeSource: directEdgeSource,
|
|
265
|
+
},
|
|
266
|
+
];
|
|
267
|
+
const visited = new Set([startingWorkspaceName, directDependencyName]);
|
|
268
|
+
let currentName = directDependencyName;
|
|
269
|
+
while (true) {
|
|
270
|
+
const currentWorkspace = workspaceByName.get(currentName);
|
|
271
|
+
if (!currentWorkspace) break;
|
|
272
|
+
const nextEdge = collectDirectEdges({
|
|
273
|
+
workspace: currentWorkspace,
|
|
274
|
+
inputDependenciesByName,
|
|
275
|
+
ignoreWorkspaceDependencies,
|
|
276
|
+
}).find(
|
|
277
|
+
({ dependencyName }) =>
|
|
278
|
+
!visited.has(dependencyName) &&
|
|
279
|
+
workspaceByName.has(dependencyName) &&
|
|
280
|
+
affectedSet.has(dependencyName),
|
|
281
|
+
);
|
|
282
|
+
if (!nextEdge) break;
|
|
283
|
+
chain.push({
|
|
284
|
+
workspaceName: nextEdge.dependencyName,
|
|
285
|
+
edgeSource: nextEdge.edgeSource,
|
|
286
|
+
});
|
|
287
|
+
visited.add(nextEdge.dependencyName);
|
|
288
|
+
currentName = nextEdge.dependencyName;
|
|
289
|
+
}
|
|
290
|
+
return chain;
|
|
291
|
+
};
|
|
207
292
|
const collectAffectedDependencies = ({
|
|
208
293
|
startingWorkspace,
|
|
209
294
|
workspaceByName,
|
|
210
295
|
inputDependenciesByName,
|
|
211
296
|
affectedSet,
|
|
212
|
-
|
|
297
|
+
ignoreWorkspaceDependencies,
|
|
213
298
|
}) => {
|
|
214
299
|
const results = [];
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const dependencyChain = [
|
|
240
|
-
...chain,
|
|
241
|
-
{
|
|
242
|
-
workspaceName: dependencyName,
|
|
243
|
-
edgeSource,
|
|
244
|
-
},
|
|
245
|
-
];
|
|
246
|
-
if (affectedSet.has(dependencyName)) {
|
|
247
|
-
results.push({
|
|
248
|
-
dependencyName,
|
|
249
|
-
chain: dependencyChain,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
visit(dependencyName, dependencyChain);
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
visit(startingWorkspace.name, [
|
|
256
|
-
{
|
|
257
|
-
workspaceName: startingWorkspace.name,
|
|
258
|
-
},
|
|
259
|
-
]);
|
|
300
|
+
const seen = new Set([startingWorkspace.name]);
|
|
301
|
+
const directEdges = collectDirectEdges({
|
|
302
|
+
workspace: startingWorkspace,
|
|
303
|
+
inputDependenciesByName,
|
|
304
|
+
ignoreWorkspaceDependencies,
|
|
305
|
+
});
|
|
306
|
+
for (const { dependencyName, edgeSource } of directEdges) {
|
|
307
|
+
if (seen.has(dependencyName)) continue;
|
|
308
|
+
if (!workspaceByName.has(dependencyName)) continue;
|
|
309
|
+
seen.add(dependencyName);
|
|
310
|
+
if (!affectedSet.has(dependencyName)) continue;
|
|
311
|
+
results.push({
|
|
312
|
+
dependencyName,
|
|
313
|
+
chain: extendChainThroughAffectedDeps({
|
|
314
|
+
startingWorkspaceName: startingWorkspace.name,
|
|
315
|
+
directDependencyName: dependencyName,
|
|
316
|
+
directEdgeSource: edgeSource,
|
|
317
|
+
workspaceByName,
|
|
318
|
+
inputDependenciesByName,
|
|
319
|
+
affectedSet,
|
|
320
|
+
ignoreWorkspaceDependencies,
|
|
321
|
+
}),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
260
324
|
return results;
|
|
261
325
|
};
|
|
326
|
+
const filterExternalDepChangesByInputs = ({
|
|
327
|
+
changesByWorkspace,
|
|
328
|
+
workspaceInputs,
|
|
329
|
+
}) => {
|
|
330
|
+
const filtered = new Map();
|
|
331
|
+
for (const { workspace, inputExternalDependencyNames } of workspaceInputs) {
|
|
332
|
+
const changes = changesByWorkspace.get(workspace.name);
|
|
333
|
+
if (!changes?.length) continue;
|
|
334
|
+
if (inputExternalDependencyNames === undefined) {
|
|
335
|
+
filtered.set(workspace.name, changes);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (inputExternalDependencyNames.length === 0) continue;
|
|
339
|
+
const allowed = new Set(inputExternalDependencyNames);
|
|
340
|
+
const matched = changes.filter((change) => allowed.has(change.name));
|
|
341
|
+
if (matched.length) filtered.set(workspace.name, matched);
|
|
342
|
+
}
|
|
343
|
+
return filtered;
|
|
344
|
+
};
|
|
262
345
|
const getFileAffectedWorkspaces = async ({
|
|
263
346
|
rootDirectory,
|
|
264
347
|
workspaceInputs,
|
|
265
348
|
changedFilePaths,
|
|
266
|
-
|
|
349
|
+
externalDepChangesByWorkspace = new Map(),
|
|
350
|
+
ignoreWorkspaceDependencies = false,
|
|
267
351
|
}) => {
|
|
268
352
|
const normalizedChangedFilePaths = changedFilePaths.map((filePath) =>
|
|
269
353
|
normalizeChangedFilePath({
|
|
@@ -285,6 +369,10 @@ const getFileAffectedWorkspaces = async ({
|
|
|
285
369
|
}),
|
|
286
370
|
);
|
|
287
371
|
}
|
|
372
|
+
const filteredExternalDepChanges = filterExternalDepChangesByInputs({
|
|
373
|
+
changesByWorkspace: externalDepChangesByWorkspace,
|
|
374
|
+
workspaceInputs,
|
|
375
|
+
});
|
|
288
376
|
const inputDependenciesByName = resolveInputWorkspaceDependencies({
|
|
289
377
|
workspaceInputs,
|
|
290
378
|
});
|
|
@@ -292,17 +380,20 @@ const getFileAffectedWorkspaces = async ({
|
|
|
292
380
|
workspaceInputs,
|
|
293
381
|
workspaceByName,
|
|
294
382
|
changedFilesByName,
|
|
383
|
+
externalDepChangesByWorkspace: filteredExternalDepChanges,
|
|
295
384
|
inputDependenciesByName,
|
|
296
|
-
|
|
385
|
+
ignoreWorkspaceDependencies,
|
|
297
386
|
});
|
|
298
387
|
const affectedWorkspaces = workspaceInputs.map(({ workspace }) => {
|
|
299
388
|
const changedFiles = changedFilesByName.get(workspace.name) ?? [];
|
|
389
|
+
const externalDependencies =
|
|
390
|
+
filteredExternalDepChanges.get(workspace.name) ?? [];
|
|
300
391
|
const dependencies = collectAffectedDependencies({
|
|
301
392
|
startingWorkspace: workspace,
|
|
302
393
|
workspaceByName,
|
|
303
394
|
inputDependenciesByName,
|
|
304
395
|
affectedSet,
|
|
305
|
-
|
|
396
|
+
ignoreWorkspaceDependencies,
|
|
306
397
|
});
|
|
307
398
|
return {
|
|
308
399
|
workspace,
|
|
@@ -310,6 +401,7 @@ const getFileAffectedWorkspaces = async ({
|
|
|
310
401
|
affectedReasons: {
|
|
311
402
|
changedFiles,
|
|
312
403
|
dependencies,
|
|
404
|
+
externalDependencies,
|
|
313
405
|
},
|
|
314
406
|
};
|
|
315
407
|
});
|
|
@@ -52,6 +52,38 @@ const resolveGitRoot = async (rootDirectory) => {
|
|
|
52
52
|
}
|
|
53
53
|
return result.stdout.trim();
|
|
54
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* Read a project-root-relative file's contents at a specific git ref via
|
|
57
|
+
* `git show <ref>:<repo-relative-path>`. Returns `null` if the file does not
|
|
58
|
+
* exist at that ref (e.g. it was added later). Throws on other git errors.
|
|
59
|
+
*/ const readProjectFileAtGitRef = async ({
|
|
60
|
+
rootDirectory,
|
|
61
|
+
ref,
|
|
62
|
+
projectRelativePath,
|
|
63
|
+
}) => {
|
|
64
|
+
const gitRoot = fs.realpathSync.native(
|
|
65
|
+
path.resolve(await resolveGitRoot(rootDirectory)),
|
|
66
|
+
);
|
|
67
|
+
const absoluteProjectRoot = fs.realpathSync.native(
|
|
68
|
+
path.resolve(rootDirectory),
|
|
69
|
+
);
|
|
70
|
+
const absoluteFile = path.resolve(absoluteProjectRoot, projectRelativePath);
|
|
71
|
+
const repoRelative = path
|
|
72
|
+
.relative(gitRoot, absoluteFile)
|
|
73
|
+
.split(path.sep)
|
|
74
|
+
.join("/");
|
|
75
|
+
const result = await runGit(["show", `${ref}:${repoRelative}`], gitRoot);
|
|
76
|
+
if (result.exitCode === 0) return result.stdout;
|
|
77
|
+
if (
|
|
78
|
+
result.stderr.includes("does not exist") ||
|
|
79
|
+
result.stderr.includes("exists on disk, but not in")
|
|
80
|
+
) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
throw new GIT_AFFECTED_ERRORS.GitCommandFailed(
|
|
84
|
+
`git show ${ref}:${repoRelative} failed (exit ${result.exitCode}): ${result.stderr.trim()}`,
|
|
85
|
+
);
|
|
86
|
+
};
|
|
55
87
|
const toProjectFilePath = ({
|
|
56
88
|
gitRoot,
|
|
57
89
|
absoluteProjectRoot,
|
|
@@ -83,6 +115,10 @@ const getGitAffectedFiles = async (options) => {
|
|
|
83
115
|
const includeStaged = !ignoreUncommitted && !ignoreStaged;
|
|
84
116
|
const includeUnstaged = !ignoreUncommitted && !ignoreUnstaged;
|
|
85
117
|
const includeUntracked = !ignoreUncommitted && !ignoreUntracked;
|
|
118
|
+
const [baseSha, headSha] = await Promise.all([
|
|
119
|
+
runGitOrThrow(["rev-parse", baseRef], gitRoot).then((out) => out.trim()),
|
|
120
|
+
runGitOrThrow(["rev-parse", headRef], gitRoot).then((out) => out.trim()),
|
|
121
|
+
]);
|
|
86
122
|
const collectors = [
|
|
87
123
|
runGitOrThrow(
|
|
88
124
|
["diff", "--name-only", "-z", baseRef, headRef],
|
|
@@ -147,7 +183,14 @@ const getGitAffectedFiles = async (options) => {
|
|
|
147
183
|
.sort((a, b) => a.projectFilePath.localeCompare(b.projectFilePath));
|
|
148
184
|
return {
|
|
149
185
|
files,
|
|
186
|
+
baseSha,
|
|
187
|
+
headSha,
|
|
150
188
|
};
|
|
151
189
|
};
|
|
152
190
|
|
|
153
|
-
export {
|
|
191
|
+
export {
|
|
192
|
+
GIT_AFFECTED_ERRORS,
|
|
193
|
+
GIT_AFFECTED_FILE_REASONS,
|
|
194
|
+
getGitAffectedFiles,
|
|
195
|
+
readProjectFileAtGitRef,
|
|
196
|
+
};
|
|
@@ -1,22 +1,90 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { parseBunLockPackageVersions } from "../internal/bun/bunLock.mjs";
|
|
4
|
+
import { BunWorkspacesError } from "../internal/core/index.mjs";
|
|
5
|
+
import { logger } from "../internal/logger/index.mjs";
|
|
6
|
+
import { computeExternalDependencyChanges } from "./externalDependencyChanges.mjs";
|
|
1
7
|
import { getFileAffectedWorkspaces } from "./fileAffectedWorkspaces.mjs";
|
|
2
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
getGitAffectedFiles,
|
|
10
|
+
readProjectFileAtGitRef,
|
|
11
|
+
} from "./gitAffectedFiles.mjs";
|
|
3
12
|
|
|
13
|
+
const BUN_LOCK_PROJECT_RELATIVE_PATH = "bun.lock";
|
|
14
|
+
const readCurrentBunLock = (rootDirectory) => {
|
|
15
|
+
const lockPath = path.join(rootDirectory, BUN_LOCK_PROJECT_RELATIVE_PATH);
|
|
16
|
+
try {
|
|
17
|
+
return fs.readFileSync(lockPath, "utf8");
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const loadVersionsAt = async (rootDirectory, ref) => {
|
|
23
|
+
const contents = await readProjectFileAtGitRef({
|
|
24
|
+
rootDirectory,
|
|
25
|
+
ref,
|
|
26
|
+
projectRelativePath: BUN_LOCK_PROJECT_RELATIVE_PATH,
|
|
27
|
+
});
|
|
28
|
+
if (contents === null) return new Map();
|
|
29
|
+
const parsed = parseBunLockPackageVersions(contents);
|
|
30
|
+
if (parsed instanceof BunWorkspacesError) {
|
|
31
|
+
logger.warn(
|
|
32
|
+
`Could not parse bun.lock at ref "${ref}": ${parsed.message}. Treating as empty.`,
|
|
33
|
+
);
|
|
34
|
+
return new Map();
|
|
35
|
+
}
|
|
36
|
+
return parsed;
|
|
37
|
+
};
|
|
38
|
+
const loadCurrentVersions = (rootDirectory) => {
|
|
39
|
+
const contents = readCurrentBunLock(rootDirectory);
|
|
40
|
+
if (contents === null) return new Map();
|
|
41
|
+
const parsed = parseBunLockPackageVersions(contents);
|
|
42
|
+
if (parsed instanceof BunWorkspacesError) {
|
|
43
|
+
logger.warn(
|
|
44
|
+
`Could not parse current bun.lock: ${parsed.message}. Treating as empty.`,
|
|
45
|
+
);
|
|
46
|
+
return new Map();
|
|
47
|
+
}
|
|
48
|
+
return parsed;
|
|
49
|
+
};
|
|
4
50
|
const getGitAffectedWorkspaces = async ({
|
|
5
51
|
rootDirectory,
|
|
6
52
|
workspacesOptions,
|
|
7
53
|
gitOptions,
|
|
8
54
|
}) => {
|
|
9
|
-
const {
|
|
55
|
+
const {
|
|
56
|
+
files: gitFiles,
|
|
57
|
+
baseSha,
|
|
58
|
+
headSha,
|
|
59
|
+
} = await getGitAffectedFiles({
|
|
10
60
|
rootDirectory,
|
|
11
61
|
...gitOptions,
|
|
12
62
|
});
|
|
13
63
|
const gitFileByPath = new Map(
|
|
14
64
|
gitFiles.map((file) => [file.projectFilePath, file]),
|
|
15
65
|
);
|
|
66
|
+
const projectWorkspaces = workspacesOptions.workspaces ?? [];
|
|
67
|
+
const externalDepChangesByWorkspace =
|
|
68
|
+
workspacesOptions.ignoreExternalDependencies || !projectWorkspaces.length
|
|
69
|
+
? new Map()
|
|
70
|
+
: computeExternalDependencyChanges({
|
|
71
|
+
workspaces: projectWorkspaces,
|
|
72
|
+
baseLock: await loadVersionsAt(rootDirectory, gitOptions.baseRef),
|
|
73
|
+
headLock:
|
|
74
|
+
gitOptions.headRef === "HEAD"
|
|
75
|
+
? loadCurrentVersions(rootDirectory)
|
|
76
|
+
: await loadVersionsAt(rootDirectory, gitOptions.headRef),
|
|
77
|
+
});
|
|
78
|
+
const {
|
|
79
|
+
workspaces: _omit,
|
|
80
|
+
ignoreExternalDependencies: _omit2,
|
|
81
|
+
...fileOpts
|
|
82
|
+
} = workspacesOptions;
|
|
16
83
|
const { affectedWorkspaces } = await getFileAffectedWorkspaces({
|
|
17
84
|
rootDirectory,
|
|
18
|
-
...
|
|
85
|
+
...fileOpts,
|
|
19
86
|
changedFilePaths: gitFiles.map((file) => file.projectFilePath),
|
|
87
|
+
externalDepChangesByWorkspace,
|
|
20
88
|
});
|
|
21
89
|
const annotatedWorkspaces = affectedWorkspaces.map((result) => ({
|
|
22
90
|
...result,
|
|
@@ -32,6 +100,8 @@ const getGitAffectedWorkspaces = async ({
|
|
|
32
100
|
}));
|
|
33
101
|
return {
|
|
34
102
|
affectedWorkspaces: annotatedWorkspaces,
|
|
103
|
+
baseSha,
|
|
104
|
+
headSha,
|
|
35
105
|
};
|
|
36
106
|
};
|
|
37
107
|
|
package/src/affected/index.mjs
CHANGED
|
@@ -3,12 +3,17 @@ import { BunWorkspacesError } from "../../internal/core/error/index.mjs";
|
|
|
3
3
|
import { createLogger, logger } from "../../internal/logger/index.mjs";
|
|
4
4
|
import { getCliCommandConfig } from "../../2392.mjs";
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Splits a multi-value CLI arg on whitespace (any of space/tab/newline). A
|
|
8
|
+
* literal space inside a value can be preserved by escaping it with a
|
|
9
|
+
* backslash (e.g. `path/with\ space`). Used for `--files` and
|
|
10
|
+
* `--workspace-patterns`, both of which accept output of `$(bw ...)`
|
|
11
|
+
* substitutions, which are typically newline-separated.
|
|
12
|
+
*/ const splitWhitespaceArg = (raw) =>
|
|
13
|
+
raw
|
|
14
|
+
.split(/(?<!\\)\s+/)
|
|
15
|
+
.filter(Boolean)
|
|
16
|
+
.map((value) => value.replace(/\\\s/g, " "));
|
|
12
17
|
const createWorkspaceInfoLines = (workspace) => [
|
|
13
18
|
`Workspace: ${workspace.name}${workspace.isRoot ? " (root)" : ""}`,
|
|
14
19
|
` - Aliases: ${workspace.aliases.join(", ")}`,
|
|
@@ -85,5 +90,5 @@ export {
|
|
|
85
90
|
createWorkspaceInfoLines,
|
|
86
91
|
handleGlobalCommand,
|
|
87
92
|
handleProjectCommand,
|
|
88
|
-
|
|
93
|
+
splitWhitespaceArg,
|
|
89
94
|
};
|
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
tagInfo,
|
|
8
8
|
workspaceInfo,
|
|
9
9
|
} from "./handleSimpleCommands.mjs";
|
|
10
|
+
import { listAffected } from "./listAffected.mjs";
|
|
10
11
|
import { mcpServer } from "./mcp.mjs";
|
|
11
|
-
import { runScript } from "./runScript/index.mjs";
|
|
12
|
+
import { runAffected, runScript } from "./runScript/index.mjs";
|
|
12
13
|
|
|
13
14
|
const defineGlobalCommands = (context) => {
|
|
14
15
|
doctor(context);
|
|
@@ -22,6 +23,8 @@ const defineProjectCommands = (context) => {
|
|
|
22
23
|
tagInfo(context);
|
|
23
24
|
mcpServer(context);
|
|
24
25
|
runScript(context);
|
|
26
|
+
listAffected(context);
|
|
27
|
+
runAffected(context);
|
|
25
28
|
};
|
|
26
29
|
|
|
27
30
|
export { defineGlobalCommands, defineProjectCommands };
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
createWorkspaceInfoLines,
|
|
8
8
|
handleGlobalCommand,
|
|
9
9
|
handleProjectCommand,
|
|
10
|
-
|
|
10
|
+
splitWhitespaceArg,
|
|
11
11
|
} from "./commandHandlerUtils.mjs";
|
|
12
12
|
import { isJSONObject } from "../../8257.mjs";
|
|
13
13
|
|
|
@@ -51,7 +51,7 @@ const listWorkspaces = handleProjectCommand(
|
|
|
51
51
|
}
|
|
52
52
|
const patterns = positionalWorkspacePatterns?.length
|
|
53
53
|
? positionalWorkspacePatterns
|
|
54
|
-
:
|
|
54
|
+
: splitWhitespaceArg(options.workspacePatterns ?? "");
|
|
55
55
|
const workspaces = patterns?.length
|
|
56
56
|
? project.findWorkspacesByPattern(...patterns)
|
|
57
57
|
: project.workspaces;
|