This is page 1 of 7. Use http://codebase.md/push-based/angular-toolkit-mcp?page={x} to view the full context. # Directory Structure ``` ├── .aiignore ├── .cursor │ ├── flows │ │ ├── component-refactoring │ │ │ ├── 01-review-component.mdc │ │ │ ├── 02-refactor-component.mdc │ │ │ ├── 03-validate-component.mdc │ │ │ └── angular-20.md │ │ ├── ds-refactoring-flow │ │ │ ├── 01-find-violations.mdc │ │ │ ├── 01b-find-all-violations.mdc │ │ │ ├── 02-plan-refactoring.mdc │ │ │ ├── 02b-plan-refactoring-for-all-violations.mdc │ │ │ ├── 03-fix-violations.mdc │ │ │ ├── 03-non-viable-cases.mdc │ │ │ ├── 04-validate-changes.mdc │ │ │ ├── 05-prepare-report.mdc │ │ │ └── clean-global-styles.mdc │ │ └── README.md │ └── mcp.json.example ├── .github │ └── workflows │ └── ci.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── assets │ ├── entain-logo.png │ └── entain.png ├── CONTRIBUTING.MD ├── docs │ ├── architecture-internal-design.md │ ├── component-refactoring-flow.md │ ├── contracts.md │ ├── ds-refactoring-flow.md │ ├── getting-started.md │ ├── README.md │ ├── tools.md │ └── writing-custom-tools.md ├── eslint.config.mjs ├── jest.config.ts ├── jest.preset.mjs ├── LICENSE ├── nx.json ├── package-lock.json ├── package.json ├── packages │ ├── .gitkeep │ ├── angular-mcp │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── assets │ │ │ │ └── .gitkeep │ │ │ └── main.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ ├── vitest.config.mts │ │ └── webpack.config.cjs │ ├── angular-mcp-server │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── angular-mcp-server.ts │ │ │ ├── prompts │ │ │ │ └── prompt-registry.ts │ │ │ ├── tools │ │ │ │ ├── ds │ │ │ │ │ ├── component │ │ │ │ │ │ ├── get-deprecated-css-classes.tool.ts │ │ │ │ │ │ ├── get-ds-component-data.tool.ts │ │ │ │ │ │ ├── list-ds-components.tool.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ ├── deprecated-css-helpers.ts │ │ │ │ │ │ ├── doc-helpers.ts │ │ │ │ │ │ ├── metadata-helpers.ts │ │ │ │ │ │ └── paths-helpers.ts │ │ │ │ │ ├── component-contract │ │ │ │ │ │ ├── builder │ │ │ │ │ │ │ ├── build-component-contract.tool.ts │ │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ │ │ ├── spec │ │ │ │ │ │ │ │ ├── css-match.spec.ts │ │ │ │ │ │ │ │ ├── dom-slots.extractor.spec.ts │ │ │ │ │ │ │ │ ├── element-helpers.spec.ts │ │ │ │ │ │ │ │ ├── inline-styles.collector.spec.ts │ │ │ │ │ │ │ │ ├── meta.generator.spec.ts │ │ │ │ │ │ │ │ ├── public-api.extractor.spec.ts │ │ │ │ │ │ │ │ ├── styles.collector.spec.ts │ │ │ │ │ │ │ │ └── typescript-analyzer.spec.ts │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ ├── build-contract.ts │ │ │ │ │ │ │ ├── css-match.ts │ │ │ │ │ │ │ ├── dom-slots.extractor.ts │ │ │ │ │ │ │ ├── element-helpers.ts │ │ │ │ │ │ │ ├── inline-styles.collector.ts │ │ │ │ │ │ │ ├── meta.generator.ts │ │ │ │ │ │ │ ├── public-api.extractor.ts │ │ │ │ │ │ │ ├── styles.collector.ts │ │ │ │ │ │ │ └── typescript-analyzer.ts │ │ │ │ │ │ ├── diff │ │ │ │ │ │ │ ├── diff-component-contract.tool.ts │ │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ │ └── schema.ts │ │ │ │ │ │ │ ├── spec │ │ │ │ │ │ │ │ ├── diff-utils.spec.ts │ │ │ │ │ │ │ │ └── dom-path-utils.spec.ts │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ ├── diff-utils.ts │ │ │ │ │ │ │ └── dom-path-utils.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── list │ │ │ │ │ │ │ ├── list-component-contracts.tool.ts │ │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ │ │ ├── spec │ │ │ │ │ │ │ │ └── contract-list-utils.spec.ts │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ └── contract-list-utils.ts │ │ │ │ │ │ └── shared │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ │ ├── spec │ │ │ │ │ │ │ └── contract-file-ops.spec.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ └── contract-file-ops.ts │ │ │ │ │ ├── component-usage-graph │ │ │ │ │ │ ├── build-component-usage-graph.tool.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ ├── config.ts │ │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ ├── angular-parser.ts │ │ │ │ │ │ ├── component-helpers.ts │ │ │ │ │ │ ├── component-usage-graph-builder.ts │ │ │ │ │ │ ├── path-resolver.ts │ │ │ │ │ │ └── unified-ast-analyzer.ts │ │ │ │ │ ├── ds.tools.ts │ │ │ │ │ ├── project │ │ │ │ │ │ ├── get-project-dependencies.tool.ts │ │ │ │ │ │ ├── report-deprecated-css.tool.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ ├── dependencies-helpers.ts │ │ │ │ │ │ └── styles-report-helpers.ts │ │ │ │ │ ├── report-violations │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ │ ├── report-all-violations.tool.ts │ │ │ │ │ │ └── report-violations.tool.ts │ │ │ │ │ ├── shared │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ ├── input-schemas.model.ts │ │ │ │ │ │ │ └── schema-helpers.ts │ │ │ │ │ │ ├── utils │ │ │ │ │ │ │ ├── component-validation.ts │ │ │ │ │ │ │ ├── cross-platform-path.ts │ │ │ │ │ │ │ ├── handler-helpers.ts │ │ │ │ │ │ │ ├── output.utils.ts │ │ │ │ │ │ │ └── regex-helpers.ts │ │ │ │ │ │ └── violation-analysis │ │ │ │ │ │ ├── base-analyzer.ts │ │ │ │ │ │ ├── coverage-analyzer.ts │ │ │ │ │ │ ├── formatters.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ └── tools.ts │ │ │ │ ├── schema.ts │ │ │ │ ├── tools.ts │ │ │ │ ├── types.ts │ │ │ │ └── utils.ts │ │ │ └── validation │ │ │ ├── angular-mcp-server-options.schema.ts │ │ │ ├── ds-components-file-loader.validation.ts │ │ │ ├── ds-components-file.validation.ts │ │ │ ├── ds-components.schema.ts │ │ │ └── file-existence.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.tsbuildinfo │ │ └── vitest.config.mts │ ├── minimal-repo │ │ └── packages │ │ ├── application │ │ │ ├── angular.json │ │ │ ├── code-pushup.config.ts │ │ │ ├── src │ │ │ │ ├── app │ │ │ │ │ ├── app.component.ts │ │ │ │ │ ├── app.config.ts │ │ │ │ │ ├── app.routes.ts │ │ │ │ │ ├── components │ │ │ │ │ │ ├── refactoring-tests │ │ │ │ │ │ │ ├── bad-alert-tooltip-input.component.ts │ │ │ │ │ │ │ ├── bad-alert.component.ts │ │ │ │ │ │ │ ├── bad-button-dropdown.component.ts │ │ │ │ │ │ │ ├── bad-document.component.ts │ │ │ │ │ │ │ ├── bad-global-this.component.ts │ │ │ │ │ │ │ ├── bad-mixed-external-assets.component.css │ │ │ │ │ │ │ ├── bad-mixed-external-assets.component.html │ │ │ │ │ │ │ ├── bad-mixed-external-assets.component.ts │ │ │ │ │ │ │ ├── bad-mixed-not-standalone.component.ts │ │ │ │ │ │ │ ├── bad-mixed.component.ts │ │ │ │ │ │ │ ├── bad-mixed.module.ts │ │ │ │ │ │ │ ├── bad-modal-progress.component.ts │ │ │ │ │ │ │ ├── bad-this-window-document.component.ts │ │ │ │ │ │ │ ├── bad-window.component.ts │ │ │ │ │ │ │ ├── complex-components │ │ │ │ │ │ │ │ ├── first-case │ │ │ │ │ │ │ │ │ ├── dashboard-demo.component.html │ │ │ │ │ │ │ │ │ ├── dashboard-demo.component.scss │ │ │ │ │ │ │ │ │ ├── dashboard-demo.component.ts │ │ │ │ │ │ │ │ │ ├── dashboard-header.component.html │ │ │ │ │ │ │ │ │ ├── dashboard-header.component.scss │ │ │ │ │ │ │ │ │ └── dashboard-header.component.ts │ │ │ │ │ │ │ │ ├── second-case │ │ │ │ │ │ │ │ │ ├── complex-badge-widget.component.scss │ │ │ │ │ │ │ │ │ ├── complex-badge-widget.component.ts │ │ │ │ │ │ │ │ │ └── complex-widget-demo.component.ts │ │ │ │ │ │ │ │ └── third-case │ │ │ │ │ │ │ │ ├── product-card.component.scss │ │ │ │ │ │ │ │ ├── product-card.component.ts │ │ │ │ │ │ │ │ └── product-showcase.component.ts │ │ │ │ │ │ │ ├── group-1 │ │ │ │ │ │ │ │ ├── bad-mixed-1.component.ts │ │ │ │ │ │ │ │ ├── bad-mixed-1.module.ts │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-1.component.css │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-1.component.html │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-1.component.ts │ │ │ │ │ │ │ │ └── bad-mixed-not-standalone-1.component.ts │ │ │ │ │ │ │ ├── group-2 │ │ │ │ │ │ │ │ ├── bad-mixed-2.component.ts │ │ │ │ │ │ │ │ ├── bad-mixed-2.module.ts │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-2.component.css │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-2.component.html │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-2.component.ts │ │ │ │ │ │ │ │ └── bad-mixed-not-standalone-2.component.ts │ │ │ │ │ │ │ ├── group-3 │ │ │ │ │ │ │ │ ├── bad-mixed-3.component.spec.ts │ │ │ │ │ │ │ │ ├── bad-mixed-3.component.ts │ │ │ │ │ │ │ │ ├── bad-mixed-3.module.ts │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-3.component.css │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-3.component.html │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-3.component.ts │ │ │ │ │ │ │ │ ├── bad-mixed-not-standalone-3.component.ts │ │ │ │ │ │ │ │ └── lazy-loader-3.component.ts │ │ │ │ │ │ │ └── group-4 │ │ │ │ │ │ │ ├── multi-violation-test.component.html │ │ │ │ │ │ │ ├── multi-violation-test.component.scss │ │ │ │ │ │ │ └── multi-violation-test.component.ts │ │ │ │ │ │ └── validation-tests │ │ │ │ │ │ ├── circular-dependency.component.ts │ │ │ │ │ │ ├── external-files-missing.component.ts │ │ │ │ │ │ ├── invalid-lifecycle.component.ts │ │ │ │ │ │ ├── invalid-pipe-usage.component.ts │ │ │ │ │ │ ├── invalid-template-syntax.component.ts │ │ │ │ │ │ ├── missing-imports.component.ts │ │ │ │ │ │ ├── missing-method.component.ts │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── standalone-module-conflict.component.ts │ │ │ │ │ │ ├── standalone-module-conflict.module.ts │ │ │ │ │ │ ├── template-reference-error.component.ts │ │ │ │ │ │ ├── type-mismatch.component.ts │ │ │ │ │ │ ├── valid.component.ts │ │ │ │ │ │ ├── wrong-decorator-usage.component.ts │ │ │ │ │ │ └── wrong-property-binding.component.ts │ │ │ │ │ └── styles │ │ │ │ │ ├── bad-global-styles.scss │ │ │ │ │ ├── base │ │ │ │ │ │ ├── _reset.scss │ │ │ │ │ │ └── base.scss │ │ │ │ │ ├── components │ │ │ │ │ │ └── components.scss │ │ │ │ │ ├── extended-deprecated-styles.scss │ │ │ │ │ ├── layout │ │ │ │ │ │ └── layout.scss │ │ │ │ │ ├── new-styles-1.scss │ │ │ │ │ ├── new-styles-10.scss │ │ │ │ │ ├── new-styles-2.scss │ │ │ │ │ ├── new-styles-3.scss │ │ │ │ │ ├── new-styles-4.scss │ │ │ │ │ ├── new-styles-5.scss │ │ │ │ │ ├── new-styles-6.scss │ │ │ │ │ ├── new-styles-7.scss │ │ │ │ │ ├── new-styles-8.scss │ │ │ │ │ ├── new-styles-9.scss │ │ │ │ │ ├── themes │ │ │ │ │ │ └── themes.scss │ │ │ │ │ └── utilities │ │ │ │ │ └── utilities.scss │ │ │ │ ├── index.html │ │ │ │ ├── main.ts │ │ │ │ └── styles.css │ │ │ ├── tsconfig.app.json │ │ │ ├── tsconfig.json │ │ │ └── tsconfig.spec.json │ │ └── design-system │ │ ├── component-options.mjs │ │ ├── storybook │ │ │ └── card │ │ │ └── card-tabs │ │ │ └── overview.mdx │ │ ├── storybook-host-app │ │ │ └── src │ │ │ └── components │ │ │ ├── badge │ │ │ │ ├── badge-tabs │ │ │ │ │ ├── api.mdx │ │ │ │ │ ├── examples.mdx │ │ │ │ │ └── overview.mdx │ │ │ │ ├── badge.component.mdx │ │ │ │ └── badge.component.stories.ts │ │ │ ├── modal │ │ │ │ ├── demo-cdk-dialog-cmp.component.ts │ │ │ │ ├── demo-modal-cmp.component.ts │ │ │ │ ├── modal-tabs │ │ │ │ │ ├── api.mdx │ │ │ │ │ ├── examples.mdx │ │ │ │ │ └── overview.mdx │ │ │ │ ├── modal.component.mdx │ │ │ │ └── modal.component.stories.ts │ │ │ └── segmented-control │ │ │ ├── segmented-control-tabs │ │ │ │ ├── api.mdx │ │ │ │ ├── examples.mdx │ │ │ │ └── overview.mdx │ │ │ ├── segmented-control.component.mdx │ │ │ └── segmented-control.component.stories.ts │ │ └── ui │ │ ├── badge │ │ │ ├── package.json │ │ │ ├── project.json │ │ │ └── src │ │ │ └── badge.component.ts │ │ ├── modal │ │ │ ├── package.json │ │ │ ├── project.json │ │ │ └── src │ │ │ ├── modal-content.component.ts │ │ │ ├── modal-header │ │ │ │ └── modal-header.component.ts │ │ │ ├── modal-header-drag │ │ │ │ └── modal-header-drag.component.ts │ │ │ └── modal.component.ts │ │ ├── rx-host-listener │ │ │ ├── package.json │ │ │ ├── project.json │ │ │ └── src │ │ │ └── rx-host-listener.ts │ │ └── segmented-control │ │ ├── package.json │ │ ├── project.json │ │ └── src │ │ ├── segmented-control.component.html │ │ ├── segmented-control.component.ts │ │ ├── segmented-control.token.ts │ │ └── segmented-option.component.ts │ └── shared │ ├── angular-ast-utils │ │ ├── .spec.swcrc │ │ ├── ai │ │ │ ├── API.md │ │ │ ├── EXAMPLES.md │ │ │ └── FUNCTIONS.md │ │ ├── docs │ │ │ └── angular-component-tree.md │ │ ├── eslint.config.mjs │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── constants.ts │ │ │ ├── decorator-config.visitor.inline-styles.spec.ts │ │ │ ├── decorator-config.visitor.spec.ts │ │ │ ├── decorator-config.visitor.ts │ │ │ ├── parse-component.ts │ │ │ ├── schema.ts │ │ │ ├── styles │ │ │ │ └── utils.ts │ │ │ ├── template │ │ │ │ ├── noop-tmpl-visitor.ts │ │ │ │ ├── template.walk.ts │ │ │ │ ├── utils.spec.ts │ │ │ │ ├── utils.ts │ │ │ │ └── utils.unit.test.ts │ │ │ ├── ts.walk.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ └── vitest.config.mts │ ├── DEPENDENCIES.md │ ├── ds-component-coverage │ │ ├── .spec.swcrc │ │ ├── ai │ │ │ ├── API.md │ │ │ ├── EXAMPLES.md │ │ │ └── FUNCTIONS.md │ │ ├── docs │ │ │ ├── examples │ │ │ │ ├── report.json │ │ │ │ └── report.md │ │ │ ├── images │ │ │ │ └── report-overview.png │ │ │ └── README.md │ │ ├── jest.config.ts │ │ ├── mocks │ │ │ └── fixtures │ │ │ └── e2e │ │ │ ├── asset-location │ │ │ │ ├── code-pushup.config.ts │ │ │ │ ├── inl-styl-inl-tmpl │ │ │ │ │ └── inl-styl-inl-tmpl.component.ts │ │ │ │ ├── inl-styl-url-tmpl │ │ │ │ │ ├── inl-styl-url-tmpl.component.html │ │ │ │ │ └── inl-styl-url-tmpl.component.ts │ │ │ │ ├── multi-url-styl-inl-tmpl │ │ │ │ │ ├── multi-url-styl-inl-tmpl-1.component.css │ │ │ │ │ ├── multi-url-styl-inl-tmpl-2.component.css │ │ │ │ │ └── multi-url-styl-inl-tmpl.component.ts │ │ │ │ ├── url-styl-inl-tmpl │ │ │ │ │ ├── url-styl-inl-tmpl.component.css │ │ │ │ │ └── url-styl-inl-tmpl.component.ts │ │ │ │ ├── url-styl-single-inl-tmpl │ │ │ │ │ ├── url-styl-inl-tmpl.component.ts │ │ │ │ │ └── url-styl-single-inl-tmpl.component.css │ │ │ │ └── url-styl-url-tmpl │ │ │ │ ├── inl-styl-url-tmpl.component.css │ │ │ │ ├── inl-styl-url-tmpl.component.html │ │ │ │ └── inl-styl-url-tmpl.component.ts │ │ │ ├── demo │ │ │ │ ├── code-pushup.config.ts │ │ │ │ ├── prompt.md │ │ │ │ └── src │ │ │ │ ├── bad-button-dropdown.component.ts │ │ │ │ ├── bad-modal-progress.component.ts │ │ │ │ ├── mixed-external-assets.component.css │ │ │ │ ├── mixed-external-assets.component.html │ │ │ │ ├── mixed-external-assets.component.ts │ │ │ │ └── sub-folder-1 │ │ │ │ ├── bad-alert.component.ts │ │ │ │ ├── button.component.ts │ │ │ │ └── sub-folder-2 │ │ │ │ ├── bad-alert-tooltip-input.component.ts │ │ │ │ └── bad-mixed.component.ts │ │ │ ├── line-number │ │ │ │ ├── code-pushup.config.ts │ │ │ │ ├── inl-styl-single.component.ts │ │ │ │ ├── inl-styl-span.component.ts │ │ │ │ ├── inl-tmpl-single.component.ts │ │ │ │ ├── inl-tmpl-span.component.ts │ │ │ │ ├── url-style │ │ │ │ │ ├── url-styl-single.component.css │ │ │ │ │ ├── url-styl-single.component.ts │ │ │ │ │ ├── url-styl-span.component.css │ │ │ │ │ └── url-styl-span.component.ts │ │ │ │ └── url-tmpl │ │ │ │ ├── url-tmpl-single.component.html │ │ │ │ ├── url-tmpl-single.component.ts │ │ │ │ ├── url-tmpl-span.component.html │ │ │ │ └── url-tmpl-span.component.ts │ │ │ ├── style-format │ │ │ │ ├── code-pushup.config.ts │ │ │ │ ├── inl-css.component.ts │ │ │ │ ├── inl-scss.component.ts │ │ │ │ ├── styles.css │ │ │ │ ├── styles.scss │ │ │ │ ├── url-css.component.ts │ │ │ │ └── url-scss.component.ts │ │ │ └── template-syntax │ │ │ ├── class-attribute.component.ts │ │ │ ├── class-binding.component.ts │ │ │ ├── code-pushup.config.ts │ │ │ └── ng-class-binding.component.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── core.config.ts │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── constants.ts │ │ │ ├── ds-component-coverage.plugin.ts │ │ │ ├── runner │ │ │ │ ├── audits │ │ │ │ │ └── ds-coverage │ │ │ │ │ ├── class-definition.utils.ts │ │ │ │ │ ├── class-definition.visitor.ts │ │ │ │ │ ├── class-definition.visitor.unit.test.ts │ │ │ │ │ ├── class-usage.utils.ts │ │ │ │ │ ├── class-usage.visitor.spec.ts │ │ │ │ │ ├── class-usage.visitor.ts │ │ │ │ │ ├── constants.ts │ │ │ │ │ ├── ds-coverage.audit.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── create-runner.ts │ │ │ │ └── schema.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ └── vitest.config.mts │ ├── LLMS.md │ ├── models │ │ ├── ai │ │ │ ├── API.md │ │ │ ├── EXAMPLES.md │ │ │ └── FUNCTIONS.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── cli.ts │ │ │ ├── diagnostics.ts │ │ │ └── mcp.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json │ ├── styles-ast-utils │ │ ├── .spec.swcrc │ │ ├── ai │ │ │ ├── API.md │ │ │ ├── EXAMPLES.md │ │ │ └── FUNCTIONS.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── postcss-safe-parser.d.ts │ │ │ ├── styles-ast-utils.spec.ts │ │ │ ├── styles-ast-utils.ts │ │ │ ├── stylesheet.parse.ts │ │ │ ├── stylesheet.parse.unit.test.ts │ │ │ ├── stylesheet.visitor.ts │ │ │ ├── stylesheet.walk.ts │ │ │ ├── types.ts │ │ │ ├── utils.ts │ │ │ └── utils.unit.test.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ └── vitest.config.mts │ ├── typescript-ast-utils │ │ ├── .spec.swcrc │ │ ├── ai │ │ │ ├── API.md │ │ │ ├── EXAMPLES.md │ │ │ └── FUNCTIONS.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── constants.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ └── vitest.config.mts │ └── utils │ ├── .spec.swcrc │ ├── ai │ │ ├── API.md │ │ ├── EXAMPLES.md │ │ └── FUNCTIONS.md │ ├── package.json │ ├── README.md │ ├── src │ │ ├── index.ts │ │ └── lib │ │ ├── execute-process.ts │ │ ├── execute-process.unit.test.ts │ │ ├── file │ │ │ ├── default-export-loader.spec.ts │ │ │ ├── default-export-loader.ts │ │ │ ├── file.resolver.ts │ │ │ └── find-in-file.ts │ │ ├── format-command-log.integration.test.ts │ │ ├── format-command-log.ts │ │ ├── logging.ts │ │ └── utils.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ ├── vite.config.ts │ └── vitest.config.mts ├── README.md ├── testing │ ├── setup │ │ ├── eslint.config.mjs │ │ ├── eslint.next.config.mjs │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.d.ts │ │ │ ├── index.mjs │ │ │ └── memfs.constants.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ ├── vitest.config.mts │ │ └── vitest.integration.config.mts │ ├── utils │ │ ├── eslint.config.mjs │ │ ├── eslint.next.config.mjs │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── constants.ts │ │ │ ├── e2e-setup.ts │ │ │ ├── execute-process-helper.mock.ts │ │ │ ├── execute-process.mock.mjs │ │ │ ├── os-agnostic-paths.ts │ │ │ ├── os-agnostic-paths.unit.test.ts │ │ │ ├── source-file-from.code.ts │ │ │ └── string.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ ├── vite.config.ts │ │ ├── vitest.config.mts │ │ └── vitest.integration.config.mts │ └── vitest-setup │ ├── eslint.config.mjs │ ├── eslint.next.config.mjs │ ├── package.json │ ├── README.md │ ├── src │ │ ├── index.ts │ │ └── lib │ │ ├── configuration.ts │ │ └── fs-memfs.setup-file.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ ├── vite.config.ts │ ├── vitest.config.mts │ └── vitest.integration.config.mts ├── tools │ ├── nx-advanced-profile.bin.js │ ├── nx-advanced-profile.js │ ├── nx-advanced-profile.postinstall.js │ └── perf_hooks.patch.js ├── tsconfig.base.json ├── tsconfig.json └── vitest.workspace.ts ``` # Files -------------------------------------------------------------------------------- /packages/.gitkeep: -------------------------------------------------------------------------------- ``` ``` -------------------------------------------------------------------------------- /packages/angular-mcp/src/assets/.gitkeep: -------------------------------------------------------------------------------- ``` ``` -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- ``` 22.14.0 ``` -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- ``` { "singleQuote": true } ``` -------------------------------------------------------------------------------- /.aiignore: -------------------------------------------------------------------------------- ``` # An .aiignore file follows the same syntax as a .gitignore file. # .gitignore documentation: https://git-scm.com/docs/gitignore # you can ignore files .DS_Store *.log *.tmp # or folders dist/ build/ out/ ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/.spec.swcrc: -------------------------------------------------------------------------------- ``` { "jsc": { "target": "es2017", "parser": { "syntax": "typescript", "decorators": true, "dynamicImport": true }, "transform": { "decoratorMetadata": true, "legacyDecorator": true }, "keepClassNames": true, "externalHelpers": true, "loose": true }, "module": { "type": "es6" }, "sourceMaps": true, "exclude": [] } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/.spec.swcrc: -------------------------------------------------------------------------------- ``` { "jsc": { "target": "es2017", "parser": { "syntax": "typescript", "decorators": true, "dynamicImport": true }, "transform": { "decoratorMetadata": true, "legacyDecorator": true }, "keepClassNames": true, "externalHelpers": true, "loose": true }, "module": { "type": "es6" }, "sourceMaps": true, "exclude": [] } ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/.spec.swcrc: -------------------------------------------------------------------------------- ``` { "jsc": { "target": "es2017", "parser": { "syntax": "typescript", "decorators": true, "dynamicImport": true }, "transform": { "decoratorMetadata": true, "legacyDecorator": true }, "keepClassNames": true, "externalHelpers": true, "loose": true }, "module": { "type": "es6" }, "sourceMaps": true, "exclude": [] } ``` -------------------------------------------------------------------------------- /packages/shared/typescript-ast-utils/.spec.swcrc: -------------------------------------------------------------------------------- ``` { "jsc": { "target": "es2017", "parser": { "syntax": "typescript", "decorators": true, "dynamicImport": true }, "transform": { "decoratorMetadata": true, "legacyDecorator": true }, "keepClassNames": true, "externalHelpers": true, "loose": true }, "module": { "type": "es6" }, "sourceMaps": true, "exclude": [] } ``` -------------------------------------------------------------------------------- /packages/shared/utils/.spec.swcrc: -------------------------------------------------------------------------------- ``` { "jsc": { "target": "es2017", "parser": { "syntax": "typescript", "decorators": true, "dynamicImport": true }, "transform": { "decoratorMetadata": true, "legacyDecorator": true }, "keepClassNames": true, "externalHelpers": true, "loose": true }, "module": { "type": "es6" }, "sourceMaps": true, "exclude": [] } ``` -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- ``` # Add files here to ignore them from prettier formatting /dist **/dist /coverage /.nx/cache /.nx/workspace-data packages/minimal-repo/packages/application *.md *.mdx **/*.md **/*.mdx # Additional file types that should be ignored *.mdc **/*.mdc .aiignore .gitignore .nvmrc .prettierignore *.gitkeep **/*.gitkeep *.swcrc **/*.swcrc *.tsbuildinfo **/*.tsbuildinfo *.png *.jpg *.jpeg *.gif *.svg **/*.png **/*.jpg **/*.jpeg **/*.gif **/*.svg ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` # See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files. # compiled output dist tmp out-tsc # dependencies node_modules # IDEs and editors /.idea .project .classpath .c9/ *.launch .settings/ *.sublime-workspace # IDE - VSCode .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json # misc /.sass-cache /connect.lock /coverage /libpeerconnection.log npm-debug.log yarn-error.log testem.log /typings # System Files .DS_Store Thumbs.db .nx/cache .nx/workspace-data test-output vite.config.*.timestamp* vitest.config.*.timestamp* .code-pushup .cursor/rules/nx-rules.mdc .cursor/mcp.json .github/instructions/nx.instructions.md ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/docs/README.md: -------------------------------------------------------------------------------- ```markdown # Design System Component Coverage This plugin checks if the design system is used correctly across a project. ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/README.md: -------------------------------------------------------------------------------- ```markdown # angular-mcp-server This library was generated with [Nx](https://nx.dev). ## Building Run `nx build angular-mcp-server` to build the library. ``` -------------------------------------------------------------------------------- /packages/shared/utils/README.md: -------------------------------------------------------------------------------- ```markdown # utils This library was generated with [Nx](https://nx.dev). ## Building Run `nx build utils` to build the library. ## Running unit tests Run `nx test utils` to execute the unit tests via [Jest](https://jestjs.io). ``` -------------------------------------------------------------------------------- /packages/shared/models/README.md: -------------------------------------------------------------------------------- ```markdown # models This library was generated with [Nx](https://nx.dev). ## Building Run `nx build models` to build the library. ## Running unit tests Run `nx test models` to execute the unit tests via [Jest](https://jestjs.io). ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/README.md: -------------------------------------------------------------------------------- ```markdown # styles-ast-utils This library was generated with [Nx](https://nx.dev). ## Building Run `nx build styles-ast-utils` to build the library. ## Running unit tests Run `nx test styles-ast-utils` to execute the unit tests via [Jest](https://jestjs.io). ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/README.md: -------------------------------------------------------------------------------- ```markdown # angular-ast-utils This library was generated with [Nx](https://nx.dev). ## Building Run `nx build angular-ast-utils` to build the library. ## Running unit tests Run `nx test angular-ast-utils` to execute the unit tests via [Jest](https://jestjs.io). ``` -------------------------------------------------------------------------------- /packages/shared/typescript-ast-utils/README.md: -------------------------------------------------------------------------------- ```markdown # typescript-ast-utils This library was generated with [Nx](https://nx.dev). ## Building Run `nx build typescript-ast-utils` to build the library. ## Running unit tests Run `nx test typescript-ast-utils` to execute the unit tests via [Jest](https://jestjs.io). ``` -------------------------------------------------------------------------------- /testing/utils/README.md: -------------------------------------------------------------------------------- ```markdown # testing-utils This library provides a set of helper functions for testing. The many content divides into: - constants - mock helper - utils for OS dependent APIs ## Building Run `nx build testing-utils` to build the library. ## Running unit tests Run `nx test testing-utils` to execute the unit tests via [Vitest](https://vitest.dev/). ``` -------------------------------------------------------------------------------- /testing/setup/README.md: -------------------------------------------------------------------------------- ```markdown # testing-vitest-setup This library provides a set of setup scripts for testing. As this package is never directly referenced, but only it's files it does not maintain a `index.ts` entry point. ## The many content divides into: - **Setup files** - obey to the naming pattern `<purpose>-setup-file.ts` and should be used in a `vite.config.ts` testing config under the `setupFiles` property. - **Global setup files** - obey to the naming pattern `<purpose>-global-setup-file.ts` and should be used in a `vite.config.ts` testing config under the `globalSetup` property. ``` -------------------------------------------------------------------------------- /testing/vitest-setup/README.md: -------------------------------------------------------------------------------- ```markdown # testing-vitest-setup This library provides a set of setup scripts for testing. As this package is never directly referenced, but only it's files it does not maintain a `index.ts` entry point. ## The many content divides into: - **Setup files** - obey to the naming pattern `<purpose>-setup-file.ts` and should be used in a `vite.config.ts` testing config under the `setupFiles` property. - **Global setup files** - obey to the naming pattern `<purpose>-global-setup-file.ts` and should be used in a `vite.config.ts` testing config under the `globalSetup` property. ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/README.md: -------------------------------------------------------------------------------- ```markdown # Validation Test Components This folder contains various Angular components designed to test different types of build and validation errors. ## Error Cases - `missing-method.component.ts` - Template calls non-existing method `nonExistentMethod()` - `wrong-property-binding.component.ts` - Template binds to non-existing property `nonExistentProperty` - `invalid-template-syntax.component.ts` - Invalid template syntax with unclosed tags and malformed bindings - `missing-imports.component.ts` - Uses Angular features without proper imports (FormsModule, CommonModule) - `circular-dependency.component.ts` - Creates circular dependency by importing itself - `invalid-lifecycle.component.ts` - Implements lifecycle interfaces incorrectly - `wrong-decorator-usage.component.ts` - Uses @Input/@Output decorators incorrectly - `template-reference-error.component.ts` - Template references undefined variables and methods - `invalid-pipe-usage.component.ts` - Uses non-existent pipes and incorrect pipe syntax - `type-mismatch.component.ts` - Type mismatches between template and component properties - `standalone-module-conflict.component.ts` - Standalone component incorrectly declared in module - `external-files-missing.component.ts` - References non-existent external template and style files ## Valid Component - `valid.component.ts` - Fully valid standalone component for comparison ``` -------------------------------------------------------------------------------- /packages/angular-mcp/README.md: -------------------------------------------------------------------------------- ```markdown # Angular Toolkit MCP A Model Context Protocol (MCP) server that provides Angular project analysis and refactoring capabilities. This server enables LLMs to analyze Angular projects for component usage patterns, dependency analysis, code quality issues, and provides automated refactoring assistance. ## Installation No installation required! Run directly with npx: ```bash npx @push-based/angular-toolkit-mcp@latest --workspaceRoot=/path/to/workspace --ds.uiRoot=packages/ui ``` ## Configuration Add the server to your MCP client configuration (e.g., Claude Desktop, Cursor, Copilot, Windsurf): ```json { "mcpServers": { "angular-toolkit": { "command": "npx", "args": [ "@push-based/angular-toolkit-mcp@latest", "--workspaceRoot=/absolute/path/to/your/angular/workspace", "--ds.uiRoot=packages/ui", "--ds.storybookDocsRoot=storybook/docs", "--ds.deprecatedCssClassesPath=design-system/component-options.mjs" ] } } } ``` ### Configuration Parameters #### Required Parameters | Parameter | Type | Description | Example | |-----------|------|-------------|---------| | `workspaceRoot` | Absolute path | Root directory of your Angular workspace | `/Users/dev/my-angular-app` | | `ds.uiRoot` | Relative path | Directory containing UI components | `packages/ui` | #### Optional Parameters | Parameter | Type | Description | Example | |-----------|------|-------------|---------| | `ds.storybookDocsRoot` | Relative path | Root directory containing Storybook documentation | `storybook/docs` | | `ds.deprecatedCssClassesPath` | Relative path | JavaScript file mapping deprecated CSS classes | `design-system/component-options.mjs` | ## Key Features - **Component Analysis**: Detect deprecated CSS classes and component usage violations - **Safe Refactoring**: Generate contracts for safe component refactoring with breaking change detection - **Dependency Mapping**: Map component dependencies across modules, templates, and styles - **ESLint Integration**: Lint Angular files with automatic ESLint configuration discovery - **Project Analysis**: Analyze buildable/publishable libraries and validate import paths - **Component Documentation**: Retrieve component data and documentation, list available components ## Available Tools ### Component Analysis - `report-violations` - Report deprecated CSS usage in a directory - `report-deprecated-css` - Report deprecated CSS classes found in styling files - `get-deprecated-css-classes` - List deprecated CSS classes for a component - `list-ds-components` - List all available Design System components - `get-ds-component-data` - Get component data including implementation and documentation - `build-component-usage-graph` - Map component imports across the project ### Component Contracts - `build_component_contract` - Generate a static surface contract for a component - `diff_component_contract` - Compare before/after contracts for breaking changes - `list_component_contracts` - List all available component contracts ### Project Analysis - `get-project-dependencies` - Analyze project dependencies and library configuration - `lint-changes` - Lint changed Angular files using ESLint rules ## Requirements - Node.js version 18 or higher ## Documentation For comprehensive documentation, guides, and workflows, see the [full documentation](https://github.com/push-based/angular-toolkit-mcp). ## License MIT License - see the [LICENSE](https://github.com/push-based/angular-toolkit-mcp/blob/main/LICENSE) file for details. --- <div align="center"> <p><strong>Sponsored by Entain</strong></p> </div> ``` -------------------------------------------------------------------------------- /.cursor/flows/README.md: -------------------------------------------------------------------------------- ```markdown # Cursor Flows This directory contains AI-assisted workflow templates (flows) for Angular component refactoring and design system migration tasks. These flows are designed to work with Cursor IDE's rule system to provide structured, step-by-step guidance for complex refactoring operations. ## What are Flows? Flows are collections of rule files (.mdc) that guide the AI through multi-step processes. Each flow contains: - **Rule files (.mdc)**: Step-by-step instructions for the AI - **Documentation**: Supporting materials and best practices - **Templates**: Reusable patterns and examples ## Available Flows ### 1. Component Refactoring Flow **Location:** `component-refactoring/` **Purpose:** Improve individual Angular components according to modern best practices **Files:** - `01-review-component.mdc` - Analyze component and create improvement plan - `02-refactor-component.mdc` - Execute refactoring checklist - `03-validate-component.mdc` - Verify improvements through contract comparison - `angular-20.md` - Angular best practices reference **Use Case:** When you need to modernize a single component's code quality, performance, or maintainability. ### 2. Design System Refactoring Flow **Location:** `ds-refactoring-flow/` **Purpose:** Migrate components from deprecated design system patterns to modern alternatives **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:** - **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 1. Copy the desired flow's `.mdc` files to your `.cursor/rules/` directory 2. The rules will be automatically available in Cursor 3. Follow the flow documentation for step-by-step guidance ## Prerequisites Before using any flow, ensure you have: - **Cursor IDE** with MCP (Model Context Protocol) server connected - **Git branch** for your refactoring work - **Component files** accessible in your workspace - **Angular project** with proper TypeScript configuration ## Flow Process Overview Most flows follow a similar pattern: 1. **Analysis** - Review current state and identify issues 2. **Planning** - Create actionable improvement checklist 3. **Execution** - Implement changes systematically 4. **Validation** - Verify improvements and quality gates 5. **Reporting** - Document changes and results ## Quality Gates Flows include human review checkpoints to ensure: - ✅ Analysis accuracy - ✅ Refactoring plan approval - ✅ Code quality validation - ✅ Final acceptance ## Documentation For detailed information about each flow, see: - [Component Refactoring Flow](../../docs/component-refactoring-flow.md) - [Architecture & Design](../../docs/architecture-internal-design.md) - [Contracts Documentation](../../docs/contracts.md) ``` -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- ```markdown # Angular MCP Toolkit Documentation Welcome to the Angular MCP (Model Context Protocol) Toolkit documentation! This toolkit provides tools for Angular development, focusing on design system migration, component refactoring, and code analysis. ## 📚 Documentation Overview This documentation is organized into several sections to help you get started quickly and understand the toolkit's capabilities: ### 🚀 Getting Started - **[Getting Started Guide](getting-started.md)** - Quick 5-minute setup guide to install and configure the Angular MCP server ### 🔧 Using the Tools - **[Tools Reference](tools.md)** - Comprehensive guide to all available AI tools for design system migration and analysis - **[Component Refactoring Flow](component-refactoring-flow.md)** - 3-step AI-assisted process for improving individual Angular components - **[Design System Refactoring Flow](ds-refactoring-flow.md)** - 5-step automated workflow for migrating legacy components to design system ### 📋 Advanced Features - **[Component Contracts](contracts.md)** - System for breaking change detection and refactoring validation - **[Writing Custom Tools](writing-custom-tools.md)** - Guide for developers to create new MCP tools ### 🏗️ Architecture & Development - **[Architecture & Internal Design](architecture-internal-design.md)** - Technical details for backend developers and tool authors ## 🎯 Quick Navigation ### For New Users 1. Start with **[Getting Started](getting-started.md)** to set up the toolkit 2. Review **[Tools Reference](tools.md)** to understand available capabilities 3. Try the **[Component Refactoring Flow](component-refactoring-flow.md)** for your first refactoring ### For Design System Migration 1. Use **[Design System Refactoring Flow](ds-refactoring-flow.md)** for systematic legacy component migration 2. When components are identified as non-viable during planning, use the **Non-Viable Cases handling** instead of normal fix violations step (requires developer review and approval) 3. Learn about **[Component Contracts](contracts.md)** for validation and safety 4. Reference **[Tools Reference](tools.md)** for specific tool details ### For Developers & Contributors 1. Read **[Architecture & Internal Design](architecture-internal-design.md)** to understand the system 2. Follow **[Writing Custom Tools](writing-custom-tools.md)** to extend functionality 3. Use **[Component Contracts](contracts.md)** for testing and validation ## 🔄 Workflow Integration The toolkit supports two main workflows: ### Individual Component Improvement ```mermaid graph LR A[Review Component] --> B[Refactor Component] --> C[Validate Component] B --> D[Human Review & Approval] C --> E[Quality Gate] ``` **Guide:** [Component Refactoring Flow](component-refactoring-flow.md) ### Design System Migration ```mermaid graph LR A[Find Violations] --> B[Plan Refactoring] --> B1{Viable?} B --> F[Human Review] B1 -->|Yes| C[Fix Violations] --> D[Validate Changes] --> E[Prepare Report] B1 -->|No + Dev Approval| B2[Non-Viable Cases<br/>Handling] C --> G[Quality Gate] D --> H[Final Validation] ``` **Guide:** [Design System Refactoring Flow](ds-refactoring-flow.md) ## 🛠️ Key Features - **AI-Powered Analysis** - Automated code analysis and refactoring suggestions - **Design System Integration** - Tools specifically designed for Angular design system adoption - **Contract-Based Validation** - Ensure refactoring safety with before/after comparisons - **Extensible Architecture** - Add custom tools and analysis capabilities - **Quality Gates** - Built-in checkpoints for human review and approval ## 📖 Document Relationships ``` Getting Started ──┐ ├─→ Tools Reference ──┐ │ ├─→ Component Refactoring Flow │ └─→ Design System Refactoring Flow │ │ └─→ Component Contracts ←──────────────┘ │ Writing Custom Tools ←──────────────────┘ │ Architecture & Internal Design ←────────┘ ``` ## 🤝 Contributing If you're interested in contributing to the toolkit: 1. Read the **[Architecture & Internal Design](architecture-internal-design.md)** for system understanding 2. Follow **[Writing Custom Tools](writing-custom-tools.md)** for adding new functionality 3. Use **[Component Contracts](contracts.md)** for testing your changes ## 📝 Need Help? - **Setup Issues**: Check [Getting Started](getting-started.md) - **Tool Usage**: Reference [Tools Reference](tools.md) - **Workflow Questions**: Review the appropriate flow documentation - **Technical Details**: Consult [Architecture & Internal Design](architecture-internal-design.md) --- <div align="center"> <p><strong>Sponsored by</strong></p> <img src="../assets/entain.png" alt="Entain" width="150"> </div> *This toolkit is designed to work with Cursor IDE, but should also be compatible with other AI-powered development environments like Windsurf or GitHub Copilot.* ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown # Angular Toolkit MCP [](https://www.npmjs.com/package/@push-based/angular-toolkit-mcp) A Model Context Protocol (MCP) server that provides Angular project analysis and refactoring capabilities. This server enables LLMs to analyze Angular projects for component usage patterns, dependency analysis, code quality issues, and provides automated refactoring assistance. ## Key Features - **Component Analysis**: Detect deprecated CSS classes and component usage violations - **Safe Refactoring**: Generate contracts for safe component refactoring with breaking change detection - **Dependency Mapping**: Map component dependencies across modules, templates, and styles - **ESLint Integration**: Lint Angular files with automatic ESLint configuration discovery - **Project Analysis**: Analyze buildable/publishable libraries and validate import paths - **Component Documentation**: Retrieve component data and documentation, list available components ## Use Cases - Angular project migration and refactoring - Component usage analysis and dependency mapping - Deprecated CSS class detection and reporting - Component contract generation for safe refactoring - Breaking change detection during component updates - Code quality analysis and improvement ## Quick Start Install and run via npx (no manual build required): ```json { "mcpServers": { "angular-toolkit": { "command": "npx", "args": [ "@push-based/angular-toolkit-mcp@latest", "--workspaceRoot=/absolute/path/to/your/angular/workspace", "--ds.uiRoot=packages/ui" ] } } } ``` **Required Node.js version:** 18 or higher ## Configuration ### Prerequisites - Node.js (version 18 or higher) with ESM support ### Installation & Setup #### For Users Simply use npx as shown in the Quick Start section above. No installation or build required. #### For Contributors (Local Development) 1. Clone the repository 2. Install dependencies and build the MCP ```bash npm install npx nx build angular-mcp ``` 3. Locate the built server After building, the server will be available at `packages/angular-mcp/dist/main.js` ### MCP Configuration Add the server to your MCP client configuration (e.g., Claude Desktop, Cursor, Copilot, Windsurf or other MCP-compatible clients): #### For Users (npx - Recommended) ```json { "mcpServers": { "angular-toolkit": { "command": "npx", "args": [ "@push-based/angular-toolkit-mcp@latest", "--workspaceRoot=/absolute/path/to/your/angular/workspace", "--ds.uiRoot=relative/path/to/ui/components", "--ds.storybookDocsRoot=relative/path/to/storybook/docs", "--ds.deprecatedCssClassesPath=relative/path/to/component-options.mjs" ] } } } ``` #### For Contributors (Local Development) When developing locally, point to the built server: ```json { "mcpServers": { "angular-toolkit-mcp": { "command": "node", "args": [ "/absolute/path/to/angular-toolkit-mcp/packages/angular-mcp/dist/main.js", "--workspaceRoot=/absolute/path/to/your/angular/workspace", "--ds.uiRoot=relative/path/to/ui/components", "--ds.storybookDocsRoot=relative/path/to/storybook/docs", "--ds.deprecatedCssClassesPath=relative/path/to/component-options.mjs" ] } } } ``` > Note: `ds.storybookDocsRoot` and `ds.deprecatedCssClassesPath` are optional. The server will start without them. Tools that require these paths will return a clear error prompting you to provide the missing parameter. > **Note**: The example file contains configuration for `ESLint` official MCP which is required for the toolkit to work properly. ### Configuration Parameters #### Required Parameters | Parameter | Type | Description | Example | |-----------|------|-------------|---------| | `workspaceRoot` | Absolute path | Root directory of your Angular workspace | `/Users/dev/my-angular-app` | | `ds.uiRoot` | Relative path | Directory containing UI components | `packages/ui` | #### Optional Parameters | Parameter | Type | Description | Example | |-----------|------|-------------|---------| | `ds.storybookDocsRoot` | Relative path | Root directory containing Storybook documentation used by documentation-related tools | `storybook/docs` | | `ds.deprecatedCssClassesPath` | Relative path | JavaScript file mapping deprecated CSS classes used by violation and deprecated CSS tools | `design-system/component-options.mjs` | When optional parameters are omitted: - `ds.storybookDocsRoot`: Tools will skip Storybook documentation lookups (e.g., `get-ds-component-data` will still return implementation/import data but may have no docs files). - `ds.deprecatedCssClassesPath`: Tools that require the mapping will fail fast with a clear error. Affected tools include: `get-deprecated-css-classes`, `report-deprecated-css`, `report-all-violations`, and `report-violations`. #### Deprecated CSS Classes File Format The `component-options.mjs` file should export an array of component configurations: ```javascript const dsComponents = [ { componentName: 'DsButton', deprecatedCssClasses: ['btn', 'btn-primary', 'legacy-button'] }, { componentName: 'DsModal', deprecatedCssClasses: ['modal', 'old-modal'] } ]; export default dsComponents; ``` ### Example Project Structure ``` my-angular-workspace/ ├── packages/ │ ├── ui/ # ds.uiRoot │ │ ├── button/ │ │ ├── modal/ │ │ └── ... │ └── design-system/ │ └── component-options.mjs # ds.deprecatedCssClassesPath ├── storybook/ │ └── docs/ # ds.storybookDocsRoot └── apps/ └── my-app/ ``` ### Troubleshooting - **Server not starting**: Ensure all paths are correct and the server is built - **Permission errors**: Check that the Node.js process has read access to all specified directories - **Component not found**: Verify that component names in `component-options.mjs` match your actual component class names - **Path resolution issues**: Use absolute paths for `workspaceRoot` and relative paths (from workspace root) for other parameters ## Available Tools ### Component Analysis - **`report-violations`**: Report deprecated CSS usage in a directory with configurable grouping format - **`report-deprecated-css`**: Report deprecated CSS classes found in styling files - **`get-deprecated-css-classes`**: List deprecated CSS classes for a component ### Component Analysis - **`list-ds-components`**: List all available Design System components in the project with their file paths and metadata - **`get-ds-component-data`**: Return data for a component including implementation files, documentation files, and import path - **`build-component-usage-graph`**: Maps where given Angular components are imported (modules, specs, templates, styles) so refactors touch every file ### Tool behavior with optional parameters The following tools work without optional params: - `get-project-dependencies` - `build-component-usage-graph` - `get-ds-component-data` (documentation section is empty if `ds.storybookDocsRoot` is not set) - Component contract tools: - `build_component_contract` - `diff_component_contract` - `list_component_contracts` The following tools require optional params to work: - Requires `ds.deprecatedCssClassesPath`: - `get-deprecated-css-classes` - `report-deprecated-css` - `report-all-violations` - `report-violations` - Requires `ds.storybookDocsRoot` for docs lookup (skipped otherwise): - `get-ds-component-data` (docs files discovery only) ### Component Contracts - **`build_component_contract`**: Generate a static surface contract for a component's template and SCSS - **`diff_component_contract`**: Compare before/after contracts for parity and surface breaking changes - **`list_component_contracts`**: List all available component contracts in the .cursor/tmp/contracts directory ### Project Analysis - **`get-project-dependencies`**: Analyze project dependencies and detect if library is buildable/publishable. Checks for peer dependencies and validates import paths for components - **`lint-changes`**: Lint changed Angular files using ESLint rules. Automatically resolves ESLint config by walking up the directory tree ## Component Contract Workflow The component contract system provides a safety net for Angular component refactoring: 1. **Generate Initial Contract**: Create a baseline contract before refactoring 2. **Perform Refactoring**: Make your component changes 3. **Generate New Contract**: Create a contract after refactoring 4. **Compare Contracts**: Detect breaking changes and API differences ```bash # 1. Generate initial contract User: build_component_contract for my-component # 2. Make component changes... # 3. Generate new contract and compare User: build_component_contract for my-component User: diff_component_contract old-contract.json new-contract.json ``` ## Angular Project Integration This MCP server is designed for Angular project analysis and refactoring. It helps with: - **Migration Planning**: Identify all usages of deprecated components/classes - **Refactoring Safety**: Generate contracts to ensure no breaking changes - **Documentation Access**: Quick access to component documentation and examples - **Code Quality**: Analyze and improve code quality across your Angular project ## Documentation For comprehensive documentation, guides, and workflows, see our [Documentation Hub](docs/README.md). ## Contributing Please read our [Contributing Guide](CONTRIBUTING.md). ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. --- <div align="center"> <p><strong>Sponsored by</strong></p> <img src="assets/entain.png" alt="Entain" width="150"> </div> ``` -------------------------------------------------------------------------------- /CONTRIBUTING.MD: -------------------------------------------------------------------------------- ```markdown # Contributing to Angular Toolkit MCP Thank you for your interest in contributing to the Angular Toolkit MCP! This document provides guidelines and information for contributors. ## 📋 Table of Contents - [Getting Started](#getting-started) - [Development Setup](#development-setup) - [Project Structure](#project-structure) - [Development Workflow](#development-workflow) - [Testing](#testing) - [Code Quality](#code-quality) - [Submitting Changes](#submitting-changes) - [Documentation](#documentation) - [Debugging](#debugging) - [Release Process](#release-process) ## 🚀 Getting Started ### Prerequisites - **Node.js** (version 18 or higher) - **npm** (comes with Node.js) - **Git** for version control ### Fork and Clone 1. Fork the repository on GitHub 2. Clone your fork locally ## 🛠️ Development Setup ### Initial Setup 1. **Install dependencies:** ```bash npm install ``` 2. **Build the project:** ```bash npx nx build angular-toolkit-mcp ``` ### Nx Workspace Commands This project uses Nx for monorepo management. Key commands: ```bash # Build all projects npx nx run-many --target=build --all # Build specific project npx nx build angular-mcp-server # Run tests for all projects npx nx run-many --target=test --all # Lint all projects npx nx run-many --target=lint --all # Check project graph npx nx graph ``` ## 🏗️ Project Structure This is an Nx monorepo with the following structure: ``` ├── packages/ │ ├── angular-mcp/ # Main MCP client application │ ├── angular-mcp-server/ # Core MCP server library │ ├── minimal-repo/ # Test fixtures and examples │ └── shared/ # Shared libraries │ ├── angular-ast-utils/ # Angular AST parsing │ ├── ds-component-coverage/ # Design system analysis │ ├── models/ # Core types and schemas │ ├── styles-ast-utils/ # CSS/SCSS AST parsing │ ├── typescript-ast-utils/ # TypeScript AST utilities │ └── utils/ # General utilities ├── testing/ # Testing utilities and setup ├── docs/ # Documentation └── tools/ # Build and development tools ``` ### Key Projects - **`angular-mcp`**: Main executable MCP client - **`angular-mcp-server`**: Core server logic and MCP tools - **Shared libraries**: Reusable utilities for AST parsing, file operations, and Angular analysis ## 🔄 Development Workflow ### 1. Create a Feature Branch ```bash git checkout -b feature/your-feature-name ``` ### 2. Make Changes - Follow the existing code style and patterns - Add tests for new functionality - Update documentation as needed ### 3. Build and Test ```bash # Build affected projects npx nx affected --target=build # Run tests npx nx affected --target=test # Lint code npx nx affected --target=lint ``` ### 4. Commit Changes Follow conventional commit format: ```bash git commit -m "feat: add new MCP tool for component analysis" git commit -m "fix: resolve dependency resolution issue" git commit -m "docs: update API documentation" ``` ## 🧪 Testing ### Running Tests ```bash # Run all tests npx nx run-many --target=test --all # Run tests for specific project npx nx test angular-mcp-server # Run tests with coverage npx nx test angular-mcp-server --coverage ``` ### Writing Tests - Use Vitest for unit testing - Follow the existing test patterns - Mock external dependencies appropriately - Test both success and error scenarios ## 📏 Code Quality ### ESLint Configuration The project uses ESLint with TypeScript and Nx-specific rules: ```bash # Lint all files npx nx run-many --target=lint --all # Lint specific project npx nx lint angular-mcp-server # Auto-fix linting issues npx nx lint angular-mcp-server --fix ``` ### Code Style Guidelines - Use TypeScript strict mode - Follow functional programming patterns where possible - Use descriptive variable and function names - Add JSDoc comments for public APIs - Prefer composition over inheritance ### Pre-commit Checks Before committing, ensure: - [ ] All tests pass - [ ] No linting errors - [ ] Code builds successfully - [ ] Documentation is updated ## 📝 Submitting Changes ### Pull Request Process 1. **Push your branch:** ```bash git push origin feature/your-feature-name ``` 2. **Create a Pull Request:** - Use a descriptive title - Include a detailed description of changes - Reference any related issues - Add screenshots for UI changes 3. **PR Requirements:** - All CI checks must pass - Code review approval required - Documentation updates included - Tests added for new functionality ## 🐛 Debugging ### Debug Server Start the MCP server in debug mode: ```bash npx nx run angular-toolkit-mcp:debug ``` This starts the server with the MCP Inspector for debugging. ### Debugging Tips - Use the MCP Inspector for real-time debugging - Check server logs for detailed error information - Use `console.log` or debugger statements in development - Test with the minimal-repo examples ## 📦 Release Process ### Publishing to npm The Angular Toolkit MCP is published to npm as `@push-based/angular-toolkit-mcp`. Only maintainers with appropriate permissions can publish new versions. ### Release Steps 1. **Update Version** Update the version in `packages/angular-mcp/package.json` following semantic versioning: - **Patch** (0.1.0 → 0.1.1): Bug fixes - **Minor** (0.1.0 → 0.2.0): New features (backwards compatible) - **Major** (0.1.0 → 1.0.0): Breaking changes 2. **Build the Package** ```bash npx nx build angular-toolkit-mcp ``` 3. **Test the Package** ```bash cd packages/angular-mcp/dist npm pack # Test the generated .tgz file node main.js --help ``` 4. **Authenticate with npm** ```bash npm login ``` Ensure you have access to the `@push-based` scope. 5. **Publish to npm** ```bash npm run publish:mcp ``` Or manually: ```bash npx nx build angular-toolkit-mcp cd packages/angular-mcp/dist npm publish ``` 6. **Verify Publication** ```bash npm view @push-based/angular-toolkit-mcp npx @push-based/angular-toolkit-mcp@latest --help ``` 7. **Tag the Release** ```bash git tag v0.1.0 git push origin v0.1.0 ``` 8. **Update Documentation** - Update CHANGELOG.md with release notes - Update any version references in documentation ### Pre-release Checklist Before publishing a new version: - [ ] All tests pass (`npx nx run-many --target=test --all`) - [ ] No linting errors (`npx nx run-many --target=lint --all`) - [ ] Build succeeds (`npx nx build angular-toolkit-mcp`) - [ ] Version number updated in package.json - [ ] CHANGELOG.md updated with changes - [ ] Documentation updated as needed - [ ] Local npm pack test successful ## 📄 License By contributing, you agree that your contributions will be licensed under the MIT License. ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/storybook/card/card-tabs/overview.mdx: -------------------------------------------------------------------------------- ```markdown ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/multi-url-styl-inl-tmpl/multi-url-styl-inl-tmpl-1.component.css: -------------------------------------------------------------------------------- ```css .btn { color: red; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/url-styl-inl-tmpl/url-styl-inl-tmpl.component.css: -------------------------------------------------------------------------------- ```css .btn { color: red; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/url-styl-single-inl-tmpl/url-styl-single-inl-tmpl.component.css: -------------------------------------------------------------------------------- ```css .btn { color: red; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/url-styl-url-tmpl/inl-styl-url-tmpl.component.css: -------------------------------------------------------------------------------- ```css .btn { color: red; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/url-style/url-styl-single.component.css: -------------------------------------------------------------------------------- ```css .btn { color: red; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/url-style/url-styl-span.component.css: -------------------------------------------------------------------------------- ```css .btn { color: blue; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/multi-url-styl-inl-tmpl/multi-url-styl-inl-tmpl-2.component.css: -------------------------------------------------------------------------------- ```css .btn-primary { color: blue; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/url-tmpl/url-tmpl-single.component.html: -------------------------------------------------------------------------------- ```html <button class="btn">click</button> ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/url-tmpl/url-tmpl-span.component.html: -------------------------------------------------------------------------------- ```html <button class="btn">click</button> ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/url-styl-url-tmpl/inl-styl-url-tmpl.component.html: -------------------------------------------------------------------------------- ```html <button class="btn">Click me</button> ``` -------------------------------------------------------------------------------- /testing/utils/src/lib/constants.ts: -------------------------------------------------------------------------------- ```typescript export const MEMFS_VOLUME = '/memfs'; ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/inl-styl-url-tmpl/inl-styl-url-tmpl.component.html: -------------------------------------------------------------------------------- ```html <button [class]="'btn'">Click me</button> ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/index.ts: -------------------------------------------------------------------------------- ```typescript export * from './lib/angular-mcp-server.js'; ``` -------------------------------------------------------------------------------- /packages/shared/typescript-ast-utils/src/lib/constants.ts: -------------------------------------------------------------------------------- ```typescript export const QUOTE_REGEX = /^['"`]+|['"`]+$/g; ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/style-format/styles.css: -------------------------------------------------------------------------------- ```css .btn { color: red; } .btn span { color: darkred; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/style-format/styles.scss: -------------------------------------------------------------------------------- ```scss .btn { color: red; span { color: darkred; } } ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/src/lib/constants.ts: -------------------------------------------------------------------------------- ```typescript export const ANGULAR_COMPONENT_DECORATOR = '@Component'; ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/src/lib/constants.ts: -------------------------------------------------------------------------------- ```typescript export const ANGULAR_DS_USAGE_PLUGIN_SLUG = 'ds-component-coverage'; ``` -------------------------------------------------------------------------------- /jest.preset.mjs: -------------------------------------------------------------------------------- ``` import nxPreset from '@nx/jest/preset'; export default { ...nxPreset }; ``` -------------------------------------------------------------------------------- /packages/shared/utils/src/lib/logging.ts: -------------------------------------------------------------------------------- ```typescript export const isVerbose = () => process.env['NG_MCP_VERBOSE'] === 'true'; ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/src/lib/styles-ast-utils.ts: -------------------------------------------------------------------------------- ```typescript export function stylesAstUtils(): string { return 'styles-ast-utils'; } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/app.routes.ts: -------------------------------------------------------------------------------- ```typescript import { Routes } from '@angular/router'; export const routes: Routes = []; ``` -------------------------------------------------------------------------------- /testing/setup/src/index.d.ts: -------------------------------------------------------------------------------- ```typescript export const EXCLUDED_FILES_TEST: string[]; export const MEMFS_VOLUME: string; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/styles.css: -------------------------------------------------------------------------------- ```css /* You can add global styles to this file, and also import other style files */ ``` -------------------------------------------------------------------------------- /vitest.workspace.ts: -------------------------------------------------------------------------------- ```typescript export default [ '**/vite.config.{mjs,js,ts,mts}', '**/vitest.config.{mjs,js,ts,mts}', ]; ``` -------------------------------------------------------------------------------- /testing/vitest-setup/src/index.ts: -------------------------------------------------------------------------------- ```typescript export { createSharedVitestConfig, type SharedVitestConfigOptions, } from './lib/configuration'; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/modal/package.json: -------------------------------------------------------------------------------- ```json { "name": "@frontend/ui/modal", "version": "0.0.0", "type": "module", "peerDependencies": {} } ``` -------------------------------------------------------------------------------- /testing/setup/src/memfs.constants.ts: -------------------------------------------------------------------------------- ```typescript /** * Constants for memfs (in-memory file system) testing setup */ export const MEMFS_VOLUME = '/memfs'; ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/src/lib/schema.ts: -------------------------------------------------------------------------------- ```typescript import { z } from 'zod'; export const AngularUnitSchema = z.enum([ 'component', 'pipe', 'directive', 'service', ]); ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/src/lib/postcss-safe-parser.d.ts: -------------------------------------------------------------------------------- ```typescript declare module 'postcss-safe-parser' { import { Parser } from 'postcss'; const safeParser: Parser; export default safeParser; } ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/tools.ts: -------------------------------------------------------------------------------- ```typescript import { ToolsConfig } from '@push-based/models'; import { dsTools } from './ds/tools.js'; export const TOOLS: ToolsConfig[] = [...dsTools] as const; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/badge/package.json: -------------------------------------------------------------------------------- ```json { "name": "@frontend/ui/badge", "version": "0.0.0", "type": "module", "peerDependencies": { "@angular/cdk": "19.2.9", "@angular/core": "19.2.7" } } ``` -------------------------------------------------------------------------------- /packages/shared/typescript-ast-utils/src/index.ts: -------------------------------------------------------------------------------- ```typescript export * from './lib/constants.js'; export * from './lib/utils.js'; export { removeQuotes } from './lib/utils.js'; export { QUOTE_REGEX } from './lib/constants.js'; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/rx-host-listener/package.json: -------------------------------------------------------------------------------- ```json { "name": "@frontend/ui/rx-host-listener", "version": "0.0.0", "type": "module", "peerDependencies": { "@angular/core": "19.2.7", "rxjs": "^7.8.1" } } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/url-tmpl/url-tmpl-span.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ templateUrl: './url-tmpl-span.component.html', selector: 'url-tmpl-span', }) export class UrlTmplSpanComponent {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-window.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-window', template: `<h3>Window usage</h3>`, }) export class BadWindowComponent { w = window; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/inl-tmpl-span.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ template: `<button class="btn">click</button>`, selector: 'inl-tmpl-span', }) export class InlStylSingleComponent {} ``` -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- ```typescript import type { Config } from 'jest'; import { getJestProjectsAsync } from '@nx/jest'; export default async (): Promise<Config> => ({ projects: await getJestProjectsAsync(), }); ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/inl-tmpl-single.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ template: '<button class="btn">click</button>', selector: 'inl-tmpl-single', }) export class InlTmplSingleComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/url-tmpl/url-tmpl-single.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ templateUrl: './url-tmpl-single.component.html', selector: 'url-tmpl-single', }) export class UrlTmplSingleComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/template-syntax/class-attribute.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'class-attribute', template: `<button class="btn">Click me</button>`, }) export class ClassAttributeComponent {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-document.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-document', template: `<h3>Document usage</h3>`, }) export class BadDocumentComponent { d = document; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/template-syntax/class-binding.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'class-binding', template: `<button [class.btn]>Click me</button>`, }) export class ClassAttributeUsageComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/url-style/url-styl-span.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ styleUrls: ['url-styl-span.component.css'], selector: 'url-styl-span', template: '', }) export class UrlStylSpanComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/src/lib/styles-ast-utils.spec.ts: -------------------------------------------------------------------------------- ```typescript import { stylesAstUtils } from './styles-ast-utils.js'; describe('stylesAstUtils', () => { it('should work', () => { expect(stylesAstUtils()).toEqual('styles-ast-utils'); }); }); ``` -------------------------------------------------------------------------------- /packages/shared/models/src/lib/diagnostics.ts: -------------------------------------------------------------------------------- ```typescript import { Issue } from '@code-pushup/models'; export interface DiagnosticsAware { // @TODO use Set<Issue & { code: number }> getIssues(): (Issue & { code?: number })[]; clear(): void; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/url-style/url-styl-single.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ styleUrls: ['url-styl-single.component.css'], selector: 'url-styl-single', template: '', }) export class UrlStylSingleComponent {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-global-this.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-global-this', template: `<h3>Global this usage</h3>`, }) export class BadGlobalThisComponent { g = globalThis; } ``` -------------------------------------------------------------------------------- /packages/shared/models/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "files": [], "include": [], "references": [ { "path": "./tsconfig.lib.json" } ], "nx": { "addTypecheckTarget": false } } ``` -------------------------------------------------------------------------------- /testing/setup/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.base.json", "files": [], "include": [], "references": [ { "path": "./tsconfig.lib.json" }, { "path": "./tsconfig.spec.json" } ] } ``` -------------------------------------------------------------------------------- /testing/utils/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.base.json", "files": [], "include": [], "references": [ { "path": "./tsconfig.lib.json" }, { "path": "./tsconfig.spec.json" } ] } ``` -------------------------------------------------------------------------------- /testing/vitest-setup/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.base.json", "files": [], "include": [], "references": [ { "path": "./tsconfig.lib.json" }, { "path": "./tsconfig.spec.json" } ] } ``` -------------------------------------------------------------------------------- /packages/angular-mcp/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.base.json", "files": [], "include": [], "references": [ { "path": "../angular-mcp-server" }, { "path": "./tsconfig.app.json" } ] } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/style-format/url-css.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'url-css', template: ` <button class="btn">Click me</button>`, styleUrls: ['./styles.css'], }) export class UrlCssComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/style-format/url-scss.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'url-scss', template: ` <button class="btn">Click me</button>`, styleUrls: ['./styles.scss'], }) export class UrlScssComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/src/index.ts: -------------------------------------------------------------------------------- ```typescript export * from './lib/styles-ast-utils.js'; export * from './lib/stylesheet.walk.js'; export * from './lib/utils.js'; export * from './lib/stylesheet.visitor.js'; export * from './lib/stylesheet.parse.js'; ``` -------------------------------------------------------------------------------- /testing/setup/src/index.mjs: -------------------------------------------------------------------------------- ``` export const EXCLUDED_FILES_TEST = [ 'mocks/**', '**/types.ts', '**/*.d.ts', '__snapshots__/**', '**/__tests__/**', '**/code-pushup.config.ts', '**/eslint*.config.js', '**/vitest*.config.mts', ]; ``` -------------------------------------------------------------------------------- /testing/utils/src/lib/string.ts: -------------------------------------------------------------------------------- ```typescript // removes all color codes from the output for snapshot readability export function removeColorCodes(stdout: string) { // eslint-disable-next-line no-control-regex return stdout.replace(/\u001B\[\d+m/g, ''); } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/inl-styl-span.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ styles: [ ` .btn { color: red; } `, ], selector: 'inl-styl-span', template: '', }) export class InlStylSingleComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/inl-styl-single.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ styles: [ ` .btn { color: red; } `, ], selector: 'inl-styl-single', template: '', }) export class InlStylSingleComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/models/src/index.ts: -------------------------------------------------------------------------------- ```typescript export type { CliArgsObject, ArgumentValue } from './lib/cli.js'; export type { ToolSchemaOptions, ToolsConfig, ToolHandlerContentResult, } from './lib/mcp.js'; export { type DiagnosticsAware } from './lib/diagnostics.js'; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/badge/project.json: -------------------------------------------------------------------------------- ```json { "name": "design-system-badge-ui", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "projectType": "library", "sourceRoot": "packages/design-system/ui/badge/src", "tags": ["type:ui", "scope:shared"] } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/modal/project.json: -------------------------------------------------------------------------------- ```json { "name": "design-system-modal-ui", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "projectType": "library", "sourceRoot": "packages/design-system/ui/modal/src", "tags": ["type:ui", "scope:shared"] } ``` -------------------------------------------------------------------------------- /packages/shared/models/src/lib/cli.ts: -------------------------------------------------------------------------------- ```typescript export type ArgumentValue = number | string | boolean | string[]; export type CliArgsObject<T extends object = Record<string, ArgumentValue>> = T extends never ? Record<string, ArgumentValue | undefined> | { _: string } : T; ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/url-styl-url-tmpl/inl-styl-url-tmpl.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'inl-styl-url-tmpl', templateUrl: './inl-styl-url-tmpl.component.html', styleUrls: ['inl-styl-url-tmpl.component.css'], }) export class InlStylUrlTmplComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/url-styl-inl-tmpl/url-styl-inl-tmpl.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'url-styl-inl-tmpl', template: `<button class="btn">Click me</button>`, styleUrls: ['./url-styl-inl-tmpl.component.css'], }) export class UrlStylInlTmplComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/style-format/inl-css.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'inl-css', template: ` <button class="btn">Click me</button>`, styles: [ ` .btn { color: red; } `, ], }) export class InlCssComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/url-styl-single-inl-tmpl/url-styl-inl-tmpl.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'url-styl-inl-tmpl', template: `<button class="btn">Click me</button>`, styleUrl: './url-styl-single-inl-tmpl.component.css', }) export class UrlStylInlTmplComponent {} ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/component-contract/list/models/types.ts: -------------------------------------------------------------------------------- ```typescript /** * List Module Types * Types specific to listing and managing contract files */ export interface ContractFileInfo { fileName: string; filePath: string; componentName: string; timestamp: string; hash: string; size: string; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo/src/sub-folder-1/button.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'ds-button', template: `<button class="ds-btn">Click me</button>`, styles: [ ` .ds-btn { color: red; } `, ], }) export class ButtonComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "files": [], "include": [], "references": [ { "path": "./tsconfig.lib.json" }, { "path": "./tsconfig.spec.json" } ], "nx": { "addTypecheckTarget": false } } ``` -------------------------------------------------------------------------------- /packages/shared/typescript-ast-utils/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "files": [], "include": [], "references": [ { "path": "./tsconfig.lib.json" }, { "path": "./tsconfig.spec.json" } ], "nx": { "addTypecheckTarget": false } } ``` -------------------------------------------------------------------------------- /testing/utils/src/index.ts: -------------------------------------------------------------------------------- ```typescript export * from './lib/source-file-from.code.js'; export * from './lib/os-agnostic-paths.js'; export * from './lib/constants.js'; export * from './lib/e2e-setup.js'; export * from './lib/string.js'; export * from './lib/execute-process-helper.mock'; ``` -------------------------------------------------------------------------------- /testing/utils/tsconfig.spec.json: -------------------------------------------------------------------------------- ```json { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../dist/out-tsc", "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"] }, "include": ["vite.config.ts", "src/**/*.ts", "src/**/*.unit.test.ts"] } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/main.ts: -------------------------------------------------------------------------------- ```typescript import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err) ); ``` -------------------------------------------------------------------------------- /packages/shared/utils/src/index.ts: -------------------------------------------------------------------------------- ```typescript export * from './lib/utils.js'; export * from './lib/execute-process.js'; export * from './lib/logging.js'; export * from './lib/file/find-in-file.js'; export * from './lib/file/file.resolver.js'; export * from './lib/file/default-export-loader.js'; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/storybook-host-app/src/components/segmented-control/segmented-control-tabs/examples.mdx: -------------------------------------------------------------------------------- ```markdown import { Canvas } from '@storybook/blocks'; import * as SegmentedControlStories from '../segmented-control.component.stories'; ## Default <Canvas of={SegmentedControlStories.Default} /> ## With Image <Canvas of={SegmentedControlStories.WithImage} /> ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/rx-host-listener/project.json: -------------------------------------------------------------------------------- ```json { "name": "design-system-rx-host-listener-ui", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "projectType": "library", "sourceRoot": "packages/design-system/ui/rx-host-listener/src", "tags": ["type:ui", "scope:shared"] } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo/src/mixed-external-assets.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'mixed-external-assets', templateUrl: './mixed-external-assets.component.html', styleUrls: ['./mixed-external-assets.component.css'], }) export class MixedExternalAssetsComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/template-syntax/ng-class-binding.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; import { NgClass } from '@angular/common'; @Component({ selector: 'ng-class-binding', imports: [NgClass], template: `<button [ngClass]="'btn'">Click me</button>`, }) export class NgClassBindingComponent {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/segmented-control/package.json: -------------------------------------------------------------------------------- ```json { "name": "@frontend/ui/segmented-control", "version": "0.0.0", "type": "module", "peerDependencies": { "@angular/cdk": "19.2.9", "@angular/common": "19.2.7", "@angular/core": "19.2.7", "@frontend/ui/rx-host-listener": "0.0.0" } } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/segmented-control/project.json: -------------------------------------------------------------------------------- ```json { "name": "design-system-segmented-control-ui", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "projectType": "library", "sourceRoot": "packages/design-system/ui/segmented-control/src", "tags": ["type:ui", "scope:shared"] } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/inl-styl-inl-tmpl/inl-styl-inl-tmpl.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'inl-styl-inl-tmpl', template: `<button class="btn">Click me</button>`, styles: [ ` .btn { color: red; } `, ], }) export class InlStylInlTmplComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/inl-styl-url-tmpl/inl-styl-url-tmpl.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'inl-styl-url-tmpl', templateUrl: './inl-styl-url-tmpl.component.html', styles: [ ` .btn { color: red; } `, ], }) export class InlStylUrlTmplComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/src/lib/template/template.walk.ts: -------------------------------------------------------------------------------- ```typescript import type { TmplAstNode, TmplAstVisitor, } from '@angular/compiler' with { 'resolution-mode': 'import' }; export function visitEachTmplChild( nodes: TmplAstNode[], visitor: TmplAstVisitor<unknown>, ) { nodes.forEach((node) => node.visit(visitor)); } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo/src/sub-folder-1/bad-alert.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-alert', template: `<div class="alert alert-danger">This is a legacy alert!</div>`, styles: [ ` .alert { color: red; } `, ], }) export class BadAlertComponent {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-mixed-external-assets.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-mixed-external-assets', templateUrl: './bad-mixed-external-assets.component.html', styleUrls: ['./bad-mixed-external-assets.component.css'], }) export class BadMixedExternalAssetsComponent {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-1/bad-mixed-external-assets-1.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-mixed-external-assets', templateUrl: './bad-mixed-external-assets-1.component.html', styleUrls: ['./bad-mixed-external-assets-1.component.css'], }) export class BadMixedExternalAssetsComponent1 {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-2/bad-mixed-external-assets-2.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-mixed-external-assets', templateUrl: './bad-mixed-external-assets-2.component.html', styleUrls: ['./bad-mixed-external-assets-2.component.css'], }) export class BadMixedExternalAssetsComponent2 {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-3/bad-mixed-external-assets-3.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-mixed-external-assets', templateUrl: './bad-mixed-external-assets-3.component.html', styleUrls: ['./bad-mixed-external-assets-3.component.css'], }) export class BadMixedExternalAssetsComponent3 {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/style-format/inl-scss.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'inl-scss', template: `<button class="btn">Click me</button>`, styles: [ ` .btn { span { color: darkred; } color: red; } `, ], }) export class InlCssComponent {} ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/eslint.config.mjs: -------------------------------------------------------------------------------- ``` import tseslint from 'typescript-eslint'; import baseConfig from '../../eslint.config.mjs'; export default tseslint.config(...baseConfig, { files: ['**/*.ts'], languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }); ``` -------------------------------------------------------------------------------- /packages/angular-mcp/eslint.config.mjs: -------------------------------------------------------------------------------- ``` import tseslint from 'typescript-eslint'; import baseConfig from '../../eslint.config.mjs'; export default tseslint.config(...baseConfig, { files: ['**/*.ts'], languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }); ``` -------------------------------------------------------------------------------- /testing/setup/eslint.next.config.mjs: -------------------------------------------------------------------------------- ``` import tseslint from 'typescript-eslint'; import baseConfig from '../../eslint.config.mjs'; export default tseslint.config(...baseConfig, { files: ['**/*.ts'], languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }); ``` -------------------------------------------------------------------------------- /testing/utils/eslint.next.config.mjs: -------------------------------------------------------------------------------- ``` import tseslint from 'typescript-eslint'; import baseConfig from '../../eslint.config.mjs'; export default tseslint.config(...baseConfig, { files: ['**/*.ts'], languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }); ``` -------------------------------------------------------------------------------- /testing/vitest-setup/eslint.next.config.mjs: -------------------------------------------------------------------------------- ``` import tseslint from 'typescript-eslint'; import baseConfig from '../../eslint.config.mjs'; export default tseslint.config(...baseConfig, { files: ['**/*.ts'], languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }); ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/eslint.config.mjs: -------------------------------------------------------------------------------- ``` import tseslint from 'typescript-eslint'; import baseConfig from '../../../eslint.config.mjs'; export default tseslint.config(...baseConfig, { files: ['**/*.ts'], languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }); ``` -------------------------------------------------------------------------------- /testing/setup/tsconfig.spec.json: -------------------------------------------------------------------------------- ```json { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../dist/out-tsc", "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"] }, "include": [ "vite.config.ts", "vitest.integration.config.mts", "src/**/*.ts", "src/**/*.unit.test.ts" ] } ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/shared/models/input-schemas.model.ts: -------------------------------------------------------------------------------- ```typescript import { ToolSchemaOptions } from '@push-based/models'; export const componentInputSchema = ( description: string, ): ToolSchemaOptions['inputSchema'] => ({ type: 'object', properties: { componentName: { type: 'string', description, }, }, required: ['componentName'], }); ``` -------------------------------------------------------------------------------- /testing/setup/package.json: -------------------------------------------------------------------------------- ```json { "name": "@push-based/testing-setup", "type": "module", "version": "0.0.1", "main": "./src/index.mjs", "types": "./src/index.d.ts", "private": true, "nx": { "sourceRoot": "testing/setup/src", "projectType": "library", "name": "testing-setup" }, "module": "./src/index.mjs" } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/multi-url-styl-inl-tmpl/multi-url-styl-inl-tmpl.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'multi-url-styl-inl-tmpl', template: `<button class="">Click me</button>`, styleUrls: [ './multi-url-styl-inl-tmpl-1.component.css', './multi-url-styl-inl-tmpl-2.component.css', ], }) export class MultiUrlStylInlTmplComponent {} ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/shared/index.ts: -------------------------------------------------------------------------------- ```typescript export * from './violation-analysis/index.js'; export * from './utils/regex-helpers.js'; export * from './utils/handler-helpers.js'; export * from './utils/component-validation.js'; export * from './utils/output.utils.js'; export * from './utils/cross-platform-path.js'; export * from './models/schema-helpers.js'; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-alert.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-alert', styles: [ ` .alert { padding: 10px; } .btn { padding: 10px; } `, ], template: `<div class="alert alert-danger">This is a legacy alert!</div>`, }) export class BadAlertComponent {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/index.html: -------------------------------------------------------------------------------- ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Minimal</title> <base href="/" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" type="image/x-icon" href="favicon.ico" /> </head> <body> <app-root></app-root> </body> </html> ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/utils.ts: -------------------------------------------------------------------------------- ```typescript import { CallToolRequest, CallToolResult, } from '@modelcontextprotocol/sdk/types.js'; export function toolNotFound(request: CallToolRequest): CallToolResult { return { content: [ { type: 'text', text: `Tool not found: ${request.params.name}`, isError: true, }, ], }; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo/src/bad-button-dropdown.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-button-dropdown', template: ` <button class="btn btn-primary">Click Me</button> <select class="dropdown"> <option>Option 1</option> <option>Option 2</option> </select> `, }) export class BadButtonDropdownComponent {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/app.config.ts: -------------------------------------------------------------------------------- ```typescript import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), ], }; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-mixed.module.ts: -------------------------------------------------------------------------------- ```typescript // generate angular module import { NgModule } from '@angular/core'; import { MixedStylesNotStandaloneComponent } from './bad-mixed-not-standalone.component'; @NgModule({ declarations: [], imports: [MixedStylesNotStandaloneComponent], exports: [MixedStylesNotStandaloneComponent], }) export class BadModuleModule {} ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/component-usage-graph/index.ts: -------------------------------------------------------------------------------- ```typescript export { buildComponentUsageGraph } from './utils/component-usage-graph-builder.js'; export { buildComponentUsageGraphTools } from './build-component-usage-graph.tool.js'; export type { ComponentUsageGraphResult, BuildComponentUsageGraphOptions, FileInfo, DependencyInfo, ComponentMetadata, } from './models/types.js'; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-1/bad-mixed-1.module.ts: -------------------------------------------------------------------------------- ```typescript // generate angular module import { NgModule } from '@angular/core'; import { MixedStylesNotStandaloneComponent1 } from './bad-mixed-not-standalone-1.component'; @NgModule({ declarations: [], imports: [MixedStylesNotStandaloneComponent1], exports: [MixedStylesNotStandaloneComponent1], }) export class BadModuleModule1 {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-2/bad-mixed-2.module.ts: -------------------------------------------------------------------------------- ```typescript // generate angular module import { NgModule } from '@angular/core'; import { MixedStylesNotStandaloneComponent2 } from './bad-mixed-not-standalone-2.component'; @NgModule({ declarations: [], imports: [MixedStylesNotStandaloneComponent2], exports: [MixedStylesNotStandaloneComponent2], }) export class BadModuleModule2 {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-3/bad-mixed-3.module.ts: -------------------------------------------------------------------------------- ```typescript // generate angular module import { NgModule } from '@angular/core'; import { MixedStylesNotStandaloneComponent3 } from './bad-mixed-not-standalone-3.component'; @NgModule({ declarations: [MixedStylesNotStandaloneComponent3], imports: [], exports: [MixedStylesNotStandaloneComponent3], }) export class BadModuleModule3 {} ``` -------------------------------------------------------------------------------- /testing/vitest-setup/tsconfig.spec.json: -------------------------------------------------------------------------------- ```json { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../dist/out-tsc", "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"] }, "references": [ { "path": "../../../testing/setup/tsconfig.json" } ], "include": ["vite.config.ts", "src/**/*.ts", "src/**/*.unit.test.ts"] } ``` -------------------------------------------------------------------------------- /packages/shared/utils/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "files": [], "include": [], "references": [ { "path": "../../../testing/vitest-setup" }, { "path": "../models" }, { "path": "./tsconfig.lib.json" }, { "path": "./tsconfig.spec.json" } ], "nx": { "addTypecheckTarget": false } } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-alert-tooltip-input.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-alert-tooltip-input', template: ` <div class="alert alert-tooltip">This is a warning alert!</div> <div class="tooltip">Hover over me!</div> <input class="form-control" placeholder="Enter text here" /> `, }) export class BadAlertTooltipInputComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo/src/sub-folder-1/sub-folder-2/bad-alert-tooltip-input.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-alert-tooltip-input', template: ` <div class="alert alert-warning">This is a warning alert!</div> <div class="tooltip">Hover over me!</div> <input class="form-control" placeholder="Enter text here" /> `, }) export class BadAlertTooltipInputComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/src/index.ts: -------------------------------------------------------------------------------- ```typescript export * from './lib/parse-component.js'; export * from './lib/constants.js'; export * from './lib/template/utils.js'; export * from './lib/template/template.walk.js'; export * from './lib/template/noop-tmpl-visitor.js'; export * from './lib/styles/utils.js'; export * from './lib/utils.js'; export * from './lib/types.js'; export * from './lib/schema.js'; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/modal/src/modal-content.component.ts: -------------------------------------------------------------------------------- ```typescript import { ChangeDetectionStrategy, Component, ViewEncapsulation, } from '@angular/core'; @Component({ selector: 'ds-modal-content', template: `<ng-content />`, host: { class: 'ds-modal-content', }, standalone: true, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, }) export class DsModalContent {} ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/tsconfig.spec.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "./out-tsc/jest", "types": ["jest", "node"], "forceConsistentCasingInFileNames": true }, "include": [ "jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts" ], "references": [ { "path": "./tsconfig.lib.json" } ] } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/tsconfig.spec.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "./out-tsc/jest", "types": ["jest", "node"], "forceConsistentCasingInFileNames": true }, "include": [ "jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts" ], "references": [ { "path": "./tsconfig.lib.json" } ] } ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/tsconfig.spec.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "./out-tsc/jest", "types": ["jest", "node"], "forceConsistentCasingInFileNames": true }, "include": [ "jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts" ], "references": [ { "path": "./tsconfig.lib.json" } ] } ``` -------------------------------------------------------------------------------- /packages/shared/typescript-ast-utils/tsconfig.spec.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "./out-tsc/jest", "types": ["jest", "node"], "forceConsistentCasingInFileNames": true }, "include": [ "jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts" ], "references": [ { "path": "./tsconfig.lib.json" } ] } ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/report-violations/index.ts: -------------------------------------------------------------------------------- ```typescript export { reportViolationsTools } from './report-violations.tool.js'; export { reportAllViolationsTools } from './report-all-violations.tool.js'; export type { ReportViolationsOptions, ViolationResult, ViolationIssue, ViolationAudit, FileViolation, FileViolationGroup, FileViolationGroups, FolderViolationSummary, FolderViolationGroups, } from './models/types.js'; ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "files": [], "include": [], "references": [ { "path": "../models" }, { "path": "../styles-ast-utils" }, { "path": "../angular-ast-utils" }, { "path": "./tsconfig.lib.json" }, { "path": "./tsconfig.spec.json" } ], "nx": { "addTypecheckTarget": false } } ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "files": [], "include": [], "references": [ { "path": "../typescript-ast-utils" }, { "path": "../utils" }, { "path": "../styles-ast-utils" }, { "path": "./tsconfig.lib.json" }, { "path": "./tsconfig.spec.json" } ], "nx": { "addTypecheckTarget": false } } ``` -------------------------------------------------------------------------------- /testing/utils/src/lib/source-file-from.code.ts: -------------------------------------------------------------------------------- ```typescript import { Project } from 'ts-morph'; import type { SourceFile as TsSourceFile } from 'typescript'; export const sourceFileFromCode = ({ path, code, }: { path?: string; code: string; }) => { const project = new Project({ useInMemoryFileSystem: true }); const tsMorphSourceFile = project.createSourceFile(path ?? 'cmp.ts', code); return tsMorphSourceFile as unknown as TsSourceFile; }; ``` -------------------------------------------------------------------------------- /packages/angular-mcp/tsconfig.app.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", "types": ["node", "express"], "rootDir": "src", "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo", "module": "NodeNext", "moduleResolution": "NodeNext" }, "include": ["src/**/*.ts"], "exclude": [], "references": [ { "path": "../angular-mcp-server/tsconfig.lib.json" } ] } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-modal-progress.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-modal-progress', template: ` <div class="modal"> <div class="modal-content"> <h2>Legacy Modal</h2> <p>This is an old modal.</p> </div> </div> <div class="progress-bar"> <div class="progress" style="width: 50%;"></div> </div> `, }) export class BadModalProgressComponent {} ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo/src/bad-modal-progress.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-modal-progress', template: ` <div class="modal"> <div class="modal-content"> <h2>Legacy Modal</h2> <p>This is an old modal.</p> </div> </div> <div class="progress-bar"> <div class="progress" style="width: 50%;"></div> </div> `, }) export class BadModalProgressComponent {} ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/tsconfig.app.json: -------------------------------------------------------------------------------- ```json /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/app", "types": [] }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"] } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/tsconfig.spec.json: -------------------------------------------------------------------------------- ```json /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", "types": ["jasmine"] }, "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] } ``` -------------------------------------------------------------------------------- /tools/nx-advanced-profile.postinstall.js: -------------------------------------------------------------------------------- ```javascript import { writeFileSync } from 'node:fs'; import { readFileSync } from 'node:fs'; // This is adding `import("./../../../../tools/perf_hooks.patch");` to your `node_modules/nx/src/utils/perf-logging.js`. writeFileSync( './node_modules/nx/src/utils/perf-logging.js', readFileSync( './node_modules/nx/src/utils/perf-logging.js', 'utf-8', ).toString() + 'import("./../../../../tools/perf_hooks.patch");', ); ``` -------------------------------------------------------------------------------- /testing/setup/eslint.config.mjs: -------------------------------------------------------------------------------- ``` import nextEslintConfig from './eslint.next.config.mjs'; // 🚨 DO NOT EDIT THIS FILE MANUALLY! 🚨 // Run `pnpm eslint-next/nx` to update or remove this file if all rules pass. // Add new rules globally to the `eslint.config.js` or locally to the `eslint.next.config.js` file. // For details, refer to: tools/scripts/eslint-next/README.md export default [ ...nextEslintConfig, { files: ['**/*'], rules: {}, }, ]; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-button-dropdown.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-bad-button-dropdown', styles: [ ` .btn { margin: 10px; } .btn-dropdown { padding: 10px; } `, ], template: ` <button class="btn btn-primary">Click Me</button> <select class="btn-dropdown"> <option>Option 1</option> <option>Option 2</option> </select> `, }) export class BadButtonDropdownComponent {} ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.base.json", "files": [], "include": [], "references": [ { "path": "../shared/ds-component-coverage" }, { "path": "../shared/styles-ast-utils" }, { "path": "../shared/angular-ast-utils" }, { "path": "../shared/utils" }, { "path": "../shared/models" }, { "path": "./tsconfig.lib.json" } ], "nx": { "addTypecheckTarget": false } } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/standalone-module-conflict.module.ts: -------------------------------------------------------------------------------- ```typescript import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { StandaloneModuleConflictComponent } from './standalone-module-conflict.component'; @NgModule({ declarations: [ StandaloneModuleConflictComponent // ERROR: Standalone components should not be declared in modules ], imports: [ CommonModule ], exports: [ StandaloneModuleConflictComponent ] }) export class StandaloneModuleConflictModule { } ``` -------------------------------------------------------------------------------- /packages/shared/typescript-ast-utils/package.json: -------------------------------------------------------------------------------- ```json { "name": "@push-based/typescript-ast-utils", "version": "0.0.1", "private": true, "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { "./package.json": "./package.json", ".": { "development": "./src/index.ts", "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" } }, "nx": { "name": "typescript-ast-utils" } } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-this-window-document.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component, inject, InjectionToken } from '@angular/core'; import { DOCUMENT } from '@angular/common'; export const WINDOW = new InjectionToken<any>('Safe access window object', { providedIn: 'root', factory: () => inject(DOCUMENT).defaultView, }); @Component({ selector: 'app-bad-this-window-document', template: `<h3>Window usage</h3>`, }) export class BadThisWindowDocumentComponent { readonly #window = inject(WINDOW); doc = this.#window.document; } ``` -------------------------------------------------------------------------------- /packages/shared/models/tsconfig.lib.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", "baseUrl": ".", "rootDir": "src", "outDir": "dist", "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", "emitDeclarationOnly": false, "forceConsistentCasingInFileNames": true, "types": ["node"] }, "include": ["src/**/*.ts"], "references": [], "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] } ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/tsconfig.lib.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", "baseUrl": ".", "rootDir": "src", "outDir": "dist", "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", "emitDeclarationOnly": false, "forceConsistentCasingInFileNames": true, "types": ["node"] }, "include": ["src/**/*.ts"], "references": [], "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] } ``` -------------------------------------------------------------------------------- /packages/shared/typescript-ast-utils/tsconfig.lib.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", "baseUrl": ".", "rootDir": "src", "outDir": "dist", "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", "emitDeclarationOnly": false, "forceConsistentCasingInFileNames": true, "types": ["node"] }, "include": ["src/**/*.ts"], "references": [], "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] } ``` -------------------------------------------------------------------------------- /packages/shared/utils/package.json: -------------------------------------------------------------------------------- ```json { "name": "@push-based/utils", "version": "0.0.1", "private": true, "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { "./package.json": "./package.json", ".": { "development": "./src/index.ts", "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" } }, "nx": { "tags": [ "scope:shared", "type:lib" ] } } ``` -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "composite": true, "declarationMap": true, "emitDeclarationOnly": true, "importHelpers": true, "isolatedModules": true, "lib": ["es2022"], "module": "NodeNext", "moduleResolution": "NodeNext", "noEmitOnError": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, "noUnusedLocals": true, "skipLibCheck": true, "strict": true, "target": "es2022" } } ``` -------------------------------------------------------------------------------- /packages/shared/models/package.json: -------------------------------------------------------------------------------- ```json { "name": "@push-based/models", "version": "0.0.1", "private": true, "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { "./package.json": "./package.json", ".": { "development": "./src/index.ts", "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" } }, "nx": { "tags": [ "scope:shared", "type:lib" ] } } ``` -------------------------------------------------------------------------------- /packages/shared/models/src/lib/mcp.ts: -------------------------------------------------------------------------------- ```typescript import { CallToolResult, ToolSchema } from '@modelcontextprotocol/sdk/types.js'; import { z } from 'zod'; import { CallToolResultSchema, CallToolRequest, } from '@modelcontextprotocol/sdk/types.js'; export type ToolSchemaOptions = z.infer<typeof ToolSchema>; export type ToolHandlerContentResult = z.infer< typeof CallToolResultSchema >['content'][number]; export type ToolsConfig = { schema: ToolSchemaOptions; handler: (o: CallToolRequest) => Promise<CallToolResult>; }; ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/src/lib/stylesheet.parse.ts: -------------------------------------------------------------------------------- ```typescript import postcss from 'postcss'; import safeParser from 'postcss-safe-parser'; /** * Parse a stylesheet content and return the AST. * PostCss is indexed with 1, so we don't need to adjust the line number to be linkable. * * @param content * @param filePath */ export function parseStylesheet(content: string, filePath: string) { return postcss().process(content, { parser: safeParser, from: filePath, map: { inline: false, // preserve line number }, }); } ``` -------------------------------------------------------------------------------- /testing/utils/package.json: -------------------------------------------------------------------------------- ```json { "name": "@push-based/testing-utils", "version": "0.0.1", "private": true, "main": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", "typings": "./dist/index.d.ts", "exports": { "./package.json": "./package.json", ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }, "nx": { "sourceRoot": "testing/utils/src", "projectType": "library", "name": "testing-utils", "targets": {} } } ``` -------------------------------------------------------------------------------- /testing/vitest-setup/package.json: -------------------------------------------------------------------------------- ```json { "name": "@push-based/testing-vitest-setup", "version": "0.0.1", "main": "./dist/index.js", "typings": "./dist/index.d.ts", "private": true, "nx": { "sourceRoot": "testing/vitest-setup/src", "projectType": "library", "name": "testing-vitest-setup" }, "exports": { "./package.json": "./package.json", ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }, "types": "./dist/index.d.ts", "module": "./dist/index.js" } ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/package.json: -------------------------------------------------------------------------------- ```json { "name": "@push-based/styles-ast-utils", "version": "0.0.1", "private": true, "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { "./package.json": "./package.json", ".": { "development": "./src/index.ts", "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" } }, "nx": { "tags": [ "scope:shared", "type:lib" ] } } ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/package.json: -------------------------------------------------------------------------------- ```json { "name": "@push-based/angular-ast-utils", "version": "0.0.1", "private": true, "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { "./package.json": "./package.json", ".": { "development": "./src/index.ts", "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" } }, "nx": { "tags": [ "scope:shared", "type:lib" ] } } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/standalone-module-conflict.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-standalone-module-conflict', standalone: true, template: ` <div> <h2>Standalone Module Conflict Test</h2> <p>This standalone component should NOT be declared in a module</p> </div> `, styles: [` div { background: #ffe6e6; padding: 15px; border: 1px solid red; } `] }) export class StandaloneModuleConflictComponent { title = 'Standalone Component in Module'; } ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/component-usage-graph/utils/angular-parser.ts: -------------------------------------------------------------------------------- ```typescript import { parseComponents } from '@push-based/angular-ast-utils'; import { ComponentMetadata } from '../models/types.js'; export async function parseAngularComponent( filePath: string, ): Promise<ComponentMetadata | null> { try { const [component] = await parseComponents([filePath]); return component ? { className: component.className } : null; } catch (ctx) { throw new Error( `Failed to parse Angular component from ${filePath}: ${(ctx as Error).message}`, ); } } ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/shared/utils/output.utils.ts: -------------------------------------------------------------------------------- ```typescript import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; export const buildText = ( text: string, isError = false, ): CallToolResult['content'][0] => { return { type: 'text', text, isError, }; }; export const buildTextResponse = (texts: string[]): CallToolResult => { return { content: [...texts.map((text) => buildText(text, false))], }; }; export const throwError = (text: string): CallToolResult => { return { content: [buildText(text, true)], }; }; ``` -------------------------------------------------------------------------------- /testing/utils/eslint.config.mjs: -------------------------------------------------------------------------------- ``` import nextEslintConfig from './eslint.next.config.mjs'; // 🚨 DO NOT EDIT THIS FILE MANUALLY! 🚨 // Run `pnpm eslint-next/nx` to update or remove this file if all rules pass. // Add new rules globally to the `eslint.config.js` or locally to the `eslint.next.config.js` file. // For details, refer to: tools/scripts/eslint-next/README.md export default [ ...nextEslintConfig, { files: ['**/*'], rules: { // ❌ Errors: 1 '@nx/dependency-checks': 'off', // ❌ 1 error 🛠️ }, }, ]; ``` -------------------------------------------------------------------------------- /testing/vitest-setup/eslint.config.mjs: -------------------------------------------------------------------------------- ``` import nextEslintConfig from './eslint.next.config.mjs'; // 🚨 DO NOT EDIT THIS FILE MANUALLY! 🚨 // Run `pnpm eslint-next/nx` to update or remove this file if all rules pass. // Add new rules globally to the `eslint.config.js` or locally to the `eslint.next.config.js` file. // For details, refer to: tools/scripts/eslint-next/README.md export default [ ...nextEslintConfig, { files: ['**/*'], rules: { // ❌ Errors: 1 '@nx/dependency-checks': 'off', // ❌ 1 error 🛠️ }, }, ]; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/storybook-host-app/src/components/badge/badge.component.mdx: -------------------------------------------------------------------------------- ```markdown import { Meta } from '@storybook/blocks'; import * as BadgeStories from './badge.component.stories'; import OverviewTab from './badge-tabs/overview.mdx'; import ExamplesTab from './badge-tabs/examples.mdx'; import ApiTab from './badge-tabs/api.mdx'; import { ComponentDocTabs } from '../../../../shared-storybook-utils/src/lib/component-doc-tabs/component-doc-tabs.mdx'; <Meta of={BadgeStories} /> # Badge Component <ComponentDocTabs OverviewTab={OverviewTab} ExamplesTab={ExamplesTab} ApiTab={ApiTab} /> ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/storybook-host-app/src/components/modal/modal.component.mdx: -------------------------------------------------------------------------------- ```markdown import { Meta } from '@storybook/blocks'; import * as ModalStories from './modal.component.stories'; import OverviewTab from './modal-tabs/overview.mdx'; import ExamplesTab from './modal-tabs/examples.mdx'; import ApiTab from './modal-tabs/api.mdx'; import { ComponentDocTabs } from '../../../../shared-storybook-utils/src/lib/component-doc-tabs/component-doc-tabs.mdx'; <Meta of={ModalStories} /> # Modal Component <ComponentDocTabs OverviewTab={OverviewTab} ExamplesTab={ExamplesTab} ApiTab={ApiTab} /> ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/modal/src/modal-header-drag/modal-header-drag.component.ts: -------------------------------------------------------------------------------- ```typescript import { ChangeDetectionStrategy, Component, ViewEncapsulation, } from '@angular/core'; @Component({ standalone: true, selector: 'ds-modal-header-drag', host: { class: 'ds-modal-header-drag', role: 'dialog', 'aria-label': 'Modal header drag dialog', }, template: `<span class="ds-modal-header-drag-rectangle"></span>`, styleUrl: 'modal-header-drag.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, }) export class DsModalHeaderDrag {} ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/package.json: -------------------------------------------------------------------------------- ```json { "name": "@push-based/angular-mcp-server", "version": "0.0.1", "private": true, "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", "scripts": { "debug": "nx run angular-mcp:debug" }, "exports": { "./package.json": "./package.json", ".": { "development": "./src/index.ts", "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" } }, "nx": { "name": "angular-mcp-server" } } ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/component-contract/list/models/schema.ts: -------------------------------------------------------------------------------- ```typescript import { ToolSchemaOptions } from '@push-based/models'; import { createProjectAnalysisSchema, COMMON_ANNOTATIONS, } from '../../../shared/models/schema-helpers.js'; /** * Schema for listing component contracts */ export const listComponentContractsSchema: ToolSchemaOptions = { name: 'list_component_contracts', description: 'List all available component contracts in the .cursor/tmp/contracts directory.', inputSchema: createProjectAnalysisSchema(), annotations: { title: 'List Component Contracts', ...COMMON_ANNOTATIONS.readOnly, }, }; ``` -------------------------------------------------------------------------------- /testing/utils/src/lib/execute-process-helper.mock.ts: -------------------------------------------------------------------------------- ```typescript import path from 'node:path'; const asyncProcessPath = path.join(__dirname, './execute-process.mock.mjs'); /** * Helps to get an async process runner config for testing. * * @param cfg can contain up to three properties for the async process runner */ export function getAsyncProcessRunnerConfig(cfg?: { throwError?: boolean; interval?: number; runs?: number; }) { const args = [ asyncProcessPath, cfg?.interval ? cfg.interval + '' : '100', cfg?.runs ? cfg.runs + '' : '4', cfg?.throwError ? '1' : '0', ]; return { command: 'node', args }; } ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/src/lib/stylesheet.visitor.ts: -------------------------------------------------------------------------------- ```typescript import { Comment, Container, Declaration, Rule, AtRule } from 'postcss'; export interface CssAstVisitor<T = void> { // Called once for the root node visitRoot?: (root: Container) => T; // Called for @rule nodes: @media, @charset, etc. visitAtRule?: (atRule: AtRule) => T; // Called for standard CSS rule nodes: .btn, .box, etc. visitRule?: (rule: Rule) => T; // Called for property declarations: color: red, width: 100px, etc. visitDecl?: (decl: Declaration) => T; // Called for comment nodes: /* some comment */ visitComment?: (comment: Comment) => T; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/schema.ts: -------------------------------------------------------------------------------- ```typescript import { z } from 'zod'; export const ComponentReplacementSchema = z.object( { componentName: z.string({ description: 'The class name of the component to search for', }), deprecatedCssClasses: z.array(z.string(), { description: 'List of deprecated CSS classes for this component', }), docsUrl: z .string({ description: 'URL to the component documentation' }) .optional(), }, { description: 'Array of components and their deprecated CSS classes' }, ); export type ComponentReplacement = z.infer<typeof ComponentReplacementSchema>; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/storybook-host-app/src/components/segmented-control/segmented-control.component.mdx: -------------------------------------------------------------------------------- ```markdown import { Meta } from '@storybook/blocks'; import * as SegmentedControlStories from './segmented-control.component.stories'; import OverviewTab from './segmented-control-tabs/overview.mdx'; import ExamplesTab from './segmented-control-tabs/examples.mdx'; import ApiTab from './segmented-control-tabs/api.mdx'; import { ComponentDocTabs } from '../../../../shared-storybook-utils/src/lib/component-doc-tabs/component-doc-tabs.mdx'; <Meta of={SegmentedControlStories} /> # Segmented Control Component <ComponentDocTabs OverviewTab={OverviewTab} ExamplesTab={ExamplesTab} ApiTab={ApiTab} /> ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/src/lib/runner/create-runner.ts: -------------------------------------------------------------------------------- ```typescript import { AuditOutputs } from '@code-pushup/models'; import { parseAngularUnit } from '@push-based/angular-ast-utils'; import { dsCompCoverageAuditOutputs } from './audits/ds-coverage/ds-coverage.audit.js'; import { ComponentCoverageRunnerOptions } from './schema.js'; export type CreateRunnerConfig = ComponentCoverageRunnerOptions; export async function runnerFunction({ directory, dsComponents, }: CreateRunnerConfig): Promise<AuditOutputs> { const parsedComponents = await parseAngularUnit(directory, 'component'); return dsCompCoverageAuditOutputs(dsComponents, parsedComponents); } ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/jest.config.ts: -------------------------------------------------------------------------------- ```typescript import { readFileSync } from 'fs'; // Reading the SWC compilation config for the spec files const swcJestConfig = JSON.parse( readFileSync(`${__dirname}/.spec.swcrc`, 'utf-8'), ); // Disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves swcJestConfig.swcrc = false; export default { displayName: '@push-based/styles-ast-utils', preset: '../../../jest.preset.mjs', testEnvironment: 'node', transform: { '^.+\\.[tj]s$': ['@swc/jest', swcJestConfig], }, moduleFileExtensions: ['ts', 'js', 'html'], coverageDirectory: 'test-output/jest/coverage', }; ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/jest.config.ts: -------------------------------------------------------------------------------- ```typescript import { readFileSync } from 'fs'; // Reading the SWC compilation config for the spec files const swcJestConfig = JSON.parse( readFileSync(`${__dirname}/.spec.swcrc`, 'utf-8'), ); // Disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves swcJestConfig.swcrc = false; export default { displayName: '@push-based/angular-ast-utils', preset: '../../../jest.preset.mjs', testEnvironment: 'node', transform: { '^.+\\.[tj]s$': ['@swc/jest', swcJestConfig], }, moduleFileExtensions: ['ts', 'js', 'html'], coverageDirectory: 'test-output/jest/coverage', }; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/external-files-missing.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-external-files-missing', standalone: true, templateUrl: './non-existent-template.html', // This file doesn't exist styleUrls: [ './missing-styles.css', // This file doesn't exist './another-missing-style.scss', // This file doesn't exist '../shared/non-existent-shared.css' // This file doesn't exist ] }) export class ExternalFilesMissingComponent { title = 'External Files Missing Component'; message = 'This component references non-existent external files'; } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/jest.config.ts: -------------------------------------------------------------------------------- ```typescript import { readFileSync } from 'fs'; // Reading the SWC compilation config for the spec files const swcJestConfig = JSON.parse( readFileSync(`${__dirname}/.spec.swcrc`, 'utf-8'), ); // Disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves swcJestConfig.swcrc = false; export default { displayName: '@push-based/typescript-ast-utils', preset: '../../../jest.preset.mjs', testEnvironment: 'node', transform: { '^.+\\.[tj]s$': ['@swc/jest', swcJestConfig], }, moduleFileExtensions: ['ts', 'js', 'html'], coverageDirectory: 'test-output/jest/coverage', }; ``` -------------------------------------------------------------------------------- /packages/shared/typescript-ast-utils/jest.config.ts: -------------------------------------------------------------------------------- ```typescript import { readFileSync } from 'fs'; // Reading the SWC compilation config for the spec files const swcJestConfig = JSON.parse( readFileSync(`${__dirname}/.spec.swcrc`, 'utf-8'), ); // Disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves swcJestConfig.swcrc = false; export default { displayName: '@push-based/typescript-ast-utils', preset: '../../../jest.preset.mjs', testEnvironment: 'node', transform: { '^.+\\.[tj]s$': ['@swc/jest', swcJestConfig], }, moduleFileExtensions: ['ts', 'js', 'html'], coverageDirectory: 'test-output/jest/coverage', }; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/missing-method.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-missing-method', standalone: true, template: ` <div> <h2>Missing Method Test</h2> <button (click)="nonExistentMethod()">Click me</button> <p>{{ getDisplayText() }}</p> <div>{{ calculateValue(42) }}</div> </div> `, styles: [` div { padding: 20px; border: 1px solid red; } `] }) export class MissingMethodComponent { title = 'Missing Method Component'; // Note: nonExistentMethod(), getDisplayText(), and calculateValue() are called in template but not defined } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-4/multi-violation-test.component.ts: -------------------------------------------------------------------------------- ```typescript 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'); } } ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/tsconfig.lib.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "compilerOptions": { "baseUrl": ".", "rootDir": "src", "outDir": "dist", "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", "emitDeclarationOnly": false, "forceConsistentCasingInFileNames": true, "types": ["node"] }, "include": ["src/**/*.ts"], "references": [ { "path": "../typescript-ast-utils/tsconfig.lib.json" }, { "path": "../utils/tsconfig.lib.json" }, { "path": "../styles-ast-utils/tsconfig.lib.json" } ], "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/circular-dependency.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; import { CircularDependencyComponent } from './circular-dependency.component'; @Component({ selector: 'app-circular-dependency', standalone: true, imports: [CircularDependencyComponent], template: ` <div> <h2>Circular Dependency Test</h2> <p>This component imports itself!</p> <app-circular-dependency></app-circular-dependency> </div> `, styles: [` div { border: 2px solid orange; margin: 5px; padding: 10px; } `] }) export class CircularDependencyComponent { title = 'Circular Dependency Component'; } ``` -------------------------------------------------------------------------------- /testing/utils/tsconfig.lib.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.base.json", "compilerOptions": { "baseUrl": ".", "rootDir": "src", "outDir": "dist", "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", "emitDeclarationOnly": false, "types": ["node", "vite/client"] }, "include": ["src/**/*.ts"], "references": [], "exclude": [ "vite.config.ts", "vite.config.mts", "vitest.config.ts", "vitest.config.mts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.test.tsx", "src/**/*.spec.tsx", "src/**/*.test.js", "src/**/*.spec.js", "src/**/*.test.jsx", "src/**/*.spec.jsx" ] } ``` -------------------------------------------------------------------------------- /testing/vitest-setup/tsconfig.lib.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.base.json", "compilerOptions": { "baseUrl": ".", "rootDir": "src", "outDir": "dist", "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", "emitDeclarationOnly": false, "types": ["node", "vite/client"] }, "include": ["src/**/*.ts"], "exclude": [ "vite.config.ts", "vite.config.mts", "vitest.config.ts", "vitest.config.mts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.test.tsx", "src/**/*.spec.tsx", "src/**/*.test.js", "src/**/*.spec.js", "src/**/*.test.jsx", "src/**/*.spec.jsx" ], "references": [] } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo/prompt.md: -------------------------------------------------------------------------------- ```markdown ## Prompt Refactor the folder packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo. Look for btn and btn-primary class and replace them with the button component in the same folder. Use the coverage MCP tool to list used classes. directly call the tool before gathereing more context ## Tool Request ```bash { "cwd": "/Users/michael_hladky/WebstormProjects/ds-mcp", "directory": "packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo", "dsComponents": [ { "componentName": "Button", "deprecatedCssClasses": [ "btn", "btn-primary" ] } ] } ``` ## Tool Response ```bash ``` ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-usage.utils.ts: -------------------------------------------------------------------------------- ```typescript import { Asset, visitEachTmplChild } from '@push-based/angular-ast-utils'; import { ComponentReplacement } from './schema.js'; import { ClassUsageVisitor } from './class-usage.visitor.js'; import type { ParsedTemplate, TmplAstNode, } from '@angular/compiler' with { 'resolution-mode': 'import' }; export async function getClassUsageIssues( componentReplacement: ComponentReplacement, asset: Asset<ParsedTemplate>, ) { const visitor = new ClassUsageVisitor(componentReplacement, asset.startLine); const parsedTemplate = await asset.parse(); visitEachTmplChild(parsedTemplate.nodes as TmplAstNode[], visitor); return visitor.getIssues(); } ``` -------------------------------------------------------------------------------- /testing/setup/tsconfig.lib.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.base.json", "compilerOptions": { "baseUrl": ".", "rootDir": "src", "outDir": "dist", "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", "emitDeclarationOnly": false, "types": ["node", "vite/client"] }, "include": ["src/**/*.ts"], "exclude": [ "vite.config.ts", "vite.config.mts", "vitest.config.ts", "vitest.config.mts", "vitest.integration.config.mts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.test.tsx", "src/**/*.spec.tsx", "src/**/*.test.js", "src/**/*.spec.js", "src/**/*.test.jsx", "src/**/*.spec.jsx" ], "references": [] } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/src/index.ts: -------------------------------------------------------------------------------- ```typescript import { dsComponentCoveragePlugin } from './lib/ds-component-coverage.plugin.js'; export { runnerFunction, type CreateRunnerConfig, } from './lib/runner/create-runner.js'; export { dsComponentCoveragePlugin, type DsComponentUsagePluginConfig, } from './lib/ds-component-coverage.plugin.js'; export { getAngularDsUsageCategoryRefs } from './lib/utils.js'; export { ANGULAR_DS_USAGE_PLUGIN_SLUG } from './lib/constants.js'; export default dsComponentCoveragePlugin; export type { ComponentReplacement, ComponentReplacementSchema, } from './lib/runner/audits/ds-coverage/schema.js'; export { ComponentCoverageRunnerOptionsSchema } from './lib/runner/schema.js'; ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/tsconfig.lib.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", "baseUrl": ".", "rootDir": "src", "outDir": "dist", "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", "emitDeclarationOnly": false, "forceConsistentCasingInFileNames": true, "types": ["node"] }, "include": ["src/**/*.ts"], "references": [ { "path": "../models/tsconfig.lib.json" }, { "path": "../styles-ast-utils/tsconfig.lib.json" }, { "path": "../angular-ast-utils/tsconfig.lib.json" } ], "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/style-format/code-pushup.config.ts: -------------------------------------------------------------------------------- ```typescript import { ComponentReplacement } from '../../../../src/index'; import { dsComponentUsagePluginCoreConfig } from '../../../../src/core.config'; const dsComponents: ComponentReplacement[] = [ { componentName: 'DSButton', deprecatedCssClasses: ['btn', 'btn-primary', 'legacy-button'], docsUrl: 'https://storybook.company.com/latest/?path=/docs/components-button--overview', }, ]; export default { persist: { outputDir: '.code-pushup/ds-component-coverage/style-format', format: ['json', 'md'], }, ...(await dsComponentUsagePluginCoreConfig({ directory: 'packages/shared/ds-component-coverage/mocks/fixtures/e2e/style-format', dsComponents, })), }; ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/src/lib/utils.ts: -------------------------------------------------------------------------------- ```typescript import { Audit, CategoryRef } from '@code-pushup/models'; import { ANGULAR_DS_USAGE_PLUGIN_SLUG } from './constants.js'; import { getCompUsageAudits } from './runner/audits/ds-coverage/utils.js'; import { ComponentReplacement } from './runner/audits/ds-coverage/schema.js'; export function getAudits( componentReplacements: ComponentReplacement[], ): Audit[] { return [...getCompUsageAudits(componentReplacements)]; } export function getAngularDsUsageCategoryRefs( componentReplacements: ComponentReplacement[], ): CategoryRef[] { return getAudits(componentReplacements).map(({ slug }) => ({ slug, plugin: ANGULAR_DS_USAGE_PLUGIN_SLUG, type: 'audit', weight: 1, })); } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number/code-pushup.config.ts: -------------------------------------------------------------------------------- ```typescript import { ComponentReplacement } from '../../../../src/index'; import { dsComponentUsagePluginCoreConfig } from '../../../../src/core.config'; const dsComponents: ComponentReplacement[] = [ { componentName: 'DSButton', deprecatedCssClasses: ['btn', 'btn-primary', 'legacy-button'], docsUrl: 'https://storybook.company.com/latest/?path=/docs/components-button--overview', }, ]; export default { persist: { outputDir: '.code-pushup/ds-component-coverage/line-number', format: ['json', 'md'], }, ...(await dsComponentUsagePluginCoreConfig({ directory: './packages/shared/ds-component-coverage/mocks/fixtures/e2e/line-number', dsComponents, })), }; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/segmented-control/src/segmented-control.token.ts: -------------------------------------------------------------------------------- ```typescript import { InjectionToken, Provider } from '@angular/core'; export type DsSegmentedControlOptions = { /* * Whether the control should take up the full width of the container. */ fullWidth: boolean; }; const DEFAULT_SC_OPTIONS: DsSegmentedControlOptions = { fullWidth: false, }; export const SEGMENTED_CONTROL_OPTIONS_TOKEN = new InjectionToken<DsSegmentedControlOptions>('SC_OPTIONS', { providedIn: 'root', factory: () => DEFAULT_SC_OPTIONS, }); export const provideSegmentedControlOptions = ( options: Partial<DsSegmentedControlOptions>, ) => ({ provide: SEGMENTED_CONTROL_OPTIONS_TOKEN, useValue: { ...DEFAULT_SC_OPTIONS, ...options }, }) satisfies Provider; ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location/code-pushup.config.ts: -------------------------------------------------------------------------------- ```typescript import { ComponentReplacement } from '../../../../src/index'; import { dsComponentUsagePluginCoreConfig } from '../../../../src/core.config'; const dsComponents: ComponentReplacement[] = [ { componentName: 'DSButton', deprecatedCssClasses: ['btn', 'btn-primary', 'legacy-button'], docsUrl: 'https://storybook.company.com/latest/?path=/docs/components-button--overview', }, ]; export default { persist: { outputDir: '.code-pushup/ds-component-coverage/asset-location', format: ['json', 'md'], }, ...(await dsComponentUsagePluginCoreConfig({ directory: './packages/shared/ds-component-coverage/mocks/fixtures/e2e/asset-location', dsComponents, })), }; ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/template-syntax/code-pushup.config.ts: -------------------------------------------------------------------------------- ```typescript import { ComponentReplacement } from '../../../../src/index'; import { dsComponentUsagePluginCoreConfig } from '../../../../src/core.config'; const dsComponents: ComponentReplacement[] = [ { componentName: 'DSButton', deprecatedCssClasses: ['btn', 'btn-primary', 'legacy-button'], docsUrl: 'https://storybook.company.com/latest/?path=/docs/components-button--overview', }, ]; export default { persist: { outputDir: '.code-pushup/ds-component-coverage/template-syntax', format: ['json', 'md'], }, ...(await dsComponentUsagePluginCoreConfig({ directory: 'packages/shared/ds-component-coverage/mocks/fixtures/e2e/template-syntax', dsComponents, })), }; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/invalid-template-syntax.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-invalid-syntax', standalone: false, template: ` <div> <h2>Invalid Template Syntax Test</h2> <p>{{ unclosedInterpolation <div [attr.data-value="missingQuotes>Content</div> <button (click)="method(">Malformed event binding</button> <input [(ngModel)]="value" [disabled]="true" [readonly]="false" /> <span *ngFor="let item of items; let i = index">{{ item.name }}</span> <div [ngClass]="{active: true, disabled: }">Class binding error</div> </div> `, styles: [` div { margin: 10px; } `] }) export class InvalidTemplateSyntaxComponent { value = ''; items = [{ name: 'test' }]; } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/wrong-property-binding.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-wrong-property', standalone: true, template: ` <div> <h2>Wrong Property Binding Test</h2> <p>{{ nonExistentProperty }}</p> <input [value]="undefinedValue" /> <div [class.active]="isActiveButNotDefined"> Status: {{ status.value }} </div> <span [hidden]="hiddenFlag">{{ missingData }}</span> </div> `, styles: [` .active { background: green; } `] }) export class WrongPropertyBindingComponent { title = 'Wrong Property Component'; // Note: nonExistentProperty, undefinedValue, isActiveButNotDefined, status, hiddenFlag, and missingData are used in template but not defined } ``` -------------------------------------------------------------------------------- /packages/shared/utils/tsconfig.spec.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "./out-tsc/vitest", "types": [ "vitest/globals", "vitest/importMeta", "vite/client", "node", "vitest" ] }, "references": [ { "path": "../../../testing/utils/tsconfig.json" }, { "path": "../../../testing/vitest-setup/tsconfig.json" } ], "include": [ "vite.config.ts", "vite.config.mts", "vitest.config.ts", "vitest.config.mts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.test.tsx", "src/**/*.spec.tsx", "src/**/*.test.js", "src/**/*.spec.js", "src/**/*.test.jsx", "src/**/*.spec.jsx", "src/**/*.d.ts" ] } ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-definition.utils.ts: -------------------------------------------------------------------------------- ```typescript import { Issue } from '@code-pushup/models'; import { Asset } from '@push-based/angular-ast-utils'; import { type Root } from 'postcss'; import { ComponentReplacement } from './schema.js'; import { visitEachStyleNode } from '@push-based/styles-ast-utils'; import { createClassDefinitionVisitor } from './class-definition.visitor.js'; export async function getClassDefinitionIssues( componentReplacement: ComponentReplacement, style: Asset<Root>, ): Promise<Issue[]> { const stylesVisitor = createClassDefinitionVisitor( componentReplacement, style.startLine, ); const ast = (await style.parse()).root as unknown as Root; visitEachStyleNode(ast.nodes, stylesVisitor); return stylesVisitor.getIssues(); } ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/src/lib/types.ts: -------------------------------------------------------------------------------- ```typescript import { Root } from 'postcss'; import type { ParsedTemplate } from '@angular/compiler' with { 'resolution-mode': 'import' }; import { z } from 'zod'; import { AngularUnitSchema } from './schema.js'; export type Asset<T> = SourceLink & { parse: () => Promise<T>; }; export type SourceLink = { filePath: string; startLine: number }; export type ParsedComponent = { className: string; fileName: string; startLine: number; } & { templateUrl: Asset<ParsedTemplate>; template: Asset<ParsedTemplate>; styles: Asset<Root>[]; styleUrls: Asset<Root>[]; styleUrl: Asset<Root>; } & { [key: string]: string; // @TODO implement all of the component props }; export type AngularUnit = z.infer<typeof AngularUnitSchema>; ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "./tsconfig.base.json", "compileOnSave": false, "files": [], "references": [ { "path": "./packages/angular-mcp-server" }, { "path": "./packages/angular-mcp" }, { "path": "./packages/shared/typescript-ast-utils" }, { "path": "./packages/shared/models" }, { "path": "./packages/shared/angular-ast-utils" }, { "path": "./packages/shared/styles-ast-utils" }, { "path": "./packages/shared/utils" }, { "path": "./packages/shared/ds-component-coverage" }, { "path": "./testing/vitest-setup" }, { "path": "./testing/setup" }, { "path": "./testing/utils" } ] } ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/schema.ts: -------------------------------------------------------------------------------- ```typescript import { ToolSchemaOptions } from '@push-based/models'; export const baseToolsSchema: ToolSchemaOptions = { name: 'base_tools', description: 'Base tools for Angular MCP server', inputSchema: { type: 'object', properties: { cwd: { type: 'string', description: 'The current working directory e.g. repository root', }, workspaceRoot: { type: 'string', description: 'The absolute path to the workspace root directory', }, verbose: { type: 'boolean', description: 'Enable verbose logging', }, }, required: ['cwd'], }, }; export type CliArguments = { cwd: string; workspaceRoot: string; verbose?: string; [key: string]: unknown; }; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/storybook-host-app/src/components/modal/modal-tabs/examples.mdx: -------------------------------------------------------------------------------- ```markdown import { Canvas } from '@storybook/blocks'; import * as ModalStories from '../modal.component.stories'; ## DsModal <Canvas of={ModalStories.Default} /> ## DsModalHeader and DsModalContent <Canvas of={ModalStories.WithTitleAndClose} /> ## DsModalHeaderDrag <Canvas of={ModalStories.WithDragger} /> ## Usage with CdkDialog <Canvas of={ModalStories.WithCdkDialog} /> ## Usage with MatDialog <Canvas of={ModalStories.WithMatDialog} /> ## Other examples <Canvas of={ModalStories.BolderSubtitleThanTitle}></Canvas> <Canvas of={ModalStories.CloseTitleLabelAction} /> <Canvas of={ModalStories.ActionsAndCenteredTitle} /> <Canvas of={ModalStories.BackBtnTitleLabelAction} /> ## All variants <Canvas of={ModalStories.ModalHeaderTypes} /> ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/utils/meta.generator.ts: -------------------------------------------------------------------------------- ```typescript import { basename, extname } from 'node:path'; import type { Meta, TemplateType } from '../../shared/models/types.js'; import { ParsedComponent } from '@push-based/angular-ast-utils'; /** * Generate contract metadata */ export function generateMeta( templatePath: string, parsedComponent: ParsedComponent, isInlineTemplate = false, ): Meta { const componentName = basename(templatePath, extname(templatePath)); const templateType: TemplateType = isInlineTemplate ? 'inline' : 'external'; return { name: parsedComponent.className || componentName, selector: parsedComponent.selector || `.${componentName}`, sourceFile: templatePath, templateType, generatedAt: new Date().toISOString(), hash: '', // Will be filled later }; } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/styles/base/_reset.scss: -------------------------------------------------------------------------------- ```scss /* Modern CSS Reset */ *, *::before, *::after { box-sizing: border-box; } * { margin: 0; } body { line-height: 1.5; -webkit-font-smoothing: antialiased; } img, picture, video, canvas, svg { display: block; max-width: 100%; } input, button, textarea, select { font: inherit; } p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; } #root, #__next { isolation: isolate; } /* Remove default button styles */ button { background: none; border: none; padding: 0; cursor: pointer; } /* Remove default list styles */ ul, ol { list-style: none; padding: 0; } /* Remove default link styles */ a { text-decoration: none; color: inherit; } /* Focus styles */ :focus-visible { outline: 2px solid #007bff; outline-offset: 2px; } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/storybook-host-app/src/components/badge/badge-tabs/examples.mdx: -------------------------------------------------------------------------------- ```markdown import { Canvas } from '@storybook/blocks'; import * as BadgeStories from '../badge.component.stories'; ## Default <Canvas of={BadgeStories.Default} /> ## With Icon ### Only for xsmall and medium badges <Canvas of={BadgeStories.WithIcon} /> ## With Icon Only ### Only for xsmall and medium badges <Canvas of={BadgeStories.WithIconOnly} /> ## With Notification Bubble ### Only for medium badges <Canvas of={BadgeStories.WithNotificationBubble} /> ## With Success ### Only for xsmall and medium badges <Canvas of={BadgeStories.WithSuccess} /> ## With Icon and Success ### Only for xsmall and medium badges <Canvas of={BadgeStories.WithIconAndSuccess} /> ## All Variants <Canvas of={BadgeStories.LargeExamples} withToolbar={false} withSource="none" /> ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/missing-imports.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-missing-imports', standalone: true, template: ` <div> <h2>Missing Imports Test</h2> <input [(ngModel)]="inputValue" placeholder="Two-way binding without FormsModule" /> <div *ngIf="showContent">Conditional content without CommonModule</div> <ul> <li *ngFor="let item of items">{{ item | uppercase }}</li> </ul> <form #myForm="ngForm"> <input name="test" ngModel required /> <button type="submit" [disabled]="myForm.invalid">Submit</button> </form> </div> `, styles: [` div { padding: 10px; } `] }) export class MissingImportsComponent { inputValue = ''; showContent = true; items = ['apple', 'banana', 'cherry']; } ``` -------------------------------------------------------------------------------- /packages/shared/utils/vite.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from 'vite'; import { resolve } from 'path'; // eslint-disable-next-line @nx/enforce-module-boundaries import { createSharedVitestConfig } from '../../../testing/vitest-setup/src/lib/configuration'; const sharedConfig = createSharedVitestConfig({ projectRoot: __dirname, workspaceRoot: '../../../', environment: 'jsdom', }); export default defineConfig(() => ({ root: __dirname, cacheDir: '../../../node_modules/.vite/packages/shared/utils', plugins: [], resolve: { alias: { '@push-based/testing-utils': resolve( __dirname, '../../../testing/utils/src/index.ts', ), }, }, // Uncomment this if you are using workers. // worker: { // plugins: [ nxViteTsPaths() ], // }, test: sharedConfig.test, })); ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/tsconfig.lib.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.base.json", "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", "baseUrl": ".", "rootDir": "src", "outDir": "dist", "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", "emitDeclarationOnly": false, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "types": ["node"] }, "include": ["src/**/*.ts"], "references": [ { "path": "../shared/ds-component-coverage/tsconfig.lib.json" }, { "path": "../shared/styles-ast-utils/tsconfig.lib.json" }, { "path": "../shared/angular-ast-utils/tsconfig.lib.json" }, { "path": "../shared/utils/tsconfig.lib.json" }, { "path": "../shared/models/tsconfig.lib.json" } ] } ``` -------------------------------------------------------------------------------- /packages/shared/utils/src/lib/format-command-log.ts: -------------------------------------------------------------------------------- ```typescript import ansis from 'ansis'; import path from 'node:path'; /** * Formats a command string with optional cwd prefix and ANSI colors. * * @param {string} command - The command to execute. * @param {string[]} args - Array of command arguments. * @param {string} [cwd] - Optional current working directory for the command. * @returns {string} - ANSI-colored formatted command string. */ export function formatCommandLog( command: string, args: string[] = [], cwd: string = process.cwd(), ): string { const relativeDir = path.relative(process.cwd(), cwd); return [ ...(relativeDir && relativeDir !== '.' ? [ansis.italic(ansis.gray(relativeDir))] : []), ansis.yellow('$'), ansis.gray(command), ansis.gray(args.map((arg) => arg).join(' ')), ].join(' '); } ``` -------------------------------------------------------------------------------- /packages/shared/models/ai/FUNCTIONS.md: -------------------------------------------------------------------------------- ```markdown # Public API — Quick Reference | Symbol | Kind | Summary | | -------------------------- | --------- | -------------------------------------------------------- | | `ArgumentValue` | type | CLI argument value types (number, string, boolean, array) | | `CliArgsObject` | type | CLI arguments object with typed values | | `DiagnosticsAware` | interface | Interface for objects that can report issues | | `ToolHandlerContentResult` | type | MCP tool handler content result | | `ToolSchemaOptions` | type | MCP tool schema configuration options | | `ToolsConfig` | type | MCP tools configuration with schema and handler | ``` -------------------------------------------------------------------------------- /packages/shared/utils/tsconfig.lib.json: -------------------------------------------------------------------------------- ```json { "extends": "../../../tsconfig.base.json", "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", "baseUrl": ".", "rootDir": "src", "outDir": "dist", "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", "emitDeclarationOnly": false, "forceConsistentCasingInFileNames": true, "types": ["node"] }, "include": ["src/**/*.ts"], "references": [ { "path": "../../../testing/vitest-setup/tsconfig.lib.json" }, { "path": "../models/tsconfig.lib.json" } ], "exclude": [ "jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "vite.config.ts", "vite.config.mts", "vitest.config.ts", "vitest.config.mts", "src/**/*.test.tsx", "src/**/*.spec.tsx", "src/**/*.test.js", "src/**/*.spec.js", "src/**/*.test.jsx", "src/**/*.spec.jsx" ] } ``` -------------------------------------------------------------------------------- /packages/shared/styles-ast-utils/src/lib/types.ts: -------------------------------------------------------------------------------- ```typescript import { AtRule, Comment, Container, Declaration, Rule } from 'postcss'; export type NodeType<K extends keyof CssAstVisitor> = K extends 'visitRoot' ? Container : K extends 'visitAtRule' ? AtRule : K extends 'visitRule' ? Rule : K extends 'visitDecl' ? Declaration : K extends 'visitComment' ? Comment : never; export interface CssAstVisitor<T = void> { // Called once for the root node visitRoot?: (root: Container) => T; // Called for @rule nodes: @media, @charset, etc. visitAtRule?: (atRule: AtRule) => T; // Called for standard CSS rule nodes: .btn, .box, etc. visitRule?: (rule: Rule) => T; // Called for property declarations: color: red, width: 100px, etc. visitDecl?: (decl: Declaration) => T; // Called for comment nodes: /* some comment */ visitComment?: (comment: Comment) => T; } ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/component-contract/builder/models/types.ts: -------------------------------------------------------------------------------- ```typescript /** * Builder Module Types * Builder-specific result types - core contract types are in shared models/types.ts */ import type { ComponentContract } from '../../shared/models/types.js'; export interface ContractResult { contract: ComponentContract; hash: string; contractFilePath: string; } export interface DecoratorInputMeta { name: string; type?: string; required?: boolean; alias?: string; } export interface DecoratorOutputMeta { name: string; type?: string; alias?: string; } export interface SignalInputMeta { name: string; type?: string; defaultValue?: string; required?: boolean; transform?: string; } export interface SignalOutputMeta { name: string; type?: string; } export interface ExtractedInputsOutputs { inputs: Record<string, DecoratorInputMeta | SignalInputMeta>; outputs: Record<string, DecoratorOutputMeta | SignalOutputMeta>; } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/tsconfig.json: -------------------------------------------------------------------------------- ```json /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { "compileOnSave": false, "compilerOptions": { "outDir": "./dist/out-tsc", "strict": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "skipLibCheck": true, "isolatedModules": true, "esModuleInterop": true, "experimentalDecorators": true, "moduleResolution": "bundler", "importHelpers": true, "target": "ES2022", "module": "ES2022" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": true, "strictInputAccessModifiers": true, "strictTemplates": true } } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/bad-mixed-external-assets.component.html: -------------------------------------------------------------------------------- ```html <!-- ❌ Bad: Using legacy button styles --> <button class="btn btn-primary">Legacy Button</button> <!-- ❌ Bad: Using custom modal structure --> <div class="modal"> <div class="modal-content"> <h2>Legacy Modal</h2> <p>This is a bad example of a modal.</p> </div> </div> <!-- ❌ Bad: Using legacy progress bar --> <div class="progress-bar"> <div class="progress" style="width: 50%"></div> </div> <!-- ❌ Bad: Hardcoded alert styles --> <div class="alert alert-danger">This is a legacy alert!</div> <!-- ❌ Bad: Using a custom dropdown instead of a component --> <select class="dropdown"> <option>Option 1</option> <option>Option 2</option> </select> <!-- ❌ Bad: Using a tooltip div instead of a component --> <div class="tooltip">Hover over me for info</div> <!-- ❌ Bad: Manually created breadcrumb navigation --> <nav class="breadcrumb"> <span>Home</span> / <span>Products</span> / <span>Details</span> </nav> ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-1/bad-mixed-external-assets-1.component.html: -------------------------------------------------------------------------------- ```html <!-- ❌ Bad: Using legacy button styles --> <button class="btn btn-primary">Legacy Button</button> <!-- ❌ Bad: Using custom modal structure --> <div class="modal"> <div class="modal-content"> <h2>Legacy Modal</h2> <p>This is a bad example of a modal.</p> </div> </div> <!-- ❌ Bad: Using legacy progress bar --> <div class="progress-bar"> <div class="progress" style="width: 50%"></div> </div> <!-- ❌ Bad: Hardcoded alert styles --> <div class="alert alert-danger">This is a legacy alert!</div> <!-- ❌ Bad: Using a custom dropdown instead of a component --> <select class="dropdown"> <option>Option 1</option> <option>Option 2</option> </select> <!-- ❌ Bad: Using a tooltip div instead of a component --> <div class="tooltip">Hover over me for info</div> <!-- ❌ Bad: Manually created breadcrumb navigation --> <nav class="breadcrumb"> <span>Home</span> / <span>Products</span> / <span>Details</span> </nav> ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-2/bad-mixed-external-assets-2.component.html: -------------------------------------------------------------------------------- ```html <!-- ❌ Bad: Using legacy button styles --> <button class="btn btn-primary">Legacy Button</button> <!-- ❌ Bad: Using custom modal structure --> <div class="modal"> <div class="modal-content"> <h2>Legacy Modal</h2> <p>This is a bad example of a modal.</p> </div> </div> <!-- ❌ Bad: Using legacy progress bar --> <div class="progress-bar"> <div class="progress" style="width: 50%"></div> </div> <!-- ❌ Bad: Hardcoded alert styles --> <div class="alert alert-danger">This is a legacy alert!</div> <!-- ❌ Bad: Using a custom dropdown instead of a component --> <select class="dropdown"> <option>Option 1</option> <option>Option 2</option> </select> <!-- ❌ Bad: Using a tooltip div instead of a component --> <div class="tooltip">Hover over me for info</div> <!-- ❌ Bad: Manually created breadcrumb navigation --> <nav class="breadcrumb"> <span>Home</span> / <span>Products</span> / <span>Details</span> </nav> ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/refactoring-tests/group-3/bad-mixed-external-assets-3.component.html: -------------------------------------------------------------------------------- ```html <!-- ❌ Bad: Using legacy button styles --> <button class="btn btn-primary">Legacy Button</button> <!-- ❌ Bad: Using custom modal structure --> <div class="modal"> <div class="modal-content"> <h2>Legacy Modal</h2> <p>This is a bad example of a modal.</p> </div> </div> <!-- ❌ Bad: Using legacy progress bar --> <div class="progress-bar"> <div class="progress" style="width: 50%"></div> </div> <!-- ❌ Bad: Hardcoded alert styles --> <div class="alert alert-danger">This is a legacy alert!</div> <!-- ❌ Bad: Using a custom dropdown instead of a component --> <select class="dropdown"> <option>Option 1</option> <option>Option 2</option> </select> <!-- ❌ Bad: Using a tooltip div instead of a component --> <div class="tooltip">Hover over me for info</div> <!-- ❌ Bad: Manually created breadcrumb navigation --> <nav class="breadcrumb"> <span>Home</span> / <span>Products</span> / <span>Details</span> </nav> ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/mocks/fixtures/e2e/demo/src/mixed-external-assets.component.html: -------------------------------------------------------------------------------- ```html <!-- ❌ Bad: Using legacy button styles --> <button class="btn btn-primary">Legacy Button</button> <!-- ❌ Bad: Using custom modal structure --> <div class="modal"> <div class="modal-content"> <h2>Legacy Modal</h2> <p>This is a bad example of a modal.</p> </div> </div> <!-- ❌ Bad: Using legacy progress bar --> <div class="progress-bar"> <div class="progress" style="width: 50%"></div> </div> <!-- ❌ Bad: Hardcoded alert styles --> <div class="alert alert-danger">This is a legacy alert!</div> <!-- ❌ Bad: Using a custom dropdown instead of a component --> <select class="dropdown"> <option>Option 1</option> <option>Option 2</option> </select> <!-- ❌ Bad: Using a tooltip div instead of a component --> <div class="tooltip">Hover over me for info</div> <!-- ❌ Bad: Manually created breadcrumb navigation --> <nav class="breadcrumb"> <span>Home</span> / <span>Products</span> / <span>Details</span> </nav> ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/report-violations/models/types.ts: -------------------------------------------------------------------------------- ```typescript import { BaseViolationOptions, BaseViolationResult, BaseViolationIssue, BaseViolationAudit, } from '../../shared/violation-analysis/types.js'; export interface ReportViolationsOptions extends BaseViolationOptions { groupBy?: 'file' | 'folder'; } export type ViolationResult = BaseViolationResult; export type ViolationIssue = BaseViolationIssue; export type ViolationAudit = BaseViolationAudit; // File-specific types (when groupBy: 'file') export interface FileViolation { fileName: string; message: string; lines: number[]; } export interface FileViolationGroup { message: string; lines: number[]; } export interface FileViolationGroups { [fileName: string]: FileViolationGroup; } // Folder-specific types (when groupBy: 'folder') export interface FolderViolationSummary { violations: number; files: string[]; } export interface FolderViolationGroups { [folderPath: string]: FolderViolationSummary; } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/code-pushup.config.ts: -------------------------------------------------------------------------------- ```typescript import { mergeConfigs } from '@code-pushup/utils'; import { dsComponentUsagePluginCoreConfig } from '../../../../../packages/shared/ds-component-coverage/src/core.config'; import { ssrAdoptionPluginCoreConfig } from '../../../../../src/ssr-adoption/src/core.config'; export default mergeConfigs( { persist: { outputDir: '.code-pushup/packages/application', format: ['json', 'md'], }, plugins: [], }, await ssrAdoptionPluginCoreConfig({ patterns: ['.'], eslintrc: 'plugins/src/ssr-adoption/src/eslint.config.ssr.cjs', }), await dsComponentUsagePluginCoreConfig({ directory: 'plugins/mocks/fixtures/minimal-repo/packages/application/src', dsComponents: [ { componentName: 'DSButton', deprecatedCssClasses: ['btn', 'btn-primary', 'legacy-button'], docsUrl: 'https://storybook.company.com/latest/?path=/docs/components-button--overview', }, ], }) ); ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/ui/rx-host-listener/src/rx-host-listener.ts: -------------------------------------------------------------------------------- ```typescript import { ChangeDetectorRef, ElementRef, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { type Observable, debounceTime, filter, fromEvent, merge, of, switchMap, tap, } from 'rxjs'; export function rxHostListener<T extends Event>(event: string): Observable<T> { const cdr = inject(ChangeDetectorRef); // Listen to event return fromEvent<T>(inject(ElementRef).nativeElement, event).pipe( debounceTime(0), tap(() => cdr.markForCheck()), // Trigger CD like @HostListener would takeUntilDestroyed(), // Unsubscribe ); } /* * @internal **/ export function rxHostPressedListener(): Observable<any> { return merge( rxHostListener('click'), rxHostListener<KeyboardEvent>('keyup').pipe( switchMap((x) => { return x.code === 'Space' || x.code === 'Enter' ? of(true) : of(null); }), filter(Boolean), ), ).pipe(debounceTime(0)); } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/invalid-pipe-usage.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-invalid-pipe', standalone: true, template: ` <div> <h2>Invalid Pipe Usage Test</h2> <p>{{ text | nonExistentPipe }}</p> <div>{{ number | currency | }}</div> <span>{{ date | customPipe: 'param1' : 'param2' }}</span> <p>{{ value | | uppercase }}</p> <div>{{ items | slice:0:3 | unknownPipe }}</div> <span>{{ text | lowercase | missingPipe: }}</span> <p>{{ data | json | customFormat: param1, param2 }}</p> </div> `, styles: [` div { padding: 5px; } `] }) export class InvalidPipeUsageComponent { text = 'Hello World'; number = 123.45; date = new Date(); value = 'test value'; items = [1, 2, 3, 4, 5]; data = { name: 'test', value: 42 }; // Note: nonExistentPipe, customPipe, unknownPipe, missingPipe, customFormat don't exist // Note: malformed pipe syntax with empty pipes and missing parameters } ``` -------------------------------------------------------------------------------- /testing/utils/vite.config.ts: -------------------------------------------------------------------------------- ```typescript /// <reference types='vitest' /> import { defineConfig } from 'vite'; import dts from 'vite-plugin-dts'; import * as path from 'path'; export default defineConfig({ root: __dirname, cacheDir: '../../node_modules/.vite/testing/testing-utils', plugins: [ dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json'), }), ], build: { outDir: './dist', emptyOutDir: true, reportCompressedSize: true, commonjsOptions: { transformMixedEsModules: true, }, lib: { // Could also be a dictionary or array of multiple entry points. entry: 'src/index.ts', name: 'testing-utils', fileName: 'index', // Change this to the formats you want to support. // Don't forget to update your package.json as well. formats: ['es'], }, rollupOptions: { // External packages that should not be bundled into your library. external: [], }, }, }); ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/src/core.config.ts: -------------------------------------------------------------------------------- ```typescript import { CategoryConfig, CoreConfig } from '@code-pushup/models'; import angularDsUsagePlugin, { DsComponentUsagePluginConfig, } from './lib/ds-component-coverage.plugin.js'; import { getAngularDsUsageCategoryRefs } from './lib/utils.js'; export async function dsComponentUsagePluginCoreConfig({ directory, dsComponents, }: DsComponentUsagePluginConfig): Promise<CoreConfig> { return { plugins: [ angularDsUsagePlugin({ directory, dsComponents, }), ], categories: await angularDsUsagePluginCategories({ dsComponents }), } as const satisfies CoreConfig; } export async function angularDsUsagePluginCategories({ dsComponents, }: Pick<DsComponentUsagePluginConfig, 'dsComponents'>): Promise< CategoryConfig[] > { return [ { slug: 'design-system-usage', title: 'Design System Usage', description: 'Usage of design system components', refs: getAngularDsUsageCategoryRefs(dsComponents), }, ]; } ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/index.ts: -------------------------------------------------------------------------------- ```typescript /** * Shared violation analysis utilities * * This module provides shared functionality for analyzing design system violations * across different reporting formats (file and folder-based). */ // Core analysis export { analyzeViolationsBase } from './base-analyzer.js'; // Coverage analysis export { analyzeProjectCoverage, validateReportInput, executeCoveragePlugin, extractComponentName, formatCoverageResult, } from './coverage-analyzer.js'; // Formatting utilities export { filterFailedAudits, createNoViolationsContent, extractIssuesFromAudits, hasViolations, normalizeFilePath, normalizeMessage, groupIssuesByFile, extractUniqueFilePaths, clearPathCache, formatViolations, } from './formatters.js'; // Types export type { BaseViolationOptions, BaseViolationResult, BaseViolationAudit, BaseViolationIssue, ReportCoverageParams, DsComponent, FormattedCoverageResult, FileGroup, FileGroups, PathCache, } from './types.js'; ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/src/lib/ds-component-coverage.plugin.ts: -------------------------------------------------------------------------------- ```typescript import { PluginConfig } from '@code-pushup/models'; import { CreateRunnerConfig, runnerFunction } from './runner/create-runner.js'; import { getAudits } from './utils.js'; import { ANGULAR_DS_USAGE_PLUGIN_SLUG } from './constants.js'; export type DsComponentUsagePluginConfig = CreateRunnerConfig; /** * Plugin to measure and assert usage of DesignSystem components in an Angular project. * It will check if the there are class names in the project that should get replaced by a DesignSystem components. * * @param options */ export function dsComponentCoveragePlugin( options: DsComponentUsagePluginConfig, ): PluginConfig { return { slug: ANGULAR_DS_USAGE_PLUGIN_SLUG, title: 'Angular Design System Coverage', icon: 'angular', description: 'A plugin to measure and assert usage of styles in an Angular project.', audits: getAudits(options.dsComponents), runner: () => runnerFunction(options), } as const; } export default dsComponentCoveragePlugin; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/template-reference-error.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-template-reference-error', standalone: true, template: ` <div> <h2>Template Reference Error Test</h2> <input #inputRef type="text" /> <button (click)="processInput(nonExistentRef.value)">Process</button> <div>{{ undefinedTemplateVar }}</div> <span>{{ someRef.innerText }}</span> <p #paragraphRef>{{ missingMethod(paragraphRef.textContent) }}</p> <div *ngFor="let item of items; let i = index"> {{ item.name }} - {{ j }} - {{ unknownVar }} </div> </div> `, styles: [` div { margin: 8px; } `] }) export class TemplateReferenceErrorComponent { items = [{ name: 'test1' }, { name: 'test2' }]; processInput(value: string): void { console.log('Processing:', value); } // Note: missingMethod is called in template but not defined // Note: nonExistentRef, undefinedTemplateVar, someRef, j, unknownVar are referenced but don't exist } ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/component-contract/index.ts: -------------------------------------------------------------------------------- ```typescript // Export shared types export type { ComponentContract, Meta, PublicApi, PropertyBinding, EventBinding, MethodSignature, ParameterInfo, ImportInfo, Slots, DomStructure, DomElement, Binding, Attribute, Event, StyleDeclarations, StyleRule, DomPathDictionary, TemplateType, } from './shared/models/types.js'; // Export module-specific types that external consumers might need export type { ContractResult } from './builder/models/types.js'; export type { ContractFileInfo } from './list/models/types.js'; // Export utilities and tools export { buildComponentContract } from './builder/utils/build-contract.js'; export { loadContract, saveContract, generateContractSummary, } from './shared/utils/contract-file-ops.js'; export { buildComponentContractTools } from './builder/build-component-contract.tool.js'; export { diffComponentContractTools } from './diff/diff-component-contract.tool.js'; export { listComponentContractsTools } from './list/list-component-contracts.tool.js'; ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/src/lib/runner/schema.ts: -------------------------------------------------------------------------------- ```typescript import { z } from 'zod'; import { ComponentReplacementSchema } from './audits/ds-coverage/schema.js'; export const baseToolsSchema = { inputSchema: { type: 'object' as const, properties: { cwd: z .string({ description: 'The current working directory' }) .optional(), tsconfig: z .string({ description: 'The absolute path to a TypeScript config file', }) .optional(), verbose: z.boolean({ description: 'More verbose output' }).optional(), progress: z.boolean({ description: 'Show progress bar' }).optional(), }, required: [] as string[], }, }; export const ComponentCoverageRunnerOptionsSchema = z.object({ directory: z .string({ description: 'The directory to run the task in' }) .default('.'), dsComponents: z.array(ComponentReplacementSchema, { description: 'Array of components and their deprecated CSS classes', }), }); export type ComponentCoverageRunnerOptions = z.infer< typeof ComponentCoverageRunnerOptionsSchema >; ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/component-usage-graph/models/schema.ts: -------------------------------------------------------------------------------- ```typescript import { ToolSchemaOptions } from '@push-based/models'; export const buildComponentUsageGraphSchema: ToolSchemaOptions = { name: 'build-component-usage-graph', description: 'Maps where given Angular components are imported (modules, specs, templates, styles) so refactors touch every file.', inputSchema: { type: 'object', properties: { directory: { type: 'string', description: 'The relative path to the directory to analyze (starting with "./path/to/dir"). This should be the root directory containing the components with violations.', }, violationFiles: { type: 'array', items: { type: 'string', }, description: 'Array of file paths with design system violations (obtained from previous report-violations calls).', }, }, required: ['directory', 'violationFiles'], }, annotations: { title: 'Build Component Usage Graph', readOnlyHint: true, openWorldHint: false, idempotentHint: true, }, }; ``` -------------------------------------------------------------------------------- /packages/angular-mcp-server/src/lib/tools/ds/report-violations/models/schema.ts: -------------------------------------------------------------------------------- ```typescript import { ToolSchemaOptions } from '@push-based/models'; export const reportViolationsSchema: ToolSchemaOptions = { name: 'report-violations', description: `Report deprecated DS CSS usage in a directory with configurable grouping format.`, inputSchema: { type: 'object', properties: { directory: { type: 'string', description: 'The relative path the directory (starting with "./path/to/dir" avoid big folders.) to run the task in starting from CWD. Respect the OS specifics.', }, componentName: { type: 'string', description: 'The class name of the component to search for (e.g., DsButton)', }, groupBy: { type: 'string', enum: ['file', 'folder'], description: 'How to group the violation results', default: 'file', }, }, required: ['directory', 'componentName'], }, annotations: { title: 'Report Violations', readOnlyHint: true, openWorldHint: false, idempotentHint: false, }, }; ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/type-mismatch.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component } from '@angular/core'; @Component({ selector: 'app-type-mismatch', standalone: true, template: ` <div> <h2>Type Mismatch Test</h2> <input [value]="numberValue" type="text" /> <div [hidden]="stringFlag">Content</div> <button [disabled]="objectValue">Click me</button> <span [title]="arrayValue">Hover me</span> <p>{{ booleanValue.length }}</p> <div>{{ numberValue.toUpperCase() }}</div> <input [(ngModel)]="readonlyValue" /> </div> `, styles: [` div { padding: 10px; } `] }) export class TypeMismatchComponent { numberValue: number = 42; stringFlag: string = 'true'; // Should be boolean for [hidden] objectValue: { name: string } = { name: 'test' }; // Should be boolean for [disabled] arrayValue: string[] = ['a', 'b', 'c']; // Should be string for [title] booleanValue: boolean = true; // Calling .length on boolean readonly readonlyValue: string = 'readonly'; // Two-way binding on readonly // Note: Type mismatches will cause runtime errors and template binding issues } ``` -------------------------------------------------------------------------------- /testing/vitest-setup/src/lib/configuration.ts: -------------------------------------------------------------------------------- ```typescript export interface SharedVitestConfigOptions { projectRoot: string; workspaceRoot: string; environment?: 'node' | 'jsdom' | 'happy-dom'; include?: string[]; exclude?: string[]; testTimeout?: number; } export function createSharedVitestConfig(options: SharedVitestConfigOptions) { const { projectRoot, workspaceRoot, environment = 'node', include = ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], exclude = ['mocks/**', '**/types.ts', '**/__snapshots__/**'], testTimeout = 25_000, } = options; // Simple path joining for Node.js const projectName = projectRoot.split('/').slice(-2).join('-'); // e.g., "shared-utils" const coverageDir = `${workspaceRoot}/coverage/${projectName}`; return { test: { coverage: { provider: 'v8' as const, reporter: ['text', 'lcov'] as const, reportsDirectory: coverageDir, exclude, }, watch: false, globals: true, environment, include, reporters: ['default'] as const, passWithNoTests: true, testTimeout, }, }; } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/wrong-decorator-usage.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component, Input, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-wrong-decorator', standalone: true, template: ` <div> <h2>Wrong Decorator Usage Test</h2> <p>Input value: {{ inputValue }}</p> <button (click)="emitEvent()">Emit Event</button> </div> `, styles: [` div { padding: 12px; } `] }) export class WrongDecoratorUsageComponent { // Wrong: @Input on a method @Input() setInputValue(value: string): void { this.inputValue = value; } // Wrong: @Output on a regular property @Output() regularProperty = 'not an event emitter'; // Wrong: @Input with wrong type @Input() inputValue: EventEmitter<string> = new EventEmitter(); // Wrong: @Output with wrong type @Output() outputValue: string = 'should be EventEmitter'; // Wrong: Multiple decorators on same property @Input() @Output() conflictedProperty = 'has both decorators'; emitEvent(): void { // This will cause runtime error since outputValue is not EventEmitter this.outputValue.emit('test'); } } ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/application/src/app/components/validation-tests/invalid-lifecycle.component.ts: -------------------------------------------------------------------------------- ```typescript import { Component, OnInit, OnDestroy, AfterViewInit } from '@angular/core'; @Component({ selector: 'app-invalid-lifecycle', standalone: true, template: ` <div> <h2>Invalid Lifecycle Test</h2> <p>This component has incorrect lifecycle implementations</p> </div> `, styles: [` div { padding: 15px; } `] }) export class InvalidLifecycleComponent implements OnInit, OnDestroy, AfterViewInit { // Wrong signature - should be ngOnInit(): void ngOnInit(param: string): boolean { console.log('Wrong ngOnInit signature', param); return true; } // Wrong signature - should be ngOnDestroy(): void ngOnDestroy(cleanup: boolean): string { console.log('Wrong ngOnDestroy signature', cleanup); return 'destroyed'; } // Wrong signature - should be ngAfterViewInit(): void ngAfterViewInit(element: HTMLElement): Promise<void> { console.log('Wrong ngAfterViewInit signature', element); return Promise.resolve(); } // Method that doesn't exist in lifecycle ngOnChange(): void { console.log('This should be ngOnChanges'); } } ``` -------------------------------------------------------------------------------- /testing/utils/src/lib/execute-process.mock.mjs: -------------------------------------------------------------------------------- ``` const interval = parseInt(process.argv[2] || 100); let runs = parseInt(process.argv[3] || 4); const throwError = process.argv[4] === '1'; /** * Custom runner implementation that simulates asynchronous situations. * It logs progress to the console with a configurable interval and defaults to 100ms. * The number of runs is also configurable and defaults to 4. * We can decide if the process should error or complete. By default, it completes. * * @arg interval: number - delay between updates in ms; defaults to 100 * @arg runs: number - number of updates; defaults to 4 * @arg throwError: '1' | '0' - if the process completes or throws; defaults to '0' **/ console.info( `process:start with interval: ${interval}, runs: ${runs}, throwError: ${throwError}`, ); await new Promise((resolve) => { const id = setInterval(() => { if (runs === 0) { clearInterval(id); if (throwError) { throw new Error('dummy-error'); } else { resolve('result'); } } else { runs--; console.info('process:update'); } }, interval); }); console.info('process:complete'); ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/src/lib/styles/utils.ts: -------------------------------------------------------------------------------- ```typescript import type { Root } from 'postcss'; import { Issue } from '@code-pushup/models'; import { Asset, ParsedComponent } from '../types.js'; export async function visitComponentStyles<T>( component: ParsedComponent, visitorArgument: T, getIssues: (tokenReplacement: T, asset: Asset<Root>) => Promise<Issue[]>, ): Promise<(Issue & { code?: number })[]> { const { styles, styleUrls, styleUrl } = component; if (styleUrls == null && styles == null && styleUrl == null) { return []; } // Handle inline styles const styleIssues: Issue[] = ( await Promise.all( (styles ?? []).flatMap(async (style: Asset<Root>) => { return getIssues(visitorArgument, style); }), ) ).flat(); const styleUrlsIssues: Issue[] = ( await Promise.all( (styleUrls ?? []).flatMap(async (styleUrl: Asset<Root>) => { return getIssues(visitorArgument, styleUrl); }), ) ).flat(); const styleUrlIssues: Issue[] = styleUrl ? await (async () => { return getIssues(visitorArgument, styleUrl); })() : []; return [...styleIssues, ...styleUrlsIssues, ...styleUrlIssues]; } ```