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
JavaScript
/*
* 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;