angular/components · error · SchematicsException

Could not find NgModule declaration inside: "${modulePath}"

Error message

Could not find NgModule declaration inside: "${modulePath}"

What it means

The module file was read and parsed, but findNgModuleMetadata could not locate an @NgModule(...) decorator expression in it, so hasNgModuleImport throws this SchematicsException. The file exists but is not a NgModule declaration file.

Source

Thrown at src/cdk/schematics/utils/ast/ng-module-imports.ts:31

 * Whether the Angular module in the given path imports the specified module class name.
 */
export function hasNgModuleImport(tree: Tree, modulePath: string, className: string): boolean {
  const moduleFileContent = tree.read(modulePath);

  if (!moduleFileContent) {
    throw new SchematicsException(`Could not read Angular module file: ${modulePath}`);
  }

  const parsedFile = ts.createSourceFile(
    modulePath,
    moduleFileContent.toString(),
    ts.ScriptTarget.Latest,
    true,
  );
  const ngModuleMetadata = findNgModuleMetadata(parsedFile);

  if (!ngModuleMetadata) {
    throw new SchematicsException(`Could not find NgModule declaration inside: "${modulePath}"`);
  }

  for (let property of ngModuleMetadata!.properties) {
    if (
      !ts.isPropertyAssignment(property) ||
      property.name.getText() !== 'imports' ||
      !ts.isArrayLiteralExpression(property.initializer)
    ) {
      continue;
    }

    if (property.initializer.elements.some(element => element.getText() === className)) {
      return true;
    }
  }

  return false;
}

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Pass the file that actually contains @NgModule({ imports: [...] }); search the project for '@NgModule' if unsure.
  2. If the app is standalone, replace the NgModule check: add the module's exports to the component's imports array and skip hasNgModuleImport.
  3. Ensure @NgModule is imported from '@angular/core' in the target file (aliased decorators won't be found).
  4. Use the project's root module path from angular.json/workspace config instead of a hardcoded guess.

Example fix

// before
hasNgModuleImport(tree, 'src/app/index.ts', 'MatDialogModule');
// after
hasNgModuleImport(tree, 'src/app/app.module.ts', 'MatDialogModule'); // file containing @NgModule
Defensive patterns

Strategy: type-guard

Validate before calling

import * as ts from 'typescript';
function containsNgModule(content: string): boolean {
  return /@NgModule\s*\(/.test(content);
}
const content = tree.read(modulePath)?.toString('utf-8') ?? '';
if (!containsNgModule(content)) {
  throw new Error(`${modulePath} has no @NgModule; pick the root module`);
}

Type guard

function isNgModuleFile(tree: Tree, modulePath: string): boolean {
  const content = tree.exists(modulePath) ? tree.read(modulePath)!.toString('utf-8') : '';
  return /@NgModule\s*\(/.test(content);
}

Try / catch

try {
  hasNgModuleImport(tree, modulePath, className);
} catch (e) {
  if (e instanceof SchematicsException && e.message.includes('Could not find NgModule declaration')) {
    console.warn(`${modulePath} is not an NgModule (standalone app or wrong file)`);
    return false;
  }
  throw e;
}

Prevention

When it happens

Trigger: hasNgModuleImport(tree, modulePath, className) pointed at a TS file that has no @NgModule decorator: a standalone component/directive file, a barrel index.ts, a module with a typo'd decorator import, or a file where @NgModule is aliased.

Common situations: Migrating to Angular 14+ standalone APIs where app.module.ts was deleted in favor of main.ts bootstrapApplication, but older schematic setup rules still reference it; accidentally passing a routing.module.ts that only declares routes; passing index.ts re-export files.

Related errors


AI-assisted analysis of angular/components@0411926e7d (2026-08-31). Data as JSON: /api/errors/9f9320ca533f8835. Report an issue: GitHub.