Provide scriptExtensions option
This commit is contained in:
parent
79d9ddb835
commit
d68ffc6f6f
|
@ -3,6 +3,7 @@
|
|||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
var _ = require('lodash');
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
var utils = require('./utils');
|
||||
|
@ -10,6 +11,7 @@ var path = require('path');
|
|||
var pluginLoader = require('./plugin-loader');
|
||||
var debug = require('debug')('loopback:boot:bootstrapper');
|
||||
var Promise = require('bluebird');
|
||||
var arrayToObject = require('./utils').arrayToObject;
|
||||
|
||||
module.exports = Bootstrapper;
|
||||
|
||||
|
@ -58,13 +60,20 @@ function Bootstrapper(options) {
|
|||
options = { appRootDir: options };
|
||||
}
|
||||
|
||||
// For setting properties without modifying the original object
|
||||
options = Object.create(options);
|
||||
|
||||
var appRootDir = options.appRootDir = options.appRootDir || process.cwd();
|
||||
var env = options.env || process.env.NODE_ENV || 'development';
|
||||
var scriptExtensions = options.scriptExtensions ?
|
||||
arrayToObject(options.scriptExtensions) :
|
||||
require.extensions;
|
||||
|
||||
var appConfigRootDir = options.appConfigRootDir || appRootDir;
|
||||
|
||||
options.rootDir = appConfigRootDir;
|
||||
options.env = env;
|
||||
options.scriptExtensions = scriptExtensions;
|
||||
this.options = options;
|
||||
|
||||
this.phases = options.phases || builtinPhases;
|
||||
|
@ -200,4 +209,3 @@ Bootstrapper.prototype.run = function(context, done) {
|
|||
});
|
||||
return done.promise;
|
||||
};
|
||||
|
||||
|
|
|
@ -33,9 +33,13 @@ PluginScript.prototype.load = function(context) {
|
|||
utils.resolveRelativePaths(pluginScripts, appRootDir);
|
||||
|
||||
pluginDirs.forEach(function(dir) {
|
||||
pluginScripts = pluginScripts.concat(utils.findScripts(dir));
|
||||
pluginScripts = pluginScripts.concat(
|
||||
utils.findScripts(dir, options.scriptExtensions)
|
||||
);
|
||||
var envdir = dir + '/' + options.env;
|
||||
pluginScripts = pluginScripts.concat(utils.findScripts(envdir));
|
||||
pluginScripts = pluginScripts.concat(
|
||||
utils.findScripts(envdir, options.scriptExtensions)
|
||||
);
|
||||
});
|
||||
|
||||
pluginScripts = _.uniq(pluginScripts);
|
||||
|
@ -58,5 +62,3 @@ PluginScript.prototype.compile = function(context) {
|
|||
plugins[name] = handler;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -34,9 +34,13 @@ Script.prototype.load = function(context) {
|
|||
utils.resolveRelativePaths(bootScripts, appRootDir);
|
||||
|
||||
bootDirs.forEach(function(dir) {
|
||||
bootScripts = bootScripts.concat(utils.findScripts(dir));
|
||||
bootScripts = bootScripts.concat(
|
||||
utils.findScripts(dir, options.scriptExtensions)
|
||||
);
|
||||
var envdir = dir + '/' + options.env;
|
||||
bootScripts = bootScripts.concat(utils.findScripts(envdir));
|
||||
bootScripts = bootScripts.concat(
|
||||
utils.findScripts(envdir, options.scriptExtensions)
|
||||
);
|
||||
});
|
||||
|
||||
// de-dedup boot scripts -ERS
|
||||
|
|
|
@ -83,6 +83,7 @@ Middleware.prototype.mergePhaseConfig = function(target, config, phase) {
|
|||
Middleware.prototype.buildInstructions = function(context, rootDir, config) {
|
||||
var phasesNames = Object.keys(config);
|
||||
var middlewareList = [];
|
||||
|
||||
phasesNames.forEach(function(phase) {
|
||||
var phaseConfig = config[phase];
|
||||
Object.keys(phaseConfig).forEach(function(middleware) {
|
||||
|
|
|
@ -30,38 +30,39 @@ util.inherits(Mixin, PluginBase);
|
|||
Mixin.prototype.buildInstructions = function(context, rootDir, config) {
|
||||
var modelsMeta = context.configurations.mixins._meta || {};
|
||||
var modelInstructions = context.instructions.models;
|
||||
var mixinDirs = this.options.mixinDirs || [];
|
||||
var mixinSources = this.options.mixinSources || modelsMeta.mixins ||
|
||||
['./mixins'];
|
||||
var scriptExtensions = this.options.scriptExtensions || require.extensions;
|
||||
|
||||
var mixinInstructions = buildAllMixinInstructions(
|
||||
rootDir, mixinDirs, mixinSources, this.options, modelInstructions);
|
||||
rootDir, this.options, mixinSources, scriptExtensions, modelInstructions);
|
||||
|
||||
return mixinInstructions;
|
||||
};
|
||||
|
||||
function buildAllMixinInstructions(appRootDir, mixinDirs, mixinSources, options,
|
||||
modelInstructions) {
|
||||
var extensions = _.without(_.keys(require.extensions),
|
||||
_.keys(getExcludedExtensions()));
|
||||
|
||||
function buildAllMixinInstructions(appRootDir, options, mixinSources,
|
||||
scriptExtensions, modelInstructions) {
|
||||
// load mixins from `options.mixins`
|
||||
var sourceFiles = options.mixins || [];
|
||||
var instructionsFromMixins = loadMixins(sourceFiles, options);
|
||||
var mixinDirs = options.mixinDirs || [];
|
||||
var instructionsFromMixins = loadMixins(sourceFiles, options.normalization);
|
||||
|
||||
// load mixins from `options.mixinDirs`
|
||||
sourceFiles = findMixinDefinitions(appRootDir, mixinDirs, extensions);
|
||||
sourceFiles = findMixinDefinitions(appRootDir, mixinDirs, scriptExtensions);
|
||||
if (sourceFiles === undefined) return;
|
||||
var instructionsFromMixinDirs = loadMixins(sourceFiles, options);
|
||||
var instructionsFromMixinDirs = loadMixins(sourceFiles,
|
||||
options.normalization);
|
||||
|
||||
/* If `mixinDirs` and `mixinSources` have any directories in common,
|
||||
* then remove the common directories from `mixinSources` */
|
||||
mixinSources = _.difference(mixinSources, mixinDirs);
|
||||
|
||||
// load mixins from `options.mixinSources`
|
||||
sourceFiles = findMixinDefinitions(appRootDir, mixinSources, extensions);
|
||||
sourceFiles = findMixinDefinitions(appRootDir, mixinSources,
|
||||
scriptExtensions);
|
||||
if (sourceFiles === undefined) return;
|
||||
var instructionsFromMixinSources = loadMixins(sourceFiles, options);
|
||||
var instructionsFromMixinSources = loadMixins(sourceFiles,
|
||||
options.normalization);
|
||||
|
||||
// Fetch unique list of mixin names, used in models
|
||||
var modelMixins = fetchMixinNamesUsedInModelInstructions(modelInstructions);
|
||||
|
@ -79,7 +80,7 @@ function buildAllMixinInstructions(appRootDir, mixinDirs, mixinSources, options,
|
|||
return _.values(mixins);
|
||||
}
|
||||
|
||||
function findMixinDefinitions(appRootDir, sourceDirs, extensions) {
|
||||
function findMixinDefinitions(appRootDir, sourceDirs, scriptExtensions) {
|
||||
var files = [];
|
||||
sourceDirs.forEach(function(dir) {
|
||||
var path = tryResolveAppPath(appRootDir, dir);
|
||||
|
@ -87,12 +88,12 @@ function findMixinDefinitions(appRootDir, sourceDirs, extensions) {
|
|||
debug('Skipping unknown module source dir %j', dir);
|
||||
return;
|
||||
}
|
||||
files = files.concat(findScripts(path, extensions));
|
||||
files = files.concat(findScripts(path, scriptExtensions));
|
||||
});
|
||||
return files;
|
||||
}
|
||||
|
||||
function loadMixins(sourceFiles, options) {
|
||||
function loadMixins(sourceFiles, normalization) {
|
||||
var mixinInstructions = {};
|
||||
sourceFiles.forEach(function(filepath) {
|
||||
var dir = path.dirname(filepath);
|
||||
|
@ -100,7 +101,7 @@ function loadMixins(sourceFiles, options) {
|
|||
var name = path.basename(filepath, ext);
|
||||
var metafile = path.join(dir, name + FILE_EXTENSION_JSON);
|
||||
|
||||
name = normalizeMixinName(name, options);
|
||||
name = normalizeMixinName(name, normalization);
|
||||
var meta = {};
|
||||
meta.name = name;
|
||||
if (utils.fileExistsSync(metafile)) {
|
||||
|
@ -135,8 +136,7 @@ function filterMixinInstructionsUsingWhitelist(instructions, includeMixins) {
|
|||
return filteredInstructions;
|
||||
}
|
||||
|
||||
function normalizeMixinName(str, options) {
|
||||
var normalization = options.normalization;
|
||||
function normalizeMixinName(str, normalization) {
|
||||
switch (normalization) {
|
||||
case false:
|
||||
case 'none':
|
||||
|
|
|
@ -46,14 +46,18 @@ Model.prototype.buildInstructions = function(context, rootDir, modelsConfig) {
|
|||
var modelSources = this.options.modelSources || modelsMeta.sources ||
|
||||
['./models'];
|
||||
var modelInstructions = buildAllModelInstructions(
|
||||
rootDir, modelsConfig, modelSources, this.options.modelDefinitions);
|
||||
rootDir, modelsConfig, modelSources, this.options.modelDefinitions,
|
||||
this.options.scriptExtensions);
|
||||
return modelInstructions;
|
||||
};
|
||||
|
||||
function buildAllModelInstructions(rootDir, modelsConfig, sources,
|
||||
modelDefinitions) {
|
||||
var registry = verifyModelDefinitions(rootDir, modelDefinitions) ||
|
||||
findModelDefinitions(rootDir, sources);
|
||||
modelDefinitions, scriptExtensions) {
|
||||
var registry = verifyModelDefinitions(rootDir, modelDefinitions,
|
||||
scriptExtensions);
|
||||
if (!registry) {
|
||||
registry = findModelDefinitions(rootDir, sources, scriptExtensions);
|
||||
}
|
||||
|
||||
var modelNamesToBuild = addAllBaseModels(registry, Object.keys(modelsConfig));
|
||||
|
||||
|
@ -134,7 +138,7 @@ function sortByInheritance(instructions) {
|
|||
});
|
||||
}
|
||||
|
||||
function verifyModelDefinitions(rootDir, modelDefinitions) {
|
||||
function verifyModelDefinitions(rootDir, modelDefinitions, scriptExtensions) {
|
||||
if (!modelDefinitions || modelDefinitions.length < 1) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -146,7 +150,8 @@ function verifyModelDefinitions(rootDir, modelDefinitions) {
|
|||
definition.sourceFile = fixFileExtension(
|
||||
fullPath,
|
||||
tryReadDir(path.dirname(fullPath)),
|
||||
true);
|
||||
scriptExtensions);
|
||||
|
||||
if (!definition.sourceFile) {
|
||||
debug('Model source code not found: %s - %s', definition.sourceFile);
|
||||
}
|
||||
|
@ -172,7 +177,7 @@ function verifyModelDefinitions(rootDir, modelDefinitions) {
|
|||
return registry;
|
||||
}
|
||||
|
||||
function findModelDefinitions(rootDir, sources) {
|
||||
function findModelDefinitions(rootDir, sources, scriptExtensions) {
|
||||
var registry = {};
|
||||
|
||||
sources.forEach(function(src) {
|
||||
|
@ -190,7 +195,8 @@ function findModelDefinitions(rootDir, sources) {
|
|||
})
|
||||
.forEach(function(f) {
|
||||
var fullPath = path.resolve(srcDir, f);
|
||||
var entry = loadModelDefinition(rootDir, fullPath, files);
|
||||
var entry = loadModelDefinition(rootDir, fullPath, files,
|
||||
scriptExtensions);
|
||||
var modelName = entry.definition.name;
|
||||
if (!modelName) {
|
||||
debug('Skipping model definition without Model name: %s',
|
||||
|
@ -204,13 +210,14 @@ function findModelDefinitions(rootDir, sources) {
|
|||
return registry;
|
||||
}
|
||||
|
||||
function loadModelDefinition(rootDir, jsonFile, allFiles) {
|
||||
function loadModelDefinition(rootDir, jsonFile, allFiles, scriptExtensions) {
|
||||
var definition = require(jsonFile);
|
||||
var basename = path.basename(jsonFile, path.extname(jsonFile));
|
||||
definition.name = definition.name || _.upperFirst(_.camelCase(basename));
|
||||
|
||||
// find a matching file with a supported extension like `.js` or `.coffee`
|
||||
var sourceFile = fixFileExtension(jsonFile, allFiles, true);
|
||||
var sourceFile = fixFileExtension(jsonFile, allFiles, scriptExtensions);
|
||||
|
||||
if (sourceFile === undefined) {
|
||||
debug('Model source code not found: %s', sourceFile);
|
||||
}
|
||||
|
@ -301,4 +308,3 @@ Model.prototype.start = function(context) {
|
|||
app.model(data._model, data.config);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
34
lib/utils.js
34
lib/utils.js
|
@ -11,6 +11,7 @@ var assert = require('assert');
|
|||
var _ = require('lodash');
|
||||
var g = require('./globalize');
|
||||
|
||||
exports.arrayToObject = arrayToObject;
|
||||
exports.tryReadDir = tryReadDir;
|
||||
exports.resolveRelativePaths = resolveRelativePaths;
|
||||
exports.assertIsValidConfig = assertIsValidConfig;
|
||||
|
@ -31,11 +32,11 @@ var FILE_EXTENSION_JSON = exports.FILE_EXTENSION_JSON = '.json';
|
|||
* @return {Array.<String>} A list of absolute paths to pass to `require()`.
|
||||
*/
|
||||
|
||||
function findScripts(dir, extensions) {
|
||||
function findScripts(dir, scriptExtensions) {
|
||||
assert(dir, 'cannot require directory contents without directory name');
|
||||
|
||||
var files = tryReadDir(dir);
|
||||
extensions = extensions || _.keys(require.extensions);
|
||||
scriptExtensions = scriptExtensions || require.extensions;
|
||||
|
||||
// sort files in lowercase alpha for linux
|
||||
files.sort(function(a, b) {
|
||||
|
@ -61,9 +62,9 @@ function findScripts(dir, extensions) {
|
|||
var filepath = path.resolve(path.join(dir, filename));
|
||||
var stats = fs.statSync(filepath);
|
||||
|
||||
// only require files supported by require.extensions (.txt .md etc.)
|
||||
// only require files supported by specified extensions
|
||||
if (stats.isFile()) {
|
||||
if (isPreferredExtension(filename))
|
||||
if (scriptExtensions && isPreferredExtension(filename, scriptExtensions))
|
||||
results.push(filepath);
|
||||
else
|
||||
debug('Skipping file %s - unknown extension', filepath);
|
||||
|
@ -102,19 +103,28 @@ function getExcludedExtensions() {
|
|||
};
|
||||
}
|
||||
|
||||
function isPreferredExtension(filename) {
|
||||
var includeExtensions = require.extensions;
|
||||
function arrayToObject(array) {
|
||||
return array.reduce(function(obj, val) {
|
||||
obj[val] = val;
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function isPreferredExtension(filename, includeExtensions) {
|
||||
assert(!!includeExtensions, '"includeExtensions" argument is required');
|
||||
|
||||
var ext = path.extname(filename);
|
||||
return (ext in includeExtensions) && !(ext in getExcludedExtensions());
|
||||
}
|
||||
|
||||
function fixFileExtension(filepath, files, onlyScriptsExportingFunction) {
|
||||
function fixFileExtension(filepath, files, scriptExtensions) {
|
||||
var results = [];
|
||||
var otherFile;
|
||||
|
||||
/* Prefer coffee scripts over json */
|
||||
if (isPreferredExtension(filepath)) return filepath;
|
||||
if (scriptExtensions && isPreferredExtension(filepath, scriptExtensions)) {
|
||||
return filepath;
|
||||
}
|
||||
|
||||
var basename = path.basename(filepath, FILE_EXTENSION_JSON);
|
||||
var sourceDir = path.dirname(filepath);
|
||||
|
@ -128,10 +138,7 @@ function fixFileExtension(filepath, files, onlyScriptsExportingFunction) {
|
|||
|
||||
if (!(otherFileExtension in getExcludedExtensions()) &&
|
||||
path.basename(f, otherFileExtension) == basename) {
|
||||
if (!onlyScriptsExportingFunction)
|
||||
results.push(otherFile);
|
||||
else if (onlyScriptsExportingFunction &&
|
||||
(typeof require.extensions[otherFileExtension]) === 'function') {
|
||||
if (!scriptExtensions || otherFileExtension in scriptExtensions) {
|
||||
results.push(otherFile);
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +164,7 @@ function resolveAppScriptPath(rootDir, relativePath, resolveOptions) {
|
|||
}
|
||||
var sourceDir = path.dirname(resolvedPath);
|
||||
var files = tryReadDir(sourceDir);
|
||||
var fixedFile = fixFileExtension(resolvedPath, files, false);
|
||||
var fixedFile = fixFileExtension(resolvedPath, files);
|
||||
return (fixedFile === undefined ? resolvedPath : fixedFile);
|
||||
}
|
||||
|
||||
|
@ -347,4 +354,3 @@ function fileExistsSync(filepath) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,30 @@ describe('Bootstrapper', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('searches boot file extensions specified in options.scriptExtensions',
|
||||
function(done) {
|
||||
var options = {
|
||||
app: app,
|
||||
appRootDir: path.join(__dirname, './fixtures/simple-app'),
|
||||
scriptExtensions: ['.customjs', '.customjs2'],
|
||||
};
|
||||
|
||||
var bootstrapper = new Bootstrapper(options);
|
||||
|
||||
var context = {
|
||||
app: app,
|
||||
};
|
||||
|
||||
bootstrapper.run(context, function(err) {
|
||||
if (err) return done(err);
|
||||
expect(process.bootFlags, 'process: bootFlags').to.eql([
|
||||
'customjs',
|
||||
'customjs2',
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
delete process.bootFlags;
|
||||
});
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function(app, callback) {
|
||||
process.bootFlags.push('customjs');
|
||||
callback();
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function(app, callback) {
|
||||
process.bootFlags.push('customjs2');
|
||||
callback();
|
||||
};
|
Loading…
Reference in New Issue