diff --git a/lib/config-loader.js b/lib/config-loader.js index 9151fff..cc7ac42 100644 --- a/lib/config-loader.js +++ b/lib/config-loader.js @@ -1,6 +1,7 @@ var fs = require('fs'); var path = require('path'); var debug = require('debug')('loopback:boot:config-loader'); +var assert = require('assert'); var ConfigLoader = exports; @@ -173,36 +174,59 @@ function mergeMiddlewareConfig(target, config, fileName) { } } +function mergeNamedItems(arr1, arr2, key) { + assert(Array.isArray(arr1), 'invalid array: ' + arr1); + assert(Array.isArray(arr2), 'invalid array: ' + arr2); + key = key || 'name'; + var result = [].concat(arr1); + for (var i = 0, n = arr2.length; i < n; i++) { + var item = arr2[i]; + var found = false; + if (item[key]) { + for (var j = 0, k = result.length; j < k; j++) { + if (result[j][key] === item[key]) { + mergeObjects(result[j], item); + found = true; + break; + } + } + } + if (!found) { + result.push(item); + } + } + return result; +} + function mergePhaseConfig(target, config, phase) { var err; - for (var middleware in config) { - if (middleware in target) { - var targetMiddleware = target[middleware]; - var configMiddleware = config[middleware]; + for (var mw in config) { + if (mw in target) { + var targetMiddleware = target[mw]; + var configMiddleware = config[mw]; if (Array.isArray(targetMiddleware) && Array.isArray(configMiddleware)) { // Both are arrays, combine them - target[middleware] = targetMiddleware.concat(configMiddleware); + target[mw] = mergeNamedItems(targetMiddleware, configMiddleware); } else if (Array.isArray(targetMiddleware)) { if (typeof configMiddleware === 'object' && Object.keys(configMiddleware).length) { // Config side is an non-empty object - targetMiddleware.push(configMiddleware); + target[mw] = mergeNamedItems(targetMiddleware, [configMiddleware]); } } else if (Array.isArray(configMiddleware)) { if (typeof targetMiddleware === 'object' && Object.keys(targetMiddleware).length) { // Target side is an non-empty object - targetMiddleware = target[middleware] = - [targetMiddleware].concat(configMiddleware); + target[mw] = mergeNamedItems([targetMiddleware], configMiddleware); } else { // Target side is empty - target[middleware] = configMiddleware; + target[mw] = configMiddleware; } } else { err = mergeObjects(targetMiddleware, configMiddleware); } } else { - err = 'The middleware "' + middleware + '" in phase "' + phase + '"' + + err = 'The middleware "' + mw + '" in phase "' + phase + '"' + 'is not defined in the main config.'; } if (err) return err; diff --git a/test/compiler.test.js b/test/compiler.test.js index 268859d..ee34e0e 100644 --- a/test/compiler.test.js +++ b/test/compiler.test.js @@ -1850,6 +1850,59 @@ describe('compiler', function() { ]); }); + it('merges config.params array to array by name', function() { + appdir.writeConfigFileSync('./middleware.json', { + routes: { + './middleware': [{ + name: 'a', + params: { + key: 'initial value' + } + }] + } + }); + + appdir.writeConfigFileSync('./middleware.local.json', { + routes: { + './middleware': [{ + name: 'a', + params: { + key: 'custom value' + } + }, { + params: { + key: '2nd value' + } + }] + } + }); + + var instructions = boot.compile(appdir.PATH); + + expect(instructions.middleware.middleware) + .to.eql([ + { + sourceFile: path.resolve(appdir.PATH, 'middleware'), + config: { + name: 'a', + phase: 'routes', + params: { + key: 'custom value' + } + } + }, + { + sourceFile: path.resolve(appdir.PATH, 'middleware'), + config: { + phase: 'routes', + params: { + key: '2nd value' + } + } + } + ]); + }); + it('flattens sub-phases', function() { appdir.writeConfigFileSync('middleware.json', { 'initial:after': {