Skip to content

Commit

Permalink
[js/web] allow build target for non dynamic import (#20898)
Browse files Browse the repository at this point in the history
### Description
<!-- Describe your changes. -->

This PR allows to build ORT web to `ort{.all|.webgpu}.bundle.min.mjs`,
which does not have any dynamic import. This makes it possible to use
ort web via static import in service worker.

Fixes #20876
  • Loading branch information
fs-eire authored Jun 3, 2024
1 parent d13cabf commit ab9f153
Show file tree
Hide file tree
Showing 6 changed files with 384 additions and 24 deletions.
197 changes: 197 additions & 0 deletions js/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"mocha": "^10.2.0",
"npmlog": "^7.0.1",
"prettier": "^3.0.3",
"terser": "^5.31.0",
"typescript": "^5.2.2"
},
"scripts": {
Expand Down
4 changes: 4 additions & 0 deletions js/web/lib/build-def.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ interface BuildDefinitions {
* defines whether to disable training APIs in WebAssembly backend.
*/
readonly DISABLE_TRAINING: boolean;
/**
* defines whether to disable dynamic importing WASM module in the build.
*/
readonly DISABLE_DYNAMIC_IMPORT: boolean;

// #endregion

Expand Down
60 changes: 40 additions & 20 deletions js/web/lib/wasm/wasm-utils-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,28 @@ export const importProxyWorker = async(): Promise<[undefined | string, Worker]>
return [url, createProxyWorker!(url)];
};

/**
* The embedded WebAssembly module.
*
* This is only available in ESM and when embedding is not disabled.
*/
const embeddedWasmModule: EmscriptenModuleFactory<OrtWasmModule>|undefined =
BUILD_DEFS.IS_ESM && BUILD_DEFS.DISABLE_DYNAMIC_IMPORT ?
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
require(
!BUILD_DEFS.DISABLE_TRAINING ? '../../dist/ort-training-wasm-simd-threaded.mjs' :
!BUILD_DEFS.DISABLE_JSEP ? '../../dist/ort-wasm-simd-threaded.jsep.mjs' :
'../../dist/ort-wasm-simd-threaded.mjs')
.default :
undefined;

/**
* Import the WebAssembly module.
*
* This function will perform the following steps:
* 1. If a preload is needed, it will preload the module and return the object URL.
* 2. Otherwise, it will perform a dynamic import of the module.
* 1. If BUILD_DEFS.DISABLE_DYNAMIC_IMPORT is true, use the embedded module.
* 2. If a preload is needed, it will preload the module and return the object URL.
* 3. Otherwise, it will perform a dynamic import of the module.
*
* @returns - A promise that resolves to a tuple of 2 elements:
* - The object URL of the preloaded module, or undefined if no preload is needed.
Expand All @@ -135,22 +151,26 @@ export const importProxyWorker = async(): Promise<[undefined | string, Worker]>
export const importWasmModule = async(
urlOverride: string|undefined, prefixOverride: string|undefined,
isMultiThreaded: boolean): Promise<[undefined | string, EmscriptenModuleFactory<OrtWasmModule>]> => {
const wasmModuleFilename = !BUILD_DEFS.DISABLE_TRAINING ? 'ort-training-wasm-simd-threaded.mjs' :
!BUILD_DEFS.DISABLE_JSEP ? 'ort-wasm-simd-threaded.jsep.mjs' :
'ort-wasm-simd-threaded.mjs';
const wasmModuleUrl = urlOverride ?? normalizeUrl(wasmModuleFilename, prefixOverride);
// need to preload if all of the following conditions are met:
// 1. not in Node.js.
// - Node.js does not have the same origin policy for creating workers.
// 2. multi-threaded is enabled.
// - If multi-threaded is disabled, no worker will be created. So we don't need to preload the module.
// 3. the absolute URL is available.
// - If the absolute URL is failed to be created, the origin cannot be determined. In this case, we will not
// preload the module.
// 4. the worker URL is not from the same origin.
// - If the worker URL is from the same origin, we can create the worker directly.
const needPreload = !isNode && isMultiThreaded && wasmModuleUrl && !isSameOrigin(wasmModuleUrl, prefixOverride);
const url =
needPreload ? (await preload(wasmModuleUrl)) : (wasmModuleUrl ?? fallbackUrl(wasmModuleFilename, prefixOverride));
return [needPreload ? url : undefined, await dynamicImportDefault<EmscriptenModuleFactory<OrtWasmModule>>(url)];
if (BUILD_DEFS.DISABLE_DYNAMIC_IMPORT) {
return [undefined, embeddedWasmModule!];
} else {
const wasmModuleFilename = !BUILD_DEFS.DISABLE_TRAINING ? 'ort-training-wasm-simd-threaded.mjs' :
!BUILD_DEFS.DISABLE_JSEP ? 'ort-wasm-simd-threaded.jsep.mjs' :
'ort-wasm-simd-threaded.mjs';
const wasmModuleUrl = urlOverride ?? normalizeUrl(wasmModuleFilename, prefixOverride);
// need to preload if all of the following conditions are met:
// 1. not in Node.js.
// - Node.js does not have the same origin policy for creating workers.
// 2. multi-threaded is enabled.
// - If multi-threaded is disabled, no worker will be created. So we don't need to preload the module.
// 3. the absolute URL is available.
// - If the absolute URL is failed to be created, the origin cannot be determined. In this case, we will not
// preload the module.
// 4. the worker URL is not from the same origin.
// - If the worker URL is from the same origin, we can create the worker directly.
const needPreload = !isNode && isMultiThreaded && wasmModuleUrl && !isSameOrigin(wasmModuleUrl, prefixOverride);
const url = needPreload ? (await preload(wasmModuleUrl)) :
(wasmModuleUrl ?? fallbackUrl(wasmModuleFilename, prefixOverride));
return [needPreload ? url : undefined, await dynamicImportDefault<EmscriptenModuleFactory<OrtWasmModule>>(url)];
}
};
Loading

0 comments on commit ab9f153

Please sign in to comment.