/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra, Zackary Jackson @ScriptedAlchemy */ export type ContainerOptionsFormat = | (string | Record)[] | Record; /** @template T @typedef {(string | Record)[] | Record} ContainerOptionsFormat */ /** * @template T * @template N * @param {ContainerOptionsFormat} options options passed by the user * @param {function(string | string[], string) : N} normalizeSimple normalize a simple item * @param {function(T, string) : N} normalizeOptions normalize a complex item * @param {function(string, N): void} fn processing function * @returns {void} */ const process = ( options: ContainerOptionsFormat, normalizeSimple: (item: string | string[], name: string) => N, normalizeOptions: (item: T, name: string) => N, fn: (name: string, item: N) => void, ): void => { const array = ( items: (string | Record)[], ): void => { for (const item of items) { if (typeof item === 'string') { fn(item, normalizeSimple(item, item)); } else if (item && typeof item === 'object') { object(item as Record); } else { throw new Error('Unexpected options format'); } } }; const object = (obj: Record): void => { for (const [key, value] of Object.entries(obj)) { if (typeof value === 'string' || Array.isArray(value)) { fn(key, normalizeSimple(value, key)); } else { fn(key, normalizeOptions(value as T, key)); } } }; if (!options) { return; } else if (Array.isArray(options)) { array(options); } else if (typeof options === 'object') { object(options); } else { throw new Error('Unexpected options format'); } }; /** * @template T * @template R * @param {ContainerOptionsFormat} options options passed by the user * @param {function(string | string[], string) : R} normalizeSimple normalize a simple item * @param {function(T, string) : R} normalizeOptions normalize a complex item * @returns {[string, R][]} parsed options */ export function parseOptions( options: ContainerOptionsFormat, normalizeSimple: (item: string | string[], name: string) => R, normalizeOptions: (item: T, name: string) => R, ): [string, R][] { const items: [string, R][] = []; process(options, normalizeSimple, normalizeOptions, (key, value) => { items.push([key, value]); }); return items; } /** * @template T * @param {string} scope scope name * @param {ContainerOptionsFormat} options options passed by the user * @returns {Record} options to spread or pass */ export function scope( scope: string, options: ContainerOptionsFormat, ): Record { const obj: Record = {}; process( options, (item) => item as string | string[] | T, (item) => item as string | string[] | T, (key, value) => { obj[ key.startsWith('./') ? `${scope}${key.slice(1)}` : `${scope}/${key}` ] = value; }, ); return obj; }