fix(@angular/build): allow serving node_modules assets in monorepo setups#32939
fix(@angular/build): allow serving node_modules assets in monorepo setups#32939maruthang wants to merge 1 commit intoangular:mainfrom
Conversation
…tups In monorepo setups where node_modules are hoisted to a parent directory above the Angular workspace root, assets loaded from those packages (e.g., fonts from CSS files) were blocked by Vite's dev server with a 403 error. This was caused by the explicit `server.fs.allow` configuration only including the workspace root's `node_modules`, which overrides Vite's default package root detection. By adding `searchForPackageRoot()` to the allow list, the monorepo root directory is included, allowing access to hoisted node_modules. Fixes angular#31016
There was a problem hiding this comment.
Code Review
This pull request updates the Vite dev server configuration to include the package root in the allowed file system paths, improving support for monorepo setups where dependencies might be hoisted. The review feedback recommends maintaining the project's lazy-loading pattern by using dynamic imports for Vite runtime utilities instead of a top-level import and identifies a redundant path entry that can be removed.
| import { readFile } from 'node:fs/promises'; | ||
| import { join } from 'node:path'; | ||
| import type { Connect, InlineConfig, SSROptions, ServerOptions } from 'vite'; | ||
| import { type Connect, type InlineConfig, type SSROptions, type ServerOptions, searchForPackageRoot } from 'vite'; |
There was a problem hiding this comment.
This change introduces a top-level runtime import of vite. The existing code in this file follows a pattern of using dynamic imports for Vite's runtime utilities (see line 152) to avoid eagerly loading the vite package when the module is first evaluated. To maintain this pattern, searchForPackageRoot should be imported dynamically where it is used, keeping this import as type-only.
| import { type Connect, type InlineConfig, type SSROptions, type ServerOptions, searchForPackageRoot } from 'vite'; | |
| import type { Connect, InlineConfig, SSROptions, ServerOptions } from 'vite'; |
| join(serverOptions.workspaceRoot, 'node_modules'), | ||
| searchForPackageRoot(serverOptions.workspaceRoot), |
There was a problem hiding this comment.
The entry join(serverOptions.workspaceRoot, 'node_modules') is redundant because searchForPackageRoot(serverOptions.workspaceRoot) will always return either the workspaceRoot itself or one of its parent directories. In either case, the local node_modules directory will be covered by the allowed package root. Additionally, using a dynamic import here maintains the lazy-loading pattern for Vite runtime dependencies used elsewhere in this file.
| join(serverOptions.workspaceRoot, 'node_modules'), | |
| searchForPackageRoot(serverOptions.workspaceRoot), | |
| (await import('vite')).searchForPackageRoot(serverOptions.workspaceRoot), |
PR Checklist
PR Type
What is the current behavior?
In monorepo setups where
node_modulesare hoisted to a parent directory above the Angular workspace root, assets loaded from those packages (e.g., font files referenced in CSS from a library in rootnode_modules) are blocked by Vite's dev server with a 403 Forbidden error.This happens because Angular explicitly sets
server.fs.allow, which overrides Vite's default package root auto-detection. The explicit list only includes{workspaceRoot}/node_modules, missing the monorepo root'snode_modules.Issue Number: #31016
What is the new behavior?
Uses Vite's built-in
searchForPackageRoot()to find the actual package installation root (which traverses up from the workspace root to find the nearest directory withpackage.json+node_modules). This directory is added tofs.allow, allowing access to hoisted node_modules in monorepo setups.From Vite docs:
Does this PR introduce a breaking change?