Skip to content

Commit 345722e

Browse files
fix(build): address review comments and add unit tests for additionalFiles
- Fix getGlobBase to return '.' for single-part patterns like 'file.txt' - Fix Windows path separator handling by using 'sep' instead of 'posix.sep' when splitting - Export getGlobBase function for testing - Add comprehensive unit tests for getGlobBase function - Add vitest configuration for @trigger.dev/build package Co-authored-by: Eric Allam <[email protected]>
1 parent 55aba75 commit 345722e

File tree

4 files changed

+115
-7
lines changed

4 files changed

+115
-7
lines changed

packages/build/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@
7474
"dev": "tshy --watch",
7575
"typecheck": "tsc --noEmit -p tsconfig.src.json",
7676
"update-version": "tsx ../../scripts/updateVersion.ts",
77-
"check-exports": "attw --pack ."
77+
"check-exports": "attw --pack .",
78+
"test": "vitest run",
79+
"test:dev": "vitest"
7880
},
7981
"dependencies": {
8082
"@prisma/config": "^6.10.0",
@@ -91,7 +93,8 @@
9193
"esbuild": "^0.23.0",
9294
"rimraf": "6.0.1",
9395
"tshy": "^3.0.2",
94-
"tsx": "4.17.0"
96+
"tsx": "4.17.0",
97+
"vitest": "^2.0.0"
9598
},
9699
"engines": {
97100
"node": ">=18.20.0"

packages/build/src/internal/additionalFiles.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { BuildManifest } from "@trigger.dev/core/v3";
22
import { BuildContext } from "@trigger.dev/core/v3/build";
33
import { copyFile, mkdir } from "node:fs/promises";
4-
import { dirname, isAbsolute, join, posix, relative, resolve } from "node:path";
4+
import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
55
import { glob } from "tinyglobby";
66

77
export type AdditionalFilesOptions = {
@@ -76,7 +76,8 @@ async function findStaticAssetFiles(
7676
// Extracts the base directory from a glob pattern (the non-wildcard prefix).
7777
// For example: "../shared/**" -> "../shared", "./assets/*.txt" -> "./assets"
7878
// For specific files without globs: "./config/settings.json" -> "./config" (parent dir)
79-
function getGlobBase(pattern: string): string {
79+
// For single-part patterns: "file.txt" -> "." (current dir)
80+
export function getGlobBase(pattern: string): string {
8081
const parts = pattern.split(/[/\\]/);
8182
const baseParts: string[] = [];
8283
let hasGlobCharacters = false;
@@ -92,8 +93,9 @@ function getGlobBase(pattern: string): string {
9293

9394
// If no glob characters were found, the pattern is a specific file path.
9495
// Return the parent directory so that relative() preserves the filename.
95-
if (!hasGlobCharacters && baseParts.length > 1) {
96-
baseParts.pop(); // Remove the filename, keep the directory
96+
// For single-part patterns (just a filename), return "." to indicate current directory.
97+
if (!hasGlobCharacters) {
98+
baseParts.pop(); // Remove the filename, keep the directory (or empty for single-part)
9799
}
98100

99101
return baseParts.length > 0 ? baseParts.join(posix.sep) : ".";
@@ -129,8 +131,9 @@ async function findStaticAssetsForMatcher(
129131
pathInsideDestinationDir = join(options.destination, relativeToGlobBase);
130132
} else {
131133
// Default behavior: compute relative path from cwd and strip ".." segments
134+
// Use platform-specific separator for splitting since path.relative() returns platform separators
132135
pathInsideDestinationDir = relative(cwd, file)
133-
.split(posix.sep)
136+
.split(sep)
134137
.filter((p) => p !== "..")
135138
.join(posix.sep);
136139
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { describe, it, expect } from "vitest";
2+
import { getGlobBase } from "../src/internal/additionalFiles.js";
3+
4+
describe("getGlobBase", () => {
5+
describe("glob patterns with wildcards", () => {
6+
it("extracts base from parent directory glob pattern", () => {
7+
expect(getGlobBase("../shared/**")).toBe("../shared");
8+
});
9+
10+
it("extracts base from relative directory glob pattern", () => {
11+
expect(getGlobBase("./assets/*.txt")).toBe("./assets");
12+
});
13+
14+
it("extracts base from nested directory glob pattern", () => {
15+
expect(getGlobBase("files/nested/**/*.js")).toBe("files/nested");
16+
});
17+
18+
it("returns current directory for top-level glob", () => {
19+
expect(getGlobBase("**/*.js")).toBe(".");
20+
});
21+
22+
it("returns current directory for star pattern", () => {
23+
expect(getGlobBase("*.js")).toBe(".");
24+
});
25+
26+
it("handles question mark wildcard", () => {
27+
expect(getGlobBase("./src/?/*.ts")).toBe("./src");
28+
});
29+
30+
it("handles bracket patterns", () => {
31+
expect(getGlobBase("./src/[abc]/*.ts")).toBe("./src");
32+
});
33+
34+
it("handles brace expansion patterns", () => {
35+
expect(getGlobBase("./src/{a,b}/*.ts")).toBe("./src");
36+
});
37+
38+
it("handles deeply nested patterns", () => {
39+
expect(getGlobBase("a/b/c/d/**")).toBe("a/b/c/d");
40+
});
41+
});
42+
43+
describe("specific file paths without globs", () => {
44+
it("returns parent directory for file in subdirectory", () => {
45+
expect(getGlobBase("./config/settings.json")).toBe("./config");
46+
});
47+
48+
it("returns parent directory for file in nested subdirectory", () => {
49+
expect(getGlobBase("../shared/utils/helpers.ts")).toBe("../shared/utils");
50+
});
51+
52+
it("returns current directory for single-part filename", () => {
53+
expect(getGlobBase("file.txt")).toBe(".");
54+
});
55+
56+
it("returns current directory for filename starting with dot", () => {
57+
expect(getGlobBase(".env")).toBe(".");
58+
});
59+
60+
it("returns parent directory for explicit relative path to file", () => {
61+
expect(getGlobBase("./file.txt")).toBe(".");
62+
});
63+
64+
it("returns parent directories for parent reference to file", () => {
65+
expect(getGlobBase("../file.txt")).toBe("..");
66+
});
67+
68+
it("handles multiple parent references", () => {
69+
expect(getGlobBase("../../config/app.json")).toBe("../../config");
70+
});
71+
});
72+
73+
describe("edge cases", () => {
74+
it("returns current directory for empty string", () => {
75+
expect(getGlobBase("")).toBe(".");
76+
});
77+
78+
it("handles Windows-style backslashes", () => {
79+
expect(getGlobBase("..\\shared\\**")).toBe("../shared");
80+
});
81+
82+
it("handles mixed forward and back slashes", () => {
83+
expect(getGlobBase("../shared\\nested/**")).toBe("../shared/nested");
84+
});
85+
86+
it("handles patterns with only dots", () => {
87+
expect(getGlobBase("./")).toBe(".");
88+
});
89+
90+
it("handles parent directory reference only", () => {
91+
expect(getGlobBase("../")).toBe("..");
92+
});
93+
});
94+
});

packages/build/vitest.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from "vitest/config";
2+
3+
export default defineConfig({
4+
test: {
5+
include: ["test/**/*.test.ts", "src/**/*.test.ts"],
6+
globals: true,
7+
},
8+
});

0 commit comments

Comments
 (0)