"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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getResourceUrl = exports.replaceResources = void 0; const ts = __importStar(require("typescript")); const direct_resource_1 = require("../loaders/direct-resource"); const inline_resource_1 = require("../loaders/inline-resource"); function replaceResources(shouldTransform, getTypeChecker, directTemplateLoading = false, inlineStyleMimeType, inlineStyleFileExtension) { if (inlineStyleMimeType && !/^text\/[-.\w]+$/.test(inlineStyleMimeType)) { throw new Error('Invalid inline style MIME type.'); } return (context) => { const typeChecker = getTypeChecker(); const resourceImportDeclarations = []; const moduleKind = context.getCompilerOptions().module; const nodeFactory = context.factory; const visitNode = (node) => { if (ts.isClassDeclaration(node)) { const decorators = ts.visitNodes(node.decorators, (node) => ts.isDecorator(node) ? visitDecorator(nodeFactory, node, typeChecker, directTemplateLoading, resourceImportDeclarations, moduleKind, inlineStyleMimeType, inlineStyleFileExtension) : node); return nodeFactory.updateClassDeclaration(node, decorators, node.modifiers, node.name, node.typeParameters, node.heritageClauses, node.members); } return ts.visitEachChild(node, visitNode, context); }; return (sourceFile) => { if (!shouldTransform(sourceFile.fileName)) { return sourceFile; } const updatedSourceFile = ts.visitNode(sourceFile, visitNode); if (resourceImportDeclarations.length) { // Add resource imports return context.factory.updateSourceFile(updatedSourceFile, ts.setTextRange(context.factory.createNodeArray([ ...resourceImportDeclarations, ...updatedSourceFile.statements, ]), updatedSourceFile.statements)); } return updatedSourceFile; }; }; } exports.replaceResources = replaceResources; function visitDecorator(nodeFactory, node, typeChecker, directTemplateLoading, resourceImportDeclarations, moduleKind, inlineStyleMimeType, inlineStyleFileExtension) { if (!isComponentDecorator(node, typeChecker)) { return node; } if (!ts.isCallExpression(node.expression)) { return node; } const decoratorFactory = node.expression; const args = decoratorFactory.arguments; if (args.length !== 1 || !ts.isObjectLiteralExpression(args[0])) { // Unsupported component metadata return node; } const objectExpression = args[0]; const styleReplacements = []; // visit all properties let properties = ts.visitNodes(objectExpression.properties, (node) => ts.isObjectLiteralElementLike(node) ? visitComponentMetadata(nodeFactory, node, styleReplacements, directTemplateLoading, resourceImportDeclarations, moduleKind, inlineStyleMimeType, inlineStyleFileExtension) : node); // replace properties with updated properties if (styleReplacements.length > 0) { const styleProperty = nodeFactory.createPropertyAssignment(nodeFactory.createIdentifier('styles'), nodeFactory.createArrayLiteralExpression(styleReplacements)); properties = nodeFactory.createNodeArray([...properties, styleProperty]); } return nodeFactory.updateDecorator(node, nodeFactory.updateCallExpression(decoratorFactory, decoratorFactory.expression, decoratorFactory.typeArguments, [nodeFactory.updateObjectLiteralExpression(objectExpression, properties)])); } function visitComponentMetadata(nodeFactory, node, styleReplacements, directTemplateLoading, resourceImportDeclarations, moduleKind, inlineStyleMimeType, inlineStyleFileExtension) { if (!ts.isPropertyAssignment(node) || ts.isComputedPropertyName(node.name)) { return node; } const name = node.name.text; switch (name) { case 'moduleId': return undefined; case 'templateUrl': const url = getResourceUrl(node.initializer, directTemplateLoading ? `!${direct_resource_1.DirectAngularResourceLoaderPath}!` : ''); if (!url) { return node; } const importName = createResourceImport(nodeFactory, url, resourceImportDeclarations, moduleKind); if (!importName) { return node; } return nodeFactory.updatePropertyAssignment(node, nodeFactory.createIdentifier('template'), importName); case 'styles': case 'styleUrls': if (!ts.isArrayLiteralExpression(node.initializer)) { return node; } const isInlineStyle = name === 'styles'; const styles = ts.visitNodes(node.initializer.elements, (node) => { if (!ts.isStringLiteral(node) && !ts.isNoSubstitutionTemplateLiteral(node)) { return node; } let url; if (isInlineStyle) { if (inlineStyleMimeType) { const data = Buffer.from(node.text).toString('base64'); url = `data:${inlineStyleMimeType};charset=utf-8;base64,${data}`; } else if (inlineStyleFileExtension) { const data = Buffer.from(node.text).toString('base64'); const containingFile = node.getSourceFile().fileName; url = `${containingFile}.${inlineStyleFileExtension}!=!${inline_resource_1.InlineAngularResourceLoaderPath}?data=${encodeURIComponent(data)}!${containingFile}`; } else { return nodeFactory.createStringLiteral(node.text); } } else { url = getResourceUrl(node); } if (!url) { return node; } return createResourceImport(nodeFactory, url, resourceImportDeclarations, moduleKind); }); // Styles should be placed first if (isInlineStyle) { styleReplacements.unshift(...styles); } else { styleReplacements.push(...styles); } return undefined; default: return node; } } function getResourceUrl(node, loader = '') { // only analyze strings if (!ts.isStringLiteral(node) && !ts.isNoSubstitutionTemplateLiteral(node)) { return null; } return `${loader}${/^\.?\.\//.test(node.text) ? '' : './'}${node.text}`; } exports.getResourceUrl = getResourceUrl; function isComponentDecorator(node, typeChecker) { if (!ts.isDecorator(node)) { return false; } const origin = getDecoratorOrigin(node, typeChecker); if (origin && origin.module === '@angular/core' && origin.name === 'Component') { return true; } return false; } function createResourceImport(nodeFactory, url, resourceImportDeclarations, moduleKind = ts.ModuleKind.ES2015) { const urlLiteral = nodeFactory.createStringLiteral(url); if (moduleKind < ts.ModuleKind.ES2015) { return nodeFactory.createPropertyAccessExpression(nodeFactory.createCallExpression(nodeFactory.createIdentifier('require'), [], [urlLiteral]), 'default'); } else { const importName = nodeFactory.createIdentifier(`__NG_CLI_RESOURCE__${resourceImportDeclarations.length}`); resourceImportDeclarations.push(nodeFactory.createImportDeclaration(undefined, undefined, nodeFactory.createImportClause(false, importName, undefined), urlLiteral)); return importName; } } function getDecoratorOrigin(decorator, typeChecker) { if (!ts.isCallExpression(decorator.expression)) { return null; } let identifier; let name = ''; if (ts.isPropertyAccessExpression(decorator.expression.expression)) { identifier = decorator.expression.expression.expression; name = decorator.expression.expression.name.text; } else if (ts.isIdentifier(decorator.expression.expression)) { identifier = decorator.expression.expression; } else { return null; } // NOTE: resolver.getReferencedImportDeclaration would work as well but is internal const symbol = typeChecker.getSymbolAtLocation(identifier); if (symbol && symbol.declarations && symbol.declarations.length > 0) { const declaration = symbol.declarations[0]; let module; if (ts.isImportSpecifier(declaration)) { name = (declaration.propertyName || declaration.name).text; module = declaration.parent.parent.parent.moduleSpecifier.text; } else if (ts.isNamespaceImport(declaration)) { // Use the name from the decorator namespace property access module = declaration.parent.parent.moduleSpecifier.text; } else if (ts.isImportClause(declaration)) { name = declaration.name.text; module = declaration.parent.moduleSpecifier.text; } else { return null; } return { name, module }; } return null; }