UNPKG

@nestia/sdk

Version:

Nestia SDK and Swagger generator

259 lines 12.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NestiaSdkApplication = void 0; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const tstl_1 = require("tstl"); const AccessorAnalyzer_1 = require("./analyses/AccessorAnalyzer"); const ConfigAnalyzer_1 = require("./analyses/ConfigAnalyzer"); const PathAnalyzer_1 = require("./analyses/PathAnalyzer"); const ReflectControllerAnalyzer_1 = require("./analyses/ReflectControllerAnalyzer"); const TypedHttpRouteAnalyzer_1 = require("./analyses/TypedHttpRouteAnalyzer"); const TypedWebSocketRouteAnalyzer_1 = require("./analyses/TypedWebSocketRouteAnalyzer"); const E2eGenerator_1 = require("./generates/E2eGenerator"); const SdkGenerator_1 = require("./generates/SdkGenerator"); const SwaggerGenerator_1 = require("./generates/SwaggerGenerator"); const IReflectOperationError_1 = require("./structures/IReflectOperationError"); const StringUtil_1 = require("./utils/StringUtil"); const VersioningStrategy_1 = require("./utils/VersioningStrategy"); class NestiaSdkApplication { constructor(config) { this.config = config; } all() { return __awaiter(this, void 0, void 0, function* () { var _a; if (!this.config.output && !((_a = this.config.swagger) === null || _a === void 0 ? void 0 : _a.output)) throw new Error([ "Error on NestiaApplication.all(): nothing to generate, configure at least one property of below:", "", " - INestiaConfig.output", " - INestiaConfig.swagger.output", " - INestiaConfig.openai.output", ].join("\n")); print_title("Nestia All Generator"); yield this.generate({ generate: (app) => __awaiter(this, void 0, void 0, function* () { if (this.config.output) { yield SdkGenerator_1.SdkGenerator.generate(app); if (this.config.e2e) yield E2eGenerator_1.E2eGenerator.generate(app); } if (this.config.swagger) yield SwaggerGenerator_1.SwaggerGenerator.generate(app); }), validate: this.config.output ? SdkGenerator_1.SdkGenerator.validate : undefined, }); }); } e2e() { return __awaiter(this, void 0, void 0, function* () { if (!this.config.output) throw new Error("Error on NestiaApplication.e2e(): configure INestiaConfig.output property."); else if (!this.config.e2e) throw new Error("Error on NestiaApplication.e2e(): configure INestiaConfig.e2e property."); const validate = (title) => (location) => __awaiter(this, void 0, void 0, function* () { const parent = path_1.default.resolve(location + "/.."); const stats = yield fs_1.default.promises.lstat(parent); if (stats.isDirectory() === false) throw new Error(`Error on NestiaApplication.e2e(): output directory of ${title} does not exists.`); }); yield validate("sdk")(this.config.output); yield validate("e2e")(this.config.e2e); print_title("Nestia E2E Generator"); yield this.generate({ generate: (app) => __awaiter(this, void 0, void 0, function* () { yield SdkGenerator_1.SdkGenerator.generate(app); yield E2eGenerator_1.E2eGenerator.generate(app); }), }); }); } sdk() { return __awaiter(this, void 0, void 0, function* () { if (!this.config.output) throw new Error("Error on NestiaApplication.sdk(): configure INestiaConfig.output property."); const parent = path_1.default.resolve(this.config.output + "/.."); const stats = yield fs_1.default.promises.lstat(parent); if (stats.isDirectory() === false) throw new Error("Error on NestiaApplication.sdk(): output directory does not exists."); print_title("Nestia SDK Generator"); yield this.generate({ generate: SdkGenerator_1.SdkGenerator.generate, validate: SdkGenerator_1.SdkGenerator.validate, }); }); } swagger() { return __awaiter(this, void 0, void 0, function* () { var _a; if (!((_a = this.config.swagger) === null || _a === void 0 ? void 0 : _a.output)) throw new Error(`Error on NestiaApplication.swagger(): configure INestiaConfig.swagger property.`); const parsed = path_1.default.parse(this.config.swagger.output); const directory = !!parsed.ext ? path_1.default.resolve(parsed.dir) : this.config.swagger.output; const stats = yield fs_1.default.promises.lstat(directory); if (stats.isDirectory() === false) throw new Error("Error on NestiaApplication.swagger(): output directory does not exists."); print_title("Nestia Swagger Generator"); yield this.generate({ generate: SwaggerGenerator_1.SwaggerGenerator.generate, }); }); } generate(props) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d; //---- // ANALYZE REFLECTS //---- const unique = new WeakSet(); const project = { config: this.config, input: yield ConfigAnalyzer_1.ConfigAnalyzer.input(this.config), checker: null, errors: [], warnings: [], }; console.log("Analyzing reflections"); const controllers = project.input.controllers .map((c) => ReflectControllerAnalyzer_1.ReflectControllerAnalyzer.analyze({ project, controller: c, unique })) .filter((c) => c !== null); if (project.warnings.length) report({ type: "warning", errors: project.warnings, }); if (project.errors.length) return report({ type: "error", errors: project.errors, }); const agg = (() => { const set = new tstl_1.HashSet(); for (const controller of controllers) for (const controllerPath of controller.paths) for (const operation of controller.operations) for (const operationPath of operation.paths) set.insert(new tstl_1.Pair(`${controllerPath}/${operationPath}`, operation.protocol === "http" ? operation.method : "")); return set.size(); })(); console.log(` - controllers: #${controllers.length}`); console.log(` - paths: #${agg}`); console.log(` - routes: #${controllers .map((c) => c.paths.length * c.operations.map((f) => f.paths.length).reduce((a, b) => a + b, 0)) .reduce((a, b) => a + b, 0)}`); //---- // ANALYZE TYPESCRIPT CODE //---- console.log("Analyzing source codes"); // METADATA COMPONENTS const collection = TypedHttpRouteAnalyzer_1.TypedHttpRouteAnalyzer.dictionary(controllers); // CONVERT TO TYPED OPERATIONS const globalPrefix = (_b = (_a = project.input.globalPrefix) === null || _a === void 0 ? void 0 : _a.prefix) !== null && _b !== void 0 ? _b : ""; const routes = []; for (const c of controllers) for (const o of c.operations) { const pathList = new Set(); const versions = VersioningStrategy_1.VersioningStrategy.merge(project)([ ...((_c = c.versions) !== null && _c !== void 0 ? _c : []), ...((_d = o.versions) !== null && _d !== void 0 ? _d : []), ]); for (const v of versions) for (const prefix of wrapPaths(c.prefixes)) for (const cPath of wrapPaths(c.paths)) for (const filePath of wrapPaths(o.paths)) pathList.add(PathAnalyzer_1.PathAnalyzer.join(globalPrefix, v, prefix, cPath, filePath)); if (o.protocol === "http") routes.push(...TypedHttpRouteAnalyzer_1.TypedHttpRouteAnalyzer.analyze({ controller: c, errors: project.errors, dictionary: collection, operation: o, paths: Array.from(pathList), })); else if (o.protocol === "websocket") routes.push(...TypedWebSocketRouteAnalyzer_1.TypedWebSocketRouteAnalyzer.analyze({ controller: c, operation: o, paths: Array.from(pathList), })); } AccessorAnalyzer_1.AccessorAnalyzer.analyze(routes); if (props.validate !== undefined) project.errors.push(...props.validate({ project, collection, routes, })); if (project.errors.length) return report({ type: "error", errors: project.errors, }); yield props.generate({ project, collection, routes, }); }); } } exports.NestiaSdkApplication = NestiaSdkApplication; const print_title = (str) => { console.log("-----------------------------------------------------------"); console.log(` ${str}`); console.log("-----------------------------------------------------------"); }; const report = (props) => { const map = new tstl_1.TreeMap(); for (const e of props.errors) map.take(new IReflectOperationError_1.IReflectOperationError.Key(e), () => []).push(...e.contents); console.log(""); print_title(`Nestia ${StringUtil_1.StringUtil.capitalize(props.type)} Report`); for (const { first: { error }, second: contents, } of map) { if (error.contents.length === 0) continue; const location = path_1.default.relative(process.cwd(), error.file); const message = [ `${location} - `, error.class, ...(error.function !== null ? [`.${error.function}()`] : [""]), ...(error.from !== null ? [` from ${error.from}`] : [""]), ":\n", contents .map((c) => { if (typeof c === "string") return ` - ${c}`; else return [ c.accessor ? ` - ${c.name}: ` : ` - ${c.name} (${c.accessor}): `, ...c.messages.map((msg) => ` - ${msg}`), ].join("\n"); }) .join("\n"), ].join(""); if (props.type === "error") throw new Error(message); else console.log(message); } }; const wrapPaths = (paths) => paths.length === 0 ? [""] : paths; //# sourceMappingURL=NestiaSdkApplication.js.map