bun-workspaces 1.5.2 → 1.7.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 +96 -2
- package/package.json +5 -1
- package/src/1108.mjs +9 -2
- package/src/ai/mcp/bwMcpServer.mjs +86 -0
- package/src/cli/index.d.ts +61 -1
- package/src/config/public.d.ts +58 -0
- package/src/config/public.mjs +2 -0
- package/src/config/rootConfig/index.mjs +1 -0
- package/src/config/rootConfig/mergeRootConfig.mjs +24 -0
- package/src/config/rootConfig/rootConfig.mjs +7 -0
- package/src/config/rootConfig/rootConfigSchema.mjs +19 -0
- package/src/config/workspaceConfig/index.mjs +1 -0
- package/src/config/workspaceConfig/mergeWorkspaceConfig.mjs +75 -0
- package/src/config/workspaceConfig/workspaceConfig.mjs +0 -8
- package/src/index.d.ts +59 -1
- package/src/internal/generated/aiDocs/docs.mjs +92 -14
- package/src/internal/generated/ajv/validateRootConfig.mjs +1 -1
- package/src/project/implementations/fileSystemProject.mjs +1 -0
- package/src/runScript/public.d.ts +40 -0
- package/src/runScript/public.mjs +1 -0
- package/src/runScript/scriptRuntimeMetadata.mjs +19 -2
- package/src/workspaces/applyWorkspacePatternConfigs.mjs +58 -0
- package/src/workspaces/dependencyGraph/validateDependencyRules.mjs +11 -10
- package/src/workspaces/findWorkspaces.mjs +10 -0
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<a href="https://bunworkspaces.com">
|
|
2
|
-
<img src="./
|
|
2
|
+
<img src="./workspaces/web/documentation-website/src/pages/public/images/png/bwunster-bg-banner-wide_3000x900.png" alt="bun-workspaces" width="100%" />
|
|
3
3
|
</a>
|
|
4
4
|
|
|
5
5
|
# bun-workspaces
|
|
@@ -248,10 +248,104 @@ const runManyScripts = async () => {
|
|
|
248
248
|
};
|
|
249
249
|
```
|
|
250
250
|
|
|
251
|
+
### Configuration
|
|
252
|
+
|
|
253
|
+
`bun-workspaces` has no required configuration, but there are optional config files.
|
|
254
|
+
|
|
255
|
+
#### Workspace Config
|
|
256
|
+
|
|
257
|
+
Workspace configs can be placed in a workspace's directory at `bw.workspace.ts`.
|
|
258
|
+
|
|
259
|
+
[Workspace configuration documentation here](https://bunworkspaces.com/config/workspace)
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// bw.workspace.ts — place in a workspace directory
|
|
263
|
+
|
|
264
|
+
// Also supported: bw.workspace.js, bw.workspace.json, bw.workspace.jsonc, or a "bw" key in package.json
|
|
265
|
+
|
|
266
|
+
import { defineWorkspaceConfig } from "bun-workspaces/config";
|
|
267
|
+
|
|
268
|
+
export default defineWorkspaceConfig({
|
|
269
|
+
alias: "my-web-app", // shorthand name; use array for multiple
|
|
270
|
+
tags: ["app", "frontend"],
|
|
271
|
+
scripts: {
|
|
272
|
+
// lower order runs first in sequenced script execution
|
|
273
|
+
build: { order: 1 },
|
|
274
|
+
test: { order: 2 },
|
|
275
|
+
},
|
|
276
|
+
rules: {
|
|
277
|
+
workspaceDependencies: {
|
|
278
|
+
// Only "my-workspace" or workspaces tagged "lib" are allowed as dependencies
|
|
279
|
+
allowPatterns: ["tag:lib", "my-workspace"],
|
|
280
|
+
// Workspaces tagged "backend" are forbidden as dependencies
|
|
281
|
+
denyPatterns: ["tag:backend"],
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### Root Config
|
|
288
|
+
|
|
289
|
+
A root config can be placed in the project root directory at `bw.root.ts`,
|
|
290
|
+
which can also apply workspace configs in bulk by using workspace patterns.
|
|
291
|
+
|
|
292
|
+
[Root configuration documentation here](https://bunworkspaces.com/config/root)
|
|
293
|
+
|
|
294
|
+
[More on workspace pattern configs here](https://bunworkspaces.com/config/workspace-pattern-configs)
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
// bw.root.ts — place in your project root directory
|
|
298
|
+
// Also supported: bw.root.js, bw.root.json, bw.root.jsonc, or a "bw" key in package.json
|
|
299
|
+
import { defineRootConfig } from "bun-workspaces/config";
|
|
300
|
+
|
|
301
|
+
export default defineRootConfig({
|
|
302
|
+
defaults: {
|
|
303
|
+
// default value for --parallel option
|
|
304
|
+
parallelMax: 4,
|
|
305
|
+
// default value for --shell option
|
|
306
|
+
shell: "system",
|
|
307
|
+
// default value for global --include-root-workspace option
|
|
308
|
+
includeRootWorkspace: false,
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
// Apply workspace configs in bulk by workspace pattern, in order.
|
|
312
|
+
// Each entry merges into matching workspaces' accumulated config.
|
|
313
|
+
// Pattern matching reflects aliases and tags added by earlier entries.
|
|
314
|
+
workspacePatternConfigs: [
|
|
315
|
+
{
|
|
316
|
+
patterns: ["path:packages/apps/**/*"],
|
|
317
|
+
config: { tags: ["app"] },
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
patterns: ["path:packages/libs/**/*"],
|
|
321
|
+
config: { tags: ["lib"] },
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
// "tag:app" matches because the first entry added it
|
|
325
|
+
patterns: ["tag:app"],
|
|
326
|
+
config: {
|
|
327
|
+
rules: {
|
|
328
|
+
workspaceDependencies: {
|
|
329
|
+
allowPatterns: ["tag:lib"], // apps may only depend on libs
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
patterns: ["tag:app"],
|
|
336
|
+
// Factory form: receives static workspace data and accumulated config
|
|
337
|
+
config: (workspace, prevConfig) => ({
|
|
338
|
+
alias: workspace.name.replace(/^@my-scope\//, ""),
|
|
339
|
+
}),
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
251
345
|
_`bun-workspaces` is independent from the [Bun](https://bun.sh) project and is not affiliated with or endorsed by Anthropic. This project aims to enhance the experience of Bun for its users._
|
|
252
346
|
|
|
253
347
|
Developed By:
|
|
254
348
|
|
|
255
349
|
<a href="https://smorsic.io" target="_blank" rel="noopener noreferrer">
|
|
256
|
-
<img src="./
|
|
350
|
+
<img src="./workspaces/web/documentation-website/src/pages/public/images/png/smorsic-banner_light_803x300.png" alt="Smorsic Labs logo" width="280" />
|
|
257
351
|
</a>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bun-workspaces",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "A monorepo management tool for Bun, with a CLI and API to enhance Bun's native workspaces.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"exports": {
|
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
"./config": {
|
|
16
16
|
"types": "./src/config/public.d.ts",
|
|
17
17
|
"default": "./src/config/public.mjs"
|
|
18
|
+
},
|
|
19
|
+
"./script": {
|
|
20
|
+
"types": "./src/runScript/public.d.ts",
|
|
21
|
+
"default": "./src/runScript/public.mjs"
|
|
18
22
|
}
|
|
19
23
|
},
|
|
20
24
|
"types": "./src/index.d.ts",
|
package/src/1108.mjs
CHANGED
|
@@ -24,7 +24,14 @@ const SCRIPT_RUNTIME_METADATA_CONFIG = {
|
|
|
24
24
|
envVarName: "BW_WORKSPACE_NAME",
|
|
25
25
|
},
|
|
26
26
|
};
|
|
27
|
-
const
|
|
28
|
-
SCRIPT_RUNTIME_METADATA_CONFIG
|
|
27
|
+
const validateScriptRuntimeMetadataKey = (key) => {
|
|
28
|
+
if (!(key in SCRIPT_RUNTIME_METADATA_CONFIG)) {
|
|
29
|
+
throw new Error(`Invalid script runtime metadata key: ${key}`);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const getScriptRuntimeMetadataConfig = (key) => {
|
|
33
|
+
validateScriptRuntimeMetadataKey(key);
|
|
34
|
+
return SCRIPT_RUNTIME_METADATA_CONFIG[key];
|
|
35
|
+
};
|
|
29
36
|
|
|
30
37
|
export { getScriptRuntimeMetadataConfig };
|
|
@@ -215,6 +215,80 @@ const runManyScripts = async () => {
|
|
|
215
215
|
}
|
|
216
216
|
`.trim();
|
|
217
217
|
|
|
218
|
+
const ROOT_CONFIG_QUICKSTART = `
|
|
219
|
+
// bw.root.ts — place in your project root directory
|
|
220
|
+
// Also supported: bw.root.js, bw.root.json, bw.root.jsonc, or a "bw" key in package.json
|
|
221
|
+
import { defineRootConfig } from "bun-workspaces/config";
|
|
222
|
+
|
|
223
|
+
export default defineRootConfig({
|
|
224
|
+
defaults: {
|
|
225
|
+
// default value for --parallel option
|
|
226
|
+
parallelMax: 4,
|
|
227
|
+
// default value for --shell option
|
|
228
|
+
shell: "system",
|
|
229
|
+
// default value for global --include-root-workspace option
|
|
230
|
+
includeRootWorkspace: false,
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
// Apply workspace configs in bulk by workspace pattern, in order.
|
|
234
|
+
// Each entry merges into matching workspaces' accumulated config.
|
|
235
|
+
// Pattern matching reflects aliases and tags added by earlier entries.
|
|
236
|
+
workspacePatternConfigs: [
|
|
237
|
+
{
|
|
238
|
+
patterns: ["path:packages/apps/**/*"],
|
|
239
|
+
config: { tags: ["app"] },
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
patterns: ["path:packages/libs/**/*"],
|
|
243
|
+
config: { tags: ["lib"] },
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
// "tag:app" matches because the first entry added it
|
|
247
|
+
patterns: ["tag:app"],
|
|
248
|
+
config: {
|
|
249
|
+
rules: {
|
|
250
|
+
workspaceDependencies: {
|
|
251
|
+
allowPatterns: ["tag:lib"], // apps may only depend on libs
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
patterns: ["tag:app"],
|
|
258
|
+
// Factory form: receives static workspace data and accumulated config
|
|
259
|
+
config: (workspace, prevConfig) => ({
|
|
260
|
+
alias: workspace.name.replace(/^@my-scope\\//, ""),
|
|
261
|
+
}),
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
});
|
|
265
|
+
`.trim();
|
|
266
|
+
const WORKSPACE_CONFIG_QUICKSTART = `
|
|
267
|
+
// bw.workspace.ts — place in a workspace directory
|
|
268
|
+
|
|
269
|
+
// Also supported: bw.workspace.js, bw.workspace.json, bw.workspace.jsonc, or a "bw" key in package.json
|
|
270
|
+
|
|
271
|
+
import { defineWorkspaceConfig } from "bun-workspaces/config";
|
|
272
|
+
|
|
273
|
+
export default defineWorkspaceConfig({
|
|
274
|
+
alias: "my-web-app", // shorthand name; use array for multiple
|
|
275
|
+
tags: ["app", "frontend"],
|
|
276
|
+
scripts: {
|
|
277
|
+
// lower order runs first in sequenced script execution
|
|
278
|
+
build: { order: 1 },
|
|
279
|
+
test: { order: 2 },
|
|
280
|
+
},
|
|
281
|
+
rules: {
|
|
282
|
+
workspaceDependencies: {
|
|
283
|
+
// Only "my-workspace" or workspaces tagged "lib" are allowed as dependencies
|
|
284
|
+
allowPatterns: ["tag:lib", "my-workspace"],
|
|
285
|
+
// Workspaces tagged "backend" are forbidden as dependencies
|
|
286
|
+
denyPatterns: ["tag:backend"],
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
`.trim();
|
|
291
|
+
|
|
218
292
|
const SERVER_INSTRUCTIONS = `
|
|
219
293
|
bun-workspaces MCP server: tools to query Bun monorepo workspace metadata and documentation resources for the bun-workspaces CLI and TypeScript API.
|
|
220
294
|
|
|
@@ -235,6 +309,18 @@ ${CLI_QUICKSTART}
|
|
|
235
309
|
\`\`\`typescript
|
|
236
310
|
${API_QUICKSTART}
|
|
237
311
|
\`\`\`
|
|
312
|
+
|
|
313
|
+
## Root config quickstart
|
|
314
|
+
|
|
315
|
+
\`\`\`typescript
|
|
316
|
+
${ROOT_CONFIG_QUICKSTART}
|
|
317
|
+
\`\`\`
|
|
318
|
+
|
|
319
|
+
## Workspace config quickstart
|
|
320
|
+
|
|
321
|
+
\`\`\`typescript
|
|
322
|
+
${WORKSPACE_CONFIG_QUICKSTART}
|
|
323
|
+
\`\`\`
|
|
238
324
|
`.trim();
|
|
239
325
|
const startBwMcpServer = async (project) => {
|
|
240
326
|
const server = createMcpServer({
|
package/src/cli/index.d.ts
CHANGED
|
@@ -25,7 +25,18 @@ export type ParallelMaxValue =
|
|
|
25
25
|
| (typeof PARALLEL_MAX_VALUES)[number]
|
|
26
26
|
| PercentageValue;
|
|
27
27
|
export type WorkspaceDependenciesRule = {
|
|
28
|
+
/**
|
|
29
|
+
* Workspace patterns that are permitted as dependencies.
|
|
30
|
+
* Only workspaces matching these patterns are allowed.
|
|
31
|
+
* When combined with `denyPatterns`, the deny list further
|
|
32
|
+
* filters this allowed subset.
|
|
33
|
+
*/
|
|
28
34
|
allowPatterns?: string[];
|
|
35
|
+
/**
|
|
36
|
+
* Workspace patterns that are forbidden as dependencies.
|
|
37
|
+
* When combined with `allowPatterns`, this further filters
|
|
38
|
+
* the subset of allowed workspaces.
|
|
39
|
+
*/
|
|
29
40
|
denyPatterns?: string[];
|
|
30
41
|
};
|
|
31
42
|
export type WorkspaceRules = {
|
|
@@ -41,12 +52,60 @@ export type ScriptConfig = {
|
|
|
41
52
|
*/
|
|
42
53
|
order?: number;
|
|
43
54
|
};
|
|
55
|
+
/** Configuration that applies to a specific workspace */
|
|
56
|
+
export type WorkspaceConfig = {
|
|
57
|
+
/**
|
|
58
|
+
* An alias or list of aliases for the workspace.
|
|
59
|
+
*
|
|
60
|
+
* These must be unique to other workspaces' aliases
|
|
61
|
+
* and package.json names.
|
|
62
|
+
*/
|
|
63
|
+
alias?: string | string[];
|
|
64
|
+
/**
|
|
65
|
+
* Tags for the workspace.
|
|
66
|
+
*
|
|
67
|
+
* These can be used to group workspaces by a common tag.
|
|
68
|
+
*/
|
|
69
|
+
tags?: string[];
|
|
70
|
+
/**
|
|
71
|
+
* Configuration that maps to a script name in the workspace's package.json.
|
|
72
|
+
*/
|
|
73
|
+
scripts?: Record<string, ScriptConfig>;
|
|
74
|
+
/**
|
|
75
|
+
* Rules that validate the workspace.
|
|
76
|
+
*/
|
|
77
|
+
rules?: WorkspaceRules;
|
|
78
|
+
};
|
|
44
79
|
export type ResolvedWorkspaceConfig = {
|
|
45
80
|
aliases: string[];
|
|
46
81
|
tags: string[];
|
|
47
82
|
scripts: Record<string, ScriptConfig>;
|
|
48
83
|
rules: WorkspaceRules;
|
|
49
84
|
};
|
|
85
|
+
/** Static workspace context passed to a {@link WorkspacePatternConfigFactory}.
|
|
86
|
+
* Contains only the immutable, package.json-derived fields — not config-derived fields like aliases or tags. */
|
|
87
|
+
export type RawWorkspace = {
|
|
88
|
+
name: string;
|
|
89
|
+
isRoot: boolean;
|
|
90
|
+
path: string;
|
|
91
|
+
matchPattern: string;
|
|
92
|
+
scripts: string[];
|
|
93
|
+
dependencies: string[];
|
|
94
|
+
dependents: string[];
|
|
95
|
+
};
|
|
96
|
+
/** A factory that returns a {@link WorkspaceConfig} to merge for a matched workspace.
|
|
97
|
+
* Receives the static workspace context and the workspace's accumulated resolved config at that point. */
|
|
98
|
+
export type WorkspacePatternConfigFactory = (
|
|
99
|
+
workspace: RawWorkspace,
|
|
100
|
+
prevConfig: ResolvedWorkspaceConfig,
|
|
101
|
+
) => WorkspaceConfig;
|
|
102
|
+
/** A single entry in {@link RootConfig.workspacePatternConfigs} */
|
|
103
|
+
export type WorkspacePatternConfigEntry = {
|
|
104
|
+
/** Workspace patterns to match. Supports all workspace pattern specifiers (name, alias, tag, path, not:). */
|
|
105
|
+
patterns: string[];
|
|
106
|
+
/** Config to merge into all matching workspaces. May be a factory receiving the workspace context and accumulated config. */
|
|
107
|
+
config: WorkspaceConfig | WorkspacePatternConfigFactory;
|
|
108
|
+
};
|
|
50
109
|
export type ResolvedRootConfig = {
|
|
51
110
|
defaults: {
|
|
52
111
|
parallelMax: number;
|
|
@@ -54,6 +113,7 @@ export type ResolvedRootConfig = {
|
|
|
54
113
|
/** `undefined` means the value was not set in the input config */
|
|
55
114
|
includeRootWorkspace: boolean | undefined;
|
|
56
115
|
};
|
|
116
|
+
workspacePatternConfigs: WorkspacePatternConfigEntry[];
|
|
57
117
|
};
|
|
58
118
|
export type OutputStreamName = "stdout" | "stderr";
|
|
59
119
|
export type ProcessOutputChunk<
|
|
@@ -251,7 +311,7 @@ export type CreateFileSystemProjectOptions = {
|
|
|
251
311
|
export type InlineScriptOptions = {
|
|
252
312
|
/** A name to act as a label for the inline script */
|
|
253
313
|
scriptName?: string;
|
|
254
|
-
/** Whether to
|
|
314
|
+
/** Whether to use the system shell or Bun shell */
|
|
255
315
|
shell?: ShellOption;
|
|
256
316
|
};
|
|
257
317
|
/** Arguments for `FileSystemProject.runWorkspaceScript` */
|
package/src/config/public.d.ts
CHANGED
|
@@ -19,7 +19,18 @@ export type ParallelMaxValue =
|
|
|
19
19
|
| (typeof PARALLEL_MAX_VALUES)[number]
|
|
20
20
|
| PercentageValue;
|
|
21
21
|
export type WorkspaceDependenciesRule = {
|
|
22
|
+
/**
|
|
23
|
+
* Workspace patterns that are permitted as dependencies.
|
|
24
|
+
* Only workspaces matching these patterns are allowed.
|
|
25
|
+
* When combined with `denyPatterns`, the deny list further
|
|
26
|
+
* filters this allowed subset.
|
|
27
|
+
*/
|
|
22
28
|
allowPatterns?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Workspace patterns that are forbidden as dependencies.
|
|
31
|
+
* When combined with `allowPatterns`, this further filters
|
|
32
|
+
* the subset of allowed workspaces.
|
|
33
|
+
*/
|
|
23
34
|
denyPatterns?: string[];
|
|
24
35
|
};
|
|
25
36
|
export type WorkspaceRules = {
|
|
@@ -65,6 +76,30 @@ export type ResolvedWorkspaceConfig = {
|
|
|
65
76
|
scripts: Record<string, ScriptConfig>;
|
|
66
77
|
rules: WorkspaceRules;
|
|
67
78
|
};
|
|
79
|
+
/** Static workspace context passed to a {@link WorkspacePatternConfigFactory}.
|
|
80
|
+
* Contains only the immutable, package.json-derived fields — not config-derived fields like aliases or tags. */
|
|
81
|
+
export type RawWorkspace = {
|
|
82
|
+
name: string;
|
|
83
|
+
isRoot: boolean;
|
|
84
|
+
path: string;
|
|
85
|
+
matchPattern: string;
|
|
86
|
+
scripts: string[];
|
|
87
|
+
dependencies: string[];
|
|
88
|
+
dependents: string[];
|
|
89
|
+
};
|
|
90
|
+
/** A factory that returns a {@link WorkspaceConfig} to merge for a matched workspace.
|
|
91
|
+
* Receives the static workspace context and the workspace's accumulated resolved config at that point. */
|
|
92
|
+
export type WorkspacePatternConfigFactory = (
|
|
93
|
+
workspace: RawWorkspace,
|
|
94
|
+
prevConfig: ResolvedWorkspaceConfig,
|
|
95
|
+
) => WorkspaceConfig;
|
|
96
|
+
/** A single entry in {@link RootConfig.workspacePatternConfigs} */
|
|
97
|
+
export type WorkspacePatternConfigEntry = {
|
|
98
|
+
/** Workspace patterns to match. Supports all workspace pattern specifiers (name, alias, tag, path, not:). */
|
|
99
|
+
patterns: string[];
|
|
100
|
+
/** Config to merge into all matching workspaces. May be a factory receiving the workspace context and accumulated config. */
|
|
101
|
+
config: WorkspaceConfig | WorkspacePatternConfigFactory;
|
|
102
|
+
};
|
|
68
103
|
export type RootConfig = {
|
|
69
104
|
defaults?: {
|
|
70
105
|
/** The maximum number of scripts that can run in parallel. (default: "auto") */
|
|
@@ -74,6 +109,14 @@ export type RootConfig = {
|
|
|
74
109
|
/** Whether to include the root workspace in the workspaces list by default. (default: false) */
|
|
75
110
|
includeRootWorkspace?: boolean;
|
|
76
111
|
};
|
|
112
|
+
/**
|
|
113
|
+
* Workspace configs applied by pattern, in order, merging left to right,
|
|
114
|
+
* using any workspaces' local configs as the starting config.
|
|
115
|
+
* Each entry's config is merged into all workspaces matching its patterns.
|
|
116
|
+
* Pattern matching reflects accumulated aliases and tags from previous entries.
|
|
117
|
+
* Factory functions are only supported in TypeScript/JavaScript config files.
|
|
118
|
+
*/
|
|
119
|
+
workspacePatternConfigs?: WorkspacePatternConfigEntry[];
|
|
77
120
|
};
|
|
78
121
|
export type ResolvedRootConfig = {
|
|
79
122
|
defaults: {
|
|
@@ -82,12 +125,27 @@ export type ResolvedRootConfig = {
|
|
|
82
125
|
/** `undefined` means the value was not set in the input config */
|
|
83
126
|
includeRootWorkspace: boolean | undefined;
|
|
84
127
|
};
|
|
128
|
+
workspacePatternConfigs: WorkspacePatternConfigEntry[];
|
|
85
129
|
};
|
|
86
130
|
export declare const defineRootConfig: (
|
|
87
131
|
config: RootConfig,
|
|
88
132
|
) => ResolvedRootConfig;
|
|
133
|
+
export type RootConfigFactory = (prev: RootConfig) => RootConfig;
|
|
134
|
+
export type RootConfigInput = RootConfig | RootConfigFactory;
|
|
135
|
+
/** Merge two or more root configs left to right, with each subsequent config taking precedence.
|
|
136
|
+
* Any argument may be a factory function receiving the accumulated config up to that point. */
|
|
137
|
+
export declare const mergeRootConfig: (
|
|
138
|
+
...configs: RootConfigInput[]
|
|
139
|
+
) => RootConfig;
|
|
89
140
|
export declare const defineWorkspaceConfig: (
|
|
90
141
|
config: WorkspaceConfig,
|
|
91
142
|
) => ResolvedWorkspaceConfig;
|
|
143
|
+
export type WorkspaceConfigFactory = (prev: WorkspaceConfig) => WorkspaceConfig;
|
|
144
|
+
export type WorkspaceConfigInput = WorkspaceConfig | WorkspaceConfigFactory;
|
|
145
|
+
/** Merge two or more workspace configs left to right, with each subsequent config taking precedence.
|
|
146
|
+
* Any argument may be a factory function receiving the accumulated config up to that point. */
|
|
147
|
+
export declare const mergeWorkspaceConfig: (
|
|
148
|
+
...configs: WorkspaceConfigInput[]
|
|
149
|
+
) => WorkspaceConfig;
|
|
92
150
|
|
|
93
151
|
export {};
|
package/src/config/public.mjs
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { defineRootConfig } from "./rootConfig/defineRootConfig.mjs";
|
|
2
|
+
export { mergeRootConfig } from "./rootConfig/mergeRootConfig.mjs";
|
|
2
3
|
export { defineWorkspaceConfig } from "./workspaceConfig/defineWorkspaceConfig.mjs";
|
|
4
|
+
export { mergeWorkspaceConfig } from "./workspaceConfig/mergeWorkspaceConfig.mjs";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Merge two or more root configs left to right, with each subsequent config taking precedence.
|
|
2
|
+
* Any argument may be a factory function receiving the accumulated config up to that point. */ const mergeRootConfig =
|
|
3
|
+
(...configs) =>
|
|
4
|
+
configs.reduce((acc, configOrFactory) => {
|
|
5
|
+
const config =
|
|
6
|
+
typeof configOrFactory === "function"
|
|
7
|
+
? configOrFactory(acc)
|
|
8
|
+
: configOrFactory;
|
|
9
|
+
const mergedPatternConfigs = [
|
|
10
|
+
...(acc.workspacePatternConfigs ?? []),
|
|
11
|
+
...(config.workspacePatternConfigs ?? []),
|
|
12
|
+
];
|
|
13
|
+
return {
|
|
14
|
+
defaults: {
|
|
15
|
+
...acc.defaults,
|
|
16
|
+
...config.defaults,
|
|
17
|
+
},
|
|
18
|
+
...(mergedPatternConfigs.length > 0 && {
|
|
19
|
+
workspacePatternConfigs: mergedPatternConfigs,
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
}, {});
|
|
23
|
+
|
|
24
|
+
export { mergeRootConfig };
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "../../runScript/index.mjs";
|
|
6
6
|
import { getUserEnvVar } from "../userEnvVars/index.mjs";
|
|
7
7
|
import { executeValidator } from "../util/validateConfig.mjs";
|
|
8
|
+
import { validateWorkspaceConfig } from "../workspaceConfig/workspaceConfig.mjs";
|
|
8
9
|
import { ROOT_CONFIG_ERRORS } from "./errors.mjs";
|
|
9
10
|
|
|
10
11
|
const rootConfig_validateRootConfig = (config) =>
|
|
@@ -17,6 +18,11 @@ const rootConfig_validateRootConfig = (config) =>
|
|
|
17
18
|
const createDefaultRootConfig = () => resolveRootConfig({});
|
|
18
19
|
const resolveRootConfig = (config) => {
|
|
19
20
|
rootConfig_validateRootConfig(config);
|
|
21
|
+
for (const entry of config.workspacePatternConfigs ?? []) {
|
|
22
|
+
if (typeof entry.config !== "function") {
|
|
23
|
+
validateWorkspaceConfig(entry.config);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
20
26
|
return {
|
|
21
27
|
defaults: {
|
|
22
28
|
parallelMax: determineParallelMax(
|
|
@@ -28,6 +34,7 @@ const resolveRootConfig = (config) => {
|
|
|
28
34
|
config.defaults?.includeRootWorkspace ??
|
|
29
35
|
getUserEnvVar("includeRootWorkspaceDefault") === "true",
|
|
30
36
|
},
|
|
37
|
+
workspacePatternConfigs: config.workspacePatternConfigs ?? [],
|
|
31
38
|
};
|
|
32
39
|
};
|
|
33
40
|
|
|
@@ -17,6 +17,25 @@ const ROOT_CONFIG_JSON_SCHEMA = {
|
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
|
+
workspacePatternConfigs: {
|
|
21
|
+
type: "array",
|
|
22
|
+
items: {
|
|
23
|
+
type: "object",
|
|
24
|
+
additionalProperties: false,
|
|
25
|
+
required: ["patterns", "config"],
|
|
26
|
+
properties: {
|
|
27
|
+
patterns: {
|
|
28
|
+
type: "array",
|
|
29
|
+
items: {
|
|
30
|
+
type: "string",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
// config may be a WorkspaceConfig object or a factory function (TS/JS configs only).
|
|
34
|
+
// Object form is validated separately via validateWorkspaceConfig after AJV.
|
|
35
|
+
config: {},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
20
39
|
},
|
|
21
40
|
};
|
|
22
41
|
let _validateSchemaType;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { resolveOptionalArray } from "../../internal/core/index.mjs";
|
|
2
|
+
|
|
3
|
+
const uniqueArray = (arr) => [...new Set(arr)];
|
|
4
|
+
const concatPatterns = (a, b) => {
|
|
5
|
+
if (!a?.length && !b?.length) return undefined;
|
|
6
|
+
return uniqueArray([...(a ?? []), ...(b ?? [])]);
|
|
7
|
+
};
|
|
8
|
+
const mergeWorkspaceDependenciesRule = (base, override) => {
|
|
9
|
+
if (!base && !override) return undefined;
|
|
10
|
+
const allowPatterns = concatPatterns(
|
|
11
|
+
base?.allowPatterns,
|
|
12
|
+
override?.allowPatterns,
|
|
13
|
+
);
|
|
14
|
+
const denyPatterns = concatPatterns(
|
|
15
|
+
base?.denyPatterns,
|
|
16
|
+
override?.denyPatterns,
|
|
17
|
+
);
|
|
18
|
+
return {
|
|
19
|
+
...(allowPatterns && {
|
|
20
|
+
allowPatterns,
|
|
21
|
+
}),
|
|
22
|
+
...(denyPatterns && {
|
|
23
|
+
denyPatterns,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
const mergeWorkspaceRules = (base, override) => {
|
|
28
|
+
const workspaceDependencies = mergeWorkspaceDependenciesRule(
|
|
29
|
+
base?.workspaceDependencies,
|
|
30
|
+
override?.workspaceDependencies,
|
|
31
|
+
);
|
|
32
|
+
return {
|
|
33
|
+
...(workspaceDependencies && {
|
|
34
|
+
workspaceDependencies,
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
const mergeScripts = (base, override) => {
|
|
39
|
+
if (!base && !override) return {};
|
|
40
|
+
if (!base) return override ?? {};
|
|
41
|
+
if (!override) return base;
|
|
42
|
+
const merged = {
|
|
43
|
+
...base,
|
|
44
|
+
};
|
|
45
|
+
for (const [key, value] of Object.entries(override)) {
|
|
46
|
+
merged[key] = base[key]
|
|
47
|
+
? {
|
|
48
|
+
...base[key],
|
|
49
|
+
...value,
|
|
50
|
+
}
|
|
51
|
+
: value;
|
|
52
|
+
}
|
|
53
|
+
return merged;
|
|
54
|
+
};
|
|
55
|
+
const applyConfig = (acc, config) => ({
|
|
56
|
+
alias: uniqueArray([
|
|
57
|
+
...resolveOptionalArray(acc.alias ?? []),
|
|
58
|
+
...resolveOptionalArray(config.alias ?? []),
|
|
59
|
+
]),
|
|
60
|
+
tags: uniqueArray([...(acc.tags ?? []), ...(config.tags ?? [])]),
|
|
61
|
+
scripts: mergeScripts(acc.scripts, config.scripts),
|
|
62
|
+
rules: mergeWorkspaceRules(acc.rules, config.rules),
|
|
63
|
+
});
|
|
64
|
+
/** Merge two or more workspace configs left to right, with each subsequent config taking precedence.
|
|
65
|
+
* Any argument may be a factory function receiving the accumulated config up to that point. */ const mergeWorkspaceConfig =
|
|
66
|
+
(...configs) =>
|
|
67
|
+
configs.reduce((acc, configOrFactory) => {
|
|
68
|
+
const config =
|
|
69
|
+
typeof configOrFactory === "function"
|
|
70
|
+
? configOrFactory(acc)
|
|
71
|
+
: configOrFactory;
|
|
72
|
+
return applyConfig(acc, config);
|
|
73
|
+
}, {});
|
|
74
|
+
|
|
75
|
+
export { mergeWorkspaceConfig };
|
|
@@ -13,14 +13,6 @@ const workspaceConfig_validateWorkspaceConfig = (config) => {
|
|
|
13
13
|
},
|
|
14
14
|
WORKSPACE_CONFIG_ERRORS.InvalidWorkspaceConfig,
|
|
15
15
|
);
|
|
16
|
-
if (
|
|
17
|
-
config.rules?.workspaceDependencies?.allowPatterns &&
|
|
18
|
-
config.rules?.workspaceDependencies?.denyPatterns
|
|
19
|
-
) {
|
|
20
|
-
throw new WORKSPACE_CONFIG_ERRORS.InvalidWorkspaceConfig(
|
|
21
|
-
"Cannot use both allowPatterns and denyPatterns in workspaceDependencies rule",
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
16
|
};
|
|
25
17
|
const resolveWorkspaceConfig = (config) => {
|
|
26
18
|
if (Array.isArray(config.aliases)) {
|
package/src/index.d.ts
CHANGED
|
@@ -28,7 +28,18 @@ export type ParallelMaxValue =
|
|
|
28
28
|
| (typeof PARALLEL_MAX_VALUES)[number]
|
|
29
29
|
| PercentageValue;
|
|
30
30
|
export type WorkspaceDependenciesRule = {
|
|
31
|
+
/**
|
|
32
|
+
* Workspace patterns that are permitted as dependencies.
|
|
33
|
+
* Only workspaces matching these patterns are allowed.
|
|
34
|
+
* When combined with `denyPatterns`, the deny list further
|
|
35
|
+
* filters this allowed subset.
|
|
36
|
+
*/
|
|
31
37
|
allowPatterns?: string[];
|
|
38
|
+
/**
|
|
39
|
+
* Workspace patterns that are forbidden as dependencies.
|
|
40
|
+
* When combined with `allowPatterns`, this further filters
|
|
41
|
+
* the subset of allowed workspaces.
|
|
42
|
+
*/
|
|
32
43
|
denyPatterns?: string[];
|
|
33
44
|
};
|
|
34
45
|
export type WorkspaceRules = {
|
|
@@ -74,6 +85,30 @@ export type ResolvedWorkspaceConfig = {
|
|
|
74
85
|
scripts: Record<string, ScriptConfig>;
|
|
75
86
|
rules: WorkspaceRules;
|
|
76
87
|
};
|
|
88
|
+
/** Static workspace context passed to a {@link WorkspacePatternConfigFactory}.
|
|
89
|
+
* Contains only the immutable, package.json-derived fields — not config-derived fields like aliases or tags. */
|
|
90
|
+
export type RawWorkspace = {
|
|
91
|
+
name: string;
|
|
92
|
+
isRoot: boolean;
|
|
93
|
+
path: string;
|
|
94
|
+
matchPattern: string;
|
|
95
|
+
scripts: string[];
|
|
96
|
+
dependencies: string[];
|
|
97
|
+
dependents: string[];
|
|
98
|
+
};
|
|
99
|
+
/** A factory that returns a {@link WorkspaceConfig} to merge for a matched workspace.
|
|
100
|
+
* Receives the static workspace context and the workspace's accumulated resolved config at that point. */
|
|
101
|
+
export type WorkspacePatternConfigFactory = (
|
|
102
|
+
workspace: RawWorkspace,
|
|
103
|
+
prevConfig: ResolvedWorkspaceConfig,
|
|
104
|
+
) => WorkspaceConfig;
|
|
105
|
+
/** A single entry in {@link RootConfig.workspacePatternConfigs} */
|
|
106
|
+
export type WorkspacePatternConfigEntry = {
|
|
107
|
+
/** Workspace patterns to match. Supports all workspace pattern specifiers (name, alias, tag, path, not:). */
|
|
108
|
+
patterns: string[];
|
|
109
|
+
/** Config to merge into all matching workspaces. May be a factory receiving the workspace context and accumulated config. */
|
|
110
|
+
config: WorkspaceConfig | WorkspacePatternConfigFactory;
|
|
111
|
+
};
|
|
77
112
|
export type RootConfig = {
|
|
78
113
|
defaults?: {
|
|
79
114
|
/** The maximum number of scripts that can run in parallel. (default: "auto") */
|
|
@@ -83,6 +118,14 @@ export type RootConfig = {
|
|
|
83
118
|
/** Whether to include the root workspace in the workspaces list by default. (default: false) */
|
|
84
119
|
includeRootWorkspace?: boolean;
|
|
85
120
|
};
|
|
121
|
+
/**
|
|
122
|
+
* Workspace configs applied by pattern, in order, merging left to right,
|
|
123
|
+
* using any workspaces' local configs as the starting config.
|
|
124
|
+
* Each entry's config is merged into all workspaces matching its patterns.
|
|
125
|
+
* Pattern matching reflects accumulated aliases and tags from previous entries.
|
|
126
|
+
* Factory functions are only supported in TypeScript/JavaScript config files.
|
|
127
|
+
*/
|
|
128
|
+
workspacePatternConfigs?: WorkspacePatternConfigEntry[];
|
|
86
129
|
};
|
|
87
130
|
export type ResolvedRootConfig = {
|
|
88
131
|
defaults: {
|
|
@@ -91,6 +134,7 @@ export type ResolvedRootConfig = {
|
|
|
91
134
|
/** `undefined` means the value was not set in the input config */
|
|
92
135
|
includeRootWorkspace: boolean | undefined;
|
|
93
136
|
};
|
|
137
|
+
workspacePatternConfigs: WorkspacePatternConfigEntry[];
|
|
94
138
|
};
|
|
95
139
|
export type OutputStreamName = "stdout" | "stderr";
|
|
96
140
|
export type ProcessOutputChunk<
|
|
@@ -288,7 +332,7 @@ export type CreateFileSystemProjectOptions = {
|
|
|
288
332
|
export type InlineScriptOptions = {
|
|
289
333
|
/** A name to act as a label for the inline script */
|
|
290
334
|
scriptName?: string;
|
|
291
|
-
/** Whether to
|
|
335
|
+
/** Whether to use the system shell or Bun shell */
|
|
292
336
|
shell?: ShellOption;
|
|
293
337
|
};
|
|
294
338
|
/** Arguments for `FileSystemProject.runWorkspaceScript` */
|
|
@@ -444,9 +488,23 @@ export declare const createMemoryProject: (
|
|
|
444
488
|
export declare const defineRootConfig: (
|
|
445
489
|
config: RootConfig,
|
|
446
490
|
) => ResolvedRootConfig;
|
|
491
|
+
export type RootConfigFactory = (prev: RootConfig) => RootConfig;
|
|
492
|
+
export type RootConfigInput = RootConfig | RootConfigFactory;
|
|
493
|
+
/** Merge two or more root configs left to right, with each subsequent config taking precedence.
|
|
494
|
+
* Any argument may be a factory function receiving the accumulated config up to that point. */
|
|
495
|
+
export declare const mergeRootConfig: (
|
|
496
|
+
...configs: RootConfigInput[]
|
|
497
|
+
) => RootConfig;
|
|
447
498
|
export declare const defineWorkspaceConfig: (
|
|
448
499
|
config: WorkspaceConfig,
|
|
449
500
|
) => ResolvedWorkspaceConfig;
|
|
501
|
+
export type WorkspaceConfigFactory = (prev: WorkspaceConfig) => WorkspaceConfig;
|
|
502
|
+
export type WorkspaceConfigInput = WorkspaceConfig | WorkspaceConfigFactory;
|
|
503
|
+
/** Merge two or more workspace configs left to right, with each subsequent config taking precedence.
|
|
504
|
+
* Any argument may be a factory function receiving the accumulated config up to that point. */
|
|
505
|
+
export declare const mergeWorkspaceConfig: (
|
|
506
|
+
...configs: WorkspaceConfigInput[]
|
|
507
|
+
) => WorkspaceConfig;
|
|
450
508
|
declare const LOG_LEVELS: readonly ["debug", "info", "warn", "error"];
|
|
451
509
|
export type LogLevel = (typeof LOG_LEVELS)[number];
|
|
452
510
|
export type LogLevelSetting = LogLevel | "silent";
|
|
@@ -199,10 +199,9 @@ project.runScriptAcrossWorkspaces({
|
|
|
199
199
|
\`\`\``;
|
|
200
200
|
const DOC_CONFIG = `## Root config
|
|
201
201
|
|
|
202
|
-
Optional project config can be placed in \`bw.root.jsonc\`/\`bw.root.json\` in the root directory.
|
|
202
|
+
Optional project config can be placed in \`bw.root.ts\`/\`bw.root.js\`/\`bw.root.jsonc\`/\`bw.root.json\` in the root directory, or in the \`"bw"\` key of \`package.json\`.
|
|
203
203
|
|
|
204
|
-
Config defaults here take precedence over environment variables
|
|
205
|
-
Explicit arguments to the CLI or API take precedence over all other settings.
|
|
204
|
+
Config defaults here take precedence over environment variables. Explicit CLI arguments or API options take precedence over all other settings.
|
|
206
205
|
|
|
207
206
|
\`\`\`jsonc
|
|
208
207
|
{
|
|
@@ -211,16 +210,33 @@ Explicit arguments to the CLI or API take precedence over all other settings.
|
|
|
211
210
|
"shell": "system", // "bun" or "system" (default "bun")
|
|
212
211
|
"includeRootWorkspace": true, // treat root package.json as a normal workspace
|
|
213
212
|
},
|
|
213
|
+
"workspacePatternConfigs": [
|
|
214
|
+
// see Workspace Pattern Configs section below
|
|
215
|
+
],
|
|
214
216
|
}
|
|
215
217
|
\`\`\`
|
|
216
218
|
|
|
219
|
+
### mergeRootConfig
|
|
220
|
+
|
|
221
|
+
\`mergeRootConfig\` merges multiple root configs left to right. Later configs take precedence for scalar fields. \`workspacePatternConfigs\` entries are concatenated. Any argument may be a factory function \`(prev: RootConfig) => RootConfig\`.
|
|
222
|
+
|
|
223
|
+
\`\`\`ts
|
|
224
|
+
import { mergeRootConfig } from "bun-workspaces/config";
|
|
225
|
+
|
|
226
|
+
export default mergeRootConfig(
|
|
227
|
+
{ defaults: { parallelMax: 4 } },
|
|
228
|
+
{ defaults: { shell: "system" } },
|
|
229
|
+
(prevConfig) => ({ defaults: { includeRootWorkspace: true } }),
|
|
230
|
+
);
|
|
231
|
+
\`\`\`
|
|
232
|
+
|
|
217
233
|
## Workspace config
|
|
218
234
|
|
|
219
|
-
Optional config can be placed in \`bw.workspace.jsonc\`/\`bw.workspace.json\` in a workspace directory.
|
|
235
|
+
Optional config can be placed in \`bw.workspace.ts\`/\`bw.workspace.js\`/\`bw.workspace.jsonc\`/\`bw.workspace.json\` in a workspace directory, or in the \`"bw"\` key of \`package.json\`.
|
|
220
236
|
|
|
221
|
-
Aliases must be unique to each workspace and
|
|
237
|
+
Aliases must be unique to each workspace and must not clash with other workspaces' \`package.json\` names.
|
|
222
238
|
|
|
223
|
-
Tags are strings to group workspaces together
|
|
239
|
+
Tags are strings to group workspaces together; they do not need to be unique.
|
|
224
240
|
|
|
225
241
|
\`\`\`jsonc
|
|
226
242
|
{
|
|
@@ -234,10 +250,11 @@ Tags are strings to group workspaces together that therefore don't need to be un
|
|
|
234
250
|
},
|
|
235
251
|
"rules": {
|
|
236
252
|
"workspaceDependencies": {
|
|
237
|
-
//
|
|
253
|
+
// allowPatterns: only workspaces matching these patterns are permitted as dependencies
|
|
238
254
|
"allowPatterns": ["my-allow-pattern-*"],
|
|
239
|
-
//
|
|
240
|
-
//
|
|
255
|
+
// denyPatterns: workspaces matching these patterns are forbidden as dependencies.
|
|
256
|
+
// When combined with allowPatterns, deny filters within the allowed subset.
|
|
257
|
+
"denyPatterns": ["my-deny-pattern-*"],
|
|
241
258
|
},
|
|
242
259
|
},
|
|
243
260
|
}
|
|
@@ -245,16 +262,77 @@ Tags are strings to group workspaces together that therefore don't need to be un
|
|
|
245
262
|
|
|
246
263
|
### Workspace Dependency Rules
|
|
247
264
|
|
|
248
|
-
Using the \`rules.workspaceDependencies\` field, you can define rules for which workspaces are allowed to be dependencies,
|
|
249
|
-
|
|
265
|
+
Using the \`rules.workspaceDependencies\` field, you can define rules for which workspaces are allowed to be dependencies, using \`allowPatterns\`, \`denyPatterns\`, or both.
|
|
266
|
+
|
|
267
|
+
\`allowPatterns\` defines the permitted subset of dependencies. \`denyPatterns\` forbids specific dependencies. When both are present, \`denyPatterns\` further filters within the subset permitted by \`allowPatterns\`.
|
|
250
268
|
|
|
251
269
|
Workspace Patterns are used to match workspaces.
|
|
252
270
|
|
|
253
|
-
|
|
271
|
+
### mergeWorkspaceConfig
|
|
254
272
|
|
|
255
|
-
|
|
273
|
+
\`mergeWorkspaceConfig\` merges multiple workspace configs left to right. Arrays (\`alias\`, \`tags\`, \`allowPatterns\`, \`denyPatterns\`) are concatenated and deduplicated. Scalar fields later wins. \`scripts\` are deep-merged per key. Any argument may be a factory function \`(prev: WorkspaceConfig) => WorkspaceConfig\`.
|
|
274
|
+
|
|
275
|
+
\`\`\`ts
|
|
276
|
+
import { mergeWorkspaceConfig } from "bun-workspaces/config";
|
|
277
|
+
|
|
278
|
+
export default mergeWorkspaceConfig(
|
|
279
|
+
{ alias: "a", tags: ["x"] },
|
|
280
|
+
{ alias: "b", scripts: { build: { order: 1 } } },
|
|
281
|
+
(prevConfig) => ({ tags: ["y"] }),
|
|
282
|
+
);
|
|
283
|
+
// result: { alias: ["a", "b"], tags: ["x", "y"], scripts: { build: { order: 1 } } }
|
|
284
|
+
\`\`\`
|
|
285
|
+
|
|
286
|
+
## Workspace Pattern Configs
|
|
287
|
+
|
|
288
|
+
The root config's \`workspacePatternConfigs\` field applies workspace configs to groups of workspaces matched by [workspace patterns](/concepts/workspace-patterns). Entries are applied in order, left to right.
|
|
289
|
+
|
|
290
|
+
Each entry's \`config\` is merged into the accumulated config of all matching workspaces using the same semantics as \`mergeWorkspaceConfig\`. The local workspace config (from \`bw.workspace.*\` or \`package.json\`) is always the starting base.
|
|
291
|
+
|
|
292
|
+
Pattern matching reflects the accumulated state: aliases and tags added by earlier entries are visible to later entries' patterns.
|
|
256
293
|
|
|
257
|
-
|
|
294
|
+
\`\`\`ts
|
|
295
|
+
import { defineRootConfig } from "bun-workspaces/config";
|
|
296
|
+
|
|
297
|
+
export default defineRootConfig({
|
|
298
|
+
workspacePatternConfigs: [
|
|
299
|
+
{
|
|
300
|
+
patterns: ["path:packages/apps/**/*"],
|
|
301
|
+
config: { tags: ["app"] },
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
// "tag:app" matches because the entry above added it
|
|
305
|
+
patterns: ["tag:app"],
|
|
306
|
+
config: {
|
|
307
|
+
rules: { workspaceDependencies: { allowPatterns: ["tag:lib"] } },
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
patterns: ["tag:app"],
|
|
312
|
+
// Factory form: JS/TS only — receives static workspace data and accumulated config
|
|
313
|
+
config: (workspace, prevConfig) => ({
|
|
314
|
+
alias: workspace.name.replace(/^@my-scope\\//, ""),
|
|
315
|
+
}),
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
});
|
|
319
|
+
\`\`\`
|
|
320
|
+
|
|
321
|
+
### Factory function context (\`RawWorkspace\`)
|
|
322
|
+
|
|
323
|
+
The factory \`(workspace: RawWorkspace, prevConfig: ResolvedWorkspaceConfig) => WorkspaceConfig\` receives:
|
|
324
|
+
|
|
325
|
+
- \`workspace.name\` — package name from package.json
|
|
326
|
+
- \`workspace.isRoot\` — whether this is the root workspace
|
|
327
|
+
- \`workspace.path\` — relative path from project root
|
|
328
|
+
- \`workspace.matchPattern\` — glob from root package.json \`workspaces\` field that matched
|
|
329
|
+
- \`workspace.scripts\` — sorted list of script names from package.json
|
|
330
|
+
- \`workspace.dependencies\` — names of workspace dependencies
|
|
331
|
+
- \`workspace.dependents\` — names of workspaces that depend on this one
|
|
332
|
+
|
|
333
|
+
\`prevConfig\` is the fully resolved workspace config at that point, including the local config and any configs applied by earlier pattern entries. It has \`aliases: string[]\`, \`tags: string[]\`, \`scripts: Record<string, ScriptConfig>\`, \`rules: WorkspaceRules\`.
|
|
334
|
+
|
|
335
|
+
## TypeScript/JSON Config Files
|
|
258
336
|
|
|
259
337
|
### TypeScript
|
|
260
338
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";module.exports = validate11;module.exports.default = validate11;const schema12 = {"type":"object","additionalProperties":false,"properties":{"defaults":{"type":"object","additionalProperties":false,"properties":{"parallelMax":{"type":["number","string"]},"shell":{"type":"string"},"includeRootWorkspace":{"type":"boolean"}}}}};function validate11(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){let vErrors = null;let errors = 0;if(errors === 0){if(data && typeof data == "object" && !Array.isArray(data)){const _errs1 = errors;for(const key0 in data){if(!(key0 === "defaults")){validate11.errors = [{instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"}];return false;break;}}if(_errs1 === errors){if(data.defaults !== undefined){let data0 = data.defaults;const _errs2 = errors;if(errors === _errs2){if(data0 && typeof data0 == "object" && !Array.isArray(data0)){const _errs4 = errors;for(const key1 in data0){if(!(((key1 === "parallelMax") || (key1 === "shell")) || (key1 === "includeRootWorkspace"))){validate11.errors = [{instancePath:instancePath+"/defaults",schemaPath:"#/properties/defaults/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"}];return false;break;}}if(_errs4 === errors){if(data0.parallelMax !== undefined){let data1 = data0.parallelMax;const _errs5 = errors;if((!((typeof data1 == "number") && (isFinite(data1)))) && (typeof data1 !== "string")){validate11.errors = [{instancePath:instancePath+"/defaults/parallelMax",schemaPath:"#/properties/defaults/properties/parallelMax/type",keyword:"type",params:{type: schema12.properties.defaults.properties.parallelMax.type},message:"must be number,string"}];return false;}var valid1 = _errs5 === errors;}else {var valid1 = true;}if(valid1){if(data0.shell !== undefined){const _errs7 = errors;if(typeof data0.shell !== "string"){validate11.errors = [{instancePath:instancePath+"/defaults/shell",schemaPath:"#/properties/defaults/properties/shell/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid1 = _errs7 === errors;}else {var valid1 = true;}if(valid1){if(data0.includeRootWorkspace !== undefined){const _errs9 = errors;if(typeof data0.includeRootWorkspace !== "boolean"){validate11.errors = [{instancePath:instancePath+"/defaults/includeRootWorkspace",schemaPath:"#/properties/defaults/properties/includeRootWorkspace/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];return false;}var valid1 = _errs9 === errors;}else {var valid1 = true;}}}}}else {validate11.errors = [{instancePath:instancePath+"/defaults",schemaPath:"#/properties/defaults/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}}}}else {validate11.errors = [{instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}validate11.errors = vErrors;return errors === 0;}
|
|
1
|
+
"use strict";module.exports = validate11;module.exports.default = validate11;const schema12 = {"type":"object","additionalProperties":false,"properties":{"defaults":{"type":"object","additionalProperties":false,"properties":{"parallelMax":{"type":["number","string"]},"shell":{"type":"string"},"includeRootWorkspace":{"type":"boolean"}}},"workspacePatternConfigs":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["patterns","config"],"properties":{"patterns":{"type":"array","items":{"type":"string"}},"config":{}}}}}};function validate11(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){let vErrors = null;let errors = 0;if(errors === 0){if(data && typeof data == "object" && !Array.isArray(data)){const _errs1 = errors;for(const key0 in data){if(!((key0 === "defaults") || (key0 === "workspacePatternConfigs"))){validate11.errors = [{instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"}];return false;break;}}if(_errs1 === errors){if(data.defaults !== undefined){let data0 = data.defaults;const _errs2 = errors;if(errors === _errs2){if(data0 && typeof data0 == "object" && !Array.isArray(data0)){const _errs4 = errors;for(const key1 in data0){if(!(((key1 === "parallelMax") || (key1 === "shell")) || (key1 === "includeRootWorkspace"))){validate11.errors = [{instancePath:instancePath+"/defaults",schemaPath:"#/properties/defaults/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"}];return false;break;}}if(_errs4 === errors){if(data0.parallelMax !== undefined){let data1 = data0.parallelMax;const _errs5 = errors;if((!((typeof data1 == "number") && (isFinite(data1)))) && (typeof data1 !== "string")){validate11.errors = [{instancePath:instancePath+"/defaults/parallelMax",schemaPath:"#/properties/defaults/properties/parallelMax/type",keyword:"type",params:{type: schema12.properties.defaults.properties.parallelMax.type},message:"must be number,string"}];return false;}var valid1 = _errs5 === errors;}else {var valid1 = true;}if(valid1){if(data0.shell !== undefined){const _errs7 = errors;if(typeof data0.shell !== "string"){validate11.errors = [{instancePath:instancePath+"/defaults/shell",schemaPath:"#/properties/defaults/properties/shell/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid1 = _errs7 === errors;}else {var valid1 = true;}if(valid1){if(data0.includeRootWorkspace !== undefined){const _errs9 = errors;if(typeof data0.includeRootWorkspace !== "boolean"){validate11.errors = [{instancePath:instancePath+"/defaults/includeRootWorkspace",schemaPath:"#/properties/defaults/properties/includeRootWorkspace/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];return false;}var valid1 = _errs9 === errors;}else {var valid1 = true;}}}}}else {validate11.errors = [{instancePath:instancePath+"/defaults",schemaPath:"#/properties/defaults/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}var valid0 = _errs2 === errors;}else {var valid0 = true;}if(valid0){if(data.workspacePatternConfigs !== undefined){let data4 = data.workspacePatternConfigs;const _errs11 = errors;if(errors === _errs11){if(Array.isArray(data4)){var valid2 = true;const len0 = data4.length;for(let i0=0; i0<len0; i0++){let data5 = data4[i0];const _errs13 = errors;if(errors === _errs13){if(data5 && typeof data5 == "object" && !Array.isArray(data5)){let missing0;if(((data5.patterns === undefined) && (missing0 = "patterns")) || ((data5.config === undefined) && (missing0 = "config"))){validate11.errors = [{instancePath:instancePath+"/workspacePatternConfigs/" + i0,schemaPath:"#/properties/workspacePatternConfigs/items/required",keyword:"required",params:{missingProperty: missing0},message:"must have required property '"+missing0+"'"}];return false;}else {const _errs15 = errors;for(const key2 in data5){if(!((key2 === "patterns") || (key2 === "config"))){validate11.errors = [{instancePath:instancePath+"/workspacePatternConfigs/" + i0,schemaPath:"#/properties/workspacePatternConfigs/items/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key2},message:"must NOT have additional properties"}];return false;break;}}if(_errs15 === errors){if(data5.patterns !== undefined){let data6 = data5.patterns;const _errs16 = errors;if(errors === _errs16){if(Array.isArray(data6)){var valid4 = true;const len1 = data6.length;for(let i1=0; i1<len1; i1++){const _errs18 = errors;if(typeof data6[i1] !== "string"){validate11.errors = [{instancePath:instancePath+"/workspacePatternConfigs/" + i0+"/patterns/" + i1,schemaPath:"#/properties/workspacePatternConfigs/items/properties/patterns/items/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid4 = _errs18 === errors;if(!valid4){break;}}}else {validate11.errors = [{instancePath:instancePath+"/workspacePatternConfigs/" + i0+"/patterns",schemaPath:"#/properties/workspacePatternConfigs/items/properties/patterns/type",keyword:"type",params:{type: "array"},message:"must be array"}];return false;}}}}}}else {validate11.errors = [{instancePath:instancePath+"/workspacePatternConfigs/" + i0,schemaPath:"#/properties/workspacePatternConfigs/items/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}var valid2 = _errs13 === errors;if(!valid2){break;}}}else {validate11.errors = [{instancePath:instancePath+"/workspacePatternConfigs",schemaPath:"#/properties/workspacePatternConfigs/type",keyword:"type",params:{type: "array"},message:"must be array"}];return false;}}var valid0 = _errs11 === errors;}else {var valid0 = true;}}}}else {validate11.errors = [{instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}validate11.errors = vErrors;return errors === 0;}
|
|
@@ -111,6 +111,7 @@ class _FileSystemProject extends ProjectBase {
|
|
|
111
111
|
options.includeRootWorkspace ??
|
|
112
112
|
rootConfig.defaults.includeRootWorkspace ??
|
|
113
113
|
getUserEnvVar("includeRootWorkspaceDefault") === "true",
|
|
114
|
+
workspacePatternConfigs: rootConfig.workspacePatternConfigs,
|
|
114
115
|
});
|
|
115
116
|
this.rootWorkspace = rootWorkspace;
|
|
116
117
|
this.workspaces = workspaces;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v9.5.1
|
|
2
|
+
|
|
3
|
+
declare const SCRIPT_RUNTIME_METADATA_CONFIG: {
|
|
4
|
+
readonly projectPath: {
|
|
5
|
+
readonly inlineName: "<projectPath>";
|
|
6
|
+
readonly envVarName: "BW_PROJECT_PATH";
|
|
7
|
+
};
|
|
8
|
+
readonly projectName: {
|
|
9
|
+
readonly inlineName: "<projectName>";
|
|
10
|
+
readonly envVarName: "BW_PROJECT_NAME";
|
|
11
|
+
};
|
|
12
|
+
readonly workspacePath: {
|
|
13
|
+
readonly inlineName: "<workspacePath>";
|
|
14
|
+
readonly envVarName: "BW_WORKSPACE_PATH";
|
|
15
|
+
};
|
|
16
|
+
readonly workspaceRelativePath: {
|
|
17
|
+
readonly inlineName: "<workspaceRelativePath>";
|
|
18
|
+
readonly envVarName: "BW_WORKSPACE_RELATIVE_PATH";
|
|
19
|
+
};
|
|
20
|
+
readonly scriptName: {
|
|
21
|
+
readonly inlineName: "<scriptName>";
|
|
22
|
+
readonly envVarName: "BW_SCRIPT_NAME";
|
|
23
|
+
};
|
|
24
|
+
readonly workspaceName: {
|
|
25
|
+
readonly inlineName: "<workspaceName>";
|
|
26
|
+
readonly envVarName: "BW_WORKSPACE_NAME";
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export type ScriptRuntimeMetadataKey =
|
|
30
|
+
keyof typeof SCRIPT_RUNTIME_METADATA_CONFIG;
|
|
31
|
+
/**
|
|
32
|
+
* This is a utility to run from a workspace's script that was called via `bun-workspaces`.
|
|
33
|
+
*
|
|
34
|
+
* It gets the value of some metadata value about the project, workspace, or script that was invoked.
|
|
35
|
+
*/
|
|
36
|
+
export declare const getWorkspaceScriptMetadata: (
|
|
37
|
+
key: ScriptRuntimeMetadataKey,
|
|
38
|
+
) => string;
|
|
39
|
+
|
|
40
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getWorkspaceScriptMetadata } from "./scriptRuntimeMetadata.mjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IS_WINDOWS } from "../internal/core/index.mjs";
|
|
1
|
+
import { BunWorkspacesError, IS_WINDOWS } from "../internal/core/index.mjs";
|
|
2
2
|
import { getScriptRuntimeMetadataConfig } from "../1108.mjs";
|
|
3
3
|
|
|
4
4
|
const createScriptRuntimeEnvVars = (metadata) => {
|
|
@@ -39,5 +39,22 @@ const interpolateScriptRuntimeMetadata = (text, metadata, shell) => {
|
|
|
39
39
|
return value;
|
|
40
40
|
});
|
|
41
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* This is a utility to run from a workspace's script that was called via `bun-workspaces`.
|
|
44
|
+
*
|
|
45
|
+
* It gets the value of some metadata value about the project, workspace, or script that was invoked.
|
|
46
|
+
*/ const getWorkspaceScriptMetadata = (key) => {
|
|
47
|
+
const { envVarName } = getScriptRuntimeMetadataConfig(key);
|
|
48
|
+
if (!(envVarName in process.env)) {
|
|
49
|
+
throw new BunWorkspacesError(
|
|
50
|
+
`getScriptMetadata() called with key "${key}" but environment variable ${envVarName} is not set. getScriptMetadata() may not have been called in a workspace script running via bun-workspaces.`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return process.env[envVarName];
|
|
54
|
+
};
|
|
42
55
|
|
|
43
|
-
export {
|
|
56
|
+
export {
|
|
57
|
+
createScriptRuntimeEnvVars,
|
|
58
|
+
getWorkspaceScriptMetadata,
|
|
59
|
+
interpolateScriptRuntimeMetadata,
|
|
60
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mergeWorkspaceConfig,
|
|
3
|
+
resolveWorkspaceConfig,
|
|
4
|
+
} from "../config/index.mjs";
|
|
5
|
+
import { matchWorkspacesByPatterns } from "./workspacePattern.mjs";
|
|
6
|
+
|
|
7
|
+
const resolvedToWorkspaceConfig = ({ aliases, tags, scripts, rules }) => ({
|
|
8
|
+
alias: aliases,
|
|
9
|
+
tags,
|
|
10
|
+
scripts,
|
|
11
|
+
rules,
|
|
12
|
+
});
|
|
13
|
+
const makeContext = (workspace) => ({
|
|
14
|
+
name: workspace.name,
|
|
15
|
+
isRoot: workspace.isRoot,
|
|
16
|
+
path: workspace.path,
|
|
17
|
+
matchPattern: workspace.matchPattern,
|
|
18
|
+
scripts: workspace.scripts,
|
|
19
|
+
dependencies: workspace.dependencies,
|
|
20
|
+
dependents: workspace.dependents,
|
|
21
|
+
});
|
|
22
|
+
const applyWorkspacePatternConfigs = (
|
|
23
|
+
workspaces,
|
|
24
|
+
workspaceMap,
|
|
25
|
+
workspaceAliases,
|
|
26
|
+
patternConfigs,
|
|
27
|
+
) => {
|
|
28
|
+
for (const entry of patternConfigs) {
|
|
29
|
+
const matched = matchWorkspacesByPatterns(entry.patterns, workspaces);
|
|
30
|
+
for (const workspace of matched) {
|
|
31
|
+
const mapEntry = workspaceMap[workspace.name];
|
|
32
|
+
const prevConfig = mapEntry.config;
|
|
33
|
+
const configToMerge =
|
|
34
|
+
typeof entry.config === "function"
|
|
35
|
+
? entry.config(makeContext(workspace), prevConfig)
|
|
36
|
+
: entry.config;
|
|
37
|
+
const resolved = resolveWorkspaceConfig(
|
|
38
|
+
mergeWorkspaceConfig(
|
|
39
|
+
resolvedToWorkspaceConfig(prevConfig),
|
|
40
|
+
configToMerge,
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
// Register any new aliases for validation
|
|
44
|
+
const previousAliases = new Set(workspace.aliases);
|
|
45
|
+
for (const alias of resolved.aliases) {
|
|
46
|
+
if (!previousAliases.has(alias)) {
|
|
47
|
+
workspaceAliases[alias] = workspace.name;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Update workspace object so subsequent pattern entries see accumulated aliases/tags
|
|
51
|
+
workspace.aliases = resolved.aliases;
|
|
52
|
+
workspace.tags = resolved.tags;
|
|
53
|
+
mapEntry.config = resolved;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export { applyWorkspacePatternConfigs };
|
|
@@ -32,16 +32,6 @@ const validateWorkspaceDependencyRules = ({ workspaceMap }) => {
|
|
|
32
32
|
const depWorkspace = workspaceMap[depName]?.workspace;
|
|
33
33
|
if (!depWorkspace) continue;
|
|
34
34
|
const chainStr = chain.join(" -> ");
|
|
35
|
-
if (rule.denyPatterns) {
|
|
36
|
-
const isDenied =
|
|
37
|
-
matchWorkspacesByPatterns(rule.denyPatterns, [depWorkspace]).length >
|
|
38
|
-
0;
|
|
39
|
-
if (isDenied) {
|
|
40
|
-
violations.push(
|
|
41
|
-
`"${workspaceName}" violates workspaceDependencies rule: workspace "${depName}" is denied by denyPatterns (dependency chain: ${chainStr})`,
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
35
|
if (rule.allowPatterns) {
|
|
46
36
|
const isAllowed =
|
|
47
37
|
matchWorkspacesByPatterns(rule.allowPatterns, [depWorkspace]).length >
|
|
@@ -50,6 +40,17 @@ const validateWorkspaceDependencyRules = ({ workspaceMap }) => {
|
|
|
50
40
|
violations.push(
|
|
51
41
|
`"${workspaceName}" violates workspaceDependencies rule: workspace "${depName}" is not permitted by allowPatterns (dependency chain: ${chainStr})`,
|
|
52
42
|
);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (rule.denyPatterns) {
|
|
47
|
+
const isDenied =
|
|
48
|
+
matchWorkspacesByPatterns(rule.denyPatterns, [depWorkspace]).length >
|
|
49
|
+
0;
|
|
50
|
+
if (isDenied) {
|
|
51
|
+
violations.push(
|
|
52
|
+
`"${workspaceName}" violates workspaceDependencies rule: workspace "${depName}" is denied by denyPatterns (dependency chain: ${chainStr})`,
|
|
53
|
+
);
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import { BUN_LOCK_ERRORS, readBunLockfile } from "../internal/bun/index.mjs";
|
|
9
9
|
import { BunWorkspacesError } from "../internal/core/index.mjs";
|
|
10
10
|
import { logger } from "../internal/logger/logger.mjs";
|
|
11
|
+
import { applyWorkspacePatternConfigs } from "./applyWorkspacePatternConfigs.mjs";
|
|
11
12
|
import {
|
|
12
13
|
resolveWorkspaceDependencies,
|
|
13
14
|
validateWorkspaceDependencyRules,
|
|
@@ -65,6 +66,7 @@ const findWorkspaces = ({
|
|
|
65
66
|
rootDirectory,
|
|
66
67
|
workspaceGlobs: _workspaceGlobs,
|
|
67
68
|
includeRootWorkspace = false,
|
|
69
|
+
workspacePatternConfigs,
|
|
68
70
|
}) => {
|
|
69
71
|
rootDirectory = path.resolve(rootDirectory);
|
|
70
72
|
logger.debug(`Finding workspaces in ${rootDirectory}`);
|
|
@@ -169,6 +171,14 @@ const findWorkspaces = ({
|
|
|
169
171
|
bunCatalogs,
|
|
170
172
|
),
|
|
171
173
|
);
|
|
174
|
+
if (workspacePatternConfigs?.length) {
|
|
175
|
+
applyWorkspacePatternConfigs(
|
|
176
|
+
workspaces,
|
|
177
|
+
workspaceMap,
|
|
178
|
+
workspaceAliases,
|
|
179
|
+
workspacePatternConfigs,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
172
182
|
validateWorkspaceDependencyRules({
|
|
173
183
|
workspaceMap,
|
|
174
184
|
});
|