@schematics/angular
Version:
Schematics specific to Angular
110 lines (109 loc) • 5.06 kB
JavaScript
"use strict";
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveBootstrappedComponentData = resolveBootstrappedComponentData;
const schematics_1 = require("@angular-devkit/schematics");
const typescript_1 = __importDefault(require("../../third_party/github.com/Microsoft/TypeScript/lib/typescript"));
const ast_utils_1 = require("../ast-utils");
const ng_ast_utils_1 = require("../ng-ast-utils");
const util_1 = require("./util");
/**
* Finds the original name and path relative to the `main.ts` of the bootrstrapped app component.
* @param tree File tree in which to look for the component.
* @param mainFilePath Path of the `main` file.
*/
function resolveBootstrappedComponentData(tree, mainFilePath) {
// First try to resolve for a standalone app.
try {
const call = (0, util_1.findBootstrapApplicationCall)(tree, mainFilePath);
if (call.arguments.length > 0 && typescript_1.default.isIdentifier(call.arguments[0])) {
const resolved = resolveIdentifier(call.arguments[0]);
if (resolved) {
return {
componentName: resolved.name,
componentImportPathInSameFile: resolved.path,
moduleName: null,
moduleImportPathInSameFile: null,
};
}
}
}
catch (e) {
// `findBootstrapApplicationCall` will throw if it can't find the `bootrstrapApplication` call.
// Catch so we can continue to the fallback logic.
if (!(e instanceof schematics_1.SchematicsException)) {
throw e;
}
}
// Otherwise fall back to resolving an NgModule-based app.
return resolveNgModuleBasedData(tree, mainFilePath);
}
/** Resolves the bootstrap data for a NgModule-based app. */
function resolveNgModuleBasedData(tree, mainFilePath) {
const appModulePath = (0, ng_ast_utils_1.getAppModulePath)(tree, mainFilePath);
const appModuleFile = (0, util_1.getSourceFile)(tree, appModulePath);
const metadataNodes = (0, ast_utils_1.getDecoratorMetadata)(appModuleFile, 'NgModule', '@angular/core');
for (const node of metadataNodes) {
if (!typescript_1.default.isObjectLiteralExpression(node)) {
continue;
}
const bootstrapProp = (0, ast_utils_1.getMetadataField)(node, 'bootstrap').find((prop) => {
return (typescript_1.default.isArrayLiteralExpression(prop.initializer) &&
prop.initializer.elements.length > 0 &&
typescript_1.default.isIdentifier(prop.initializer.elements[0]));
});
const componentIdentifier = (bootstrapProp?.initializer)
.elements[0];
const componentResult = componentIdentifier ? resolveIdentifier(componentIdentifier) : null;
const bootstrapCall = (0, ng_ast_utils_1.findBootstrapModuleCall)(tree, mainFilePath);
if (componentResult &&
bootstrapCall &&
bootstrapCall.arguments.length > 0 &&
typescript_1.default.isIdentifier(bootstrapCall.arguments[0])) {
const moduleResult = resolveIdentifier(bootstrapCall.arguments[0]);
if (moduleResult) {
return {
componentName: componentResult.name,
componentImportPathInSameFile: componentResult.path,
moduleName: moduleResult.name,
moduleImportPathInSameFile: moduleResult.path,
};
}
}
}
return null;
}
/** Resolves an identifier to its original name and path that it was imported from. */
function resolveIdentifier(identifier) {
const sourceFile = identifier.getSourceFile();
// Try to resolve the import path by looking at the top-level named imports of the file.
for (const node of sourceFile.statements) {
if (!typescript_1.default.isImportDeclaration(node) ||
!typescript_1.default.isStringLiteral(node.moduleSpecifier) ||
!node.importClause ||
!node.importClause.namedBindings ||
!typescript_1.default.isNamedImports(node.importClause.namedBindings)) {
continue;
}
for (const element of node.importClause.namedBindings.elements) {
if (element.name.text === identifier.text) {
return {
// Note that we use `propertyName` if available, because it contains
// the real name in the case where the import is aliased.
name: (element.propertyName || element.name).text,
path: node.moduleSpecifier.text,
};
}
}
}
return null;
}