Framework detection and project structure analysis plugin for the Angular Modernization Platform.
The @angular-modernizer/plugin-analyzer package provides general analysis rules for Angular projects. It currently ships one rule, the framework detector. The analyze-project MCP tool runs this plugin (registered under the name analyzer).
This plugin follows the plugin contract from @angular-modernizer/plugin-system. The framework detector needs no PublicApi tool; it reads the project and its file system from the context:
class FrameworkDetectorRule implements AnalysisRule {
async analyze(context: AnalysisContext): Promise<AnalysisResult[]> {
const { project, filePath } = context;
// checks nx.json and angular.json in the project root
}
}
Rule ID: analyzer:framework-detector
Detects whether a project uses Angular CLI or Nx from the configuration files in the project root (the directory of the tsconfig, otherwise the directory of the analyzed file).
Detection Priority:
nx.json presentangular.json presentExample Output:
[
{
"ruleId": "analyzer:framework-detector",
"message": "Project uses Angular CLI",
"filePath": "/path/to/project/src/main.ts",
"metadata": {
"framework": "Angular CLI",
"hasNxConfig": false,
"hasAngularConfig": true,
"projectRoot": "/path/to/project"
}
}
]
framework is Nx, Angular CLI or Unknown.
import { Kernel, RealFileSystemAdapter } from '@angular-modernizer/core';
import { createPublicApi } from '@angular-modernizer/api';
import { ContextFactory } from '@angular-modernizer/plugin-system';
import { AnalyzerPlugin } from '@angular-modernizer/plugin-analyzer';
const kernel = new Kernel({
tsConfigPath: './tsconfig.json',
fileSystem: new RealFileSystemAdapter(),
plugins: [new AnalyzerPlugin()],
});
await kernel.initialize();
const plugin = kernel.getPlugin('analyzer')!;
const frameworkRule = plugin
.getAnalysisRules()
.find((r) => r.id === 'analyzer:framework-detector')!;
const project = kernel.getProject();
const sourceFile = project.getSourceFiles()[0]!;
const context = ContextFactory.createAnalysisContext({
sourceFile,
project,
api: createPublicApi(project),
});
const [result] = await frameworkRule.analyze(context);
console.log('Framework detected:', result?.metadata?.framework);
The analyze-project MCP tool creates a kernel with AnalyzerPlugin and runs its rules over the project files; with includeDetails the rule findings are returned in results. Framework, architecture and technology stack in the tool result are detected by the tool itself:
const analysis = await callTool('analyze-project', {
rootPath: '/path/to/angular/project',
includeDetails: true,
});
// structuredContent (abridged)
{
"framework": { "detected": true, "type": "...", "confidence": 0.9, "indicators": ["..."] },
"architecture": { "pattern": "...", "confidence": 0.8, "layers": [] },
"technologyStack": { "languages": [], "frameworks": [], "buildTools": [], "packageManager": "pnpm" },
"results": [{ "ruleId": "analyzer:framework-detector", "message": "...", "filePath": "..." }]
}
packages/plugin-analyzer/
src/
rules/
framework-detector.rule.ts
index.ts # AnalyzerPlugin, FrameworkDetectorRule
__tests__/
framework-detector.rule.test.ts
fixtures/
package.json
tsconfig.json
jest.config.js
README.md
pnpm --filter @angular-modernizer/plugin-analyzer test
pnpm --filter @angular-modernizer/plugin-analyzer test -- --coverage
__tests__/fixtures/
sample-angular-cli-app/
angular.json
tsconfig.json
src/main.ts
sample-nx-app/
nx.json
tsconfig.json
src/main.ts
sample-unknown-app/
tsconfig.json
src/main.ts
sample-app/
legacy.component.ts
legacy.directive.ts
modern.component.ts
import { Kernel } from '@angular-modernizer/core';
import { ContextFactory } from '@angular-modernizer/plugin-system';
import { AnalyzerPlugin } from '../src/index.js';
import { resolve } from 'path';
describe('FrameworkDetectorRule', () => {
it('should detect Angular CLI project', async () => {
const plugin = new AnalyzerPlugin();
const kernel = new Kernel({
tsConfigPath: resolve(__dirname, './fixtures/sample-angular-cli-app/tsconfig.json'),
plugins: [plugin],
});
await kernel.initialize();
const project = kernel.getProject();
const rule = plugin
.getAnalysisRules()
.find((r) => r.id === 'analyzer:framework-detector')!;
const context = ContextFactory.createAnalysisContext({
sourceFile: project.getSourceFiles()[0]!,
project,
api: {},
});
const [result] = await rule.analyze(context);
expect(result?.metadata?.framework).toBe('Angular CLI');
});
});
@angular-modernizer/api - Public API with analysis tools@angular-modernizer/core - Kernel and infrastructure@angular-modernizer/plugin-system - Plugin contracts and contextsWhen adding new analysis rules:
AnalysisRule interface: implement all required membersPublicApi tools: do not implement low-level AST logic directly// 1. Implement the rule in src/rules/
class MyAnalysisRule implements AnalysisRule {
readonly id = 'analyzer:my-analysis';
readonly name = 'My Analysis Rule';
readonly description = 'Analyzes something specific';
readonly severity = 'info' as const;
readonly category = 'project-analysis';
async analyze(context: AnalysisContext): Promise<AnalysisResult[]> {
const { api, sourceFile, filePath } = context;
const usesInject = api.transformation.importManager.hasImport(sourceFile, 'inject', '@angular/core');
return [{ ruleId: this.id, message: `inject imported: ${usesInject}`, filePath }];
}
}
// 2. Add to the plugin (src/index.ts)
class AnalyzerPlugin implements Plugin {
getAnalysisRules(): AnalysisRule[] {
return [this.frameworkDetectorRule, new MyAnalysisRule()];
}
}
Project-level analysis plugin for the Angular Modernizer.
AnalyzerPlugin registers FrameworkDetectorRule, which reports whether the project is an Angular CLI or an Nx workspace. The rules of this plugin only report; they never modify code.