-
-
Notifications
You must be signed in to change notification settings - Fork 957
fix(build): add destination option to additionalFiles extension #2855
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(build): add destination option to additionalFiles extension #2855
Conversation
When using glob patterns with parent directory references (../), the
default behavior strips ".." segments resulting in unexpected paths.
For example, "../shared/**" would place files at "shared/" instead of
preserving the original path structure.
This adds an optional "destination" parameter that allows users to
explicitly specify where matched files should be placed:
additionalFiles({
files: ["../shared/**"],
destination: "apps/shared"
})
When destination is specified, files are placed relative to the glob
pattern's base directory under the destination path. This is useful
in monorepo setups where files need to maintain their structure.
Also updates documentation to explain this behavior and the new option.
Slack thread: https://triggerdotdev.slack.com/archives/C08N6PJTK2Q/p1767946469914139?thread_ts=1756405171.439939&cid=C08N6PJTK2Q
🦋 Changeset detectedLatest commit: 674f5c7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 27 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
WalkthroughAdds an optional Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📜 Recent review detailsConfiguration used: Repository UI Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (5)📓 Common learnings📚 Learning: 2025年11月27日T16:27:35.304ZApplied to files:
📚 Learning: 2025年11月27日T16:27:35.304ZApplied to files:
📚 Learning: 2025年11月27日T16:27:35.304ZApplied to files:
📚 Learning: 2025年11月27日T16:26:58.661ZApplied to files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (24)
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Review: fix(build): add destination option to additionalFiles extension
Summary
This PR adds a destination option to the additionalFiles extension to address path resolution issues when using glob patterns with parent directory references (../). The implementation is clean and the documentation is thorough.
Code Quality & Best Practices ✅
- Good: Well-documented with JSDoc comments including examples
- Good: The
getGlobBasehelper function is clearly commented and handles common glob characters - Good: Backwards compatible - existing behavior is preserved when
destinationis not specified - Minor: The type
AdditionalFilesOptionsis duplicated in bothpackages/build/src/extensions/core/additionalFiles.tsandpackages/build/src/internal/additionalFiles.ts. Consider exporting from one location and importing in the other to maintain DRY principles.
Potential Issues ⚠️
-
Path separator inconsistency: In
getGlobBase(), the pattern is split by/[/\\]/(both forward and back slashes) but rejoined withposix.sep(always/). While this works, it could cause issues on Windows if the input pattern uses backslashes and the result is compared with a path using native separators. Consider usingpath.sepfor the join, or documenting that glob patterns should always use forward slashes. -
Glob base extraction edge cases: The
getGlobBasefunction may not handle all glob patterns correctly:- Patterns like
../**would return..which resolves correctly - But patterns like
{a,b}/**would return.(treating{a,b}as glob) - Consider whether patterns starting with brace expansion need special handling
- Patterns like
-
Security consideration: When
destinationis user-provided, there's no validation that it doesn't contain path traversal sequences like../../../etc. While this is a build-time operation running in the user's own environment, it's worth documenting that users should not pass untrusted input to this option.
Performance ✅
- The
getGlobBasefunction is called once per file in the loop at line 115, but since it only depends on thematcher(which is constant for all files from that matcher), it could be hoisted outside the file loop for a minor optimization. However, this is unlikely to matter in practice.
Test Coverage ⚠️
- No unit tests added: This PR adds new functionality (
getGlobBase, destination path computation) but no tests. Consider adding tests for:getGlobBasewith various patterns (../shared/**,./assets/*.txt,**/*.js,{a,b}/**)- Path computation with destination specified vs not specified
- Edge cases like empty destination, absolute destination paths
Documentation ✅
- Good: Comprehensive documentation update in
additionalFiles.mdx - Good: Typo fix (
effects→affects) - Good: Multiple examples showing different use cases including monorepo setups
Suggestions
-
Add unit tests for the new functionality, particularly for
getGlobBaseand the path computation logic. -
Consider extracting
getGlobBasecall outside the file loop:
const globBase = options?.destination ? getGlobBase(matcher) : null; const absoluteGlobBase = globBase && !isAbsolute(globBase) ? resolve(cwd, globBase) : globBase; for (const file of files) { // ... use absoluteGlobBase }
- Consider validating that
destinationdoesn't start with/or contain..to prevent unexpected behavior.
Overall
This is a well-implemented feature that solves a real problem for monorepo users. The code is clean, the documentation is excellent, and the implementation is backwards compatible. The main gap is test coverage - adding tests would significantly improve confidence in the path handling logic, especially around edge cases.
Recommendation: Approve with suggestion to add tests in a follow-up PR.
Review Complete
Your review story is ready!
Comment !reviewfast on this PR to re-generate the story.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/build/src/internal/additionalFiles.ts (1)
76-135: Fix Windows path separator bug and add destination path safety validation.Three security/correctness issues:
Windows separator mismatch (critical):
relative(cwd, file)returns platform-specific separators (backslashes on Windows), but code splits byposix.sep(/). This means the..filtering on line 125 silently fails on Windows—a malicious glob pattern could escape via../segments.Absolute
destinationbypass: Ifoptions.destinationis absolute,join(destinationPath, pathInsideDestinationDir)ignoresdestinationPathentirely, bypassing output directory bounds.No final safety guard: No validation ensures the computed
destinationactually stays withindestinationPathbeforecopyFile()executes.Proposed fix
-import { dirname, isAbsolute, join, posix, relative, resolve } from "node:path"; +import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path"; function getGlobBase(pattern: string): string { const parts = pattern.split(/[/\\]/); const baseParts: string[] = []; for (const part of parts) { if (part.includes("*") || part.includes("?") || part.includes("[") || part.includes("{")) { break; } baseParts.push(part); } - return baseParts.length > 0 ? baseParts.join(posix.sep) : "."; + return baseParts.length > 0 ? baseParts.join(sep) : "."; } async function findStaticAssetsForMatcher( matcher: string, destinationPath: string, options?: { cwd?: string; ignore?: string[]; destination?: string } ): Promise<MatchedStaticAssets> { const result: MatchedStaticAssets = []; const files = await glob({ patterns: [matcher], cwd: options?.cwd, ignore: options?.ignore ?? [], onlyFiles: true, absolute: true, }); const cwd = options?.cwd ?? process.cwd(); + const validateRelativePath = (path: string): void => { + if (isAbsolute(path) || path.split(sep).includes("..")) { + throw new Error(`[additionalFiles] Path traversal detected: ${path}`); + } + }; + + const resolveDestinationSafely = (root: string, rel: string): string => { + const resolved = resolve(root, rel); + if (!resolved.startsWith(root + sep) && resolved !== root) { + throw new Error( + `[additionalFiles] Refusing to write outside outputPath: ${resolved} is not under ${root}` + ); + } + return resolved; + }; for (const file of files) { let pathInsideDestinationDir: string; if (options?.destination) { + if (isAbsolute(options.destination)) { + throw new Error(`[additionalFiles] destination must be a relative path, got: ${options.destination}`); + } const globBase = getGlobBase(matcher); const absoluteGlobBase = isAbsolute(globBase) ? globBase : resolve(cwd, globBase); const relativeToGlobBase = relative(absoluteGlobBase, file); + validateRelativePath(relativeToGlobBase); pathInsideDestinationDir = join(options.destination, relativeToGlobBase); } else { pathInsideDestinationDir = relative(cwd, file) - .split(posix.sep) + .split(sep) .filter((p) => p !== "..") - .join(posix.sep); + .join(sep); + validateRelativePath(pathInsideDestinationDir); } - const relativeDestinationPath = join(destinationPath, pathInsideDestinationDir); + const relativeDestinationPath = resolveDestinationSafely(destinationPath, pathInsideDestinationDir); result.push({ source: file, destination: relativeDestinationPath, }); } return result; }
🧹 Nitpick comments (3)
packages/build/src/extensions/core/additionalFiles.ts (1)
4-25: Document (and/or validate) thatdestinationmust be a safe relative path.Given the internal code ultimately does
join(manifest.outputPath, destination, ...), allowing absolute paths or..segments indestinationcan escape the build directory unless explicitly prevented. Consider adding a one-liner to the JSDoc like "Must be a relative path and must not contain..".docs/config/extensions/additionalFiles.mdx (1)
27-83: Add a short note:destinationis relative (no absolute paths / no..).This will prevent readers from assuming they can set
/tmp/fooor../../somethingand also aligns with the safest implementation.packages/build/src/internal/additionalFiles.ts (1)
30-50: Minor:options.files ?? []is unnecessary (and can hide invalid inputs).
AdditionalFilesOptions.filesis required in the public type; if this can beundefinedin practice, consider making it optional at the type level and validating early. Otherwise, dropping?? []makes misconfiguration fail fast.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
docs/config/extensions/additionalFiles.mdxpackages/build/src/extensions/core/additionalFiles.tspackages/build/src/internal/additionalFiles.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
Files:
packages/build/src/internal/additionalFiles.tspackages/build/src/extensions/core/additionalFiles.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Files:
packages/build/src/internal/additionalFiles.tspackages/build/src/extensions/core/additionalFiles.ts
**/*.{js,ts,jsx,tsx,json,md,css,scss}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier
Files:
packages/build/src/internal/additionalFiles.tspackages/build/src/extensions/core/additionalFiles.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
packages/build/src/internal/additionalFiles.tspackages/build/src/extensions/core/additionalFiles.ts
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/repo.mdc:0-0
Timestamp: 2025年11月27日T16:26:47.602Z
Learning: Refer to the monorepo structure documentation at repo.md before making changes or adding new files
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
Applied to files:
packages/build/src/internal/additionalFiles.tsdocs/config/extensions/additionalFiles.mdxpackages/build/src/extensions/core/additionalFiles.ts
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure build process in trigger.config.ts using `build` object with external packages, extensions, and JSX settings
Applied to files:
packages/build/src/internal/additionalFiles.tsdocs/config/extensions/additionalFiles.mdx
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Specify task locations in trigger.config.ts using the `dirs` array, with automatic exclusion of .test and .spec files
Applied to files:
packages/build/src/internal/additionalFiles.tsdocs/config/extensions/additionalFiles.mdxpackages/build/src/extensions/core/additionalFiles.ts
📚 Learning: 2025年11月27日T16:26:47.602Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/repo.mdc:0-0
Timestamp: 2025年11月27日T16:26:47.602Z
Learning: Refer to the monorepo structure documentation at repo.md before making changes or adding new files
Applied to files:
docs/config/extensions/additionalFiles.mdx
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `metadata.parent` and `metadata.root` to update parent and root task metadata from child tasks
Applied to files:
docs/config/extensions/additionalFiles.mdx
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure Trigger.dev project in `trigger.config.ts` using `defineConfig()` with project ref and task directories
Applied to files:
docs/config/extensions/additionalFiles.mdx
📚 Learning: 2025年11月27日T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025年11月27日T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
Applied to files:
docs/config/extensions/additionalFiles.mdx
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks
Applied to files:
docs/config/extensions/additionalFiles.mdx
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution
Applied to files:
docs/config/extensions/additionalFiles.mdx
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export every task, including subtasks
Applied to files:
docs/config/extensions/additionalFiles.mdx
🧬 Code graph analysis (1)
packages/build/src/internal/additionalFiles.ts (1)
packages/build/src/extensions/core/additionalFiles.ts (1)
AdditionalFilesOptions(4-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (25)
- GitHub Check: Cursor Bugbot
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: typecheck / typecheck
- GitHub Check: claude-review
- GitHub Check: Analyze (javascript-typescript)
ericallam
commented
Jan 9, 2026
@claude add a patch changeset file please
Claude finished @ericallam's task in 39s —— View job
Adding Changeset
- Create patch changeset file for
@trigger.dev/build - Commit and push changes
Added changeset file .changeset/light-apricots-listen.md with a patch bump for @trigger.dev/build.
|
PR Review: fix(build): add destination option to additionalFiles extension
Summary
This PR adds a destination option to the additionalFiles extension to give users explicit control over where files are placed when using glob patterns with parent directory references (../). This is a useful enhancement for monorepo setups.
Code Quality and Best Practices
Positives:
- Well-documented with JSDoc comments and comprehensive documentation updates
- The type definition is duplicated in both files (
packages/build/src/extensions/core/additionalFiles.tsandpackages/build/src/internal/additionalFiles.ts), which is intentional since the extension re-exports the options type - The
getGlobBasehelper function is well-commented and handles common glob characters - The changeset is properly included
Suggestions:
-
Type duplication: Consider importing the
AdditionalFilesOptionstype from the internal module in the extension file rather than duplicating it. This ensures they stay in sync:export type { AdditionalFilesOptions } from "../../internal/additionalFiles.js";
-
Typo fix in docs: Good catch on fixing "effects" → "affects" in the documentation.
Potential Bugs or Issues
-
Cross-platform path handling (Minor): The
getGlobBasefunction usesposix.sepwhen joining base parts (line 90), but splits on both/and\(line 79). While this is likely intentional for cross-platform input handling with POSIX-style output, consider adding a comment to clarify this design decision. -
Path separator inconsistency: In the new code path (lines 113-120),
join()fromnode:pathis used (which is platform-specific), while the original code (lines 123-126) usesposix.sep. This could cause inconsistent behavior on Windows. Consider usingposix.joinconsistently:pathInsideDestinationDir = posix.join(options.destination, relativeToGlobBase);
-
Absolute destination paths: If a user provides an absolute path for
destination(e.g.,/apps/shared), the behavior might be unexpected. Consider validating thatdestinationis a relative path, or documenting the expected behavior.
Performance Considerations
- No concerns: The
getGlobBasefunction is called once per file, which is minimal overhead. The implementation is efficient.
Security Concerns
- Path traversal (Low risk): The
destinationoption could theoretically allow placing files outside the intended build directory if combined with..segments. However, since this is a build-time configuration controlled by the developer (not user input), the risk is minimal. Still, you might consider normalizing the destination path and ensuring files stay within the output directory:const normalizedDest = posix.normalize(options.destination); if (normalizedDest.startsWith('..')) { throw new Error('destination cannot reference parent directories'); }
Test Coverage
Concern: There don't appear to be any unit tests for the additionalFiles functionality, including this new feature. Given that this touches file system operations and path manipulation (which can be tricky cross-platform), I'd recommend adding tests for:
- Basic destination functionality with simple patterns
- Nested directory structures with
destination - Edge cases like empty destination, destination with trailing slashes
- The
getGlobBasefunction (could be unit tested in isolation) - Patterns without glob characters (e.g., exact file paths)
Example test cases for getGlobBase:
expect(getGlobBase("../shared/**")).toBe("../shared"); expect(getGlobBase("./assets/*.txt")).toBe("./assets"); expect(getGlobBase("files/**/*.js")).toBe("files"); expect(getGlobBase("**/*.js")).toBe("."); expect(getGlobBase("exact-file.txt")).toBe("exact-file.txt");
Overall Assessment
This is a well-implemented feature that solves a real pain point for monorepo users. The documentation is thorough and the implementation is straightforward.
Recommended before merge:
- Address the path separator inconsistency (use
posix.joinconsistently) - Consider adding basic unit tests for the new functionality
Nice to have:
- Validate that
destinationdoesn't contain path traversal sequences - Consider extracting and sharing the type definition to avoid duplication
Good work on this enhancement! 👍
... option When a pattern has no glob characters (e.g., './config/settings.json'), return the parent directory instead of the full path. This ensures that relative() preserves the filename when computing the destination path. Co-authored-by: Eric Allam <ericallam@users.noreply.github.com>
b30a65c to
55aba75
Compare
Code Review: fix(build): add destination option to additionalFiles extension
Thanks for this PR! The feature to add a destination option for the additionalFiles extension is useful for monorepo setups. Here is my feedback:
Overall Assessment
The PR provides a clean solution to a real problem - when using glob patterns with parent directory references (../), the default behavior strips .. segments, which can be confusing. The new destination option gives users explicit control.
Code Quality and Best Practices
Strengths:
- Good JSDoc documentation on the
destinationoption with practical examples - The
getGlobBasefunction is well-documented with clear comments explaining its purpose - Documentation updates are thorough and include practical monorepo examples
- Nice typo fix ("effects" -> "affects") in the docs
Suggestions:
-
Type Duplication: The
AdditionalFilesOptionstype is duplicated in both files:packages/build/src/extensions/core/additionalFiles.tspackages/build/src/internal/additionalFiles.ts
Consider exporting from one location and importing in the other to maintain DRY principles and avoid drift.
-
Consider extracting
getGlobBase: This function might be useful elsewhere. Consider whether it should be a shared utility.
Potential Bugs or Issues
-
Path Separator Consistency (Medium Priority):
IngetGlobBase, the code splits on both/and\\(/[/\\]/), but joins withposix.sep(which is always/). While this works, mixingjoin()(which uses native separators) withposix.sepingetGlobBasecould theoretically cause issues on Windows:// Line 129 uses join() which uses native path.sep on Windows pathInsideDestinationDir = join(options.destination, relativeToGlobBase);
Consider using
posix.join()consistently for the destination path to ensure consistent forward slashes across platforms. -
Empty Pattern Edge Case:
ThegetGlobBasefunction returns"."for an empty string input. While unlikely in practice, you might want to validate input patterns before processing. -
Single-file Glob Patterns:
For a pattern like"../shared/specific-file.ts"(no glob characters),getGlobBasereturns"../shared"(pops the last segment). This seems intentional but the behavior should be documented or tested to ensure it produces the expected destination paths.
Performance Considerations
The implementation looks efficient:
getGlobBaseis called once per file rather than being precomputed per matcher, which is slightly suboptimal but unlikely to matter in practice given typical file counts- Consider caching
getGlobBase(matcher)andabsoluteGlobBaseoutside the file loop (lines 124-125) since they do not change per file
Security Concerns
-
Path Traversal (Low Risk):
Thedestinationoption allows users to specify arbitrary paths. While this is user-controlled configuration (not untrusted input), users could potentially specify destinations like"../../some-sensitive-location". This is likely acceptable since:- Users have full control over their config file
- Build output is generally sandboxed to the build directory
However, you might want to add a note in the documentation about ensuring the destination stays within expected boundaries.
-
The existing
..stripping behavior in the default path (lines 132-135) is a good security practice - this is preserved correctly.
Test Coverage
This is the main concern: I could not find any unit tests for the new functionality. Consider adding tests for:
- Basic
destinationoption functionality getGlobBasefunction with various patterns:- Glob patterns:
"../shared/**","./assets/*.ts" - Specific files:
"../file.ts" - Edge cases:
"**","file.ts", empty strings
- Glob patterns:
- Path computation with nested directories
- Backward compatibility (ensure behavior without
destinationis unchanged)
Example test cases for getGlobBase:
expect(getGlobBase("../shared/**")).toBe("../shared"); expect(getGlobBase("./assets/*.ts")).toBe("./assets"); expect(getGlobBase("../shared/specific-file.ts")).toBe("../shared"); expect(getGlobBase("**")).toBe("."); expect(getGlobBase("file.ts")).toBe(".");
Minor Suggestions
-
The
matchesvariable was removed (line ~73 in original) - good cleanup since it was unused. -
Consider adding a warning log when
destinationis used with a pattern that does not contain..(since users might not needdestinationin that case).
Summary
This is a solid PR that addresses a real user pain point. The code is clean and well-documented. The main recommendation is to add unit tests to ensure the getGlobBase function and destination path computation work correctly across edge cases.
Once tests are added, this looks good to merge! 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/build/src/internal/additionalFiles.ts (1)
30-51: Add tests for the destination option and getGlobBase helper.The
destinationoption andgetGlobBasehelper currently have no test coverage. Add unit tests to verify:
- Destination path resolution works correctly for various glob patterns
- Monorepo scenarios with parent directory references like
../are handled properly- Edge cases like single-file patterns, absolute paths, and complex globs are tested
- The glob base extraction logic handles all pattern types
Use vitest for tests in the packages/build package.
🤖 Fix all issues with AI agents
In @packages/build/src/internal/additionalFiles.ts:
- Around line 76-100: getGlobBase incorrectly returns a filename for single-part
non-glob patterns like "file.txt"; update the non-glob branch in getGlobBase so
that if (!hasGlobCharacters) you return "." when baseParts.length <= 1 (covering
zero- or single-part patterns) and only pop the last part when baseParts.length
> 1, then join with posix.sep as before; reference function name getGlobBase and
variable baseParts to locate the change.
- Around line 131-136: The fallback handling for computing
pathInsideDestinationDir uses relative(cwd, file) then splits on posix.sep,
which fails on Windows because path.relative returns backslashes; update the
logic in the block that sets pathInsideDestinationDir to either split on
path.sep (so split(relative(cwd, file), path.sep) then filter out "..") or
replace the call to relative with posix.relative(cwd, file) so the returned
string uses "/" consistently before splitting by posix.sep; ensure you reference
and update the code around the pathInsideDestinationDir assignment and the use
of relative/cwd/file accordingly.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/build/src/internal/additionalFiles.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
Files:
packages/build/src/internal/additionalFiles.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Files:
packages/build/src/internal/additionalFiles.ts
**/*.{js,ts,jsx,tsx,json,md,css,scss}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier
Files:
packages/build/src/internal/additionalFiles.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
packages/build/src/internal/additionalFiles.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
Applied to files:
packages/build/src/internal/additionalFiles.ts
📚 Learning: 2025年11月27日T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025年11月27日T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
Applied to files:
packages/build/src/internal/additionalFiles.ts
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Specify task locations in trigger.config.ts using the `dirs` array, with automatic exclusion of .test and .spec files
Applied to files:
packages/build/src/internal/additionalFiles.ts
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure build process in trigger.config.ts using `build` object with external packages, extensions, and JSX settings
Applied to files:
packages/build/src/internal/additionalFiles.ts
🧬 Code graph analysis (1)
packages/build/src/internal/additionalFiles.ts (1)
packages/build/src/extensions/core/additionalFiles.ts (1)
AdditionalFilesOptions(4-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (24)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: typecheck / typecheck
- GitHub Check: claude-review
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
packages/build/src/internal/additionalFiles.ts (4)
4-4: LGTM!The new imports
isAbsoluteandresolveare correctly added and used in the destination path resolution logic.
7-28: Excellent documentation and type definition.The JSDoc clearly explains the purpose and behavior of the
destinationoption with a helpful monorepo example. The optional field correctly uses TypeScript's type syntax per coding guidelines.
60-64: LGTM!The function signatures are correctly updated to propagate the
destinationoption through the call chain while maintaining backward compatibility with the optional parameter.Also applies to: 102-106
122-129: LGTM! Well-implemented destination logic.The destination path calculation correctly:
- Extracts the glob pattern's base directory
- Resolves relative patterns to absolute paths
- Preserves file structure relative to the glob base
- Places files under the specified destination
This matches the PR's intended behavior for monorepo setups.
ericallam
commented
Jan 9, 2026
@claude can you add unit tests to this PR and please address all review comments
Claude finished @ericallam's task in 2m 56s —— View job
Adding Unit Tests and Addressing Review Comments
- Read current implementation and understand the code
- Analyze all review comments to address
- Fix edge case for single-part non-glob patterns in
getGlobBase - Fix Windows path separator handling in fallback behavior
- Create vitest config for packages/build
- Add unit tests for
getGlobBasefunction - Commit and push changes
Changes made:
-
Fixed
getGlobBasesingle-part pattern edge case (packages/build/src/internal/additionalFiles.ts:97-98)- Previously, patterns like
"file.txt"would return"file.txt"as the base, causingrelative()to return an empty string and losing the filename - Now correctly returns
"."for single-part patterns
- Previously, patterns like
-
Fixed Windows path separator handling (
packages/build/src/internal/additionalFiles.ts:136)- Changed from
.split(posix.sep)to.split(sep)in the fallback behavior - This ensures
".."segments are properly filtered on Windows wherepath.relative()returns backslashes
- Changed from
-
Added unit tests (
packages/build/test/additionalFiles.test.ts)- 21 test cases covering:
- Glob patterns with wildcards (
../shared/**,./assets/*.txt,**/*.js, etc.) - Specific file paths without globs (including single-part patterns)
- Edge cases (empty string, Windows backslashes, mixed separators)
- Glob patterns with wildcards (
- 21 test cases covering:
-
Added vitest configuration for
@trigger.dev/buildpackage
| Branch
...Files - 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 <ericallam@users.noreply.github.com>
ericallam
commented
Jan 9, 2026
@claude always run pnpm install and commit the pnpm lockfile when making any changes around deps
Claude finished @ericallam's task in 20s —— View job
Updating pnpm Lockfile
- Run
pnpm installto update lockfile - Commit and push the updated lockfile
pnpm install because the command requires approval. To enable this, you'll need to add pnpm to the --allowedTools configuration for the Claude Code Action.
Alternatively, you can run pnpm install locally and commit the updated lockfile.
| Branch
PR Review: fix(build): add destination option to additionalFiles extension
Summary
This PR adds a destination option to the additionalFiles extension to address path resolution issues when using glob patterns with parent directory references (../). The current behavior strips .. segments, which can lead to unexpected file placement in monorepo setups.
✅ Code Quality and Best Practices
Strengths:
- Well-documented: The JSDoc comments on the
destinationoption are thorough with clear examples - Consistent type duplication: The
AdditionalFilesOptionstype is defined in both the internal module and the extension module, following the existing pattern in the codebase - Clean separation of concerns: The
getGlobBasehelper function is well-isolated and properly exported for testing - Documentation: The MDX docs clearly explain the problem and solution with good examples
Minor suggestions:
- Consider extracting the
AdditionalFilesOptionstype to a shared location to avoid duplication betweensrc/extensions/core/additionalFiles.tsandsrc/internal/additionalFiles.ts. If one is updated without the other, it could lead to inconsistencies.
🐛 Potential Bugs or Issues
-
Path separator inconsistency in
getGlobBase: The function splits on both/and\\but always joins withposix.sep(forward slash). While this works for the glob pattern parsing use case, it may produce unexpected results on Windows if the caller expects backslashes. This seems intentional for cross-platform glob support, but worth noting. -
Edge case with trailing slashes:
getGlobBase("./") // returns "." getGlobBase("../") // returns ".."
These work correctly per the tests, but consider whether patterns like
"./foo/"(directory with trailing slash but no glob) behave as expected - currently it would return"."because it pops the empty string after the trailing slash. -
No validation on
destination: If a user provides an absolute path fordestination, it would work but could place files outside the build directory unexpectedly. Consider adding validation or documentation warning.
⚡ Performance Considerations
The implementation looks efficient:
getGlobBaseis called once per file only whendestinationis specified- The glob base calculation is O(n) where n is the number of path segments (typically small)
- No unnecessary file system operations added
No performance concerns identified.
🔒 Security Concerns
-
Path traversal consideration: The
destinationoption accepts arbitrary strings. While this is used within the build process context, malicious or accidental values like../../..could potentially place files outside the intended build directory. Thejoin()call withdestinationPathshould prevent escaping the build output directory, but this assumption depends on howdestinationPathis constructed upstream.Recommendation: Consider adding validation that the final
relativeDestinationPathis within the expected build output directory, or document thatdestinationshould be a relative path without parent directory references.
🧪 Test Coverage
Strengths:
- Comprehensive unit tests for
getGlobBasecovering:- Various glob patterns with wildcards
- Specific file paths without globs
- Edge cases (empty string, Windows backslashes, mixed slashes, trailing slashes)
- Good test organization with descriptive test names
Gaps:
-
No integration tests: The tests only cover the
getGlobBasehelper function. Consider adding tests forfindStaticAssetsForMatcherwith thedestinationoption to verify the full path resolution logic works correctly end-to-end. -
Missing test cases for
getGlobBase:- Pattern with only wildcards:
"**"or"***" - Negation patterns:
"!foo/**"(if supported) - Very deep nesting:
"a/b/c/d/e/f/g/**"
- Pattern with only wildcards:
-
Missing integration test scenarios:
- Verify files are actually copied to correct destination with
destinationoption - Test behavior when
destinationis an absolute path - Test behavior when
destinationcontains..segments
- Verify files are actually copied to correct destination with
📝 Additional Notes
-
Typo fix: Good catch fixing "effects" → "affects" in the documentation.
-
vitest addition: The package.json changes to add vitest and test scripts are appropriate. The
vitest.config.tsfollows the pattern established in@trigger.dev/core. -
Semicolon missing: In
vitest.config.tsline 8, there is no semicolon after the closing parenthesis. This is fine if the project uses a linter that allows it, but check project conventions.
Overall Assessment
This is a well-implemented feature that solves a real problem for monorepo users. The code is clean, well-documented, and includes appropriate tests for the new helper function.
Suggested improvements before merging:
- Add at least one integration test that verifies the full
destinationflow - Consider adding validation/documentation for the
destinationparameter to prevent path traversal issues - Consider extracting the shared type to avoid duplication
Verdict: 👍 Approve with minor suggestions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @packages/build/package.json:
- Around line 77-79: The lockfile is outdated after adding the vitest dependency
(scripts "test" and "test:dev" in package.json), causing pipeline failures; run
`pnpm install` in the repository to regenerate `pnpm-lock.yaml`, verify the
lockfile changes reflect the new vitest dependency, and commit the updated
`pnpm-lock.yaml` alongside the package.json changes so the CI pipeline passes.
🧹 Nitpick comments (1)
packages/build/test/additionalFiles.test.ts (1)
43-71: Consider adding a test for absolute paths.The test suite doesn't appear to cover absolute paths (e.g.,
/absolute/path/**orC:\\absolute\\path\\**). While these may not be common in practice, they could occur and it would be good to verify the behavior.Suggested test cases
Add these tests to the "edge cases" describe block:
+ it("handles absolute Unix-style paths with globs", () => { + expect(getGlobBase("/absolute/path/**")).toBe("/absolute/path"); + }); + + it("handles absolute Unix-style file paths", () => { + expect(getGlobBase("/absolute/path/file.txt")).toBe("/absolute/path"); + });
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/build/package.jsonpackages/build/src/internal/additionalFiles.tspackages/build/test/additionalFiles.test.tspackages/build/vitest.config.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
Files:
packages/build/vitest.config.tspackages/build/src/internal/additionalFiles.tspackages/build/test/additionalFiles.test.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Files:
packages/build/vitest.config.tspackages/build/src/internal/additionalFiles.tspackages/build/test/additionalFiles.test.ts
**/*.{js,ts,jsx,tsx,json,md,css,scss}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier
Files:
packages/build/vitest.config.tspackages/build/package.jsonpackages/build/src/internal/additionalFiles.tspackages/build/test/additionalFiles.test.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
packages/build/vitest.config.tspackages/build/src/internal/additionalFiles.tspackages/build/test/additionalFiles.test.ts
**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use vitest for all tests in the Trigger.dev repository
Files:
packages/build/test/additionalFiles.test.ts
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.test.{ts,tsx,js,jsx}: Test files should live beside the files under test and use descriptivedescribeanditblocks
Avoid mocks or stubs in tests; use helpers from@internal/testcontainerswhen Redis or Postgres are needed
Use vitest for unit tests
Files:
packages/build/test/additionalFiles.test.ts
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
📚 Learning: 2025年11月27日T16:27:48.109Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025年11月27日T16:27:48.109Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vitest for unit tests
Applied to files:
packages/build/vitest.config.tspackages/build/package.jsonpackages/build/test/additionalFiles.test.ts
📚 Learning: 2025年11月27日T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025年11月27日T16:26:37.432Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Use vitest for all tests in the Trigger.dev repository
Applied to files:
packages/build/vitest.config.tspackages/build/package.jsonpackages/build/test/additionalFiles.test.ts
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure build process in trigger.config.ts using `build` object with external packages, extensions, and JSX settings
Applied to files:
packages/build/vitest.config.tspackages/build/src/internal/additionalFiles.ts
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
Applied to files:
packages/build/vitest.config.tspackages/build/src/internal/additionalFiles.ts
📚 Learning: 2025年11月27日T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025年11月27日T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.test.{ts,tsx} : Test files should only import classes and functions from `app/**/*.ts` files and should not import `env.server.ts` directly or indirectly; pass configuration through options instead
Applied to files:
packages/build/vitest.config.ts
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure Trigger.dev project in `trigger.config.ts` using `defineConfig()` with project ref and task directories
Applied to files:
packages/build/vitest.config.tspackages/build/src/internal/additionalFiles.ts
📚 Learning: 2025年11月27日T16:27:48.109Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025年11月27日T16:27:48.109Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Test files should live beside the files under test and use descriptive `describe` and `it` blocks
Applied to files:
packages/build/vitest.config.tspackages/build/test/additionalFiles.test.ts
📚 Learning: 2025年11月27日T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025年11月27日T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Specify task locations in trigger.config.ts using the `dirs` array, with automatic exclusion of .test and .spec files
Applied to files:
packages/build/vitest.config.tspackages/build/src/internal/additionalFiles.tspackages/build/test/additionalFiles.test.ts
📚 Learning: 2025年11月26日T14:40:07.146Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 2710
File: packages/schema-to-json/package.json:0-0
Timestamp: 2025年11月26日T14:40:07.146Z
Learning: Node.js 24+ has native TypeScript support and can execute .ts files directly without tsx or ts-node for scripts that use only erasable TypeScript syntax (type annotations, interfaces, etc.). The trigger.dev repository uses Node.js 24.11.1+ and scripts like updateVersion.ts can be run with `node` instead of `tsx`.
Applied to files:
packages/build/package.json
📚 Learning: 2025年11月27日T16:26:44.496Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/executing-commands.mdc:0-0
Timestamp: 2025年11月27日T16:26:44.496Z
Learning: For running tests, navigate into the package directory and run `pnpm run test --run` to enable single-file test execution (e.g., `pnpm run test ./src/engine/tests/ttl.test.ts --run`)
Applied to files:
packages/build/package.json
📚 Learning: 2025年11月27日T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025年11月27日T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL
Applied to files:
packages/build/package.json
📚 Learning: 2025年11月27日T16:27:48.109Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025年11月27日T16:27:48.109Z
Learning: pnpm version `10.23.0` and Node.js version `20.11.1` are required for development
Applied to files:
packages/build/package.json
📚 Learning: 2025年11月27日T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025年11月27日T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
Applied to files:
packages/build/src/internal/additionalFiles.ts
🧬 Code graph analysis (3)
packages/build/vitest.config.ts (1)
packages/trigger-sdk/src/v3/config.ts (1)
defineConfig(11-13)
packages/build/src/internal/additionalFiles.ts (1)
packages/build/src/extensions/core/additionalFiles.ts (1)
AdditionalFilesOptions(4-25)
packages/build/test/additionalFiles.test.ts (1)
packages/build/src/internal/additionalFiles.ts (1)
getGlobBase(80-102)
🪛 GitHub Actions: 🤖 PR Checks
packages/build/package.json
[error] 1-1: pnpm install --frozen-lockfile failed: lockfile is outdated. specifiers in the lockfile don't match package.json (1 dependency added: vitest@^2.0.0). Run 'pnpm install' or 'pnpm install --no-frozen-lockfile' to update.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: claude-review
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (8)
packages/build/package.json (1)
77-79: LGTM!The test scripts follow standard Vitest conventions.
vitest runexecutes tests once, whilevitestruns in watch mode for development.packages/build/vitest.config.ts (1)
1-8: LGTM!The Vitest configuration is appropriate for this package. The include patterns cover both dedicated test directories and co-located tests, and enabling globals provides a convenient testing experience.
packages/build/test/additionalFiles.test.ts (1)
1-94: Excellent test coverage!The test suite is well-organized with descriptive blocks and covers a comprehensive range of cases including:
- Various glob wildcards (*, ?, [], {})
- Relative and parent directory references
- Specific file paths vs. glob patterns
- Cross-platform path separators (Windows backslashes, mixed slashes)
- Edge cases (empty string, single filename, dot files)
The tests properly validate that the function normalizes paths to POSIX separators, which is important for cross-platform compatibility.
packages/build/src/internal/additionalFiles.ts (5)
4-4: LGTM!The additional imports (
isAbsolute,resolve) are necessary for the destination-aware path resolution logic introduced in this PR.
9-27: Well-documented feature addition.The documentation clearly explains the destination option's purpose and behavior, with a practical monorepo example. This addresses the core problem described in the PR objectives where parent directory references were being normalized incorrectly.
124-139: Correct platform-specific path handling.The path calculation logic properly handles cross-platform differences:
- Line 136 correctly uses platform-specific
septo split the output ofrelative(), which returns platform-specific separators- Line 138 normalizes to POSIX separators for consistency
- Lines 124-131 handle the destination option by computing paths relative to the glob base
This approach ensures consistent behavior across Windows and Unix-like systems while preserving the original structure.
80-102: Consider edge case: patterns with only glob characters.What happens if the pattern consists only of glob characters, like
"*"or"**"? The function would:
- Split into
["*"]or["**"]- Immediately detect glob characters and break
basePartswould be empty- Return
"."This behavior seems correct (returns current directory), but it would be good to verify this is intentional and add test coverage if missing.
Looking at the test file, line 23 covers
"*.js"→"."and line 19 covers"**/*.js"→".", which validates this behavior. No action needed.
76-102: Verify the intended behavior for glob patterns with trailing slashes.The
getGlobBasefunction's handling of trailing slashes (e.g.,"../shared/") produces"../shared", which occurs because empty strings resulting from trailing separators are also collected and then popped. Confirm whether this behavior is correct by reviewing test cases and actual usage patterns in the codebase, or add explicit test coverage for trailing slash scenarios if it's currently missing.
ericallam
commented
Jan 9, 2026
@claude can you try doing pnpm install again and committing the lock file
Claude finished @ericallam's task in 35s —— View job
Running pnpm install and Committing Lockfile
- Run
pnpm installto update lockfile - Commit and push the updated lockfile
pnpm install because pnpm is not installed in this environment and the commands needed to install it (corepack enable, corepack prepare) require approval.
To fix this, you have a couple of options:
-
Add pnpm-related commands to your allowed tools - You can update the Claude Code Action
--allowedToolsconfiguration to includecorepackandpnpmcommands. -
Run locally - You can run
pnpm installon your local machine and commit the updatedpnpm-lock.yamlfile.
ericallam
commented
Jan 9, 2026
@claude try Running pnpm install and Committing Lockfile again
Claude finished @ericallam's task in 2m 7s —— View job
Running pnpm install and Committing Lockfile
- Run
pnpm installto update lockfile - Commit and push the updated lockfile
Successfully ran pnpm install and committed the updated pnpm-lock.yaml file. The lockfile now includes the vitest dependency added in packages/build/package.json.
| Branch
PR Review: fix(build): add destination option to additionalFiles extension
Summary
This PR adds a destination option to the additionalFiles build extension, solving a real problem with monorepo setups where glob patterns with .. references get their parent directory segments stripped, leading to unexpected file placement.
✅ Strengths
-
Well-documented: Excellent JSDoc comments and comprehensive MDX documentation with practical monorepo examples.
-
Backward compatible: The
destinationoption is optional, preserving existing behavior when not specified. -
Good test coverage: The
getGlobBasefunction has thorough unit tests covering glob patterns, specific file paths, and edge cases including Windows-style paths. -
Clean implementation: The
getGlobBasehelper is well-designed with clear comments explaining its purpose.
🔍 Potential Issues & Suggestions
1. Type Definition Duplication
The AdditionalFilesOptions type with the destination property and its JSDoc is duplicated in two files:
packages/build/src/extensions/core/additionalFiles.ts(lines 4-25)packages/build/src/internal/additionalFiles.ts(lines 7-28)
Suggestion: Consider exporting the type from one location and importing it in the other to maintain a single source of truth.
2. Missing Integration Tests
The tests only cover the getGlobBase helper function. There are no integration tests for findStaticAssetsForMatcher with the destination option to verify the full path resolution logic.
Suggestion: Add tests that exercise the complete flow, for example:
- Verify that
../shared/utils.tswithdestination: "libs"produceslibs/utils.ts - Test multiple files with nested directories to ensure structure is preserved
3. Path Separator Handling on Windows
In getGlobBase, the function joins parts with posix.sep (line 101), which correctly normalizes to forward slashes. However, the tests for Windows-style backslashes (line 77-82) expect the output to use forward slashes:
it("handles Windows-style backslashes", () => { expect(getGlobBase("..\\shared\\**")).toBe("../shared"); });
This works correctly due to the posix.sep usage, but it's worth noting this intentional cross-platform normalization in a comment.
4. Edge Case: Empty Destination String
What happens if destination: "" is passed? The code would check if (options?.destination) which would be falsy for an empty string, falling back to default behavior. This might be the intended behavior, but it's worth documenting or validating.
5. Potential Path Traversal Consideration
The destination option accepts arbitrary strings. While this is a build-time configuration (not user input), it might be worth considering whether paths like ../../../ in the destination could write files outside the build directory.
// This could potentially write outside the build directory additionalFiles({ files: ["./config.json"], destination: "../../../../etc" })
Suggestion: Consider adding validation or a note in the documentation that the destination should be a relative path within the build output.
📝 Minor Suggestions
-
Vitest config consistency: The new
vitest.config.tsmatches the pattern inpackages/core/vitest.config.ts- good consistency. -
Typo fix: Good catch on changing "effects" to "affects" in the documentation.
Overall Assessment
This is a well-implemented feature that solves a genuine pain point for monorepo users. The code is clean, well-documented, and maintains backward compatibility. The main suggestions are around adding integration tests and considering the type duplication.
Recommendation: ✅ Approve with minor suggestions
Co-authored-by: Eric Allam <ericallam@users.noreply.github.com>
PR Review: fix(build): add destination option to additionalFiles extension
Summary
This PR adds an optional destination parameter to the additionalFiles extension to control where matched files are placed when using glob patterns with parent directory references (..). This is a useful enhancement for monorepo setups.
✅ Code Quality and Best Practices
Strengths:
- Clean implementation with clear separation of concerns
- Good JSDoc documentation on the
destinationoption with practical examples - The
getGlobBasehelper function is well-documented and handles various edge cases - Documentation is thorough and provides good examples for users
Minor Suggestions:
- The
AdditionalFilesOptionstype is duplicated in bothpackages/build/src/extensions/core/additionalFiles.tsandpackages/build/src/internal/additionalFiles.ts. Consider exporting the type from one location and importing in the other to maintain DRY principles.
🐛 Potential Bugs or Issues
-
Path traversal concern: When
destinationis specified, there's no validation to prevent users from accidentally specifying paths that could escape the destination directory (e.g.,destination: "../../../etc"). While this is user-controlled configuration, a warning or validation could prevent misconfiguration. -
Trailing slash handling in
getGlobBase: The function handles"../"and"./"patterns, but the behavior might be unexpected when a trailing slash is present:getGlobBase("../")returns".."(after popping empty string)- This works because splitting
"../"gives[".", ".", ""]- wait, actually it should give["..", ""]. Let me verify: splitting by/gives["..", ""], and since no glob chars, it pops to get[".."], returning"..". This looks correct.
-
Cross-platform path handling: The code uses both
posix.sepandsep(platform-specific). The logic ingetGlobBasejoins withposix.sep, which is consistent. However, infindStaticAssetsForMatcher, when destination is set,join()is used (which is platform-specific), but in the default path,posix.sepis used for joining. This inconsistency could lead to different behavior on Windows vs Unix:// With destination - uses platform join() pathInsideDestinationDir = join(options.destination, relativeToGlobBase); // Without destination - uses posix.sep .join(posix.sep);
⚡ Performance Considerations
- The implementation is efficient -
getGlobBaseis called once per matcher with destination specified - No performance concerns with this approach
🔒 Security Concerns
- Low risk: The
destinationoption only controls the output path within the build directory. Since this is configuration provided by the developer intrigger.config.ts, the risk is minimal. - Consider documenting that the destination path should be relative and within the project structure.
🧪 Test Coverage
Strengths:
- Excellent unit test coverage for
getGlobBasefunction - Tests cover various glob patterns, specific file paths, and edge cases
- Good handling of Windows-style paths in tests
Areas for Improvement:
-
Missing integration tests: The tests only cover the
getGlobBasehelper. Consider adding tests forfindStaticAssetsForMatcherwith thedestinationoption to verify end-to-end behavior. -
Missing tests for the actual file copying with destination: A test that verifies files are actually copied to the correct destination path would increase confidence.
-
Edge cases not tested:
- What happens when
destinationis an empty string? - What happens when
destinationis an absolute path? - What happens with multiple files matching the same glob pattern?
- What happens when
📝 Documentation
- The docs update is clear and provides helpful examples
- Good fix for the typo: "effects" → "affects"
- The monorepo example is practical and demonstrates the problem well
Overall Assessment
This is a well-implemented feature that solves a real problem for monorepo users. The code is clean, well-documented, and the test coverage for the helper function is thorough.
Recommendations before merging:
- Consider adding validation or a warning for potentially problematic
destinationvalues - Ensure consistent path separator usage across platforms (minor)
- Consider adding an integration test for the full flow with
destinationoption
Good work! 👍
ericallam
commented
Jan 9, 2026
@claude our PR checks unit tests merge reports is failing with the following:
Run pnpm dlx vitest@3.1.4 run --merge-reports --pass-with-no-tests
Progress: resolved 1, reused 0, downloaded 0, added 0
Packages: +48
++++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 97, reused 0, downloaded 48, added 48, done
⎯⎯⎯⎯⎯⎯ Unhandled Error ⎯⎯⎯⎯⎯⎯⎯
Error: vitest.mergeReports() requires all blob files to be generated by the same Vitest version, received
- "packages_build-blob-1-1.json" uses v2.1.9
- "packages_core-blob-1-1.json" uses v3.1.4
- "packages_redis-worker-blob-1-1.json" uses v3.1.4
- "packages_schema-to-json-blob-1-1.json" uses v3.1.4
- "packages_trigger-sdk-blob-1-1.json" uses v3.1.4
make sure you are using the correct version of vitest in the build package please
Claude finished @ericallam's task in 1m 34s —— View job
Fixing Vitest Version Mismatch
- Investigate the vitest version issue in
packages/build - Update vitest to v3.1.4 to match other packages
- Update pnpm-lock.yaml
- Commit and push changes
Issue found: packages/build/package.json had "vitest": "^2.0.0" while the root package.json has "vitest": "3.1.4". This caused the merge-reports step to fail since the blobs were generated with different vitest versions.
Fix applied: Updated packages/build/package.json to use "vitest": "3.1.4" (exact version match with the root) and regenerated the lockfile.
| Branch
...eporting Co-authored-by: Eric Allam <ericallam@users.noreply.github.com>
PR Review: Add destination option to additionalFiles extension
Summary
This PR adds an optional destination parameter to the additionalFiles build extension, addressing a real pain point in monorepo setups where .. segments in glob patterns get stripped during file copying.
Code Quality and Best Practices ✅
Strengths:
- Clean, well-documented code with comprehensive JSDoc comments
- The
getGlobBase()function is well-isolated and testable - Good separation of concerns between the public API and internal implementation
- Consistent coding style throughout
- Type definitions are duplicated appropriately between the public API (
extensions/core/additionalFiles.ts) and internal implementation
Minor Suggestions:
- The type
AdditionalFilesOptionsis defined in two places (src/extensions/core/additionalFiles.tsandsrc/internal/additionalFiles.ts). Consider exporting from one location and importing in the other to maintain a single source of truth, or add a comment explaining the intentional duplication.
Potential Bugs or Issues ⚠️
-
Path separator inconsistency (line 101 in
additionalFiles.ts): ThegetGlobBase()function always joins withposix.sep(forward slash), even when handling Windows-style backslashes in input. This is fine for glob patterns (which typically use forward slashes), but worth verifying thatresolve()on Windows handles this correctly when passed toresolve(cwd, globBase)at line 127. -
Edge case with trailing slashes: The tests cover
"./"and"../"but the behavior when a user passes a destination with a trailing slash (e.g.,destination: "apps/shared/") isn't explicitly tested. Whilejoin()should handle this, it would be good to add a test case for confidence. -
Absolute paths in
destination: What happens if a user passes an absolute path asdestination? The current implementation would create files outside the build directory. Consider validating thatdestinationis a relative path, or documenting this behavior explicitly.
Performance Considerations ✅
- The
getGlobBase()function is called once per matcher, not per file, so there's no performance concern there - File operations are sequential within each matcher, which is appropriate for avoiding file system contention
- No unnecessary allocations or operations introduced
Security Concerns ⚠️
-
Path traversal: If
destinationaccepts user input (unlikely in this context but worth considering), a malicious value like../../../etccould write files outside the intended directory. Since this is a build configuration that developers control, this is low risk, but you might want to add a note in the documentation thatdestinationshould be a relative path within the build output. -
The existing behavior of stripping
..segments actually served as an implicit security measure - worth noting that the newdestinationoption bypasses this, putting more responsibility on the user to specify safe paths.
Test Coverage ✅
Excellent test coverage for getGlobBase():
- 21 test cases covering glob patterns, specific files, and edge cases
- Good coverage of Windows-style paths and mixed separators
- Edge cases like empty strings handled
Missing test coverage:
- No integration tests for
findStaticAssetsForMatcher()with the newdestinationoption - No tests verifying the end-to-end behavior of
addAdditionalFilesToBuild()with different destination values - Consider adding tests for:
- Multiple files matching a single glob with
destination - Nested directory structures preserved correctly
- Error cases (e.g., invalid patterns)
- Multiple files matching a single glob with
Documentation ✅
The documentation updates are thorough and well-written:
- Clear explanation of the problem being solved
- Practical monorepo example structure
- Multiple usage examples including combining multiple
additionalFilesextensions - Appropriate use of
<Note>components for important details
Small typo fix included: "effects" → "affects" (good catch!)
Overall Assessment
This is a well-implemented feature that solves a real problem. The code is clean, well-documented, and includes good test coverage for the core logic.
Recommendations before merging:
- Consider adding an integration test for the
destinationoption - Optionally add validation/documentation for absolute paths in
destination - Consider consolidating the
AdditionalFilesOptionstype definition
Verdict: Approve with minor suggestions 👍
PR Review: fix(build): add destination option to additionalFiles extension
Overall, this is a well-implemented feature that solves a real problem for monorepo users. The code is clean, well-documented, and includes good test coverage. Here's my detailed review:
Code Quality and Best Practices
Strengths:
- Excellent JSDoc documentation on both the public API (
additionalFiles.ts) and internal implementation (internal/additionalFiles.ts) - The
getGlobBasefunction is well-isolated and pure, making it easy to test - Good separation of concerns - the glob base extraction logic is cleanly separated from the file path computation
- Documentation is thorough and includes practical monorepo examples
Minor suggestions:
-
The
AdditionalFilesOptionstype is duplicated in bothsrc/extensions/core/additionalFiles.tsandsrc/internal/additionalFiles.tswith identical JSDoc. Consider exporting from one location and re-exporting from the other to avoid potential drift. -
In
getGlobBase, the function usesposix.sepfor joining but splits on both/and\\. This is intentional cross-platform handling, but a brief comment explaining why would help future maintainers.
Potential Bugs or Issues
-
Path traversal concern (line 131): When using
join(options.destination, relativeToGlobBase), ifrelativeToGlobBasesomehow contains..segments (which could happen if the glob matches files outside the expected base), files could be written outside the intended destination. Consider validating that the final path is actually under the destination directory, or documenting this as expected behavior. -
Empty destination string: If a user passes
destination: "", the current code will use the empty string injoin(), which should work but may produce unexpected results. Consider either documenting this or adding validation. -
Absolute paths in destination: The code doesn't explicitly handle if someone passes an absolute path for
destination. The behavior would likely be platform-dependent viajoin(). This edge case should either be documented or validated.
Performance Considerations
The implementation looks performant:
- The
getGlobBasefunction is O(n) where n is the number of path segments - efficient - No unnecessary iterations or allocations
- The glob operation is already the bottleneck, and no additional file system operations were added
Security Concerns
-
Path traversal: As mentioned above, there's a potential for path traversal if the glob matches unexpected files. In a build tool context, this is likely acceptable since the user controls the configuration, but it's worth being aware of.
-
No new external dependencies for the core logic - vitest is dev-only, which is good.
Test Coverage
Strengths:
- Comprehensive test suite for
getGlobBasecovering:- Various glob patterns (
**,*,?, brackets, braces) - Specific file paths without globs
- Edge cases (empty strings, Windows paths, mixed separators)
- Various glob patterns (
- Tests are well-organized with descriptive names
Suggestions for improvement:
-
Integration tests: Consider adding integration tests that actually exercise
findStaticAssetsForMatcherwith thedestinationoption. The current tests only cover thegetGlobBasehelper function. -
Test the main code path: Tests for scenarios like:
destinationwith a glob pattern that has..segmentsdestinationwith an absolute glob base- Multiple files matched with a destination
- Edge case: destination with trailing slash vs without
-
vitest.config.ts: The config uses
globals: true. Consider whether this is consistent with other test configurations in the monorepo.
Documentation
The documentation changes are excellent:
- Clear explanation of the problem with monorepos
- Practical examples with directory structures
- Shows both single and multiple destination configurations
- Good use of
<Note>components to highlight important behavior
Summary
This is a solid PR that addresses a real pain point. The implementation is clean and well-tested for the core utility function. My main recommendations are:
- Consider adding integration tests for the
destinationoption path - Document or validate edge cases around empty/absolute destination paths
- Consider consolidating the duplicated
AdditionalFilesOptionstype definition
Verdict: Approve with minor suggestions - The code is production-ready, and the suggestions above are nice-to-haves rather than blockers.
ericallam
commented
Jan 9, 2026
@claude can you add a patch changeset for this please
Claude finished @ericallam's task in 1m 12s —— View job
Adding Patch Changeset
- Find existing changeset file for this PR
- Create patch changeset for
@trigger.dev/build - Commit and push changes
Added changeset file .changeset/polite-lobsters-double.md with a patch bump for @trigger.dev/build.
| Branch
Co-authored-by: Eric Allam <ericallam@users.noreply.github.com>
PR Review: Add destination option to additionalFiles extension
Summary
This PR adds a destination option to the additionalFiles build extension, solving a real problem where glob patterns with parent directory references (../) would have their .. segments stripped, causing unexpected file placement. This is particularly useful for monorepo setups.
✅ Strengths
1. Well-designed solution
The destination option elegantly solves the problem while maintaining backward compatibility. The default behavior (stripping .. segments) is preserved when destination is not specified.
2. Excellent documentation
The docs (additionalFiles.mdx) are comprehensive with clear examples showing:
- The problem with default behavior
- How to use the
destinationoption - How to handle multiple directories with different destinations
3. Good test coverage
The 24 unit tests for getGlobBase cover:
- Various glob patterns with wildcards
- Specific file paths without globs
- Edge cases including empty strings and Windows-style backslashes
4. Clean implementation
The getGlobBase function is well-documented and handles edge cases appropriately (glob vs specific file patterns, cross-platform path separators).
⚠️ Issues & Suggestions
1. Duplicate type definitions (Code Quality)
AdditionalFilesOptions is defined in two places:
packages/build/src/internal/additionalFiles.ts:7packages/build/src/extensions/core/additionalFiles.ts:4
While both currently have the same properties, this duplication could lead to inconsistencies if one is updated but not the other. Consider:
- Having the public extension import and re-export the internal type, OR
- Creating a shared types file
// Option A: In extensions/core/additionalFiles.ts export type { AdditionalFilesOptions } from "../../internal/additionalFiles.js";
2. Missing integration tests
The tests only cover the getGlobBase utility function. Consider adding integration tests for findStaticAssetsForMatcher that verify the actual file copying behavior with the destination option. This would ensure:
- Files are copied to the correct destination paths
- The relative structure is preserved as documented
- Edge cases like nested directories work correctly
3. Potential path traversal concern (Security)
When destination is specified, there's no validation that the resulting path stays within the expected output directory. While this is a build-time operation (not runtime) and users control the config, consider adding a check:
// In findStaticAssetsForMatcher after computing pathInsideDestinationDir const normalizedPath = posix.normalize(pathInsideDestinationDir); if (normalizedPath.startsWith('..')) { throw new Error(`Destination path escapes build directory: ${pathInsideDestinationDir}`); }
4. Minor: vitest globals configuration
In vitest.config.ts, you have globals: true, but the test file explicitly imports { describe, it, expect } from vitest. Either:
- Remove the imports in test files to use globals, OR
- Remove
globals: truefrom the config since you're using explicit imports
5. Consider documenting the empty result warning behavior
When no files match a pattern, a warning is logged. This is good, but users might want to know about this behavior in the docs, especially for patterns that legitimately match zero files in some environments.
🔍 Code Quality Notes
Path handling is correct:
- Uses platform-specific
sepfor splittingrelative()output - Uses
posix.sepfor joining output paths (ensuring consistent forward slashes) - Handles both forward slashes and backslashes in glob patterns
The changeset is appropriate:
- Patch version bump is correct for this backward-compatible addition
- Description is clear and concise
Summary
This is a well-implemented feature that solves a real problem for monorepo users. The main suggestions are:
- Consider deduplicating the type definitions
- Add integration tests for the destination option
- Optionally add path traversal validation
The PR is in good shape and ready to merge after addressing the duplicate type definitions.
When using glob patterns with parent directory references (../), the default behavior strips ".." segments resulting in unexpected paths. For example, "../shared/**" would place files at "shared/" instead of preserving the original path structure.
This adds an optional "destination" parameter that allows users to explicitly specify where matched files should be placed:
additionalFiles({
files: ["../shared/**"],
destination: "apps/shared"
})
When destination is specified, files are placed relative to the glob pattern's base directory under the destination path. This is useful in monorepo setups where files need to maintain their structure.
Also updates documentation to explain this behavior and the new option.