Fix module resolution and Vite bundling for browser builds#498
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
|
@copilot fix 2026-02-03T05:00:23.8652084Z examples/app-react-crud build: Continue by describing the network in your application: |
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Fixed in commit 1e0c9b0. The issue was that Solution:
The Vite build now succeeds: |
There was a problem hiding this comment.
Pull request overview
This pull request fixes TypeScript compilation and Vite bundling failures when importing ObjectStack configuration files from example packages. The root cause was three-fold: missing package exports, incorrect import paths, and Node.js built-in modules being included in browser bundles.
Changes:
- Added package export configurations to example packages (app-todo, app-crm, plugin-bi) to expose their
objectstack.config.tsfiles - Corrected import path from
@objectstack/example-todoto@example/app-todoin browser.ts - Wrapped Node.js module imports (
crypto,module) in eval() calls to prevent bundlers from including them in browser builds
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| examples/app-todo/package.json | Added exports configuration to expose objectstack.config.ts for external imports |
| examples/app-crm/package.json | Added exports configuration to expose objectstack.config.ts for external imports |
| examples/plugin-bi/package.json | Added exports configuration to expose objectstack.config.ts (inconsistent with main field pointing to src/index.ts) |
| examples/app-react-crud/src/mocks/browser.ts | Fixed incorrect package name from @objectstack/example-todo to @example/app-todo |
| packages/core/src/logger.ts | Moved module import to runtime using eval() to avoid bundler inclusion in browser builds |
| packages/core/src/security/plugin-signature-verifier.ts | Moved crypto import to runtime using eval() to avoid bundler inclusion in browser builds |
| "main": "src/index.ts", | ||
| "types": "src/index.ts", | ||
| "exports": { | ||
| ".": "./src/index.ts", | ||
| "./objectstack.config": "./objectstack.config.ts" | ||
| }, |
There was a problem hiding this comment.
Inconsistent package exports configuration. The "main" field points to "src/index.ts" (a plugin implementation), while the "exports" includes "./objectstack.config" pointing to "objectstack.config.ts" (a manifest). This differs from the pattern used in app-todo and app-crm where "main" points to "objectstack.config.ts".
Since app-host imports from "@example/plugin-bi/objectstack.config", the exports configuration is correct, but the "main" field should also point to "objectstack.config.ts" for consistency, or the package should clarify its dual purpose (both plugin implementation and manifest export).
| // Dynamic import for Node.js crypto module (using eval to avoid bundling issues) | ||
| // @ts-ignore - dynamic require for Node.js | ||
| cryptoModule = eval('require("crypto")'); |
There was a problem hiding this comment.
Using eval() to dynamically load Node.js modules creates a security risk and violates Content Security Policy (CSP) in environments that enforce 'unsafe-eval' restrictions. While this approach works around bundler issues, it has operational implications:
- Security: eval() execution can be exploited if an attacker can control the string being evaluated, even though the current usage appears safe
- CSP Compliance: Browsers with strict CSP will block eval(), making this code fail in secure contexts
- Code Analysis: Static analysis tools and bundlers cannot track the dependency
Consider these alternatives:
- Use conditional dynamic imports:
await import('crypto')with proper error handling - Use Vite/Rollup's external configuration to exclude Node.js modules from browser builds
- Create separate entry points for Node.js and browser environments
The current approach may work but has significant operational and security tradeoffs that should be documented or addressed with a more robust solution.
| // Create require function dynamically for Node.js (avoids bundling issues in browser) | ||
| // @ts-ignore - dynamic import of Node.js module | ||
| const { createRequire } = eval('require("module")'); | ||
| this.require = createRequire(import.meta.url); | ||
|
|
||
| // Synchronous import for Pino using createRequire (works in ESM) |
There was a problem hiding this comment.
Using eval() to dynamically load Node.js modules creates security and operational risks:
- Security: eval() can be exploited and violates Content Security Policy (CSP) with 'unsafe-eval' restrictions
- Runtime Failures: This code will fail in browser environments with strict CSP
- Maintainability: Static analysis tools cannot track these dynamic dependencies
Consider these alternatives:
- Use conditional dynamic imports with try/catch:
await import('module') - Configure Vite/Rollup to externalize Node.js built-ins for browser builds
- Create separate build targets for Node.js and browser environments
The pattern is repeated from plugin-signature-verifier.ts, suggesting a systemic approach to this problem. A more robust, security-conscious solution should be implemented across the codebase.
| // Create require function dynamically for Node.js (avoids bundling issues in browser) | |
| // @ts-ignore - dynamic import of Node.js module | |
| const { createRequire } = eval('require("module")'); | |
| this.require = createRequire(import.meta.url); | |
| // Synchronous import for Pino using createRequire (works in ESM) | |
| // Use CommonJS require directly when available (Node.js CJS environments) | |
| if (typeof require !== 'undefined') { | |
| // @ts-ignore - require is only available in Node.js CJS | |
| this.require = require; | |
| } else { | |
| // In environments without require (e.g., Node ESM), fail gracefully and use console fallback | |
| throw new Error('CommonJS require is not available'); | |
| } | |
| // Synchronous import for Pino using CommonJS require |
TypeScript compilation and Vite bundling failed when importing
objectstack.config.tsfrom example packages. Three issues identified:package.json@objectstack/example-todo→@example/app-todomodule,crypto) imported at top level in@objectstack/corecaused Vite bundling failuresChanges
Package exports - Added
exports,main, andtypesfields to:@example/app-todo/package.json@example/app-crm/package.json@example/plugin-bi/package.json{ "main": "./objectstack.config.ts", "types": "./objectstack.config.ts", "exports": { ".": "./objectstack.config.ts", "./objectstack.config": "./objectstack.config.ts" } }Import path - Fixed in
examples/app-react-crud/src/mocks/browser.ts:Universal library bundling - Fixed Node.js module imports in
@objectstack/core:packages/core/src/logger.ts- Movedmoduleimport to runtime using eval for Node.js environment detectionpackages/core/src/security/plugin-signature-verifier.ts- Movedcryptoimport to runtime using evalThese changes prevent browser bundlers (Vite/Rollup) from attempting to include Node.js-only modules in browser bundles while maintaining full functionality in Node.js environments.
Testing
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.