mirror of
https://github.com/nasa/openmct.git
synced 2025-01-19 03:06:54 +00:00
[Framework] Add RequireJS configurator
Add a configuration step (as part of the resolve phase) to the framework layer, where bundle-defined paths and shims are passed to RequireJS configuration. This permits both the use of non-AMD modules and the exposure of libraries across bundles. WTD-568.
This commit is contained in:
parent
d281bd52de
commit
96aaea5e58
@ -12,7 +12,9 @@ define({
|
||||
DEFAULT_BUNDLE: {
|
||||
"sources": "src",
|
||||
"resources": "res",
|
||||
"test": "test",
|
||||
"libraries": "lib",
|
||||
"tests": "test",
|
||||
"configuration": {},
|
||||
"extensions": {}
|
||||
}
|
||||
});
|
@ -23,6 +23,7 @@ define(
|
||||
'./resolve/ImplementationLoader',
|
||||
'./resolve/ExtensionResolver',
|
||||
'./resolve/BundleResolver',
|
||||
'./resolve/RequireConfigurator',
|
||||
'./register/CustomRegistrars',
|
||||
'./register/ExtensionRegistrar',
|
||||
'./bootstrap/ApplicationBootstrapper'
|
||||
@ -37,6 +38,7 @@ define(
|
||||
ImplementationLoader,
|
||||
ExtensionResolver,
|
||||
BundleResolver,
|
||||
RequireConfigurator,
|
||||
CustomRegistrars,
|
||||
ExtensionRegistrar,
|
||||
ApplicationBootstrapper
|
||||
@ -55,10 +57,14 @@ define(
|
||||
function initializeApplication($http, $log) {
|
||||
var app = angular.module(Constants.MODULE_NAME, ["ngRoute"]),
|
||||
loader = new BundleLoader($http, $log),
|
||||
resolver = new BundleResolver(new ExtensionResolver(
|
||||
new ImplementationLoader(require),
|
||||
resolver = new BundleResolver(
|
||||
new ExtensionResolver(
|
||||
new ImplementationLoader(require),
|
||||
$log
|
||||
),
|
||||
new RequireConfigurator(requirejs),
|
||||
$log
|
||||
), $log),
|
||||
),
|
||||
registrar = new ExtensionRegistrar(
|
||||
app,
|
||||
new CustomRegistrars(app, $log),
|
||||
|
@ -101,6 +101,33 @@ define(
|
||||
|
||||
return resolvePath(subpath);
|
||||
},
|
||||
/**
|
||||
* Get the path to this bundle's library folder. If an
|
||||
* argument is provided, the path will be to the library
|
||||
* file within the bundle's resource file.
|
||||
*
|
||||
* @memberof Bundle#
|
||||
* @param {string} [libraryFile] optionally, give a path to
|
||||
* a specific library file in the bundle.
|
||||
* @returns {string}
|
||||
*/
|
||||
getLibraryPath: function (libraryFile) {
|
||||
var subpath = libraryFile ?
|
||||
[ definition.libraries, libraryFile ] :
|
||||
[ definition.libraries ];
|
||||
|
||||
return resolvePath(subpath);
|
||||
},
|
||||
/**
|
||||
* Get library configuration for this bundle. This is read
|
||||
* from the bundle's definition; if the bundle is well-formed,
|
||||
* it will resemble a require.config object.
|
||||
* @memberof Bundle#
|
||||
* @returns {object}
|
||||
*/
|
||||
getConfiguration: function () {
|
||||
return definition.configuration || {};
|
||||
},
|
||||
/**
|
||||
* Get a log-friendly name for this bundle; this will
|
||||
* include both the key (machine-readable name for this
|
||||
|
@ -15,7 +15,7 @@ define(
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function BundleResolver(extensionResolver, $log) {
|
||||
function BundleResolver(extensionResolver, requireConfigurator, $log) {
|
||||
|
||||
/**
|
||||
* Merge resolved bundles (where each is expressed as an
|
||||
@ -88,6 +88,10 @@ define(
|
||||
* extensions belonging to those categories
|
||||
*/
|
||||
resolveBundles: function (bundles) {
|
||||
// First, make sure Require is suitably configured
|
||||
requireConfigurator.configure(bundles);
|
||||
|
||||
// Then, resolve all extension implementations.
|
||||
return Promise.all(bundles.map(resolveBundle))
|
||||
.then(mergeResolvedBundles);
|
||||
}
|
||||
|
90
platform/framework/src/resolve/RequireConfigurator.js
Normal file
90
platform/framework/src/resolve/RequireConfigurator.js
Normal file
@ -0,0 +1,90 @@
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Handles configuration of RequireJS to expose libraries
|
||||
* from bundles with module names that can be used from other
|
||||
* bundles.
|
||||
* @constructor
|
||||
* @param requirejs an instance of RequireJS
|
||||
*/
|
||||
function RequireConfigurator(requirejs) {
|
||||
// Utility function to clone part of a bundle definition
|
||||
function clone(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
// Look up module configuration from the bundle definition.
|
||||
// This will adjust paths to libraries as-needed.
|
||||
function getConfiguration(bundle) {
|
||||
var configuration = bundle.getConfiguration();
|
||||
|
||||
// Adjust paths to point to libraries
|
||||
if (configuration.paths) {
|
||||
// Don't modify the actual bundle definition...
|
||||
configuration = configuration.clone(configuration);
|
||||
// ...replace values in a clone instead.
|
||||
Object.keys(configuration.paths).forEach(function (path) {
|
||||
configuration.paths[path] =
|
||||
bundle.getLibraryPath(configuration.paths[path]);
|
||||
});
|
||||
}
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
// Build up paths and shim values from multiple bundles;
|
||||
// this is sensitive to the value from baseConfiguration
|
||||
// passed via reduce in buildConfiguration below, insofar
|
||||
// as it assumes paths and shim will have initial empty values.
|
||||
function mergeConfigurations(base, next) {
|
||||
["paths", "shim"].forEach(function (k) {
|
||||
Object.keys(next[k] || {}).forEach(function (p) {
|
||||
base[k][p] = next[k][p];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Build a configuration object, to pass to requirejs.config,
|
||||
// based on the defined configurations for all bundles.
|
||||
// The paths and shim properties from all bundles will be
|
||||
// merged to allow one requirejs.config call.
|
||||
function buildConfiguration(bundles) {
|
||||
// Provide an initial requirejs configuration...
|
||||
var baseConfiguration = {
|
||||
baseUrl: "",
|
||||
paths: {},
|
||||
shim: {}
|
||||
},
|
||||
// ...and pull out all bundle-specific parts
|
||||
bundleConfigurations = bundles.map(getConfiguration);
|
||||
|
||||
// Reduce this into one configuration object.
|
||||
return bundleConfigurations.reduce(
|
||||
mergeConfigurations,
|
||||
baseConfiguration
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Configure RequireJS to utilize any path/shim definitions
|
||||
* provided by these bundles.
|
||||
*
|
||||
* @param {Bundle[]} the bundles to include in this
|
||||
* configuration
|
||||
*/
|
||||
configure: function (bundles) {
|
||||
return requirejs.config(buildConfiguration(bundles));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return RequireConfigurator;
|
||||
|
||||
}
|
||||
);
|
@ -10,6 +10,7 @@ define(
|
||||
|
||||
describe("The bundle resolver", function () {
|
||||
var mockExtensionResolver,
|
||||
mockRequireConfigurator,
|
||||
mockLog,
|
||||
resolver;
|
||||
|
||||
@ -18,18 +19,27 @@ define(
|
||||
"extensionResolver",
|
||||
["resolve"]
|
||||
);
|
||||
mockRequireConfigurator = jasmine.createSpyObj(
|
||||
"requireConfigurator",
|
||||
["configure"]
|
||||
);
|
||||
mockLog = jasmine.createSpyObj(
|
||||
"$log",
|
||||
["error", "warn", "info", "debug"]
|
||||
);
|
||||
resolver = new BundleResolver(mockExtensionResolver, mockLog);
|
||||
|
||||
mockExtensionResolver.resolve.andReturn(Promise.resolve("a"));
|
||||
|
||||
resolver = new BundleResolver(
|
||||
mockExtensionResolver,
|
||||
mockRequireConfigurator,
|
||||
mockLog
|
||||
);
|
||||
});
|
||||
|
||||
it("invokes the extension resolver for all bundle extensions", function () {
|
||||
var result;
|
||||
|
||||
mockExtensionResolver.resolve.andReturn(Promise.resolve("a"));
|
||||
|
||||
resolver.resolveBundles([
|
||||
new Bundle("x", { extensions: { tests: [ {}, {}, {} ] } }),
|
||||
new Bundle("y", { extensions: { tests: [ {}, {} ], others: [ {}, {} ] } }),
|
||||
@ -50,6 +60,18 @@ define(
|
||||
});
|
||||
});
|
||||
|
||||
it("configures require before loading implementations", function () {
|
||||
var bundles = [
|
||||
new Bundle("x", { extensions: { tests: [ {}, {}, {} ] } }),
|
||||
new Bundle("y", { extensions: { tests: [ {}, {} ], others: [ {}, {} ] } }),
|
||||
new Bundle("z", { extensions: { others: [ {} ] } })
|
||||
];
|
||||
|
||||
resolver.resolveBundles(bundles);
|
||||
expect(mockRequireConfigurator.configure)
|
||||
.toHaveBeenCalledWith(bundles);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
30
platform/framework/test/resolve/RequireConfiguratorSpec.js
Normal file
30
platform/framework/test/resolve/RequireConfiguratorSpec.js
Normal file
@ -0,0 +1,30 @@
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,runs*/
|
||||
|
||||
define(
|
||||
["../../src/resolve/RequireConfigurator"],
|
||||
function (RequireConfigurator) {
|
||||
"use strict";
|
||||
|
||||
describe("The RequireJS configurator", function () {
|
||||
var mockRequire,
|
||||
configurator;
|
||||
|
||||
beforeEach(function () {
|
||||
mockRequire = jasmine.createSpyObj(
|
||||
"requirejs",
|
||||
[ "config" ]
|
||||
);
|
||||
configurator = new RequireConfigurator(mockRequire);
|
||||
});
|
||||
|
||||
it("configures RequireJS when invoked", function () {
|
||||
// Verify precondition - no config call
|
||||
expect(mockRequire.config).not.toHaveBeenCalled();
|
||||
// Configure with an empty set of bundles
|
||||
configurator.configure([]);
|
||||
// Should have invoked require.config
|
||||
expect(mockRequire.config).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -10,5 +10,6 @@
|
||||
"register/ServiceCompositor",
|
||||
"resolve/BundleResolver",
|
||||
"resolve/ExtensionResolver",
|
||||
"resolve/ImplementationLoader"
|
||||
"resolve/ImplementationLoader",
|
||||
"resolve/RequireConfigurator"
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user