From 4e15c526d49191d69e713a744d2a3b9ae6df4733 Mon Sep 17 00:00:00 2001 From: Pradnya Baviskar Date: Thu, 30 Apr 2015 11:23:44 +0530 Subject: [PATCH] support 'mixinsources' option - By default looks for mixinsources in directory - Loads only mixins used through models --- lib/compiler.js | 45 ++++++++++++-- test/compiler.test.js | 141 ++++++++++++++++++++++++++++++++++-------- test/executor.test.js | 19 ++++-- 3 files changed, 170 insertions(+), 35 deletions(-) diff --git a/lib/compiler.js b/lib/compiler.js index 70459ab..d093d07 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -85,8 +85,9 @@ module.exports = function compile(options) { modelsRootDir, modelsConfig, modelSources); var mixinDirs = options.mixinDirs || []; + var mixinSources = options.mixinSources || modelsMeta.mixins || ['./mixins']; var mixinInstructions = buildAllMixinInstructions( - appRootDir, mixinDirs, options); + appRootDir, mixinDirs, mixinSources, options, modelInstructions); // When executor passes the instruction to loopback methods, // loopback modifies the data. Since we are loading the data using `require`, @@ -606,16 +607,37 @@ function resolveAppScriptPath(rootDir, relativePath, resolveOptions) { return (fixedFile === undefined ? resolvedPath : fixedFile); } -function buildAllMixinInstructions(appRootDir, mixinDirs, options) { +function buildAllMixinInstructions(appRootDir, mixinDirs, mixinSources, options, + modelInstructions) { var extensions = _.without(_.keys(require.extensions), _.keys(getExcludedExtensions())); var files = options.mixins || []; - mixinDirs.forEach(function(dir) { + var mixins = loadMixins(appRootDir, mixinDirs, files, extensions, options); + if (mixins === undefined) return; + + // Fetch unique list of mixin names, used in models + var modelMixins = _.unique(fetchMixinsUsedInModels(modelInstructions)); + + files = []; + var mixinsFromMixinSources = loadMixins(appRootDir, mixinSources, files, + extensions, options); + if (mixinsFromMixinSources === undefined) return; + + // Filter in only mixins, that are used in models + mixinsFromMixinSources = filterMixins(mixinsFromMixinSources, modelMixins); + + mixins = mixins.concat(mixinsFromMixinSources); + + return mixins; +} + +function loadMixins(appRootDir, sourceDirs, files, extensions, options) { + sourceDirs.forEach(function(dir) { dir = tryResolveAppPath(appRootDir, dir); if (!dir) { debug('Skipping unknown module source dir %j', dir); - return; + return undefined; } files = files.concat(findScripts(dir, extensions)); }); @@ -640,6 +662,21 @@ function buildAllMixinInstructions(appRootDir, mixinDirs, options) { return mixins; } +function fetchMixinsUsedInModels(modelInstructions) { + return _.flatten(modelInstructions + .map(function(model) { + return model.definition && model.definition.mixins ? + Object.keys(model.definition.mixins) : []; + })); +} + +function filterMixins(mixinInstructions, includeMixins) { + return mixinInstructions + .filter(function(m) { + return (includeMixins.indexOf(m.name) === -1 ? false : true); + }); +} + function normalizeMixinName(str, options) { var normalization = options.normalization; switch (normalization) { diff --git a/test/compiler.test.js b/test/compiler.test.js index 9ed42e3..0973ed8 100644 --- a/test/compiler.test.js +++ b/test/compiler.test.js @@ -1016,41 +1016,129 @@ describe('compiler', function() { }); describe('for mixins', function() { - function verifyMixinIsFoundViaMixinDirs(sourceFile, mixinDirs) { - var appJS = appdir.writeFileSync(sourceFile, ''); + describe(' - mixinDirs', function() { + function verifyMixinIsFoundViaMixinDirs(sourceFile, mixinDirs) { + var appJS = appdir.writeFileSync(sourceFile, ''); - var instructions = boot.compile({ - appRootDir: appdir.PATH, - mixinDirs: mixinDirs + var instructions = boot.compile({ + appRootDir: appdir.PATH, + mixinDirs: mixinDirs + }); + + expect(instructions.mixins[0].sourceFile).to.eql(appJS); + } + + it('supports `mixinDirs` option', function() { + verifyMixinIsFoundViaMixinDirs('mixins/other.js', ['./mixins']); }); - expect(instructions.mixins[0].sourceFile).to.eql(appJS); - } + it('resolves relative path in `mixinDirs` option', function() { + verifyMixinIsFoundViaMixinDirs('custom-mixins/other.js', + ['./custom-mixins']); + }); - it('supports `mixinDirs` option', function() { - verifyMixinIsFoundViaMixinDirs('mixins/other.js', ['./mixins']); + it('resolves module relative path in `mixinDirs` option', function() { + verifyMixinIsFoundViaMixinDirs('node_modules/custom-mixins/other.js', + ['custom-mixins']); + }); }); - it('resolves relative path in `mixinDirs` option', function() { - verifyMixinIsFoundViaMixinDirs('custom-mixins/vehicle.js', - ['./custom-mixins']); - }); + describe(' - mixinSources', function() { + beforeEach(function() { + appdir.createConfigFilesSync({}, {}, { + Car: { dataSource: 'db' } + }); + appdir.writeConfigFileSync('models/car.json', { + name: 'Car', + mixins: {'TimeStamps': {} } + }); + }); - it('resolves module relative path in `mixinDirs` option', function() { - verifyMixinIsFoundViaMixinDirs('node_modules/custom-mixins/vehicle.js', - ['custom-mixins']); + function verifyMixinIsFoundViaMixinSources(sourceFile, mixinSources) { + var appJS = appdir.writeFileSync(sourceFile, ''); + + var instructions = boot.compile({ + appRootDir: appdir.PATH, + mixinSources: mixinSources + }); + + expect(instructions.mixins[0].sourceFile).to.eql(appJS); + } + + it('supports `mixinSources` option', function() { + verifyMixinIsFoundViaMixinSources('mixins/time-stamps.js', + ['./mixins']); + }); + + it('resolves relative path in `mixinSources` option', function() { + verifyMixinIsFoundViaMixinSources('custom-mixins/time-stamps.js', + ['./custom-mixins']); + }); + + it('resolves module relative path in `mixinSources` option', + function() { + verifyMixinIsFoundViaMixinSources( + 'node_modules/custom-mixins/time-stamps.js', + ['custom-mixins']); + }); + + it('supports `mixins` option in `model-config.json`', function() { + appdir.createConfigFilesSync({}, {}, { + _meta: { + mixins: ['./custom-mixins'] + }, + Car: { + dataSource: 'db' + } + }); + + var appJS = appdir.writeFileSync('custom-mixins/time-stamps.js', ''); + var instructions = boot.compile(appdir.PATH); + expect(instructions.mixins[0].sourceFile).to.eql(appJS); + }); + + it('sets by default `mixinSources` to `mixins` directory', function() { + var appJS = appdir.writeFileSync('mixins/time-stamps.js', ''); + var instructions = boot.compile(appdir.PATH); + expect(instructions.mixins[0].sourceFile).to.eql(appJS); + }); + + it('loads only mixins used by models', function() { + var appJS = appdir.writeFileSync('mixins/time-stamps.js', ''); + appdir.writeFileSync('mixins/foo.js', ''); + + var instructions = boot.compile(appdir.PATH); + expect(instructions.mixins).to.have.length(1); + expect(instructions.mixins[0].sourceFile).to.eql(appJS); + }); + + it('loads mixins from model using mixin name', function() { + var appJS = appdir.writeFileSync('mixins/time-stamps.js', ''); + appdir.writeConfigFileSync('mixins/time-stamps.json', { + name: 'Timestamping' + }); + + appdir.writeConfigFileSync('models/car.json', { + name: 'Car', + mixins: {'Timestamping': {} } + }); + + var instructions = boot.compile(appdir.PATH); + expect(instructions.mixins).to.have.length(1); + expect(instructions.mixins[0].sourceFile).to.eql(appJS); + }); }); describe('name normalization', function() { var options; beforeEach(function() { - options = { appRootDir: appdir.PATH, mixinDirs: ['./mixins'] }; + options = { appRootDir: appdir.PATH, mixinDirs: ['./custom-mixins'] }; - appdir.writeFileSync('mixins/foo.js', ''); - appdir.writeFileSync('mixins/time-stamps.js', ''); - appdir.writeFileSync('mixins/camelCase.js', ''); - appdir.writeFileSync('mixins/PascalCase.js', ''); - appdir.writeFileSync('mixins/space name.js', ''); + appdir.writeFileSync('custom-mixins/foo.js', ''); + appdir.writeFileSync('custom-mixins/time-stamps.js', ''); + appdir.writeFileSync('custom-mixins/camelCase.js', ''); + appdir.writeFileSync('custom-mixins/PascalCase.js', ''); + appdir.writeFileSync('custom-mixins/space name.js', ''); }); it('supports classify', function() { @@ -1146,15 +1234,15 @@ describe('compiler', function() { }); it('extends definition from JSON with same file name', function() { - var appJS = appdir.writeFileSync('mixins/foo-bar.js', ''); + var appJS = appdir.writeFileSync('custom-mixins/foo-bar.js', ''); - appdir.writeConfigFileSync('mixins/foo-bar.json', { + appdir.writeConfigFileSync('custom-mixins/foo-bar.json', { description: 'JSON file name same as JS file name' }); - appdir.writeConfigFileSync('mixins/FooBar.json', { + appdir.writeConfigFileSync('custom-mixins/FooBar.json', { description: 'JSON file name same as normalized name of mixin' }); var options = { appRootDir: appdir.PATH, - mixinDirs: ['./mixins'], + mixinDirs: ['./custom-mixins'], normalization: 'classify' }; var instructions = boot.compile(options); @@ -1166,7 +1254,6 @@ describe('compiler', function() { } ]); }); - }); }); diff --git a/test/executor.test.js b/test/executor.test.js index 850e46e..cbde95a 100644 --- a/test/executor.test.js +++ b/test/executor.test.js @@ -293,7 +293,8 @@ describe('executor', function() { }); describe ('for mixins', function() { - it('defines mixins from instructions', function() { + var options; + beforeEach(function() { appdir.writeFileSync('mixins/example.js', 'module.exports = ' + 'function(Model, options) {}'); @@ -306,15 +307,25 @@ describe('executor', function() { name: 'Timestamping' }); - var options = { - appRootDir: appdir.PATH, - mixinDirs: ['./mixins'] + options = { + appRootDir: appdir.PATH }; + }); + it('defines mixins from instructions - using `mixinDirs`', function() { + options.mixinDirs = ['./mixins']; boot(app, options); var modelBuilder = app.registry.modelBuilder; + var registry = modelBuilder.mixins.mixins; + expect(Object.keys(registry)).to.eql(['Example', 'Timestamping']); + }); + it('defines mixins from instructions - using `mixinSources`', function() { + options.mixinSources = ['./mixins']; + boot(app, options); + + var modelBuilder = app.registry.modelBuilder; var registry = modelBuilder.mixins.mixins; expect(Object.keys(registry)).to.eql(['Example', 'Timestamping']); });