Skip to content

Commit

Permalink
Merge pull request strongloop#86 from strongloop/feature/load-component
Browse files Browse the repository at this point in the history
Configure components via `component-config.json`
  • Loading branch information
bajtos committed Jan 7, 2015
2 parents 1ef2616 + f8247be commit 8aa7156
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 5 deletions.
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ var addInstructionsToBrowserify = require('./lib/bundler');
* for files containing model definitions.
* @property {Object} [middleware] Middleware configuration to use instead
* of `{appRootDir}/middleware.json`
* @property {Object} [components] Component configuration to use instead
* of `{appRootDir}/component-config.json`
* @property {Array.<String>} [bootDirs] List of directories where to look
* for boot scripts.
* @property {Array.<String>} [bootScripts] List of script files to execute
Expand Down
19 changes: 14 additions & 5 deletions lib/bundler.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var cloneDeep = require('lodash').cloneDeep;

module.exports = function addInstructionsToBrowserify(instructions, bundler) {
bundleModelScripts(instructions, bundler);
bundleComponentScripts(instructions, bundler);
bundleOtherScripts(instructions, bundler);
bundleInstructions(instructions, bundler);
};
Expand All @@ -22,19 +23,27 @@ function bundleOtherScripts(instructions, bundler) {
}

function bundleModelScripts(instructions, bundler) {
var files = instructions.models
bundleSourceFiles(instructions, 'models', bundler);
}

function bundleComponentScripts(instructions, bundler) {
bundleSourceFiles(instructions, 'components', bundler);
}

function bundleSourceFiles(instructions, type, bundler) {
var files = instructions[type]
.map(function(m) { return m.sourceFile; })
.filter(function(f) { return !!f; });

var modelToFileMapping = instructions.models
var instructionToFileMapping = instructions[type]
.map(function(m) { return files.indexOf(m.sourceFile); });

addScriptsToBundle('models', files, bundler);
addScriptsToBundle(type, files, bundler);

// Update `sourceFile` properties with the new paths
modelToFileMapping.forEach(function(fileIx, modelIx) {
instructionToFileMapping.forEach(function(fileIx, sourceIx) {
if (fileIx === -1) return;
instructions.models[modelIx].sourceFile = files[fileIx];
instructions[type][sourceIx].sourceFile = files[fileIx];
});
}

Expand Down
24 changes: 24 additions & 0 deletions lib/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ module.exports = function compile(options) {
var middlewareInstructions =
buildMiddlewareInstructions(middlewareRootDir, middlewareConfig);

var componentRootDir = appRootDir; // not configurable yet
var componentConfig = options.components ||
ConfigLoader.loadComponents(componentRootDir, env);
var componentInstructions =
buildComponentInstructions(componentRootDir, componentConfig);

// require directories
var bootDirs = options.bootDirs || []; // precedence
bootDirs = bootDirs.concat(path.join(appRootDir, 'boot'));
Expand Down Expand Up @@ -81,6 +87,7 @@ module.exports = function compile(options) {
dataSources: dataSourcesConfig,
models: modelInstructions,
middleware: middlewareInstructions,
components: componentInstructions,
files: {
boot: bootScripts
}
Expand Down Expand Up @@ -471,3 +478,20 @@ function resolveMiddlewareParams(rootDir, params) {
}
});
}

function buildComponentInstructions(rootDir, componentConfig) {
return Object.keys(componentConfig).map(function(name) {
var sourceFile;
if (name.indexOf('./') === 0 || name.indexOf('../') === 0) {
// Relative path
sourceFile = path.resolve(rootDir, name);
} else {
sourceFile = require.resolve(name);
}

return {
sourceFile: sourceFile,
config: componentConfig[name]
};
});
}
19 changes: 19 additions & 0 deletions lib/config-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ ConfigLoader.loadMiddleware = function(rootDir, env) {
return loadNamed(rootDir, env, 'middleware', mergeMiddlewareConfig);
};

/**
* Load component config from `component-config.json` and friends.
* @param {String} rootDir Directory where to look for files.
* @param {String} env Environment, usually `process.env.NODE_ENV`
* @returns {Object}
*/
ConfigLoader.loadComponents = function(rootDir, env) {
return loadNamed(rootDir, env, 'component-config', mergeComponentConfig);
};

/*-- Implementation --*/

/**
Expand Down Expand Up @@ -162,6 +172,15 @@ function mergePhaseConfig(target, config, phase) {
}
}

function mergeComponentConfig(target, config, fileName) {
for (var c in target) {
var err = mergeObjects(target[c], config[c]);
if (err) {
throw new Error('Cannot apply ' + fileName + ' to `' + c + '`: ' + err);
}
}
}

function mergeObjects(target, config, keyPrefix) {
for (var key in config) {
var fullKey = keyPrefix ? keyPrefix + '.' + key : key;
Expand Down
9 changes: 9 additions & 0 deletions lib/executor.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module.exports = function execute(app, instructions, callback) {
setupDataSources(app, instructions);
setupModels(app, instructions);
setupMiddleware(app, instructions);
setupComponents(app, instructions);

// Run the boot scripts in series synchronously or asynchronously
// Please note async supports both styles
Expand Down Expand Up @@ -282,6 +283,14 @@ function setupMiddleware(app, instructions) {
});
}

function setupComponents(app, instructions) {
instructions.components.forEach(function(data) {
debug('Configuring component %j', data.sourceFile);
var configFn = require(data.sourceFile);
configFn(app, data.config);
});
}

function runBootScripts(app, instructions, callback) {
runScripts(app, instructions.files.boot, callback);
}
Expand Down
4 changes: 4 additions & 0 deletions test/browser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ describe('browser support', function() {
expect(app.models.Customer.settings)
.to.have.property('_customized', 'Customer');

// configured in fixtures/browser-app/component-config.json
// and fixtures/browser-app/components/dummy-component.js
expect(app.dummyComponentOptions).to.eql({ option: 'value' });

done();
});
});
Expand Down
29 changes: 29 additions & 0 deletions test/compiler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,35 @@ describe('compiler', function() {
});
});
});

describe('for components', function() {
it('loads component configs from multiple files', function() {
appdir.createConfigFilesSync();
appdir.writeConfigFileSync('component-config.json', {
debug: { option: 'value' }
});
appdir.writeConfigFileSync('component-config.local.json', {
debug: { local: 'applied' }
});

var env = process.env.NODE_ENV || 'development';
appdir.writeConfigFileSync('component-config.' + env + '.json', {
debug: { env: 'applied' }
});

var instructions = boot.compile(appdir.PATH);

var component = instructions.components[0];
expect(component).to.eql({
sourceFile: require.resolve('debug'),
config: {
option: 'value',
local: 'applied',
env: 'applied'
}
});
});
});
});

function getNameProperty(obj) {
Expand Down
17 changes: 17 additions & 0 deletions test/executor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,22 @@ describe('executor', function() {
done();
});
});

it('configures components', function() {
appdir.writeConfigFileSync('component-config.json', {
'./components/test-component': {
option: 'value'
}
});

appdir.writeFileSync('components/test-component/index.js',
'module.exports = ' +
'function(app, options) { app.componentOptions = options; }');

boot(app, appdir.PATH);

expect(app.componentOptions).to.eql({ option: 'value' });
});
});

function assertValidDataSource(dataSource) {
Expand All @@ -439,6 +455,7 @@ function someInstructions(values) {
models: values.models || [],
dataSources: values.dataSources || { db: { connector: 'memory' } },
middleware: values.middleware || { phases: [], middleware: [] },
components: values.components || [],
files: {
boot: []
}
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/browser-app/component-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"./components/dummy-component": {
"option": "value"
}
}
3 changes: 3 additions & 0 deletions test/fixtures/browser-app/components/dummy-component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = function(app, options) {
app.dummyComponentOptions = options;
};

0 comments on commit 8aa7156

Please sign in to comment.