bun-workspaces 1.9.0 → 1.11.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/AGENTS.md +537 -0
- package/README.md +4 -2
- package/package.json +1 -1
- package/src/2392.mjs +10 -1
- package/src/5166.mjs +1 -0
- package/src/affected/fileAffectedWorkspaces.mjs +7 -1
- package/src/affected/gitAffectedFiles.mjs +26 -1
- package/src/ai/mcp/bwMcpServer.mjs +5 -1
- package/src/ai/mcp/serverState.mjs +10 -1
- package/src/ai/mcp/tools.mjs +1 -1
- package/src/cli/commands/commandHandlerUtils.mjs +11 -10
- package/src/cli/commands/commands.mjs +1 -1
- package/src/cli/commands/handleSimpleCommands.mjs +7 -6
- package/src/cli/commands/listAffected.mjs +17 -13
- package/src/cli/commands/mcp.mjs +11 -1
- package/src/cli/commands/runScript/output/renderGroupedOutput.mjs +3 -2
- package/src/cli/commands/runScript/output/renderPlainOutput.mjs +4 -1
- package/src/cli/commands/runScript/scriptRunFlow.mjs +8 -3
- package/src/cli/createCli.mjs +8 -2
- package/src/cli/globalOptions/globalOptions.mjs +5 -4
- package/src/cli/index.d.ts +35 -2
- package/src/config/rootConfig/loadRootConfig.mjs +2 -1
- package/src/config/rootConfig/rootConfig.mjs +5 -9
- package/src/config/userEnvVars/userEnvVars.mjs +12 -1
- package/src/config/util/loadConfig.mjs +23 -2
- package/src/config/workspaceConfig/loadWorkspaceConfig.mjs +2 -1
- package/src/index.d.ts +11 -0
- package/src/internal/core/language/string/index.mjs +1 -0
- package/src/internal/core/language/string/sanitizeOutput.mjs +15 -0
- package/src/internal/core/runtime/tempFile.mjs +20 -2
- package/src/internal/generated/aiDocs/docs.mjs +19 -8
- package/src/project/implementations/fileSystemProject/affectedWorkspaces.mjs +2 -0
- package/src/project/implementations/fileSystemProject/fileSystemProject.mjs +39 -14
- package/src/project/implementations/projectBase.mjs +11 -17
- package/src/runScript/scriptExecution.mjs +1 -1
- package/src/runScript/workspaceScriptMetadata.mjs +24 -1
- package/src/workspaces/applyWorkspacePatternConfigs.mjs +6 -1
- package/src/workspaces/dependencyGraph/validateDependencyRules.mjs +14 -7
- package/src/workspaces/findWorkspaces.mjs +4 -0
- package/src/workspaces/workspacePattern.mjs +134 -46
|
@@ -12,95 +12,183 @@ const WORKSPACE_PATTERN_NEGATION_PREFIXES = [
|
|
|
12
12
|
WORKSPACE_PATTERN_NEGATION_SHORT_PREFIX,
|
|
13
13
|
];
|
|
14
14
|
const WORKSPACE_PATTERN_SEPARATOR = ":";
|
|
15
|
+
const WORKSPACE_PATTERN_REGEX_PREFIX = "re:";
|
|
16
|
+
const validateRegexSource = (source, originalPattern) => {
|
|
17
|
+
try {
|
|
18
|
+
new RegExp(source);
|
|
19
|
+
} catch (cause) {
|
|
20
|
+
throw new WORKSPACE_PATTERN_ERRORS.InvalidWorkspacePattern(
|
|
21
|
+
`Invalid regex in workspace pattern "${originalPattern}": ${cause.message}`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
15
25
|
const parseWorkspacePattern = (pattern) => {
|
|
16
26
|
const negationPrefix = WORKSPACE_PATTERN_NEGATION_PREFIXES.find((prefix) =>
|
|
17
27
|
pattern.startsWith(prefix),
|
|
18
28
|
);
|
|
19
29
|
const isNegated = !!negationPrefix;
|
|
20
|
-
const
|
|
30
|
+
const afterNegation = negationPrefix
|
|
21
31
|
? pattern.slice(negationPrefix.length)
|
|
22
32
|
: pattern;
|
|
33
|
+
// The "@root" selector resolves to the project's root workspace. Recognized
|
|
34
|
+
// immediately after optional negation, so "not:@root" / "!@root" also work.
|
|
35
|
+
// A target-scoped value of "@root" (e.g. "name:@root") is treated as a literal,
|
|
36
|
+
// not a root selector.
|
|
37
|
+
if (afterNegation === /* inlined export .ROOT_WORKSPACE_SELECTOR */ "@root") {
|
|
38
|
+
return {
|
|
39
|
+
target: "default",
|
|
40
|
+
value: /* inlined export .ROOT_WORKSPACE_SELECTOR */ "@root",
|
|
41
|
+
isNegated,
|
|
42
|
+
isRegex: false,
|
|
43
|
+
isRootSelector: true,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// "re:" before any target consumes the rest as a regex against the default target.
|
|
47
|
+
// e.g. "re:path:foo" → default-target regex over literal source "path:foo".
|
|
48
|
+
if (afterNegation.startsWith(WORKSPACE_PATTERN_REGEX_PREFIX)) {
|
|
49
|
+
const value = afterNegation.slice(WORKSPACE_PATTERN_REGEX_PREFIX.length);
|
|
50
|
+
validateRegexSource(value, pattern);
|
|
51
|
+
return {
|
|
52
|
+
target: "default",
|
|
53
|
+
value,
|
|
54
|
+
isNegated,
|
|
55
|
+
isRegex: true,
|
|
56
|
+
isRootSelector: false,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
23
59
|
const target = TARGETS.find((target) =>
|
|
24
|
-
|
|
60
|
+
afterNegation.startsWith(target + WORKSPACE_PATTERN_SEPARATOR),
|
|
25
61
|
);
|
|
26
62
|
if (!target) {
|
|
27
63
|
return {
|
|
28
64
|
target: "default",
|
|
29
|
-
value:
|
|
65
|
+
value: afterNegation,
|
|
30
66
|
isNegated,
|
|
67
|
+
isRegex: false,
|
|
68
|
+
isRootSelector: false,
|
|
31
69
|
};
|
|
32
70
|
}
|
|
33
|
-
const
|
|
71
|
+
const afterTarget = afterNegation.slice(
|
|
34
72
|
target.length + WORKSPACE_PATTERN_SEPARATOR.length,
|
|
35
73
|
);
|
|
74
|
+
if (afterTarget.startsWith(WORKSPACE_PATTERN_REGEX_PREFIX)) {
|
|
75
|
+
const value = afterTarget.slice(WORKSPACE_PATTERN_REGEX_PREFIX.length);
|
|
76
|
+
validateRegexSource(value, pattern);
|
|
77
|
+
return {
|
|
78
|
+
target,
|
|
79
|
+
value,
|
|
80
|
+
isNegated,
|
|
81
|
+
isRegex: true,
|
|
82
|
+
isRootSelector: false,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
36
85
|
return {
|
|
37
86
|
target,
|
|
38
|
-
value,
|
|
87
|
+
value: afterTarget,
|
|
39
88
|
isNegated,
|
|
89
|
+
isRegex: false,
|
|
90
|
+
isRootSelector: false,
|
|
40
91
|
};
|
|
41
92
|
};
|
|
42
|
-
const stringifyWorkspacePattern = (pattern) => {
|
|
43
|
-
return `${pattern.target}${WORKSPACE_PATTERN_SEPARATOR}${pattern.value}`;
|
|
44
|
-
};
|
|
45
93
|
const PATTERN_TARGET_HANDLERS = {
|
|
46
|
-
default: (pattern, workspaces
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
94
|
+
default: (pattern, workspaces) => {
|
|
95
|
+
// Plain string at the default target matches name OR alias. Wildcard and
|
|
96
|
+
// regex forms intentionally narrow to name only to avoid ambiguity — use
|
|
97
|
+
// an explicit "alias:" prefix to match aliases by wildcard/regex.
|
|
98
|
+
if (pattern.isRegex) {
|
|
99
|
+
const regex = new RegExp(pattern.value);
|
|
100
|
+
return workspaces.filter((workspace) => regex.test(workspace.name));
|
|
101
|
+
}
|
|
102
|
+
if (pattern.value.includes("*")) {
|
|
103
|
+
const wildcardRegex = createWildcardRegex(pattern.value);
|
|
104
|
+
return workspaces.filter((workspace) =>
|
|
105
|
+
wildcardRegex.test(workspace.name),
|
|
57
106
|
);
|
|
58
|
-
}
|
|
107
|
+
}
|
|
108
|
+
return workspaces.filter(
|
|
109
|
+
(workspace) =>
|
|
110
|
+
workspace.name === pattern.value ||
|
|
111
|
+
workspace.aliases.includes(pattern.value),
|
|
112
|
+
);
|
|
59
113
|
},
|
|
60
|
-
name: (pattern, workspaces
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
114
|
+
name: (pattern, workspaces) => {
|
|
115
|
+
if (pattern.isRegex) {
|
|
116
|
+
const regex = new RegExp(pattern.value);
|
|
117
|
+
return workspaces.filter((workspace) => regex.test(workspace.name));
|
|
118
|
+
}
|
|
119
|
+
if (pattern.value.includes("*")) {
|
|
120
|
+
const wildcardRegex = createWildcardRegex(pattern.value);
|
|
121
|
+
return workspaces.filter((workspace) =>
|
|
122
|
+
wildcardRegex.test(workspace.name),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return workspaces.filter((workspace) => workspace.name === pattern.value);
|
|
66
126
|
},
|
|
67
|
-
alias: (pattern, workspaces
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
127
|
+
alias: (pattern, workspaces) => {
|
|
128
|
+
if (pattern.isRegex) {
|
|
129
|
+
const regex = new RegExp(pattern.value);
|
|
130
|
+
return workspaces.filter((workspace) =>
|
|
131
|
+
workspace.aliases.some((alias) => regex.test(alias)),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (pattern.value.includes("*")) {
|
|
135
|
+
const wildcardRegex = createWildcardRegex(pattern.value);
|
|
136
|
+
return workspaces.filter((workspace) =>
|
|
137
|
+
workspace.aliases.some((alias) => wildcardRegex.test(alias)),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return workspaces.filter((workspace) =>
|
|
141
|
+
workspace.aliases.includes(pattern.value),
|
|
142
|
+
);
|
|
73
143
|
},
|
|
74
144
|
path: (pattern, workspaces) => {
|
|
145
|
+
if (pattern.isRegex) {
|
|
146
|
+
const regex = new RegExp(pattern.value);
|
|
147
|
+
// Normalize backslashes so regex sources stay portable: a single
|
|
148
|
+
// forward-slash-based regex works on both Windows and POSIX paths.
|
|
149
|
+
return workspaces.filter((workspace) =>
|
|
150
|
+
regex.test(workspace.path.replaceAll("\\", "/")),
|
|
151
|
+
);
|
|
152
|
+
}
|
|
75
153
|
return workspaces.filter((workspace) =>
|
|
76
154
|
new bun.Glob(pattern.value.replace(/\/+$/, "")).match(workspace.path),
|
|
77
155
|
);
|
|
78
156
|
},
|
|
79
|
-
tag: (pattern, workspaces
|
|
157
|
+
tag: (pattern, workspaces) => {
|
|
158
|
+
if (pattern.isRegex) {
|
|
159
|
+
const regex = new RegExp(pattern.value);
|
|
160
|
+
return workspaces.filter((workspace) =>
|
|
161
|
+
workspace.tags.some((tag) => regex.test(tag)),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (pattern.value.includes("*")) {
|
|
165
|
+
const wildcardRegex = createWildcardRegex(pattern.value);
|
|
166
|
+
return workspaces.filter((workspace) =>
|
|
167
|
+
workspace.tags.some((tag) => wildcardRegex.test(tag)),
|
|
168
|
+
);
|
|
169
|
+
}
|
|
80
170
|
return workspaces.filter((workspace) =>
|
|
81
|
-
|
|
82
|
-
? workspace.tags.some((tag) => wildcardRegex.test(tag))
|
|
83
|
-
: workspace.tags.includes(pattern.value),
|
|
171
|
+
workspace.tags.includes(pattern.value),
|
|
84
172
|
);
|
|
85
173
|
},
|
|
86
174
|
};
|
|
87
|
-
const matchWorkspacesByPattern = (pattern, workspaces) =>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const matchWorkspacesByPatterns = (patterns, workspaces) => {
|
|
175
|
+
const matchWorkspacesByPattern = (pattern, workspaces, rootWorkspace) => {
|
|
176
|
+
if (pattern.isRootSelector) {
|
|
177
|
+
return rootWorkspace ? [rootWorkspace] : [];
|
|
178
|
+
}
|
|
179
|
+
return PATTERN_TARGET_HANDLERS[pattern.target](pattern, workspaces);
|
|
180
|
+
};
|
|
181
|
+
const matchWorkspacesByPatterns = (patterns, workspaces, rootWorkspace) => {
|
|
94
182
|
const parsedPatterns = patterns.map(parseWorkspacePattern);
|
|
95
183
|
const excludePatterns = parsedPatterns.filter((pattern) => pattern.isNegated);
|
|
96
184
|
const includePatterns = parsedPatterns.filter(
|
|
97
185
|
(pattern) => !pattern.isNegated,
|
|
98
186
|
);
|
|
99
187
|
const excludeWorkspaces = excludePatterns.flatMap((pattern) =>
|
|
100
|
-
matchWorkspacesByPattern(pattern, workspaces),
|
|
188
|
+
matchWorkspacesByPattern(pattern, workspaces, rootWorkspace),
|
|
101
189
|
);
|
|
102
190
|
const includeWorkspaces = includePatterns.flatMap((pattern) =>
|
|
103
|
-
matchWorkspacesByPattern(pattern, workspaces),
|
|
191
|
+
matchWorkspacesByPattern(pattern, workspaces, rootWorkspace),
|
|
104
192
|
);
|
|
105
193
|
return includeWorkspaces.filter(
|
|
106
194
|
(workspace, index, arr) =>
|
|
@@ -118,8 +206,8 @@ export {
|
|
|
118
206
|
WORKSPACE_PATTERN_NEGATION_PREFIX,
|
|
119
207
|
WORKSPACE_PATTERN_NEGATION_PREFIXES,
|
|
120
208
|
WORKSPACE_PATTERN_NEGATION_SHORT_PREFIX,
|
|
209
|
+
WORKSPACE_PATTERN_REGEX_PREFIX,
|
|
121
210
|
WORKSPACE_PATTERN_SEPARATOR,
|
|
122
211
|
matchWorkspacesByPatterns,
|
|
123
212
|
parseWorkspacePattern,
|
|
124
|
-
stringifyWorkspacePattern,
|
|
125
213
|
};
|