UNPKG

@udraft/core

Version:

uDraft is a language and stack agnostic code-generation tool that simplifies full-stack development by converting a single YAML file into code for rapid development.

308 lines (296 loc) 13.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (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 () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const Case = __importStar(require("case")); const path = __importStar(require("path")); const renderer_1 = require("../entities/renderer"); const package_1 = require("../helpers/package"); const queries_1 = require("../shortcuts/queries"); const attributes_1 = require("../shortcuts/attributes"); const KEYS = { pubspec: "pubspec", baseApiClient: "ApiClient", }; const DEFAULT_API_CLIENT_CODE = ` import 'package:dio/dio.dart'; class ApiClient { final Dio dio; ApiClient({required this.dio}); Future<dynamic> request( String path, { required String method, String? contentType, Map<String, dynamic>? queryParameters, dynamic data, }) async { try { final response = await dio.request( path, options: Options( method: method, contentType: contentType, ), queryParameters: queryParameters, data: data, ); return response.data; } on DioException catch (e) { throw ApiException( code: e.response?.statusCode, message: e.message ?? 'Unknown error', ); } } } class ApiResponse<T> { final T? data; final String? error; final int? statusCode; ApiResponse.success(this.data) : statusCode = 200, error = null; ApiResponse.error({this.statusCode, String? message}) : data = null, error = message ?? 'Unknown error'; bool get isSuccess => error == null; } class ApiException implements Exception { final int? code; final String message; ApiException({this.code, required this.message}); @override String toString() => message; } `.trim(); class DartApiClientRenderer extends renderer_1.URenderer { constructor(options) { super("dart@api-client"); this._serviceDir = "lib/apis"; this._baseClientPath = "lib/core/api_client.dart"; if (options === null || options === void 0 ? void 0 : options.serviceDir) this._serviceDir = options.serviceDir; if (options === null || options === void 0 ? void 0 : options.baseClientPath) this._baseClientPath = options.baseClientPath; this._where = options === null || options === void 0 ? void 0 : options.where; } $moduleServiceName(module) { return `${Case.pascal(module.$name())}Api`; } $fileName(module, extension = true) { return `${Case.snake(module.$name())}_api${extension ? ".dart" : ""}`; } resolveFeatureRoute(module, feature) { const moduleConfig = (0, queries_1.$attr)(module, (0, attributes_1._http)()); const featureConfig = (0, queries_1.$attr)(feature, (0, attributes_1._http)()); return (((moduleConfig === null || moduleConfig === void 0 ? void 0 : moduleConfig.url) || "") + "/" + ((featureConfig === null || featureConfig === void 0 ? void 0 : featureConfig.url) || "")).replace(/\/{2,}/, "/"); } getHttpMethod(feature) { var _a; return (((_a = (0, queries_1.$attr)(feature, (0, attributes_1._http)())) === null || _a === void 0 ? void 0 : _a.method) || "get").toUpperCase(); } select() { return __awaiter(this, void 0, void 0, function* () { const modules = []; const features = []; this.$features((m, f) => !!(0, queries_1.$attr)(f, (0, attributes_1._http)()) && (this._where ? this._where(m, f) : true)).forEach((feature) => { const mod = (0, queries_1.$attr)(feature, (0, attributes_1._rootModule)()); if (mod && !modules.find((m) => m.$name() == mod.$name())) modules.push(mod); if (!features.find((f) => f.$name() == feature.$name())) features.push(feature); }); const paths = [ { key: KEYS.pubspec, path: "pubspec.yaml" }, { key: KEYS.baseApiClient, path: this._baseClientPath, meta: { isBase: true }, }, ]; modules.forEach((module) => { if (module.$features().length > 0) { paths.push({ key: this.$moduleServiceName(module), meta: { module: module.$name() }, path: path.join(this._serviceDir, this.$fileName(module)), }); } }); return { paths, modules, features, }; }); } render() { return __awaiter(this, void 0, void 0, function* () { var _a; const output = [ { key: KEYS.pubspec, content: (0, package_1.addPubspecDependency)(((_a = this.$content(KEYS.pubspec)) === null || _a === void 0 ? void 0 : _a.content) || "", [{ name: "dio", version: "^5.4.0" }]), }, ]; // Generate base API client if missing const baseApiClientPath = this.$path(KEYS.baseApiClient); const baseApiClient = this.$content(KEYS.baseApiClient); if (!(baseApiClient === null || baseApiClient === void 0 ? void 0 : baseApiClient.content)) { output.push({ key: KEYS.baseApiClient, content: DEFAULT_API_CLIENT_CODE, meta: { isBase: true }, }); } // Generate module APIs const modules = this.$selection().modules || []; for (const module of modules) { const features = module.$features(this.$selection().features); if (features.length === 0) continue; const serviceName = this.$moduleServiceName(module); const modulePath = this.$path(serviceName); const dartClassRenderer = this.$draft().$requireRenderer(this, "dart@classes"); if (!modulePath) continue; let imports = `import "${this.$resolveRelativePath(modulePath.path, (baseApiClientPath === null || baseApiClientPath === void 0 ? void 0 : baseApiClientPath.path) + "")}";`; let importedModels = []; let methods = []; const importModel = (model) => { var _a; const modelKey = dartClassRenderer.$key(model); const modelPath = (_a = dartClassRenderer.$path(modelKey)) === null || _a === void 0 ? void 0 : _a.path; if (importedModels.includes(modelKey)) return; imports += `\nimport "${this.$resolveRelativePath(modulePath.path, modelPath + "")}";`; }; features.forEach((feature) => { const methodName = Case.camel(feature.$name()); const inputModel = feature.$input(); const outputModel = feature.$output(); const httpConfig = (0, queries_1.$attr)(feature, (0, attributes_1._http)()); const contentType = httpConfig === null || httpConfig === void 0 ? void 0 : httpConfig.contentType; const params = (httpConfig === null || httpConfig === void 0 ? void 0 : httpConfig.params) || {}; const method = this.getHttpMethod(feature); const route = this.resolveFeatureRoute(module, feature); // Process route with path parameters let processedRoute = route.replace(/{([^}]+)}/g, (match, param) => { const fieldName = params[param]; return inputModel && fieldName ? `\${${Case.camel(inputModel.$name())}.${fieldName}}` : match; }); // Build query parameters const queryParamEntries = ((inputModel === null || inputModel === void 0 ? void 0 : inputModel.$fields()) || []) .filter((field) => !Object.values(params).some((n) => n == field.$name())) .map((field) => `'${dartClassRenderer.$fieldName(field)}': ${Case.camel((inputModel === null || inputModel === void 0 ? void 0 : inputModel.$name()) + "")}.${dartClassRenderer.$fieldName(field)}`); const queryParamsCode = queryParamEntries.length > 0 ? `queryParameters: {${queryParamEntries.join(", ")}}` : ""; // Build data based on content type // And handle input model let inputType = "void"; let dataCode = ""; if (inputModel) { const inputClassName = dartClassRenderer.$className(inputModel); importModel(inputModel); inputType = inputClassName; const inputVar = Case.camel(inputModel.$name()); dataCode = `data: ${inputVar}.toJson(),`; } // Handle output model let outputType = "void"; if (outputModel) { const outputClassName = dartClassRenderer.$className(outputModel); importModel(outputModel); outputType = outputClassName; } // Build method parameters const methodParams = inputModel ? `${inputType} ${Case.camel(inputModel.$name())}` : ""; const sendQuery = (httpConfig === null || httpConfig === void 0 ? void 0 : httpConfig.noBody) !== undefined ? httpConfig.noBody : ["GET", "SEARCH", "DELETE"].includes(method); // Build method implementation methods.push(` Future<ApiResponse<${outputType}>> ${methodName}(${methodParams}) async { try { ${outputModel ? "final response = " : ""}await client.request( '${processedRoute}', method: '${method}',${contentType ? `\n contentType: '${contentType}',` : ""}${sendQuery && queryParamsCode ? `\n ${queryParamsCode},` : ""}${!sendQuery ? `\n ${dataCode}` : ""} ); return ApiResponse.success(${outputModel ? `${dartClassRenderer.$className(outputModel)}.fromJson(response)` : "null"}); } on ApiException catch (e) { return ApiResponse.error( statusCode: e.code, message: e.message, ); } }`); }); const content = ` ${imports} class ${serviceName} { final ApiClient client; ${serviceName}({ required this.client, }); ${methods.join("\n")} } `.trim(); output.push({ key: serviceName, content, meta: modulePath.meta, }); } return output; }); } } exports.default = DartApiClientRenderer; //# sourceMappingURL=dart-api-client-renderer.js.map