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.
This commit is contained in:
parent
26abb43ad4
commit
aa4cbdd80f
|
@ -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);
|
||||
|
||||
|
|
|
@ -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' }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"_meta": {
|
||||
"sources": [
|
||||
"./models",
|
||||
"../../../node_modules/loopback/common/models"
|
||||
"loopback/common/models"
|
||||
]
|
||||
},
|
||||
"Customer": {
|
||||
|
|
Loading…
Reference in New Issue