Skip to content

fix: resolve template directory path after esbuild bundling#117

Merged
recoupableorg merged 2 commits intomainfrom
fix/loadtemplate-path-resolution
Mar 31, 2026
Merged

fix: resolve template directory path after esbuild bundling#117
recoupableorg merged 2 commits intomainfrom
fix/loadtemplate-path-resolution

Conversation

@sidneyswift
Copy link
Copy Markdown
Contributor

@sidneyswift sidneyswift commented Mar 31, 2026

Summary

  • Template files (style guide, caption guide, moods, movements, reference images) were silently failing to load in production because __dirname points to esbuild's build output directory, not the source directory where additionalFiles places the templates
  • Adds resolveTemplatesDir() that tries __dirname-relative first, then falls back to process.cwd()-relative path
  • Adds diagnostic logging so template load failures show up in the Trigger.dev dashboard instead of being silently swallowed

Before: Production generates generic portraits (no style guide, no scene instructions, no caption rules)
After: Production follows the full template — bedroom setting, purple LED, deadpan expression, proper captions

Test plan

  • All 191 tests pass locally
  • Dev mode run produces correct bedroom-style video with template applied
  • Verified production currently generates wrong output (bright outdoor portrait vs dark bedroom)
  • After merge + pnpm run deploy:trigger-prod, trigger a production run and verify video matches template style

Made with Cursor

Summary by CodeRabbit

  • Bug Fixes

    • Clearer validation and error reporting when template directories or required files are missing.
    • JSON parse and non-recoverable I/O errors are now surfaced instead of being silently ignored.
    • Only absent files are treated as missing (null); other failures propagate after logging.
  • Chores

    • Expanded diagnostic logging and per-file labels for easier troubleshooting.
    • Added discovery/warning for reference images and a final summary of template load results.

esbuild changes __dirname to the build output directory, so
path.resolve(__dirname, "../content/templates") fails silently in
production — all template files (style guide, caption guide, moods,
movements, reference images) load as null and the pipeline generates
generic content instead of following template instructions.

Adds resolveTemplatesDir() that tries __dirname first (works locally
with tsx) then falls back to process.cwd()-relative path (works in
Trigger.dev deployments where additionalFiles preserves the source
directory structure).

Also adds diagnostic logging so template load failures are visible in
the Trigger.dev dashboard instead of being silently swallowed.

Made-with: Cursor
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d7ebe8aa-4055-4431-86c0-8d6300fbf7cd

📥 Commits

Reviewing files that changed from the base of the PR and between e1dd819 and ed2cb86.

📒 Files selected for processing (1)
  • src/content/loadTemplate.ts

📝 Walkthrough

Walkthrough

Replaced a single __dirname-based template path with a resolver that probes multiple filesystem locations, added @trigger.dev/sdk/v3 logging, and expanded loadTemplate/loadJsonFile to validate directories, enumerate files, pass per-file labels, and emit detailed success/warning/error logs.

Changes

Cohort / File(s) Summary
Template loading / JSON IO
src/content/loadTemplate.ts
Added resolveTemplatesDir() to probe multiple candidate paths for bundled templates; integrated @trigger.dev/sdk/v3 logging; updated loadTemplate() to log __dirname, process.cwd(), resolved paths, validate template directory existence (fail on missing), attempt directory listing with error logs, discover/reference images (warn if missing), and summarize results; changed loadJsonFile() to accept a label, log file-size on success, return null only for ENOENT (with warning), and re-throw parse/I/O errors after logging.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit peeks in every path, 🐇
Seeking templates, near and far,
It logs each step and names each file,
Counts images, notes a missing jar,
Hops off proud — the bundles found! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: resolving the template directory path issue that occurs after esbuild bundling in production.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/loadtemplate-path-resolution

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (2)
src/content/loadTemplate.ts (2)

216-233: Differentiate missing files from malformed JSON.

Right now parse errors and read errors both return null, which can still silently degrade output quality for broken template files. Prefer null only for ENOENT, and throw for invalid JSON/other I/O errors.

Proposed fix
 async function loadJsonFile<T>(filePath: string, label: string): Promise<T | null> {
   try {
     const raw = await fs.readFile(filePath, "utf-8");
     const parsed = JSON.parse(raw) as T;
     logger.log(`loadTemplate: loaded ${label}`, {
       path: filePath,
-      sizeBytes: raw.length,
+      sizeBytes: Buffer.byteLength(raw, "utf8"),
     });
     return parsed;
   } catch (err) {
-    logger.warn(`loadTemplate: FAILED to load ${label}`, {
-      path: filePath,
-      error: String(err),
-    });
-    return null;
+    const e = err as NodeJS.ErrnoException;
+    if (e?.code === "ENOENT") {
+      logger.warn(`loadTemplate: missing optional ${label}`, {
+        path: filePath,
+      });
+      return null;
+    }
+    logger.error(`loadTemplate: invalid ${label}`, {
+      path: filePath,
+      error: String(err),
+    });
+    throw err;
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/content/loadTemplate.ts` around lines 216 - 233, In loadJsonFile,
distinguish missing-file (ENOENT) from parse/other I/O errors: catch the error,
if err.code === "ENOENT" log the missing-file case with logger.warn and return
null; for all other errors (e.g., JSON.parse failures or other read issues) log
the error with logger.warn including String(err) and rethrow the error so
callers can handle it; keep references to loadJsonFile, fs.readFile, JSON.parse
and logger.warn when updating the catch block.

81-84: Consider capping directory-content log payload size.

Logging full recursive file lists can become noisy/expensive in production runs; count + sample is usually enough.

Proposed tweak
-    logger.log("loadTemplate: directory contents", {
-      templateDir,
-      files: entries,
-    });
+    logger.log("loadTemplate: directory contents", {
+      templateDir,
+      count: entries.length,
+      sample: entries.slice(0, 50),
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/content/loadTemplate.ts` around lines 81 - 84, The log in loadTemplate
that calls logger.log("loadTemplate: directory contents", { templateDir, files:
entries }) can emit a huge payload; change it to include a count and a sample
instead of the full entries array — e.g., log templateDir, totalFiles:
entries.length, and sampleFiles: entries.slice(0, N) (where N is a small
constant) and optionally a flag hasMore when entries.length > N; update the
logger call in loadTemplate to use these fields rather than passing the entire
entries object.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/content/loadTemplate.ts`:
- Line 80: The code uses fs.readdir(templateDir, { recursive: true }) which
requires Node >=18.17.0 (or 20.1.0+); add an engines.node field to package.json
(e.g., "node": ">=18.17.0") or confirm the deployment/runtime (Trigger.dev)
guarantees a compatible Node version so the call in loadTemplate.ts (fs.readdir,
templateDir, entries) won’t fail at runtime.

---

Nitpick comments:
In `@src/content/loadTemplate.ts`:
- Around line 216-233: In loadJsonFile, distinguish missing-file (ENOENT) from
parse/other I/O errors: catch the error, if err.code === "ENOENT" log the
missing-file case with logger.warn and return null; for all other errors (e.g.,
JSON.parse failures or other read issues) log the error with logger.warn
including String(err) and rethrow the error so callers can handle it; keep
references to loadJsonFile, fs.readFile, JSON.parse and logger.warn when
updating the catch block.
- Around line 81-84: The log in loadTemplate that calls
logger.log("loadTemplate: directory contents", { templateDir, files: entries })
can emit a huge payload; change it to include a count and a sample instead of
the full entries array — e.g., log templateDir, totalFiles: entries.length, and
sampleFiles: entries.slice(0, N) (where N is a small constant) and optionally a
flag hasMore when entries.length > N; update the logger call in loadTemplate to
use these fields rather than passing the entire entries object.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d034fae-f59c-4ec9-b45f-56ec47969e18

📥 Commits

Reviewing files that changed from the base of the PR and between ce0efce and e1dd819.

📒 Files selected for processing (1)
  • src/content/loadTemplate.ts


// List what's actually in the template directory
try {
const entries = await fs.readdir(templateDir, { recursive: true });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Node engine constraints from package manifests =="
fd -i '^package\.json$' -x sh -c '
  echo "--- $1"
  jq -r ".engines.node // \"<engines.node not set>\"" "$1"
' sh {}

echo
echo "== Sites using recursive readdir =="
rg -nP --type=ts '\breaddir\s*\([^)]*\{[^}]*\brecursive\s*:\s*true'

echo
echo "== Trigger config context =="
fd -i 'trigger.config.ts' -x sed -n '1,180p' {}

Repository: recoupable/tasks

Length of output: 1437


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Node version config files =="
fd -E node_modules '\.nvmrc|\.node-version|engines\.node' -x cat {} 2>/dev/null || true

echo
echo "== Dockerfile/Docker references =="
fd -E node_modules 'Dockerfile|docker-compose' -x head -20 {} \; 2>/dev/null || true

echo
echo "== Context around loadTemplate.ts to understand usage =="
head -n 100 src/content/loadTemplate.ts | tail -n 30

Repository: recoupable/tasks

Length of output: 1087


🌐 Web query:

Node.js fs.readdir recursive option support added when version

💡 Result:

The { recursive: true } option for fs.readdir(), fsPromises.readdir(), and fs.readdirSync() was added in Node.js v20.1.0 (and backported to v18.17.0). [1] [2]


Document Node.js version requirement for readdir({ recursive: true }).

The { recursive: true } option requires Node.js 18.17.0 or 20.1.0+. The code already has error handling with try-catch, but there's no explicit Node version constraint in package.json. Add an engines.node field to ensure the runtime meets this requirement:

"engines": {
  "node": ">=18.17.0"
}

Alternatively, verify that Trigger.dev's runtime constraints already enforce a compatible Node version.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/content/loadTemplate.ts` at line 80, The code uses
fs.readdir(templateDir, { recursive: true }) which requires Node >=18.17.0 (or
20.1.0+); add an engines.node field to package.json (e.g., "node": ">=18.17.0")
or confirm the deployment/runtime (Trigger.dev) guarantees a compatible Node
version so the call in loadTemplate.ts (fs.readdir, templateDir, entries) won’t
fail at runtime.

- loadJsonFile: distinguish ENOENT (return null) from parse/IO errors
  (rethrow) so malformed JSON surfaces instead of being silently swallowed
- directory contents log: cap payload to count + 10-file sample instead
  of dumping the full entries array

Made-with: Cursor
@recoupableorg recoupableorg merged commit d974928 into main Mar 31, 2026
1 of 2 checks passed
* 2. process.cwd()-relative (works in Trigger.dev deployments where
* additionalFiles preserves source-root-relative paths)
*/
async function resolveTemplatesDir(): Promise<string> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SRP - new lib file for any function with a name different from the file name.

const templatesDir = await resolveTemplatesDir();
const templateDir = path.join(templatesDir, templateName);

logger.log("loadTemplate: resolving paths", {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRY - use the shared logStep function

Comment on lines +58 to +93
const templatesDir = await resolveTemplatesDir();
const templateDir = path.join(templatesDir, templateName);

logger.log("loadTemplate: resolving paths", {
__dirname,
cwd: process.cwd(),
templatesDir,
templateDir,
});

// Check the template directory exists
try {
await fs.access(templateDir);
} catch {
logger.error("loadTemplate: template directory does not exist", {
templateDir,
});
throw new Error(`Template directory not found: ${templateDir}`);
}

// List what's actually in the template directory
const MAX_SAMPLE_FILES = 10;
try {
const entries = await fs.readdir(templateDir, { recursive: true });
logger.log("loadTemplate: directory contents", {
templateDir,
totalFiles: entries.length,
sampleFiles: entries.slice(0, MAX_SAMPLE_FILES),
...(entries.length > MAX_SAMPLE_FILES && { hasMore: true }),
});
} catch (err) {
logger.error("loadTemplate: failed to list directory", {
templateDir,
error: String(err),
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OCP

  • actual: changes added directly inline to existing loadTemplate function
  • required: new lib file for new code. only one line function call added here.

Copy link
Copy Markdown
Contributor

@sweetmantech sweetmantech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing unit tests

sweetmantech added a commit that referenced this pull request Apr 1, 2026
…-resolution"

This reverts commit d974928, reversing
changes made to 4d10af6.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants