feat(App): Loads mode extensions on demand (#3312)
* feat: Dynamic plugin imports * Remove unused file * Fix basic dev mode * Fix the modes issue that Joe requested * Merge fixes * Add the ability to publish directory contents * PR review changes ---------
This commit is contained in:
parent
2f355fa158
commit
a688f03fdb
@ -10,7 +10,6 @@ const configs = {
|
||||
const ohif = {
|
||||
layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout',
|
||||
sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack',
|
||||
hangingProtocol: '@ohif/extension-default.hangingProtocolModule.default',
|
||||
measurements: '@ohif/extension-default.panelModule.measure',
|
||||
thumbnailList: '@ohif/extension-default.panelModule.seriesList',
|
||||
};
|
||||
@ -47,8 +46,8 @@ const extensionDependencies = {
|
||||
function modeFactory({ modeConfiguration }) {
|
||||
return {
|
||||
id,
|
||||
routeName: 'viewer',
|
||||
displayName: 'Basic Viewer CS3D',
|
||||
routeName: 'dev',
|
||||
displayName: 'Basic Dev Viewer',
|
||||
/**
|
||||
* Lifecycle hooks
|
||||
*/
|
||||
@ -134,6 +133,15 @@ function modeFactory({ modeConfiguration }) {
|
||||
'MoreTools',
|
||||
]);
|
||||
},
|
||||
onModeExit: ({ servicesManager }) => {
|
||||
const {
|
||||
toolGroupService,
|
||||
measurementService,
|
||||
toolbarService,
|
||||
} = servicesManager.services;
|
||||
|
||||
toolGroupService.destroy();
|
||||
},
|
||||
validationTags: {
|
||||
study: [],
|
||||
series: [],
|
||||
@ -177,7 +185,7 @@ function modeFactory({ modeConfiguration }) {
|
||||
},
|
||||
],
|
||||
extensions: extensionDependencies,
|
||||
hangingProtocol: [ohif.hangingProtocol],
|
||||
hangingProtocol: 'default',
|
||||
sopClassHandlers: [
|
||||
dicomvideo.sopClassHandler,
|
||||
ohif.sopClassHandler,
|
||||
|
||||
9
platform/public/demo.html
Normal file
9
platform/public/demo.html
Normal file
@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Demo Page</h1>
|
||||
<p>This demo page shows how to include directories of content in the pluginConfig.json file.
|
||||
The idea is that it is possible to serve up non-compiled content such as the microscopy mode
|
||||
which will not be included in the compile of OHIF but can be included later on dynamically.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,11 +1,14 @@
|
||||
const pluginConfig = require('../pluginConfig.json');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const glob = require('glob');
|
||||
|
||||
const autogenerationDisclaimer = `
|
||||
// THIS FILE IS AUTOGENERATED AS PART OF THE EXTENSION AND MODE PLUGIN PROCESS.
|
||||
// IT SHOULD NOT BE MODIFIED MANUALLY \n`;
|
||||
|
||||
const extractName = (val) => (typeof val === 'string') ? val : val.packageName;
|
||||
|
||||
function constructLines(input, categoryName) {
|
||||
let pluginCount = 0;
|
||||
|
||||
@ -19,14 +22,10 @@ function constructLines(input, categoryName) {
|
||||
input.forEach(entry => {
|
||||
if (entry.default === false) return;
|
||||
|
||||
const packageName = entry.packageName;
|
||||
const defaultImportName = `${categoryName}${pluginCount}`;
|
||||
const packageName = extractName(entry);
|
||||
|
||||
lines.importLines.push(
|
||||
`import ${defaultImportName} from '${packageName}';\n`
|
||||
);
|
||||
lines.addToWindowLines.push(
|
||||
`${categoryName}.push(${defaultImportName});\n`
|
||||
`${categoryName}.push("${packageName}");\n`
|
||||
);
|
||||
|
||||
pluginCount++;
|
||||
@ -49,7 +48,7 @@ function getFormattedWindowBlock(addToWindowLines) {
|
||||
let content =
|
||||
'const extensions = [];\n' +
|
||||
'const modes = [];\n' +
|
||||
'const modesFactory = [];\n' +
|
||||
'\n// Not required any longer\n' +
|
||||
'window.extensions = extensions;\n' +
|
||||
'window.modes = modes;\n\n';
|
||||
|
||||
@ -60,47 +59,55 @@ function getFormattedWindowBlock(addToWindowLines) {
|
||||
return content;
|
||||
}
|
||||
|
||||
function getRuntimeLoadModesExtensions() {
|
||||
return (
|
||||
'\n\n// Add a dynamic runtime loader\n' +
|
||||
'export default async () => {\n' +
|
||||
' for(const modeFactory of modesFactory) {\n' +
|
||||
' const newModes = await modeFactory(modes,extensions);\n' +
|
||||
' newModes.forEach(newMode => modes.push(newMode));\n' +
|
||||
'}\n}\n\n\n' +
|
||||
'async function loadRuntimeImports(config) {\n' +
|
||||
' if (config && config.modes && config.modes.length) {\n' +
|
||||
' for (const modeName of config.modes) {\n' +
|
||||
' const existingMode = modes.find(mode => mode.id === modeName);\n' +
|
||||
' if (!existingMode) {\n' +
|
||||
' if (modeName === `@ohif/mode-test`) {\n' +
|
||||
' const mode = await import(`@ohif/mode-test`);\n' +
|
||||
' window.modes.push(mode.default);\n' +
|
||||
' const extension = await import(`@ohif/extension-test`);\n' +
|
||||
' window.extensions.push(extension.default);\n' +
|
||||
' }\n' +
|
||||
' }\n' +
|
||||
' };\n' +
|
||||
' }\n' +
|
||||
'}\n' +
|
||||
'export { loadRuntimeImports };\n\n'
|
||||
);
|
||||
function getRuntimeLoadModesExtensions(modules) {
|
||||
const dynamicLoad = [];
|
||||
dynamicLoad.push(
|
||||
'\n\n// Add a dynamic runtime loader',
|
||||
'async function loadModule(module) {',
|
||||
' if (typeof module !== \'string\') return module;');
|
||||
modules.forEach(module => {
|
||||
const packageName = extractName(module);
|
||||
dynamicLoad.push(
|
||||
` if( module==="${packageName}") {`,
|
||||
` const imported = await import("${packageName}");`,
|
||||
' return imported.default;',
|
||||
' }'
|
||||
);
|
||||
})
|
||||
dynamicLoad.push(
|
||||
' return (await import(module)).default;',
|
||||
'}\n',
|
||||
'// Import a list of items (modules or string names)',
|
||||
'// @return a Promise evaluating to a list of modules',
|
||||
'export default function importItems(modules) {',
|
||||
' return Promise.all(modules.map(loadModule));',
|
||||
'}\n',
|
||||
'export { loadModule, modes, extensions, importItems };\n\n');
|
||||
return dynamicLoad.join('\n');
|
||||
}
|
||||
|
||||
const fromDirectory = (srcDir, path) => {
|
||||
if (!path) return;
|
||||
if (path[0] === '.') return srcDir + '/../../..' + path.substring(1);
|
||||
if (path[0] === '~') return os.homedir() + path.substring(1);
|
||||
return path;
|
||||
}
|
||||
|
||||
const createCopyPluginToDistForLink = (
|
||||
SRC_DIR,
|
||||
DIST_DIR,
|
||||
srcDir,
|
||||
distDir,
|
||||
plugins,
|
||||
folderName
|
||||
) => {
|
||||
return plugins
|
||||
.map(plugin => {
|
||||
const from = `${SRC_DIR}/../node_modules/${plugin.packageName}/${folderName}/`;
|
||||
const fromDir = fromDirectory(srcDir, plugin.directory);
|
||||
const from = fromDir || `${srcDir}/../node_modules/${plugin.packageName}/${folderName}/`;
|
||||
const exists = fs.existsSync(from);
|
||||
return exists
|
||||
? {
|
||||
from,
|
||||
to: DIST_DIR,
|
||||
to: distDir,
|
||||
toType: 'dir',
|
||||
}
|
||||
: undefined;
|
||||
@ -134,23 +141,20 @@ function writePluginImportsFile(SRC_DIR, DIST_DIR) {
|
||||
|
||||
const extensionLines = constructLines(pluginConfig.extensions, 'extensions');
|
||||
const modeLines = constructLines(pluginConfig.modes, 'modes');
|
||||
const modesFactoryLines = constructLines(
|
||||
pluginConfig.modesFactory,
|
||||
'modesFactory'
|
||||
);
|
||||
|
||||
pluginImportsJsContent += getFormattedImportBlock([
|
||||
...extensionLines.importLines,
|
||||
...modeLines.importLines,
|
||||
...modesFactoryLines.importLines,
|
||||
]);
|
||||
pluginImportsJsContent += getFormattedWindowBlock([
|
||||
...extensionLines.addToWindowLines,
|
||||
...modeLines.addToWindowLines,
|
||||
...modesFactoryLines.addToWindowLines,
|
||||
]);
|
||||
|
||||
pluginImportsJsContent += getRuntimeLoadModesExtensions();
|
||||
pluginImportsJsContent += getRuntimeLoadModesExtensions([
|
||||
...pluginConfig.extensions,
|
||||
...pluginConfig.modes,
|
||||
]);
|
||||
|
||||
fs.writeFileSync(
|
||||
`${SRC_DIR}/pluginImports.js`,
|
||||
@ -172,10 +176,8 @@ function writePluginImportsFile(SRC_DIR, DIST_DIR) {
|
||||
SRC_DIR,
|
||||
DIST_DIR,
|
||||
[
|
||||
...pluginConfig.modesFactory,
|
||||
...pluginConfig.modes,
|
||||
...pluginConfig.extensions,
|
||||
...pluginConfig.umd,
|
||||
],
|
||||
'public'
|
||||
);
|
||||
@ -184,10 +186,9 @@ function writePluginImportsFile(SRC_DIR, DIST_DIR) {
|
||||
SRC_DIR,
|
||||
DIST_DIR,
|
||||
[
|
||||
...pluginConfig.modesFactory,
|
||||
...pluginConfig.modes,
|
||||
...pluginConfig.extensions,
|
||||
...pluginConfig.umd,
|
||||
...pluginConfig.public,
|
||||
],
|
||||
'public'
|
||||
);
|
||||
@ -198,10 +199,8 @@ function writePluginImportsFile(SRC_DIR, DIST_DIR) {
|
||||
SRC_DIR,
|
||||
DIST_DIR,
|
||||
[
|
||||
...pluginConfig.modesFactory,
|
||||
...pluginConfig.modes,
|
||||
...pluginConfig.extensions,
|
||||
...pluginConfig.umd,
|
||||
],
|
||||
'dist'
|
||||
);
|
||||
@ -210,15 +209,13 @@ function writePluginImportsFile(SRC_DIR, DIST_DIR) {
|
||||
SRC_DIR,
|
||||
DIST_DIR,
|
||||
[
|
||||
...pluginConfig.modesFactory,
|
||||
...pluginConfig.modes,
|
||||
...pluginConfig.extensions,
|
||||
...pluginConfig.umd,
|
||||
],
|
||||
'dist'
|
||||
);
|
||||
|
||||
console.log('copy plugins', [
|
||||
console.warn('copy plugins', [
|
||||
...copyPluginPublicToDistBuild,
|
||||
...copyPluginPublicToDistLink,
|
||||
...copyPluginDistToDistBuild,
|
||||
|
||||
@ -1,60 +1,83 @@
|
||||
{
|
||||
"extensions": [
|
||||
{
|
||||
"packageName": "@ohif/extension-default"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-cornerstone",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-measurement-tracking",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-cornerstone-dicom-sr",
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-default",
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-dicom-microscopy",
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-dicom-pdf",
|
||||
"version": "3.0.1"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-dicom-video",
|
||||
"version": "3.0.1"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-tmtv",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-cornerstone-dicom-seg",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-dicom-microscopy",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-dicom-pdf",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-dicom-video",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-tmtv",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-test",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/extension-cornerstone-dicom-rt",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
}
|
||||
],
|
||||
"modes": [
|
||||
{
|
||||
"packageName": "@ohif/mode-longitudinal",
|
||||
"packageName": "@ohif/mode-longitudinal"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/mode-tmtv"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/mode-microscopy"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/mode-test",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/mode-microscopy",
|
||||
"version": "3.0.0"
|
||||
},
|
||||
{
|
||||
"packageName": "@ohif/mode-tmtv",
|
||||
"packageName": "@ohif/mode-basic-dev-mode",
|
||||
"default": false,
|
||||
"version": "3.0.0"
|
||||
}
|
||||
],
|
||||
"modesFactory": [],
|
||||
"umd": []
|
||||
"public": [
|
||||
{
|
||||
"directory": "./platform/public"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ window.config = {
|
||||
},
|
||||
],
|
||||
extensions: [],
|
||||
modes: ['@ohif/mode-test'],
|
||||
modes: ['@ohif/mode-test', '@ohif/mode-basic-dev-mode'],
|
||||
showStudyList: true,
|
||||
maxNumberOfWebWorkers: 4,
|
||||
// below flag is for performance reasons, but it might not work for all servers
|
||||
|
||||
@ -21,6 +21,8 @@ import {
|
||||
// utils,
|
||||
} from '@ohif/core';
|
||||
|
||||
import loadModules from './pluginImports';
|
||||
|
||||
/**
|
||||
* @param {object|func} appConfigOrFunc - application configuration, or a function that returns application configuration
|
||||
* @param {object[]} defaultExtensions - array of extension objects
|
||||
@ -74,8 +76,12 @@ async function appInit(appConfigOrFunc, defaultExtensions, defaultModes) {
|
||||
* Example: [ext1, ext2, ext3]
|
||||
* Example2: [[ext1, config], ext2, [ext3, config]]
|
||||
*/
|
||||
const loadedExtensions = await loadModules([
|
||||
...defaultExtensions,
|
||||
...appConfig.extensions,
|
||||
]);
|
||||
await extensionManager.registerExtensions(
|
||||
[...defaultExtensions, ...appConfig.extensions],
|
||||
loadedExtensions,
|
||||
appConfig.dataSources
|
||||
);
|
||||
|
||||
@ -87,24 +93,38 @@ async function appInit(appConfigOrFunc, defaultExtensions, defaultModes) {
|
||||
throw new Error('No modes are defined! Check your app-config.js');
|
||||
}
|
||||
|
||||
for (let i = 0; i < defaultModes.length; i++) {
|
||||
const { modeFactory, id } = defaultModes[i];
|
||||
const loadedModes = await loadModules([
|
||||
...(appConfig.modes || []),
|
||||
...defaultModes,
|
||||
]);
|
||||
|
||||
// If the appConfig contains configuration for this mode, use it.
|
||||
const modeConfig =
|
||||
appConfig.modeConfig && appConfig.modeConfig[i]
|
||||
? appConfig.modeConfig[id]
|
||||
: {};
|
||||
// This is the name for the loaded istance object
|
||||
appConfig.loadedModes = [];
|
||||
const modesById = new Set();
|
||||
for (let i = 0; i < loadedModes.length; i++) {
|
||||
let mode = loadedModes[i];
|
||||
if (!mode) continue;
|
||||
const { id } = mode;
|
||||
|
||||
const mode = modeFactory(modeConfig);
|
||||
if (mode.modeFactory) {
|
||||
// If the appConfig contains configuration for this mode, use it.
|
||||
const modeConfig =
|
||||
appConfig.modeConfig && appConfig.modeConfig[i]
|
||||
? appConfig.modeConfig[id]
|
||||
: {};
|
||||
|
||||
appConfig.modes.push(mode);
|
||||
mode = mode.modeFactory(modeConfig);
|
||||
}
|
||||
|
||||
if (modesById.has(id)) continue;
|
||||
// Prevent duplication
|
||||
modesById.add(id);
|
||||
if (!mode || typeof mode !== 'object') continue;
|
||||
appConfig.loadedModes.push(mode);
|
||||
}
|
||||
|
||||
// remove modes that are not objects, or have no id
|
||||
appConfig.modes = appConfig.modes.filter(
|
||||
mode => typeof mode === 'object' && mode.id
|
||||
);
|
||||
// Hack alert - don't touch the original modes definition,
|
||||
// but there are still dependencies on having the appConfig modes defined
|
||||
appConfig.modes = appConfig.loadedModes;
|
||||
|
||||
return {
|
||||
appConfig,
|
||||
|
||||
@ -16,25 +16,24 @@ import { history } from './utils/history';
|
||||
* pluginImports.js imports all of the modes and extensions and adds them
|
||||
* to the window for processing.
|
||||
*/
|
||||
import loadDynamicImports, { loadRuntimeImports } from './pluginImports.js';
|
||||
import {
|
||||
modes as defaultModes,
|
||||
extensions as defaultExtensions,
|
||||
} from './pluginImports';
|
||||
|
||||
loadDynamicImports().then(() => {
|
||||
loadRuntimeImports(window.config).then(() => {
|
||||
/**
|
||||
* Combine our appConfiguration with installed extensions and modes.
|
||||
* In the future appConfiguration may contain modes added at runtime.
|
||||
* */
|
||||
const appProps = {
|
||||
config: window ? window.config : {},
|
||||
defaultExtensions: window.extensions,
|
||||
defaultModes: window.modes,
|
||||
};
|
||||
/**
|
||||
* Combine our appConfiguration with installed extensions and modes.
|
||||
* In the future appConfiguration may contain modes added at runtime.
|
||||
* */
|
||||
const appProps = {
|
||||
config: window ? window.config : {},
|
||||
defaultExtensions,
|
||||
defaultModes,
|
||||
};
|
||||
|
||||
/** Create App */
|
||||
const app = React.createElement(App, appProps, null);
|
||||
/** Render */
|
||||
ReactDOM.render(app, document.getElementById('root'));
|
||||
});
|
||||
});
|
||||
/** Create App */
|
||||
const app = React.createElement(App, appProps, null);
|
||||
/** Render */
|
||||
ReactDOM.render(app, document.getElementById('root'));
|
||||
|
||||
export { history };
|
||||
|
||||
@ -9,6 +9,7 @@ import ViewportGrid from '@components/ViewportGrid';
|
||||
import Compose from './Compose';
|
||||
import getStudies from './studiesList';
|
||||
import { history } from '../../utils/history';
|
||||
import loadModules from '../../pluginImports';
|
||||
|
||||
const { getSplitParam } = utils;
|
||||
|
||||
@ -102,6 +103,8 @@ export default function ModeRoute({
|
||||
const [studyInstanceUIDs, setStudyInstanceUIDs] = useState();
|
||||
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [allExtensionsLoaded, setAllExtensionsLoaded] = useState(false);
|
||||
|
||||
const layoutTemplateData = useRef(false);
|
||||
const locationRef = useRef(null);
|
||||
const isMounted = useRef(false);
|
||||
@ -235,41 +238,52 @@ export default function ModeRoute({
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: For some reason this is running before the Providers
|
||||
// are calling setServiceImplementation
|
||||
// TODO -> iterate through services.
|
||||
|
||||
// Extension
|
||||
|
||||
// Add SOPClassHandlers to a new SOPClassManager.
|
||||
displaySetService.init(extensionManager, sopClassHandlers);
|
||||
|
||||
extensionManager.onModeEnter({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
commandsManager,
|
||||
});
|
||||
|
||||
// use the URL hangingProtocolId if it exists, otherwise use the one
|
||||
// defined in the mode configuration
|
||||
const hangingProtocolIdToUse = hangingProtocolService.getProtocolById(
|
||||
runTimeHangingProtocolId
|
||||
)
|
||||
? runTimeHangingProtocolId
|
||||
: hangingProtocol;
|
||||
|
||||
// Sets the active hanging protocols - if hangingProtocol is undefined,
|
||||
// resets to default. Done before the onModeEnter to allow the onModeEnter
|
||||
// to perform custom hanging protocol actions
|
||||
hangingProtocolService.setActiveProtocolIds(hangingProtocolIdToUse);
|
||||
|
||||
mode?.onModeEnter({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
commandsManager,
|
||||
});
|
||||
|
||||
const setupRouteInit = async () => {
|
||||
const loadedExtensions = await loadModules(Object.keys(extensions));
|
||||
for (const extension of loadedExtensions) {
|
||||
const { id: extensionId } = extension;
|
||||
if (
|
||||
extensionManager.registeredExtensionIds.indexOf(extensionId) === -1
|
||||
) {
|
||||
await extensionManager.registerExtension(extension);
|
||||
}
|
||||
}
|
||||
setAllExtensionsLoaded(true);
|
||||
|
||||
// TODO: For some reason this is running before the Providers
|
||||
// are calling setServiceImplementation
|
||||
// TODO -> iterate through services.
|
||||
|
||||
// Extension
|
||||
|
||||
// Add SOPClassHandlers to a new SOPClassManager.
|
||||
displaySetService.init(extensionManager, sopClassHandlers);
|
||||
|
||||
extensionManager.onModeEnter({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
commandsManager,
|
||||
});
|
||||
|
||||
// use the URL hangingProtocolId if it exists, otherwise use the one
|
||||
// defined in the mode configuration
|
||||
const hangingProtocolIdToUse = hangingProtocolService.getProtocolById(
|
||||
runTimeHangingProtocolId
|
||||
)
|
||||
? runTimeHangingProtocolId
|
||||
: hangingProtocol;
|
||||
|
||||
// Sets the active hanging protocols - if hangingProtocol is undefined,
|
||||
// resets to default. Done before the onModeEnter to allow the onModeEnter
|
||||
// to perform custom hanging protocol actions
|
||||
hangingProtocolService.setActiveProtocolIds(hangingProtocolIdToUse);
|
||||
|
||||
mode?.onModeEnter({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
commandsManager,
|
||||
});
|
||||
|
||||
/**
|
||||
* The next line should get all the query parameters provided by the URL
|
||||
* - except the StudyInstanceUIDs - and create an object called filters
|
||||
@ -382,8 +396,8 @@ export default function ModeRoute({
|
||||
<CombinedContextProvider>
|
||||
<DragAndDropProvider>
|
||||
{layoutTemplateData.current &&
|
||||
studyInstanceUIDs?.length &&
|
||||
studyInstanceUIDs[0] !== undefined &&
|
||||
studyInstanceUIDs?.[0] !== undefined &&
|
||||
allExtensionsLoaded &&
|
||||
renderLayoutData({
|
||||
...layoutTemplateData.current.props,
|
||||
ViewportGridComp: ViewportGridWithDataSource,
|
||||
|
||||
@ -336,7 +336,7 @@ function WorkList({
|
||||
: []
|
||||
}
|
||||
>
|
||||
{appConfig.modes.map((mode, i) => {
|
||||
{appConfig.loadedModes.map((mode, i) => {
|
||||
const isFirst = i === 0;
|
||||
|
||||
const isValidMode = mode.isValidMode({ modalities });
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import ModeRoute from '@routes/Mode';
|
||||
import checkExtensionDependencies from './checkExtensionDependencies';
|
||||
|
||||
/*
|
||||
Routes uniquely define an entry point to:
|
||||
@ -41,8 +40,6 @@ export default function buildModeRoutes({
|
||||
});
|
||||
|
||||
modes.forEach(mode => {
|
||||
checkExtensionDependencies(mode, extensionManager);
|
||||
|
||||
// todo: for each route. add route to path.
|
||||
dataSourceNames.forEach(dataSourceName => {
|
||||
const path = `/${mode.routeName}/${dataSourceName}`;
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
export default function checkExtensionDependencies(mode, extensionManager) {
|
||||
const extensionDependencies = mode.extensions;
|
||||
|
||||
const dependencyString = `Unmet extension dependency in mode: ${mode.id}`;
|
||||
|
||||
Object.keys(extensionDependencies).forEach(extensionId => {
|
||||
const extensionInstalled = extensionManager.registeredExtensionIds.includes(
|
||||
extensionId
|
||||
);
|
||||
|
||||
if (!extensionInstalled) {
|
||||
throw new Error(
|
||||
`${dependencyString}: extension ${extensionId} not found`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function areVersionsCompatible(semanticVersion, installedVersion) {
|
||||
if (semanticVersion.includes('^')) {
|
||||
// Major must match
|
||||
const versionLessCaret = semanticVersion.split('^')[1];
|
||||
|
||||
// Index 0 is the major version.
|
||||
return versionLessCaret[0] === installedVersion[0];
|
||||
} else if (semanticVersion.includes('~')) {
|
||||
// Major and minor must match
|
||||
const versionLessTilde = semanticVersion.split('~')[1];
|
||||
|
||||
// Index 0 is the major version.
|
||||
// Index 2 is the minor version.
|
||||
return (
|
||||
versionLessTilde[0] === installedVersion[0] &&
|
||||
versionLessTilde[2] === installedVersion[2]
|
||||
);
|
||||
} else {
|
||||
return semanticVersion === installedVersion;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user