Skip to content

Commit

Permalink
filter workspaces when installing
Browse files Browse the repository at this point in the history
  • Loading branch information
dylan-conway committed Jan 1, 2025
1 parent 1ae8552 commit d20c3f2
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/bun.js/api/glob.zig
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ pub fn match(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame
var str = str_arg.toSlice(globalThis, arena.allocator());
defer str.deinit();

if (this.is_ascii and isAllAscii(str.slice())) return JSC.JSValue.jsBoolean(globImpl.Ascii.match(this.pattern, str.slice()));
if (this.is_ascii and isAllAscii(str.slice())) return JSC.JSValue.jsBoolean(globImpl.Ascii.match(this.pattern, str.slice()).matches());

const codepoints = codepoints: {
if (this.pattern_codepoints) |cp| break :codepoints cp.items[0..];
Expand Down
2 changes: 1 addition & 1 deletion src/glob/GlobWalker.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1358,7 +1358,7 @@ pub fn GlobWalker_(
return GlobAscii.match(
pattern_component.patternSlice(this.pattern),
filepath,
);
).matches();
}
const codepoints = this.componentStringUnicode(pattern_component);
return matchImpl(
Expand Down
32 changes: 22 additions & 10 deletions src/glob/ascii.zig
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,18 @@ pub fn valid_glob_indices(glob: []const u8, indices: std.ArrayList(BraceIndex))
}
}

pub const MatchResult = enum {
no_match,
match,

negate_no_match,
negate_match,

pub fn matches(this: MatchResult) bool {
return this == .match or this == .negate_match;
}
};

/// This function checks returns a boolean value if the pathname `path` matches
/// the pattern `glob`.
///
Expand Down Expand Up @@ -208,7 +220,7 @@ pub fn valid_glob_indices(glob: []const u8, indices: std.ArrayList(BraceIndex))
/// Multiple "!" characters negate the pattern multiple times.
/// "\"
/// Used to escape any of the special characters above.
pub fn match(glob: []const u8, path: []const u8) bool {
pub fn match(glob: []const u8, path: []const u8) MatchResult {
// This algorithm is based on https://research.swtch.com/glob
var state = State{};
// Store the state when we see an opening '{' brace in a stack.
Expand Down Expand Up @@ -290,7 +302,7 @@ pub fn match(glob: []const u8, path: []const u8) bool {
(glob[state.glob_index] == ',' or glob[state.glob_index] == '}'))
{
if (state.skipBraces(glob, false) == .Invalid)
return false; // invalid pattern!
return .no_match; // invalid pattern!
}

continue;
Expand Down Expand Up @@ -321,7 +333,7 @@ pub fn match(glob: []const u8, path: []const u8) bool {
while (state.glob_index < glob.len and (first or glob[state.glob_index] != ']')) {
var low = glob[state.glob_index];
if (!unescape(&low, glob, &state.glob_index))
return false; // Invalid pattern
return .no_match; // Invalid pattern
state.glob_index += 1;

// If there is a - and the following character is not ],
Expand All @@ -332,7 +344,7 @@ pub fn match(glob: []const u8, path: []const u8) bool {
state.glob_index += 1;
var h = glob[state.glob_index];
if (!unescape(&h, glob, &state.glob_index))
return false; // Invalid pattern!
return .no_match; // Invalid pattern!
state.glob_index += 1;
break :blk h;
} else low;
Expand All @@ -342,7 +354,7 @@ pub fn match(glob: []const u8, path: []const u8) bool {
first = false;
}
if (state.glob_index >= glob.len)
return false; // Invalid pattern!
return .no_match; // Invalid pattern!
state.glob_index += 1;
if (is_match != class_negated) {
state.path_index += 1;
Expand All @@ -351,7 +363,7 @@ pub fn match(glob: []const u8, path: []const u8) bool {
},
'{' => if (state.path_index < path.len) {
if (brace_stack.len >= brace_stack.stack.len)
return false; // Invalid pattern! Too many nested braces.
return .no_match; // Invalid pattern! Too many nested braces.

// Push old state to the stack, and reset current state.
state = brace_stack.push(&state);
Expand Down Expand Up @@ -380,7 +392,7 @@ pub fn match(glob: []const u8, path: []const u8) bool {
var cc = c;
// Match escaped characters as literals.
if (!unescape(&cc, glob, &state.glob_index))
return false; // Invalid pattern;
return .no_match; // Invalid pattern;

const is_match = if (cc == '/')
isSeparator(path[state.path_index])
Expand Down Expand Up @@ -416,7 +428,7 @@ pub fn match(glob: []const u8, path: []const u8) bool {
if (brace_stack.len > 0) {
// If in braces, find next option and reset path to index where we saw the '{'
switch (state.skipBraces(glob, true)) {
.Invalid => return false,
.Invalid => return .no_match,
.Comma => {
state.path_index = brace_stack.last().path_index;
continue;
Expand All @@ -440,10 +452,10 @@ pub fn match(glob: []const u8, path: []const u8) bool {
}
}

return negated;
return if (negated) .negate_match else .no_match;
}

return !negated;
return if (!negated) .match else .negate_no_match;
}

inline fn isSeparator(c: u8) bool {
Expand Down
2 changes: 2 additions & 0 deletions src/install/install.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8537,6 +8537,7 @@ pub const PackageManager = struct {
pub fn supportsWorkspaceFiltering(this: Subcommand) bool {
return switch (this) {
.outdated => true,
.install => true,
// .pack => true,
else => false,
};
Expand Down Expand Up @@ -9516,6 +9517,7 @@ pub const PackageManager = struct {
clap.parseParam("-D, --development") catch unreachable,
clap.parseParam("--optional Add dependency to \"optionalDependencies\"") catch unreachable,
clap.parseParam("-E, --exact Add the exact version instead of the ^range") catch unreachable,
clap.parseParam("--filter <STR>... Install packages for the matching workspaces") catch unreachable,
clap.parseParam("<POS> ... ") catch unreachable,
});

Expand Down
24 changes: 24 additions & 0 deletions src/install/lockfile.zig
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,30 @@ pub const Tree = struct {

continue;
}

// only do this when parent is root. workspaces are always dependencies of the root
// package, and the root package is always called with `processSubtree`
if (parent_pkg_id == 0 and builder.manager.options.filter_patterns.len > 0) {
const workspace_name = if (builder.dependencies[dep_id].behavior.isWorkspaceOnly())
pkg_names[builder.resolutions[dep_id]].slice(builder.buf())
else
pkg_names[0].slice(builder.buf());

var match = false;

for (builder.manager.options.filter_patterns) |pattern| {
// use ascii matcher and let utf8 always fail
switch (bun.glob.Ascii.match(pattern, workspace_name)) {
.match, .negate_match => match = true,
.negate_no_match => match = false,
.no_match => {},
}
}

if (!match) {
continue;
}
}
}

const hoisted: HoistDependencyResult = hoisted: {
Expand Down

0 comments on commit d20c3f2

Please sign in to comment.