"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 */ Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = require("fs"); const path_1 = require("path"); const ts = require("typescript"); const decorators_1 = require("./utils/decorators"); const functions_1 = require("./utils/functions"); const line_mappings_1 = require("./utils/line-mappings"); const property_name_1 = require("./utils/property-name"); /** * Collector that can be used to find Angular templates and stylesheets referenced within * given TypeScript source files (inline or external referenced files) */ class ComponentResourceCollector { constructor(typeChecker) { this.typeChecker = typeChecker; this.resolvedTemplates = []; this.resolvedStylesheets = []; } visitNode(node) { if (node.kind === ts.SyntaxKind.ClassDeclaration) { this._visitClassDeclaration(node); } } _visitClassDeclaration(node) { if (!node.decorators || !node.decorators.length) { return; } const ngDecorators = decorators_1.getAngularDecorators(this.typeChecker, node.decorators); const componentDecorator = ngDecorators.find(dec => dec.name === 'Component'); // In case no "@Component" decorator could be found on the current class, skip. if (!componentDecorator) { return; } const decoratorCall = componentDecorator.node.expression; // In case the component decorator call is not valid, skip this class declaration. if (decoratorCall.arguments.length !== 1) { return; } const componentMetadata = functions_1.unwrapExpression(decoratorCall.arguments[0]); // Ensure that the component metadata is an object literal expression. if (!ts.isObjectLiteralExpression(componentMetadata)) { return; } const sourceFile = node.getSourceFile(); const sourceFileName = sourceFile.fileName; // Walk through all component metadata properties and determine the referenced // HTML templates (either external or inline) componentMetadata.properties.forEach(property => { if (!ts.isPropertyAssignment(property)) { return; } const propertyName = property_name_1.getPropertyNameText(property.name); const filePath = path_1.resolve(sourceFileName); if (propertyName === 'styles' && ts.isArrayLiteralExpression(property.initializer)) { property.initializer.elements.forEach(el => { if (ts.isStringLiteralLike(el)) { // Need to add an offset of one to the start because the template quotes are // not part of the template content. const templateStartIdx = el.getStart() + 1; this.resolvedStylesheets.push({ filePath: filePath, container: node, content: el.text, inline: true, start: templateStartIdx, getCharacterAndLineOfPosition: pos => ts.getLineAndCharacterOfPosition(sourceFile, pos + templateStartIdx), }); } }); } // In case there is an inline template specified, ensure that the value is statically // analyzable by checking if the initializer is a string literal-like node. if (propertyName === 'template' && ts.isStringLiteralLike(property.initializer)) { // Need to add an offset of one to the start because the template quotes are // not part of the template content. const templateStartIdx = property.initializer.getStart() + 1; this.resolvedTemplates.push({ filePath: filePath, container: node, content: property.initializer.text, inline: true, start: templateStartIdx, getCharacterAndLineOfPosition: pos => ts.getLineAndCharacterOfPosition(sourceFile, pos + templateStartIdx) }); } if (propertyName === 'styleUrls' && ts.isArrayLiteralExpression(property.initializer)) { property.initializer.elements.forEach(el => { if (ts.isStringLiteralLike(el)) { const stylesheetPath = path_1.resolve(path_1.dirname(sourceFileName), el.text); // In case the stylesheet does not exist in the file system, skip it gracefully. if (!fs_1.existsSync(stylesheetPath)) { return; } this.resolvedStylesheets.push(this.resolveExternalStylesheet(stylesheetPath, node)); } }); } if (propertyName === 'templateUrl' && ts.isStringLiteralLike(property.initializer)) { const templatePath = path_1.resolve(path_1.dirname(sourceFileName), property.initializer.text); // In case the template does not exist in the file system, skip this // external template. if (!fs_1.existsSync(templatePath)) { return; } const fileContent = fs_1.readFileSync(templatePath, 'utf8'); const lineStartsMap = line_mappings_1.computeLineStartsMap(fileContent); this.resolvedTemplates.push({ filePath: templatePath, container: node, content: fileContent, inline: false, start: 0, getCharacterAndLineOfPosition: pos => line_mappings_1.getLineAndCharacterFromPosition(lineStartsMap, pos), }); } }); } /** Resolves an external stylesheet by reading its content and computing line mappings. */ resolveExternalStylesheet(filePath, container) { const fileContent = fs_1.readFileSync(filePath, 'utf8'); const lineStartsMap = line_mappings_1.computeLineStartsMap(fileContent); return { filePath: filePath, container: container, content: fileContent, inline: false, start: 0, getCharacterAndLineOfPosition: pos => line_mappings_1.getLineAndCharacterFromPosition(lineStartsMap, pos), }; } } exports.ComponentResourceCollector = ComponentResourceCollector; //# sourceMappingURL=component-resource-collector.js.map