Skip to content

Commit 1fbdd5b

Browse files
author
Szymon.Poltorak
committed
refactor(): allow to pass component name without DS prefix
1 parent 619fa8a commit 1fbdd5b

File tree

10 files changed

+81
-20
lines changed

10 files changed

+81
-20
lines changed

packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/build-component-contract.tool.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from '../shared/utils/contract-file-ops.js';
1111
import { ContractResult } from './models/types.js';
1212
import { resolveCrossPlatformPath } from '../../shared/utils/cross-platform-path.js';
13+
import { validateAndNormalizeComponentName } from '../../shared/utils/component-validation.js';
1314

1415
interface BuildComponentContractOptions extends BaseHandlerOptions {
1516
directory: string;
@@ -30,8 +31,10 @@ export const buildComponentContractHandler = createHandler<
3031
templateFile,
3132
styleFile,
3233
typescriptFile,
33-
dsComponentName,
3434
} = params;
35+
36+
// Normalize component name at handler entry point
37+
const dsComponentName = validateAndNormalizeComponentName(params.dsComponentName);
3538

3639
const effectiveTemplatePath = resolveCrossPlatformPath(
3740
directory,

packages/angular-mcp-server/src/lib/tools/ds/component-contract/diff/diff-component-contract.tool.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import { diffComponentContractSchema } from './models/schema.js';
1010
import type { DomPathDictionary } from '../shared/models/types.js';
1111
import { loadContract } from '../shared/utils/contract-file-ops.js';
12-
import { componentNameToKebabCase } from '../../shared/utils/component-validation.js';
12+
import { componentNameToKebabCase, validateAndNormalizeComponentName } from '../../shared/utils/component-validation.js';
1313
import { basename } from 'node:path';
1414
import {
1515
consolidateAndPruneRemoveOperationsWithDeduplication,
@@ -35,6 +35,9 @@ export const diffComponentContractHandler = createHandler<
3535
>(
3636
diffComponentContractSchema.name,
3737
async (params, { workspaceRoot }) => {
38+
// Normalize component name at handler entry point
39+
const dsComponentName = validateAndNormalizeComponentName(params.dsComponentName);
40+
3841
const effectiveBeforePath = resolveCrossPlatformPath(
3942
params.directory,
4043
params.contractBeforePath,
@@ -57,7 +60,7 @@ export const diffComponentContractHandler = createHandler<
5760
const diffData = {
5861
before: effectiveBeforePath,
5962
after: effectiveAfterPath,
60-
dsComponentName: params.dsComponentName,
63+
dsComponentName: dsComponentName,
6164
timestamp: new Date().toISOString(),
6265
domPathDictionary: domPathDict.paths,
6366
changes: groupedChanges,
@@ -68,7 +71,7 @@ export const diffComponentContractHandler = createHandler<
6871
const normalizedDiffData = normalizePathsInObject(diffData, workspaceRoot);
6972

7073
// Create component-specific diffs directory
71-
const componentKebab = componentNameToKebabCase(params.dsComponentName);
74+
const componentKebab = componentNameToKebabCase(dsComponentName);
7275
const diffDir = resolveCrossPlatformPath(
7376
workspaceRoot,
7477
`.cursor/tmp/contracts/${componentKebab}/diffs`,

packages/angular-mcp-server/src/lib/tools/ds/component/get-deprecated-css-classes.tool.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
COMMON_ANNOTATIONS,
66
} from '../shared/models/schema-helpers.js';
77
import { getDeprecatedCssClasses } from './utils/deprecated-css-helpers.js';
8+
import { validateAndNormalizeComponentName } from '../shared/utils/component-validation.js';
89

910
interface DeprecatedCssClassesOptions {
1011
componentName: string;
@@ -28,8 +29,11 @@ export const getDeprecatedCssClassesHandler = createHandler<
2829
>(
2930
getDeprecatedCssClassesSchema.name,
3031
async ({ componentName }, { cwd, deprecatedCssClassesPath }) => {
32+
// Normalize component name at handler entry point
33+
const normalizedComponentName = validateAndNormalizeComponentName(componentName);
34+
3135
return getDeprecatedCssClasses(
32-
componentName,
36+
normalizedComponentName,
3337
deprecatedCssClassesPath,
3438
cwd,
3539
);

packages/angular-mcp-server/src/lib/tools/ds/component/get-ds-component-data.tool.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { COMMON_ANNOTATIONS } from '../shared/models/schema-helpers.js';
77
import { getComponentPathsInfo } from './utils/paths-helpers.js';
88
import { getComponentDocPathsForName } from './utils/doc-helpers.js';
99
import {
10-
validateComponentName,
10+
validateAndNormalizeComponentName,
1111
componentNameToKebabCase,
1212
} from '../shared/utils/component-validation.js';
1313
import { resolveCrossPlatformPath } from '../shared/utils/cross-platform-path.js';
@@ -115,11 +115,13 @@ export const getDsComponentDataHandler = createHandler<
115115
>(
116116
getDsComponentDataToolSchema.name,
117117
async (
118-
{ componentName, sections = ['all'] },
118+
params,
119119
{ cwd, uiRoot, storybookDocsRoot },
120120
) => {
121+
const { sections = ['all'] } = params;
122+
let { componentName } = params;
121123
try {
122-
validateComponentName(componentName);
124+
componentName = validateAndNormalizeComponentName(componentName);
123125

124126
const includeAll = sections.includes('all');
125127
const includeImplementation =

packages/angular-mcp-server/src/lib/tools/ds/project/get-project-dependencies.tool.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
COMMON_ANNOTATIONS,
99
} from '../shared/models/schema-helpers.js';
1010
import { analyzeProjectDependencies } from './utils/dependencies-helpers.js';
11-
import { validateComponentName } from '../shared/utils/component-validation.js';
11+
import { validateAndNormalizeComponentName } from '../shared/utils/component-validation.js';
1212

1313
interface ProjectDependenciesOptions extends BaseHandlerOptions {
1414
directory: string;
@@ -42,10 +42,11 @@ export const getProjectDependenciesHandler = createHandler<
4242
>(
4343
getProjectDependenciesSchema.name,
4444
async (params, { cwd, workspaceRoot, uiRoot }) => {
45-
const { directory, componentName } = params;
45+
const { directory } = params;
46+
let { componentName } = params;
4647

4748
if (componentName) {
48-
validateComponentName(componentName);
49+
componentName = validateAndNormalizeComponentName(componentName);
4950
}
5051

5152
return await analyzeProjectDependencies(

packages/angular-mcp-server/src/lib/tools/ds/project/report-deprecated-css.tool.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
BaseHandlerOptions,
66
RESULT_FORMATTERS,
77
} from '../shared/utils/handler-helpers.js';
8+
import { validateAndNormalizeComponentName } from '../shared/utils/component-validation.js';
89
import {
910
createDirectoryComponentSchema,
1011
COMMON_ANNOTATIONS,
@@ -40,7 +41,10 @@ export const reportDeprecatedCssHandler = createHandler<
4041
>(
4142
reportDeprecatedCssSchema.name,
4243
async (params, { cwd, deprecatedCssClassesPath }) => {
43-
const { directory, componentName } = params;
44+
const { directory } = params;
45+
46+
// Normalize component name at handler entry point
47+
const componentName = validateAndNormalizeComponentName(params.componentName);
4448

4549
const deprecated = getDeprecatedCssClasses(
4650
componentName,

packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-violations.tool.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
BaseHandlerOptions,
44
RESULT_FORMATTERS,
55
} from '../shared/utils/handler-helpers.js';
6+
import { validateAndNormalizeComponentName } from '../shared/utils/component-validation.js';
67
import {
78
createViolationReportingSchema,
89
COMMON_ANNOTATIONS,
@@ -36,7 +37,13 @@ export const reportViolationsHandler = createHandler<
3637
// Default to 'file' grouping if not specified
3738
const groupBy = params.groupBy || 'file';
3839

39-
const result = await analyzeViolationsBase<ViolationResult>(params);
40+
// Normalize component name at handler entry point
41+
const normalizedParams = {
42+
...params,
43+
componentName: validateAndNormalizeComponentName(params.componentName),
44+
};
45+
46+
const result = await analyzeViolationsBase<ViolationResult>(normalizedParams);
4047

4148
const formattedContent = formatViolations(result, params.directory, {
4249
groupBy,

packages/angular-mcp-server/src/lib/tools/ds/shared/utils/component-validation.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
import { COMPONENT_REGEXES } from './regex-helpers.js';
22

3+
/**
4+
* Normalizes a component name to ensure it has the "Ds" prefix
5+
* @param componentName The component name (e.g., "Button" or "DsButton")
6+
* @returns The normalized component name with "Ds" prefix (e.g., "DsButton")
7+
*/
8+
export function normalizeComponentName(componentName: string): string {
9+
return componentName.startsWith('Ds') ? componentName : `Ds${componentName}`;
10+
}
11+
312
/**
413
* Validates that a component name is a valid Design System component name
14+
* Accepts both formats: "Button" and "DsButton"
515
* @param componentName The component name to validate
616
* @throws Error if the component name is invalid
717
*/
@@ -14,22 +24,34 @@ export function validateComponentName(
1424
!COMPONENT_REGEXES.isValidDsComponent(componentName)
1525
) {
1626
throw new Error(
17-
'Invalid component name. Must be a string starting with "Ds".',
27+
'Invalid component name. Must be a valid PascalCase string (e.g., "Button" or "DsButton").',
1828
);
1929
}
2030
}
2131

32+
/**
33+
* Validates and normalizes a component name to ensure it has the "Ds" prefix
34+
* Accepts both formats: "Button" and "DsButton" but always returns "DsButton"
35+
* @param componentName The component name to validate and normalize
36+
* @returns The normalized component name with "Ds" prefix (e.g., "DsButton")
37+
* @throws Error if the component name is invalid
38+
*/
39+
export function validateAndNormalizeComponentName(componentName: unknown): string {
40+
validateComponentName(componentName);
41+
return normalizeComponentName(componentName);
42+
}
43+
2244
/**
2345
* Converts a Design System component name to kebab case
24-
* @param componentName The component name (e.g., "DsButton")
46+
* @param componentName The component name (e.g., "DsButton" or "Button")
2547
* @returns The kebab case name (e.g., "button")
2648
*/
2749
export function componentNameToKebabCase(componentName: string): string {
2850
const kebabCase = COMPONENT_REGEXES.toKebabCase(componentName);
2951

3052
if (!kebabCase?.trim()?.length) {
3153
throw new Error(
32-
'Invalid component name. Must be a string starting with "Ds".',
54+
'Invalid component name. Must be a valid PascalCase string (e.g., "Button" or "DsButton").',
3355
);
3456
}
3557

@@ -38,7 +60,7 @@ export function componentNameToKebabCase(componentName: string): string {
3860

3961
/**
4062
* Creates a tag name from a component name
41-
* @param componentName The component name (e.g., "DsButton")
63+
* @param componentName The component name (e.g., "DsButton" or "Button")
4264
* @returns The tag name (e.g., "ds-button")
4365
*/
4466
export function componentNameToTagName(componentName: string): string {

packages/angular-mcp-server/src/lib/tools/ds/shared/utils/handler-helpers.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
CallToolRequest,
33
CallToolResult,
44
} from '@modelcontextprotocol/sdk/types.js';
5-
import { validateComponentName } from './component-validation.js';
5+
import { validateComponentName, validateAndNormalizeComponentName } from './component-validation.js';
66
import { buildTextResponse, throwError } from './output.utils.js';
77
import * as process from 'node:process';
88

@@ -44,6 +44,21 @@ export function validateCommonInputs(params: BaseHandlerOptions): void {
4444
}
4545
}
4646

47+
/**
48+
* Validates and normalizes common input parameters, returning normalized params
49+
*/
50+
export function validateAndNormalizeCommonInputs<T extends BaseHandlerOptions>(params: T): T {
51+
if (params.componentName) {
52+
params.componentName = validateAndNormalizeComponentName(params.componentName);
53+
}
54+
55+
if (params.directory && typeof params.directory !== 'string') {
56+
throw new Error('Directory parameter is required and must be a string');
57+
}
58+
59+
return params;
60+
}
61+
4762
/**
4863
* Sets up common environment for handlers
4964
*/

packages/angular-mcp-server/src/lib/tools/ds/shared/utils/regex-helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ export const COMPONENT_REGEXES = {
6060
.toLowerCase(),
6161

6262
/**
63-
* Validates DS component name format
63+
* Validates DS component name format (accepts both "DsButton" and "Button" formats)
6464
*/
6565
isValidDsComponent: (name: string): boolean =>
66-
/^Ds[A-Z][a-zA-Z0-9]*$/.test(name),
66+
/^(Ds)?[A-Z][a-zA-Z0-9]*$/.test(name),
6767

6868
/**
6969
* Extracts component name from coverage titles

0 commit comments

Comments
 (0)