Fix Next.js build failure with runtime dynamic imports in WidgetRegistry#429
Fix Next.js build failure with runtime dynamic imports in WidgetRegistry#429
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…idgetRegistry Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Fixes a Next.js (Turbopack) build failure caused by bundlers attempting static analysis on runtime-determined widget module URLs, by switching WidgetRegistry’s module loader to a runtime-only dynamic import pattern. Also strengthens guidance around the security implications of loading widgets from URLs and updates the site build config to transpile workspace packages.
Changes:
- Replace
import(source.url)with aFunction-constructed dynamic import to bypass bundler static analysis for runtime URLs. - Add security warnings to
WidgetSourceModuledocs about trusted URL requirements and CSP constraints. - Add
transpilePackagestoapps/siteNext.js config for monorepo/workspace package transpilation.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| packages/types/src/widget.ts | Expands documentation for module-based widget sources with security/CSP warnings. |
| packages/core/src/registry/WidgetRegistry.ts | Implements runtime-only dynamic import for source.type: 'module' widget loading. |
| apps/site/next.config.mjs | Adds transpilePackages to ensure workspace dependencies are transpiled by Next.js. |
| // CSP Consideration: If your application uses strict Content Security Policy, | ||
| // ensure dynamic imports are allowed or use 'inline' or 'registry' source types. |
There was a problem hiding this comment.
This CSP guidance is incomplete: the Function constructor itself requires allowing unsafe-eval in CSP (in addition to any dynamic import allowances). Please adjust the comment so users don’t interpret this as only a dynamic-import restriction.
| // CSP Consideration: If your application uses strict Content Security Policy, | |
| // ensure dynamic imports are allowed or use 'inline' or 'registry' source types. | |
| // CSP Consideration: This pattern relies on the Function constructor plus | |
| // dynamic import(). In a strict Content Security Policy, this typically | |
| // requires allowing 'unsafe-eval' in script-src (for Function) and ensuring | |
| // that dynamic module imports from the widget origins are permitted. If you | |
| // cannot relax CSP to allow this, use 'inline' or 'registry' source types | |
| // instead of 'module'. |
| const dynamicImport = new Function('url', 'return import(url)'); | ||
| const mod = await dynamicImport(source.url); |
There was a problem hiding this comment.
new Function('url', 'return import(url)') is being constructed each time a module widget is resolved. Consider hoisting this to a module-level constant (and giving it an explicit (url: string) => Promise<unknown> type) to avoid repeated Function compilation and to centralize/audit the unsafe-eval usage.
| const dynamicImport = new Function('url', 'return import(url)'); | ||
| const mod = await dynamicImport(source.url); | ||
| const exportName = source.exportName ?? 'default'; | ||
| const component = mod[exportName]; |
There was a problem hiding this comment.
There are unit tests for WidgetRegistry, but none exercise the source.type === 'module' branch. Since this PR changes how module widgets are imported, please add a test that loads a small fixture module URL and verifies export resolution (including exportName handling) to prevent regressions.
| * and controlled by your application. This feature uses dynamic imports | ||
| * which bypass static analysis and may be restricted by Content Security Policy. |
There was a problem hiding this comment.
The CSP note here only mentions dynamic imports, but this code path also uses new Function(...), which is blocked unless script-src allows unsafe-eval. Please update the documentation to explicitly call out the unsafe-eval requirement (or clarify the alternative source types for environments where unsafe-eval is disallowed).
| * and controlled by your application. This feature uses dynamic imports | |
| * which bypass static analysis and may be restricted by Content Security Policy. | |
| * and controlled by your application. | |
| * | |
| * This feature typically uses dynamic `import()` to load widget code and, | |
| * depending on your runtime loader, may also rely on runtime evaluation | |
| * (for example, via `new Function(...)`) to wire up the widget. | |
| * | |
| * Both mechanisms can be restricted by Content Security Policy (CSP): | |
| * - Dynamic `import()` requires the module URL to be allowed by `script-src`. | |
| * - `new Function(...)` is blocked unless `script-src` explicitly allows | |
| * `'unsafe-eval'`. | |
| * | |
| * In environments where `'unsafe-eval'` is disallowed, prefer `inline` or | |
| * `registry` widget sources, or provide a custom loader implementation that | |
| * does not rely on `new Function(...)`. |
Turbopack fails on
await import(source.url)when bundling WidgetRegistry because it attempts static analysis on runtime-determined URLs.Changes
Core fix - Replace static import with Function constructor pattern:
Security documentation - Added warnings to
WidgetSourceModuletype that URLs must be from trusted sources, never user input.Next.js configuration - Added
transpilePackagesarray for workspace dependencies to ensure proper TypeScript transpilation.Notes
inlineorregistrysource types insteadOriginal prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.