microsoft / microsoft/FluidFramework

Duplicate Code: package filtering logic duplicated across build-cli and build-infrastructure

Open
#26,800 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
TypeScript
Stars
4.9k
Forks
586
Avg merge
1d 15h
Merged PRs (30d)
146

Description

Analysis of commit 928277dfed7029d7ddc344e2c6389582ba762a2b

Assignee: @copilot

Summary

The package filtering logic is duplicated (near-exact copy/paste) between build-cli and build-infrastructure. The functions are identical (or trivially identical) and exceed the duplication threshold, increasing the risk of divergent behavior when one copy is updated without the other.

Duplication Details

Pattern: filterPackages (+ helper scopesToPrefix) duplicated across packages
  • Severity: Medium

  • Occurrences: 2 (exact duplicates)

  • Locations:

    • build-tools/packages/build-cli/src/filter.ts (lines 248–285, helper at 336–338)
    • build-tools/packages/build-infrastructure/src/filter.ts (lines 297–334, helper at 287–289)
  • Code Sample (from build-tools/packages/build-cli/src/filter.ts):

    export async function filterPackages(T extends FilterablePackage)(
    	packages: T[],
    	filters: PackageFilterOptions,
    ): Promise(T[]) {
    	const filtered = packages.filter((pkg) => {
    		if (filters === undefined) {
    			return true;
    		}
    
    		const isPrivate: boolean = pkg.private ?? false;
    		if (filters.private !== undefined && filters.private !== isPrivate) {
    			return false;
    		}
    
    		const scopeIn = scopesToPrefix(filters?.scope);
    		const scopeOut = scopesToPrefix(filters?.skipScope);
    
    		if (scopeIn !== undefined) {
    			let found = false;
    			for (const scope of scopeIn) {
    				found ||= pkg.name.startsWith(scope);
    			}
    			if (!found) {
    				return false;
    			}
    		}
    		if (scopeOut !== undefined) {
    			for (const scope of scopeOut) {
    				if (pkg.name.startsWith(scope) === true) {
    					return false;
    				}
    			}
    		}
    		return true;
    	});
    
    	return filtered;
    }
    
    function scopesToPrefix(scopes: string[] | undefined): string[] | undefined {
    	return scopes === undefined ? undefined : scopes.map((s) => `\$\{s}/`);
    }
    

Impact Analysis

  • Maintainability: Any future change (e.g., adding new filter criteria, changing scope semantics) must be replicated and kept in sync across both packages.
  • Bug Risk: High chance of inconsistent fixes when only one copy is updated.
  • Code Bloat: Two copies of non-trivial filtering logic.

Refactoring Recommendations

  1. Extract shared filtering utility

    • Extract filterPackages + scopesToPrefix into a shared module (suggestion):
      • build-tools/packages/build-tools/src/common/packageFilters.ts (or similar)
    • Update both build-cli and build-infrastructure to import the shared implementation.
    • Estimated effort: Low–Medium (1–3 hours, mostly wiring/exports).
  2. Define a single source of truth and re-export

    • If the dependency direction allows, keep implementation in one package and re-export from the other.
    • This is less ideal than a true shared utility but still removes duplication.

Implementation Checklist

  • Confirm both copies are intended to have identical semantics
  • Choose a shared home package/module for the implementation
  • Move code and update imports
  • Run existing package/unit tests for both affected packages
  • Ensure no API surface regressions

Analysis Metadata

  • Detection Method: Serena semantic symbol comparison (find_symbol on filterPackages and scopesToPrefix)
  • Analyzed Files (focused set): build-cli + build-infrastructure filter modules
  • Commit: 928277dfed7029d7ddc344e2c6389582ba762a2b
  • Analysis Date: 2026-03-20

Generated by Duplicate Code Detector ·

To install this agentic workflow, run

gh aw add github/gh-aw/.github/workflows/duplicate-code-detector.md@94662b1dee8ce96c876ba9f33b3ab8be32de82a4

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by comparing filterPackages and scopesToPrefix in build-tools/packages/build-cli/src/filter.ts and build-tools/packages/build-infrastructure/src/filter.ts, then inspect the suggested shared package location and dependency direction. Choose a shared home, update both packages to use it, and run the existing package/unit tests for both affected packages to verify behavior and API compatibility.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
build-system, tooling
Issue type
Refactor
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.