refactor: Reduce bundle size (#1575)

* async load cornerstoneWADOImageLoader chunk

* Lazy initializing of CornerstoneWADIImageLoader - delayed till Viewer creation

* Async loading of react-vtkjs-viewport

* withCornerstone HOC to wrap routes requiring CornerstoneWADOImageLoader initiazation

* removed dead code in comments

* initWeWorkers is no more. Remove al its references.

Co-authored-by: kedar.netelixir <kedar.choudhary@netelixir.com>
Co-authored-by: Danny Brown <danny.ri.brown@gmail.com>
This commit is contained in:
Kedar 2020-04-02 23:01:14 +05:30 committed by GitHub
parent e09a3d2e93
commit 81dcbe294d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 174 additions and 136 deletions

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { getImageData, loadImageData } from 'react-vtkjs-viewport';
import { getReactVtkjsViewport } from './utils/getReactVtkjsViewport';
import ConnectedVTKViewport from './ConnectedVTKViewport';
import LoadingIndicator from './LoadingIndicator.js';
import OHIF from '@ohif/core';
@ -69,7 +69,7 @@ class OHIFVTKViewport extends Component {
};
static defaultProps = {
onScroll: () => {},
onScroll: () => { },
};
static id = 'OHIFVTKViewport';
@ -133,7 +133,8 @@ class OHIFVTKViewport extends Component {
displaySetInstanceUID,
SOPClassUID,
SOPInstanceUID,
frameIndex
frameIndex,
reactVtkjsViewport
) => {
const stack = OHIFVTKViewport.getCornerstoneStack(
studies,
@ -144,7 +145,10 @@ class OHIFVTKViewport extends Component {
frameIndex
);
const imageDataObject = getImageData(stack.imageIds, displaySetInstanceUID);
const imageDataObject = reactVtkjsViewport.getImageData(
stack.imageIds,
displaySetInstanceUID
);
let labelmapDataObject;
let labelmapColorLUT;
@ -257,7 +261,9 @@ class OHIFVTKViewport extends Component {
return volumeActor;
}
setStateFromProps() {
async setStateFromProps() {
const reactVtkjsViewport = await getReactVtkjsViewport();
const { studies, displaySet } = this.props.viewportData;
const {
StudyInstanceUID,
@ -295,8 +301,10 @@ class OHIFVTKViewport extends Component {
studies,
StudyInstanceUID,
displaySetInstanceUID,
sopClassUIDs[0],
SOPInstanceUID,
frameIndex
frameIndex,
reactVtkjsViewport
);
this.imageDataObject = imageDataObject;
@ -317,7 +325,7 @@ class OHIFVTKViewport extends Component {
dataDetails,
},
() => {
this.loadProgressively(imageDataObject);
this.loadProgressively(imageDataObject, reactVtkjsViewport);
// TODO: There must be a better way to do this.
// We do this so that if all the data is available the react-vtkjs-viewport
@ -335,26 +343,26 @@ class OHIFVTKViewport extends Component {
);
}
componentDidMount() {
this.setStateFromProps();
async componentDidMount() {
await this.setStateFromProps();
}
componentDidUpdate(prevProps) {
async componentDidUpdate(prevProps) {
const { displaySet } = this.props.viewportData;
const prevDisplaySet = prevProps.viewportData.displaySet;
if (
displaySet.displaySetInstanceUID !==
prevDisplaySet.displaySetInstanceUID ||
prevDisplaySet.displaySetInstanceUID ||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
displaySet.frameIndex !== prevDisplaySet.frameIndex
) {
this.setStateFromProps();
await this.setStateFromProps();
}
}
loadProgressively(imageDataObject) {
loadImageData(imageDataObject);
loadProgressively(imageDataObject, reactVtkjsViewport) {
reactVtkjsViewport.loadImageData(imageDataObject);
const { isLoading, insertPixelDataPromises } = imageDataObject;

View File

@ -1,9 +1,16 @@
import React, { useEffect, useCallback } from 'react';
import { View2D } from 'react-vtkjs-viewport';
// import { View2D } from 'react-vtkjs-viewport';
import PropTypes from 'prop-types';
import asyncComponent from './asyncComponent.js';
import { getReactVtkjsViewport } from './utils/getReactVtkjsViewport';
import './VTKViewport.css';
const View2D = asyncComponent(async () => {
const reactVtkjsViewport = await getReactVtkjsViewport();
return { default: reactVtkjsViewport.View2D };
});
const VTKViewport = props => {
const style = { width: '100%', height: '100%', position: 'relative' };
@ -52,7 +59,7 @@ VTKViewport.propTypes = {
};
VTKViewport.defaultProps = {
onScroll: () => {},
onScroll: () => { },
};
export default VTKViewport;

View File

@ -1,10 +1,5 @@
import throttle from 'lodash.throttle';
import {
vtkInteractorStyleMPRCrosshairs,
vtkInteractorStyleMPRWindowLevel,
vtkInteractorStyleMPRRotate,
vtkSVGCrosshairsWidget,
} from 'react-vtkjs-viewport';
import { getReactVtkjsViewport } from './utils/getReactVtkjsViewport';
import setMPRLayout from './utils/setMPRLayout.js';
import setViewportToVTK from './utils/setViewportToVTK.js';
@ -124,16 +119,18 @@ const commandsModule = ({ commandsManager }) => {
_setView(api, [0, 1, 0], [0, 0, 1]);
},
enableRotateTool: () => {
enableRotateTool: async () => {
const reactVtkjsViewport = await getReactVtkjsViewport();
apis.forEach(api => {
const istyle = vtkInteractorStyleMPRRotate.newInstance();
const istyle = reactVtkjsViewport.vtkInteractorStyleMPRRotate.newInstance();
api.setInteractorStyle({ istyle });
});
},
enableCrosshairsTool: () => {
enableCrosshairsTool: async () => {
const reactVtkjsViewport = await getReactVtkjsViewport();
apis.forEach((api, apiIndex) => {
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
const istyle = reactVtkjsViewport.vtkInteractorStyleMPRCrosshairs.newInstance();
api.setInteractorStyle({
istyle,
@ -141,7 +138,7 @@ const commandsModule = ({ commandsManager }) => {
});
});
},
enableLevelTool: () => {
enableLevelTool: async () => {
function updateVOI(apis, windowWidth, windowCenter) {
apis.forEach(api => {
api.updateVOI(windowWidth, windowCenter);
@ -162,8 +159,9 @@ const commandsModule = ({ commandsManager }) => {
},
};
const reactVtkjsViewport = await getReactVtkjsViewport();
apis.forEach(api => {
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance();
const istyle = reactVtkjsViewport.vtkInteractorStyleMPRWindowLevel.newInstance();
api.setInteractorStyle({ istyle, callbacks });
});
@ -260,14 +258,15 @@ const commandsModule = ({ commandsManager }) => {
}
// Add widgets and set default interactorStyle of each viewport.
const reactVtkjsViewport = await getReactVtkjsViewport();
apis.forEach((api, apiIndex) => {
api.addSVGWidget(
vtkSVGCrosshairsWidget.newInstance(),
reactVtkjsViewport.vtkSVGCrosshairsWidget.newInstance(),
'crosshairsWidget'
);
const uid = api.uid;
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
const istyle = reactVtkjsViewport.vtkInteractorStyleMPRCrosshairs.newInstance();
api.setInteractorStyle({
istyle,

View File

@ -0,0 +1,5 @@
export function getReactVtkjsViewport() {
return import(
/* webpackChunkName: "ReactVtkjsViewport" */ 'react-vtkjs-viewport'
);
}

View File

@ -1,10 +1,10 @@
import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import {
clearStudyLoadingProgress,
setStudyLoadingProgress,
} from '../redux/actions';
import StackManager from '../utils/StackManager';
import { getCornerstoneWADOImageLoader } from '../utils/cornerstoneWADOImageLoader';
class BaseLoadingListener {
constructor(stack, options = {}) {
@ -103,7 +103,8 @@ class DICOMFileLoadingListener extends BaseLoadingListener {
this._checkCachedData();
}
_checkCachedData() {
async _checkCachedData() {
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(
this._dataSetUrl
);

View File

@ -1,8 +1,8 @@
import DICOMWeb from '../../../DICOMWeb';
import metadataProvider from '../../../classes/MetadataProvider';
import getWADORSImageId from '../../../utils/getWADORSImageId';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import getReferencedSeriesSequence from './getReferencedSeriesSequence';
import { getCornerstoneWADOImageLoader } from '../../../utils/cornerstoneWADOImageLoader';
/**
* Create a plain JS object that describes a study (a study descriptor object)
@ -160,6 +160,7 @@ async function makeSOPInstance(server, study, instance) {
const wadoRSMetadata = Object.assign(instance);
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
if (sopInstance.NumberOfFrames) {
for (let i = 0; i < sopInstance.NumberOfFrames; i++) {
const wadorsImageId = getWADORSImageId(sopInstance, i);

View File

@ -0,0 +1,5 @@
export function getCornerstoneWADOImageLoader() {
return import(
/* webpackChunkName: "CornerstoneWADOImageLoader" */ 'cornerstone-wado-image-loader'
);
}

View File

@ -1,7 +1,7 @@
import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import { api } from 'dicomweb-client';
import DICOMWeb from '../DICOMWeb';
import { getCornerstoneWADOImageLoader } from './cornerstoneWADOImageLoader';
const getImageId = imageObj => {
if (!imageObj) {
@ -86,7 +86,7 @@ const getImageLoaderType = imageId => {
};
class DicomLoaderService {
getLocalData(dataset, studies) {
async getLocalData(dataset, studies) {
if (dataset && dataset.localFile) {
// Use referenced imageInstance
const imageInstance = getImageInstance(dataset);
@ -98,6 +98,7 @@ class DicomLoaderService {
}
if (!someInvalidStrings(imageId)) {
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);
}
}
@ -176,16 +177,16 @@ class DicomLoaderService {
}
}
*getLoaderIterator(dataset, studies) {
yield this.getLocalData(dataset, studies);
async *getLoaderIterator(dataset, studies) {
yield await this.getLocalData(dataset, studies);
yield this.getDataByImageType(dataset);
yield this.getDataByDatasetType(dataset);
}
findDicomDataPromise(dataset, studies) {
async findDicomDataPromise(dataset, studies) {
const loaderIterator = this.getLoaderIterator(dataset, studies);
// it returns first valid retriever method.
for (const loader of loaderIterator) {
for await (const loader of loaderIterator) {
if (loader) {
return loader;
}

View File

@ -34,10 +34,7 @@ import i18n from '@ohif/i18n';
import './config';
/** Utils */
import {
getUserManagerForOpenIdConnectClient,
initWebWorkers,
} from './utils/index.js';
import { getUserManagerForOpenIdConnectClient } from './utils/index.js';
/** Extensions */
import { GenericViewerCommands, MeasurementsPanel } from './appExtensions';
@ -140,7 +137,6 @@ class App extends Component {
*/
_initHotkeys(appConfigHotkeys);
_initServers(servers);
initWebWorkers();
}
render() {

View File

@ -1,7 +1,4 @@
import OHIF from '@ohif/core';
import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import dicomParser from 'dicom-parser';
import version from './version.js';
let homepage;
@ -15,31 +12,4 @@ window.info = {
homepage,
};
// For debugging
//if (process.env.node_env === 'development') {
window.cornerstone = cornerstone;
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
//}
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
OHIF.user.getAccessToken = () => {
// TODO: Get the Redux store from somewhere else
const state = window.store.getState();
if (!state.oidc || !state.oidc.user) {
return;
}
return state.oidc.user.access_token;
};
cornerstoneWADOImageLoader.configure({
beforeSend: function(xhr) {
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
if (headers.Authorization) {
xhr.setRequestHeader('Authorization', headers.Authorization);
}
},
});

View File

@ -3,7 +3,7 @@ import FileLoaderService from './localFileLoaders/fileLoaderService';
const processFile = async file => {
try {
const fileLoaderService = new FileLoaderService(file);
const imageId = fileLoaderService.addFile(file);
const imageId = await fileLoaderService.addFile(file);
const image = await fileLoaderService.loadFile(file, imageId);
const dataset = await fileLoaderService.getDataset(image, imageId);
const studies = await fileLoaderService.getStudies(dataset, imageId);

View File

@ -1,13 +1,14 @@
import * as dcmjs from 'dcmjs';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import FileLoader from './fileLoader';
import OHIF from '@ohif/core';
import { getCornerstoneWADOImageLoader } from '../../utils/cornerstoneWADOImageLoader';
const metadataProvider = OHIF.cornerstone.metadataProvider;
const DICOMFileLoader = new (class extends FileLoader {
fileType = 'application/dicom';
loadFile(file, imageId) {
async loadFile(file, imageId) {
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);
}

View File

@ -1,7 +1,7 @@
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import FileLoader from './fileLoader';
import PDFFileLoader from './pdfFileLoader';
import DICOMFileLoader from './dicomFileLoader';
import { getCornerstoneWADOImageLoader } from '../../utils/cornerstoneWADOImageLoader';
class FileLoaderService extends FileLoader {
fileType;
@ -53,7 +53,8 @@ class FileLoaderService extends FileLoader {
return result;
}
addFile(file) {
async addFile(file) {
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
return cornerstoneWADOImageLoader.wadouri.fileManager.add(file);
}

View File

@ -1,9 +1,10 @@
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import FileLoader from './fileLoader';
import { getCornerstoneWADOImageLoader } from '../../utils/cornerstoneWADOImageLoader';
const PDFFileLoader = new (class extends FileLoader {
fileType = 'application/pdf';
loadFile(file, imageId) {
async loadFile(file, imageId) {
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);
}

View File

@ -1,4 +1,5 @@
import asyncComponent from '../components/AsyncComponent.js';
import { withCornerstone } from '../utils/cornerstoneWADOImageLoader';
import OHIF from '@ohif/core';
const { urlUtil: UrlUtil } = OHIF.utils;
@ -9,8 +10,10 @@ const IHEInvokeImageDisplay = asyncComponent(() =>
/* webpackChunkName: "IHEInvokeImageDisplay" */ './IHEInvokeImageDisplay.js'
)
);
const ViewerRouting = asyncComponent(() =>
import(/* webpackChunkName: "ViewerRouting" */ './ViewerRouting.js')
const ViewerRouting = withCornerstone(
asyncComponent(() =>
import(/* webpackChunkName: "ViewerRouting" */ './ViewerRouting.js')
)
);
const StudyListRouting = asyncComponent(() =>
@ -18,12 +21,18 @@ const StudyListRouting = asyncComponent(() =>
/* webpackChunkName: "StudyListRouting" */ '../studylist/StudyListRouting.js'
)
);
const StandaloneRouting = asyncComponent(() =>
import(/* webpackChunkName: "ConnectedStandaloneRouting" */ '../connectedComponents/ConnectedStandaloneRouting.js')
const StandaloneRouting = withCornerstone(
asyncComponent(() =>
import(
/* webpackChunkName: "ConnectedStandaloneRouting" */ '../connectedComponents/ConnectedStandaloneRouting.js'
)
)
);
const ViewerLocalFileData = asyncComponent(() =>
import(
/* webpackChunkName: "ViewerLocalFileData" */ '../connectedComponents/ViewerLocalFileData.js'
const ViewerLocalFileData = withCornerstone(
asyncComponent(() =>
import(
/* webpackChunkName: "ViewerLocalFileData" */ '../connectedComponents/ViewerLocalFileData.js'
)
)
);

View File

@ -0,0 +1,79 @@
import OHIF from '@ohif/core';
import cornerstone from 'cornerstone-core';
import dicomParser from 'dicom-parser';
import React from 'react';
export function getCornerstoneWADOImageLoader() {
return import(
/* webpackChunkName: "CornerstoneWADOImageLoader" */ 'cornerstone-wado-image-loader'
);
}
let initialized = false;
export async function initCornerstoneWADOImageLoader() {
if (initialized) return;
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
// For debugging
//if (process.env.node_env === 'development') {
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
//}
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
OHIF.user.getAccessToken = () => {
// TODO: Get the Redux store from somewhere else
const state = window.store.getState();
if (!state.oidc || !state.oidc.user) {
return;
}
return state.oidc.user.accesstoken;
};
cornerstoneWADOImageLoader.configure({
beforeSend: function (xhr) {
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
if (headers.Authorization) {
xhr.setRequestHeader('Authorization', headers.Authorization);
}
},
});
const config = {
maxWebWorkers: Math.max(navigator.hardwareConcurrency - 1, 1),
startWebWorkersOnDemand: true,
taskConfiguration: {
decodeTask: {
initializeCodecsOnStartup: false,
usePDFJS: false,
strict: false,
},
},
};
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
initialized = true;
}
export function withCornerstone(Component) {
// eslint-disable-next-line react/display-name
return props => {
const [
cornerstoneWADOImageLoaderInitialized,
setCornerstoneWADOImageLoaderInitialized,
] = React.useState(false);
React.useEffect(() => {
initCornerstoneWADOImageLoader().then(() =>
setCornerstoneWADOImageLoaderInitialized(true)
);
}, []);
if (!cornerstoneWADOImageLoaderInitialized) {
return null;
}
return <Component {...props} />;
};
}

View File

@ -1,4 +1,3 @@
import getUserManagerForOpenIdConnectClient from './getUserManagerForOpenIdConnectClient.js';
import initWebWorkers from './initWebWorkers.js';
export { getUserManagerForOpenIdConnectClient, initWebWorkers };
export { getUserManagerForOpenIdConnectClient };

View File

@ -5,7 +5,7 @@ describe('utils', () => {
const utilExports = Object.keys(utils).sort();
expect(utilExports).toEqual(
['getUserManagerForOpenIdConnectClient', 'initWebWorkers'].sort()
['getUserManagerForOpenIdConnectClient'].sort()
);
});
});

View File

@ -1,22 +0,0 @@
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
let initialized = false;
export default function initWebWorkers() {
const config = {
maxWebWorkers: Math.max(navigator.hardwareConcurrency - 1, 1),
startWebWorkersOnDemand: true,
taskConfiguration: {
decodeTask: {
initializeCodecsOnStartup: false,
usePDFJS: false,
strict: false,
},
},
};
if (!initialized) {
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
initialized = true;
}
}

View File

@ -1,23 +0,0 @@
import initWebWorkers from './initWebWorkers.js';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
describe('initWebWorkers', () => {
it("initializes cornerstoneWADOImageLoader's web workers", () => {
initWebWorkers();
expect(
cornerstoneWADOImageLoader.webWorkerManager.initialize
).toHaveBeenCalled();
});
});
describe('initWebWorkers', () => {
it("initializes cornerstoneWADOImageLoader's web workers only once", () => {
initWebWorkers();
initWebWorkers();
expect(
cornerstoneWADOImageLoader.webWorkerManager.initialize
).toHaveBeenCalledTimes(1);
});
});