Merge branch 'release/2.2.0' into production
This commit is contained in:
commit
56c74174f1
|
@ -5,6 +5,7 @@ var path = require('path');
|
||||||
var toposort = require('toposort');
|
var toposort = require('toposort');
|
||||||
var ConfigLoader = require('./config-loader');
|
var ConfigLoader = require('./config-loader');
|
||||||
var debug = require('debug')('loopback:boot:compiler');
|
var debug = require('debug')('loopback:boot:compiler');
|
||||||
|
var Module = require('module');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gather all bootstrap-related configuration data and compile it into
|
* Gather all bootstrap-related configuration data and compile it into
|
||||||
|
@ -245,7 +246,12 @@ function findModelDefinitions(rootDir, sources) {
|
||||||
var registry = {};
|
var registry = {};
|
||||||
|
|
||||||
sources.forEach(function(src) {
|
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);
|
var files = tryReadDir(srcDir);
|
||||||
files
|
files
|
||||||
.filter(function(f) {
|
.filter(function(f) {
|
||||||
|
@ -267,6 +273,36 @@ function findModelDefinitions(rootDir, sources) {
|
||||||
return registry;
|
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) {
|
function loadModelDefinition(rootDir, jsonFile) {
|
||||||
var definition = require(jsonFile);
|
var definition = require(jsonFile);
|
||||||
|
|
||||||
|
|
|
@ -156,6 +156,10 @@ function defineModels(app, instructions) {
|
||||||
throw new Error('Cannot configure unknown model ' + name);
|
throw new Error('Cannot configure unknown model ' + name);
|
||||||
}
|
}
|
||||||
debug('Configuring existing model %s', 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 {
|
} else {
|
||||||
debug('Creating new model %s %j', name, data.definition);
|
debug('Creating new model %s %j', name, data.definition);
|
||||||
model = app.loopback.createModel(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) {
|
function forEachKeyedObject(obj, fn) {
|
||||||
if(typeof obj !== 'object') return;
|
if(typeof obj !== 'object') return;
|
||||||
|
|
||||||
|
|
12
package.json
12
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "loopback-boot",
|
"name": "loopback-boot",
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"description": "Convention-based bootstrapper for LoopBack applications",
|
"description": "Convention-based bootstrapper for LoopBack applications",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"StrongLoop",
|
"StrongLoop",
|
||||||
|
@ -27,17 +27,17 @@
|
||||||
"commondir": "0.0.1",
|
"commondir": "0.0.1",
|
||||||
"debug": "^2.0.0",
|
"debug": "^2.0.0",
|
||||||
"lodash.clonedeep": "^2.4.1",
|
"lodash.clonedeep": "^2.4.1",
|
||||||
"semver": "^2.3.0",
|
"semver": "^4.1.0",
|
||||||
"toposort": "^0.2.10",
|
"toposort": "^0.2.10",
|
||||||
"underscore": "^1.6.0"
|
"underscore": "^1.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"browserify": "^4.1.8",
|
"browserify": "^6.1.0",
|
||||||
"fs-extra": "^0.10.0",
|
"fs-extra": "^0.12.0",
|
||||||
"jshint": "^2.5.6",
|
"jshint": "^2.5.6",
|
||||||
"loopback": "^1.5.0",
|
"loopback": "^2.5.0",
|
||||||
"mocha": "^1.19.0",
|
"mocha": "^1.19.0",
|
||||||
"must": "^0.12.0",
|
"must": "^0.12.0",
|
||||||
"supertest": "^0.13.0"
|
"supertest": "^0.14.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ describe('browser support', function() {
|
||||||
function browserifyTestApp(appDir, next) {
|
function browserifyTestApp(appDir, next) {
|
||||||
var b = browserify({
|
var b = browserify({
|
||||||
basedir: appDir,
|
basedir: appDir,
|
||||||
|
debug: true
|
||||||
});
|
});
|
||||||
b.require('./app.js', { expose: 'browser-app' });
|
b.require('./app.js', { expose: 'browser-app' });
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ function browserifyTestApp(appDir, next) {
|
||||||
|
|
||||||
var bundlePath = sandbox.resolve('browser-app-bundle.js');
|
var bundlePath = sandbox.resolve('browser-app-bundle.js');
|
||||||
var out = fs.createWriteStream(bundlePath);
|
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('error', function(err) { return next(err); });
|
||||||
out.on('close', function() {
|
out.on('close', function() {
|
||||||
|
@ -69,6 +70,9 @@ function createBrowserLikeContext() {
|
||||||
debug: process.env.DEBUG
|
debug: process.env.DEBUG
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// used by DataSource.prototype.ready
|
||||||
|
setTimeout: setTimeout,
|
||||||
|
|
||||||
// used by `debug` module
|
// used by `debug` module
|
||||||
document: { documentElement: { style: {} } },
|
document: { documentElement: { style: {} } },
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
it('handles model definitions with no code', function() {
|
||||||
appdir.createConfigFilesSync({}, {}, {
|
appdir.createConfigFilesSync({}, {}, {
|
||||||
Car: { dataSource: 'db' }
|
Car: { dataSource: 'db' }
|
||||||
|
|
|
@ -160,6 +160,22 @@ describe('executor', function() {
|
||||||
expect(actual).to.equal('not attached');
|
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() {
|
describe('with boot and models files', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
process.bootFlags = process.bootFlags || [];
|
process.bootFlags = process.bootFlags || [];
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
{
|
{
|
||||||
|
"_meta": {
|
||||||
|
"sources": [
|
||||||
|
"./models",
|
||||||
|
"loopback/common/models"
|
||||||
|
]
|
||||||
|
},
|
||||||
"Customer": {
|
"Customer": {
|
||||||
"dataSource": "db"
|
"dataSource": "db"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue