Custom Configuration Sources
Custom File Formats
Alternative file formats are not included by default in order to keep package size down, but are easy to write.
Use DefauiltSequence.CONFIGURATION for sequencing.
By contract, config file sources must to remove the config property from the context if they handled it.
This allows detection of "no sources handled the requested file".
Here's a YAML ConfigurationSource, for example:
% npm install yaml
import { ConfigurationError } from '@versionzero/configurator';
import { ConfigurationSource, ObjectSource } from '@versionzero/configurator/sources';
import { promises as fs } from 'fs';
import { YAML } from 'yaml'
export class YamlFileSource
extends ObjectSource
{
constructor(options) {
super(
{
...options,
sequence: options?.sequence || DefaultSequence.CONFIGURATION,
contextName: options?.contextName ?? 'config'
})
}
async load(schema, context) {
// intercept context field as a filename and rewrite it as an object
let filename = context[this.contextName];
if (filename && typeof filename !== 'string') {
throw new ConfiguratorError(`Invalid configuration path ${filename}`);
}
if (!filename || typeof filename !== 'string' || !filename.toLowerCase().endsWith('.yaml')) {
// as we've divided up config file loading, we'll assume another parser will load it
return new Map();
}
try {
const data = await fs.readFile(filename, 'utf8');
const assignments = super.load(schema, {[this.contextName]: YAML.parse(data)});
delete context[this.contextName];
return assignments;
}
catch (error) {
if (error.code === 'ENOENT') {
throw new ConfiguratorError(`Configuration path ${filename} not found`);
}
else if (error.code === 'EISDIR') {
throw new ConfiguratorError(`Configuration path ${filename} is a directory not a YAML file`);
}
else {
throw new ConfiguratorError(`Error loading YAML configuration file: ${error.message}`, {cause: error});
}
}
}
}