UNPKG

trifid-core

Version:
113 lines (112 loc) 4.05 kB
import fs from 'node:fs/promises'; import { dirname } from 'node:path'; import merge from 'lodash/merge.js'; import JSON5 from 'json5'; import { parse } from 'yaml'; import cloneDeep from 'lodash/cloneDeep.js'; import { cwdCallback } from "../resolvers.js"; import parser from "./parser.js"; import { extendsResolver, globalsResolver, pluginsResolver, serverResolver, templateResolver, } from "./resolvers.js"; import { defaultPort, maxDepth } from "./default.js"; const resolveConfig = async (rawConfig, fileFullPath = undefined, depth = 0) => { if (depth >= maxDepth) { throw new Error('reached max configuration depth, maybe you went in an infinite loop. Please check the extends values from your configuration file recursively'); } const currentPath = fileFullPath ?? process.cwd(); const config = parser(rawConfig); addDefaultFields(config); const context = dirname(currentPath); // fetch all configuration files from which this one is extending let configs = []; if (Array.isArray(config.extends) && config.extends.length > 0) { config.extends = extendsResolver(config.extends, context); configs = await Promise.all(config.extends.map((configPath) => resolveConfigFile(configPath, depth + 1))); } // merge all fields const plugins = {}; configs.forEach((c) => { // merge template, globals and server parts config.globals = merge({}, c.globals ?? {}, config.globals ?? {}); config.server = merge({}, c.server ?? {}, config.server ?? {}); config.template = merge({}, c.template ?? {}, config.template ?? {}); // merge plugins for (const [name, plugin] of Object.entries(c.plugins ?? {})) { plugins[name] = plugin; } }); for (const [name, plugin] of Object.entries(config.plugins ?? {})) { plugins[name] = plugin; } // apply all resolvers config.plugins = pluginsResolver(plugins, context); config.globals = globalsResolver(config.globals, context); config.server = serverResolver(config.server, context); config.template = templateResolver(config.template, context); // we don't need the extends field anymore delete config.extends; return config; }; const resolveConfigFile = async (filePath, depth = 0) => { // read config file const fileFullPath = cwdCallback(filePath); const fileContent = await fs.readFile(fileFullPath, 'utf-8'); let parsed; const fileExtension = `${fileFullPath.split('.').pop()}`.toLocaleLowerCase(); if (['yaml', 'yml'].includes(fileExtension)) { parsed = parse(`${fileContent}`); } else { parsed = JSON5.parse(fileContent); } return await resolveConfig(parsed, fileFullPath, depth); }; /** * Add default fields for a configuration. * Warning: this function mutates the config object. * * @param config Trifid configuration. */ const addDefaultFields = (config) => { if (!config.server) { config.server = { listener: {}, }; } if (!config.globals) { config.globals = {}; } if (!config.plugins) { config.plugins = {}; } }; /** * Add the default port for the server configuration. * Warning: this function mutates the config object. * * @param config Trifid configuration. */ const addDefaultPort = (config) => { const server = config.server ?? (config.server = {}); const listener = server.listener ?? (server.listener = {}); if (listener.port === undefined) { listener.port = defaultPort; } }; /** * Expand configuration and add default fields. * * @param configFile Configuration file path or configuration object. */ const handler = async (configFile) => { let config; if (typeof configFile === 'string') { config = await resolveConfigFile(configFile); } else { config = await resolveConfig(cloneDeep(configFile)); } addDefaultFields(config); addDefaultPort(config); return config; }; export default handler;