Skip to content

fix: detect affected projects for files inside root but outside sourceRoot#61

Merged
nirsky merged 3 commits intomainfrom
fix/root-fallback-project-index
Apr 20, 2026
Merged

fix: detect affected projects for files inside root but outside sourceRoot#61
nirsky merged 3 commits intomainfrom
fix/root-fallback-project-index

Conversation

@nirsky
Copy link
Copy Markdown
Collaborator

@nirsky nirsky commented Apr 16, 2026

Summary

  • Problem: ProjectIndex only matched files against sourceRoot, so config files (project.json, jest.config.js, tsconfig.json) inside a project's root but outside its sourceRoot were never attributed to their owning project
  • Fix: Added root_entries fallback to ProjectIndex that activates only when no sourceRoot matched — tsconfig excludes are intentionally not applied to the root fallback since config files should always mark a project as affected
  • Overhead: Zero for files that match a sourceRoot (existing fast path unchanged); the fallback loop only runs for changed files outside all sourceRoot paths

Closes #60

Test plan

  • Added test_project_index_root_fallback — verifies config files in root (outside sourceRoot) are detected
  • Added test_project_index_root_fallback_with_tsconfig_excludes — verifies tsconfig excludes still apply to sourceRoot matches but don't block root fallback
  • All 183 unit tests pass
  • Clippy clean

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved file-to-project attribution so configuration and other files located in a project's root (even when outside the source area) are correctly attributed; preserves prior fast-path behavior when fallback isn't needed and ensures root-fallback matches are not blocked by per-project exclude rules.
  • Tests
    • Added cases covering root-fallback matching, exclude behavior, and nested-project attribution.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2c58b258-fa50-4555-8713-23134668db64

📥 Commits

Reviewing files that changed from the base of the PR and between c82ad61 and 852901c.

📒 Files selected for processing (1)
  • src/utils.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils.rs

📝 Walkthrough

Walkthrough

ProjectIndex now indexes both project source_root and root. get_package_names_by_path first matches by source_root with tsconfig exclude filtering, then—when root_entries exists—falls back to matching by root for projects not already seen, skipping tsconfig excludes for the fallback.

Changes

Cohort / File(s) Summary
ProjectIndex & lookup logic
src/utils.rs
Added root_entries: Vec<(PathBuf, Vec<String>)> to ProjectIndex; new populates root_entries when root != source_root. get_package_names_by_path retains fast-path when root_entries is empty; otherwise scans entries (source_root) first with tsconfig exclusion checks, tracks seen projects, then falls back to root_entries for unmatched projects without applying tsconfig excludes.
Tests (lookup & edge cases)
src/utils.rs (tests)
Added/updated tests for root-fallback matching (files inside root but outside source_root), ensuring tsconfig excludes still apply to source_root matches but are bypassed for root fallback, and validating nested-project attribution so fallback doesn't over-attribute.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • EladBezalel

Poem

🐇 I hopped through roots and source alike,
Searched config shadows by moonlight's strike.
Now projects answer when files call —
No config hideaway escapes my haul. 🎩✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main fix: detecting affected projects for files inside root but outside sourceRoot, which matches the primary objective.
Linked Issues check ✅ Passed The code changes fully address issue #60 by implementing root_entries indexing and fallback matching logic with proper tsconfig exclude handling as specified.
Out of Scope Changes check ✅ Passed All changes are directly related to the core objective of fixing root-based project detection; no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/root-fallback-project-index

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/utils.rs (1)

93-123: ⚠️ Potential issue | 🟠 Major

Root fallback is skipped for nested projects once any ancestor source_root matches.

matched_source_root is global, so one prefix match suppresses all root fallback checks. That still misses nested layouts like apps/parent + apps/parent/child: a file such as apps/parent/child/project.json will match the parent source_root, set the flag, and never be attributed to the child project even though it is inside child.root. This breaks the stated “any file inside a project root marks that project affected” rule for overlapping roots.

Suggested fix
 pub fn get_package_names_by_path(&self, file_path: &Path) -> Vec<String> {
   let mut result = Vec::new();
-  let mut matched_source_root = false;
+  let mut matched_projects = rustc_hash::FxHashSet::default();

   // Primary: match against sourceRoot (with tsconfig exclude filtering)
   for (root, names) in &self.entries {
     if file_path.starts_with(root) {
-      matched_source_root = true;
       for name in names {
+        matched_projects.insert(name.clone());
         if let Some(excl) = self.excludes.get(name) {
           if excl.is_excluded(file_path) {
             debug!(
               "File {:?} excluded by tsconfig for project '{}'",
               file_path, name
@@
       }
     }
   }

-  if !matched_source_root {
-    for (root, names) in &self.root_entries {
-      if file_path.starts_with(root) {
-        for name in names {
-          result.push(name.clone());
-        }
-      }
+  // Fallback per project root, not globally.
+  for (root, names) in &self.root_entries {
+    if file_path.starts_with(root) {
+      for name in names {
+        if !matched_projects.contains(name) {
+          result.push(name.clone());
+        }
+      }
     }
   }

   result
 }

Please also add a regression test for a nested parent/child project layout so this case stays covered.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils.rs` around lines 93 - 123, The current global matched_source_root
bool suppresses all root fallback when any sourceRoot prefix matched, which
prevents nested child project roots from being considered; fix this by
collecting the actual matched source root paths (e.g., Vec<PathBuf>
matched_source_roots) while iterating self.entries and only suppress a given
fallback root from self.root_entries if one of the matched_source_roots
legitimately should shadow that specific root (i.e., a matched source_root
equals or is a child of that fallback root in a way that truly covers it),
otherwise allow the fallback root to be attributed; update the loop over
self.root_entries to consult matched_source_roots (instead of a single bool) and
add a regression test that exercises a parent/child layout like apps/parent and
apps/parent/child so project.json inside the child is attributed to the child
project.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/utils.rs`:
- Around line 93-123: The current global matched_source_root bool suppresses all
root fallback when any sourceRoot prefix matched, which prevents nested child
project roots from being considered; fix this by collecting the actual matched
source root paths (e.g., Vec<PathBuf> matched_source_roots) while iterating
self.entries and only suppress a given fallback root from self.root_entries if
one of the matched_source_roots legitimately should shadow that specific root
(i.e., a matched source_root equals or is a child of that fallback root in a way
that truly covers it), otherwise allow the fallback root to be attributed;
update the loop over self.root_entries to consult matched_source_roots (instead
of a single bool) and add a regression test that exercises a parent/child layout
like apps/parent and apps/parent/child so project.json inside the child is
attributed to the child project.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 68632d43-e16d-477f-b4cc-06b9214eeb68

📥 Commits

Reviewing files that changed from the base of the PR and between d5f105b and 2d039ea.

📒 Files selected for processing (1)
  • src/utils.rs

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 1 file

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 16, 2026

📦 Preview Release Available

A preview release has been published for commit 852901c.

Installation

npm install https://github.com/frontops-dev/domino/releases/download/pr-61-852901c/front-ops-domino-1.2.2.tgz

Running the preview

npx https://github.com/frontops-dev/domino/releases/download/pr-61-852901c/front-ops-domino-1.2.2.tgz affected

Details

@nirsky
Copy link
Copy Markdown
Collaborator Author

nirsky commented Apr 16, 2026

Fixed @coderabbitai comment

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

Tip

For best results, initiate chat on the files or code changes.

@nirsky: Thanks for the update! The fix looks good. 🎉


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@oreporan
Copy link
Copy Markdown

+1 on this

Copy link
Copy Markdown
Collaborator

@EladBezalel EladBezalel left a comment

Choose a reason for hiding this comment

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

Clean fix — the fallback design is correct and zero-cost for the common case. A few suggestions below, nothing blocking.

Comment thread src/utils.rs Outdated
Comment thread src/utils.rs
Comment thread src/utils.rs
Comment thread src/utils.rs
nirsky and others added 3 commits April 20, 2026 14:20
…eRoot

ProjectIndex only matched files against sourceRoot, so config files like
project.json, jest.config.js, and tsconfig.json that live in a project's
root directory but outside its sourceRoot were never attributed to their
owning project.

Add root_entries fallback to ProjectIndex that activates only when no
sourceRoot matched. tsconfig excludes are intentionally not applied to the
root fallback since config files should always mark a project as affected.

Closes #60

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace global matched_source_root boolean with per-project tracking so
that nested child projects get attributed via root fallback even when a
parent's sourceRoot is a prefix match. Add regression test for the
nested parent/child layout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Early-return before allocating hashset when root_entries is empty
  (common case for non-Nx workspaces where root == sourceRoot)
- Use FxHashSet<&str> instead of FxHashSet<String> to avoid per-call
  string clones
- Add test asserting root-level spec file bypasses tsconfig excludes
  (documents the design choice)
- Add negative attribution test for nested layout: file inside parent
  only must not attribute to child

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nirsky nirsky force-pushed the fix/root-fallback-project-index branch from c82ad61 to 852901c Compare April 20, 2026 11:21
@nirsky nirsky merged commit 2314ee3 into main Apr 20, 2026
26 of 37 checks passed
@nirsky nirsky deleted the fix/root-fallback-project-index branch April 20, 2026 11:39
nirsky added a commit that referenced this pull request Apr 20, 2026
Source-typed config files at project root (e.g. jest.config.js,
webpack.config.ts when sourceRoot is "<proj>/src") are classified as
source by extension but never walked by the analyzer, which only
descends sourceRoot. Step 6a used to silently `continue` on such files,
so the root fallback added in #61 never got a chance to run for them —
changes to project-level config files went undetected.

When a changed source file isn't in `analyzer.files`, fall back to
`ProjectIndex::get_package_names_by_path` and mark the owning projects
affected, mirroring how Step 6b handles assets. The new `root_entries`
index from #61 now actually covers this case end-to-end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Changed files inside project root but outside sourceRoot are not detected as affected

3 participants