@trapi/metadata
Version:
Generate REST-API metadata scheme from TypeScript Decorators.
140 lines • 5.46 kB
JavaScript
"use strict";
/*
* Copyright (c) 2022-2023.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MetadataGenerator = void 0;
const minimatch_1 = require("minimatch");
const typescript_1 = require("typescript");
const cache_1 = require("../../cache");
const decorator_1 = require("../../decorator");
const resolver_1 = require("../../resolver");
const controller_1 = require("../controller");
class MetadataGenerator {
// -------------------------------------------------------------------------
constructor(context) {
this.referenceTypes = {};
this.circularDependencyResolvers = new Array();
this.nodes = [];
this.config = context.options;
this.cache = new cache_1.CacheClient(context.options.cache);
this.decoratorResolver = new decorator_1.DecoratorResolver();
this.program = (0, typescript_1.createProgram)(context.sourceFiles, context.compilerOptions || {});
this.typeChecker = this.program.getTypeChecker();
resolver_1.TypeNodeResolver.clearCache();
}
// -------------------------------------------------------------------------
async generate() {
const sourceFileSize = this.buildNodesFromSourceFiles();
let cache = await this.cache.get(sourceFileSize);
if (!cache) {
if (this.config.decorators) {
this.decoratorResolver.apply(this.config.decorators);
}
if (this.config.preset) {
await this.decoratorResolver.applyPreset(this.config.preset);
}
this.buildControllers();
this.circularDependencyResolvers.forEach((resolve) => resolve(this.referenceTypes));
cache = {
controllers: this.controllers,
referenceTypes: this.referenceTypes,
sourceFilesSize: sourceFileSize,
};
await this.cache.save(cache);
}
return {
controllers: cache.controllers,
referenceTypes: cache.referenceTypes,
};
}
buildNodesFromSourceFiles() {
let endSize = 0;
this.program.getSourceFiles().forEach((sf) => {
if (this.isIgnoredPath(sf.fileName) &&
!this.isAllowedPath(sf.fileName)) {
return;
}
endSize += sf.end;
(0, typescript_1.forEachChild)(sf, (node) => {
if ((0, typescript_1.isModuleDeclaration)(node)) {
/**
* For some reason unknown to me, TS resolves both `declare module` and `namespace` to
* the same kind (`ModuleDeclaration`). In order to figure out whether it's one or the other,
* we check the node flags. They tell us whether it is a namespace or not.
*/
// tslint:disable-next-line:no-bitwise
if ((node.flags & typescript_1.NodeFlags.Namespace) === 0 && node.body && (0, typescript_1.isModuleBlock)(node.body)) {
node.body.statements.forEach((statement) => {
this.nodes.push(statement);
});
return;
}
}
this.nodes.push(node);
});
});
return endSize;
}
// -------------------------------------------------------------------------
/**
* Check if the source file path is in the ignored path list.
*
* @param filePath
* @protected
*/
isIgnoredPath(filePath) {
if (typeof this.config.ignore === 'undefined') {
return false;
}
return this.config.ignore.some((item) => (0, minimatch_1.minimatch)(filePath, item));
}
/**
* Check if the source file path is in the ignored path list.
*
* @param filePath
* @protected
*/
isAllowedPath(filePath) {
if (typeof this.config.allow === 'undefined') {
return false;
}
return this.config.allow.some((item) => (0, minimatch_1.minimatch)(filePath, item));
}
// -------------------------------------------------------------------------
isExportedNode(node) {
return true;
}
// -------------------------------------------------------------------------
addReferenceType(referenceType) {
if (!referenceType.refName) {
return;
}
this.referenceTypes[referenceType.refName] = referenceType;
}
getReferenceType(refName) {
return this.referenceTypes[refName];
}
registerDependencyResolver(callback) {
this.circularDependencyResolvers.push(callback);
}
buildControllers() {
this.controllers = [];
for (let i = 0; i < this.nodes.length; i++) {
const node = this.nodes[i];
if (!(0, typescript_1.isClassDeclaration)(node)) {
continue;
}
const generator = new controller_1.ControllerGenerator(node, this);
if (!generator.isValid()) {
continue;
}
this.controllers.push(generator.generate());
}
}
}
exports.MetadataGenerator = MetadataGenerator;
//# sourceMappingURL=module.js.map