Skip to content

Commit

Permalink
feat: Use a global import function to perform lazy loading (#1344)
Browse files Browse the repository at this point in the history
  • Loading branch information
wayfarer3130 authored Jul 5, 2024
1 parent ea0dc03 commit 4653c8b
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 32 deletions.
8 changes: 7 additions & 1 deletion common/reviews/api/core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ type Cornerstone3DConfig = {
strictZSpacingForVolumeViewport: boolean;
};
enableCacheOptimization: boolean;
peerImport?: (moduleId: string) => any;
};

// @public (undocumented)
Expand Down Expand Up @@ -2574,6 +2575,9 @@ type OrientationVectors = {
viewUp: Point3;
};

// @public (undocumented)
export function peerImport(moduleId: string): any;

// @public (undocumented)
function performCacheOptimizationForVolume(volume: any): void;

Expand Down Expand Up @@ -4404,6 +4408,8 @@ export class WSIViewport extends Viewport implements IWSIViewport {
// (undocumented)
getCurrentImageIdIndex(): number;
// (undocumented)
static getDicomMicroscopyViewer: () => Promise<any>;
// (undocumented)
getFrameNumber(): number;
// (undocumented)
getFrameOfReferenceUID: () => string;
Expand Down Expand Up @@ -4480,7 +4486,7 @@ export class WSIViewport extends Viewport implements IWSIViewport {
// (undocumented)
setCamera(camera: ICamera): void;
// (undocumented)
setDataIds(imageIds: string[]): void;
setDataIds(imageIds: string[]): Promise<void>;
// (undocumented)
setFrameNumber(frame: number): Promise<void>;
// (undocumented)
Expand Down
40 changes: 28 additions & 12 deletions packages/core/src/RenderingEngine/WSIViewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,26 @@ import {
WSIViewportInput,
VOIRange,
} from '../types';
import uuidv4 from '../utilities/uuidv4';
import * as metaData from '../metaData';
import { Transform } from './helpers/cpuFallback/rendering/transform';
import Viewport from './Viewport';
import { getOrCreateCanvas } from './helpers';
import { EPSILON } from '../constants';
import { triggerEvent } from '../utilities';
import { peerImport } from '../init';

const _map = Symbol.for('map');
const EVENT_POSTRENDER = 'postrender';
/**
* An object representing a single stack viewport, which is a camera
* looking into an internal scene, and an associated target output `canvas`.
* A viewport which shows a microscopy view using the dicom-microscopy-viewer
* library. This viewport accepts standard CS3D annotations, and responds
* similar to how the other types of viewports do for things like zoom/pan.
*
* This viewport required the `dicom-microscopy-viewer` import to be available
* from the peerImport function in the CS3D init configuration. See the
* example `initDemo.js` for one possible implementation, but the actual
* implementation of this will depend on your platform.
*/
class WSIViewport extends Viewport implements IWSIViewport {
public modality;
Expand Down Expand Up @@ -76,7 +84,7 @@ class WSIViewport extends Viewport implements IWSIViewport {
// use absolute positioning internally.
this.element.style.position = 'relative';
this.microscopyElement = document.createElement('div');
this.microscopyElement.id = crypto.randomUUID();
this.microscopyElement.id = uuidv4();
this.microscopyElement.innerText = 'Initial';
this.microscopyElement.style.background = 'grey';
this.microscopyElement.style.width = '100%';
Expand Down Expand Up @@ -209,8 +217,11 @@ class WSIViewport extends Viewport implements IWSIViewport {

public getImageData() {
const { metadata } = this;
if (!metadata) {
return;
}

const spacing = metadata.spacing;
const { spacing } = metadata;

return {
dimensions: metadata.dimensions,
Expand Down Expand Up @@ -342,11 +353,15 @@ class WSIViewport extends Viewport implements IWSIViewport {
};

/**
* Need to return this as a function to prevent webpack from munging it.
* Encapsulate the dicom microscopy fetch so that it can be replaced
* with the browser import function. Webpack munges this and then throws
* exceptions trying to get this working, so this has to be provided externally
* as a globalThis.browserImportFunction taking the package name, and a set
* of options defining how to get the value out of the package.
*/
private getImportPath() {
return '/dicom-microscopy-viewer/dicomMicroscopyViewer.min.js';
}
public static getDicomMicroscopyViewer = async () => {
return peerImport('dicom-microscopy-viewer');
};

/**
* The FOR for whole slide imaging is the frame of reference in the DICOM
Expand Down Expand Up @@ -434,16 +449,15 @@ class WSIViewport extends Viewport implements IWSIViewport {
);
}

this.setWSI(imageIds, webClient);
// Returns the Promise from the child element.
return this.setWSI(imageIds, webClient);
}

public async setWSI(imageIds: string[], client) {
this.microscopyElement.style.background = 'red';
this.microscopyElement.innerText = 'Loading';
this.imageIds = imageIds;
// Import the straight module so that webpack doesn't touch it.
await import(/* webpackIgnore: true */ this.getImportPath());
const DicomMicroscopyViewer = (window as any).dicomMicroscopyViewer;
const DicomMicroscopyViewer = await WSIViewport.getDicomMicroscopyViewer();
this.frameOfReferenceUID = null;

const metadataDicomweb = this.imageIds.map((imageId) => {
Expand Down Expand Up @@ -479,6 +493,8 @@ class WSIViewport extends Viewport implements IWSIViewport {
const imageFlavor = image.ImageType[2];
if (imageFlavor === 'VOLUME' || imageFlavor === 'THUMBNAIL') {
volumeImages.push(image);
} else {
console.log('Unknown image type', image.ImageType);
}
});
this.metadataDicomweb = volumeImages;
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
setConfiguration,
getWebWorkerManager,
canRenderFloatTextures,
peerImport,
} from './init';

// Classes
Expand Down Expand Up @@ -87,6 +88,7 @@ export {
// init
init,
isCornerstoneInitialized,
peerImport,
// configs
getConfiguration,
setConfiguration,
Expand Down
25 changes: 13 additions & 12 deletions packages/core/src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,17 @@ const defaultConfig: Cornerstone3DConfig = {
},
// cache
enableCacheOptimization: true,
/**
* Imports peer modules.
* This may just fallback to the default import, but many packaging
* systems don't deal with peer imports properly.
*/
peerImport: (moduleId) => null,
};

let config: Cornerstone3DConfig = {
gpuTier: undefined,
detectGPUConfig: {},
isMobile: false, // is mobile device
rendering: {
useCPURendering: false,
// GPU rendering options
preferSizeOverAccuracy: false,
useNorm16Texture: false,
strictZSpacingForVolumeViewport: true,
},
// cache
enableCacheOptimization: true,
...defaultConfig,
rendering: { ...defaultConfig.rendering },
};

let webWorkerManager = null;
Expand Down Expand Up @@ -313,6 +309,10 @@ function getWebWorkerManager() {
return webWorkerManager;
}

function peerImport(moduleId: string) {
return config.peerImport(moduleId);
}

export {
init,
getShouldUseCPURendering,
Expand All @@ -327,4 +327,5 @@ export {
setConfiguration,
getWebWorkerManager,
canRenderFloatTextures,
peerImport,
};
6 changes: 6 additions & 0 deletions packages/core/src/types/Cornerstone3DConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ type Cornerstone3DConfig = {
* buffers.
*/
enableCacheOptimization: boolean;
/**
* This function returns an imported module for the given module id.
* It allows replacing broken packing system imports with external importers
* that perform lazy imports.
*/
peerImport?: (moduleId: string) => any;
};

export default Cornerstone3DConfig;
7 changes: 7 additions & 0 deletions packages/docs/docs/contribute/linking.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ comes with various webpack configurations, you can use/run the
force the reflected changes to be built again which is faster than `yarn build`.
and it also watches for changes to the source code and rebuilds the package.

## External Components

Some components such as the `dicom-microscopy-viewer` are linked externally as
optional inclusions in the overall `cornerstone3D` package. You will need to
add a peerImport function which can import the required modules, and register
your function with the cornerstone init method.

## Tips

1. `yarn link` is actually a symlink between packages. If your linking is not working,
Expand Down
2 changes: 1 addition & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ export default defineConfig({
command: 'yarn build-and-serve-static-examples',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120 * 1000,
timeout: 150 * 1000,
},
});
3 changes: 2 additions & 1 deletion utils/ExampleRunner/example-runner-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const rootPath = path.resolve(path.join(__dirname, '../..'));
program
.option('-c, --config [file.js]', 'Configuration file')
.option('--no-browser', 'Do not open the browser')
.option('--https', 'Enable https')
.parse(process.argv);

const options = program.opts();
Expand Down Expand Up @@ -283,7 +284,7 @@ function run() {
// You can run this with --no-cache after the serve to prevent caching
// which can help when doing certain types of development.
shell.exec(
`webpack serve --host 0.0.0.0 --progress --config ${webpackConfigPath}`
`webpack serve --host 0.0.0.0 ${options.https ? '--https' : ''} --progress --config ${webpackConfigPath}`
);
} else {
console.log('=> To run an example:');
Expand Down
6 changes: 6 additions & 0 deletions utils/ExampleRunner/template-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ module.exports = {
open: false,
port: ${process.env.CS3D_PORT || 3000},
historyApiFallback: true,
allowedHosts: [
'127.0.0.1',
'localhost',
// Change the next line to add your localhostname to run via localhostname
// 'braveheart2',
],
headers: {
"Cross-Origin-Embedder-Policy": "require-corp",
"Cross-Origin-Opener-Policy": "same-origin"
Expand Down
21 changes: 20 additions & 1 deletion utils/demo/helpers/initDemo.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,25 @@ export default async function initDemo() {
initProviders();
initCornerstoneDICOMImageLoader();
initVolumeLoader();
await csRenderInit();
await csRenderInit({ peerImport });
await csToolsInit();
}

/**
* This is one example of how to import peer modules that works with webpack
* It in fact just uses the default import from the browser, so it should work
* on any standards compliant ecmascript environment.
*/
export async function peerImport(moduleId) {
if (moduleId === 'dicom-microscopy-viewer') {
return importGlobal(
'/dicom-microscopy-viewer/dicomMicroscopyViewer.min.js',
'dicomMicroscopyViewer'
);
}
}

async function importGlobal(path, globalName) {
await import(/* webpackIgnore: true */ path);
return window[globalName];
}
41 changes: 37 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2130,7 +2130,7 @@
"@docusaurus/theme-search-algolia" "2.3.1"
"@docusaurus/types" "2.3.1"

"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2":
"@docusaurus/react-loadable@5.5.2":
version "5.5.2"
resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce"
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
Expand Down Expand Up @@ -18931,6 +18931,14 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1:
dependencies:
"@babel/runtime" "^7.10.3"

"react-loadable@npm:@docusaurus/react-loadable@5.5.2":
version "5.5.2"
resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce"
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
dependencies:
"@types/react" "*"
prop-types "^15.6.2"

react-resize-detector@6.7.8:
version "6.7.8"
resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-6.7.8.tgz#318c85d1335e50f99d4fb8eb9ec34e066db597d0"
Expand Down Expand Up @@ -20793,7 +20801,7 @@ string-similarity@^4.0.4:
resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b"
integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==

"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand All @@ -20811,6 +20819,15 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"

"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@^2.1.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
Expand Down Expand Up @@ -20884,7 +20901,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand Down Expand Up @@ -20917,6 +20934,13 @@ strip-ansi@^5.1.0:
dependencies:
ansi-regex "^4.1.0"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^7.0.0, strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
Expand Down Expand Up @@ -22982,7 +23006,7 @@ workerpool@6.2.1:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand All @@ -23008,6 +23032,15 @@ wrap-ansi@^6.0.1:
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
Expand Down

0 comments on commit 4653c8b

Please sign in to comment.