|
| 1 | +import type { Config } from './types' |
| 2 | +import { resolve } from 'node:path' |
| 3 | +import process from 'node:process' |
| 4 | +import { deepMerge } from './utils' |
| 5 | + |
| 6 | +/** |
| 7 | + * Load Config |
| 8 | + * |
| 9 | + * @param {object} options - The configuration options. |
| 10 | + * @param {string} options.name - The name of the configuration file. |
| 11 | + * @param {string} [options.cwd] - The current working directory. |
| 12 | + * @param {string} [options.endpoint] - The API endpoint to fetch config from in browser environments. |
| 13 | + * @param {string} [options.headers] - The headers to send with the request in browser environments. |
| 14 | + * @param {T} options.defaultConfig - The default configuration. |
| 15 | + * @returns {Promise<T>} The merged configuration. |
| 16 | + * @example ```ts |
| 17 | + * // Merges arrays if both configs are arrays, otherwise does object deep merge |
| 18 | + * await loadConfig({ |
| 19 | + * name: 'example', |
| 20 | + * endpoint: '/api/my-custom-config/endpoint', |
| 21 | + * defaultConfig: [{ foo: 'bar' }] |
| 22 | + * }) |
| 23 | + * ``` |
| 24 | + */ |
| 25 | +export async function loadConfig<T>({ |
| 26 | + name, |
| 27 | + cwd, |
| 28 | + defaultConfig, |
| 29 | + endpoint, |
| 30 | + headers = { |
| 31 | + 'Accept': 'application/json', |
| 32 | + 'Content-Type': 'application/json', |
| 33 | + }, |
| 34 | +}: Config<T>): Promise<T> { |
| 35 | + // If running in a server (Bun) environment, load the config from the file system |
| 36 | + if (typeof window === 'undefined') { |
| 37 | + // back 3 times to get out of node_modules into the root directory, assuming the config is in the root directory |
| 38 | + const configPath = resolve(cwd || '../../../', `${name}.config`) |
| 39 | + |
| 40 | + try { |
| 41 | + const importedConfig = await import(configPath) |
| 42 | + const loadedConfig = importedConfig.default || importedConfig |
| 43 | + return deepMerge(defaultConfig, loadedConfig) as T |
| 44 | + } |
| 45 | + // eslint-disable-next-line unused-imports/no-unused-vars |
| 46 | + catch (error: any) { |
| 47 | + return defaultConfig |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + if (!endpoint) { |
| 52 | + console.warn('An API endpoint is required to load the client config.') |
| 53 | + return defaultConfig |
| 54 | + } |
| 55 | + |
| 56 | + // If running in a browser environment, load the config from an API endpoint |
| 57 | + try { |
| 58 | + const response = await fetch(endpoint, { |
| 59 | + method: 'GET', |
| 60 | + headers, |
| 61 | + }) |
| 62 | + |
| 63 | + if (!response.ok) { |
| 64 | + throw new Error(`HTTP error! status: ${response.status}`) |
| 65 | + } |
| 66 | + |
| 67 | + const loadedConfig = await response.json() as T |
| 68 | + return deepMerge(defaultConfig, loadedConfig) as T |
| 69 | + } |
| 70 | + catch (error) { |
| 71 | + console.error('Failed to load client config:', error) |
| 72 | + return defaultConfig |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +export * from './types' |
| 77 | +export * from './utils' |
0 commit comments