Skip to content

Core: Move the component-meta invalidation state machine into core - #35806

Merged
valentinpalkovic merged 9 commits into
nextfrom
valentin/component-meta-project-tracker
Aug 11, 2026
Merged

valentinpalkovic merged 9 commits into
nextfrom
valentin/component-meta-project-tracker

Conversation

@valentinpalkovic

@valentinpalkovic valentinpalkovic commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Note

Stacked on #35750. That one records per-component docgen baselines from built sandboxes; this one lifts React's component-meta invalidation state machine into core, so the Angular analyzer in #35805 consumes it instead of writing a second copy.

What I did

React's component-meta analyzer keeps one TypeScript LanguageService per matched tsconfig. The Angular analyzer arriving in the next slice needs exactly the same thing, and the same four answers from it: which files the project claims, what version each file is on, when a change event invalidates a cached snapshot, and how to stay fresh when an editor save races the debounced watcher.

All of that was inline in React. This moves it into storybook/internal/component-meta as ProjectFileTracker, so there is one implementation rather than two drifting ones.

                        ┌── React    ComponentMetaProject  ── loses ~150 lines
 ProjectFileTracker ────┤
   root set             └── Angular  (next slice)          ── consumes it instead of
   script versions                                            writing a second copy
   change invalidation
   mtime freshness

The tracker knows nothing about either renderer. It takes a structural ProjectFileSystem rather than importing typescript, so typeof ts satisfies it and core never names the package:

export interface ProjectFileSystem {
  fileExists(path: string): boolean;
  readFile(path: string): string | undefined;
  getModifiedTime?(path: string): Date | undefined;
}

How this is measured

React's existing component-meta suite is the proof: it exercises the same invalidation paths through the extracted tracker, unchanged.

Command What it does Run from
yarn test --project "@storybook/react" React's component-meta and manager suites, which is where the moved behaviour is asserted repo root
yarn nx run-many -t check The structural ProjectFileSystem boundary is a compile-time claim, so the typecheck is part of the evidence repo root

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

Manual testing

This is a behaviour-preserving move with no user-facing surface, so the meaningful check is that React docgen still invalidates correctly:

  1. yarn task sandbox --template react-vite/default-ts --start-from auto
  2. In the sandbox's .storybook/main.ts, set features: { experimentalDocgenServer: true }
  3. yarn storybook, open Example/Button → Docs and confirm the props table is populated
  4. Add a new prop to src/stories/Button.tsx and save. The table should gain the prop without a restart - that path is the tracker's freshness handling.

Documentation

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

Internal module with no public export, so nothing user-facing to document.

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.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Added shared project file tracking for path filtering, snapshot caching, versioning, root-file synchronization, and watcher invalidation. React component metadata extraction now uses this tracker and validates rewrites with unchanged modification times.

Changes

Project File Tracking

Layer / File(s) Summary
Tracker contracts and snapshot handling
code/core/src/component-meta/types.ts, code/core/src/component-meta/ProjectFileTracker.ts, code/core/src/component-meta/index.ts
Added filesystem and snapshot-cache contracts. Added path filtering, snapshot caching, and file-version tracking.
Root-file and invalidation handling
code/core/src/component-meta/ProjectFileTracker.ts
Added root-file inclusion, watcher invalidation, targeted freshness checks, and lazy command-line root-set synchronization.
React project integration
code/renderers/react/src/componentManifest/componentMeta/ComponentMetaProject.ts, code/renderers/react/src/componentManifest/componentMeta/ComponentMetaManager.ts
React component metadata projects delegate snapshot reads, file changes, project versions, root files, and freshness checks to ProjectFileTracker.
Rewrite invalidation validation
code/renderers/react/src/componentManifest/componentMeta/componentMetaExtractor.test-helpers.ts, code/renderers/react/src/componentManifest/componentMeta/ComponentMetaManager.test.ts, code/core/src/component-meta/ProjectFileTracker.test.ts, code/core/src/shared/open-service/services/docgen/worker/docgen-worker-client.test.ts
Rewritten files now report shared file changes. Tests cover unchanged-mtime rewrites, path filtering, path normalization, cache reuse, watcher invalidation, and worker listener ordering.

Sequence Diagram(s)

sequenceDiagram
  participant ComponentMetaManager
  participant ComponentMetaProject
  participant ProjectFileTracker
  participant ProjectFileSystem
  ComponentMetaManager->>ComponentMetaProject: onFilesChanged(changes)
  ComponentMetaProject->>ProjectFileTracker: invalidate changed files
  ProjectFileTracker->>ProjectFileSystem: check mtimes and read snapshots
  ProjectFileTracker-->>ComponentMetaProject: updated versions
  ComponentMetaProject->>ProjectFileTracker: getSnapshot(filePath)
  ProjectFileTracker-->>ComponentMetaProject: refreshed snapshot
Loading

Possibly related PRs

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
code/core/src/component-meta/ProjectFileTracker.ts (1)

189-190: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the provenance-only comment.

The Adapted from URL records origin. It does not explain a maintenance requirement. Delete it or replace it with a concise rationale for the implementation.

As per coding guidelines, comments must explain maintenance-relevant rationale and must not contain provenance claims.

🤖 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/core/src/component-meta/ProjectFileTracker.ts` around lines 189 - 190,
Remove the provenance-only “Adapted from” comment and its URL near
ProjectFileTracker; do not replace it unless a concise maintenance-relevant
rationale is needed.

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.

Inline comments:
In `@code/core/src/component-meta/ProjectFileTracker.ts`:
- Around line 35-45: Normalize all path inputs consistently in
ProjectFileTracker: apply the existing path normalization to initial and
refreshed commandLine.fileNames, and to public getSnapshot/getScriptVersion
inputs. Use the normalized key for snapshots, fileVersions, and filesystem
calls, including onFilesChanged and ensureFresh, so every tracker state lookup
and update uses the same path representation.
- Around line 20-22: Update filterSourceFilePaths to exclude paths only when
node_modules is a complete normalized path segment, rather than matching the
substring anywhere; retain valid paths such as node_modules-tools/Tag.tsx.

In
`@code/renderers/react/src/componentManifest/componentMeta/ComponentMetaManager.test.ts`:
- Around line 247-290: Convert this regression test to use memfs instead of a
host temporary directory: add a top-level node:fs mock with spy enabled,
configure the mocked filesystem APIs with vi.mocked(), and reset and seed vol in
beforeEach. Update the test setup and file operations around
ComponentMetaManager and the timestamp assertions to operate on the memfs volume
while preserving the same coarse-mtime scenario and expectations.

---

Nitpick comments:
In `@code/core/src/component-meta/ProjectFileTracker.ts`:
- Around line 189-190: Remove the provenance-only “Adapted from” comment and its
URL near ProjectFileTracker; do not replace it unless a concise
maintenance-relevant rationale is needed.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88b463ac-efc4-4ac3-bc24-d3367fa767a2

📥 Commits

Reviewing files that changed from the base of the PR and between b46845d and b7d5cfb.

📒 Files selected for processing (7)
  • code/core/src/component-meta/ProjectFileTracker.ts
  • code/core/src/component-meta/index.ts
  • code/core/src/component-meta/types.ts
  • code/renderers/react/src/componentManifest/componentMeta/ComponentMetaManager.test.ts
  • code/renderers/react/src/componentManifest/componentMeta/ComponentMetaManager.ts
  • code/renderers/react/src/componentManifest/componentMeta/ComponentMetaProject.ts
  • code/renderers/react/src/componentManifest/componentMeta/componentMetaExtractor.test-helpers.ts

Comment thread code/core/src/component-meta/ProjectFileTracker.ts
Comment thread code/core/src/component-meta/ProjectFileTracker.ts
@valentinpalkovic valentinpalkovic added maintenance User-facing maintenance tasks ci:normal Run our default set of CI jobs (choose this for most PRs). labels Aug 7, 2026
@storybook-app-bot

storybook-app-bot Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Package Benchmarks

Commit: 1b8eb03, ran on 11 August 2026 at 12:51:25 UTC

No significant changes detected, all good. 👏

@valentinpalkovic valentinpalkovic removed the ci:normal Run our default set of CI jobs (choose this for most PRs). label Aug 7, 2026
@valentinpalkovic
valentinpalkovic marked this pull request as draft August 8, 2026 05:36
@valentinpalkovic valentinpalkovic self-assigned this Aug 8, 2026
@valentinpalkovic valentinpalkovic added angular ci:normal Run our default set of CI jobs (choose this for most PRs). labels Aug 8, 2026
Comment thread code/core/src/component-meta/ProjectFileTracker.ts Outdated
Comment thread code/core/src/component-meta/ProjectFileTracker.ts Outdated
Comment thread code/core/src/component-meta/ProjectFileTracker.ts Outdated
Comment thread code/core/src/component-meta/ProjectFileTracker.ts Outdated
Comment thread code/core/src/component-meta/ProjectFileTracker.ts Outdated
Comment thread code/core/src/component-meta/ProjectFileTracker.ts Outdated
Comment thread code/core/src/component-meta/ProjectFileTracker.ts Outdated
Comment thread code/core/src/component-meta/ProjectFileTracker.ts Outdated
Comment thread code/core/src/component-meta/types.ts Outdated
Comment thread code/renderers/react/src/componentManifest/componentMeta/ComponentMetaProject.ts Outdated
@valentinpalkovic
valentinpalkovic marked this pull request as ready for review August 8, 2026 20:51
@valentinpalkovic valentinpalkovic added the qa:skip Pull Requests that do not need any QA. (e.g. documentation) label Aug 8, 2026
@valentinpalkovic
valentinpalkovic force-pushed the valentin/component-meta-project-tracker branch from 88ed261 to d8bd95f Compare August 10, 2026 10:17
Comment thread code/core/src/component-meta/ProjectFileTracker.ts Outdated
Comment thread code/core/src/component-meta/ProjectFileTracker.ts Outdated
@valentinpalkovic
valentinpalkovic force-pushed the valentin/component-meta-project-tracker branch from d8bd95f to a6efdc2 Compare August 10, 2026 20:38
Base automatically changed from valentin/sb11-angular-docgen-sandbox-baselines to next August 11, 2026 10:17
valentinpalkovic and others added 6 commits August 11, 2026 12:17
Both the React and the Angular component-meta analyzers keep one TypeScript
LanguageService per matched tsconfig, and both need the same thing from it:
which files the project claims, what version each is on, when a change event
invalidates a snapshot, and how to stay fresh when an edit races the debounced
watcher. React had all of that inline.

`ProjectFileTracker` owns it once. React loses about 150 lines of it and reads
the same behaviour back; the Angular analyzer arriving next consumes it instead
of writing a second copy.

The tracker deliberately knows nothing about either renderer: it takes a
structural `ProjectFileSystem` rather than importing `typescript`, so `typeof ts`
satisfies it without core naming the package.
`getSnapshot` and `getScriptVersion` read `snapshots` and `fileVersions` with
the raw path while every other entry point normalized first, so the class had
two possible keys for one file and a reader had to track which method did
which. Normalize in both readers, and match `node_modules` as a whole path
segment so a source directory like `src/node_modules-tools/` is no longer
dropped from watcher discovery.

`commandLine.fileNames` needs no normalizing here: `parseTsconfigCommandLine`
already does it for every project the manager builds.
Co-authored-by: Valentin Palkovic <dev@valentinpalkovic.dev>
The JSDoc header had its continuation markers at column 0, which oxfmt
rejects, so `format-check` was failing.

While in there, applied the repo's own comment guidance to this file and to
`ProjectFileTracker`. Pinned upstream permalinks with line ranges are named
in AGENTS.md as noise that rots, and four of them here already point at a
commit-pinned range that no longer means anything; they now name the upstream
construct instead. The header listed the extraction paths as an implementation
outline, so it says why props come from the story rather than what the code
does. Em dashes swapped for hyphens per the house rule.
@valentinpalkovic
valentinpalkovic force-pushed the valentin/component-meta-project-tracker branch from a6efdc2 to 30e3af7 Compare August 11, 2026 10:17
checkRootFilesUpdate compared the freshly parsed command line's
fileNames against the normalized fileNames already stored on the
tracker, and stored the un-normalized ones back on a match failure.
Windows-separator paths from a fresh ts.sys parse would never
equal their forward-slash counterparts, causing spurious project
version bumps, and un-normalized paths would then leak into
getScriptFileNames().
toBeGreaterThan(0) also passed if unref() ran after only one of the
two constructor listeners attached; toBe(2) makes the test catch that
ordering regression.
Adds slash() and isInNodeModules() to core's shared utils, exported from
storybook/internal/common, and replaces the hand-rolled copies in
component-meta, the module-graph service, addon-mcp and the Vue docgen
project manager.
@valentinpalkovic
valentinpalkovic merged commit f7e5c4c into next Aug 11, 2026
142 checks passed
@valentinpalkovic
valentinpalkovic deleted the valentin/component-meta-project-tracker branch August 11, 2026 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

angular ci:normal Run our default set of CI jobs (choose this for most PRs). maintenance User-facing maintenance tasks qa:skip Pull Requests that do not need any QA. (e.g. documentation)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants