UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

197 lines 9.29 kB
"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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ComponentResourceCollector = void 0; const path_1 = require("path"); const ts = __importStar(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, _fileSystem) { this.typeChecker = typeChecker; this._fileSystem = _fileSystem; this.resolvedTemplates = []; this.resolvedStylesheets = []; } visitNode(node) { if (node.kind === ts.SyntaxKind.ClassDeclaration) { this._visitClassDeclaration(node); } } _visitClassDeclaration(node) { const decorators = ts.getDecorators(node); if (!decorators || !decorators.length) { return; } const ngDecorators = (0, decorators_1.getAngularDecorators)(this.typeChecker, 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 = (0, 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 filePath = this._fileSystem.resolve(sourceFile.fileName); const sourceFileDirPath = (0, path_1.dirname)(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 = (0, property_name_1.getPropertyNameText)(property.name); if (propertyName === 'styles') { const elements = ts.isArrayLiteralExpression(property.initializer) ? property.initializer.elements : [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; const content = stripBom(el.text); this.resolvedStylesheets.push({ filePath, container: node, content, 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, 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)) { this._trackExternalStylesheet(sourceFileDirPath, el, node); } }); } if (propertyName === 'styleUrl' && ts.isStringLiteralLike(property.initializer)) { this._trackExternalStylesheet(sourceFileDirPath, property.initializer, node); } if (propertyName === 'templateUrl' && ts.isStringLiteralLike(property.initializer)) { const templateUrl = property.initializer.text; const templatePath = this._fileSystem.resolve(sourceFileDirPath, templateUrl); // In case the template does not exist in the file system, skip this // external template. if (!this._fileSystem.fileExists(templatePath)) { return; } const fileContent = stripBom(this._fileSystem.read(templatePath) || ''); if (fileContent) { const lineStartsMap = (0, line_mappings_1.computeLineStartsMap)(fileContent); this.resolvedTemplates.push({ filePath: templatePath, container: node, content: fileContent, inline: false, start: 0, getCharacterAndLineOfPosition: p => (0, line_mappings_1.getLineAndCharacterFromPosition)(lineStartsMap, p), }); } } }); } /** Resolves an external stylesheet by reading its content and computing line mappings. */ resolveExternalStylesheet(filePath, container) { // Strip the BOM to avoid issues with the Sass compiler. See: // https://github.com/angular/components/issues/24227#issuecomment-1200934258 const fileContent = stripBom(this._fileSystem.read(filePath) || ''); if (!fileContent) { return null; } const lineStartsMap = (0, line_mappings_1.computeLineStartsMap)(fileContent); return { filePath: filePath, container: container, content: fileContent, inline: false, start: 0, getCharacterAndLineOfPosition: pos => (0, line_mappings_1.getLineAndCharacterFromPosition)(lineStartsMap, pos), }; } _trackExternalStylesheet(sourceFileDirPath, node, container) { const stylesheetPath = this._fileSystem.resolve(sourceFileDirPath, node.text); const stylesheet = this.resolveExternalStylesheet(stylesheetPath, container); if (stylesheet) { this.resolvedStylesheets.push(stylesheet); } } } exports.ComponentResourceCollector = ComponentResourceCollector; /** Strips the BOM from a string. */ function stripBom(content) { return content.replace(/\uFEFF/g, ''); } //# sourceMappingURL=component-resource-collector.js.map