@angular/cdk
Version:
Angular Material Component Development Kit
197 lines • 9.29 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 __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