bun-workspaces 1.8.2 → 1.10.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.
Files changed (47) hide show
  1. package/AGENTS.md +537 -0
  2. package/README.md +51 -13
  3. package/package.json +1 -1
  4. package/src/2392.mjs +184 -3
  5. package/src/5166.mjs +1 -0
  6. package/src/8529.mjs +10 -0
  7. package/src/affected/affectedBaseRef.mjs +12 -0
  8. package/src/affected/externalDependencyChanges.mjs +47 -0
  9. package/src/affected/fileAffectedWorkspaces.mjs +152 -54
  10. package/src/affected/gitAffectedFiles.mjs +44 -1
  11. package/src/affected/gitAffectedWorkspaces.mjs +73 -3
  12. package/src/affected/index.mjs +2 -0
  13. package/src/ai/mcp/serverState.mjs +1 -1
  14. package/src/cli/commands/commandHandlerUtils.mjs +12 -7
  15. package/src/cli/commands/commands.mjs +4 -1
  16. package/src/cli/commands/handleSimpleCommands.mjs +2 -2
  17. package/src/cli/commands/listAffected.mjs +184 -0
  18. package/src/cli/commands/runScript/handleRunAffected.mjs +99 -0
  19. package/src/cli/commands/runScript/handleRunScript.mjs +19 -202
  20. package/src/cli/commands/runScript/index.mjs +1 -0
  21. package/src/cli/commands/runScript/scriptRunFlow.mjs +213 -0
  22. package/src/cli/index.d.ts +749 -134
  23. package/src/config/public.d.ts +66 -2
  24. package/src/config/rootConfig/rootConfig.mjs +9 -9
  25. package/src/config/rootConfig/rootConfigSchema.mjs +3 -0
  26. package/src/config/workspaceConfig/mergeWorkspaceConfig.mjs +33 -19
  27. package/src/config/workspaceConfig/workspaceConfig.mjs +3 -0
  28. package/src/config/workspaceConfig/workspaceConfigSchema.mjs +26 -0
  29. package/src/index.d.ts +307 -5
  30. package/src/index.mjs +1 -0
  31. package/src/internal/bun/bunLock.mjs +33 -0
  32. package/src/internal/generated/aiDocs/docs.mjs +169 -9
  33. package/src/internal/generated/ajv/validateRootConfig.mjs +1 -1
  34. package/src/internal/generated/ajv/validateWorkspaceConfig.mjs +1 -1
  35. package/src/project/implementations/fileSystemProject/affectedWorkspaces.mjs +227 -0
  36. package/src/project/implementations/{fileSystemProject.mjs → fileSystemProject/fileSystemProject.mjs} +169 -12
  37. package/src/project/implementations/fileSystemProject/index.mjs +4 -0
  38. package/src/project/implementations/memoryProject.mjs +1 -0
  39. package/src/project/implementations/projectBase.mjs +11 -17
  40. package/src/project/index.mjs +1 -1
  41. package/src/rslib-runtime.mjs +0 -31
  42. package/src/workspaces/applyWorkspacePatternConfigs.mjs +16 -2
  43. package/src/workspaces/dependencyGraph/resolveDependencies.mjs +68 -18
  44. package/src/workspaces/dependencyGraph/validateDependencyRules.mjs +14 -7
  45. package/src/workspaces/findWorkspaces.mjs +3 -0
  46. package/src/workspaces/workspace.mjs +8 -2
  47. package/src/workspaces/workspacePattern.mjs +134 -46
@@ -1,21 +1,9 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { expandHomePath } from "../../../internal/core/index.mjs";
4
1
  import { logger } from "../../../internal/logger/index.mjs";
5
2
  import {
6
3
  handleProjectCommand,
7
- splitWorkspacePatterns,
4
+ splitWhitespaceArg,
8
5
  } from "../commandHandlerUtils.mjs";
9
- import {
10
- getDefaultOutputStyle,
11
- validateOutputStyle,
12
- } from "./output/outputStyle.mjs";
13
- import {
14
- createScriptEvent,
15
- createScriptEventTarget,
16
- renderGroupedOutput,
17
- } from "./output/renderGroupedOutput.mjs";
18
- import { renderPlainOutput } from "./output/renderPlainOutput.mjs";
6
+ import { handleScriptRunFlow } from "./scriptRunFlow.mjs";
19
7
 
20
8
  const runScript = handleProjectCommand(
21
9
  "runScript",
@@ -62,200 +50,29 @@ const runScript = handleProjectCommand(
62
50
  }
63
51
  const workspacePatterns = positionalWorkspacePatterns?.length
64
52
  ? positionalWorkspacePatterns
65
- : splitWorkspacePatterns(options.workspacePatterns ?? "");
53
+ : splitWhitespaceArg(options.workspacePatterns ?? "");
66
54
  logger.debug(
67
55
  `Command: Run ${options.inline ? "inline " : ""}script ${JSON.stringify(script)} for ${workspacePatterns.length ? "workspaces " + workspacePatterns.join(", ") : "all workspaces"}`,
68
56
  );
69
57
  logger.debug(`Options: ${JSON.stringify(options)}`);
70
- const outputStyle = options.outputStyle
71
- ? validateOutputStyle(options.outputStyle)
72
- : getDefaultOutputStyle();
73
- logger.debug(`Effective output style: ${outputStyle}`);
74
- const scriptEventTarget = createScriptEventTarget();
75
- const { output, summary, workspaces } = project.runScriptAcrossWorkspaces({
76
- workspacePatterns: workspacePatterns.length
77
- ? workspacePatterns
78
- : undefined,
79
- script,
80
- inline: options.inline
81
- ? options.inlineName || options.shell
82
- ? {
83
- scriptName: options.inlineName,
84
- shell: options.shell,
85
- }
86
- : true
87
- : undefined,
88
- args: scriptArgs,
89
- dependencyOrder: options.depOrder,
90
- ignoreDependencyFailure: options.ignoreDepFailure,
91
- ignoreOutput: outputStyle === "none",
92
- onScriptEvent: (event, { workspace, exitResult }) => {
93
- setTimeout(() =>
94
- // place at end of call stack so listeners in render func receive event
95
- scriptEventTarget.dispatchEvent(
96
- createScriptEvent[event]({
97
- workspace,
98
- exitResult,
99
- }),
100
- ),
101
- );
58
+ await handleScriptRunFlow({
59
+ project,
60
+ context: {
61
+ outputWriters,
62
+ terminalWidth,
63
+ terminalHeight,
102
64
  },
103
- parallel:
104
- typeof options.parallel === "boolean" ||
105
- typeof options.parallel === "undefined"
106
- ? undefined
107
- : options.parallel === "true"
108
- ? true
109
- : options.parallel === "false"
110
- ? false
111
- : {
112
- max: options.parallel,
113
- },
114
- });
115
- const scriptName = options.inline
116
- ? options.inlineName || "(inline)"
117
- : script;
118
- logger.debug(`Script name: ${scriptName}`);
119
- const stripDisruptiveControls = workspaces.length > 1 || !!options.parallel;
120
- logger.debug(`Strip disruptive controls: ${stripDisruptiveControls}`);
121
- let groupedLines = "auto";
122
- if (options.groupedLines) {
123
- if (options.groupedLines === "all") {
124
- groupedLines = "all";
125
- } else if (options.groupedLines === "auto") {
126
- groupedLines = "auto";
127
- } else {
128
- const parsedGroupedLines = parseInt(options.groupedLines);
129
- if (parsedGroupedLines <= 0 || isNaN(parsedGroupedLines)) {
130
- logger.error(
131
- `Invalid max grouped lines value: ${options.groupedLines}. Must be a positive number or "all".`,
132
- );
133
- process.exit(1);
134
- return;
135
- }
136
- groupedLines = parsedGroupedLines;
137
- }
138
- }
139
- logger.debug(`Effective grouped lines: ${JSON.stringify(groupedLines)}`);
140
- if (!options.prefix) {
141
- logger.warn(
142
- "--no-prefix is deprecated and will be removed in a future version. Use --output-style=plain instead.",
143
- );
144
- if (!options.outputStyle) {
145
- options.outputStyle = "plain";
146
- }
147
- }
148
- const outputStyleHandlers = {
149
- grouped: () =>
150
- renderGroupedOutput(
151
- workspaces,
152
- output,
153
- summary,
154
- scriptEventTarget,
155
- groupedLines,
156
- outputWriters,
157
- terminalWidth,
158
- terminalHeight,
159
- ),
160
- prefixed: () =>
161
- renderPlainOutput(output, outputWriters, {
162
- prefix: true,
163
- stripDisruptiveControls,
164
- }),
165
- plain: () =>
166
- renderPlainOutput(output, outputWriters, {
167
- prefix: false,
168
- stripDisruptiveControls,
65
+ script,
66
+ scriptArgs,
67
+ cliOptions: options,
68
+ runner: (scriptOptions) =>
69
+ project.runScriptAcrossWorkspaces({
70
+ ...scriptOptions,
71
+ workspacePatterns: workspacePatterns.length
72
+ ? workspacePatterns
73
+ : undefined,
169
74
  }),
170
- none: async () => {
171
- // no-op
172
- },
173
- };
174
- await outputStyleHandlers[outputStyle]();
175
- const exitResults = await summary;
176
- exitResults.scriptResults.forEach(
177
- ({ success, metadata: { workspace }, exitCode }) => {
178
- const isSkipped = exitCode === -1;
179
- if (isSkipped) {
180
- logger.info(
181
- `➖ ${workspace.name}: ${scriptName} (skipped due to dependency failure)`,
182
- );
183
- } else {
184
- logger.info(
185
- `${success ? "✅" : "❌"} ${workspace.name}: ${scriptName}${exitCode ? ` (exited with code ${exitCode})` : ""}`,
186
- );
187
- }
188
- },
189
- );
190
- const s = exitResults.scriptResults.length === 1 ? "" : "s";
191
- const skippedCount = exitResults.scriptResults.filter(
192
- ({ exitCode }) => exitCode === -1,
193
- ).length;
194
- const skippedMessage = skippedCount ? ` (${skippedCount} skipped)` : "";
195
- if (exitResults.failureCount) {
196
- const message = `${exitResults.failureCount} of ${exitResults.scriptResults.length} script${s} failed${skippedMessage}`;
197
- logger.info(message);
198
- } else {
199
- logger.info(
200
- `${exitResults.scriptResults.length} script${s} ran successfully${skippedMessage}`,
201
- );
202
- }
203
- if (options.jsonOutfile) {
204
- const fullOutputPath = path.resolve(
205
- project.rootDirectory,
206
- expandHomePath(options.jsonOutfile),
207
- );
208
- // Check if can make directory
209
- const jsonOutputDir = path.dirname(fullOutputPath);
210
- if (!fs.existsSync(jsonOutputDir)) {
211
- try {
212
- logger.debug(
213
- `Creating JSON output file directory "${jsonOutputDir}"`,
214
- );
215
- fs.mkdirSync(jsonOutputDir, {
216
- recursive: true,
217
- });
218
- } catch (error) {
219
- logger.error(
220
- `Failed to create JSON output file directory "${jsonOutputDir}": ${error}`,
221
- );
222
- process.exit(1);
223
- return;
224
- }
225
- } else if (fs.statSync(jsonOutputDir).isFile()) {
226
- logger.error(
227
- `Given JSON output file directory "${jsonOutputDir}" is an existing file`,
228
- );
229
- process.exit(1);
230
- return;
231
- }
232
- // Check if can make file
233
- if (
234
- fs.existsSync(fullOutputPath) &&
235
- fs.statSync(fullOutputPath).isDirectory()
236
- ) {
237
- logger.error(
238
- `Given JSON output file path "${fullOutputPath}" is an existing directory`,
239
- );
240
- process.exit(1);
241
- return;
242
- }
243
- try {
244
- logger.debug(`Writing JSON output file "${fullOutputPath}"`);
245
- fs.writeFileSync(fullOutputPath, JSON.stringify(exitResults, null, 2));
246
- } catch (error) {
247
- logger.error(
248
- `Failed to write JSON output file "${fullOutputPath}": ${error}`,
249
- );
250
- process.exit(1);
251
- return;
252
- }
253
- logger.info(`JSON output written to ${fullOutputPath}`);
254
- }
255
- if (exitResults.failureCount) {
256
- process.exit(1);
257
- return;
258
- }
75
+ });
259
76
  },
260
77
  );
261
78
 
@@ -1,4 +1,5 @@
1
1
  export * from "./handleRunScript.mjs";
2
+ export * from "./handleRunAffected.mjs";
2
3
  export * from "./output/renderPlainOutput.mjs";
3
4
 
4
5
  export {};
@@ -0,0 +1,213 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { expandHomePath } from "../../../internal/core/index.mjs";
4
+ import { logger } from "../../../internal/logger/index.mjs";
5
+ import {
6
+ getDefaultOutputStyle,
7
+ validateOutputStyle,
8
+ } from "./output/outputStyle.mjs";
9
+ import {
10
+ createScriptEvent,
11
+ createScriptEventTarget,
12
+ renderGroupedOutput,
13
+ } from "./output/renderGroupedOutput.mjs";
14
+ import { renderPlainOutput } from "./output/renderPlainOutput.mjs";
15
+
16
+ const handleScriptRunFlow = async ({
17
+ project,
18
+ context,
19
+ script,
20
+ scriptArgs,
21
+ cliOptions,
22
+ runner,
23
+ }) => {
24
+ const outputStyle = cliOptions.outputStyle
25
+ ? validateOutputStyle(cliOptions.outputStyle)
26
+ : getDefaultOutputStyle();
27
+ logger.debug(`Effective output style: ${outputStyle}`);
28
+ const scriptEventTarget = createScriptEventTarget();
29
+ const inline = cliOptions.inline
30
+ ? cliOptions.inlineName || cliOptions.shell
31
+ ? {
32
+ scriptName: cliOptions.inlineName,
33
+ shell: cliOptions.shell,
34
+ }
35
+ : true
36
+ : undefined;
37
+ const parallel =
38
+ typeof cliOptions.parallel === "boolean" ||
39
+ typeof cliOptions.parallel === "undefined"
40
+ ? undefined
41
+ : cliOptions.parallel === "true"
42
+ ? true
43
+ : cliOptions.parallel === "false"
44
+ ? false
45
+ : {
46
+ max: cliOptions.parallel,
47
+ };
48
+ const { output, summary, workspaces } = await runner({
49
+ script: script,
50
+ inline,
51
+ args: scriptArgs,
52
+ dependencyOrder: cliOptions.depOrder,
53
+ ignoreDependencyFailure: cliOptions.ignoreDepFailure,
54
+ ignoreOutput: outputStyle === "none",
55
+ onScriptEvent: (event, { workspace, exitResult }) => {
56
+ setTimeout(() =>
57
+ // place at end of call stack so listeners in render func receive event
58
+ scriptEventTarget.dispatchEvent(
59
+ createScriptEvent[event]({
60
+ workspace,
61
+ exitResult,
62
+ }),
63
+ ),
64
+ );
65
+ },
66
+ parallel,
67
+ });
68
+ const scriptName = cliOptions.inline
69
+ ? cliOptions.inlineName || "(inline)"
70
+ : script;
71
+ logger.debug(`Script name: ${scriptName}`);
72
+ const stripDisruptiveControls =
73
+ workspaces.length > 1 || !!cliOptions.parallel;
74
+ logger.debug(`Strip disruptive controls: ${stripDisruptiveControls}`);
75
+ let groupedLines = "auto";
76
+ if (cliOptions.groupedLines) {
77
+ if (cliOptions.groupedLines === "all") {
78
+ groupedLines = "all";
79
+ } else if (cliOptions.groupedLines === "auto") {
80
+ groupedLines = "auto";
81
+ } else {
82
+ const parsedGroupedLines = parseInt(cliOptions.groupedLines);
83
+ if (parsedGroupedLines <= 0 || isNaN(parsedGroupedLines)) {
84
+ logger.error(
85
+ `Invalid max grouped lines value: ${cliOptions.groupedLines}. Must be a positive number or "all".`,
86
+ );
87
+ process.exit(1);
88
+ return;
89
+ }
90
+ groupedLines = parsedGroupedLines;
91
+ }
92
+ }
93
+ logger.debug(`Effective grouped lines: ${JSON.stringify(groupedLines)}`);
94
+ if (!cliOptions.prefix) {
95
+ logger.warn(
96
+ "--no-prefix is deprecated and will be removed in a future version. Use --output-style=plain instead.",
97
+ );
98
+ if (!cliOptions.outputStyle) {
99
+ cliOptions.outputStyle = "plain";
100
+ }
101
+ }
102
+ const outputStyleHandlers = {
103
+ grouped: () =>
104
+ renderGroupedOutput(
105
+ workspaces,
106
+ output,
107
+ summary,
108
+ scriptEventTarget,
109
+ groupedLines,
110
+ context.outputWriters,
111
+ context.terminalWidth,
112
+ context.terminalHeight,
113
+ ),
114
+ prefixed: () =>
115
+ renderPlainOutput(output, context.outputWriters, {
116
+ prefix: true,
117
+ stripDisruptiveControls,
118
+ }),
119
+ plain: () =>
120
+ renderPlainOutput(output, context.outputWriters, {
121
+ prefix: false,
122
+ stripDisruptiveControls,
123
+ }),
124
+ none: async () => {
125
+ // no-op
126
+ },
127
+ };
128
+ await outputStyleHandlers[outputStyle]();
129
+ const exitResults = await summary;
130
+ exitResults.scriptResults.forEach(
131
+ ({ success, metadata: { workspace }, exitCode }) => {
132
+ const isSkipped = exitCode === -1;
133
+ if (isSkipped) {
134
+ logger.info(
135
+ `➖ ${workspace.name}: ${scriptName} (skipped due to dependency failure)`,
136
+ );
137
+ } else {
138
+ logger.info(
139
+ `${success ? "✅" : "❌"} ${workspace.name}: ${scriptName}${exitCode ? ` (exited with code ${exitCode})` : ""}`,
140
+ );
141
+ }
142
+ },
143
+ );
144
+ const s = exitResults.scriptResults.length === 1 ? "" : "s";
145
+ const skippedCount = exitResults.scriptResults.filter(
146
+ ({ exitCode }) => exitCode === -1,
147
+ ).length;
148
+ const skippedMessage = skippedCount ? ` (${skippedCount} skipped)` : "";
149
+ if (exitResults.failureCount) {
150
+ const message = `${exitResults.failureCount} of ${exitResults.scriptResults.length} script${s} failed${skippedMessage}`;
151
+ logger.info(message);
152
+ } else {
153
+ logger.info(
154
+ `${exitResults.scriptResults.length} script${s} ran successfully${skippedMessage}`,
155
+ );
156
+ }
157
+ if (cliOptions.jsonOutfile) {
158
+ const fullOutputPath = path.resolve(
159
+ project.rootDirectory,
160
+ expandHomePath(cliOptions.jsonOutfile),
161
+ );
162
+ // Check if can make directory
163
+ const jsonOutputDir = path.dirname(fullOutputPath);
164
+ if (!fs.existsSync(jsonOutputDir)) {
165
+ try {
166
+ logger.debug(`Creating JSON output file directory "${jsonOutputDir}"`);
167
+ fs.mkdirSync(jsonOutputDir, {
168
+ recursive: true,
169
+ });
170
+ } catch (error) {
171
+ logger.error(
172
+ `Failed to create JSON output file directory "${jsonOutputDir}": ${error}`,
173
+ );
174
+ process.exit(1);
175
+ return;
176
+ }
177
+ } else if (fs.statSync(jsonOutputDir).isFile()) {
178
+ logger.error(
179
+ `Given JSON output file directory "${jsonOutputDir}" is an existing file`,
180
+ );
181
+ process.exit(1);
182
+ return;
183
+ }
184
+ // Check if can make file
185
+ if (
186
+ fs.existsSync(fullOutputPath) &&
187
+ fs.statSync(fullOutputPath).isDirectory()
188
+ ) {
189
+ logger.error(
190
+ `Given JSON output file path "${fullOutputPath}" is an existing directory`,
191
+ );
192
+ process.exit(1);
193
+ return;
194
+ }
195
+ try {
196
+ logger.debug(`Writing JSON output file "${fullOutputPath}"`);
197
+ fs.writeFileSync(fullOutputPath, JSON.stringify(exitResults, null, 2));
198
+ } catch (error) {
199
+ logger.error(
200
+ `Failed to write JSON output file "${fullOutputPath}": ${error}`,
201
+ );
202
+ process.exit(1);
203
+ return;
204
+ }
205
+ logger.info(`JSON output written to ${fullOutputPath}`);
206
+ }
207
+ if (exitResults.failureCount) {
208
+ process.exit(1);
209
+ return;
210
+ }
211
+ };
212
+
213
+ export { handleScriptRunFlow };