Skip to content

feat: Lazy-load @napi-rs/canvas to fix Jest open handle warnings#89

Open
JonRC wants to merge 1 commit intomehmet-kozan:mainfrom
JonRC:fix/lazy-canvas-factory
Open

feat: Lazy-load @napi-rs/canvas to fix Jest open handle warnings#89
JonRC wants to merge 1 commit intomehmet-kozan:mainfrom
JonRC:fix/lazy-canvas-factory

Conversation

@JonRC
Copy link

@JonRC JonRC commented Feb 4, 2026

Summary

This PR implements lazy-loading for the @napi-rs/canvas module to prevent Jest from reporting "CustomGC" open handle warnings when using pdf-parse for text-only operations.

Problem

When importing pdf-parse in a Jest test environment, Jest's --detectOpenHandles flag reports a "CustomGC" open handle. This occurs because:

  1. pdfjs-dist automatically loads @napi-rs/canvas in Node.js environments
  2. The canvas module creates a native GC (garbage collection) handle
  3. This handle persists for the process lifetime, triggering Jest's warning

This affects users who only need text extraction (getText(), getInfo()) but still see canvas-related warnings in their test output.

Solution

Introduced LazyCanvasFactory that defers loading of @napi-rs/canvas until canvas operations are actually needed:

  • New file: src/pdf-parse/LazyCanvasFactory.ts - Factory that lazy-loads the canvas module on first use
  • Modified: src/pdf-parse/PDFParse.ts - Uses LazyCanvasFactory as default CanvasFactory in Node.js
  • Modified: src/pdf-parse/index.ts - Exports new utilities for explicit control
  • New tests: tests/unit/test-cleanup/lazy-canvas.test.ts - Verifies lazy loading behavior

Key Changes

  1. Lazy Loading: Canvas module is only loaded when getImage() or getScreenshot() is called
  2. Cleanup: destroy() now calls clearCanvasCache() if canvas was loaded
  3. Exports: Users can access isCanvasLoaded() and clearCanvasCache() for explicit control
  4. Backward Compatible: Users can still pass custom CanvasFactory as before

Files Changed

File Change
src/pdf-parse/LazyCanvasFactory.ts New lazy-loading canvas factory
src/pdf-parse/PDFParse.ts Use LazyCanvasFactory as default
src/pdf-parse/index.ts Export new utilities
tests/unit/test-cleanup/lazy-canvas.test.ts New tests

Test Plan

  • All existing unit tests pass (105 tests)
  • New lazy-canvas tests pass (4 tests)
  • getImage() works correctly with lazy-loaded canvas
  • getScreenshot() works correctly with lazy-loaded canvas
  • getText() works without loading canvas module
  • Code passes Biome linting
  • Manual verification in Jest project with --detectOpenHandles

- Add LazyCanvasFactory that defers canvas module loading until needed
- Set LazyCanvasFactory as default in Node.js environments
- Add clearCanvasCache() cleanup in destroy() method
- Export isCanvasLoaded() and clearCanvasCache() for explicit control
- Add tests for lazy loading behavior

Fixes issue where Jest reports "CustomGC" open handle when using
pdf-parse for text-only operations. The canvas module is now only
loaded when getImage() or getScreenshot() is called.
@JonRC
Copy link
Author

JonRC commented Feb 4, 2026

@mehmet-kozan The CI checks are failing because fork PRs don't have access to repository secrets needed for the private submodule checkout. Could you please manually re-run the failed workflows?

@JonRC
Copy link
Author

JonRC commented Feb 4, 2026

@mehmet-kozan, also let me know if this improvement makes sense.

@mehmet-kozan
Copy link
Owner

@JonRC thanks for your contribution. I was able to reproduce the issue using the jest --detectOpenHandles flag. There is already an option for a custom canvas factory, see:

We can add a integration test for jest --detectOpenHandles flag tod tests/integration dir.
Node.js-related code must be placed in the pdf-parse/worker or pdf-parse/node submodule for browser builds. We can add the LazyCanvasFactory implementation to one of these submodules.

I will also add you permanently to the pro package repository, see: #77

We can rework this feature together in another branch if you’d like. This would also allow me to test the contribution workflow.

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