Source: utils/custom-renderer.js

import * as v from 'valibot'
import config from '../../config/index.js'
import logger from './logger.js'
import { types } from './logging.js'

/**
 * Extract invalid schema paths from a validation error
 * @param {ValiError} error - Validation error object
 * @returns {Array<Array<string>>} Array of path arrays containing invalid fields
 */
export const invalidSchemaPaths = (error) => {
  if (error instanceof v.ValiError) {
    const paths = []
    for (const issue of error.issues) {
      if (issue.path) {
        paths.push(issue.path.flatMap((p) => p.key))
      }
    }
    return paths
  }
  throw new TypeError(`error is not a validation error: ${error.name}`)
}

/**
 * Render a template with validation in non-production environments
 *
 * If validation error is raised, it's logged and re-thrown.
 *
 * Motivation: we want to ensure in development/test environments, that the data passed to
 * our templates is valid. This will help us ensure that we're testing the right thing.
 *
 * Note: Relies on {@link config.environment}
 *
 * @param {Object} renderer - Renderer object
 * @param {Function} renderer.render - Render function that takes template and params
 * @param {string} template - Path to template
 * @param {Object} schema - Valibot schema
 * @param {Object} params - Template parameters
 * @returns {string} Rendered template
 */
export const render = (renderer, template, schema, params) => {
  let parsed = params
  if (config.environment !== 'production' && config.environment !== 'staging') {
    try {
      parsed = v.parse(schema, params)
    } catch (error) {
      if (error instanceof v.ValiError) {
        // the below will only show up in the terminal when testing
        // console.debug({ params, message: 'failed validation input' })
        const paths = invalidSchemaPaths(error)
        logger.warn(
          validationErrorMessage(error, template),
          {
            errorMessage: `${error.message}`,
            pathsWithIssues: paths.length < 10 ? paths : paths.slice(0, 9),
            type: types.App
          }
        )
      }
      throw error
    }
  }
  return renderer.render(template, parsed)
}

/**
 * Creates an error message from the given error
 *
 * @param {ValiError} error validation error
 * @param {string} template template name
 * @returns string
 */
function validationErrorMessage (error, template) {
  const numIssues = error.issues.length
  const message = `Found ${numIssues} validation issue${numIssues === 1 ? '' : 's'} in template params for '${template}'`
  return message
}