@enspirit/emb
Version:
A replacement for our Makefile-for-monorepos
50 lines (49 loc) • 1.87 kB
JavaScript
const TPL_REGEX = /(?<!\\)\${(?:(\w+):)?(\w+)(?::-(.*?))?}/g;
export class TemplateExpander {
expansions = [];
get expansionCount() {
return this.expansions.length;
}
async expand(str, options = {}) {
return (str || '')
.toString()
.replaceAll(TPL_REGEX, (match, source, key, fallback) => {
const src = source ?? options.default ?? '';
const provider = options.sources?.[src];
if (!provider) {
if (fallback !== undefined) {
return this.track(src, key, fallback);
}
throw new Error(`Invalid expand provider '${source}' ('${match}')`);
}
const val = provider[key];
if (!val && fallback === undefined) {
throw new Error(`Could not expand '${match}' and no default value provided`);
}
if (val !== undefined && val !== null) {
return this.track(src, key, val);
}
return this.track(src, key, fallback ?? '');
})
.replaceAll('\\${', '${');
}
async expandRecord(record, options) {
if (typeof record === 'string') {
const out = await this.expand(record, options);
return out;
}
if (Array.isArray(record)) {
const out = await Promise.all(record.map((v) => this.expandRecord(v, options)));
return out;
}
const entries = await Promise.all(Object.entries(record).map(async ([k, v]) => {
const expandedValue = await this.expandRecord(v, options);
return [k, expandedValue];
}));
return Object.fromEntries(entries);
}
track(source, variable, value) {
this.expansions.push({ source, value, variable });
return String(value);
}
}