UNPKG

dipend

Version:

This library implements a dependency injection (DI) system in JavaScript/TypeScript, making it easier to manage dependencies in modular applications.

103 lines (101 loc) 6.45 kB
/* * Copyright 2025 Saulo V. Alvarenga. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AddClassConstructorMetadataTransform = void 0; const tslib_1 = require("tslib"); const typescript_1 = tslib_1.__importDefault(require("typescript")); const base_transform_1 = require("./base-transform"); class AddClassConstructorMetadataTransform extends base_transform_1.BaseTransform { getConstructorParameterTypes(node) { return node.members.filter(this.tsInstance.isConstructorDeclaration).flatMap((constructor) => constructor.parameters .filter((param) => param.type) .map((param) => { const type = this.typeChecker.getTypeAtLocation(param.type); const typeSymbol = type.getSymbol(); const typeSymbolDeclarations = typeSymbol?.declarations || []; if (typeSymbolDeclarations.length === 0) return null; const declaration = typeSymbolDeclarations[0]; return this.tsInstance.isInterfaceDeclaration(declaration) ? this.interfaces.get(declaration) || null : this.tsInstance.isClassDeclaration(declaration) && declaration.name ? declaration.name : null; }) .filter(Boolean)); } findSymbolMetadataProperty(classMembers) { return classMembers.find((member) => this.tsInstance.isPropertyDeclaration(member) && this.tsInstance.isComputedPropertyName(member.name) && this.tsInstance.isPropertyAccessExpression(member.name.expression) && member.name.expression.getText() === "Symbol.metadata"); } createSymbolMetadataProperty() { return this.tsFactory.createPropertyDeclaration([this.tsFactory.createModifier(this.tsInstance.SyntaxKind.StaticKeyword)], this.tsFactory.createComputedPropertyName(this.tsFactory.createPropertyAccessExpression(this.tsFactory.createIdentifier("Symbol"), "metadata")), undefined, this.tsFactory.createKeywordTypeNode(typescript_1.default.SyntaxKind.AnyKeyword), this.tsFactory.createObjectLiteralExpression()); } diConstructorMetadataExists(property) { if (this.tsInstance.isPropertyAssignment(property) && this.tsInstance.isComputedPropertyName(property.name) && this.tsInstance.isCallExpression(property.name.expression)) { const callExpr = property.name.expression; if (this.tsInstance.isPropertyAccessExpression(callExpr.expression) && this.tsInstance.isIdentifier(callExpr.expression.expression) && callExpr.expression.expression.text === "Symbol" && callExpr.expression.name.text === "for") { const arg = callExpr.arguments[0]; return this.tsInstance.isStringLiteral(arg) && arg.text === "di:constructor:param_types"; } } return false; } removeDIConstructorMetadataPropertyIfExists(symbolMetadataPropertyInitializer) { const updatedProperties = symbolMetadataPropertyInitializer.properties.filter((prop) => !this.diConstructorMetadataExists(prop)); return this.tsFactory.updateObjectLiteralExpression(symbolMetadataPropertyInitializer, updatedProperties); } createDIConstructorMetadata(constructorParamArray) { return this.tsFactory.createPropertyAssignment(this.tsFactory.createComputedPropertyName(this.tsFactory.createCallExpression(this.tsFactory.createPropertyAccessExpression(this.tsFactory.createIdentifier("Symbol"), "for"), undefined, [this.tsFactory.createStringLiteral("di:constructor:param_types")])), this.tsFactory.createArrayLiteralExpression(constructorParamArray)); } createDIConstructorMetadataProperty(classMembers, symbolMetadataProperty, symbolMetadataPropertyInitializer, constructorParamArray) { if (!this.tsInstance.isObjectLiteralExpression(symbolMetadataPropertyInitializer)) throw new Error("Symbol.metadata property must be an object."); const updatedObjectLiteralExpression = this.tsFactory.updateObjectLiteralExpression(symbolMetadataPropertyInitializer, [ ...this.removeDIConstructorMetadataPropertyIfExists(symbolMetadataPropertyInitializer).properties, this.createDIConstructorMetadata(constructorParamArray), ]); const updatedSymbolMetadataProperty = this.tsFactory.updatePropertyDeclaration(symbolMetadataProperty, symbolMetadataProperty.modifiers, symbolMetadataProperty.name, symbolMetadataProperty.questionToken, symbolMetadataProperty.type, updatedObjectLiteralExpression); const index = classMembers.indexOf(symbolMetadataProperty); if (index !== -1) classMembers[index] = updatedSymbolMetadataProperty; } addClassConstructorMetadataToClass(node, constructorParamArray) { const classMembers = Array.from(node.members); let symbolMetadataProperty = this.findSymbolMetadataProperty(classMembers); if (symbolMetadataProperty?.initializer === undefined) { symbolMetadataProperty = this.createSymbolMetadataProperty(); classMembers.push(symbolMetadataProperty); } this.createDIConstructorMetadataProperty(classMembers, symbolMetadataProperty, symbolMetadataProperty.initializer, constructorParamArray); return this.tsFactory.updateClassDeclaration(node, node.modifiers, node.name, node.typeParameters, node.heritageClauses, classMembers); } execute(node) { if (!this.tsInstance.isClassDeclaration(node)) return; const constructorParamArray = this.getConstructorParameterTypes(node); return this.addClassConstructorMetadataToClass(node, constructorParamArray); } } exports.AddClassConstructorMetadataTransform = AddClassConstructorMetadataTransform;