UNPKG

chai-latte

Version:

Build expressive & readable fluent interface libraries.

172 lines (168 loc) 7.51 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getBuilderFromLocalFile = exports.createTypegingForBuilders = exports.generateTypedApiFromPath = exports.readConfigFile = void 0; const tslib_1 = require("tslib"); const fs_extra_1 = tslib_1.__importDefault(require("fs-extra")); const path_1 = tslib_1.__importDefault(require("path")); const dedent_1 = tslib_1.__importDefault(require("dedent")); const ConfigurableCallback_1 = require("../builder/lib/ConfigurableCallback"); const readConfigFile = (filePath) => { const configPath = path_1.default.resolve(filePath, 'codegen.json'); const fileContent = fs_extra_1.default.readFileSync(configPath, 'utf8'); const json = JSON.parse(fileContent); return json; }; exports.readConfigFile = readConfigFile; const generateTypedApiFromPath = async (config) => { const compiled = await (0, exports.getBuilderFromLocalFile)(config); const typings = await (0, exports.createTypegingForBuilders)(config, compiled); await writeGeneratedFile(config, typings); return true; }; exports.generateTypedApiFromPath = generateTypedApiFromPath; const writeGeneratedFile = async (config, typings) => { const outputFilePath = await getOutputFilePath(config); await fs_extra_1.default.ensureFile(outputFilePath); await fs_extra_1.default.writeFile(outputFilePath, typings); }; const getOutputFilePath = async (config) => { const pathStats = await fs_extra_1.default.stat(config.input + '.ts'); let configFilePath = config.input; if (pathStats.isFile()) { const filePaths = config.input.split('/'); filePaths.pop(); configFilePath = filePaths.join('/'); } const outputFileName = config.output || 'generated.ts'; const relativePath = path_1.default.relative(__dirname, configFilePath + '/' + outputFileName); const absolutePath = path_1.default.resolve(__dirname, relativePath); return absolutePath; }; const createTypegingForBuilders = async (config, compiled) => { const getTabs = (tabs) => { const TAB = ' '; return { start: Array.from({ length: tabs }).map(() => '').join(TAB), end: Array.from({ length: tabs - 1 }).map(() => '').join(TAB), next: tabs + 1, }; }; const getArgumentNameFromArgType = (arg) => { const firstChar = arg.name.toLowerCase().slice(0, 1); const lastChars = arg.name.slice(1); const argName = firstChar + lastChars; switch (argName) { case 'string': return 'str'; case 'boolean': return 'bool'; case 'number': return 'num'; case 'object': return 'obj'; } return argName; }; const getArgumentTypeFromCallback = (callback, arg) => { const originalCallback = callback.originCallbackByArg.get(arg); const rowIdx = (originalCallback || callback).expression.index; const callIdx = callback.callIndex; return `Arg<${rowIdx}, ${callIdx}>`; }; const buildKeys = (currentTabs, obj) => { const tabs = getTabs(currentTabs); let typings = "{\n"; Object.keys(obj).map(key => { if (key.startsWith('__')) return; typings += `${tabs.start}${key}: ${buildValue(tabs.next, obj[key])}\n`; }).join(';\n'); typings += `${tabs.end}};`; return typings; }; const buildFunction = (currentTabs, fn) => { const callback = ConfigurableCallback_1.ConfigurableCallback.configByCallback.get(fn); const tabs = getTabs(currentTabs); const returnByArg = Array.from(callback.returnByArg)[0]; const returned = returnByArg[1]; const isLastCall = returned == callback.expression.callback; let typings = '{\n'; Object.keys(callback.props).forEach((key) => { typings += `${tabs.start}${key}: ${buildValue(tabs.next, callback.props[key])}\n`; }); callback.returnByArg.forEach((value, arg) => { const argName = getArgumentNameFromArgType(arg); const argType = getArgumentTypeFromCallback(callback, arg); const functionCallType = `${tabs.start}(${argName}: ${argType})`; if (isLastCall) { const originalCallback = callback.originCallbackByArg.get(arg); const rowIdx = (originalCallback || callback).expression.index; typings += `${functionCallType} : Return<${rowIdx}>;\n`; } else { typings += `${functionCallType} : ${buildKeys(tabs.next, value)}\n`; } }); typings += `${tabs.end}};`; return typings; }; const buildValue = (tabs, val) => { if (typeof val === 'object') { return buildKeys(tabs, val); } if (typeof val === 'function') { return buildFunction(tabs, val); } return 'any'; }; const typings = (0, dedent_1.default) ` ${createBaseTypings(config)} type Root = ${buildKeys(3, compiled)} export default builder as unknown as Root; `; return typings; }; exports.createTypegingForBuilders = createTypegingForBuilders; const getBuilderFromLocalFile = async (config) => { const relativePath = path_1.default.relative(__dirname, config.input); const { default: builders } = await Promise.resolve().then(() => tslib_1.__importStar(require('./' + relativePath))); return builders; }; exports.getBuilderFromLocalFile = getBuilderFromLocalFile; const buildTypeForObject = ({ api, builder, args, index }) => { let typeings = ''; const [key, value] = Object.entries(api)[0]; const innerType = typeof value === 'function' ? buildTypesForFunction({ callback: value, builder, args, index }) : buildTypeForObject({ api: value, builder, args, index }); typeings += `${key}: { ${innerType}; }`; return typeings; }; const buildTypesForFunction = ({ callback, builder, args, index }) => { const callbackConfig = ConfigurableCallback_1.ConfigurableCallback.configByCallback.get(callback); const returnByArg = Array.from(callbackConfig.returnByArg)[0]; const returned = returnByArg[1]; const argumentOrder = args.lastIndexOf(undefined) + 1; const argumentName = args[argumentOrder]; args[argumentOrder] = undefined; const argumentType = `Arg<${index}, ${argumentOrder}>`; const isLastCall = builder.callback == returned; const returnType = isLastCall ? `Return<${index}>` : `{ ${buildTypeForObject({ api: returned, builder, args, index })} }`; return `(${argumentName}: ${argumentType}) : ${returnType}`; }; const createBaseTypings = (config) => { // console.log('builderFilePath', builderFilePath); const localPath = './' + config.input.split('/').pop(); return (0, dedent_1.default) ` /* ------------------------------------ * Generated by chai-latte * Please do not edit this file directly * Instead, edit the file '${localPath}' * ------------------------------------ */ import builder from '${localPath}'; type Expressions = typeof builder.__expressions; type ExpressionCallback<Idx extends number> = Expressions[Idx][0]['callback']; type Arg<Idx extends number, ArgIndex extends number> = Parameters<ExpressionCallback<Idx>>[ArgIndex]; type Return<Idx extends number> = ReturnType<ExpressionCallback<Idx>>; `; }; //# sourceMappingURL=index.js.map