UNPKG

@redocly/respect-core

Version:
178 lines 6.54 kB
import { red } from 'colorette'; const hasCurlyBraces = (input) => { return /\{.*?\}/.test(input); }; export function getValueFromContext({ value, ctx, logger, }) { if (!value) return value; if (typeof value === 'object') { for (const key in value) { value[key] = getValueFromContext({ value: value[key], ctx, logger }); } return value; } if (typeof value === 'number' || typeof value === 'boolean') { return value; } if (value.toString().startsWith('$faker.')) { return getFakeData({ pointer: value.slice(1), ctx, logger }); } if (hasCurlyBraces(value)) { // multiple variables in string => {$var1} {$var2} return replaceVariablesInString(value, ctx, logger); } else { // single variable in string => $var1 return resolveValue(value, ctx, logger); } } export function replaceFakerVariablesInString(input, ctx, logger) { const startIndex = input.indexOf('{'); if (startIndex !== -1) { const substringAfterFirstBrace = input.substring(startIndex + 1); const fakerFunction = substringAfterFirstBrace.slice(0, -1); const fakerValue = getFakeData({ pointer: fakerFunction.slice(1), ctx, logger }); return fakerValue && input.replace(/{(.*)}/, fakerValue); } else { return input; } } function runInContext(code, context) { const contextKeys = Object.keys(context); const contextValues = Object.values(context); return new Function(...contextKeys, `return ${code}`)(...contextValues); } function replaceVariablesInString(input, ctx, logger) { // Regular expression to match content inside ${...} const regex = /\{\$(\{[^{}]*\}|[^{}])*\}/g; let result = input; // Replace each match with its interpolated value result = result.replace(regex, (match, _code) => { return interpolate(match, ctx, logger); }); return result; } function interpolate(part, ctx, logger) { if (!part.includes('$')) return part; if (part.includes('$faker.')) { return replaceFakerVariablesInString(part, ctx, logger); } const value = getFrom(ctx)(removeFigureBrackets(part)); return value !== undefined ? value : ''; } const resolveValue = (value, ctx, logger) => { if (!value) return value; const path = value.toString(); if (!path.includes('$')) { return value; } // if path has $file('...') syntax, return the path but trim the $file(' and ') if (path.startsWith('$file(') && path.endsWith(')')) { return path.slice(7, -2); } // $sourceDescriptions.<name>.workflows.<workflowId> if (path.startsWith('$sourceDescriptions.') && path.includes('.workflows.')) { const parts = path.split('.'); const sourceDescriptionName = parts[1]; const workflowId = parts[3]; if (!sourceDescriptionName || !workflowId) { return undefined; } const sourceDescriptions = getFrom(ctx)('$sourceDescriptions'); if (!sourceDescriptions[sourceDescriptionName]) { return undefined; } return sourceDescriptions[sourceDescriptionName].workflows.find((workflow) => workflow.workflowId === workflowId); } if (path && path.trim().startsWith('faker.')) { return getFakeData({ pointer: path, ctx, logger }); } return path ? getFrom(ctx)(replaceSquareBrackets(path)) : path.replace(/\$\{([^}]+)}/g, (_, path) => getFrom(ctx)(replaceSquareBrackets(path))); }; function removeFigureBrackets(input) { return input.replace(/\{(.*)\}/, '$1'); } function replaceSquareBrackets(input) { return input.replace(/\['(.*?)'\]/g, '.$1'); } const getFrom = ($, originalPointer = '') => (pointer) => { if (!originalPointer) { originalPointer = pointer; // Store the first pointer only during the initial call } if (!pointer) return $; const [key, ...rest] = pointer.split('.'); if (!$) { throw Error(`Cannot get ${red(key)} from ${red(originalPointer)}`); } if ($?.[key] === undefined) { const reason = ` Undefined ${red(key)} at ${red(originalPointer)}.`; const additionalInfo = Object.keys($).length ? `Did you mean to use another key? Available keys:\n${Object.keys($).join(', ')}.\n` : ''; const errorMessage = `${reason} ${additionalInfo}`; throw Error(errorMessage); } return getFrom($[key], originalPointer)(rest.join('.')); }; export function getFakeData({ pointer, ctx, logger, }) { const segments = pointer.split('.'); const fakerContext = { ctx: { faker: ctx.$faker } }; try { const escapedSegments = segments .map((segment) => segment.trim()) .map((segment, idx) => // Dumb function check (if ends by ')'), if function goes first dont need to put '.', // if goes second and so on - must be prepended by '.', like ["escaped-field"].func() segment.endsWith(')') ? `${idx == 0 ? '' : '.'}${segment}` : `["${segment}"]`) .join(''); return runInContext(`ctx${escapedSegments}`, fakerContext); } catch (err) { logger.error(red(err.toString())); return undefined; } } function modifyJSON(value, ctx, logger) { if (typeof value === 'string') { if (value) return getValueFromContext({ value, ctx, logger }); } if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || typeof value === 'undefined' || value instanceof Blob || value === null) return; for (const i in value) { if (typeof value[i] === 'string') { if (value[i]) value[i] = getValueFromContext({ value: value[i], ctx, logger }); } else { modifyJSON(value[i], ctx, logger); } } } export function parseJson(objectToResolve, ctx, logger) { return modifyJSON(objectToResolve, ctx, logger) || objectToResolve; } export function resolvePath(path, pathParams) { if (!path) return; const paramsWithBraces = {}; for (const param in pathParams) { paramsWithBraces[`{${param}}`] = String(pathParams[param]); } return path .split(/(\{[a-zA-Z0-9_.-]+\}+)/g) .map((key) => (paramsWithBraces[key] ? paramsWithBraces[key] : key)) .join(''); } //# sourceMappingURL=get-value-from-context.js.map