UNPKG

rests

Version:

Easily generate API client's SDK — organize and simplify API Requests.

302 lines (257 loc) 9.43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const path = require("path"); const { capitalize, dent, mergeOptions } = require("./helpers"); /** * Generate typescript interface */ function generateTypes(schema, options) { let types = `/*! * Made with Rests * github.com/el1s7/rests */ type json = | string | number | boolean | null | json[] | {[key: string]: json}; interface FormData { [Symbol.iterator](): IterableIterator<[string, File | string]>; /** Returns an array of key, value pairs for every entry in the list. */ entries(): IterableIterator<[string, File | string]>; /** Returns a list of keys in the list. */ keys(): IterableIterator<string>; /** Returns a list of values in the list. */ values(): IterableIterator<File | string>; } interface ResponseObject { statusCode: number, statusText: string, headers: Headers, type: "basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect" , ok: boolean, json?: any, text?: string, formData?: FormData, blob?: Blob, message?: string } type HookRequest = { /** * Fetch URL */ url: string, /** * Fetch Options */ options: any, /** * The parameters supplied for this request */ params: any /** * Rests instance */ instance: any /** * Endpoint Key, e.g "user.login" */ key: string }; interface Hooks { /** * A global hook function that is called on request. * */ on_request?: (request: HookRequest) => any, /** * A hook function that is called on successful response, you can also modify and return a different response. */ on_success?: (response: ResponseObject, request?: HookRequest) => any, /** * A hook function that is called on errors. * * * To return a different error: */ on_error?: (error: ResponseObject | unknown, request?: HookRequest) => any, } interface Params { [name: string]:{ /** The parameter HTTP name */ name?: string, /** Required or not */ required?: boolean, /** A help message to throw in case of errors */ help?: string, /** Param type (default: any)*/ type?: "string" | "number" | "array" | "object" | "boolean" | "any", /** Example value */ example?: any, /** Format functions that accepts supplied value and returns formatted value. */ format?: (value: any)=>any, /** Regex validation */ validate?: RegExp | string, /** Array validation */ in?: any[], max?: number, min?: number, /** Default value */ default?: any, /** HTTP Location */ location?: "body" | "headers" | "query" | "path", } } interface Options extends Hooks { base?: string, sandboxBase?: string, headers?: any, params?: Params, /** * Set default values for parameters */ values?: { [param_name: string]: any } /** * Node-Fetch option for adding a proxy */ fetch_agent?: any, } interface newCategoryOptions { /** * Override global options for this category */ $options: Options; } interface newCategoryWithOptions extends newCategoryOptions { [param: string]: any | Options; } type newCategoryValues = { [param: string]: any } | newCategoryWithOptions; declare class HideFuncProps<T>{ private name; private apply; private bind; private arguments; private call; private caller; private length; private prototype; private toString; //public set: (values: newCategoryValues) => T; } interface updateOptions<X> extends HideFuncProps<X>{ set: (values: newCategoryValues) => X } interface newCategory<T> extends HideFuncProps<T> { new(values: newCategoryValues): T & updateOptions<T>; } `; let config = Object.assign({ output: null, includeExamples: true, template: null }, options); if (config.template) { types = fs.readFileSync(path.join(process.cwd(), config.template), 'utf-8'); } const getJSDoc = (string, tabs = 0, tabFirst = false) => { let padding = '\t'.repeat(tabs); return (`${tabFirst ? padding : ''}/** ${padding} * ${string.replace(/^(\s|\r?\n)/, '').replace(/\r?\n/g, `\n${padding} * `)} ${padding} */`); }; function makeTypes(tree, parent, categoryOptions = {}, parentTreeKey) { let category_help; for (var category in tree) { var category_tree = tree[category]; if ((category == 'help' || category == '$help') && typeof category_tree === "string") { //@ts-ignore category_help = tree[category]; } if (!category_tree || typeof category_tree !== 'object') { continue; } let helpMessage = ''; let treeKey = parentTreeKey ? parentTreeKey + capitalize(category) : capitalize(category); //Is Endpoint if (category_tree.hasOwnProperty('path')) { let endpoint = category_tree; let endpointParams = Object.assign(Object.assign({}, categoryOptions === null || categoryOptions === void 0 ? void 0 : categoryOptions.params), endpoint.params); let parseEndpointParams = Object.keys(endpointParams || {}) .sort((b, a) => { var _a, _b, _c, _d; return (endpointParams[a].required && !endpointParams[b].required ? 1 : (!endpointParams[a].required && endpointParams[b].required ? -1 : (!((_a = endpoint === null || endpoint === void 0 ? void 0 : endpoint.params) === null || _a === void 0 ? void 0 : _a[a]) && ((_b = endpoint === null || endpoint === void 0 ? void 0 : endpoint.params) === null || _b === void 0 ? void 0 : _b[b]) ? -1 : (!((_c = endpoint === null || endpoint === void 0 ? void 0 : endpoint.params) === null || _c === void 0 ? void 0 : _c[b]) && ((_d = endpoint === null || endpoint === void 0 ? void 0 : endpoint.params) === null || _d === void 0 ? void 0 : _d[a]) ? 1 : 0)))); }) .map((param) => { var _a, _b, _c; let ps = endpointParams[param]; if (ps['$initsOnly']) { return null; } let help = ps.help ? ps.help : '', exampleValue = (_b = (_a = ps.example) !== null && _a !== void 0 ? _a : ps.default) !== null && _b !== void 0 ? _b : (_c = categoryOptions === null || categoryOptions === void 0 ? void 0 : categoryOptions.values) === null || _c === void 0 ? void 0 : _c[param], helpWithExample = config.includeExamples && exampleValue ? dent(` ${help} @example \`${JSON.stringify(exampleValue)}\` `, 7) : '', required = ps.required && false ? '' : '?', type = (ps.type || "any").replace(/("|')/g, '').replace("array", "any[]").replace("object", "json"); return dent(` ${getJSDoc(helpWithExample || help, 1, true)} ${param}${required}: ${type} `, 6); }).filter(p => p).join('\n'); let endpointParamsType = parseEndpointParams ? `params?: {${parseEndpointParams}\n} | FormData` : ''; helpMessage = endpoint.help || `${capitalize(category)} - ${((endpoint === null || endpoint === void 0 ? void 0 : endpoint.method) || 'get').toUpperCase()} request`; let endpoint_type = dent(` ${getJSDoc(helpMessage, 1)} ${category}: (${endpointParamsType}) => Promise<ResponseObject>; `, 4); parent.push(endpoint_type); } //Is Category, recursion else { // Skip Special Object (i.e Options) if (category.substr(0, 1) === '$') { continue; } let subcategory = category_tree; let nextOptions = categoryOptions; if (subcategory.$options) { nextOptions = mergeOptions(categoryOptions, subcategory.$options); } helpMessage = category_help || `${capitalize(category)} Endpoints Category`; let category_type = dent(` export interface ${treeKey} extends newCategory<${treeKey}> { `, 5); let children = []; makeTypes(subcategory, children, nextOptions, treeKey); category_type += children.join('\n'); category_type += '\n}\n'; types += category_type; parent.push(dent(` ${getJSDoc(helpMessage, 1)} ${category}: ${treeKey} `, 5)); } } return types; } let rootType = dent(`export interface API extends updateOptions<API> { `, 2); let rootChildren = []; const generated = makeTypes(schema, rootChildren, (schema === null || schema === void 0 ? void 0 : schema.$options) || {}, "API"); rootType += rootChildren.join('\n'); rootType += '\n}'; types += dent(` ${rootType} declare const API: API; export default API; `, 2); if (fs && fs.writeFileSync && options.output) { fs.writeFileSync(config.output, types, { encoding: "utf8" }); } return types; } module.exports = generateTypes;