Skip to content

Conversation

@Palanikannan1437
Copy link
Member

@Palanikannan1437 Palanikannan1437 commented Jan 20, 2026

Description

This PR fixes and stabilizes the PDF export functionality. It addresses issues where exports were either failing or producing incomplete/incorrect PDFs especially with images and custom nodes, and improves the overall reliability of the export flow. The changes ensure consistent rendering, proper data handling, and predictable output across different scenarios.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Improvement (change that would cause existing functionality to not work as expected)

Screenshots and Media (if applicable)

N/A

Test Scenarios

  • Exported PDFs for multiple projects with varying content sizes.
  • Verified correct rendering of text, layout, and metadata.
  • Tested export flow on fresh and existing projects.
  • Confirmed no regressions in related export/download functionality.

References

N/A

Summary by CodeRabbit

  • New Features

    • PDF export: export pages/documents with selectable page sizes/orientations, custom metadata, filename handling, embedded fonts, icons and improved styling; supports opting out of asset inclusion and preserves user mentions.
  • Improvements

    • More reliable exports with image processing, timeouts/retries, and clearer error responses.
    • Editor stability tweak to reduce remount issues when content context changes.
  • Tests

    • Added comprehensive tests for PDF rendering and export utilities.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

Adds a complete PDF export feature: HTTP controller and schema, TipTap-to-PDF rendering stack (colors, styles, icons, node/mark renderers), export service orchestrating content fetch/image processing/rendering, page-service helpers, tests, and related package/config updates.

Changes

Cohort / File(s) Summary
Package & Tooling
apps/live/package.json, apps/live/vitest.config.ts, apps/live/tsconfig.json
Added Vitest and coverage config, new dependencies for PDF rendering and image processing, and TypeScript changes to include tests and JSX.
Controllers
apps/live/src/controllers/pdf-export.controller.ts, apps/live/src/controllers/index.ts
New PdfExportController with request parsing, auth, effect-driven orchestration, error mapping to HTTP responses; controller registered in exported CONTROLLERS.
Schema & Errors
apps/live/src/schema/pdf-export.ts
New request schema PdfExportRequestBody, TPdfExportRequestBody type, and tagged error classes (validation, auth, content/metadata/image/generation/timeout errors) plus union PdfExportError.
PDF Public API Barrel
apps/live/src/lib/pdf/index.ts
Centralized exports for PDF utilities, renderers, styles, and types.
Rendering Types & Styles
apps/live/src/lib/pdf/types.ts, apps/live/src/lib/pdf/styles.ts, apps/live/src/lib/pdf/colors.ts
New TipTap/PDF types, comprehensive pdfStyles, and color system/resolvers mapped from editor tokens and CSS variables.
Renderers & Icons
apps/live/src/lib/pdf/mark-renderers.ts, apps/live/src/lib/pdf/node-renderers.tsx, apps/live/src/lib/pdf/icons.tsx
Mark renderer registry and applyMarks; extensive node renderer registry with contextual rendering; React-PDF SVG icon components and getFileIcon helper.
PDF Document Generation
apps/live/src/lib/pdf/plane-pdf-exporter.tsx
createPdfDocument and helpers to render to Blob or Buffer; font registration and Document/Page composition.
PDF Export Service & Utils
apps/live/src/services/pdf-export/pdf-export.service.ts, apps/live/src/services/pdf-export/types.ts, apps/live/src/services/pdf-export/effect-utils.ts, apps/live/src/services/pdf-export/index.ts
Service layer and exportToPdf orchestration (content fetch, image extraction, user mentions, image processing with sharp, rendering), effect utilities (timeout/retry, recover, tryAsync), and type declarations.
Page Service Enhancements
apps/live/src/services/page/core.service.ts
Added TUserMention, fetchUserMentions, resolveImageAssetUrl(s); refactored some methods to async/await and tightened fetchDescriptionBinary to return Buffer.
Tests
apps/live/tests/lib/pdf/pdf-rendering.test.ts, apps/live/tests/services/pdf-export/effect-utils.test.ts
End-to-end rendering tests for many node/mark types and metadata; unit tests for effect utilities (timeout, retry, recover, tryAsync).
Minor UI
apps/web/core/components/editor/rich-text/description-input/root.tsx
Added key={entityId} to RichTextEditor to influence remount behavior.

Sequence Diagram

sequenceDiagram
    participant Client
    participant PdfExportController
    participant PdfExportService
    participant PageService
    participant ImageProcessor
    participant PdfRenderer

    Client->>PdfExportController: POST /export-pdf (with cookie)
    activate PdfExportController
    PdfExportController->>PdfExportController: parseRequest (validate, auth)
    alt Auth Failed
        PdfExportController-->>Client: 401 Unauthorized
    else Validation Failed
        PdfExportController-->>Client: 400 Bad Request
    else Success
        PdfExportController->>PdfExportService: exportToPdf(input)
        activate PdfExportService

        PdfExportService->>PageService: fetchPageContent(pageId)
        activate PageService
        PageService-->>PdfExportService: contentJSON, titleHTML, descriptionBinary
        deactivate PageService

        PdfExportService->>PdfExportService: extractImageAssetIds(content)

        PdfExportService->>PageService: fetchUserMentions(pageId)
        PageService-->>PdfExportService: userMentions[]

        alt Images to process
            PdfExportService->>PageService: resolveImageAssetUrls(assetIds)
            PageService-->>PdfExportService: assetId→URL map

            loop Each Image
                PdfExportService->>ImageProcessor: fetch & process (sharp) → base64
                ImageProcessor-->>PdfExportService: data URL
            end
        end

        PdfExportService->>PdfRenderer: renderPlaneDocToPdfBuffer(document, metadata)
        activate PdfRenderer
        PdfRenderer-->>PdfExportService: PDF Buffer
        deactivate PdfRenderer

        PdfExportService-->>PdfExportController: { pdfBuffer, outputFileName, pageId }
        deactivate PdfExportService

        PdfExportController->>Client: 200 + PDF stream (Content-Disposition)
    end
    deactivate PdfExportController
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through nodes, colors, and fonts so bright,
I stitched images sharp and made pages tight,
Marks and icons danced in careful array,
Now exports hum and PDFs leap away,
A rabbit's small cheer for every byte!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'fix: pdf export' is vague and generic, using only high-level descriptors without conveying what specific aspect of PDF export was fixed or improved. Consider a more specific title like 'fix: improve pdf export reliability with image handling and custom node rendering' to better convey the scope of changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings

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
Contributor

@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: 4

🤖 Fix all issues with AI agents
In `@apps/live/package.json`:
- Around line 31-33: Add the listed external packages into the pnpm workspace
catalog with the exact versions provided (entries for "@effect/platform",
"@effect/platform-node", "@fontsource/inter", "@react-pdf/renderer",
"@react-pdf/types", "effect", "sharp", "@types/pdf-parse",
"@vitest/coverage-v8", "pdf-parse", and "vitest" using the versions in the
review), then update apps/live/package.json dependency entries for those same
package names to reference the catalog: versions instead of direct semver
ranges; ensure you modify the pnpm-workspace.yaml catalog section to include
each package/version and replace the corresponding dependency strings in
package.json with catalog:package@version tokens.

In `@apps/live/src/lib/pdf/plane-pdf-exporter.tsx`:
- Around line 14-45: Wrap the Font.register(...) invocation in a try-catch to
surface missing or incompatible font files: catch any error thrown by
Font.register (referencing Font.register and interFontDir / the hardcoded
filenames like inter-latin-400-normal.woff2) log a clear diagnostic (e.g.,
console.error or processLogger.error) that includes the error object and the
attempted file paths, then rethrow the error so callers know registration
failed; this ensures failures in plane-pdf-exporter.tsx are logged and do not
fail silently.

In `@apps/live/src/services/page/core.service.ts`:
- Around line 41-62: fetchDescriptionBinary currently assumes axios returns a
Node Buffer and throws if Buffer.isBuffer fails; instead detect and convert
ArrayBuffer or typed array views to a Buffer before returning. In the
fetchDescriptionBinary method (responseType: "arraybuffer"), check if
response.data is an ArrayBuffer or ArrayBuffer view (use ArrayBuffer.isView) and
create a Buffer from it (Buffer.from(...)), then proceed with the
Buffer.isBuffer check/return; keep the existing error handling (AppError and
logger) intact.

In `@apps/live/vitest.config.ts`:
- Around line 1-21: The config uses __dirname in the resolve.alias
(path.resolve(__dirname, "./src")) which breaks under ESM; replace this by
deriving dirname from import.meta.url (use fileURLToPath(import.meta.url) to get
the file path then path.dirname) and pass that dirname into path.resolve for the
"@" alias; add the import for fileURLToPath from "url" and compute a const
dirname (or similar) at the top of the module, then update the alias to use
path.resolve(dirname, "./src").
🧹 Nitpick comments (6)
apps/live/src/controllers/pdf-export.controller.ts (2)

15-27: Cookie validation has a subtle correctness issue.

The cookie variable is assigned req.headers.cookie || "", which means it will be an empty string when the cookie header is absent. However, !cookie evaluates to true for an empty string, so the logic works correctly. That said, the pattern is slightly confusing—consider simplifying:

-      const cookie = req.headers.cookie || "";
-      if (!cookie) {
+      const cookie = req.headers.cookie;
+      if (!cookie) {

This makes the intent clearer and avoids the unnecessary fallback to empty string before immediately checking for absence.


113-118: Type narrowing relies on structural discrimination.

The check if ("error" in result && "status" in result) works because PdfExportResult contains pdfBuffer, outputFileName, and pageId—none of which overlap with error/status. However, this is fragile if PdfExportResult ever gains an error or status field.

Consider using a discriminated union pattern with a _tag or success field for more robust type narrowing.

apps/live/tests/lib/pdf/pdf-rendering.test.ts (1)

442-443: Buffer length comparison may produce flaky tests.

Asserting that a4Buffer.length !== letterBuffer.length relies on PDF generation producing different byte counts for different page sizes. While generally true, this could be flaky if:

  • Compression algorithms produce varying output
  • Future optimizations change the generation

Consider verifying page dimensions from PDF metadata instead if the test framework supports it.

Also applies to: 464-465

apps/live/src/lib/pdf/plane-pdf-exporter.tsx (1)

67-82: Buffer/Blob conversion is straightforward but could benefit from error handling.

Both renderPlaneDocToPdfBuffer and renderPlaneDocToPdfBlob don't have try-catch blocks. If PDF generation fails (e.g., invalid image URLs, unsupported content), the error will propagate up. While this may be intentional for the caller to handle, consider whether wrapping in a domain-specific error would improve debugging.

apps/live/src/services/pdf-export/pdf-export.service.ts (1)

23-27: Duplicate TipTapNode type definition.

This local TipTapNode type duplicates the definition in @/lib/pdf/types.ts. Import it instead to maintain a single source of truth and avoid drift.

Proposed fix
-import type { PDFExportMetadata, TipTapDocument } from "@/lib/pdf";
+import type { PDFExportMetadata, TipTapDocument, TipTapNode } from "@/lib/pdf";
 import { renderPlaneDocToPdfBuffer } from "@/lib/pdf";

Then remove lines 23-27.

apps/live/src/lib/pdf/colors.ts (1)

86-93: Consider handling edge cases in CSS variable parsing.

The regex handles the expected pattern well. Consider whether malformed inputs (e.g., unclosed parentheses, extra whitespace) should return null silently or warrant a warning log for debugging.

Comment on lines +31 to +33
"@effect/platform": "^0.94.0",
"@effect/platform-node": "^0.104.0",
"@fontsource/inter": "5.2.8",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's see the structure and locate the relevant files
git ls-files | grep -E "(apps/live/package.json|pnpm-workspace.yaml)" | head -20

Repository: makeplane/plane

Length of output: 101


🏁 Script executed:

# Read the apps/live/package.json file to see the actual dependencies at the mentioned lines
cat -n apps/live/package.json

Repository: makeplane/plane

Length of output: 3319


🏁 Script executed:

# Also check the pnpm-workspace.yaml to see if catalog is defined
cat pnpm-workspace.yaml

Repository: makeplane/plane

Length of output: 994


Add packages to catalog: in both files.

Lines 31–33, 43–45, 52, 58, and 73–80 introduce external packages with version ranges. Per repo policy, external dependencies must use catalog: with versions declared in pnpm-workspace.yaml. These packages are missing from the catalog and need to be added there before updating package.json references.

Update pnpm-workspace.yaml to include:

"@effect/platform": ^0.94.0
"@effect/platform-node": ^0.104.0
"@fontsource/inter": 5.2.8
"@react-pdf/renderer": ^4.3.0
"@react-pdf/types": ^2.9.2
"effect": ^3.16.3
"sharp": ^0.34.3
"@types/pdf-parse": ^1.1.5
"@vitest/coverage-v8": ^4.0.8
"pdf-parse": ^2.4.5
"vitest": ^4.0.8

Then update apps/live/package.json to use catalog: for these entries, as shown in the original diff.

🤖 Prompt for AI Agents
In `@apps/live/package.json` around lines 31 - 33, Add the listed external
packages into the pnpm workspace catalog with the exact versions provided
(entries for "@effect/platform", "@effect/platform-node", "@fontsource/inter",
"@react-pdf/renderer", "@react-pdf/types", "effect", "sharp",
"@types/pdf-parse", "@vitest/coverage-v8", "pdf-parse", and "vitest" using the
versions in the review), then update apps/live/package.json dependency entries
for those same package names to reference the catalog: versions instead of
direct semver ranges; ensure you modify the pnpm-workspace.yaml catalog section
to include each package/version and replace the corresponding dependency strings
in package.json with catalog:package@version tokens.

Comment on lines 14 to 45
Font.register({
family: "Inter",
fonts: [
{
src: path.join(interFontDir, "files/inter-latin-400-normal.woff2"),
fontWeight: 400,
},
{
src: path.join(interFontDir, "files/inter-latin-400-italic.woff2"),
fontWeight: 400,
fontStyle: "italic",
},
{
src: path.join(interFontDir, "files/inter-latin-600-normal.woff2"),
fontWeight: 600,
},
{
src: path.join(interFontDir, "files/inter-latin-600-italic.woff2"),
fontWeight: 600,
fontStyle: "italic",
},
{
src: path.join(interFontDir, "files/inter-latin-700-normal.woff2"),
fontWeight: 700,
},
{
src: path.join(interFontDir, "files/inter-latin-700-italic.woff2"),
fontWeight: 700,
fontStyle: "italic",
},
],
});
Copy link
Contributor

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:

# First, locate and examine the file
find . -name "plane-pdf-exporter.tsx" -type f

Repository: makeplane/plane

Length of output: 105


🏁 Script executed:

# Check the file content around the font registration
cat -n apps/live/src/lib/pdf/plane-pdf-exporter.tsx | head -50

Repository: makeplane/plane

Length of output: 2048


🏁 Script executed:

# Check if `@fontsource/inter` is in package.json
find . -name "package.json" -type f | xargs grep -l "@fontsource/inter"

Repository: makeplane/plane

Length of output: 83


🏁 Script executed:

# Check the actual structure of `@fontsource/inter` if it exists in node_modules
find . -path "*/node_modules/@fontsource/inter/files/*" -type f 2>/dev/null | head -20

Repository: makeplane/plane

Length of output: 41


🏁 Script executed:

# Check which version of `@fontsource/inter` is in the package.json
cat apps/live/package.json | grep -A 2 -B 2 "@fontsource/inter"

Repository: makeplane/plane

Length of output: 262


🏁 Script executed:

# Check if there's any error handling around Font.register in the file
cat -n apps/live/src/lib/pdf/plane-pdf-exporter.tsx | grep -A 5 -B 5 "Font.register"

Repository: makeplane/plane

Length of output: 484


🏁 Script executed:

# Search for any error handling or try-catch blocks in this file
cat apps/live/src/lib/pdf/plane-pdf-exporter.tsx | grep -E "(try|catch|Error|error)" -i

Repository: makeplane/plane

Length of output: 41


🌐 Web query:

@fontsource/inter package file structure woff2 filenames

💡 Result:

Short answer — filenames use this pattern inside the package/files folder:

  • inter---<style>.woff2
    • subset ∈ {latin, latin-ext, cyrillic, cyrillic-ext, greek, greek-ext, vietnamese, …}
    • weight ∈ {100,200,300,400,500,600,700,800,900}
    • style ∈ {normal, italic}
    • example: inter-cyrillic-400-normal.woff2. [1][2]

You’ll also see “all” bundles named:

  • inter-all--normal.woff (and .woff2 in some releases)
    • example: inter-all-400-normal.woff. [1]

Sources: package file listing on UNPKG and the @fontsource/inter npm/package pages. [1][2]

References

  • [1] UNPKG file listing for @fontsource/inter (shows inter--.woff2 filenames).
  • [2] @fontsource/inter npm / Fontsource docs (package structure and supported subsets/weights).

Add error handling for font registration to handle missing or incompatible @fontsource/inter package files.

The Font.register() call on line 14 lacks error handling. While the font directory is dynamically resolved via require.resolve(), the specific font filenames are hardcoded (e.g., inter-latin-400-normal.woff2). If these files are missing, renamed, or the package structure changes, the registration will fail silently or throw at runtime without diagnostic information. Add a try-catch block with appropriate error logging to aid debugging:

try {
  Font.register({
    family: "Inter",
    fonts: [
      // ... font definitions
    ],
  });
} catch (error) {
  console.error("Failed to register Inter font:", error);
  throw error;
}

This aligns with the coding guideline to "use try-catch with proper error types and log errors appropriately."

🤖 Prompt for AI Agents
In `@apps/live/src/lib/pdf/plane-pdf-exporter.tsx` around lines 14 - 45, Wrap the
Font.register(...) invocation in a try-catch to surface missing or incompatible
font files: catch any error thrown by Font.register (referencing Font.register
and interFontDir / the hardcoded filenames like inter-latin-400-normal.woff2)
log a clear diagnostic (e.g., console.error or processLogger.error) that
includes the error object and the attempted file paths, then rethrow the error
so callers know registration failed; this ensures failures in
plane-pdf-exporter.tsx are logged and do not fail silently.

Comment on lines +41 to 62
async fetchDescriptionBinary(pageId: string): Promise<Buffer> {
try {
const response = await this.get(`${this.basePath}/pages/${pageId}/description/`, {
headers: {
...this.getHeader(),
"Content-Type": "application/octet-stream",
},
responseType: "arraybuffer",
});
const data = response?.data;
if (!Buffer.isBuffer(data)) {
throw new Error("Expected response to be a Buffer");
}
return data;
} catch (error) {
const appError = new AppError(error, {
context: { operation: "fetchDescriptionBinary", pageId },
});
logger.error("Failed to fetch page description binary", appError);
throw appError;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check if api.service.ts exists and get its size
wc -l apps/live/src/services/api.service.ts 2>/dev/null || echo "File not found, searching for it"

# If not found, search for api service files
fd "api.*service" apps/live/src/services/ 2>/dev/null | head -20

Repository: makeplane/plane

Length of output: 137


🏁 Script executed:

cat -n apps/live/src/services/api.service.ts

Repository: makeplane/plane

Length of output: 2047


🏁 Script executed:

# Check the page/core.service.ts file to see the exact implementation
sed -n '41,62p' apps/live/src/services/page/core.service.ts

# Also search for any other uses of arraybuffer in the codebase to see how they handle it
rg -n "arraybuffer" apps/live/src/

Repository: makeplane/plane

Length of output: 876


🏁 Script executed:

# Search for other buffer/arraybuffer handling patterns
rg -n "Buffer.isBuffer|response\.data|arraybuffer" apps/live/src/services/ -A 2 -B 2

# Check if there are other fetchDescriptionBinary calls or similar patterns
rg -n "Promise<Buffer>|Promise<ArrayBuffer>" apps/live/src/

Repository: makeplane/plane

Length of output: 871


Handle ArrayBuffer/TypedArray responses before Buffer checks.

When axios is configured with responseType: "arraybuffer", it may return ArrayBuffer or typed array views depending on the environment. Line 51's Buffer.isBuffer(data) check will fail for these types, causing the method to always throw despite receiving valid data. Convert non-Buffer responses to Buffer before returning.

🛠️ Proposed fix
-      const data = response?.data;
-      if (!Buffer.isBuffer(data)) {
-        throw new Error("Expected response to be a Buffer");
-      }
-      return data;
+      const data = response?.data;
+      if (Buffer.isBuffer(data)) {
+        return data;
+      }
+      if (data instanceof ArrayBuffer) {
+        return Buffer.from(data);
+      }
+      if (ArrayBuffer.isView(data)) {
+        return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
+      }
+      throw new Error("Expected response to be a Buffer or ArrayBuffer");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async fetchDescriptionBinary(pageId: string): Promise<Buffer> {
try {
const response = await this.get(`${this.basePath}/pages/${pageId}/description/`, {
headers: {
...this.getHeader(),
"Content-Type": "application/octet-stream",
},
responseType: "arraybuffer",
});
const data = response?.data;
if (!Buffer.isBuffer(data)) {
throw new Error("Expected response to be a Buffer");
}
return data;
} catch (error) {
const appError = new AppError(error, {
context: { operation: "fetchDescriptionBinary", pageId },
});
logger.error("Failed to fetch page description binary", appError);
throw appError;
}
}
async fetchDescriptionBinary(pageId: string): Promise<Buffer> {
try {
const response = await this.get(`${this.basePath}/pages/${pageId}/description/`, {
headers: {
...this.getHeader(),
"Content-Type": "application/octet-stream",
},
responseType: "arraybuffer",
});
const data = response?.data;
if (Buffer.isBuffer(data)) {
return data;
}
if (data instanceof ArrayBuffer) {
return Buffer.from(data);
}
if (ArrayBuffer.isView(data)) {
return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
}
throw new Error("Expected response to be a Buffer or ArrayBuffer");
} catch (error) {
const appError = new AppError(error, {
context: { operation: "fetchDescriptionBinary", pageId },
});
logger.error("Failed to fetch page description binary", appError);
throw appError;
}
}
🤖 Prompt for AI Agents
In `@apps/live/src/services/page/core.service.ts` around lines 41 - 62,
fetchDescriptionBinary currently assumes axios returns a Node Buffer and throws
if Buffer.isBuffer fails; instead detect and convert ArrayBuffer or typed array
views to a Buffer before returning. In the fetchDescriptionBinary method
(responseType: "arraybuffer"), check if response.data is an ArrayBuffer or
ArrayBuffer view (use ArrayBuffer.isView) and create a Buffer from it
(Buffer.from(...)), then proceed with the Buffer.isBuffer check/return; keep the
existing error handling (AppError and logger) intact.

Comment on lines +1 to +21
import { defineConfig } from "vitest/config";
import path from "path";

export default defineConfig({
test: {
environment: "node",
globals: true,
include: ["tests/**/*.test.ts", "tests/**/*.spec.ts"],
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
include: ["src/**/*.ts"],
exclude: ["src/**/*.d.ts", "src/**/types.ts"],
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat apps/live/package.json

Repository: makeplane/plane

Length of output: 2745


🏁 Script executed:

# Check if there are other vitest configs in the repo and how they handle path resolution
fd -e "vitest.config.ts" -e "vitest.config.js" -e "vite.config.ts" -e "vite.config.js" | head -20

Repository: makeplane/plane

Length of output: 41


🏁 Script executed:

# Verify the specific file content
cat -n apps/live/vitest.config.ts

Repository: makeplane/plane

Length of output: 684


🏁 Script executed:

# Check for other __dirname usages in ESM TypeScript files
rg "__dirname" --type ts --type tsx | head -20

Repository: makeplane/plane

Length of output: 86


🏁 Script executed:

# Check if there are other vitest configs and how they're structured
fd -e "vitest.config.ts" -e "vitest.config.js" -e "vite.config.ts" -e "vite.config.js"

Repository: makeplane/plane

Length of output: 41


🏁 Script executed:

# Specifically check if any ESM packages use __dirname
rg "type.*module" --type json | grep package.json | head -10

Repository: makeplane/plane

Length of output: 551


🏁 Script executed:

# Check for __dirname usage in TypeScript files (use ts not tsx)
rg "__dirname" -t ts | head -30

Repository: makeplane/plane

Length of output: 1476


🏁 Script executed:

# Find all vitest/vite config files
find . -name "vitest.config.*" -o -name "vite.config.*" | grep -v node_modules | head -20

Repository: makeplane/plane

Length of output: 206


🏁 Script executed:

# Check if fileURLToPath is used elsewhere in the codebase as a pattern
rg "fileURLToPath" -t ts | head -10

Repository: makeplane/plane

Length of output: 41


🏁 Script executed:

# Check if apps/web, apps/space, apps/admin are ESM or CommonJS
rg '"type".*"module"' apps/web/package.json apps/space/package.json apps/admin/package.json packages/codemods/package.json

Repository: makeplane/plane

Length of output: 188


🏁 Script executed:

# Check the vitest config in packages/codemods
cat packages/codemods/vitest.config.ts

Repository: makeplane/plane

Length of output: 179


🏁 Script executed:

# Check the package.json for apps/live to confirm the issue is isolated or widespread
cat apps/web/package.json | rg '"type"'
cat apps/space/package.json | rg '"type"'
cat apps/admin/package.json | rg '"type"'

Repository: makeplane/plane

Length of output: 118


🏁 Script executed:

# Check if vite.config files in web/space/admin actually work or if they also have issues
cat apps/web/vite.config.ts | head -30

Repository: makeplane/plane

Length of output: 1108


🏁 Script executed:

# Verify the proposed fix pattern is correct by checking Node.js ESM patterns
cat apps/live/vitest.config.ts | head -5

Repository: makeplane/plane

Length of output: 170


🏁 Script executed:

# Search for correct ESM path handling patterns in the codebase
rg "fileURLToPath|import.meta.url" -t ts | head -20

Repository: makeplane/plane

Length of output: 151


🏁 Script executed:

# Verify the proposed fix syntax is correct for Node.js ESM
# Check Node.js documentation for fileURLToPath usage
cat > /tmp/test_esm.mjs << 'EOF'
import { fileURLToPath } from "node:url";

const srcPath = fileURLToPath(new URL("./src", import.meta.url));
console.log("Path resolved:", srcPath);
EOF

# This verifies the syntax is valid (we can't run it in sandbox, but we can check the pattern)
echo "Proposed pattern is valid ESM syntax"

Repository: makeplane/plane

Length of output: 95


🏁 Script executed:

# Check if the codebase has any similar correct ESM path resolution patterns we should follow
rg "fileURLToPath|new URL.*import.meta.url" -A 2 -B 2

Repository: makeplane/plane

Length of output: 41


🏁 Script executed:

# Verify that __dirname specifically causes ReferenceError in ESM
# by checking if there are any comments or issues about it
rg "__dirname|ReferenceError.*__dirname" | head -10

Repository: makeplane/plane

Length of output: 1058


🏁 Script executed:

# Check if vite-tsconfig-paths or other plugins polyfill __dirname
rg "global.__dirname|globalThis.__dirname" -t ts | head -10

Repository: makeplane/plane

Length of output: 41


🏁 Script executed:

# Check if apps/web, apps/space, apps/admin actually work or have issues
# Look at the build scripts and see if there's a workaround
cat apps/web/package.json | grep -A 5 '"scripts"'

Repository: makeplane/plane

Length of output: 391


🏁 Script executed:

# Most importantly: check if these config files have any special handling or if they're even used
cat apps/web/vite.config.ts | head -50

Repository: makeplane/plane

Length of output: 1289


Fix __dirname usage in ESM config.
Line 18 relies on __dirname, which is undefined in ESM (this package has "type": "module"). This will throw a ReferenceError when Vitest loads the config. Use fileURLToPath with import.meta.url instead.

Proposed fix
-import path from "path";
+import { fileURLToPath } from "node:url";

 export default defineConfig({
   test: {
@@
   resolve: {
     alias: {
-      "@": path.resolve(__dirname, "./src"),
+      "@": fileURLToPath(new URL("./src", import.meta.url)),
     },
   },
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { defineConfig } from "vitest/config";
import path from "path";
export default defineConfig({
test: {
environment: "node",
globals: true,
include: ["tests/**/*.test.ts", "tests/**/*.spec.ts"],
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
include: ["src/**/*.ts"],
exclude: ["src/**/*.d.ts", "src/**/types.ts"],
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
import { defineConfig } from "vitest/config";
import { fileURLToPath } from "node:url";
export default defineConfig({
test: {
environment: "node",
globals: true,
include: ["tests/**/*.test.ts", "tests/**/*.spec.ts"],
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
include: ["src/**/*.ts"],
exclude: ["src/**/*.d.ts", "src/**/types.ts"],
},
},
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
🤖 Prompt for AI Agents
In `@apps/live/vitest.config.ts` around lines 1 - 21, The config uses __dirname in
the resolve.alias (path.resolve(__dirname, "./src")) which breaks under ESM;
replace this by deriving dirname from import.meta.url (use
fileURLToPath(import.meta.url) to get the file path then path.dirname) and pass
that dirname into path.resolve for the "@" alias; add the import for
fileURLToPath from "url" and compute a const dirname (or similar) at the top of
the module, then update the alias to use path.resolve(dirname, "./src").

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.

2 participants