Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion src/glob/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub fn detect_glob_syntax(potential_pattern: &[u8]) -> bool {
if let Some(idx) = slice.iter().position(|&b| b == token) {
// Check for even number of backslashes preceding the
// token to know that it's not escaped
let mut i = idx;
let mut i = potential_pattern.len() - slice.len() + idx;
let mut backslash_count: u16 = 0;

while i > 0 && potential_pattern[i - 1] == b'\\' {
Expand All @@ -53,3 +53,36 @@ pub fn detect_glob_syntax(potential_pattern: &[u8]) -> bool {

false
}

#[cfg(test)]
mod tests {
use super::detect_glob_syntax;

#[test]
fn detects_unescaped_tokens() {
assert!(detect_glob_syntax(b"*.ts"));
assert!(detect_glob_syntax(b"a/{b,c}/d"));
assert!(detect_glob_syntax(b"a[bc]d"));
assert!(detect_glob_syntax(b"a?c"));
assert!(detect_glob_syntax(b"!foo"));
}

#[test]
fn ignores_escaped_tokens() {
assert!(!detect_glob_syntax(b"a\\*b"));
assert!(!detect_glob_syntax(b"a\\{b\\}c"));
assert!(!detect_glob_syntax(b"plain/path.txt"));
// even backslash count = escaped backslash, unescaped token
assert!(detect_glob_syntax(b"a\\\\*b"));
}

#[test]
fn detects_unescaped_token_after_escaped_one() {
// https://github.com/oven-sh/bun/pull/34275
assert!(detect_glob_syntax(b"\\*x*"));
assert!(detect_glob_syntax(b"\\{a\\}{b,c}"));
assert!(detect_glob_syntax(b"\\?a?"));
assert!(detect_glob_syntax(b"\\[a\\]b[cd]"));
assert!(!detect_glob_syntax(b"\\*x\\*"));
}
}
25 changes: 24 additions & 1 deletion test/cli/install/bad-workspace.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { spawnSync } from "bun";
import { beforeEach, expect, setDefaultTimeout, test } from "bun:test";
import { mkdirSync, writeFileSync } from "fs";
import { bunEnv, bunExe, tempDir, tmpdirSync } from "harness";
import { bunEnv, bunExe, isWindows, tempDir, tmpdirSync } from "harness";
import { join } from "path";

let cwd: string;

Expand Down Expand Up @@ -58,6 +59,28 @@ test("non-string workspaces entry prints the error without literal markup", asyn
expect(exitCode).toBe(1);
});

// https://github.com/oven-sh/bun/pull/34275 (`*` is not a valid filename character on Windows)
test.skipIf(isWindows)("workspace glob with an escaped token followed by an unescaped one is expanded", async () => {
using dir = tempDir("workspace-escaped-glob", {
"package.json": JSON.stringify({ name: "root", workspaces: ["\\*x*"] }),
"*xpkg/package.json": JSON.stringify({ name: "xpkg", version: "1.0.0" }),
});
await using proc = Bun.spawn({
cmd: [bunExe(), "install"],
cwd: String(dir),
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);

expect(stderr).not.toContain("Workspace not found");
const lockfile = await Bun.file(join(String(dir), "bun.lock")).text();
expect(lockfile).toContain('"*xpkg"');
expect(lockfile).toContain('"xpkg@workspace:');
expect({ stdout, stderr, exitCode }).toMatchObject({ exitCode: 0 });
});

test("workspace with ./ should not crash", () => {
writeFileSync(
`${cwd}/package.json`,
Expand Down
Loading