"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.io/license */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.findAppConfig = void 0; const path_1 = require("path"); const typescript_1 = __importDefault(require("../../third_party/github.com/Microsoft/TypeScript/lib/typescript")); const util_1 = require("./util"); /** * Resolves the node that defines the app config from a bootstrap call. * @param bootstrapCall Call for which to resolve the config. * @param tree File tree of the project. * @param filePath File path of the bootstrap call. */ function findAppConfig(bootstrapCall, tree, filePath) { if (bootstrapCall.arguments.length > 1) { const config = bootstrapCall.arguments[1]; if (typescript_1.default.isObjectLiteralExpression(config)) { return { filePath, node: config }; } if (typescript_1.default.isIdentifier(config)) { return resolveAppConfigFromIdentifier(config, tree, filePath); } } return null; } exports.findAppConfig = findAppConfig; /** * Resolves the app config from an identifier referring to it. * @param identifier Identifier referring to the app config. * @param tree File tree of the project. * @param bootstapFilePath Path of the bootstrap call. */ function resolveAppConfigFromIdentifier(identifier, tree, bootstapFilePath) { const sourceFile = identifier.getSourceFile(); for (const node of sourceFile.statements) { // Only look at relative imports. This will break if the app uses a path // mapping to refer to the import, but in order to resolve those, we would // need knowledge about the entire program. if (!typescript_1.default.isImportDeclaration(node) || !node.importClause?.namedBindings || !typescript_1.default.isNamedImports(node.importClause.namedBindings) || !typescript_1.default.isStringLiteralLike(node.moduleSpecifier) || !node.moduleSpecifier.text.startsWith('.')) { continue; } for (const specifier of node.importClause.namedBindings.elements) { if (specifier.name.text !== identifier.text) { continue; } // Look for a variable with the imported name in the file. Note that ideally we would use // the type checker to resolve this, but we can't because these utilities are set up to // operate on individual files, not the entire program. const filePath = (0, path_1.join)((0, path_1.dirname)(bootstapFilePath), node.moduleSpecifier.text + '.ts'); const importedSourceFile = (0, util_1.getSourceFile)(tree, filePath); const resolvedVariable = findAppConfigFromVariableName(importedSourceFile, (specifier.propertyName || specifier.name).text); if (resolvedVariable) { return { filePath, node: resolvedVariable }; } } } const variableInSameFile = findAppConfigFromVariableName(sourceFile, identifier.text); return variableInSameFile ? { filePath: bootstapFilePath, node: variableInSameFile } : null; } /** * Finds an app config within the top-level variables of a file. * @param sourceFile File in which to search for the config. * @param variableName Name of the variable containing the config. */ function findAppConfigFromVariableName(sourceFile, variableName) { for (const node of sourceFile.statements) { if (typescript_1.default.isVariableStatement(node)) { for (const decl of node.declarationList.declarations) { if (typescript_1.default.isIdentifier(decl.name) && decl.name.text === variableName && decl.initializer && typescript_1.default.isObjectLiteralExpression(decl.initializer)) { return decl.initializer; } } } } return null; }