From cd1ded58dd9dfb118087516f05b191c1dd7ed4a3 Mon Sep 17 00:00:00 2001 From: Marat Dreizin Date: Mon, 8 Aug 2016 10:54:58 +0300 Subject: [PATCH] feat(ConfigOptionsResolver): adds `ConfigOptionsResolver` to resolve `options` --- src/Config.js | 148 ++++++++++------------ src/ConfigBuilder.js | 1 - src/ConfigCleanupTransform.js | 21 ++++ src/ConfigCommand.js | 35 ++++++ src/ConfigContainer.js | 120 ++++++++++++++++++ src/ConfigDefaultTransform.js | 6 + src/ConfigDefaultsCommand.js | 21 ++++ src/ConfigExtendCommand.js | 133 ++++++++++++++++++++ src/ConfigExtendTransform.js | 112 ----------------- src/ConfigFactory.js | 30 +++-- src/ConfigLoader.js | 9 +- src/ConfigMergeCommand.js | 25 ++++ src/ConfigOptionsResolver.js | 72 +++++++++++ src/index.js | 41 ++++-- test/Config.spec.js | 60 +++------ test/ConfigBuilder.spec.js | 11 +- test/ConfigCache.spec.js | 26 +++- test/ConfigDefaultsCommand.spec.js | 50 ++++++++ test/ConfigEnvironment.spec.js | 11 +- test/ConfigExtendCommand.spec.js | 161 ++++++++++++++++++++++++ test/ConfigExtendTransform.spec.js | 90 ------------- test/ConfigFactory.spec.js | 13 +- test/ConfigFinder.spec.js | 11 +- test/ConfigLoader.spec.js | 11 +- test/ConfigMergeCommand.spec.js | 53 ++++++++ test/ConfigNameResolver.spec.js | 13 +- test/ConfigOptionsResolver.spec.js | 69 ++++++++++ test/ConfigPathResolver.spec.js | 11 +- test/ConfigPatternCache.spec.js | 11 +- test/ConfigProxy.spec.js | 52 ++++++++ test/helpers/MockConfigContainer.js | 81 ++++++++++++ test/helpers/TestFactory.js | 100 --------------- test/helpers/getConfigCommand.js | 14 +++ test/helpers/getConfigDependencyTree.js | 13 ++ test/index.spec.js | 7 +- 35 files changed, 1152 insertions(+), 490 deletions(-) create mode 100644 src/ConfigCleanupTransform.js create mode 100644 src/ConfigCommand.js create mode 100644 src/ConfigContainer.js create mode 100644 src/ConfigDefaultTransform.js create mode 100644 src/ConfigDefaultsCommand.js create mode 100644 src/ConfigExtendCommand.js delete mode 100644 src/ConfigExtendTransform.js create mode 100644 src/ConfigMergeCommand.js create mode 100644 src/ConfigOptionsResolver.js create mode 100644 test/ConfigDefaultsCommand.spec.js create mode 100644 test/ConfigExtendCommand.spec.js delete mode 100644 test/ConfigExtendTransform.spec.js create mode 100644 test/ConfigMergeCommand.spec.js create mode 100644 test/ConfigOptionsResolver.spec.js create mode 100644 test/ConfigProxy.spec.js create mode 100644 test/helpers/MockConfigContainer.js delete mode 100644 test/helpers/TestFactory.js create mode 100644 test/helpers/getConfigCommand.js create mode 100644 test/helpers/getConfigDependencyTree.js diff --git a/src/Config.js b/src/Config.js index dc8c920..d2aa46d 100644 --- a/src/Config.js +++ b/src/Config.js @@ -1,54 +1,55 @@ import { - isFunction, - isObject, - defaultsDeep, - mergeWith, set, unset, get, has } from 'lodash'; -import ConfigExtendTransform from './ConfigExtendTransform'; import ConfigDependency from './ConfigDependency'; /** - * @function - * @name ConfigTransform - * @param {Config} config - * @returns {*} - */ - -/** - * @typedef {Object|ConfigTransform} ConfigDefaultsOptions + * @private + * @type {String} */ +const DEPENDENCY_TREE = 'DEPENDENCY_TREE'; /** - * @typedef {Object|ConfigTransform} ConfigMergeOptions + * @private + * @type {WeakMap} */ +const FACTORY = new WeakMap(); /** - * @typedef {String|Object|Object} ConfigExtendOptions + * @private + * @type {WeakMap} */ +const DEFAULTS_COMMAND = new WeakMap(); /** * @private - * @type {String} + * @type {WeakMap} */ -const DEPENDENCY_TREE = 'DEPENDENCY_TREE'; +const MERGE_COMMAND = new WeakMap(); /** * @private * @type {WeakMap} */ -const LOADER = new WeakMap(); +const EXTEND_COMMAND = new WeakMap(); /** * @private - * @param {Object|Function} value - * @param {Config} context - * @returns {*} + * @param {Config} config + * @param {ConfigCommand} command + * @param {...*} values + * @returns {Config} */ -const evalValue = (value, context) => isFunction(value) ? value.call(context, context) : value; +const executeCommand = (config, command, ...values) => { + for (const value of values) { + command.execute(config, value); + } + + return config; +}; /** * @class @@ -56,19 +57,48 @@ const evalValue = (value, context) => isFunction(value) ? value.call(context, co class Config { /** * @constructor - * @param {ConfigLoader} loader + * @param {ConfigFactory} factory + * @param {ConfigDefaultsCommand} defaultsCommand + * @param {ConfigMergeCommand} mergeCommand + * @param {ConfigExtendCommand} extendCommand */ - constructor(loader) { - LOADER.set(this, loader); + constructor(factory, defaultsCommand, mergeCommand, extendCommand) { + FACTORY.set(this, factory); + DEFAULTS_COMMAND.set(this, defaultsCommand); + MERGE_COMMAND.set(this, mergeCommand); + EXTEND_COMMAND.set(this, extendCommand); } /** - * @protected * @readonly - * @type {ConfigLoader} + * @type {ConfigFactory} */ - get loader() { - return LOADER.get(this); + get factory() { + return FACTORY.get(this); + } + + /** + * @readonly + * @type {ConfigDefaultsCommand} + */ + get defaultsCommand() { + return DEFAULTS_COMMAND.get(this); + } + + /** + * @readonly + * @type {ConfigMergeCommand} + */ + get mergeCommand() { + return MERGE_COMMAND.get(this); + } + + /** + * @readonly + * @type {ConfigExtendCommand} + */ + get extendCommand() { + return EXTEND_COMMAND.get(this); } /** @@ -116,17 +146,11 @@ class Config { * }; * }); * @description Adds `values` if they are missing - * @param {...ConfigDefaultsOptions} values + * @param {...ConfigOptions} values * @returns {Config} */ defaults(...values) { - for (const value of Object.values(values)) { - const properties = evalValue(value, this); - - defaultsDeep(this, properties); - } - - return this; + return executeCommand(this, this.defaultsCommand, ...values); } /** @@ -147,21 +171,11 @@ class Config { * }; * }); * @description Merges `values` - * @param {...ConfigMergeOptions} values + * @param {...ConfigOptions} values * @returns {Config} */ merge(...values) { - for (const value of Object.values(values)) { - const properties = evalValue(value, this); - - mergeWith(this, properties, (x, y) => { // eslint-disable-line consistent-return - if (Array.isArray(x)) { - return x.concat(y); - } - }); - } - - return this; + return executeCommand(this, this.mergeCommand, ...values); } /** @@ -204,41 +218,11 @@ class Config { * }] * }); * @description Helps to extend config using local file or shareable config file which should be hosted under `node_modules` - * @param {...ConfigExtendTransform} values + * @param {...ConfigExtendPossibleOptions} values * @returns {Config} */ extend(...values) { - const map = ConfigExtendTransform.initWith(...values); - - for (const [key, value] of map.entries()) { - const config = this.loader.loadConfig(key); - - if (config instanceof Config) { - this.dependencyTree.children.push(config.dependencyTree); - - let prevConfig = config.clone(); - - value.forEach(x => { - const currConfig = x.call(this, prevConfig); - - if (!isObject(currConfig)) { - prevConfig = {}; - } else { - prevConfig = currConfig; - } - - if (!(prevConfig instanceof Config)) { - prevConfig = new Config(this.loader).merge(prevConfig); - } - }); - - if (prevConfig instanceof Config) { - this.merge(prevConfig.toObject()); - } - } - } - - return this; + return executeCommand(this, this.extendCommand, ...values); } /** @@ -257,7 +241,7 @@ class Config { * @returns {Config} */ clone() { - return new Config(this.loader).merge(this.toObject()); + return new Config(this.factory, this.defaultsCommand, this.mergeCommand, this.extendCommand).merge(this.toObject()); } /** diff --git a/src/ConfigBuilder.js b/src/ConfigBuilder.js index 9f8aa32..c3be696 100644 --- a/src/ConfigBuilder.js +++ b/src/ConfigBuilder.js @@ -43,7 +43,6 @@ class ConfigBuilder { } /** - * @private * @readonly * @type {ConfigFactory} */ diff --git a/src/ConfigCleanupTransform.js b/src/ConfigCleanupTransform.js new file mode 100644 index 0000000..b640e00 --- /dev/null +++ b/src/ConfigCleanupTransform.js @@ -0,0 +1,21 @@ +/** + * @private + * @type {String[]} + */ +const EXCLUDE_FIELDS = [ + 'filename', + 'DEPENDENCY_TREE' +]; + +/** + * Removes system properties + * @param {Config} config + * @returns {Config} + */ +export default config => { + EXCLUDE_FIELDS.forEach(function(name) { + delete config[name]; + }); + + return config; +}; diff --git a/src/ConfigCommand.js b/src/ConfigCommand.js new file mode 100644 index 0000000..a7ef2f8 --- /dev/null +++ b/src/ConfigCommand.js @@ -0,0 +1,35 @@ +/** + * @private + * @type {WeakMap} + */ +const OPTIONS_RESOLVER = new WeakMap(); + +/** + * @class + */ +class ConfigCommand { + /** + * @constructor + * @param {ConfigOptionsResolver} optionsResolver + */ + constructor(optionsResolver) { + OPTIONS_RESOLVER.set(this, optionsResolver); + } + + /** + * @abstract + * @param {Config} config + * @param {ConfigOptions} options + * @returns {void} + */ + execute(config, options) {} // eslint-disable-line no-unused-vars + + /** + * @type {ConfigOptionsResolver} + */ + get optionsResolver() { + return OPTIONS_RESOLVER.get(this); + } +} + +export default ConfigCommand; diff --git a/src/ConfigContainer.js b/src/ConfigContainer.js new file mode 100644 index 0000000..a7c67c3 --- /dev/null +++ b/src/ConfigContainer.js @@ -0,0 +1,120 @@ +import { + Container, + Transient +} from 'constitute'; +import Config from './Config'; +import ConfigCache from './ConfigCache'; +import ConfigEnvironment from './ConfigEnvironment'; +import ConfigPatternCache from './ConfigPatternCache'; +import ConfigNameResolver from './ConfigNameResolver'; +import ConfigPathResolver from './ConfigPathResolver'; +import ConfigLoader from './ConfigLoader'; +import ConfigFinder from './ConfigFinder'; +import ConfigFactory from './ConfigFactory'; +import ConfigBuilder from './ConfigBuilder'; +import ConfigOptionsResolver from './ConfigOptionsResolver'; +import ConfigDefaultsCommand from './ConfigDefaultsCommand'; +import ConfigMergeCommand from './ConfigMergeCommand'; +import ConfigExtendCommand from './ConfigExtendCommand'; + +/** + * @private + * @type {WeakMap} + */ +const CONTAINER = new WeakMap(); + +/** + * @class + */ +class ConfigContainer { + /** + * @constructor + */ + constructor() { + CONTAINER.set(this, new Container()); + + this.setUp(); + } + + /** + * @protected + * @type {Container} + */ + get container() { + return CONTAINER.get(this); + } + + /** + * @protected + * @returns {void} + */ + setUp() { + const container = this.container; + + container.bindValue(ConfigContainer, this); + container.bindValue(ConfigEnvironment, new ConfigEnvironment(Object.entries(process.env))); + container.bindClass(ConfigCache, ConfigCache, [ + ConfigEnvironment + ]); + container.bindValue(ConfigPatternCache, new ConfigPatternCache()); + container.bindClass(ConfigNameResolver, ConfigNameResolver, [ + ConfigEnvironment, + ConfigPatternCache + ]); + container.bindClass(ConfigPathResolver, ConfigPathResolver, [ + ConfigNameResolver + ]); + container.bindClass(ConfigLoader, ConfigLoader, [ + ConfigPathResolver, + ConfigCache, + ConfigFactory + ]); + container.bindClass(ConfigFinder, ConfigFinder, [ + ConfigPathResolver + ]); + container.bindClass(ConfigFactory, ConfigFactory, [ + ConfigContainer + ]); + container.bindClass(Config, Config, Transient.with([ + ConfigFactory, + ConfigDefaultsCommand, + ConfigMergeCommand, + ConfigExtendCommand + ])); + container.bindClass(ConfigBuilder, ConfigBuilder, Transient.with([ + ConfigFactory + ])); + container.bindClass(ConfigOptionsResolver, ConfigOptionsResolver, [ + ConfigNameResolver + ]); + container.bindClass(ConfigDefaultsCommand, ConfigDefaultsCommand, [ + ConfigOptionsResolver + ]); + container.bindClass(ConfigMergeCommand, ConfigMergeCommand, [ + ConfigOptionsResolver + ]); + container.bindClass(ConfigExtendCommand, ConfigExtendCommand, Transient.with([ + ConfigOptionsResolver, + ConfigLoader, + ConfigFactory + ])); + } + + /** + * @param {*} T + * @returns {*} + */ + resolve(T) { + return this.container.constitute(T); + } + + /** + * @param {*} T + * @returns {Function} + */ + proxy(T) { + return () => this.resolve(T); + } +} + +export default ConfigContainer; diff --git a/src/ConfigDefaultTransform.js b/src/ConfigDefaultTransform.js new file mode 100644 index 0000000..55736d5 --- /dev/null +++ b/src/ConfigDefaultTransform.js @@ -0,0 +1,6 @@ +/** + * Returns config without any modifications + * @param {Config} config + * @returns {Config} + */ +export default config => config; diff --git a/src/ConfigDefaultsCommand.js b/src/ConfigDefaultsCommand.js new file mode 100644 index 0000000..5e661c9 --- /dev/null +++ b/src/ConfigDefaultsCommand.js @@ -0,0 +1,21 @@ +import { + defaultsDeep +} from 'lodash'; +import ConfigCommand from './ConfigCommand'; + +/** + * @class + * @extends {ConfigCommand} + */ +class ConfigDefaultsCommand extends ConfigCommand { + /** + * @override + */ + execute(config, options) { + const value = this.optionsResolver.resolve(config, options); + + defaultsDeep(config, value); + } +} + +export default ConfigDefaultsCommand; diff --git a/src/ConfigExtendCommand.js b/src/ConfigExtendCommand.js new file mode 100644 index 0000000..ac51a7c --- /dev/null +++ b/src/ConfigExtendCommand.js @@ -0,0 +1,133 @@ +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} ConfigExtendOptions + */ + +/** + * @typedef {String|Object|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; diff --git a/src/ConfigExtendTransform.js b/src/ConfigExtendTransform.js deleted file mode 100644 index db7ed30..0000000 --- a/src/ConfigExtendTransform.js +++ /dev/null @@ -1,112 +0,0 @@ -import { - isFunction, - isString, - isObject -} from 'lodash'; - -/** - * @private - * @type {String[]} - */ -const EXCLUDE_FIELDS = [ - 'filename', - 'DEPENDENCY_TREE' -]; - -/** - * @private - * @param {Config} config - * @returns {Config} - */ -const DEFAULT_TRANSFORM = config => config; - -/** - * @private - * @param {Config} config - * @returns {config} - */ -const CLEANUP_TRANSFORM = config => { - EXCLUDE_FIELDS.forEach(function(name) { - delete config[name]; - }); - - return config; -}; - -/** - * @class - * @extends {Map} - * @private - */ -class ConfigExtendTransform extends Map { - /** - * @override - * @param {*} key - * @param {Function} [value] - * @throws {ReferenceError} - * @returns {Function[]} - */ - set(key, value = ConfigExtendTransform.DEFAULT) { - if (!isFunction(value)) { - throw new ReferenceError(`\`${value}\` is not \`Function\``); - } - - const values = this.has(key) ? this.get(key) : []; - - if (this.has(key)) { - values.pop(); - } - - values.push(value, ConfigExtendTransform.CLEANUP); - - return super.set(key, values); - } - - /** - * @param {...ConfigExtendOptions} values - * @returns {ConfigExtendTransform} - */ - setAll(...values) { - values.forEach(obj => { - if (isString(obj)) { - this.set(obj); - } else if (isObject(obj)) { - for (const [key, value] of Object.entries(obj)) { - if (Array.isArray(value)) { - value.filter(isFunction).forEach(x => this.set(key, x)); - } else if (isFunction(value)) { - this.set(key, value); - } - } - } - }); - - return this; - } - - /** - * @readonly - * @type {Function} - */ - static get DEFAULT() { - return DEFAULT_TRANSFORM; - } - - /** - * @readonly - * @type {Function} - */ - static get CLEANUP() { - return CLEANUP_TRANSFORM; - } - - /** - * @param {...ConfigExtendOptions} values - * @returns {ConfigExtendTransform} - */ - static initWith(...values) { - return new ConfigExtendTransform().setAll(...values); - } -} - -export default ConfigExtendTransform; diff --git a/src/ConfigFactory.js b/src/ConfigFactory.js index 605ce6f..0773a0e 100644 --- a/src/ConfigFactory.js +++ b/src/ConfigFactory.js @@ -9,7 +9,7 @@ import ConfigList from './ConfigList'; * @private * @type {WeakMap} */ -const LOADER = new WeakMap(); +const CONTAINER = new WeakMap(); /** * @class @@ -17,19 +17,29 @@ const LOADER = new WeakMap(); class ConfigFactory { /** * @constructor - * @param {ConfigLoader} loader + * @param {ConfigContainer} container */ - constructor(loader) { - LOADER.set(this, loader); + constructor(container) { + CONTAINER.set(this, container); } /** - * @protected * @readonly - * @type {ConfigLoader} + * @type {ConfigContainer} */ - get loader() { - return LOADER.get(this); + get container() { + return CONTAINER.get(this); + } + + /** + * @private + * @param {Object} options + * @returns {Config} + */ + initWith(options) { + const config = this.container.resolve(Config); + + return config.merge(options); } /** @@ -44,9 +54,9 @@ class ConfigFactory { } if (Array.isArray(value)) { - config = ConfigList.from(value, x => new Config(this.loader).merge(x)); + config = ConfigList.from(value, x => this.initWith(x)); } else if (isObject(value)) { - config = new Config(this.loader).merge(value); + config = this.initWith(value); } return config; diff --git a/src/ConfigLoader.js b/src/ConfigLoader.js index 2055d9f..ca9801e 100644 --- a/src/ConfigLoader.js +++ b/src/ConfigLoader.js @@ -1,7 +1,6 @@ import { isString } from 'lodash'; -import ConfigFactory from './ConfigFactory'; /** * @private @@ -29,15 +28,15 @@ class ConfigLoader { * @constructor * @param {ConfigPathResolver} pathResolver * @param {ConfigCache} cache + * @param {ConfigFactory} factory */ - constructor(pathResolver, cache) { + constructor(pathResolver, cache, factory) { PATH_RESOLVER.set(this, pathResolver); CACHE.set(this, cache); - FACTORY.set(this, new ConfigFactory(this)); + FACTORY.set(this, factory); } /** - * @protected * @readonly * @type {ConfigPathResolver} */ @@ -46,7 +45,6 @@ class ConfigLoader { } /** - * @protected * @readonly * @type {ConfigCache} */ @@ -55,7 +53,6 @@ class ConfigLoader { } /** - * @protected * @readonly * @type {ConfigFactory} */ diff --git a/src/ConfigMergeCommand.js b/src/ConfigMergeCommand.js new file mode 100644 index 0000000..e662cbf --- /dev/null +++ b/src/ConfigMergeCommand.js @@ -0,0 +1,25 @@ +import { + mergeWith +} from 'lodash'; +import ConfigCommand from './ConfigCommand'; + +/** + * @class + * @extends {ConfigCommand} + */ +class ConfigMergeCommand extends ConfigCommand { + /** + * @override + */ + execute(config, options) { + const value = this.optionsResolver.resolve(config, options); + + mergeWith(config, value, (x, y) => { // eslint-disable-line consistent-return + if (Array.isArray(x)) { + return x.concat(y); + } + }); + } +} + +export default ConfigMergeCommand; diff --git a/src/ConfigOptionsResolver.js b/src/ConfigOptionsResolver.js new file mode 100644 index 0000000..2faeb4f --- /dev/null +++ b/src/ConfigOptionsResolver.js @@ -0,0 +1,72 @@ +import { + isFunction, + isString +} from 'lodash'; +import RecursiveIterator from 'recursive-iterator'; + +/** + * @function + * @name ConfigTransform + * @param {Config} config + * @returns {Config|Object} + */ + +/** + * @typedef {Object|ConfigTransform} ConfigOptions + */ + +/** + * @private + * @type {WeakMap} + */ +const NAME_RESOLVER = new WeakMap(); + +/** + * @class + */ +class ConfigOptionsResolver { + /** + * @constructor + * @param {ConfigNameResolver} nameResolver + */ + constructor(nameResolver) { + NAME_RESOLVER.set(this, nameResolver); + } + + /** + * @readonly + * @type {ConfigNameResolver} + */ + get nameResolver() { + return NAME_RESOLVER.get(this); + } + + /** + * @private + * @param {Config} config + * @param {ConfigOptions} options + * @returns {Object} + */ + static valueOf(config, options) { + return isFunction(options) ? options.call(config, config) : options; + } + + /** + * @param {Config} config + * @param {ConfigOptions} options + * @returns {Object} + */ + resolve(config, options) { + const value = ConfigOptionsResolver.valueOf(config, options); + + for (const {parent, node, key} of new RecursiveIterator(value, 1, true)) { + if (isString(node)) { + parent[key] = this.nameResolver.resolve(node); + } + } + + return value; + } +} + +export default ConfigOptionsResolver; diff --git a/src/index.js b/src/index.js index 4766a0a..ab33b68 100644 --- a/src/index.js +++ b/src/index.js @@ -8,21 +8,28 @@ import ConfigPatternCache from './ConfigPatternCache'; import ConfigNameResolver from './ConfigNameResolver'; import ConfigPathResolver from './ConfigPathResolver'; import ConfigFactory from './ConfigFactory'; -import container from './container'; +import ConfigContainer from './ConfigContainer'; +import ConfigOptionsResolver from './ConfigOptionsResolver'; + +/** + * @private + * @type {ConfigContainer} + */ +const container = new ConfigContainer(); /** * Proxy class which automatically fills {@link Config} constructor dependencies * @class * @extends {Config} */ -const ConfigProxy = container.proxyClass(ConfigBase); +const ConfigProxy = container.proxy(ConfigBase); /** * Proxy class which automatically fills {@link ConfigBuilder} constructor dependencies * @class * @extends {ConfigBuilder} */ -const ConfigBuilderProxy = container.proxyClass(ConfigBuilderBase); +const ConfigBuilderProxy = container.proxy(ConfigBuilderBase); /** * @module webpack-config @@ -79,45 +86,55 @@ export { /** * @type {ConfigFactory} */ - ConfigFactory + ConfigFactory, + + /** + * @type {ConfigOptionsResolver} + */ + ConfigOptionsResolver }; /** * @type {ConfigEnvironment} */ -export const environment = container.constitute(ConfigEnvironment); +export const environment = container.resolve(ConfigEnvironment); /** * @type {ConfigCache} */ -export const cache = container.constitute(ConfigCache); +export const cache = container.resolve(ConfigCache); /** * @type {ConfigPatternCache} */ -export const patternCache = container.constitute(ConfigPatternCache); +export const patternCache = container.resolve(ConfigPatternCache); /** * @type {ConfigNameResolver} */ -export const nameResolver = container.constitute(ConfigNameResolver); +export const nameResolver = container.resolve(ConfigNameResolver); /** * @type {ConfigNameResolver} */ -export const pathResolver = container.constitute(ConfigPathResolver); +export const pathResolver = container.resolve(ConfigPathResolver); /** * @type {ConfigLoader} */ -export const loader = container.constitute(ConfigLoader); +export const loader = container.resolve(ConfigLoader); /** * @type {ConfigFinder} */ -export const finder = container.constitute(ConfigFinder); +export const finder = container.resolve(ConfigFinder); /** * @type {ConfigFactory} */ -export const factory = container.constitute(ConfigFactory); +export const factory = container.resolve(ConfigFactory); + +/** + * @type {ConfigOptionsResolver} + */ +export const optionsResolver = container.resolve(ConfigOptionsResolver); diff --git a/test/Config.spec.js b/test/Config.spec.js index 5688a00..9028903 100644 --- a/test/Config.spec.js +++ b/test/Config.spec.js @@ -1,15 +1,15 @@ -import { - resolve -} from 'path'; import Config from '../src/Config'; -import ConfigDependency from '../src/ConfigDependency'; -import TestFactory from './helpers/TestFactory'; +import MockConfigContainer from './helpers/MockConfigContainer'; describe('Config', () => { - let config; + let container = new MockConfigContainer(), + /** + * @type {Config} + */ + config; beforeEach(() => { - config = TestFactory.createConfig(); + config = container.resolve(Config); }); describe('.FILENAME', () => { @@ -19,7 +19,7 @@ describe('Config', () => { }); describe('#defaults()', () => { - it('should not add extra `values`', () => { + it('should merge successfully', () => { const date1 = new Date(), date2 = new Date(); @@ -47,58 +47,30 @@ describe('Config', () => { }); describe('#merge()', () => { - it('should merge `values`', function() { + it('should merge successfully', function() { config.merge({ - foo: { - bar: 'bar1' - }, + foo: 'foo1', bar: ['bar1'] }, { - foo: { - bar: 'bar2' - }, + foo: 'foo2', bar: ['bar2'] }, x => { expect(x).toBe(config); return { - foo: { - bar: 'bar3' - } + foo: 'bar3' }; }, () => {}); expect(config.toObject()).toEqual({ - foo: { - bar: 'bar3' - }, + foo: 'bar3', bar: ['bar1', 'bar2'] }); }); }); describe('#extend()', () => { - it('should have `dependencyTree`', () => { - const paths = []; - - config.extend('./test/fixtures/webpack.1.config.js'); - - expect(config.dependencyTree).toEqual(jasmine.any(ConfigDependency)); - - for (const {node} of config.dependencyTree) { - paths.push(node.root.filename); - } - - expect(paths).toEqual([ - resolve('./test/fixtures/webpack.1.config.js'), - resolve('./test/fixtures/webpack.2.config.js'), - resolve('./test/fixtures/webpack.3.config.js'), - resolve('./test/fixtures/webpack.5.config.js'), - resolve('./test/fixtures/webpack.4.config.js') - ]); - }); - - it('should extend using `String`', () => { + it('should extend successfully using `String`', () => { config.extend('./test/fixtures/webpack.1.config.js'); expect(config.toObject()).toEqual({ @@ -112,7 +84,7 @@ describe('Config', () => { }); }); - it('should extend using `Object`', () => { + it('should extend successfully `Object`', () => { config.extend({ './test/fixtures/webpack.1.config.js': x => { expect(x).toEqual(jasmine.any(Config)); @@ -132,7 +104,7 @@ describe('Config', () => { }); }); - it('should extend using `Object`', () => { + it('should extend successfully `Object`', () => { config.extend({ './test/fixtures/webpack.1.config.js': [ x => { diff --git a/test/ConfigBuilder.spec.js b/test/ConfigBuilder.spec.js index 7e19667..323f0ad 100644 --- a/test/ConfigBuilder.spec.js +++ b/test/ConfigBuilder.spec.js @@ -1,10 +1,15 @@ -import TestFactory from './helpers/TestFactory'; +import ConfigBuilder from '../src/ConfigBuilder'; +import MockConfigContainer from './helpers/MockConfigContainer'; describe('ConfigBuilder', () => { - let builder; + let container = new MockConfigContainer(), + /** + * @type {ConfigBuilder} + */ + builder; beforeEach(() => { - builder = TestFactory.createConfigBuilder(); + builder = container.resolve(ConfigBuilder); }); describe('#merge()', () => { diff --git a/test/ConfigCache.spec.js b/test/ConfigCache.spec.js index 1d7572d..6587697 100644 --- a/test/ConfigCache.spec.js +++ b/test/ConfigCache.spec.js @@ -1,15 +1,22 @@ import { resolve } from 'path'; -import TestFactory from './helpers/TestFactory'; +import ConfigCache from '../src/ConfigCache'; +import MockConfigContainer from './helpers/MockConfigContainer'; describe('ConfigCache', () => { const FILENAME = resolve('./test/fixtures/webpack.1.config.js'); - let cache; + let container = new MockConfigContainer(), + /** + * @type {ConfigCache} + */ + cache; beforeEach(() => { - cache = TestFactory.createConfigCache(); + cache = container.resolve(ConfigCache); + + cache.persistent = true; }); describe('#get()', () => { @@ -17,8 +24,6 @@ describe('ConfigCache', () => { const config1 = cache.get(FILENAME), config2 = cache.get(FILENAME); - cache.persistent = true; - expect(config1).toBe(config2); }); @@ -31,5 +36,16 @@ describe('ConfigCache', () => { expect(config1).not.toBe(config2); }); + + it('should resolve `__esModule`', () => { + cache.set('./test/fixtures/webpack.6.config.js', { + __esModule: true, + 'default': {} + }); + + const config = cache.get('./test/fixtures/webpack.6.config.js'); + + expect(config).toBeTruthy(); + }); }); }); diff --git a/test/ConfigDefaultsCommand.spec.js b/test/ConfigDefaultsCommand.spec.js new file mode 100644 index 0000000..e7ac0ac --- /dev/null +++ b/test/ConfigDefaultsCommand.spec.js @@ -0,0 +1,50 @@ +import ConfigDefaultsCommand from '../src/ConfigDefaultsCommand'; +import MockConfigContainer from './helpers/MockConfigContainer'; +import getConfigCommand from './helpers/getConfigCommand'; + +describe('ConfigDefaultsCommand', () => { + let container = new MockConfigContainer(), + /** + * @type {Config} + */ + config, + /** + * @type {ConfigDefaultsCommand} + */ + command; + + beforeEach(() => { + [config, command] = getConfigCommand(container, ConfigDefaultsCommand); + }); + + describe('#execute()', () => { + it('should execute successfully', () => { + const date1 = new Date(), + date2 = new Date(); + + command.execute(config, { + foo: 'foo1', + date: date1 + }); + command.execute(config, { + foo: 'foo2', + bar: ['bar2'], + date: date2 + }); + command.execute(config, x => { + expect(x).toBe(config); + + return { + foo: 'foo2' + }; + }); + command.execute(() => {}); + + expect(config.toObject()).toEqual({ + foo: 'foo1', + bar: ['bar2'], + date: date1 + }); + }); + }); +}); diff --git a/test/ConfigEnvironment.spec.js b/test/ConfigEnvironment.spec.js index 3fced83..96e3e85 100644 --- a/test/ConfigEnvironment.spec.js +++ b/test/ConfigEnvironment.spec.js @@ -1,10 +1,15 @@ -import TestFactory from './helpers/TestFactory'; +import ConfigEnvironment from '../src/ConfigEnvironment'; +import MockConfigContainer from './helpers/MockConfigContainer'; describe('ConfigEnvironment', () => { - let environment; + let container = new MockConfigContainer(), + /** + * @type {ConfigEnvironment} + */ + environment; beforeEach(() => { - environment = TestFactory.createConfigEnvironment(); + environment = container.resolve(ConfigEnvironment); }); describe('#setAll()', () => { diff --git a/test/ConfigExtendCommand.spec.js b/test/ConfigExtendCommand.spec.js new file mode 100644 index 0000000..6f2e00e --- /dev/null +++ b/test/ConfigExtendCommand.spec.js @@ -0,0 +1,161 @@ +import { + resolve +} from 'path'; +import Config from '../src/Config'; +import ConfigExtendCommand from '../src/ConfigExtendCommand'; +import DEFAULT_TRANSFORM from '../src/ConfigDefaultTransform'; +import CLEANUP_TRANSFORM from '../src/ConfigCleanupTransform'; +import ConfigDependency from '../src/ConfigDependency'; +import MockConfigContainer from './helpers/MockConfigContainer'; +import getConfigDependencyTree from './helpers/getConfigDependencyTree'; +import getConfigCommand from './helpers/getConfigCommand'; + +describe('ConfigExtendCommand', () => { + let container = new MockConfigContainer(), + /** + * @type {Config} + */ + config, + /** + * @type {ConfigExtendCommand} + */ + command, + customTransform = () => {}; + + beforeEach(() => { + [config, command] = getConfigCommand(container, ConfigExtendCommand); + }); + + describe('.normalizeOptions()', () => { + it('should normalize `String`', () => { + const options = ConfigExtendCommand.normalizeOptions('./test/fixtures/webpack.1.config.js'); + + expect(options).toEqual([{ + filename: './test/fixtures/webpack.1.config.js', + transforms: [ + DEFAULT_TRANSFORM, + CLEANUP_TRANSFORM + ] + }]); + }); + + it('should normalize `Object`', () => { + const options = ConfigExtendCommand.normalizeOptions({ + './test/fixtures/webpack.1.config.js': customTransform, + './test/fixtures/webpack.2.config.js': customTransform + }); + + expect(options).toEqual([{ + filename: './test/fixtures/webpack.1.config.js', + transforms: [ + customTransform, + CLEANUP_TRANSFORM + ] + }, { + filename: './test/fixtures/webpack.2.config.js', + transforms: [ + customTransform, + CLEANUP_TRANSFORM + ] + }]); + }); + + it('should normalize `Object`', () => { + const options = ConfigExtendCommand.normalizeOptions({ + './test/fixtures/webpack.1.config.js': [customTransform, customTransform], + './test/fixtures/webpack.2.config.js': [customTransform] + }); + + expect(options).toEqual([{ + filename: './test/fixtures/webpack.1.config.js', + transforms: [ + customTransform, + customTransform, + CLEANUP_TRANSFORM + ] + }, { + filename: './test/fixtures/webpack.2.config.js', + transforms: [ + customTransform, + CLEANUP_TRANSFORM + ] + }]); + }); + }); + + describe('#execute()', () => { + it('should add `dependencyTree` property', () => { + command.execute(config, './test/fixtures/webpack.1.config.js'); + + const paths = getConfigDependencyTree(config); + + expect(config.dependencyTree).toEqual(jasmine.any(ConfigDependency)); + expect(paths).toEqual([ + resolve('./test/fixtures/webpack.1.config.js'), + resolve('./test/fixtures/webpack.2.config.js'), + resolve('./test/fixtures/webpack.3.config.js'), + resolve('./test/fixtures/webpack.5.config.js'), + resolve('./test/fixtures/webpack.4.config.js') + ]); + }); + + it('should execute successfully using `String`', () => { + command.execute(config, './test/fixtures/webpack.1.config.js'); + + expect(config.toObject()).toEqual({ + tags: [ + 'config1', + 'config2', + 'config3', + 'config5', + 'config4' + ] + }); + }); + + it('should execute successfully using `Object`', () => { + config.extend({ + './test/fixtures/webpack.1.config.js': x => { + expect(x).toEqual(jasmine.any(Config)); + + return { + foo: 'foo1' + }; + } + }); + + expect(config.toObject()).toEqual({ + foo: 'foo1' + }); + }); + + it('should execute successfully using `Object`', () => { + config.extend({ + './test/fixtures/webpack.1.config.js': [ + x => { + expect(x).toEqual(jasmine.any(Config)); + }, + x => { + expect(x).toEqual(jasmine.any(Config)); + + return { + foo: 'foo1' + }; + }, + x => { + expect(x).toEqual(jasmine.any(Config)); + + return x.merge({ + bar: 'bar1' + }); + } + ] + }); + + expect(config.toObject()).toEqual({ + foo: 'foo1', + bar: 'bar1' + }); + }); + }); +}); diff --git a/test/ConfigExtendTransform.spec.js b/test/ConfigExtendTransform.spec.js deleted file mode 100644 index a247e76..0000000 --- a/test/ConfigExtendTransform.spec.js +++ /dev/null @@ -1,90 +0,0 @@ -import { - last -} from 'lodash'; -import ConfigExtendTransform from '../src/ConfigExtendTransform'; - -describe('ConfigExtendTransform', () => { - const transform1 = () => {}, - transform2 = () => {}; - - let map; - - beforeEach(() => { - map = new ConfigExtendTransform(); - }); - - describe('#set()', () => { - it('should throw `ReferenceError` when `value` is not `Function`', () => { - expect(() => { - map.set('./test/fixtures/webpack.1.config.js', false); - }).toThrowError(ReferenceError); - }); - - it('should add `ConfigExtendTransform.DEFAULT` and `ConfigExtendTransform.CLEANUP` by default', () => { - map.set('./test/fixtures/webpack.1.config.js'); - - expect(map.get('./test/fixtures/webpack.1.config.js')).toEqual([ - ConfigExtendTransform.DEFAULT, - ConfigExtendTransform.CLEANUP - ]); - }); - - it('should prepend `ConfigExtendTransform.CLEANUP`', () => { - map.set('./test/fixtures/webpack.1.config.js', transform1) - .set('./test/fixtures/webpack.1.config.js', transform1) - .set('./test/fixtures/webpack.1.config.js', transform1); - - expect(last(map.get('./test/fixtures/webpack.1.config.js'))).toEqual(ConfigExtendTransform.CLEANUP); - }); - }); - - describe('#setAll()', () => { - it('should add `String`', () => { - map.setAll('./test/fixtures/webpack.1.config.js'); - - expect(map.get('./test/fixtures/webpack.1.config.js')).toEqual([ - ConfigExtendTransform.DEFAULT, - ConfigExtendTransform.CLEANUP - ]); - }); - - it('should add `Object`', () => { - map.setAll({ - './test/fixtures/webpack.1.config.js': transform1 - }, { - './test/fixtures/webpack.2.config.js': transform2 - }); - - expect(map.get('./test/fixtures/webpack.1.config.js')).toEqual([ - transform1, - ConfigExtendTransform.CLEANUP - ]); - expect(map.get('./test/fixtures/webpack.2.config.js')).toEqual([ - transform2, - ConfigExtendTransform.CLEANUP - ]); - }); - - it('should add `Object`', () => { - map.setAll({ - './test/fixtures/webpack.1.config.js': [ - transform1, - transform2 - ] - }, { - './test/fixtures/webpack.1.config.js': [ - transform1, - transform2 - ] - }); - - expect(map.get('./test/fixtures/webpack.1.config.js')).toEqual([ - transform1, - transform2, - transform1, - transform2, - ConfigExtendTransform.CLEANUP - ]); - }); - }); -}); diff --git a/test/ConfigFactory.spec.js b/test/ConfigFactory.spec.js index 4115064..600c8f6 100644 --- a/test/ConfigFactory.spec.js +++ b/test/ConfigFactory.spec.js @@ -1,12 +1,17 @@ import Config from '../src/Config'; import ConfigList from '../src/ConfigList'; -import TestFactory from './helpers/TestFactory'; +import ConfigFactory from '../src/ConfigFactory'; +import MockConfigContainer from './helpers/MockConfigContainer'; describe('ConfigFactory', () => { - let factory; + let container = new MockConfigContainer(), + /** + * @type {ConfigFactory} + */ + factory; beforeEach(() => { - factory = TestFactory.createConfigFactory(); + factory = container.resolve(ConfigFactory); }); describe('#createConfig()', () => { @@ -34,7 +39,7 @@ describe('ConfigFactory', () => { }); }); - it('should create `MultiConfig` from `Object[]`', () => { + it('should create `ConfigList` from `Object[]`', () => { const configs = factory.createConfig([{ foo: 'foo1' }]); diff --git a/test/ConfigFinder.spec.js b/test/ConfigFinder.spec.js index 4bb0599..244d0f7 100644 --- a/test/ConfigFinder.spec.js +++ b/test/ConfigFinder.spec.js @@ -1,13 +1,18 @@ import { resolve } from 'path'; -import TestFactory from './helpers/TestFactory'; +import ConfigFinder from '../src/ConfigFinder'; +import MockConfigContainer from './helpers/MockConfigContainer'; describe('ConfigFinder', () => { - let finder; + let container = new MockConfigContainer(), + /** + * @type {ConfigFinder} + */ + finder; beforeEach(() => { - finder = TestFactory.createConfigFinder(); + finder = container.resolve(ConfigFinder); }); describe('#findConfigs()', () => { diff --git a/test/ConfigLoader.spec.js b/test/ConfigLoader.spec.js index 6c11271..b37fa25 100644 --- a/test/ConfigLoader.spec.js +++ b/test/ConfigLoader.spec.js @@ -1,13 +1,18 @@ import { resolve } from 'path'; -import TestFactory from './helpers/TestFactory'; +import ConfigLoader from '../src/ConfigLoader'; +import MockConfigContainer from './helpers/MockConfigContainer'; describe('ConfigLoader', () => { - let loader; + let container = new MockConfigContainer(), + /** + * @type {ConfigLoader} + */ + loader; beforeEach(() => { - loader = TestFactory.createConfigLoader(); + loader = container.resolve(ConfigLoader); }); describe('#loadConfig()', () => { diff --git a/test/ConfigMergeCommand.spec.js b/test/ConfigMergeCommand.spec.js new file mode 100644 index 0000000..52ffff4 --- /dev/null +++ b/test/ConfigMergeCommand.spec.js @@ -0,0 +1,53 @@ +import ConfigMergeCommand from '../src/ConfigMergeCommand'; +import MockConfigContainer from './helpers/MockConfigContainer'; +import getConfigCommand from './helpers/getConfigCommand'; + +describe('ConfigMergeCommand', () => { + let container = new MockConfigContainer(), + /** + * @type {Config} + */ + config, + /** + * @type {ConfigMergeCommand} + */ + command; + + beforeEach(() => { + [config, command] = getConfigCommand(container, ConfigMergeCommand); + }); + + describe('#execute()', () => { + it('should execute successfully', () => { + command.execute(config, { + foo: { + bar: 'bar1' + }, + bar: ['bar1'] + }); + command.execute(config, { + foo: { + bar: 'bar2' + }, + bar: ['bar2'] + }); + command.execute(config, x => { + expect(x).toBe(config); + + return { + foo: { + bar: 'bar3' + } + }; + }); + command.execute(config, () => {}); + + expect(config.toObject()).toEqual({ + foo: { + bar: 'bar3' + }, + bar: ['bar1', 'bar2'] + }); + }); + }); +}); diff --git a/test/ConfigNameResolver.spec.js b/test/ConfigNameResolver.spec.js index 12b077d..89e48b3 100644 --- a/test/ConfigNameResolver.spec.js +++ b/test/ConfigNameResolver.spec.js @@ -1,11 +1,16 @@ -import TestFactory from './helpers/TestFactory'; +import ConfigNameResolver from '../src/ConfigNameResolver'; +import MockConfigContainer from './helpers/MockConfigContainer'; describe('ConfigNameResolver', () => { - let environment, - nameResolver; + let container = new MockConfigContainer(), + /** + * @type {ConfigNameResolver} + */ + nameResolver, + environment; beforeEach(() => { - nameResolver = TestFactory.createConfigNameResolver(); + nameResolver = container.resolve(ConfigNameResolver); environment = nameResolver.environment; }); diff --git a/test/ConfigOptionsResolver.spec.js b/test/ConfigOptionsResolver.spec.js new file mode 100644 index 0000000..a58a528 --- /dev/null +++ b/test/ConfigOptionsResolver.spec.js @@ -0,0 +1,69 @@ +import Config from '../src/Config'; +import ConfigOptionsResolver from '../src/ConfigOptionsResolver'; +import MockConfigContainer from './helpers/MockConfigContainer'; + +describe('ConfigOptionsResolver', () => { + let container = new MockConfigContainer(), + /** + * @type {Config} + */ + config, + /** + * @type {ConfigOptionsResolver} + */ + optionsResolver, + environment; + + beforeEach(() => { + config = container.resolve(Config); + optionsResolver = container.resolve(ConfigOptionsResolver); + environment = optionsResolver.nameResolver.environment; + }); + + describe('#resolve()', () => { + beforeEach(() => { + environment.setAll({ + foo: 'foo1', + bar: 'bar1' + }); + }); + + it('should resolve `Function` values', () => { + const obj = { + foo: 'foo1' + }, + options = optionsResolver.resolve(config, x => { + expect(x).toEqual(jasmine.any(Config)); + + return obj; + }); + + expect(options).toEqual(obj); + }); + + it('should resolve `Object` values', () => { + const obj = { + bar: 'bar1' + }, + options = optionsResolver.resolve(config, obj); + + expect(options).toEqual(obj); + }); + + it('should resolve `environment` variables', () => { + const options = optionsResolver.resolve(config, { + foo: '[foo]', + deep: { + bar: '[bar]' + } + }); + + expect(options).toEqual({ + foo: 'foo1', + deep: { + bar: 'bar1' + } + }); + }); + }); +}); diff --git a/test/ConfigPathResolver.spec.js b/test/ConfigPathResolver.spec.js index ec0819a..4fe43ef 100644 --- a/test/ConfigPathResolver.spec.js +++ b/test/ConfigPathResolver.spec.js @@ -1,13 +1,18 @@ import { resolve } from 'path'; -import TestFactory from './helpers/TestFactory'; +import ConfigPathResolver from '../src/ConfigPathResolver'; +import MockConfigContainer from './helpers/MockConfigContainer'; describe('ConfigPathResolver', () => { - let pathResolver; + let container = new MockConfigContainer(), + /** + * @type {ConfigPathResolver} + */ + pathResolver; beforeEach(() => { - pathResolver = TestFactory.createConfigPathResolver(); + pathResolver = container.resolve(ConfigPathResolver); }); describe('#resolve()', () => { diff --git a/test/ConfigPatternCache.spec.js b/test/ConfigPatternCache.spec.js index 3fdf73e..9c28d2c 100644 --- a/test/ConfigPatternCache.spec.js +++ b/test/ConfigPatternCache.spec.js @@ -1,10 +1,15 @@ -import TestFactory from './helpers/TestFactory'; +import ConfigPatternCache from '../src/ConfigPatternCache'; +import MockConfigContainer from './helpers/MockConfigContainer'; describe('ConfigPatternCache', () => { - let patternCache; + let container = new MockConfigContainer(), + /** + * @type {ConfigPatternCache} + */ + patternCache; beforeEach(() => { - patternCache = TestFactory.createConfigPatternCache(); + patternCache = container.resolve(ConfigPatternCache); }); describe('#set()', () => { diff --git a/test/ConfigProxy.spec.js b/test/ConfigProxy.spec.js new file mode 100644 index 0000000..98f94f0 --- /dev/null +++ b/test/ConfigProxy.spec.js @@ -0,0 +1,52 @@ +import ConfigDependency from '../src/ConfigDependency'; +import ConfigProxy from '../src'; +import getConfigDependencyTree from './helpers/getConfigDependencyTree'; + +describe('ConfigProxy', () => { + let config; + + beforeEach(() => { + config = new ConfigProxy(); + }); + + describe('#merge()', () => { + it('should do merge successfully', () => { + config.merge({ + foo: 'foo1', + bar: 'bar1' + }); + + expect(config.toObject()).toEqual({ + foo: 'foo1', + bar: 'bar1' + }); + }); + }); + + describe('#defaults()', () => { + it('should do merge successfully', () => { + config.merge({ + foo: 'foo1', + bar: 'bar1' + }).defaults({ + bar: 'bar2' + }); + + expect(config.toObject()).toEqual({ + foo: 'foo1', + bar: 'bar1' + }); + }); + }); + + describe('#extend()', () => { + it('should do extend successfully', () => { + config.extend('./test/fixtures/webpack.1.config.js'); + + const paths = getConfigDependencyTree(config); + + expect(config.dependencyTree).toEqual(jasmine.any(ConfigDependency)); + expect(paths.length).toEqual(5); + }); + }); +}); diff --git a/test/helpers/MockConfigContainer.js b/test/helpers/MockConfigContainer.js new file mode 100644 index 0000000..e8924a6 --- /dev/null +++ b/test/helpers/MockConfigContainer.js @@ -0,0 +1,81 @@ +import { + Transient +} from 'constitute'; +import ConfigContainer from '../../src/ConfigContainer'; +import ConfigPatternCache from '../../src/ConfigPatternCache'; +import ConfigEnvironment from '../../src/ConfigEnvironment'; +import ConfigNameResolver from '../../src/ConfigNameResolver'; +import ConfigPathResolver from '../../src/ConfigPathResolver'; +import ConfigCache from '../../src/ConfigCache'; +import ConfigLoader from '../../src/ConfigLoader'; +import ConfigFactory from '../../src/ConfigFactory'; +import ConfigFinder from '../../src/ConfigFinder'; +import Config from '../../src/Config'; +import ConfigBuilder from '../../src/ConfigBuilder'; +import ConfigOptionsResolver from '../../src/ConfigOptionsResolver'; +import ConfigDefaultsCommand from '../../src/ConfigDefaultsCommand'; +import ConfigMergeCommand from '../../src/ConfigMergeCommand'; +import ConfigExtendCommand from '../../src/ConfigExtendCommand'; + +/** + * @class + * @extends {MockConfigContainer} + */ +class MockConfigContainer extends ConfigContainer { + /** + * @override + */ + setUp() { + const container = this.container; + + container.bindValue(ConfigContainer, this); + container.bindClass(ConfigPatternCache, ConfigPatternCache, Transient.with([])); + container.bindClass(ConfigEnvironment, ConfigEnvironment, Transient.with([])); + container.bindClass(ConfigNameResolver, ConfigNameResolver, Transient.with([ + ConfigEnvironment, + ConfigPatternCache + ])); + container.bindClass(ConfigPathResolver, ConfigPathResolver, Transient.with([ + ConfigNameResolver + ])); + container.bindClass(ConfigCache, ConfigCache, Transient.with([ + ConfigEnvironment + ])); + container.bindClass(ConfigLoader, ConfigLoader, Transient.with([ + ConfigPathResolver, + ConfigCache, + ConfigFactory + ])); + container.bindClass(ConfigFactory, ConfigFactory, Transient.with([ + ConfigContainer + ])); + container.bindClass(ConfigFinder, ConfigFinder, Transient.with([ + ConfigPathResolver + ])); + container.bindClass(Config, Config, Transient.with([ + ConfigFactory, + ConfigDefaultsCommand, + ConfigMergeCommand, + ConfigExtendCommand + ])); + container.bindClass(ConfigBuilder, ConfigBuilder, Transient.with([ + ConfigFactory + ])); + container.bindClass(ConfigOptionsResolver, ConfigOptionsResolver, Transient.with([ + ConfigNameResolver + ])); + container.bindClass(ConfigDefaultsCommand, ConfigDefaultsCommand, Transient.with([ + ConfigOptionsResolver + ])); + container.bindClass(ConfigMergeCommand, ConfigMergeCommand, Transient.with([ + ConfigOptionsResolver + ])); + container.bindClass(ConfigExtendCommand, ConfigExtendCommand, Transient.with([ + ConfigOptionsResolver, + ConfigLoader, + ConfigFactory + ])); + } +} + +export default MockConfigContainer; diff --git a/test/helpers/TestFactory.js b/test/helpers/TestFactory.js deleted file mode 100644 index bf4b920..0000000 --- a/test/helpers/TestFactory.js +++ /dev/null @@ -1,100 +0,0 @@ -import Config from '../../src/Config'; -import ConfigLoader from '../../src/ConfigLoader'; -import ConfigEnvironment from '../../src/ConfigEnvironment'; -import ConfigNameResolver from '../../src/ConfigNameResolver'; -import ConfigPatternCache from '../../src/ConfigPatternCache'; -import ConfigPathResolver from '../../src/ConfigPathResolver'; -import ConfigCache from '../../src/ConfigCache'; -import ConfigFactory from '../../src/ConfigFactory'; -import ConfigBuilder from '../../src/ConfigBuilder'; -import ConfigFinder from '../../src/ConfigFinder'; - -class TestFactory { - /** - * @returns {ConfigBuilder} - */ - static createConfigBuilder() { - const factory = TestFactory.createConfigFactory(); - - return new ConfigBuilder(factory); - } - - /** - * @returns {ConfigCache} - */ - static createConfigCache() { - return new ConfigCache(TestFactory.createConfigEnvironment()); - } - - /** - * @return {ConfigEnvironment} - */ - static createConfigEnvironment() { - return new ConfigEnvironment(); - } - - /** - * @return {ConfigFactory} - */ - static createConfigFactory() { - const loader = TestFactory.createConfigLoader(); - - return new ConfigFactory(loader); - } - - /** - * @return {ConfigFinder} - */ - static createConfigFinder() { - const pathResolver = TestFactory.createConfigPathResolver(); - - return new ConfigFinder(pathResolver); - } - - /** - * @return {ConfigLoader} - */ - static createConfigLoader() { - const pathResolver = TestFactory.createConfigPathResolver(), - cache = TestFactory.createConfigCache(); - - return new ConfigLoader(pathResolver, cache); - } - - /** - * @return {ConfigNameResolver} - */ - static createConfigNameResolver() { - const environment = TestFactory.createConfigEnvironment(), - patternCache = TestFactory.createConfigPatternCache(); - - return new ConfigNameResolver(environment, patternCache); - } - - /** - * @return {ConfigPathResolver} - */ - static createConfigPathResolver() { - const nameResolver = TestFactory.createConfigNameResolver(); - - return new ConfigPathResolver(nameResolver); - } - - /** - * @return {ConfigPatternCache} - */ - static createConfigPatternCache() { - return new ConfigPatternCache(); - } - - /** - * @return {Config} - */ - static createConfig() { - const loader = TestFactory.createConfigLoader(); - - return new Config(loader); - } -} - -export default TestFactory; diff --git a/test/helpers/getConfigCommand.js b/test/helpers/getConfigCommand.js new file mode 100644 index 0000000..820dd66 --- /dev/null +++ b/test/helpers/getConfigCommand.js @@ -0,0 +1,14 @@ +import Config from '../../src/Config'; + +/** + * @private + * @param {ConfigContainer} container + * @param {ConfigCommand} Command + * @returns {Array} + */ +export default (container, Command) => { + const config = container.resolve(Config), + command = container.resolve(Command); + + return [config, command]; +}; diff --git a/test/helpers/getConfigDependencyTree.js b/test/helpers/getConfigDependencyTree.js new file mode 100644 index 0000000..832e94e --- /dev/null +++ b/test/helpers/getConfigDependencyTree.js @@ -0,0 +1,13 @@ +/** + * @param {Config} config + * @returns {String[]} + */ +export default config => { + const paths = []; + + for (const {node} of config.dependencyTree) { + paths.push(node.root.filename); + } + + return paths; +}; diff --git a/test/index.spec.js b/test/index.spec.js index 324c66e..ce9533d 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -11,6 +11,7 @@ import { ConfigFinder, ConfigFactory, ConfigBuilder as ConfigBuilderProxy, + ConfigOptionsResolver, environment, cache, patternCache, @@ -18,7 +19,8 @@ import { pathResolver, loader, finder, - factory + factory, + optionsResolver } from '../src'; describe('Module', () => { @@ -34,7 +36,8 @@ describe('Module', () => { [ConfigPathResolver, pathResolver], [ConfigLoader, loader], [ConfigFinder, finder], - [ConfigFactory, factory] + [ConfigFactory, factory], + [ConfigOptionsResolver, optionsResolver] ]; proxies.forEach(proxy => {