npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@angular-modernizer/plugin-architecture

v1.2.0

Published

Architecture and code quality analysis rules for the Angular Modernization Platform

Readme

@angular-modernizer/plugin-architecture

Architecture and code quality analysis plugin for the Angular Modernization Platform.

Overview

The plugin provides 17 analysis rules for structural problems in Angular code bases: import cycles, imports across layer boundaries, files in the wrong layer, oversized classes and components, services that mix concerns, and groups of shared code that could become a library. All rules only report; the plugin has no transformation or validation rules. Fixes are done by transform rules in other plugins (for example the service bag split in @angular-modernizer/plugin-angular).

ArchitecturePlugin is the entry point for the kernel. Besides the rules, the package exports building blocks that other plugins and tools reuse: the value-import graph, the module resolver, the layer path matcher, the library extraction scanner and helpers for responsibility patterns and RxJS subscription safety.

Installation

npm install @angular-modernizer/plugin-architecture

The package is an ES module and needs Node.js ^20.19.0 || ^22.12.0 || >=24.0.0. It depends on @angular-modernizer/plugin-system, @angular-modernizer/api, @angular-modernizer/core and ts-morph.

Analysis Rules

| Rule ID | Severity | Description | |-|-|-| | solid:circular-dependency | error | Runtime import cycles (aliases, relative paths, barrels) between project files; cycles with DI or decorator usage are flagged as NG0919 risks | | solid:import-direction-violation | error | Imports against built-in layer conventions (app, features, shared, core, libs, ...) inferred from paths and tsconfig aliases | | architecture:layer-boundary-violation | error | Imports that violate the layer topology configured in layers or layersSource | | architecture:content-placement | warning | Files outside the layers the placement map allows for their content type | | solid:god-object-violation | error | Classes with more than 30 methods, more than 1000 lines, more than 10 dependencies or mixed architectural concerns | | architecture:component-complexity | warning | Components with more than 10 methods, more than 2 concerns or without OnPush | | architecture:service-architecture | warning | Service files that export interfaces, types or enums, and services with more than 5 constructor dependencies | | architecture:service-bag | error | Services and static helpers with 11 or more public methods that mix concerns or backend areas | | architecture:static-in-stateful-class | warning | Public static methods in undecorated classes with instance state | | architecture:library-extraction-opportunity | warning | Cohesive artifact groups in shared folders that could be extracted into Angular libraries | | architecture:model-separation | warning | Files that export models, interfaces, enums, tokens, constants, functions or classes next to implementation code | | architecture:rxjs-memory-leak | warning | Undisposed subscriptions and missing takeUntil patterns | | architecture:advanced-content-placement | warning | Folder structure and domain-driven design patterns | | architecture:error-handling-pattern | warning | Empty or log-only catch blocks, silent catchError, thrown non-Error values, unhandled async errors | | architecture:inheritance-analysis | warning | Deep inheritance chains, god object base classes, composition over inheritance | | architecture:performance-pattern | warning | Performance anti-patterns: complex template getters and expressions, missing trackBy, OnPush or virtual scrolling | | architecture:cross-file-architecture | warning | Architectural dependencies across multiple files and architectural drift |

The first ten rule classes are exported (CircularDependencyRule, ImportDirectionViolationRule, LayerBoundaryViolationRule, ContentPlacementRule, GodObjectRule, ComponentComplexityRule, ServiceArchitectureRule, ServiceBagAnalysisRule, StaticInStatefulClassRule, LibraryExtractionAnalysisRule). The last seven are only available through ArchitecturePlugin.getAnalysisRules().

Three rule IDs use the solid: prefix; the rules belong to this plugin.

Configuration

ArchitecturePlugin.initialize() reads .angular-modernizer.json from the project root of the kernel (the directory of tsConfigPath, otherwise the current working directory). If the file cannot be loaded, the error is logged and all rules run with their defaults and without layers.

Layers

architecture:layer-boundary-violation and architecture:content-placement need a layer topology. Without it both rules report nothing.

{
  "layers": [
    {
      "name": "core",
      "alias": "@core",
      "paths": ["src/app/core/**"],
      "canImportFrom": []
    },
    {
      "name": "shared",
      "alias": "@shared",
      "paths": ["src/app/shared/**"],
      "canImportFrom": ["@core"]
    },
    {
      "name": "features",
      "alias": "@features",
      "paths": ["src/app/features/**"],
      "canImportFrom": ["@core", "@shared"]
    }
  ]
}
  • name: layer name used in messages and in canImportFrom
  • alias: tsconfig path alias of the layer
  • paths: globs relative to the project root. A layer without paths uses the directory its alias maps to in tsconfig paths. The most specific matching pattern wins, independent of the order of the layers.
  • canImportFrom: names or aliases of the layers this layer may import from. An empty array forbids imports from all other layers.

The layer-boundary rule checks every import and export ... from. Specifiers are resolved with TypeScript module resolution (tsconfig paths, baseUrl, relative paths, index.ts); unresolved aliased imports fall back to alias-prefix matching. Type-only imports are reported too and marked with typeOnly: true in the metadata.

Layers from an external source

A project that already defines its layers for an arch-validator script can reference that module instead of repeating it:

{
  "layersSource": {
    "format": "arch-validator",
    "path": "tools/arch-validator/config.js",
    "exportName": "CONFIG"
  }
}

Each layer key becomes name, paths becomes [key + '/**'], pathAlias becomes alias and allowedImports becomes canImportFrom. When both layers and layersSource are set, layersSource wins and the differences are logged and returned by ArchitecturePlugin.getLayerDriftWarnings().

Content placement

architecture:content-placement also needs a placement map: the layers (names or aliases) allowed per content type. Content types: service, component, guard, interceptor, directive, pipe, model, utility.

{
  "rules": {
    "architecture:content-placement": {
      "placement": {
        "service": ["@core", "@shared"],
        "guard": ["@core"],
        "component": ["@shared", "@features"]
      }
    }
  }
}

Barrel files (index.ts, public-api.ts, exports.ts), specs, tests, config files and declaration files are skipped.

Library extraction

architecture:library-extraction-opportunity reads these fields from rules['architecture:library-extraction-opportunity']:

| Field | Default | Meaning | |-|-|-| | minCohesionScore | 0.3 | Minimum cohesion (0-1) of an artifact cluster | | minReusabilityScore | 20 | Minimum reusability score (0-100) of an artifact | | targetFolders | ["shared", "common", "core", "libs"] | Folder names to scan; a path segment matches when it contains a name | | categoryOverrides | {} | Fixed category per class name (ui, forms, layout, utils, data, core, feature) | | excludePatterns | [] | Globs of files to skip; spec and test files are always skipped |

The other fields of the config type (libraryType, outputFolder, libraryPrefix, ...) are used by the library extraction transform, not by this rule.

The scan covers the whole project. The rule runs it once per project and file count and reports each opportunity once, at its primary file.

Other rules

The remaining rules have fixed thresholds and need no configuration. solid:circular-dependency resolves imports with the tsconfig the project was loaded from (else the nearest tsconfig.json) and reads files from disk, so the result does not depend on which files a scan has loaded.

Usage

Via MCP

The scan-architecture MCP tool runs this plugin; scan-all runs it together with the other analysis plugins.

{
  "rootPath": "/path/to/angular/project",
  "rules": ["solid:circular-dependency", "architecture:layer-boundary-violation"]
}

Without rules, all 17 rules run.

With the Kernel

import path from 'node:path';
import { Kernel } from '@angular-modernizer/core';
import { createPublicApi } from '@angular-modernizer/api';
import { ContextFactory } from '@angular-modernizer/plugin-system';
import { ArchitecturePlugin } from '@angular-modernizer/plugin-architecture';

const kernel = new Kernel({
  tsConfigPath: path.join(projectPath, 'tsconfig.json'),
  plugins: [new ArchitecturePlugin()],
});
await kernel.initialize();

const project = kernel.getProject();
const api = createPublicApi(project);
const rules = kernel
  .getPlugin('@angular-modernizer/plugin-architecture')!
  .getAnalysisRules();

for (const sourceFile of project.getSourceFiles()) {
  const context = ContextFactory.createAnalysisContext({
    sourceFile,
    project,
    api,
  });
  for (const rule of rules) {
    const results = await rule.analyze(context);
    results.forEach((r) => console.info(`${r.ruleId} ${r.filePath}:${r.line}: ${r.message}`));
  }
}

Single rule

import { LayerBoundaryViolationRule } from '@angular-modernizer/plugin-architecture';

const rule = new LayerBoundaryViolationRule(layers, { projectRoot });
const results = await rule.analyze(context);

Commands

pnpm --filter @angular-modernizer/plugin-architecture build
pnpm --filter @angular-modernizer/plugin-architecture test

Related Packages