Skip to content

Angular: Serve ancestor node_modules for addon-vitest in browser mode - #35600

Merged
valentinpalkovic merged 3 commits into
nextfrom
claude/storybook-issue-35578-kgy20p
Jul 27, 2026
Merged

valentinpalkovic merged 3 commits into
nextfrom
claude/storybook-issue-35578-kgy20p

Conversation

@brandonroberts

@brandonroberts brandonroberts commented Jul 26, 2026 •

Copy link
Copy Markdown
Contributor

Closes #35578

What I did

Fixes @storybook/addon-vitest failing to run story tests in an Angular workspace library (ng generate library), where every suite fails with:

Failed to import test file .../my-workspace/node_modules/@angular/compiler/fesm2022/compiler.mjs
Caused by: TypeError: Failed to fetch dynamically imported module: .../node_modules/@angular/compiler/fesm2022/compiler.mjs

Root cause. In an Angular workspace, ng generate library creates projects/<lib>/package.json while node_modules stays at the workspace root. Vite 7 only treats pnpm-workspace.yaml, lerna.json, or a workspaces field as workspace-root markers (not .git or a lockfile), so searchForWorkspaceRoot resolves the root to projects/<lib>. Vitest's browser server then builds server.fs.allow from that subfolder, and the @angular/compiler setup file injected by storybookAngularVitest — which resolves from the ancestor node_modules — falls outside the allow list and 404s in the browser.

Fix. storybookAngularVitest() now adds every ancestor directory that contains node_modules to the browser server's fs.allow from a configureServer hook. Notes on the approach:

  • configureServer is used deliberately: Vitest builds the browser server's allow-list itself, so entries added via the config hook or static server.fs.allow never reach it.
  • All ancestors are kept (not just the nearest) because Storybook's own cache creates a node_modules/.cache in the project directory, which would otherwise shadow the real workspace root higher up.
  • Entries are normalized with Vite's normalizePath to match Vite's own allow-list convention on Windows.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

findNodeModulesRoots is covered by unit tests (code/frameworks/angular-vite/src/node/find-node-modules-roots.test.ts), including the case where a nearer node_modules/.cache must not shadow the real workspace root.

Manual testing

  1. Scaffold an Angular workspace library and add Storybook:
    ng new my-workspace --no-create-application
    cd my-workspace
    ng generate library my-lib
    npx storybook@latest init
    (This produces projects/my-lib/vitest.config.ts and projects/my-lib/.storybook, with node_modules at the workspace root.)
  2. Run the Vitest addon tests (from the Storybook UI "run tests", or vitest run against projects/my-lib/vitest.config.ts).
  3. Before this change: every story suite fails with Failed to fetch dynamically imported module … @angular/compiler.
  4. After this change: the story suites collect and run normally.

This was verified end-to-end against exactly this scaffold: baseline reproduces the reported error (3 files failed); with the fix the suites pass (3 files / 8 tests).

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update MIGRATION.MD

No documentation changes — this is an internal bug fix.

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Declare whether manual QA will be needed for this PR during the next release, through qa:needed or qa:skip

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🤖 Generated with Claude Code


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved Angular Vitest/Vite dev-server support for nested Angular library workspaces.
    • Automatically expands Vite’s filesystem allow-list to include required ancestor node_modules roots, ensuring Angular compiler setup files resolve reliably.
    • More consistently handles scenarios with multiple possible node_modules locations while selecting the correct workspace-level roots.
  • Tests
    • Added Vitest coverage for detecting node_modules ancestor roots and the case where none are found.

In an Angular workspace library, `ng generate library` creates
`projects/<lib>/package.json` while `node_modules` stays at the workspace
root. Vite 7 only treats `pnpm-workspace.yaml`, `lerna.json`, or a
`workspaces` field as workspace-root markers (not `.git` or a lockfile), so
`searchForWorkspaceRoot` resolves the root to `projects/<lib>`. Vitest's
browser server then builds `server.fs.allow` from that subfolder, and the
`@angular/compiler` setup file injected by `storybookAngularVitest` — which
resolves from the ancestor `node_modules` — falls outside the allow list and
fails with "Failed to fetch dynamically imported module".

Add every ancestor directory that contains `node_modules` to the browser
server's `fs.allow` from `storybookAngularVitest`'s `configureServer`. That
hook is used deliberately: Vitest builds the browser server's allow-list
itself, so entries added via the `config` hook or static config never reach
it. All ancestors are kept (not just the nearest) because Storybook's cache
creates a `node_modules/.cache` in the project directory, which would
otherwise shadow the real workspace root higher up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FsoQV75vUEGjWo6YFd35cj
@brandonroberts
brandonroberts requested a review from a team July 26, 2026 20:47
@brandonroberts brandonroberts changed the title fix(angular-vite): serve ancestor node_modules for addon-vitest in browser mode Angular: Serve ancestor node_modules for addon-vitest in browser mode Jul 26, 2026
@brandonroberts brandonroberts added the bug label Jul 26, 2026 — with Claude
@coderabbitai

coderabbitai Bot commented Jul 26, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 844f125a-a12d-441b-9e50-acf6c8e88992

📥 Commits

Reviewing files that changed from the base of the PR and between 191eb19 and 47c3b0b.

📒 Files selected for processing (1)
  • code/frameworks/angular-vite/src/node/vitest.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/frameworks/angular-vite/src/node/vitest.ts

📝 Walkthrough

Walkthrough

Adds findNodeModulesRoots to locate all ancestor node_modules directories and updates the Angular Vitest Vite plugin to include those roots in server.fs.allow. Tests cover workspace, cache, and missing-dependency layouts.

Changes

Angular Vite filesystem access

Layer / File(s) Summary
Node_modules ancestor discovery and tests
code/frameworks/angular-vite/src/node/vitest.ts, code/frameworks/angular-vite/src/node/find-node-modules-roots.test.ts
Adds the exported findNodeModulesRoots helper and tests for workspace ancestors, cache-style nearer directories, and empty results.
Vite filesystem allow-list integration
code/frameworks/angular-vite/src/node/vitest.ts
Adds a configureServer hook that appends discovered node_modules roots to the Vite filesystem allow-list.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • storybookjs/storybook#35578 — Directly covers adding findNodeModulesRoots and expanding Vite’s server.fs.allow for Angular workspace libraries.

Sequence Diagram(s)

sequenceDiagram
  participant AngularVitestPlugin
  participant findNodeModulesRoots
  participant ViteServer
  AngularVitestPlugin->>findNodeModulesRoots: discover ancestor node_modules roots
  findNodeModulesRoots-->>AngularVitestPlugin: return normalized roots
  AngularVitestPlugin->>ViteServer: append roots to server.fs.allow
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
code/frameworks/angular-vite/src/node/find-node-modules-roots.test.ts (1)

10-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the repository’s spy-enabled, typed Vitest mock pattern.

This mock omits { spy: true }, configures mock behavior from test bodies/afterEach, and uses fs as any, which defeats type safety. Align it with the project’s spy-mocking.mdc pattern and move mock setup/reset into beforeEach.

As per coding guidelines, Vitest mocks require spy: true, behavior in beforeEach, and type-safe access through vi.mocked().

Also applies to: 21-43

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/frameworks/angular-vite/src/node/find-node-modules-roots.test.ts` around
lines 10 - 17, Update the node:fs mock and test lifecycle in
find-node-modules-roots.test.ts to follow the repository’s spy-enabled Vitest
pattern: configure the mock with { spy: true }, move mock setup and reset into
beforeEach, and replace the fs as any access with a type-safe vi.mocked()
reference. Preserve the existing mocked filesystem behavior and test coverage.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@code/frameworks/angular-vite/src/node/find-node-modules-roots.test.ts`:
- Around line 10-17: Update the node:fs mock and test lifecycle in
find-node-modules-roots.test.ts to follow the repository’s spy-enabled Vitest
pattern: configure the mock with { spy: true }, move mock setup and reset into
beforeEach, and replace the fs as any access with a type-safe vi.mocked()
reference. Preserve the existing mocked filesystem behavior and test coverage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d44c8134-9bfc-4d3c-a5d9-ea58820a18d1

📥 Commits

Reviewing files that changed from the base of the PR and between e4e223a and f0727f8.

📒 Files selected for processing (2)
  • code/frameworks/angular-vite/src/node/find-node-modules-roots.test.ts
  • code/frameworks/angular-vite/src/node/vitest.ts

…ots test

Switch to `vi.mock('node:fs', { spy: true })` with the mock behavior set in
`beforeEach` and type-safe `vi.mocked(existsSync)` access, matching the repo's
Vitest mocking guidance and removing the `fs as any` cast.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FsoQV75vUEGjWo6YFd35cj
@brandonroberts brandonroberts added ci:normal Run our default set of CI jobs (choose this for most PRs). qa:needed Pull Requests that will need manual QA prior to release. labels Jul 26, 2026 — with Claude
Comment thread code/frameworks/angular-vite/src/node/vitest.ts
Comment thread code/frameworks/angular-vite/src/node/vitest.ts Outdated
…les roots

Address review feedback: ensure the workspace-root node_modules are allowed
even if `server.fs.allow` is ever absent, by initializing it before pushing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FsoQV75vUEGjWo6YFd35cj
@valentinpalkovic
valentinpalkovic merged commit b6eca7f into next Jul 27, 2026
142 checks passed
@valentinpalkovic
valentinpalkovic deleted the claude/storybook-issue-35578-kgy20p branch July 27, 2026 07:38
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Core Team Projects Jul 27, 2026
@github-actions github-actions Bot mentioned this pull request Jul 28, 2026
2 tasks done
@valentinpalkovic valentinpalkovic added sev:S2 High priority. Fix within weeks. patch:yes Bugfix & documentation PR that need to be picked to main branch labels Jul 31, 2026
@github-actions

github-actions Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor
Fails
🚫

node failed.

Log

Details
Error:  Error: Could not find the Dangerfile at scripts/dangerfile.ts - if it is local, perhaps you have a typo? If it's using a remote file, it doesn't have a repo reference.
    at /usr/src/danger/dist/platforms/GitHub.js:161:27
    at step (/usr/src/danger/dist/platforms/GitHub.js:44:23)
    at Object.next (/usr/src/danger/dist/platforms/GitHub.js:25:53)
    at /usr/src/danger/dist/platforms/GitHub.js:19:71
    at new Promise (<anonymous>)
    at __awaiter (/usr/src/danger/dist/platforms/GitHub.js:15:12)
    at Object.executeRuntimeEnvironment (/usr/src/danger/dist/platforms/GitHub.js:144:88)
    at /usr/src/danger/dist/commands/danger-runner.js:101:47
    at step (/usr/src/danger/dist/commands/danger-runner.js:34:23)
    at Object.next (/usr/src/danger/dist/commands/danger-runner.js:15:53)
danger-results://tmp/danger-results-e636bbc0.json

Generated by 🚫 dangerJS against 47c3b0b

@github-actions github-actions Bot mentioned this pull request Aug 6, 2026
15 tasks done
@github-actions github-actions Bot added the patch:done Patch/release PRs already cherry-picked to main/release branch label Aug 6, 2026
@ndelangen ndelangen removed the patch:yes Bugfix & documentation PR that need to be picked to main branch label Aug 28, 2026
@ndelangen ndelangen removed the qa:needed Pull Requests that will need manual QA prior to release. label Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

angular bug ci:normal Run our default set of CI jobs (choose this for most PRs). patch:done Patch/release PRs already cherry-picked to main/release branch sev:S2 High priority. Fix within weeks.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Bug]: @storybook/addon-vitest fails with Angular workspace library — cannot serve node_modules when vitest.config.ts is inside library project

4 participants