From 927bee82f75ad8a1d47decf3f86f09f0e9def2ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 21 Oct 2014 09:38:25 +0200 Subject: [PATCH 1/5] Use loopback 2.x in unit tests. --- package.json | 2 +- test/browser.test.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 779f7d2..969053b 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "browserify": "^4.1.8", "fs-extra": "^0.10.0", "jshint": "^2.5.6", - "loopback": "^1.5.0", + "loopback": "^2.5.0", "mocha": "^1.19.0", "must": "^0.12.0", "supertest": "^0.13.0" diff --git a/test/browser.test.js b/test/browser.test.js index 869b451..d8e5019 100644 --- a/test/browser.test.js +++ b/test/browser.test.js @@ -69,6 +69,9 @@ function createBrowserLikeContext() { debug: process.env.DEBUG }, + // used by DataSource.prototype.ready + setTimeout: setTimeout, + // used by `debug` module document: { documentElement: { style: {} } }, From c04946073f350a66e1c622103b1b70b4465ffd58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 21 Oct 2014 09:39:57 +0200 Subject: [PATCH 2/5] package: update dependency versions --- package.json | 8 ++++---- test/browser.test.js | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 969053b..bfa2f3d 100644 --- a/package.json +++ b/package.json @@ -27,17 +27,17 @@ "commondir": "0.0.1", "debug": "^2.0.0", "lodash.clonedeep": "^2.4.1", - "semver": "^2.3.0", + "semver": "^4.1.0", "toposort": "^0.2.10", "underscore": "^1.6.0" }, "devDependencies": { - "browserify": "^4.1.8", - "fs-extra": "^0.10.0", + "browserify": "^6.1.0", + "fs-extra": "^0.12.0", "jshint": "^2.5.6", "loopback": "^2.5.0", "mocha": "^1.19.0", "must": "^0.12.0", - "supertest": "^0.13.0" + "supertest": "^0.14.0" } } diff --git a/test/browser.test.js b/test/browser.test.js index d8e5019..87769d1 100644 --- a/test/browser.test.js +++ b/test/browser.test.js @@ -33,6 +33,7 @@ describe('browser support', function() { function browserifyTestApp(appDir, next) { var b = browserify({ basedir: appDir, + debug: true }); b.require('./app.js', { expose: 'browser-app' }); @@ -40,7 +41,7 @@ function browserifyTestApp(appDir, next) { var bundlePath = sandbox.resolve('browser-app-bundle.js'); var out = fs.createWriteStream(bundlePath); - b.bundle({ debug: true }).pipe(out); + b.bundle().pipe(out); out.on('error', function(err) { return next(err); }); out.on('close', function() { From 26abb43ad43118472e0d57caa83722accd0f4264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 21 Oct 2014 10:06:28 +0200 Subject: [PATCH 3/5] Skip definitions of built-in loopback models LoopBack built-in models are special: they follow the loopback-boot structure and provide `common/models/{name}.json` files, but they are also automatically loaded (created) by loopback. This change modifies `executor` to recognize built-in models and do not redefine them. --- lib/executor.js | 13 +++++++++++++ test/executor.test.js | 16 ++++++++++++++++ test/fixtures/browser-app/model-config.json | 6 ++++++ 3 files changed, 35 insertions(+) diff --git a/lib/executor.js b/lib/executor.js index ceef61f..61d38e8 100644 --- a/lib/executor.js +++ b/lib/executor.js @@ -156,6 +156,10 @@ function defineModels(app, instructions) { throw new Error('Cannot configure unknown model ' + name); } debug('Configuring existing model %s', name); + } else if (isBuiltinLoopBackModel(app, data)) { + model = app.loopback[name]; + assert(model, 'app.loopback[model] should have been defined'); + debug('Configuring built-in LoopBack model %s', name); } else { debug('Creating new model %s %j', name, data.definition); model = app.loopback.createModel(data.definition); @@ -176,6 +180,15 @@ function defineModels(app, instructions) { }); } +function isBuiltinLoopBackModel(app, data) { + // 1. Built-in models are exposed on the loopback object + if (!app.loopback[data.name]) return false; + + // 2. Built-in models have a script file `loopback/{facet}/models/{name}.js` + return data.sourceFile && + /node_modules\/loopback\/[^\/]+\/models\/[^\/]+\.js$/.test(data.sourceFile); +} + function forEachKeyedObject(obj, fn) { if(typeof obj !== 'object') return; diff --git a/test/executor.test.js b/test/executor.test.js index 4a63929..c4f248c 100644 --- a/test/executor.test.js +++ b/test/executor.test.js @@ -160,6 +160,22 @@ describe('executor', function() { expect(actual).to.equal('not attached'); }); + it('skips definition of already defined LoopBack models', function() { + var builtinModel = { + name: 'User', + definition: fs.readJsonFileSync( + require.resolve('loopback/common/models/user.json') + ), + config: { dataSource: 'db' }, + sourceFile: require.resolve('loopback/common/models/user.js') + }; + builtinModel.definition.redefined = true; + + boot.execute(app, someInstructions({ models: [ builtinModel ] })); + + expect(app.models.User.settings.redefined, 'redefined').to.not.equal(true); + }); + describe('with boot and models files', function() { beforeEach(function() { process.bootFlags = process.bootFlags || []; diff --git a/test/fixtures/browser-app/model-config.json b/test/fixtures/browser-app/model-config.json index 3566c55..d895c8a 100644 --- a/test/fixtures/browser-app/model-config.json +++ b/test/fixtures/browser-app/model-config.json @@ -1,4 +1,10 @@ { + "_meta": { + "sources": [ + "./models", + "../../../node_modules/loopback/common/models" + ] + }, "Customer": { "dataSource": "db" } From aa4cbdd80ffbd3effd43a0daac16e0d9fd30c879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 21 Oct 2014 16:51:17 +0200 Subject: [PATCH 4/5] compiler: support module-relative model sources Interpret model sources in the same way how `require.resolve` interprets the path: - values starting with `./` and `../` are relative to the file where they are specified - other values are relative to node modules folders This way it's possible to specify a source `loopback/common/models` and have it resolved to whatever place the loopback is installed. --- lib/compiler.js | 38 ++++++++++++++++++++- test/compiler.test.js | 24 +++++++++++++ test/fixtures/browser-app/model-config.json | 2 +- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/lib/compiler.js b/lib/compiler.js index d5d870a..3e1df63 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -5,6 +5,7 @@ var path = require('path'); var toposort = require('toposort'); var ConfigLoader = require('./config-loader'); var debug = require('debug')('loopback:boot:compiler'); +var Module = require('module'); /** * Gather all bootstrap-related configuration data and compile it into @@ -245,7 +246,12 @@ function findModelDefinitions(rootDir, sources) { var registry = {}; sources.forEach(function(src) { - var srcDir = path.resolve(rootDir, src); + var srcDir = resolveSourceDir(rootDir, src); + if (!srcDir) { + debug('Skipping unknown module source dir %j', src); + return; + } + var files = tryReadDir(srcDir); files .filter(function(f) { @@ -267,6 +273,36 @@ function findModelDefinitions(rootDir, sources) { return registry; } +function resolveSourceDir(rootDir, sourceDir) { + var srcDir = path.resolve(rootDir, sourceDir); + if (fs.existsSync(srcDir)) + return srcDir; + + // Handle module-relative path, e.g. `loopback/common/models` + var start = sourceDir.substring(0, 2); + if (start !== './' && start !== '..') { + // Module.globalPaths is a list of globally configured paths like + // [ env.NODE_PATH values, $HOME/.node_modules, etc. ] + // Module._nodeModulePaths(rootDir) returns a list of paths like + // [ rootDir/node_modules, rootDir/../node_modules, etc. ] + var modulePaths = Module.globalPaths + .concat(Module._nodeModulePaths(rootDir)); + + srcDir = modulePaths + .map(function(candidateDir) { + return path.join(candidateDir, sourceDir); + }) + .filter(function(candidate) { + return fs.existsSync(candidate); + }) + [0]; + if (srcDir) + return srcDir; + } + + return undefined; +} + function loadModelDefinition(rootDir, jsonFile) { var definition = require(jsonFile); diff --git a/test/compiler.test.js b/test/compiler.test.js index 45e17ec..f902140 100644 --- a/test/compiler.test.js +++ b/test/compiler.test.js @@ -501,6 +501,30 @@ describe('compiler', function() { }); }); + it('supports sources relative to node_modules', function() { + appdir.createConfigFilesSync({}, {}, { + User: { dataSource: 'db' } + }); + + var instructions = boot.compile({ + appRootDir: appdir.PATH, + modelSources: [ + 'loopback/common/models', + 'loopback/common/dir-does-not-exist' + ] + }); + + expect(instructions.models).to.have.length(1); + expect(instructions.models[0]).to.eql({ + name: 'User', + config: { + dataSource: 'db' + }, + definition: require('loopback/common/models/user.json'), + sourceFile: require.resolve('loopback/common/models/user.js') + }); + }); + it('handles model definitions with no code', function() { appdir.createConfigFilesSync({}, {}, { Car: { dataSource: 'db' } diff --git a/test/fixtures/browser-app/model-config.json b/test/fixtures/browser-app/model-config.json index d895c8a..c0686a2 100644 --- a/test/fixtures/browser-app/model-config.json +++ b/test/fixtures/browser-app/model-config.json @@ -2,7 +2,7 @@ "_meta": { "sources": [ "./models", - "../../../node_modules/loopback/common/models" + "loopback/common/models" ] }, "Customer": { From 5cea78c6aa498e148488f5ef16c8137c0fd8a23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Wed, 22 Oct 2014 08:57:44 +0200 Subject: [PATCH 5/5] 2.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bfa2f3d..61d1219 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-boot", - "version": "2.1.0", + "version": "2.2.0", "description": "Convention-based bootstrapper for LoopBack applications", "keywords": [ "StrongLoop",