mirror of
https://github.com/nasa/openmct.git
synced 2025-06-01 15:10:50 +00:00
Merge remote-tracking branch 'origin/wtd568' into open-master
This commit is contained in:
commit
f8d66dff12
@ -1,5 +1,5 @@
|
|||||||
Framework-level components for Open MCT Web. This is Angular and Require,
|
Framework-level components for Open MCT Web. This is Angular and Require,
|
||||||
with an extra layer to mediate between them and act as an extension
|
with an extra layer to mediate between them and act as an extension
|
||||||
mechanism to allow plug-ins to be introduced declaratively.
|
mechanism to allow plug-ins to be introduced declaratively.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
@ -68,6 +68,21 @@ definition. The implementation name should not include the bundle path,
|
|||||||
or the name of the source folder; these will be pre-pended by the framework
|
or the name of the source folder; these will be pre-pended by the framework
|
||||||
during this stage. The implementation name should include a `.js` extension.
|
during this stage. The implementation name should include a `.js` extension.
|
||||||
|
|
||||||
|
Bundles may utilize third-party libraries, and may wish to expose these such
|
||||||
|
that other bundles may use them. Require JS may need special configuration
|
||||||
|
to recognize and utilize third-party libraries, and when exposing a
|
||||||
|
third-party library it may be desirable to do so under a short name
|
||||||
|
(to avoid long relative paths.) Such configuration is performed during the
|
||||||
|
resolution stage, immediately before implementations are loaded. Any
|
||||||
|
`configuration` properties from a bundle's definition (`bundle.json`) will
|
||||||
|
be used to perform this configuration; these `configuration` should take
|
||||||
|
the same form as needed to populate a
|
||||||
|
[`require.config`](http://requirejs.org/docs/api.html#config) call.
|
||||||
|
At present, only `shim` and `paths` options are supported; any `paths` will
|
||||||
|
be prepended with the bundle's library path (the bundle's `lib` folder, by
|
||||||
|
default; this directory name can be overridden by specifying a `libraries`
|
||||||
|
property in `bundles.json`.)
|
||||||
|
|
||||||
An extension is resolved by loading its implementing script, if one has been
|
An extension is resolved by loading its implementing script, if one has been
|
||||||
declared. If none is declared, the extension's raw definition is used
|
declared. If none is declared, the extension's raw definition is used
|
||||||
instead. To ensure that extensions look similar regardless of whether or
|
instead. To ensure that extensions look similar regardless of whether or
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "Open MCT Web Framework Component",
|
"name": "Open MCT Web Framework Component",
|
||||||
"description": "Framework layer for Open MCT Web; interprets bundle definitions and serves as an intermediary between Require and Angular.",
|
"description": "Framework layer for Open MCT Web; interprets bundle definitions and serves as an intermediary between Require and Angular.",
|
||||||
|
"libraries": "lib",
|
||||||
|
"configuration": {
|
||||||
|
"paths": {
|
||||||
|
"angular": "angular.min"
|
||||||
|
},
|
||||||
|
"shim": {
|
||||||
|
"angular": {
|
||||||
|
"exports": "angular"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"extensions": {}
|
"extensions": {}
|
||||||
}
|
}
|
@ -12,7 +12,9 @@ define({
|
|||||||
DEFAULT_BUNDLE: {
|
DEFAULT_BUNDLE: {
|
||||||
"sources": "src",
|
"sources": "src",
|
||||||
"resources": "res",
|
"resources": "res",
|
||||||
"test": "test",
|
"libraries": "lib",
|
||||||
|
"tests": "test",
|
||||||
|
"configuration": {},
|
||||||
"extensions": {}
|
"extensions": {}
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -23,6 +23,7 @@ define(
|
|||||||
'./resolve/ImplementationLoader',
|
'./resolve/ImplementationLoader',
|
||||||
'./resolve/ExtensionResolver',
|
'./resolve/ExtensionResolver',
|
||||||
'./resolve/BundleResolver',
|
'./resolve/BundleResolver',
|
||||||
|
'./resolve/RequireConfigurator',
|
||||||
'./register/CustomRegistrars',
|
'./register/CustomRegistrars',
|
||||||
'./register/ExtensionRegistrar',
|
'./register/ExtensionRegistrar',
|
||||||
'./bootstrap/ApplicationBootstrapper'
|
'./bootstrap/ApplicationBootstrapper'
|
||||||
@ -37,6 +38,7 @@ define(
|
|||||||
ImplementationLoader,
|
ImplementationLoader,
|
||||||
ExtensionResolver,
|
ExtensionResolver,
|
||||||
BundleResolver,
|
BundleResolver,
|
||||||
|
RequireConfigurator,
|
||||||
CustomRegistrars,
|
CustomRegistrars,
|
||||||
ExtensionRegistrar,
|
ExtensionRegistrar,
|
||||||
ApplicationBootstrapper
|
ApplicationBootstrapper
|
||||||
@ -55,10 +57,14 @@ define(
|
|||||||
function initializeApplication($http, $log) {
|
function initializeApplication($http, $log) {
|
||||||
var app = angular.module(Constants.MODULE_NAME, ["ngRoute"]),
|
var app = angular.module(Constants.MODULE_NAME, ["ngRoute"]),
|
||||||
loader = new BundleLoader($http, $log),
|
loader = new BundleLoader($http, $log),
|
||||||
resolver = new BundleResolver(new ExtensionResolver(
|
resolver = new BundleResolver(
|
||||||
new ImplementationLoader(require),
|
new ExtensionResolver(
|
||||||
|
new ImplementationLoader(require),
|
||||||
|
$log
|
||||||
|
),
|
||||||
|
new RequireConfigurator(requirejs),
|
||||||
$log
|
$log
|
||||||
), $log),
|
),
|
||||||
registrar = new ExtensionRegistrar(
|
registrar = new ExtensionRegistrar(
|
||||||
app,
|
app,
|
||||||
new CustomRegistrars(app, $log),
|
new CustomRegistrars(app, $log),
|
||||||
|
@ -101,6 +101,33 @@ define(
|
|||||||
|
|
||||||
return resolvePath(subpath);
|
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
|
* Get a log-friendly name for this bundle; this will
|
||||||
* include both the key (machine-readable name for this
|
* include both the key (machine-readable name for this
|
||||||
|
@ -15,7 +15,7 @@ define(
|
|||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function BundleResolver(extensionResolver, $log) {
|
function BundleResolver(extensionResolver, requireConfigurator, $log) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge resolved bundles (where each is expressed as an
|
* Merge resolved bundles (where each is expressed as an
|
||||||
@ -88,6 +88,10 @@ define(
|
|||||||
* extensions belonging to those categories
|
* extensions belonging to those categories
|
||||||
*/
|
*/
|
||||||
resolveBundles: function (bundles) {
|
resolveBundles: function (bundles) {
|
||||||
|
// First, make sure Require is suitably configured
|
||||||
|
requireConfigurator.configure(bundles);
|
||||||
|
|
||||||
|
// Then, resolve all extension implementations.
|
||||||
return Promise.all(bundles.map(resolveBundle))
|
return Promise.all(bundles.map(resolveBundle))
|
||||||
.then(mergeResolvedBundles);
|
.then(mergeResolvedBundles);
|
||||||
}
|
}
|
||||||
|
91
platform/framework/src/resolve/RequireConfigurator.js
Normal file
91
platform/framework/src/resolve/RequireConfigurator.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*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 = 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];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 () {
|
describe("The bundle resolver", function () {
|
||||||
var mockExtensionResolver,
|
var mockExtensionResolver,
|
||||||
|
mockRequireConfigurator,
|
||||||
mockLog,
|
mockLog,
|
||||||
resolver;
|
resolver;
|
||||||
|
|
||||||
@ -18,18 +19,27 @@ define(
|
|||||||
"extensionResolver",
|
"extensionResolver",
|
||||||
["resolve"]
|
["resolve"]
|
||||||
);
|
);
|
||||||
|
mockRequireConfigurator = jasmine.createSpyObj(
|
||||||
|
"requireConfigurator",
|
||||||
|
["configure"]
|
||||||
|
);
|
||||||
mockLog = jasmine.createSpyObj(
|
mockLog = jasmine.createSpyObj(
|
||||||
"$log",
|
"$log",
|
||||||
["error", "warn", "info", "debug"]
|
["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 () {
|
it("invokes the extension resolver for all bundle extensions", function () {
|
||||||
var result;
|
var result;
|
||||||
|
|
||||||
mockExtensionResolver.resolve.andReturn(Promise.resolve("a"));
|
|
||||||
|
|
||||||
resolver.resolveBundles([
|
resolver.resolveBundles([
|
||||||
new Bundle("x", { extensions: { tests: [ {}, {}, {} ] } }),
|
new Bundle("x", { extensions: { tests: [ {}, {}, {} ] } }),
|
||||||
new Bundle("y", { extensions: { tests: [ {}, {} ], others: [ {}, {} ] } }),
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
63
platform/framework/test/resolve/RequireConfiguratorSpec.js
Normal file
63
platform/framework/test/resolve/RequireConfiguratorSpec.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,runs*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../../src/resolve/RequireConfigurator", "../../src/load/Bundle"],
|
||||||
|
function (RequireConfigurator, Bundle) {
|
||||||
|
"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();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("assembles configurations from bundles", function () {
|
||||||
|
configurator.configure([
|
||||||
|
new Bundle("test/a", { configuration: {
|
||||||
|
paths: { a: "path/to/a", b: "path/to/b" }
|
||||||
|
} }),
|
||||||
|
new Bundle("test/b", { configuration: {
|
||||||
|
paths: { b: "path/to/b" },
|
||||||
|
shim: {
|
||||||
|
b: { "exports": "someExport" },
|
||||||
|
c: {}
|
||||||
|
}
|
||||||
|
} }),
|
||||||
|
new Bundle("test/c", { configuration: {
|
||||||
|
shim: {
|
||||||
|
c: { "exports": "someOtherExport" }
|
||||||
|
}
|
||||||
|
} })
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(mockRequire.config).toHaveBeenCalledWith({
|
||||||
|
baseUrl: "",
|
||||||
|
paths: {
|
||||||
|
a: "test/a/lib/path/to/a",
|
||||||
|
b: "test/b/lib/path/to/b"
|
||||||
|
},
|
||||||
|
shim: {
|
||||||
|
b: { "exports": "someExport" },
|
||||||
|
c: { "exports": "someOtherExport" }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -10,5 +10,6 @@
|
|||||||
"register/ServiceCompositor",
|
"register/ServiceCompositor",
|
||||||
"resolve/BundleResolver",
|
"resolve/BundleResolver",
|
||||||
"resolve/ExtensionResolver",
|
"resolve/ExtensionResolver",
|
||||||
"resolve/ImplementationLoader"
|
"resolve/ImplementationLoader",
|
||||||
|
"resolve/RequireConfigurator"
|
||||||
]
|
]
|
||||||
|
36
test.html
36
test.html
@ -110,7 +110,9 @@
|
|||||||
|
|
||||||
// Run all test suites contained in all bundles
|
// Run all test suites contained in all bundles
|
||||||
function runSuites(bundles) {
|
function runSuites(bundles) {
|
||||||
var components = [], count = 0;
|
var components = [],
|
||||||
|
configuration = { baseUrl: "" },
|
||||||
|
count = 0;
|
||||||
|
|
||||||
function addSuite(bundle) {
|
function addSuite(bundle) {
|
||||||
function fixPath(name) {
|
function fixPath(name) {
|
||||||
@ -124,6 +126,7 @@
|
|||||||
components = components.concat(suiteSpecs.map(fixPath));
|
components = components.concat(suiteSpecs.map(fixPath));
|
||||||
count += 1;
|
count += 1;
|
||||||
if (count === bundles.length) {
|
if (count === bundles.length) {
|
||||||
|
require.config(configuration);
|
||||||
runSpecs(components);
|
runSpecs(components);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,7 +134,36 @@
|
|||||||
loadJSON(bundle + "/test/suite.json", addSpecs);
|
loadJSON(bundle + "/test/suite.json", addSpecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bundles.forEach(addSuite);
|
// Some AMD modules are configured in bundle.json files,
|
||||||
|
// so those need to be read and a require definition built
|
||||||
|
function readConfig(bundle, definition) {
|
||||||
|
var lib = bundle.libraries || "lib",
|
||||||
|
bundleConfig = definition.configuration || {};
|
||||||
|
|
||||||
|
// Merge in paths
|
||||||
|
Object.keys(bundleConfig.paths || {}).forEach(function (path) {
|
||||||
|
configuration.paths = configuration.paths || {};
|
||||||
|
configuration.paths[path] =
|
||||||
|
bundle + "/" + lib + "/" + bundleConfig.paths[path];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Merge in shims
|
||||||
|
Object.keys(bundleConfig.shim || {}).forEach(function (shim) {
|
||||||
|
configuration.shim = configuration.shim || {};
|
||||||
|
configuration.shim[shim] = bundleConfig.shim[shim];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addBundle(bundle) {
|
||||||
|
function readConfigAndContinue(definition) {
|
||||||
|
readConfig(bundle, definition);
|
||||||
|
addSuite(bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadJSON(bundle + "/bundle.json", readConfigAndContinue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bundles.forEach(addBundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the ball rolling; load and run all test suites
|
// Set the ball rolling; load and run all test suites
|
||||||
|
Loading…
x
Reference in New Issue
Block a user