Skip to content

fix(new-compiler): use dynamic import for lmdb to avoid CJS transform errors#2015

Merged
AndreyHirsa merged 1 commit intomainfrom
fix/lmdb-dynamic-import
Feb 24, 2026
Merged

fix(new-compiler): use dynamic import for lmdb to avoid CJS transform errors#2015
AndreyHirsa merged 1 commit intomainfrom
fix/lmdb-dynamic-import

Conversation

@AndreyHirsa
Copy link
Contributor

@AndreyHirsa AndreyHirsa commented Feb 24, 2026

Summary

Use dynamic import for lmdb to fix SyntaxError: 'super' keyword unexpected here caused by bundlers/require hooks transforming lmdb's CJS bundle.

Changes

  • Changed openDatabaseConnection to async and load lmdb via await import("lmdb") at call time instead of top-level static import

Testing

Business logic tests added:

  • Reproduced the error with Next.js 15.1.11 — confirmed fix resolves it
  • Existing metadata unit tests pass
  • All tests pass locally

Visuals

N/A - no UI changes.

Checklist

  • Changeset added (if version bump needed)
  • No breaking changes (or documented below)

Summary by CodeRabbit

Bug Fixes

  • Resolved a SyntaxError that occurred when using bundlers or require hooks with the compiler package.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

A patch release for @lingo.dev/compiler is prepared to fix a SyntaxError caused by bundlers transforming lmdb's CommonJS bundle. The openDatabaseConnection function is refactored to use dynamic import of lmdb at runtime instead of direct import, and is converted to async.

Changes

Cohort / File(s) Summary
Release Documentation
.changeset/hungry-taxes-mate.md
New changeset entry marking a patch release with a fix for SyntaxError triggered by bundler transformation of lmdb's CJS bundle.
Database Connection Refactoring
packages/new-compiler/src/metadata/manager.ts
Converted openDatabaseConnection to async function using dynamic import of lmdb at runtime. Switched lmdb from direct value import to type-only import. Updated call site in runWithDbConnection to await the async function.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • vrcprl
  • cherkanovart

Poem

🐰 A bundler's curse, a CJS bind,
Dynamic imports come to mind,
Async flows where sync once stood,
Now lmdb loads as it should! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: switching lmdb to dynamic import to fix CJS transform errors.
Description check ✅ Passed The description covers the summary, key changes, and testing information. It follows most template sections but omits the test coverage details section.
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 (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/lmdb-dynamic-import

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/new-compiler/src/metadata/manager.ts (1)

21-32: ⚠️ Potential issue | 🟡 Minor

Module-load failures get a misleading error label.

await import("lmdb") (and fs.mkdirSync) share the same catch block that always emits "Failed to open LMDB at ${dbPath}". If lmdb fails to load (e.g., native binding ABI mismatch), the outer label incorrectly implies a DB-open failure. The root message is forwarded so it's still diagnosable, but it slows debugging.

🔍 Proposed fix: distinguish import failure from open failure
 async function openDatabaseConnection(dbPath: string, noSync: boolean): Promise<RootDatabase> {
-  try {
-    fs.mkdirSync(dbPath, { recursive: true });
-    const { open } = await import("lmdb");
-    return open({
-      path: dbPath,
-      compression: true,
-      noSync,
-    });
-  } catch (error) {
-    const message = error instanceof Error ? error.message : String(error);
-    throw new Error(`Failed to open LMDB at ${dbPath}: ${message}`);
-  }
+  fs.mkdirSync(dbPath, { recursive: true });
+  let open: (typeof import("lmdb"))["open"];
+  try {
+    ({ open } = await import("lmdb"));
+  } catch (error) {
+    const message = error instanceof Error ? error.message : String(error);
+    throw new Error(`Failed to load lmdb module: ${message}`);
+  }
+  try {
+    return open({
+      path: dbPath,
+      compression: true,
+      noSync,
+    });
+  } catch (error) {
+    const message = error instanceof Error ? error.message : String(error);
+    throw new Error(`Failed to open LMDB at ${dbPath}: ${message}`);
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/new-compiler/src/metadata/manager.ts` around lines 21 - 32, The
current try/catch wraps both fs.mkdirSync and await import("lmdb") so import
failures get mislabeled as "Failed to open LMDB"; split the operations and add
distinct error handling: perform fs.mkdirSync(dbPath, { recursive: true })
first, then wrap await import("lmdb") in its own try/catch and throw a clear
"Failed to load lmdb module: <error>" message if it fails, and finally call
open({ path: dbPath, compression: true, noSync }) in a separate try/catch that
throws "Failed to open LMDB at ${dbPath}: <error>" on failure; reference the
import("lmdb"), open({...}), fs.mkdirSync, dbPath and noSync identifiers when
making these localized error messages.
🧹 Nitpick comments (1)
packages/new-compiler/src/metadata/manager.ts (1)

23-23: Optional: cache the resolved open reference across calls.

While Node.js caches the module after the first await import("lmdb"), a new microtask is scheduled on every invocation. A module-level lazy singleton makes the caching explicit and eliminates that overhead.

♻️ Proposed refactor: lazy-cached import
+let _open: (typeof import("lmdb"))["open"] | undefined;
+async function getLmdbOpen(): Promise<(typeof import("lmdb"))["open"]> {
+  if (!_open) {
+    ({ open: _open } = await import("lmdb"));
+  }
+  return _open;
+}

 async function openDatabaseConnection(dbPath: string, noSync: boolean): Promise<RootDatabase> {
   try {
     fs.mkdirSync(dbPath, { recursive: true });
-    const { open } = await import("lmdb");
+    const open = await getLmdbOpen();
     return open({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/new-compiler/src/metadata/manager.ts` at line 23, The code currently
does an await import("lmdb") inside the function, scheduling a microtask each
call; introduce a module-level lazy singleton (e.g., a cachedOpen or getOpen
reference) to store the resolved open function and reuse it across invocations;
change usages that currently do const { open } = await import("lmdb") to
retrieve the cached open (initializing it on first call) so subsequent calls
reuse the same open reference without re-importing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/new-compiler/src/metadata/manager.ts`:
- Around line 21-32: The current try/catch wraps both fs.mkdirSync and await
import("lmdb") so import failures get mislabeled as "Failed to open LMDB"; split
the operations and add distinct error handling: perform fs.mkdirSync(dbPath, {
recursive: true }) first, then wrap await import("lmdb") in its own try/catch
and throw a clear "Failed to load lmdb module: <error>" message if it fails, and
finally call open({ path: dbPath, compression: true, noSync }) in a separate
try/catch that throws "Failed to open LMDB at ${dbPath}: <error>" on failure;
reference the import("lmdb"), open({...}), fs.mkdirSync, dbPath and noSync
identifiers when making these localized error messages.

---

Nitpick comments:
In `@packages/new-compiler/src/metadata/manager.ts`:
- Line 23: The code currently does an await import("lmdb") inside the function,
scheduling a microtask each call; introduce a module-level lazy singleton (e.g.,
a cachedOpen or getOpen reference) to store the resolved open function and reuse
it across invocations; change usages that currently do const { open } = await
import("lmdb") to retrieve the cached open (initializing it on first call) so
subsequent calls reuse the same open reference without re-importing.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e08bbca and 81736ac.

📒 Files selected for processing (2)
  • .changeset/hungry-taxes-mate.md
  • packages/new-compiler/src/metadata/manager.ts

@AndreyHirsa AndreyHirsa merged commit 13aeb36 into main Feb 24, 2026
14 checks passed
@AndreyHirsa AndreyHirsa deleted the fix/lmdb-dynamic-import branch February 24, 2026 11:04
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