Fix: externalize database drivers to prevent dynamic require error#291
Fix: externalize database drivers to prevent dynamic require error#291
Conversation
…o ESM The optional database driver packages (pg, mysql2, etc.) were being bundled by tsup into ESM chunks. When their internal CJS require() calls for Node.js built-ins like "events" executed in the ESM context, it caused "Dynamic require of 'events' is not supported" at runtime. Marking them as external ensures they are loaded from node_modules at runtime via standard import statements instead of being inlined. Closes #288 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Updates the backend build configuration to ensure DB driver packages remain unbundled so DBHub can dynamically load optional connector drivers at runtime without triggering Node ESM “Dynamic require” failures.
Changes:
- Mark common database driver packages as
externalintsup.config.tsto prevent bundling their CJS internals into ESM chunks.
| // Database drivers are optionalDependencies loaded at runtime via dynamic | ||
| // import(). They must be external so tsup does not bundle their CJS code | ||
| // into ESM chunks (which causes "Dynamic require of X is not supported"). | ||
| external: ['pg', 'mysql2', 'mariadb', 'mssql', 'better-sqlite3'], |
There was a problem hiding this comment.
external: ['mysql2', ...] does not externalize the subpath import used by the MySQL connector (import mysql from "mysql2/promise"). With esbuild/tsup, externals are exact-match unless you add patterns, so mysql2/promise may still get bundled and reintroduce the CJS-in-ESM require() runtime failure for the MySQL connector. Add mysql2/promise (or use a wildcard like mysql2/*) to the external list.
| external: ['pg', 'mysql2', 'mariadb', 'mssql', 'better-sqlite3'], | |
| external: ['pg', 'mysql2', 'mysql2/promise', 'mariadb', 'mssql', 'better-sqlite3'], |
There was a problem hiding this comment.
Verified — esbuild (which tsup uses) treats external entries as prefix matches, not exact matches. external: ['mysql2'] already externalizes mysql2/promise.
Evidence from the current build output:
# dist/mysql-I35IQ2GH.js line 16:
import mysql from "mysql2/promise";
The mysql chunk is 469 lines (just connector code), not the thousands of lines mysql2 would add if bundled. No change needed here.
Summary
optionalDependencieswith dynamicimport(), but tsup bundled their CJS code into ESM chunksrequire("events")calls fail with "Dynamic require of 'events' is not supported"externallist ensures they're loaded fromnode_modulesat runtime via standard ES importsChanges
external: ['pg', 'mysql2', 'mariadb', 'mssql', 'better-sqlite3']totsup.config.tsCloses #288
Test plan
pnpm run build:backendsucceedsimport pg from "pg"(external) instead of inlining pg internalsDynamic require/__require/createRequireshims in outputload-connectors.test.tspasses (10/10)🤖 Generated with Claude Code