UNPKG

@fontoxml/fontoxml-development-tools

Version:

Development tools for Fonto.

156 lines (139 loc) 3.9 kB
import fs from 'fs'; import path from 'path'; const BASE_CONFIG_FILE_NAME = Symbol('base configuration file name'); const BASE_LOCATION = Symbol('base configuration file location'); const CONFIG_FILE_NAME = Symbol('configuration file name'); const LOCATION = Symbol('configuration file location'); const SERIALIZERS = Symbol('configuration file serializers'); function suffixPath(configLocation, configFileName) { const suffix = path.sep + configFileName; return configLocation.substr(-suffix.length) !== suffix ? configLocation + suffix : configLocation; } function readJsonOrReturnObject(jsonPath) { try { return JSON.parse(fs.readFileSync(jsonPath)); } catch (_error) { return {}; } } /** * Manages a unified configuration file for all modules that register their config. */ export default class ConfigManager { /** * @param {Array} configLocation The main config file location to which changes will be stored. * @param {Array} baseConfigLocation A base config file to inherit from. * @param {string} configFileName The name of the config file. * @param {string} baseConfigFilename The name of the base config file. * * @constructor */ constructor( configLocation, baseConfigLocation, configFileName, baseConfigFilename ) { this[BASE_CONFIG_FILE_NAME] = baseConfigFilename; this[BASE_LOCATION] = baseConfigLocation; this[CONFIG_FILE_NAME] = configFileName; this[LOCATION] = configLocation; this[SERIALIZERS] = {}; this.read(); } /** * @param {string} configName A key of the configuration object that you can share with other modules, or not. * @param {*} defaultValue If no configuration is found, use this. * @param {*|function(config): ?*} [serialize] * * @return {*} */ registerConfig(configName, defaultValue, serialize) { if (!this[configName]) { this[configName] = defaultValue || null; } Object.assign(this[SERIALIZERS], { [configName]: serialize, }); return this[configName]; } /** * @return {string} The location of the configuration file. */ getLocation() { return this[LOCATION]; } /** * Checks the path & exist status for all possible configuration file locations. * * @return {Array} */ getStatus() { return [ suffixPath(this[LOCATION], this[CONFIG_FILE_NAME]), suffixPath(this[BASE_LOCATION], this[BASE_CONFIG_FILE_NAME]), ].map((configLocation) => ({ path: configLocation, exists: fs.existsSync(configLocation), })); } /** * Read all possible configuration files and assign them to one new object. */ read() { Object.assign( this, this.getStatus().reduce( (config, file) => file.exists ? Object.assign( readJsonOrReturnObject(file.path), config ) : config, {} ) ); } /** * Save a stringified version of the configuration object. * * @return {Promise} Resolves to the successful configuration location. */ save() { const configLocation = suffixPath( this[LOCATION], this[CONFIG_FILE_NAME] ); return new Promise((resolve, reject) => fs.writeFile(configLocation, this.toString(), (error) => { return error ? reject(error) : resolve(configLocation); }) ); } /** * Prepare a stringified version of the configuration object - one that can be deserialized too. * * @return {string} */ toString() { const serializedConfig = Object.keys(this[SERIALIZERS]).reduce( (config, serializerName) => { const serializerFn = this[SERIALIZERS][serializerName]; const serialized = typeof serializerFn === 'function' ? serializerFn(this[serializerName]) : this[serializerName]; return serialized !== null && serialized !== undefined ? Object.assign(config, { [serializerName]: serialized, }) : config; }, {} ); return JSON.stringify(serializedConfig, null, '\t'); } }