diff --git a/src/cdk/schematics/utils/ast/ng-module-imports.ts b/src/cdk/schematics/utils/ast/ng-module-imports.ts index 03922faff46..41ee271d70c 100644 --- a/src/cdk/schematics/utils/ast/ng-module-imports.ts +++ b/src/cdk/schematics/utils/ast/ng-module-imports.ts @@ -21,19 +21,7 @@ export function hasNgModuleImport(tree: Tree, modulePath: string, className: str const parsedFile = ts.createSourceFile(modulePath, moduleFileContent.toString(), ts.ScriptTarget.Latest, true); - let ngModuleMetadata: ts.ObjectLiteralExpression | null = null; - - const findModuleDecorator = (node: ts.Node) => { - if (ts.isDecorator(node) && ts.isCallExpression(node.expression) && - isNgModuleCallExpression(node.expression)) { - ngModuleMetadata = node.expression.arguments[0] as ts.ObjectLiteralExpression; - return; - } - - ts.forEachChild(node, findModuleDecorator); - }; - - ts.forEachChild(parsedFile, findModuleDecorator); + const ngModuleMetadata = findNgModuleMetadata(parsedFile); if (!ngModuleMetadata) { throw new SchematicsException(`Could not find NgModule declaration inside: "${modulePath}"`); @@ -66,6 +54,29 @@ function resolveIdentifierOfExpression(expression: ts.Expression): ts.Identifier return null; } +/** + * Finds a NgModule declaration within the specified TypeScript node and returns the + * corresponding metadata for it. This function searches breadth first because + * NgModule's are usually not nested within other expressions or declarations. + */ +function findNgModuleMetadata(rootNode: ts.Node): ts.ObjectLiteralExpression | null { + // Add immediate child nodes of the root node to the queue. + const nodeQueue: ts.Node[] = [...rootNode.getChildren()]; + + while (nodeQueue.length) { + const node = nodeQueue.shift()!; + + if (ts.isDecorator(node) && ts.isCallExpression(node.expression) && + isNgModuleCallExpression(node.expression)) { + return node.expression.arguments[0] as ts.ObjectLiteralExpression; + } else { + nodeQueue.push(...node.getChildren()); + } + } + + return null; +} + /** Whether the specified call expression is referring to a NgModule definition. */ function isNgModuleCallExpression(callExpression: ts.CallExpression): boolean { if (!callExpression.arguments.length ||