Fitbit/webpack-config

View on GitHub
src/ConfigExtendCommand.js

Summary

Maintainability
A
35 mins
Test Coverage
import {
    isObject,
    isString
} from 'lodash';
import Config from './Config';
import ConfigCommand from './ConfigCommand';
import DEFAULT_TRANSFORM from './ConfigDefaultTransform';
import CLEANUP_TRANSFORM from './ConfigCleanupTransform';

/**
 * @typedef {Object<String,ConfigTransform[]>} ConfigExtendOptions
 */

/**
 * @typedef {String|Object<String,ConfigTransform>|ConfigExtendOptions} ConfigExtendPossibleOptions
 */

/**
 * @private
 * @type {WeakMap}
 */
const LOADER = new WeakMap();

/**
 * @private
 * @type {WeakMap}
 */
const FACTORY = new WeakMap();

/**
 * @class
 * @extends {ConfigCommand}
 */
class ConfigExtendCommand extends ConfigCommand {
    /**
     * @constructor
     * @param {ConfigOptionsResolver} optionsResolver
     * @param {ConfigLoader} loader
     * @param {ConfigFactory} factory
     */
    constructor(optionsResolver, loader, factory) {
        super(optionsResolver);

        LOADER.set(this, loader);
        FACTORY.set(this, factory);
    }

    /**
     * @readonly
     * @type {ConfigLoader}
     */
    get loader() {
        return LOADER.get(this);
    }

    /**
     * @readonly
     * @type {ConfigFactory}
     */
    get factory() {
        return FACTORY.get(this);
    }

    /**
     * @override
     */
    execute(config, options) {
        const normalizedOptions = ConfigExtendCommand.normalizeOptions(options);

        normalizedOptions.forEach(value => {
            const { filename, transforms } = this.optionsResolver.resolve(config, value);
            const pendingConfig = this.loader.loadConfig(filename);

            if (pendingConfig instanceof Config) {
                config.dependencyTree.children.push(pendingConfig.dependencyTree);

                let prevConfig = pendingConfig.clone();

                transforms.forEach(transform => {
                    const currConfig = transform.call(config, prevConfig);

                    if (!isObject(currConfig)) {
                        prevConfig = {};
                    } else {
                        prevConfig = currConfig;
                    }

                    if (!(prevConfig instanceof Config)) {
                        prevConfig = this.factory.createConfig({}).merge(prevConfig);
                    }
                });

                if (prevConfig instanceof Config) {
                    config.merge(prevConfig.toObject());
                }
            }
        });
    }

    /**
     * @param {ConfigExtendPossibleOptions} options
     * @returns {ConfigExtendOptions[]}
     */
    static normalizeOptions(options) {
        let normalizedOptions = [];

        if (isString(options)) {
            normalizedOptions = [{
                filename: options,
                transforms: [
                    DEFAULT_TRANSFORM,
                    CLEANUP_TRANSFORM
                ]
            }];
        } else if (isObject(options)) {
            for (const [filename, transform] of Object.entries(options)) {
                const transforms = Array.isArray(transform) ? transform : [transform];

                normalizedOptions.push({
                    filename,
                    transforms: [
                        ...transforms,
                        CLEANUP_TRANSFORM
                    ]
                });
            }
        }

        return normalizedOptions;
    }
}

export default ConfigExtendCommand;