Skip to content

Commit d4b77fb

Browse files
authored
Merge pull request #18 from push-based/fix/diffing-fix
fix: make params optional
2 parents 1bbdb8e + ad47245 commit d4b77fb

File tree

7 files changed

+69
-46
lines changed

7 files changed

+69
-46
lines changed

docs/component-refactoring-flow.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,10 @@ At this point, all checklist items have been processed. You must review the refa
155155
### Tools used
156156

157157
- `build_component_contract` - Creates component contracts for safe refactoring
158-
- Parameters: `componentFile`, `dsComponentName` (set to "AUTO")
158+
- Parameters: `saveLocation`, `typescriptFile` (required), `templateFile` (optional), `styleFile` (optional), `dsComponentName` (optional, set to "AUTO")
159159
- Returns: contract path with component's public API, DOM structure, and styles
160160
- Purpose: Establish baseline for validation comparison
161+
- Note: Template and style files are optional for components with inline templates/styles
161162

162163
- `get-ds-component-data` - Retrieves Design System component information when needed
163164
- Parameters: `componentName`, `sections` (optional) - Array of sections to include: "implementation", "documentation", "stories", "all"
@@ -234,9 +235,10 @@ The rule implements a comprehensive validation process:
234235
### Tools used
235236

236237
- `build_component_contract` - Creates post-refactor component contract
237-
- Parameters: `saveLocation`, `templateFile`, `styleFile`, `typescriptFile`, `dsComponentName`
238+
- Parameters: `saveLocation`, `typescriptFile` (required), `templateFile` (optional), `styleFile` (optional), `dsComponentName` (optional)
238239
- Returns: updated contract path with refactored component state
239240
- Purpose: Capture final component state for comparison
241+
- Note: Template and style files are optional for components with inline templates/styles
240242

241243
- `diff_component_contract` - Compares baseline and updated contracts
242244
- Parameters: `saveLocation`, `contractBeforePath`, `contractAfterPath`, `dsComponentName`

docs/contracts.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,19 @@ Because contracts track every public-facing facet of a component, any refactor t
6565
Rules taking care about the contract building during the workflow, but if you need to build it "manually" say in the chat:
6666

6767
```
68-
build_component_contract(saveLocation, templateFile, styleFile, typescriptFile, dsComponentName)
68+
build_component_contract(saveLocation, typescriptFile, templateFile?, styleFile?, dsComponentName?)
6969
```
7070

7171
> Replace the parameters with:
7272
> - `saveLocation`: Path where to save the contract file (supports absolute and relative paths)
73-
> - `templateFile`: Path to the component template file (.html or .ts for inline)
74-
> - `styleFile`: Path to the component style file (.scss, .css, etc.)
75-
> - `typescriptFile`: Path to the TypeScript component file (.ts)
76-
> - `dsComponentName`: Optional design system component name (e.g., `DsBadge`)
73+
> - `typescriptFile`: Path to the TypeScript component file (.ts) — **Required**
74+
> - `templateFile`: *(Optional)* Path to the component template file (.html). Omit for inline templates
75+
> - `styleFile`: *(Optional)* Path to the component style file (.scss, .css, etc.). Omit for inline styles or components without styles
76+
> - `dsComponentName`: *(Optional)* Design system component name (e.g., `DsBadge`)
7777
>
7878
> The tool analyses the template, TypeScript, and styles, then saves the contract to your specified location.
79+
>
80+
> **Note**: Angular components can have inline templates and styles. If `templateFile` or `styleFile` are not provided, the tool will extract inline template/styles from the TypeScript file.
7981
8082
## When to Build a Contract
8183

docs/ds-refactoring-flow.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -479,9 +479,10 @@ At this point, initial refactoring is complete.
479479
### Tools used
480480

481481
- `build_component_contract` - Creates component contracts for safe refactoring
482-
- Parameters: `directory`, `templateFile`, `styleFile`, `typescriptFile`, `dsComponentName`
482+
- Parameters: `saveLocation`, `typescriptFile` (required), `templateFile` (optional), `styleFile` (optional), `dsComponentName` (optional)
483483
- Returns: contract with public API, DOM structure, styles, and metadata
484484
- Generates: JSON contract files with SHA-256 hashes for validation
485+
- Note: Template and style files are optional—extracts inline templates/styles from TypeScript when not provided
485486

486487
### Flow
487488

@@ -566,9 +567,10 @@ This is your last chance to make changes before opening the pull request.
566567
- Features: Automatic ESLint config resolution, comprehensive rule coverage
567568

568569
- `build_component_contract` - Creates contracts for refactored components
569-
- Parameters: `saveLocation`, `templateFile`, `styleFile`, `typescriptFile`, `dsComponentName`
570+
- Parameters: `saveLocation`, `typescriptFile` (required), `templateFile` (optional), `styleFile` (optional), `dsComponentName` (optional)
570571
- Returns: JSON contract with public API, DOM structure, and styles
571572
- Purpose: Capture post-refactoring component state
573+
- Note: Template and style files are optional for components with inline templates/styles
572574

573575
- `list_component_contracts` - Lists available component contracts
574576
- Parameters: `directory`

docs/tools.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,12 @@ This document provides comprehensive guidance for AI agents working with Angular
114114
**AI Usage**: Generate contracts before refactoring to track breaking changes
115115
**Key Parameters**:
116116
- `saveLocation`: Path where to save the contract file (supports absolute and relative paths)
117-
- `templateFile`: Template file name (.html or .ts for inline)
118-
- `styleFile`: Style file name (.scss, .css, etc.)
119-
- `typescriptFile`: TypeScript component file (.ts)
120-
- `dsComponentName`: Optional design system component name
117+
- `typescriptFile`: **Required** TypeScript component file (.ts)
118+
- `templateFile`: *Optional* Template file name (.html). Omit for inline templates
119+
- `styleFile`: *Optional* Style file name (.scss, .css, etc.). Omit for inline styles or no styles
120+
- `dsComponentName`: *Optional* design system component name
121121
**Output**: Component contract file with API surface
122-
**Best Practice**: Create contracts before major refactoring for comparison
122+
**Best Practice**: Create contracts before major refactoring for comparison. Template and style files are optional—the tool will extract inline templates/styles from the TypeScript file when not provided
123123

124124
#### `diff_component_contract`
125125
**Purpose**: Compares before/after contracts to identify breaking changes

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import { resolveCrossPlatformPath } from '../../shared/utils/cross-platform-path
1010

1111
interface BuildComponentContractOptions extends BaseHandlerOptions {
1212
saveLocation: string;
13-
templateFile: string;
14-
styleFile: string;
13+
templateFile?: string;
14+
styleFile?: string;
1515
typescriptFile: string;
1616
dsComponentName?: string;
1717
}
@@ -30,13 +30,20 @@ export const buildComponentContractHandler = createHandler<
3030
dsComponentName = '',
3131
} = params;
3232

33-
const effectiveTemplatePath = resolveCrossPlatformPath(cwd, templateFile);
34-
const effectiveScssPath = resolveCrossPlatformPath(cwd, styleFile);
3533
const effectiveTypescriptPath = resolveCrossPlatformPath(
3634
cwd,
3735
typescriptFile,
3836
);
3937

38+
// If templateFile or styleFile are not provided, use the TypeScript file path
39+
// This indicates inline template/styles
40+
const effectiveTemplatePath = templateFile
41+
? resolveCrossPlatformPath(cwd, templateFile)
42+
: effectiveTypescriptPath;
43+
const effectiveScssPath = styleFile
44+
? resolveCrossPlatformPath(cwd, styleFile)
45+
: effectiveTypescriptPath;
46+
4047
const contract = await buildComponentContract(
4148
effectiveTemplatePath,
4249
effectiveScssPath,

packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/models/schema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ export const buildComponentContractSchema: ToolSchemaOptions = {
1919
templateFile: {
2020
type: 'string',
2121
description:
22-
'Path to the component template file (.html) or TypeScript component file (.ts) for inline templates. Supports both absolute and relative paths.',
22+
'Path to the component template file (.html). Optional - if not provided or if the path matches typescriptFile, will extract inline template from the component. Supports both absolute and relative paths.',
2323
},
2424
styleFile: {
2525
type: 'string',
2626
description:
27-
'Path to the component style file (.scss, .sass, .less, .css). Supports both absolute and relative paths.',
27+
'Path to the component style file (.scss, .sass, .less, .css). Optional - if not provided or if the path matches typescriptFile, will extract inline styles from the component. Supports both absolute and relative paths.',
2828
},
2929
typescriptFile: {
3030
type: 'string',
@@ -37,7 +37,7 @@ export const buildComponentContractSchema: ToolSchemaOptions = {
3737
default: '',
3838
},
3939
},
40-
required: ['saveLocation', 'templateFile', 'styleFile', 'typescriptFile'],
40+
required: ['saveLocation', 'typescriptFile'],
4141
},
4242
annotations: {
4343
title: 'Build Component Contract',

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

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,52 @@ import type { ComponentContract } from '../../shared/models/types.js';
1111
import { relative } from 'node:path';
1212

1313
/**
14-
* Build a complete component contract from template and style files
14+
* Build a complete component contract from template and style files.
15+
* Template and style paths can be the same as TypeScript path for inline templates/styles.
1516
*/
1617
export async function buildComponentContract(
1718
templatePath: string,
1819
scssPath: string,
1920
cwd: string,
2021
typescriptPath: string,
2122
): Promise<ComponentContract> {
22-
const resolvedScssPath = resolveCrossPlatformPathAndValidate(cwd, scssPath);
23-
const resolvedTemplatePath = resolveCrossPlatformPathAndValidate(
24-
cwd,
25-
templatePath,
26-
);
2723
const componentTsPath = resolveCrossPlatformPathAndValidate(
2824
cwd,
2925
typescriptPath,
3026
);
3127

32-
const requiredPaths = [
33-
{ path: resolvedScssPath, name: 'Style file' },
34-
{ path: resolvedTemplatePath, name: 'Template file' },
35-
{ path: componentTsPath, name: 'Component TypeScript file' },
36-
];
28+
// Validate TypeScript file exists (required)
29+
if (!existsSync(componentTsPath)) {
30+
throw new Error(`Component TypeScript file not found: ${componentTsPath}`);
31+
}
32+
33+
// Resolve and validate template path
34+
// If it's the same as TS path, it means inline template
35+
const resolvedTemplatePath = resolveCrossPlatformPathAndValidate(
36+
cwd,
37+
templatePath,
38+
);
39+
const isInlineTemplate = resolvedTemplatePath === componentTsPath;
40+
41+
if (!isInlineTemplate && !existsSync(resolvedTemplatePath)) {
42+
throw new Error(`Template file not found: ${resolvedTemplatePath}`);
43+
}
44+
45+
// Resolve and validate style path
46+
// If it's the same as TS path, it means inline styles or no external styles
47+
const resolvedScssPath = resolveCrossPlatformPathAndValidate(cwd, scssPath);
48+
const isInlineOrNoStyles = resolvedScssPath === componentTsPath;
3749

38-
for (const { path, name } of requiredPaths) {
39-
if (!existsSync(path)) {
40-
throw new Error(`${name} not found: ${path}`);
41-
}
50+
if (!isInlineOrNoStyles && !existsSync(resolvedScssPath)) {
51+
throw new Error(`Style file not found: ${resolvedScssPath}`);
4252
}
4353

4454
const sources = {
4555
ts: readFileSync(componentTsPath, 'utf-8'),
46-
scss: readFileSync(resolvedScssPath, 'utf-8'),
47-
template: readFileSync(resolvedTemplatePath, 'utf-8'),
56+
scss: isInlineOrNoStyles ? '' : readFileSync(resolvedScssPath, 'utf-8'),
57+
template: isInlineTemplate
58+
? ''
59+
: readFileSync(resolvedTemplatePath, 'utf-8'),
4860
};
4961

5062
const [parsedComponent] = await parseComponents([componentTsPath]);
@@ -55,28 +67,26 @@ export async function buildComponentContract(
5567
const relativeTemplatePath = relative(cwd, resolvedTemplatePath);
5668
const relativeScssPath = relative(cwd, resolvedScssPath);
5769

58-
const meta = generateMeta(relativeTemplatePath, parsedComponent, false);
70+
const meta = generateMeta(
71+
relativeTemplatePath,
72+
parsedComponent,
73+
isInlineTemplate,
74+
);
5975
const publicApi = extractPublicApi(parsedComponent);
6076
const { slots, dom } = await extractSlotsAndDom(parsedComponent);
6177

62-
// -------------------------------------------------------------------------
63-
// Collect styles from both external SCSS and inline `styles` array
64-
// -------------------------------------------------------------------------
6578
const styleBuckets: import('../../shared/models/types.js').StyleDeclarations[] =
6679
[];
6780

68-
// External stylesheet – only if it is a real stylesheet different from the TS file
69-
if (resolvedScssPath !== componentTsPath) {
81+
if (!isInlineOrNoStyles) {
7082
const externalStyles = await collectStylesV2(resolvedScssPath, dom);
7183
externalStyles.sourceFile = relativeScssPath;
7284
styleBuckets.push(externalStyles);
7385
}
7486

75-
// Inline styles declared on the component decorator
7687
const inlineStyles = await collectInlineStyles(parsedComponent, dom);
7788
styleBuckets.push(inlineStyles);
7889

79-
// Merge collected buckets giving later buckets precedence on selector clashes
8090
const styles = styleBuckets.reduce<
8191
import('../../shared/models/types.js').StyleDeclarations
8292
>(

0 commit comments

Comments
 (0)