diff --git a/.cursor/flows/README.md b/.cursor/flows/README.md index 5570c43..a35a5d0 100644 --- a/.cursor/flows/README.md +++ b/.cursor/flows/README.md @@ -27,21 +27,34 @@ Flows are collections of rule files (.mdc) that guide the AI through multi-step **Location:** `ds-refactoring-flow/` **Purpose:** Migrate components from deprecated design system patterns to modern alternatives -**Files:** -- `01-find-violations.mdc` - Identify deprecated component usage -- `02-plan-refactoring.mdc` - Create detailed migration strategy +**Flow Options:** + +**Option A: Targeted Approach** (recommended for focused, incremental migrations) +- `01-find-violations.mdc` - Identify specific deprecated component usage +- `02-plan-refactoring.mdc` - Create detailed migration strategy for specific cases + +**Option B: Comprehensive Approach** (recommended for large-scale migrations) +- `01b-find-all-violations.mdc` - Scan entire codebase, group by folders, select subfolder for detailed analysis +- `02b-plan-refactoring-for-all-violations.mdc` - Create comprehensive migration plan for all violations in scope + +**Continuation Steps** (used with both approaches): - `03-non-viable-cases.mdc` - Handle non-migratable components by marking them for exclusion - `03-fix-violations.mdc` - Execute code changes - `04-validate-changes.mdc` - Verify improvements through contract comparison - `05-prepare-report.mdc` - Generate testing checklists and documentation - `clean-global-styles.mdc` - Independent analysis of deprecated CSS usage +**Choosing Your Approach:** +- **Targeted (01 → 02)**: Use when working on specific components or small sets of violations. Provides focused analysis and incremental progress. +- **Comprehensive (01b → 02b)**: Use when planning large-scale migrations across multiple folders. Provides broad overview first, then detailed planning for selected scope. + **Special Handling:** - **Non-Viable Cases**: When components are identified as non-viable during the planning step, use `03-non-viable-cases.mdc` instead of proceeding with the normal fix violations step. This marks components with special prefixes (`after-migration-[ORIGINAL_CLASS]`) to exclude them from future violation reports. **Use Cases:** -- **Primary Flow**: Migrating components to modern design system patterns -- **Non-Viable Handling**: Alternative handling within the main flow for legacy components that cannot be migrated +- **Targeted Flow**: Incremental migration of specific components or small violation sets +- **Comprehensive Flow**: Large-scale migration planning across multiple directories +- **Non-Viable Handling**: Alternative handling within either flow for legacy components that cannot be migrated ## How to Use Flows diff --git a/.cursor/rules/01-find-violations.mdc b/.cursor/flows/ds-refactoring-flow/01b-find-all-violations.mdc similarity index 75% rename from .cursor/rules/01-find-violations.mdc rename to .cursor/flows/ds-refactoring-flow/01b-find-all-violations.mdc index 9cee81b..714da1b 100644 --- a/.cursor/rules/01-find-violations.mdc +++ b/.cursor/flows/ds-refactoring-flow/01b-find-all-violations.mdc @@ -6,13 +6,11 @@ alwaysApply: false You are an AI assistant tasked with helping a developer identify and plan refactoring for legacy component usage. Follow these instructions carefully to complete the task in two main steps. First, I will provide you with the following information: -{{COMPONENT_NAME}} {{DIRECTORY}} Step 1: Find violations -1. Run a scan using the report-violations function with the following parameters: - - component: {{COMPONENT_NAME}} +1. Run a scan using the report-all-violations function with the following parameters: - directory: {{DIRECTORY}} - groupBy: "folder" @@ -22,8 +20,8 @@ Step 1: Find violations - If the function call returns an error, respond with: 🚨 *Tool execution failed* – [error message] Then stop execution. - - If scanResult.totalViolations is 0, respond with: - "✅ No legacy usage of {{COMPONENT_NAME}} found." + - If no violations are found, respond with: + "✅ No legacy usage found." Then stop execution. - Otherwise, continue to the next step. @@ -50,8 +48,7 @@ Once the user provides a subfolder choice, proceed as follows: Then stop execution. 2. Run a file-level scan: - - Use the report-violations function with these parameters: - - component: {{COMPONENT_NAME}} + - Use the report-all-violations function with these parameters: - directory: {{SUBFOLDER}} - groupBy: "file" - Store the result in a variable called fileScan. @@ -73,12 +70,4 @@ Once the user provides a subfolder choice, proceed as follows: ... - After the tag, prompt the user with: - ❓ **Please attach the "Plan Phase" rules now so I can start refactoring planning.** - -As in Step 1, any side remarks should go in an optional ... tag. - -Final instructions: -- Always use the exact format and wording provided for outputs and prompts. -- Do not add any explanations or additional text unless explicitly instructed. -- If you encounter any situations not covered by these instructions, respond with: - ⚠️ Unexpected situation encountered. Please provide further guidance. \ No newline at end of file + ❓ **Please attach the "Plan Phase" rules now so I can start refactoring planning.** \ No newline at end of file diff --git a/.cursor/flows/ds-refactoring-flow/02b-plan-refactoring-for-all-violations.mdc b/.cursor/flows/ds-refactoring-flow/02b-plan-refactoring-for-all-violations.mdc new file mode 100644 index 0000000..0da85fb --- /dev/null +++ b/.cursor/flows/ds-refactoring-flow/02b-plan-refactoring-for-all-violations.mdc @@ -0,0 +1,37 @@ +--- +description: +globs: +alwaysApply: false +--- +You are an AI assistant tasked with helping a development team migrate legacy components to a new design system. Your goal is to analyze the current codebase, identify areas that need updating, and provide a detailed plan for the migration process. This task will be completed in three phases: a comprehensive analysis, a detailed plan creation, and a checklist creation. + +You will be working with the following inputs: +{{FOLDER_PATH}}: The path to the folder containing the legacy components +{{COMPONENT_DOCS}}: The official documentation for the target design-system components +{{COMPONENT_CODE}}: The source files of the target design-system components +{{USAGE_GRAPH}}: A graph showing the usage of the legacy components in the specified folder +{{LIBRARY_DATA}}: Information about library type + +# Phase 1: Comprehensive Analysis + +1. Review all provided inputs: COMPONENT_DOCS, COMPONENT_CODE, USAGE_GRAPH, and LIBRARY_DATA. + +2. Analyze the current codebase, focusing on: + a. The approved markup and API for the target components + b. The actual implementation of the design-system components + c. All files (templates, TS, styles, specs, NgModules) that reference the legacy components + d. Dependencies and library information + +3. Create a comprehensive summary of the analysis, including: + a. Total number of files affected + b. Assessment of migration complexity (Low, Medium, High) + c. Any potential non-viable migrations that may require manual rethinking + d. Key decisions or assumptions made during the analysis + e. Insights gained from examining the component files + f. Implications of the LIBRARY_DATA on the migration process + +Write your comprehensive analysis in tags. + +# Phase 2: Detailed Plan Creation + +Please think about this problem thoroughly \ No newline at end of file diff --git a/.cursor/rules/02-plan-refactoring.mdc b/.cursor/rules/02-plan-refactoring.mdc deleted file mode 100644 index d756b8c..0000000 --- a/.cursor/rules/02-plan-refactoring.mdc +++ /dev/null @@ -1,81 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- -You are an AI assistant tasked with helping a development team migrate legacy components to a new design system. Your goal is to analyze the current codebase, identify areas that need updating, and provide a detailed plan for the migration process. This task will be completed in three phases: a comprehensive analysis, a detailed plan creation, and a checklist creation. - -You will be working with the following inputs: -{{COMPONENT_NAME}}: The name of the target design-system component -{{FOLDER_PATH}}: The path to the folder containing the legacy components -{{COMPONENT_DOCS}}: The official documentation for the target design-system component -{{COMPONENT_CODE}}: The source files of the target design-system component -{{USAGE_GRAPH}}: A graph showing the usage of the legacy component in the specified folder -{{LIBRARY_DATA}}: Information about library type - -# Phase 1: Comprehensive Analysis - -1. Review all provided inputs: COMPONENT_DOCS, COMPONENT_CODE, USAGE_GRAPH, and LIBRARY_DATA. - -2. Analyze the current codebase, focusing on: - a. The approved markup and API for the target component - b. The actual implementation of the design-system component - c. All files (templates, TS, styles, specs, NgModules) that reference the legacy component - d. Dependencies and library information - -3. Create a comprehensive summary of the analysis, including: - a. Total number of files affected - b. Assessment of migration complexity (Low, Medium, High) - c. Any potential non-viable migrations that may require manual rethinking - d. Key decisions or assumptions made during the analysis - e. Insights gained from examining the component files - f. Implications of the LIBRARY_DATA on the migration process - -Write your comprehensive analysis in tags. - -# Phase 2: Detailed Plan Creation - -Please think about this problem thoroughly and in great detail. Consider multiple approaches and show your complete reasoning. Please perform a thourough and Based on your comprehensive analysis, create a detailed migration plan: - -1. For each affected file: - a. Compare the old markup against the design-system exemplar from the COMPONENT_DOCS. - b. Classify the migration effort as: - - Simple swap (straight replacement with no loss of behavior, styling, responsive rules, animation, click/test-ID, or accessibility attributes) - - Requires restructure (minor code or CSS tweaks needed to preserve behaviors or visuals that the design-system component lacks) - - Non-viable (needs manual rethink) - c. Assign a complexity score on a scale of 1-10, adding: - - +1 per removed animation or breakpoint - - +2 per business variant that needs to be rebuilt - -2. Create an actionable plan ordered by effort, including: - a. File path & type - b. Refactor classification - c. Concrete edits needed (template, TS, styles, NgModule, spec) - d. Verification notes (2-3 static checks that can be performed by reading files only) - e. Complexity score - -3. If any items are classified as non-viable, explicitly highlight these in a separate section of your plan. - -4. Review your detailed plan against the COMPONENT_DOCS to ensure all recommendations align with the official documentation. - -5. Identify any ambiguities in your plan that could be interpreted multiple ways and list these in a separate section. - -Write your detailed migration plan in tags. - -# Phase 3: Checklist Creation - -After the user approves the plan and clarifies any ambiguities: - -1. Create a checklist that lists only actual changes as checkboxes. -2. Create a "check" phase where all verifications (2-3 static checks that can be performed by reading files only) are listed as checkboxes. -3. Ensure the checklist is comprehensive and follows directly from the approved migration plan. - -Write your checklist in tags. - -Your final output should include only the following: -1. The block -2. The block -3. The following approval request: "🛠️ Approve this plan or specify adjustments?" -4. If applicable, an ambiguity safeguard: "❓ The plan contains ambiguities: [short description]. Please clarify." - -After the user approves the plan and clarifies any ambiguities, provide only the block in your response. Also, remember to save the checklist in a file at .cursor/tmp/refactoring-checklis-{{FOLDER_PATH}}.md. \ No newline at end of file diff --git a/.cursor/rules/03-fix-violations.mdc b/.cursor/rules/03-fix-violations.mdc deleted file mode 100644 index 6b921a4..0000000 --- a/.cursor/rules/03-fix-violations.mdc +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- - -You are an AI assistant tasked with refactoring code based on a checklist and updating the checklist accordingly. Follow these steps carefully: - -1. Read the refactoring checklist from the file located at .cursor/tmp/refactoring-checklist-{{FOLDER_PATH}}. The content of this checklist is provided here: - - -{{CHECKLIST_CONTENT}} - - -2. For each component mentioned in the checklist, use the `build_component_contract` tool to create contracts. The syntax for using this tool is: - -build_component_contract(component_file, dsComponentName) - -Replace "component_files" with the actual files of the component. - -3. Execute the checklist items one by one. For each item: - a. Analyze the component using the contract built in step 2. - b. Determine if any changes are needed based on the checklist item. - c. If changes are needed, describe the changes you would make. - d. DO NOT BUILD CONTRACTS FOR THE UPDATED COMPONENT STATES - -4. Update the checklist file with the changes made. For each item, add a note describing what was changed or why no change was needed. - -5. Reflect on the changes you've made. If anything is unclear or you have additional suggestions: - a. Explicitly ask the user for confirmation. - b. Provide a clear explanation of your uncertainty or suggestion. - -6. Prepare your final output in the following format: - - - -[List the updated checklist items here, including notes on changes made or why no changes were needed] - - - -[Include any reflections, uncertainties, or additional suggestions here] - - - -[List any specific points where user confirmation is needed] - - - -Remember, your final output should only include the content within the tags. Do not include any of your thought process or the steps you took to arrive at this output. diff --git a/.cursor/rules/03-non-viable-cases.mdc b/.cursor/rules/03-non-viable-cases.mdc deleted file mode 100644 index 0d8f22d..0000000 --- a/.cursor/rules/03-non-viable-cases.mdc +++ /dev/null @@ -1,100 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- -You are a design system migration specialist executing a non-migratable component workflow. Follow this systematic process: - -## PHASE 1: IDENTIFICATION & DISCOVERY -**Step 1:** Identify and output the target component class name from previous conversation context -- Output: [CLASS_NAME] - -**Step 2:** Run CSS discovery in parallel using report-deprecated-css tool: -- **Global Styles Directory:** [USER_PROVIDED_INPUT] (if not provided, request from user) -- **Styles Overrides Directory:** [USER_PROVIDED_INPUT] (if not provided, request from user) -- **Fallback behavior:** If only one directory provided, run tool for that directory only -- **Error handling:** If neither directory provided, ask user for at least one input -- Component: [IDENTIFIED_CLASS_NAME] - -**Step 3:** Create implementation checklist -- Count total violations found across both directories -- Generate checklist item for each violation location -- **Validation Check:** Verify checklist item count = total violation count -- **Save checklist to:** `.cursor/tmp/css-cleanup/[class-name]-[scope]-non-viable-migration-checklist.md` -- **DO NOT output checklist content in chat** - only reference checklist file location -- Output format: - -[NUMBER] -[NUMBER] -Items match violations: [TRUE/FALSE] -`.cursor/tmp/css-cleanup/[class-name]-[scope]-non-viable-migration-checklist.md` - - -## PHASE 2: IMPLEMENTATION -**Work from checklist file** - reference saved checklist and update it as you progress through each item. - -Execute each checklist item systematically in this exact order: - -**Step 1: HTML Template Updates (FIRST PRIORITY)** -- Replace original component classes with "after-migration-[ORIGINAL_CLASS]" in HTML files/templates -- This must be done BEFORE any CSS changes -- Update all instances found in the violation reports -- **Update checklist:** Mark HTML items as complete - -**Step 2: CSS Selector Duplication (NOT REPLACEMENT)** -- DUPLICATE CSS selectors, do NOT replace them -- Transform: `.custom-radio {}` → `.custom-radio, .after-migration-custom-radio {}` -- Keep original selector intact alongside new prefixed selector -- This ensures both old and new classes work with identical styling -- Maintain visual parity between original and prefixed versions -- **Update checklist:** Mark CSS items as complete - -## PHASE 3: VALIDATION (Success Criteria) - MANDATORY EXECUTION -**CRITICAL:** Execute validation steps from checklist using actual tools, not just manual verification. - -**Validation 1 - CSS Count Consistency:** -- **TOOL REQUIRED:** Re-run report-deprecated-css tool on both original directories -- Compare counts with original baseline -- **Update checklist:** Mark validation item as complete -- Output: - [NUMBER] - [NUMBER] - PASS/FAIL - Deprecated class count remains identical to original - - -**Validation 2 - Violation Reduction:** -- **TOOL REQUIRED:** Run report-violations tool against modified component scope -- Compare with original violation count -- **Update checklist:** Mark validation item as complete -- Output: - [NUMBER] - [NUMBER] - [NUMBER] - [NUMBER] - PASS/FAIL - 0 violations OR exactly X fewer violations (where X = number of replacements made) - - -**Final Step:** Update saved checklist file with validation results and mark all items complete. - -## OUTPUT REQUIREMENTS -- Start with identified class name in tags -- Show violation counts and checklist summary in tags (NO detailed checklist content) -- Reference checklist file location only -- Provide step-by-step implementation status with checklist updates -- Report validation results in and tags with clear pass/fail status -- **Throughout process:** Update checklist file, don't repeat content in chat - -Execute each phase completely before proceeding to the next. Request confirmation if validation criteria are not met. - -## USAGE -To invoke this workflow, user can say: -- "Execute non-viable handling for [component]" -- "Run the non-migratable component workflow" -- "Handle non-viable component migration" -- Or simply reference this rule: @non-viable-handling -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/04-validate-changes.mdc b/.cursor/rules/04-validate-changes.mdc deleted file mode 100644 index 900b156..0000000 --- a/.cursor/rules/04-validate-changes.mdc +++ /dev/null @@ -1,67 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- -You are an AI assistant tasked with analyzing refactored code and component contracts. Your goal is to identify potential issues, breaking changes, and risky points that require attention from the development team. Follow these steps carefully: - -First, you will be provided with two inputs: - - -{{REFACTORED_FILES}} - - -This contains the list of files that have been refactored. - - -{{COMPONENT_CONTRACTS}} - - -This contains the list of available component contracts. - -Now, follow these steps: - -1. Fix eslint issues in {{REFACTORED_FILES}} using `lint-files`. DO NOT go to next step until lint errors are fixed. If there are unfixable errors ask user what to do. - -2. Use the `build_component_contract` tool to capture the refactored state of the components: - build_component_contract {{REFACTORED_FILES}} - -3. Use the `list_component_contracts` tool to get the list of available contracts: - list_component_contracts - -4. Use the `diff_component_contract` tool to get a diff of before and after contracts. You'll need to do this for each component contract. For example: - diff_component_contract old_contract new_contract - Replace "old_contract" and "new_contract" with the actual contract names from step 3. - -5. Analyze the diff to identify any potential breaking or questionable changes. Look for: - - Changes in function signatures - - Modifications to data structures - - Alterations in component interfaces - - Any other changes that might affect the behavior or usage of the components - -6. Reflect on your analysis. In your reflection, consider: - - The severity of each change - - Potential impacts on other parts of the system - - Backwards compatibility issues - - Performance implications - -7. Create a final validation report. This report should: - - Summarize the changes found - - Highlight any risky points that require elevated attention - - Provide recommendations for the developer, QA, or UAT team - -Your final output should be structured as follows: - - -[Your detailed analysis of the changes, including all potential issues and their implications] - - - -[List any questions or issues that require further clarification from the user] - - - -[Your final validation report, highlighting risky points and providing recommendations] - - -Remember, your goal is to provide a thorough and accurate analysis that will help the development team understand the implications of their refactoring. Be specific in your observations and clear in your recommendations. \ No newline at end of file diff --git a/.cursor/rules/05-prepare-report.mdc b/.cursor/rules/05-prepare-report.mdc deleted file mode 100644 index 765ba5e..0000000 --- a/.cursor/rules/05-prepare-report.mdc +++ /dev/null @@ -1,64 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- -You are an AI assistant tasked with analyzing chat history, creating testing checklists, and generating documentation for code changes. Follow these instructions carefully: - -1. First, review the entire chat history provided: - -{{CHAT_HISTORY}} - - -2. Analyze the chat history, focusing on: - - Refactoring changes discussed - - Any analysis or insights provided about the code - - Specific areas of the code that were modified - - Any potential risks or concerns mentioned - -3. Reflect on this information, considering: - - The overall impact of the changes - - Potential edge cases or scenarios that might be affected - - Any areas that might require special attention during testing - -4. Create detailed testing checklists for three roles: Developer, Manual QA Engineer, and UAT Professional. For each role, provide a list of specific items to test or verify. Include the following in your checklists: - - Highlight any uncertainties that need clarification - - Specify verification points that need to be made - - Ensure coverage of both functional and non-functional aspects affected by the changes - -Format your checklists using markdown, with each role as a second-level heading (##) and checklist items as bullet points (-). - -5. Save the testing checklists in a verification document. Use the following path: - .cursor/tmp/verification-checklist-{{FOLDER}}.md - -6. Generate a semantic commit message for the changes discussed in the chat. The commit message should: - - Start with the [AI] mark - - Follow the conventional commit format (type: description) - - Briefly summarize the main changes or purpose of the commit - -7. Create a short PR (Pull Request) description based on the changes discussed in the chat. The description should: - - Summarize the main changes and their purpose - - Mention any significant refactoring or improvements made - - Highlight any areas that require special attention during review - -Provide your output in the following format: - - -Your analysis of the chat history and reflection on the information - - - -Your detailed testing checklists for Developer, Manual QA Engineer, and UAT Professional - - - -The path where the verification document is saved - - - -Your generated semantic commit message - - - -Your short PR description - \ No newline at end of file diff --git a/.cursor/rules/clean-global-styles.mdc b/.cursor/rules/clean-global-styles.mdc deleted file mode 100644 index 518fa86..0000000 --- a/.cursor/rules/clean-global-styles.mdc +++ /dev/null @@ -1,43 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- - -You are an AI assistant tasked with analyzing a project for deprecated CSS classes and component violations. You will be provided with three inputs: - -{{SOURCE_PATH}} - - -{{GLOBAL_STYLES_PATH}} - - -{{COMPONENT_NAME}} - -Follow these steps to complete the task: - -1. Use the `report-deprecated-css` tool to find occurrences of deprecated CSS classes in the global styles: - report-deprecated-css {{GLOBAL_STYLES_PATH}} -2. Use the `report-deprecated-css` tool to find occurrences of deprecated CSS classes in the source folder: - report-deprecated-css {{SOURCE_PATH}} -3. Use the `report-violations` tool to find usages of deprecated component classes in the source folder: - report-violations {{SOURCE_PATH}} -4. Analyze the results from the tool calls: - a. If violations are found in the source folder, state the number of violations and recommend fixing them first. - b. If no violations are found, list the deprecated CSS (if any) found in the global styles and source path. -5. Format your final output using the following structure: - - [Include your analysis of the results here] - - - [If violations were found: Recommend fixing them. - If only deprecated CSS is found: State that deprecated CSS was found in the project.] - - - [Leave this section empty if violations were found. - If no violations were found but deprecated CSS exists, ask whether to: - -- Remove the deprecated CSS -- Save it in .cursor/tmp/{{COMPONENT_NAME}}-deprecated-css.md -- Do nothing] - diff --git a/.cursor/rules/discover-affected-urls.mdc b/.cursor/rules/discover-affected-urls.mdc deleted file mode 100644 index 2916ff1..0000000 --- a/.cursor/rules/discover-affected-urls.mdc +++ /dev/null @@ -1,81 +0,0 @@ ---- -description: Discovering URL(s) where given component(s) can be found -globs: -alwaysApply: false ---- -{ - "schema_version": "1.0", - "task": "Angular routing analysis and URL discovery", - "description": "Analyze Angular routing hierarchy using bottom-up approach: identify complete routing structure from app_directory (top point) and parent routes for specific files (bottom point), then connect them bottom-up with comprehensive redirect resolution. Output plain text with markdown syntax throughout; at the end, persist discovered URLs to JSON files as specified.", - "inputs": { - "app_directory": "Directory path where route definition files should be scanned (e.g., 'src/app')", - "files": "Array of specific file paths to analyze (e.g., ['src/app/components/user.component.ts'])" - }, - "phases": [ - { - "id": 1, - "name": "Routing Hierarchy Discovery & Component Search", - "requires_approval": false, - "response_format": "plain text with markdown syntax wrapped with tags, maximum 200 tokens, NO file generation", - "steps": [ - "TOP POINT DISCOVERY: Scan app_directory completely to identify ALL routing files and build complete routing hierarchy tree (app.routes.ts, app-routing.module.ts, main.ts, and all loadChildren declarations).", - "BOTTOM-UP DUAL DISCOVERY: For each file in 'files' parameter, extract component class name AND template selector. From these specific components, search upward for BOTH: (1) Direct parent routes referencing the component class, (2) Parent components using the template selector. MANDATORY: Trace ALL upward paths from each specific component - do not stop after finding first parent.", - "BOTTOM-UP ROUTE MAPPING: From each specific component file, map ALL discovered upward paths. A single component may have multiple parent relationships (direct route definitions + template selector usage). Document every upward path discovered from the bottom point.", - "ENHANCED CLASSIFICATION: Classify each component as DIRECTLY-ROUTABLE (found in route definitions), INDIRECTLY-ROUTABLE (template selector usage only), or BOTH. Components can have multiple routing scenarios simultaneously.", - "VALIDATION: Present 'Files to Analyze', 'TOP POINT: Complete App Routing Hierarchy', 'BOTTOM POINT: All Route Scenarios per File', and 'ROUTE COMPLETENESS CHECK' confirming exhaustive search was performed." - ] - }, - { - "id": 2, - "name": "Non-Routable Component Analysis", - "requires_approval": false, - "response_format": "plain text with markdown syntax wrapped with tags, maximum 200 tokens, NO file generation", - "steps": [ - "IDENTIFY NON-ROUTABLE COMPONENTS: From the files list, identify components without direct route definitions (modals, dialogs, library components, child components).", - "ROUTABLE PARENT DISCOVERY: For non-routable components, traverse the component hierarchy upward through multiple levels to find the first routable parent component. Handle multi-level chains where non-routable components are nested within other non-routable components.", - "MODAL/LIBRARY TRIGGER ANALYSIS: For modal and library components, identify the parent component where they are used, triggered, or instantiated (MatDialog.open, component selectors, service calls).", - "USAGE RELATIONSHIP MAPPING: Document the complete relationship chain including all intermediate levels: Non-routable Component → Intermediate Parent(s) → Final Routable Parent → Route Path. Show the full traversal path for multi-level hierarchies.", - "NON-ROUTABLE VALIDATION: Confirm all non-routable components have identified parent relationships before proceeding to route construction." - ] - }, - { - "id": 3, - "name": "Exhaustive Route Construction & Verification", - "requires_approval": false, - "response_format": "plain text with markdown syntax wrapped with tags, maximum 200 tokens, NO file generation", - "steps": [ - "COMPREHENSIVE REDIRECT MAPPING: Scan ALL routing files in the complete hierarchy for 'redirectTo' declarations. Create redirect mapping table with [original_path] → [redirectTo_path], including application-level, legacy, and inter-module redirects. Check for conflicting redirects and redirect loops.", - "BOTTOM-UP EXHAUSTIVE TRACING: For each component from Phase 1, trace ALL upward paths to build complete route chains: (1) Direct bottom-up routes from component class to routing hierarchy, (2) Indirect bottom-up routes from template selector through parent components to routing hierarchy. Cross-reference both upward traces to ensure no paths are missed.", - "MULTI-SCENARIO PATH CONSTRUCTION: Build complete paths for every routing scenario discovered. Apply redirect resolution at each level. Document all paths: direct routes, redirected routes, and parent-access routes for non-routable components.", - "ROUTE COMPLETENESS VERIFICATION: Cross-check that all component routing scenarios from Phase 1 have corresponding path constructions. Verify no discovered routes were omitted from path building.", - "MANDATORY PHASE 3 EXAMPLES: Provide 1-3 concrete route examples per routing scenario (not just per file). If component has both direct and indirect routes, show examples for BOTH. Include realistic culture codes and parameters." - ] - }, - { - "id": 4, - "name": "Parameter & Visibility Analysis", - "requires_approval": false, - "response_format": "plain text with markdown syntax wrapped with tags, maximum 200 tokens, NO file generation", - "steps": [ - "Extract parameter constraints from TypeScript files using ALL established routing connections from Phase 3.", - "Scan for visibility conditions: @if, *ngIf directives, route guards, resolvers, route data, permissions, and feature flags along ALL discovered route paths.", - "HUMAN-FRIENDLY VISIBILITY SUMMARY: Produce a concise developer-friendly object with keys such as: 'authorization' ('required' | 'not required'), 'guards' (array of guard class names), 'resolvers' (string like 'A or B' when multiple), 'redirectBehavior' (e.g., '/old redirects to /new'), 'featureFlags' (array), 'conditions' (bullet-like phrases distilled from *ngIf/@if and route data), and optional 'notes'. Normalize common patterns (e.g., allowAnonymous: true → authorization: 'not required'). Include inherited conditions across the bottom-up chain.", - "Provide realistic examples with different culture codes and parameter values for ALL traced paths (direct and indirect).", - "Validate that all routing scenarios maintain parameter inheritance and visibility constraints across different access methods." - ] - }, - { - "id": 5, - "name": "MANDATORY Comprehensive Summary Generation", - "requires_approval": false, - "response_format": "plain text with markdown syntax wrapped with tags", - "steps": [ - "COLLECT ALL SCENARIOS: Gather route examples from Phase 3 for EVERY routing scenario discovered. Include both direct routes and parent-access routes. MANDATORY: Every component must have at least one URL example for each routing scenario identified.", - "GENERATE SUMMARY STRUCTURE: Create '🎯 QUICK ACCESS SUMMARY' section with exact format: Header 'Files Analyzed: [List all files]' followed by subsections '## File: [Filename.extension]' for each file.", - "FORMAT VALIDATION: Use MANDATORY bullet list format with markdown bullets (-) for route examples. CRITICAL: Each route example MUST start with '- /' (markdown bullet + forward slash). FORBIDDEN: NO tables, NO pipes (|), NO table formatting whatsoever.", - "COMPLETE SCENARIO COVERAGE: Include ALL discovered routing scenarios: direct routes, redirected routes, and parent-access routes. Add clear notation: '(direct route)', '(redirects to X)', '(non-routable, found in parent component at URL)'. MANDATORY: Cover every routing scenario from Phase 1 discovery.", - "FINAL VERIFICATION AND PERSISTENCE (MANDATORY): Confirm the summary contains ALL files AND ALL routing scenarios. Then, for each analyzed component, WRITE a JSON file at `.cursor/tmp/discovered-urls/{{component-name}}.json` with schema: { \"component\": { \"urls\": [from summary], \"visibilityConditions\": } }. Use the component TypeScript class name when available for {{component-name}}, otherwise the component file basename without extensions. Create the target directory if it does not exist and overwrite existing files." - ] - } - ] -} \ No newline at end of file diff --git a/docs/ds-refactoring-flow.md b/docs/ds-refactoring-flow.md index 949ce3c..8703967 100644 --- a/docs/ds-refactoring-flow.md +++ b/docs/ds-refactoring-flow.md @@ -5,12 +5,22 @@ This document describes a 5-step AI-assisted refactoring process for migrating legacy components to the design system. Each step uses a specific rule file (.mdc) that guides the Cursor agent through automated analysis and code changes. -**Process Summary:** -1. **Find Violations** → Identify deprecated component usage -2. **Plan Refactoring** → Create detailed migration strategy - - **If viable:** Proceed to step 3 with normal checklist - - **If non-viable:** Use alternative handling (see Non-Viable Cases section below) +**Flow Approaches:** + +The refactoring process offers two alternative approaches for the initial phases: + +**Option A: Targeted Approach** (recommended for focused, incremental migrations) +1. **Find Violations** (`01-find-violations.mdc`) → Identify specific deprecated component usage +2. **Plan Refactoring** (`02-plan-refactoring.mdc`) → Create detailed migration strategy for specific cases + +**Option B: Comprehensive Approach** (recommended for large-scale migrations) +1. **Find All Violations** (`01b-find-all-violations.mdc`) → Scan entire codebase, group by folders, select subfolder for detailed analysis +2. **Plan Refactoring for All Violations** (`02b-plan-refactoring-for-all-violations.mdc`) → Create comprehensive migration plan for all violations in scope + +**Continuation Steps** (used with both approaches): 3. **Fix Violations** → Execute code changes + - **If viable:** Proceed with normal checklist (`03-fix-violations.mdc`) + - **If non-viable:** Use alternative handling (`03-non-viable-cases.mdc`) 4. **Validate Changes** → Verify refactoring safety 5. **Prepare Report** → Generate testing checklists and documentation @@ -78,6 +88,153 @@ The rule enforces strict output formatting with `` and `` t Claude-4-Sonnet +## 01b-find-all-violations.mdc (Alternative Comprehensive Approach) + +### Goal + +Perform comprehensive analysis of an entire codebase to identify all deprecated component usage patterns across multiple directories and components. This rule provides a broader scope alternative to the targeted approach, allowing teams to understand the full migration scope before focusing on specific areas for detailed planning. + +### Process + +To start this process, drag file `01b-find-all-violations.mdc` to the cursor chat and provide the `directory` parameter, your chat message will look like this: + +``` +@01b-find-all-violations.mdc directory=path/to/directory +``` + +This rule follows a comprehensive two-step process to find all violations: + +**Step 1: Global folder-level scan** +- Scans the entire specified directory tree for all deprecated component violations +- Groups results by folder to provide overview of violation distribution +- Provides a ranked list of folders with violation counts and file counts +- Allows user to understand migration scope across the entire codebase +- Enables selection of specific subfolder for focused analysis + +**Step 2: Targeted file-level scan** +- Performs detailed scanning of the selected subfolder from step 1 +- Groups violations by individual files within the selected scope +- Outputs a sorted list of files with violation counts for precise targeting +- Prepares comprehensive data for the planning phase + +> After the second scan, AI will explicitly ask you to attach the comprehensive planning rule. + +### Tools used + +- `report-all-violations` - Comprehensive MCP tool that analyzes all deprecated design system CSS usage across the codebase + - Parameters: `directory`, `groupBy` (folder/file) + - Returns: Complete violation data with counts and locations across all components + - Supports both folder-level overview and file-level detailed analysis + +### Flow + +> You don't need to manually perform any of the listed actions except providing the `directory=path/to/folder` in the initial message. + +1. **Initial Setup**: User provides `{{DIRECTORY}}` parameter for comprehensive analysis +2. **Global Scan**: Execute `report-all-violations` with `groupBy: "folder"` for complete overview +3. **Error Handling**: Check for tool errors or zero violations across entire codebase +4. **Folder Results**: Display ranked folder list with comprehensive violation and file counts +5. **User Selection**: Prompt user to choose a subfolder from the comprehensive results for detailed focus +6. **Targeted Scan**: Execute `report-all-violations` with `groupBy: "file"` on selected subfolder +7. **File Results**: Display sorted file list with precise violation counts for planning +8. **Transition**: Prompt user to attach comprehensive planning rules for next step + +The rule enforces structured output with `` and `` tags, and uses `` blocks for error messages and progress updates. + +### Preferred model + +Claude-4-Sonnet + +## 02b-plan-refactoring-for-all-violations.mdc (Alternative Comprehensive Approach) + +### Goal + +Create a comprehensive migration strategy for large-scale refactoring of legacy components across multiple files and violation types. This rule provides a thorough analysis approach for complex migration scenarios involving extensive deprecated component usage, focusing on creating detailed implementation plans for comprehensive scope migrations. + +### Process + +To start this process, drag file `02b-plan-refactoring-for-all-violations.mdc` to the cursor chat after completing the comprehensive violations analysis. + +The rule implements an enhanced three-phase comprehensive planning process: + +**Phase 1: Comprehensive Analysis** +- Reviews extensive component documentation, implementation code, and usage patterns across all violation files +- Analyzes complete scope of affected files (templates, TypeScript, styles, specs, NgModules) +- Assesses migration complexity across the entire selected scope and identifies potential non-viable migrations +- Evaluates library dependencies and their comprehensive impact on large-scale migration +- Considers cross-component dependencies and interaction patterns + +**Phase 2: Detailed Plan Creation for Complete Scope** +- Compares deprecated usage patterns against design system exemplars across all files +- Classifies each migration comprehensively as: Simple swap, Requires restructure, or Non-viable +- Assigns complexity scores (1-10) with comprehensive penalties for animations/breakpoints/variants across scope +- Creates comprehensive actionable plans ordered by effort with concrete edits needed for all files +- Provides extensive verification notes for static file-based checks across the migration scope +- Considers migration dependencies and optimal sequencing for large-scale changes + +--- +**🚦 Quality Gate 1 (Comprehensive Review)** + +Before proceeding to Phase 3, you must review the comprehensive migration plan. + +**Required Actions:** +- Review the complete migration strategy for all identified violations +- **If all components are viable for migration:** Approve or request modifications, then proceed with comprehensive checklist creation +- **If some components are non-viable:** Developer must thoroughly review and confirm non-viability decisions, then use `@03-non-viable-cases.mdc` for non-viable cases +- **If mixed viability:** Proceed with checklist for viable cases and handle non-viable cases separately +- Clarify any uncertainties regarding the comprehensive approach + +**Next Step:** +- **All viable migrations:** When satisfied with the plan, the agent will create a comprehensive checklist +- **Mixed or non-viable migrations:** Use appropriate handling rules for different component categories +--- + +**Phase 3: Comprehensive Checklist Creation** +- Generates extensive checklist covering all viable migrations with detailed checkbox items +- Creates comprehensive verification phase with static checks across all affected files +- Includes dependency management and sequencing considerations for large-scale migrations +- Saves comprehensive checklist to `.cursor/tmp/refactoring-checklist-{{FOLDER_PATH}}.md` + +> After the comprehensive checklist is generated and you see it in the chat, it is time to attach the fix violations rule. + +### Tools used + +- `get-ds-component-data` - Retrieves comprehensive component information for all involved components + - Returns: Complete implementation files, documentation files, import paths for multiple components + - Provides: Component source code, API documentation, usage examples across scope + +- `build-component-usage-graph` - Maps comprehensive component dependencies and usage patterns + - Parameters: `directory`, `violationFiles` (from comprehensive scan, automatically picked up) + - Returns: Complete graph showing where all components are imported and used across scope + - Analyzes: modules, specs, templates, styles, reverse dependencies for large-scale impact + +- `get-project-dependencies` - Analyzes project structure and dependencies for comprehensive scope + - Parameters: `directory`, optional `componentName` + - Returns: library type (buildable/publishable), peer dependencies for scope assessment + - Validates: import paths, workspace configuration for large-scale migration compatibility + +### Flow + +> You don't need to manually perform any of the listed actions. + +1. **Comprehensive Input Gathering**: Collect all component data, folder path, documentation, code, usage graphs, and library data for complete scope +2. **Extensive Analysis**: Review all inputs and analyze comprehensive codebase impact across all violation files +3. **Large-Scale Complexity Assessment**: Evaluate migration difficulty and identify non-viable cases across complete scope +4. **Comprehensive File Classification**: Categorize each file's migration requirements within the broader context +5. **Detailed Plan Creation**: Generate comprehensive migration steps with complexity scores for all files +6. **Extensive Verification Design**: Create static checks for comprehensive plan validation across scope +7. **Large-Scale Ambiguity Resolution**: Identify and request clarification for unclear aspects across scope +8. **Comprehensive Approval Process**: Present complete plan with "🛠️ Approve this comprehensive plan or specify adjustments?" +9. **Extensive Checklist Generation**: Create detailed actionable checklist after approval covering all scope +10. **Comprehensive File Persistence**: Save complete checklist to temporary file for large-scale reference + +The rule enforces structured output with ``, ``, and `` tags, and includes enhanced ambiguity safeguards for complex large-scale scenarios. + +### Preferred model + +- Non-complex cases: Claude-4-Sonnet +- Complex large-scale cases: Claude-4-Sonnet (thinking) + ## 02-plan-refactoring.mdc ### Goal diff --git a/docs/tools.md b/docs/tools.md index e83eed6..c25c4f9 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -16,6 +16,15 @@ This document provides comprehensive guidance for AI agents working with Angular **Output**: Structured violation reports grouped by file or folder **Best Practice**: Always run this before other migration tools to establish baseline +#### `report-all-violations` +**Purpose**: Reports all deprecated DS CSS usage for every DS component within a directory +**AI Usage**: Use for a fast, global inventory of violations across the codebase before narrowing to specific components +**Key Parameters**: +- `directory`: Target analysis directory (use relative paths like `./src/app`) +- `groupBy`: `"file"` or `"folder"` for result organization (default: `"file"`) +**Output**: Structured violation reports grouped by file or folder covering all DS components +**Best Practice**: Use to discover all violations and establish the baseline for subsequent refactoring. + #### `get-project-dependencies` **Purpose**: Analyzes project structure, dependencies, and buildability **AI Usage**: Validate project architecture before suggesting refactoring strategies diff --git a/packages/angular-mcp-server/src/lib/tools/ds/ds.tools.ts b/packages/angular-mcp-server/src/lib/tools/ds/ds.tools.ts index a4f3e7e..579a6da 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/ds.tools.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/ds.tools.ts @@ -6,7 +6,10 @@ import { ToolSchemaOptions } from '@push-based/models'; import { dsComponentCoveragePlugin } from '@push-based/ds-component-coverage'; import { baseToolsSchema } from '../schema'; import { join } from 'node:path'; -import { reportViolationsTools } from './report-violations/index.js'; +import { + reportViolationsTools, + reportAllViolationsTools, +} from './report-violations/index.js'; export const componentCoverageToolsSchema: ToolSchemaOptions = { name: 'ds_component-coverage', @@ -127,4 +130,8 @@ export const componentCoverageTools = [ }, ]; -export const dsTools = [...componentCoverageTools, ...reportViolationsTools]; +export const dsTools = [ + ...componentCoverageTools, + ...reportViolationsTools, + ...reportAllViolationsTools, +]; diff --git a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/index.ts b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/index.ts index eec0fe3..0ea9e88 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/index.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/index.ts @@ -1,4 +1,5 @@ -export { reportViolationsTools } from './report-violations.tool.js'; +export { reportViolationsTools } from './report-violations.tool'; +export { reportAllViolationsTools } from './report-all-violations.tool'; export type { ReportViolationsOptions, diff --git a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts new file mode 100644 index 0000000..a624d56 --- /dev/null +++ b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts @@ -0,0 +1,110 @@ +import { + BaseHandlerOptions, + createHandler, +} from '../shared/utils/handler-helpers.js'; +import { + COMMON_ANNOTATIONS, + createProjectAnalysisSchema, +} from '../shared/models/schema-helpers.js'; +import { + analyzeProjectCoverage, + extractComponentName, +} from '../shared/violation-analysis/coverage-analyzer.js'; +import { + formatViolations, + filterFailedAudits, + groupIssuesByFile, +} from '../shared/violation-analysis/formatters.js'; +import { loadAndValidateDsComponentsFile } from '../../../validation/ds-components-file-loader.validation.js'; +import { RESULT_FORMATTERS } from '../shared/utils/handler-helpers.js'; + +interface ReportAllViolationsOptions extends BaseHandlerOptions { + directory: string; + groupBy?: 'file' | 'folder'; +} + +export const reportAllViolationsSchema = { + name: 'report-all-violations', + description: + 'Scan a directory for deprecated design system CSS classes defined in the config at `deprecatedCssClassesPath`, and output a usage report', + inputSchema: createProjectAnalysisSchema({ + groupBy: { + type: 'string', + enum: ['file', 'folder'], + description: 'How to group the results', + default: 'file', + }, + }), + annotations: { + title: 'Report All Violations', + ...COMMON_ANNOTATIONS.readOnly, + }, +}; + +export const reportAllViolationsHandler = createHandler< + ReportAllViolationsOptions, + string[] +>( + reportAllViolationsSchema.name, + async (params, { cwd, deprecatedCssClassesPath }) => { + const groupBy = params.groupBy || 'file'; + const dsComponents = await loadAndValidateDsComponentsFile( + cwd, + deprecatedCssClassesPath || '', + ); + + const coverageResult = await analyzeProjectCoverage({ + cwd, + returnRawData: true, + directory: params.directory, + dsComponents, + }); + + const raw = coverageResult.rawData?.rawPluginResult; + if (!raw) return []; + + const failedAudits = filterFailedAudits(raw); + if (failedAudits.length === 0) return ['No violations found.']; + + if (groupBy === 'file') { + const lines: string[] = []; + for (const audit of failedAudits) { + extractComponentName(audit.title); + const fileGroups = groupIssuesByFile( + audit.details?.issues ?? [], + params.directory, + ); + for (const [fileName, { lines: fileLines, message }] of Object.entries( + fileGroups, + )) { + const sorted = + fileLines.length > 1 + ? [...fileLines].sort((a, b) => a - b) + : fileLines; + const lineInfo = + sorted.length > 1 + ? `lines ${sorted.join(', ')}` + : `line ${sorted[0]}`; + lines.push(`${fileName} (${lineInfo}): ${message}`); + } + } + return lines; + } + + const formattedContent = formatViolations(raw, params.directory, { + groupBy: 'folder', + }); + return formattedContent.map( + (item: { type?: string; text?: string } | string) => + typeof item === 'string' ? item : (item?.text ?? String(item)), + ); + }, + (result) => RESULT_FORMATTERS.list(result, 'Design System Violations:'), +); + +export const reportAllViolationsTools = [ + { + schema: reportAllViolationsSchema, + handler: reportAllViolationsHandler, + }, +]; diff --git a/packages/angular-mcp-server/src/lib/tools/ds/tools.ts b/packages/angular-mcp-server/src/lib/tools/ds/tools.ts index 07c47ec..ea98cb7 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/tools.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/tools.ts @@ -1,5 +1,8 @@ import { ToolsConfig } from '@push-based/models'; -import { reportViolationsTools } from './report-violations'; +import { + reportViolationsTools, + reportAllViolationsTools, +} from './report-violations'; import { getProjectDependenciesTools } from './project/get-project-dependencies.tool'; import { reportDeprecatedCssTools } from './project/report-deprecated-css.tool'; import { buildComponentUsageGraphTools } from './component-usage-graph'; @@ -14,6 +17,7 @@ import { export const dsTools: ToolsConfig[] = [ // Project tools ...reportViolationsTools, + ...reportAllViolationsTools, ...getProjectDependenciesTools, ...reportDeprecatedCssTools, ...buildComponentUsageGraphTools, diff --git a/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.html b/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.html new file mode 100644 index 0000000..d9a47d9 --- /dev/null +++ b/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.html @@ -0,0 +1,89 @@ + + +
+

Multi-Violation Test Component

+ + +
+

Button Violations (DsButton)

+ + + +
+ + +
+

Badge Violations (DsBadge)

+ 50% OFF +
+ Special Product + NEW +
+
+ + +
+

Tabs Violations (DsTabsModule)

+ +
+
Content for Tab 1
+
Content for Tab 2
+
Content for Tab 3
+
+
+ + +
+

Card Violations (DsCard)

+
+
+

Legacy Card Header

+ +
+
+

This is a legacy card implementation using deprecated CSS classes.

+ FEATURED +
+ +
+
+ + +
+

Mixed Violations

+
+ +
+ + +
+
+
+
diff --git a/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.scss b/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.scss new file mode 100644 index 0000000..35e5e41 --- /dev/null +++ b/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.scss @@ -0,0 +1,178 @@ +/* Multi-violation test component styles using deprecated CSS classes */ + +.test-container { + padding: 20px; + max-width: 800px; + margin: 0 auto; +} + +/* ❌ BAD: DsButton deprecated styles - 'btn', 'btn-primary', 'legacy-button' */ +.btn { + display: inline-block; + padding: 8px 16px; + margin: 4px; + border: 1px solid #ccc; + border-radius: 4px; + background-color: #f8f9fa; + color: #333; + cursor: pointer; + font-size: 14px; + text-decoration: none; + + &:hover { + background-color: #e9ecef; + } +} + +.btn-primary { + background-color: #007bff; + border-color: #007bff; + color: white; + + &:hover { + background-color: #0056b3; + } +} + +.legacy-button { + background: linear-gradient(45deg, #ff6b6b, #feca57); + border: none; + padding: 10px 20px; + color: white; + border-radius: 8px; + cursor: pointer; + font-weight: bold; + margin: 4px; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0,0,0,0.2); + } +} + +/* ❌ BAD: DsBadge deprecated styles - 'offer-badge' */ +.offer-badge { + background-color: #ff4757; + color: white; + padding: 4px 8px; + border-radius: 12px; + font-size: 12px; + font-weight: bold; + text-transform: uppercase; + margin-left: 8px; + display: inline-block; + + &:before { + content: "🔥 "; + } +} + +/* ❌ BAD: DsTabsModule deprecated styles - 'tab-nav', 'nav-tabs', 'tab-nav-item' */ +.nav-tabs { + display: flex; + list-style: none; + padding: 0; + margin: 0; + border-bottom: 2px solid #dee2e6; +} + +.tab-nav { + background-color: #f8f9fa; + border-radius: 4px 4px 0 0; +} + +.tab-nav-item { + padding: 12px 16px; + cursor: pointer; + border: 1px solid transparent; + border-bottom: none; + background-color: #f8f9fa; + color: #495057; + + &:hover { + background-color: #e9ecef; + } + + &.active { + background-color: white; + border-color: #dee2e6; + color: #007bff; + border-bottom: 2px solid white; + margin-bottom: -2px; + } +} + +/* ❌ BAD: DsCard deprecated styles - 'card' */ +.card { + border: 1px solid #dee2e6; + border-radius: 8px; + background-color: white; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + margin: 16px 0; + overflow: hidden; +} + +.card-header { + padding: 16px; + background-color: #f8f9fa; + border-bottom: 1px solid #dee2e6; + display: flex; + justify-content: space-between; + align-items: center; + + h4 { + margin: 0; + color: #495057; + } +} + +.card-body { + padding: 16px; + + p { + margin: 0 0 12px 0; + color: #6c757d; + } +} + +.card-footer { + padding: 16px; + background-color: #f8f9fa; + border-top: 1px solid #dee2e6; + text-align: right; +} + +/* Section styling */ +.button-section, +.badge-section, +.tabs-section, +.card-section, +.mixed-section { + margin-bottom: 32px; + + h3 { + color: #495057; + border-bottom: 1px solid #dee2e6; + padding-bottom: 8px; + margin-bottom: 16px; + } +} + +.product-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px; + border: 1px solid #dee2e6; + border-radius: 4px; + margin: 8px 0; + background-color: #f8f9fa; +} + +.tab-content { + padding: 20px; + border: 1px solid #dee2e6; + border-top: none; + background-color: white; + min-height: 100px; +} diff --git a/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.ts b/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.ts new file mode 100644 index 0000000..8eb70a3 --- /dev/null +++ b/packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.ts @@ -0,0 +1,29 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-multi-violation-test', + templateUrl: './multi-violation-test.component.html', + styleUrls: ['./multi-violation-test.component.scss'], + standalone: true +}) +export class MultiViolationTestComponent { + activeTab = 0; + showCard = true; + notifications = 5; + + constructor() { + console.log('MultiViolationTestComponent initialized'); + } + + switchTab(index: number) { + this.activeTab = index; + } + + toggleCard() { + this.showCard = !this.showCard; + } + + handleButtonClick() { + console.log('Legacy button clicked'); + } +} diff --git a/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-definition.visitor.ts b/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-definition.visitor.ts index b6f246d..495686b 100644 --- a/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-definition.visitor.ts +++ b/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-definition.visitor.ts @@ -54,12 +54,14 @@ export const createClassDefinitionVisitor = ( }, visitRule(rule: Rule) { - getMatchingClassNames( + const matchingClassNames = getMatchingClassNames( { selector: rule.selector }, deprecatedCssClasses, - ).forEach((className) => { + ); + + if (matchingClassNames.length > 0) { const message = classUsageMessage({ - className, + className: matchingClassNames.join(', '), rule, componentName: componentReplacement.componentName, docsUrl: componentReplacement.docsUrl, @@ -70,7 +72,7 @@ export const createClassDefinitionVisitor = ( severity: 'error', source: styleAstRuleToSource(rule, isInline ? startLine : 0), }); - }); + } }, }; }; diff --git a/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-definition.visitor.unit.test.ts b/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-definition.visitor.unit.test.ts index 60c96d6..960ce19 100644 --- a/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-definition.visitor.unit.test.ts +++ b/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-definition.visitor.unit.test.ts @@ -3,15 +3,46 @@ import { createClassDefinitionVisitor } from './class-definition.visitor'; import postcss from 'postcss'; import { visitEachChild } from '@push-based/styles-ast-utils'; -describe('ClassUsageStylesheetVisitor', () => { +describe('ClassDefinitionVisitor', () => { let cssAstVisitor: ReturnType; - it('should not find class when it is not a class-binding', () => { + it('should find deprecated class in CSS selector', () => { const styles = ` /* This comment is here */ .btn { color: red; } + `; + + cssAstVisitor = createClassDefinitionVisitor({ + deprecatedCssClasses: ['btn'], + componentName: 'DsButton', + docsUrl: 'docs.example.com/DsButton', + }); + + const ast = postcss.parse(styles, { from: 'styles.css' }); + visitEachChild(ast, cssAstVisitor); + + expect(cssAstVisitor.getIssues()).toHaveLength(1); + const message = cssAstVisitor.getIssues()[0].message; + expect(message).toContain('btn'); + expect(message).toContain('DsButton'); + expect(cssAstVisitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'styles.css', + position: expect.any(Object), + }), + }), + ); + }); + + it('should not find class when it is not deprecated', () => { + const styles = ` + .safe-class { + color: red; + } #btn-1 { color: green; @@ -20,7 +51,21 @@ describe('ClassUsageStylesheetVisitor', () => { button { color: blue; } + `; + + cssAstVisitor = createClassDefinitionVisitor({ + deprecatedCssClasses: ['btn'], + componentName: 'DsButton', + }); + const ast = postcss.parse(styles, { from: 'styles.css' }); + visitEachChild(ast, cssAstVisitor); + + expect(cssAstVisitor.getIssues()).toHaveLength(0); + }); + + it('should find deprecated class in complex selector', () => { + const styles = ` div > button.btn { color: blue; } @@ -32,26 +77,179 @@ describe('ClassUsageStylesheetVisitor', () => { }); const ast = postcss.parse(styles, { from: 'styles.css' }); - visitEachChild(ast, cssAstVisitor); - expect(cssAstVisitor?.getIssues?.()).toStrictEqual([ - { - filePath: - '/Users/michael_hladky/WebstormProjects/code-pushup-design-system/plugins/ds-component-coverage/styles.css', - length: 4, - message: 'Class ".btn" is used in the stylesheet.', - severity: 'info', - start: 60, - }, - { - filePath: - '/Users/michael_hladky/WebstormProjects/code-pushup-design-system/plugins/ds-component-coverage/styles.css', - length: 4, - message: 'Class ".btn" is used in the stylesheet.', - severity: 'info', - start: 283, - }, - ]); + expect(cssAstVisitor.getIssues()).toHaveLength(1); + const message = cssAstVisitor.getIssues()[0].message; + expect(message).toContain('btn'); + expect(message).toContain('DsButton'); + expect(cssAstVisitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'styles.css', + position: expect.any(Object), + }), + }), + ); + }); + + describe('deduplication', () => { + it('should deduplicate multiple deprecated classes in same selector', () => { + const styles = ` + .btn.btn-primary { + color: red; + } + `; + + cssAstVisitor = createClassDefinitionVisitor({ + deprecatedCssClasses: ['btn', 'btn-primary'], + componentName: 'DsButton', + docsUrl: 'docs.example.com/DsButton', + }); + + const ast = postcss.parse(styles, { from: 'styles.css' }); + visitEachChild(ast, cssAstVisitor); + + expect(cssAstVisitor.getIssues()).toHaveLength(1); + const message = cssAstVisitor.getIssues()[0].message; + expect(message).toContain('btn, btn-primary'); + expect(message).toContain('DsButton'); + expect(cssAstVisitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'styles.css', + position: expect.any(Object), + }), + }), + ); + }); + + it('should deduplicate multiple deprecated classes in comma-separated selectors', () => { + const styles = ` + .btn, .btn-primary { + color: red; + } + `; + + cssAstVisitor = createClassDefinitionVisitor({ + deprecatedCssClasses: ['btn', 'btn-primary'], + componentName: 'DsButton', + docsUrl: 'docs.example.com/DsButton', + }); + + const ast = postcss.parse(styles, { from: 'styles.css' }); + visitEachChild(ast, cssAstVisitor); + + expect(cssAstVisitor.getIssues()).toHaveLength(1); + const message = cssAstVisitor.getIssues()[0].message; + expect(message).toContain('btn, btn-primary'); + expect(message).toContain('DsButton'); + expect(cssAstVisitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'styles.css', + position: expect.any(Object), + }), + }), + ); + }); + + it('should deduplicate three deprecated classes in same selector', () => { + const styles = ` + .btn.btn-primary.btn-large { + color: red; + } + `; + + cssAstVisitor = createClassDefinitionVisitor({ + deprecatedCssClasses: ['btn', 'btn-primary', 'btn-large'], + componentName: 'DsButton', + docsUrl: 'docs.example.com/DsButton', + }); + + const ast = postcss.parse(styles, { from: 'styles.css' }); + visitEachChild(ast, cssAstVisitor); + + expect(cssAstVisitor.getIssues()).toHaveLength(1); + const message = cssAstVisitor.getIssues()[0].message; + expect(message).toContain('btn, btn-primary, btn-large'); + expect(message).toContain('DsButton'); + expect(cssAstVisitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'styles.css', + position: expect.any(Object), + }), + }), + ); + }); + + it('should still create single issue for single deprecated class', () => { + const styles = ` + .btn { + color: red; + } + `; + + cssAstVisitor = createClassDefinitionVisitor({ + deprecatedCssClasses: ['btn', 'btn-primary'], + componentName: 'DsButton', + docsUrl: 'docs.example.com/DsButton', + }); + + const ast = postcss.parse(styles, { from: 'styles.css' }); + visitEachChild(ast, cssAstVisitor); + + expect(cssAstVisitor.getIssues()).toHaveLength(1); + const message = cssAstVisitor.getIssues()[0].message; + expect(message).toContain('btn'); + expect(message).not.toContain(','); + expect(message).toContain('DsButton'); + expect(cssAstVisitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'styles.css', + position: expect.any(Object), + }), + }), + ); + }); + + it('should handle mixed deprecated and non-deprecated classes', () => { + const styles = ` + .btn.safe-class.btn-primary { + color: red; + } + `; + + cssAstVisitor = createClassDefinitionVisitor({ + deprecatedCssClasses: ['btn', 'btn-primary'], + componentName: 'DsButton', + docsUrl: 'docs.example.com/DsButton', + }); + + const ast = postcss.parse(styles, { from: 'styles.css' }); + visitEachChild(ast, cssAstVisitor); + + expect(cssAstVisitor.getIssues()).toHaveLength(1); + const message = cssAstVisitor.getIssues()[0].message; + expect(message).toContain('btn, btn-primary'); + expect(message).not.toContain('safe-class'); + expect(message).toContain('DsButton'); + expect(cssAstVisitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'styles.css', + position: expect.any(Object), + }), + }), + ); + }); }); }); diff --git a/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-usage.visitor.spec.ts b/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-usage.visitor.spec.ts index 36b0a42..11cf000 100644 --- a/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-usage.visitor.spec.ts +++ b/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-usage.visitor.spec.ts @@ -18,7 +18,7 @@ describe('ClassCollectorVisitor', () => { beforeEach(() => { visitor = new ClassUsageVisitor({ componentName: 'CounterComponent', - deprecatedCssClasses: ['count'], + deprecatedCssClasses: ['count', 'count-badge', 'count-item'], docsUrl: 'my.doc#CounterComponent', }); }); @@ -449,4 +449,113 @@ describe('ClassCollectorVisitor', () => { }), ]); }); + + // Deduplication tests + describe('deduplication', () => { + it('should deduplicate multiple deprecated classes in same class attribute', () => { + const template = `
Content
`; + + const ast = parseTemplate(template, 'template.html'); + ast.nodes.forEach((node) => node.visit(visitor)); + + expect(visitor.getIssues()).toHaveLength(1); + const message = visitor.getIssues()[0].message; + expect(message).toContain('count, count-badge'); + expect(message).toContain('CounterComponent'); + expect(visitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'template.html', + position: expect.any(Object), + }), + }), + ); + }); + + it('should deduplicate multiple deprecated classes in ngClass array', () => { + const template = `
Content
`; + + const ast = parseTemplate(template, 'template.html'); + ast.nodes.forEach((node) => node.visit(visitor)); + + expect(visitor.getIssues()).toHaveLength(1); + const message = visitor.getIssues()[0].message; + expect(message).toContain('count, count-badge'); + expect(message).toContain('CounterComponent'); + expect(visitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'template.html', + position: expect.any(Object), + }), + }), + ); + }); + + it('should deduplicate multiple deprecated classes in ngClass object', () => { + const template = `
Content
`; + + const ast = parseTemplate(template, 'template.html'); + ast.nodes.forEach((node) => node.visit(visitor)); + + expect(visitor.getIssues()).toHaveLength(1); + const message = visitor.getIssues()[0].message; + expect(message).toContain('count, count-badge'); + expect(message).toContain('CounterComponent'); + expect(visitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'template.html', + position: expect.any(Object), + }), + }), + ); + }); + + it('should still create single issue for single deprecated class', () => { + const template = `
Content
`; + + const ast = parseTemplate(template, 'template.html'); + ast.nodes.forEach((node) => node.visit(visitor)); + + expect(visitor.getIssues()).toHaveLength(1); + const message = visitor.getIssues()[0].message; + expect(message).toContain('count'); + expect(message).not.toContain(','); + expect(message).toContain('CounterComponent'); + expect(visitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'template.html', + position: expect.any(Object), + }), + }), + ); + }); + + it('should deduplicate three deprecated classes in same class attribute', () => { + const template = `
Content
`; + + const ast = parseTemplate(template, 'template.html'); + ast.nodes.forEach((node) => node.visit(visitor)); + + expect(visitor.getIssues()).toHaveLength(1); + const message = visitor.getIssues()[0].message; + expect(message).toContain('count, count-badge, count-item'); + expect(message).toContain('CounterComponent'); + expect(visitor.getIssues()[0]).toEqual( + expect.objectContaining({ + severity: 'error', + source: expect.objectContaining({ + file: 'template.html', + position: expect.any(Object), + }), + }), + ); + }); + }); }); diff --git a/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-usage.visitor.ts b/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-usage.visitor.ts index acc0024..15a6872 100644 --- a/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-usage.visitor.ts +++ b/packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-usage.visitor.ts @@ -97,22 +97,25 @@ export class ClassUsageVisitor const { deprecatedCssClasses, ...compRepl } = this.componentReplacement; if (attribute.name === 'class' && this.currentElement) { const classNames = parseClassNames(attribute.value); - for (const className of classNames) { - if (deprecatedCssClasses.includes(className)) { - const isInline = - attribute.sourceSpan.start.file.url.match(/\.ts$/) != null; - const startLine = isInline ? this.startLine : 0; - this.issues.push({ - severity: 'error', // @TODO if we consider transformations this needs to be dynamic - message: generateClassUsageMessage({ - ...compRepl, - element: this.currentElement, - className, - attribute: `${attribute.name}`, - }), - source: tmplAstElementToSource(this.currentElement, startLine), - }); - } + const deprecatedClassesFound = classNames.filter((cn) => + deprecatedCssClasses.includes(cn), + ); + + if (deprecatedClassesFound.length > 0) { + const isInline = + attribute.sourceSpan.start.file.url.match(/\.ts$/) != null; + const startLine = isInline ? this.startLine : 0; + + this.issues.push({ + severity: 'error', + message: generateClassUsageMessage({ + ...compRepl, + element: this.currentElement, + className: deprecatedClassesFound.join(', '), + attribute: `${attribute.name}`, + }), + source: tmplAstElementToSource(this.currentElement, startLine), + }); } } } @@ -149,22 +152,22 @@ export class ClassUsageVisitor deprecatedCssClasses, ); - // Create issues for found deprecated classes - foundClassNames.forEach((className) => { + // Create single issue for all found deprecated classes + if (foundClassNames.length > 0 && this.currentElement) { this.issues.push({ severity: 'error', // @TODO if we consider transformations this needs to be dynamic message: generateClassUsageMessage({ ...compRepl, - element: this.currentElement!, - className, + element: this.currentElement, + className: foundClassNames.join(', '), attribute: attribute.name === 'ngClass' ? `[${attribute.name}]` : attribute.name, }), - source: tmplAstElementToSource(this.currentElement!, this.startLine), + source: tmplAstElementToSource(this.currentElement, this.startLine), }); - }); + } } }