Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
};