From 3925c91fa6e570f0234358c4e986f34129c4ece4 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Mon, 27 Jan 2025 11:40:50 -0800 Subject: [PATCH] export test --- js/web/lib/build-def.d.ts | 5 +++ js/web/lib/wasm/wasm-factory.ts | 66 ++++++++++++++++------------ js/web/lib/wasm/wasm-utils-import.ts | 32 ++++++++++++++ js/web/package.json | 4 +- js/web/script/build.ts | 14 ++++-- 5 files changed, 88 insertions(+), 33 deletions(-) diff --git a/js/web/lib/build-def.d.ts b/js/web/lib/build-def.d.ts index 59f64a3179..889d92b757 100644 --- a/js/web/lib/build-def.d.ts +++ b/js/web/lib/build-def.d.ts @@ -52,6 +52,11 @@ interface BuildDefinitions { * placeholder for the import.meta.url in ESM. in CJS, this is undefined. */ readonly ESM_IMPORT_META_URL: string | undefined; + /** + * placeholder for the import.meta.meta in ESM. in CJS, this is undefined. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + readonly ESM_IMPORT_META_ENV: any; // #endregion diff --git a/js/web/lib/wasm/wasm-factory.ts b/js/web/lib/wasm/wasm-factory.ts index 0f49d25040..a636c7dfc5 100644 --- a/js/web/lib/wasm/wasm-factory.ts +++ b/js/web/lib/wasm/wasm-factory.ts @@ -4,7 +4,11 @@ import { Env } from 'onnxruntime-common'; import type { OrtWasmModule } from './wasm-types'; -import { importWasmModule, inferWasmPathPrefixFromScriptSrc } from './wasm-utils-import'; +import { + importWasmModule, + inferWasmPathPrefixFromScriptSrc, + tryOverwriteDefaultWasmUrlForBundlers, +} from './wasm-utils-import'; let wasm: OrtWasmModule | undefined; let initialized = false; @@ -118,6 +122,39 @@ export const initializeWebAssembly = async (flags: Env.WebAssemblyFlags): Promis const [objectUrl, ortWasmFactory] = await importWasmModule(mjsPathOverride, wasmPrefixOverride, numThreads > 1); + const config: Partial = { + /** + * The number of threads. WebAssembly will create (Module.numThreads - 1) workers. If it is 1, no worker will be + * created. + */ + numThreads, + }; + + if (wasmBinaryOverride) { + // Set a custom buffer which contains the WebAssembly binary. This will skip the wasm file fetching. + config.wasmBinary = wasmBinaryOverride; + } else if (wasmPathOverride || wasmPrefixOverride) { + // A callback function to locate the WebAssembly file. The function should return the full path of the file. + // + // Since Emscripten 3.1.58, this function is only called for the .wasm file. + config.locateFile = (fileName) => wasmPathOverride ?? wasmPrefixOverride + fileName; + } else if (mjsPathOverride && mjsPathOverride.indexOf('blob:') !== 0) { + // if mjs path is specified, use it as the base path for the .wasm file. + config.locateFile = (fileName) => new URL(fileName, mjsPathOverride).href; + } else if (objectUrl) { + const inferredWasmPathPrefix = inferWasmPathPrefixFromScriptSrc(); + if (inferredWasmPathPrefix) { + // if the wasm module is preloaded, use the inferred wasm path as the base path for the .wasm file. + config.locateFile = (fileName) => inferredWasmPathPrefix + fileName; + } + } else { + // try to overwrite the default wasm URL for bundlers if needed + const wasmFileUrl = await tryOverwriteDefaultWasmUrlForBundlers(); + if (wasmFileUrl) { + config.locateFile = () => wasmFileUrl; + } + } + let isTimeout = false; const tasks: Array> = []; @@ -137,33 +174,6 @@ export const initializeWebAssembly = async (flags: Env.WebAssemblyFlags): Promis // promise for module initialization tasks.push( new Promise((resolve, reject) => { - const config: Partial = { - /** - * The number of threads. WebAssembly will create (Module.numThreads - 1) workers. If it is 1, no worker will be - * created. - */ - numThreads, - }; - - if (wasmBinaryOverride) { - // Set a custom buffer which contains the WebAssembly binary. This will skip the wasm file fetching. - config.wasmBinary = wasmBinaryOverride; - } else if (wasmPathOverride || wasmPrefixOverride) { - // A callback function to locate the WebAssembly file. The function should return the full path of the file. - // - // Since Emscripten 3.1.58, this function is only called for the .wasm file. - config.locateFile = (fileName) => wasmPathOverride ?? wasmPrefixOverride + fileName; - } else if (mjsPathOverride && mjsPathOverride.indexOf('blob:') !== 0) { - // if mjs path is specified, use it as the base path for the .wasm file. - config.locateFile = (fileName) => new URL(fileName, mjsPathOverride).href; - } else if (objectUrl) { - const inferredWasmPathPrefix = inferWasmPathPrefixFromScriptSrc(); - if (inferredWasmPathPrefix) { - // if the wasm module is preloaded, use the inferred wasm path as the base path for the .wasm file. - config.locateFile = (fileName) => inferredWasmPathPrefix + fileName; - } - } - ortWasmFactory(config).then( // wasm module initialized successfully (module) => { diff --git a/js/web/lib/wasm/wasm-utils-import.ts b/js/web/lib/wasm/wasm-utils-import.ts index 871b575d71..de29b414ea 100644 --- a/js/web/lib/wasm/wasm-utils-import.ts +++ b/js/web/lib/wasm/wasm-utils-import.ts @@ -213,3 +213,35 @@ export const importWasmModule = async ( return [needPreload ? url : undefined, await dynamicImportDefault>(url)]; } }; + +/** + * Try to overwrite the default wasm URL for bundlers if needed. + * + * This function is used to provide best-effort support for out of the box compatibility with bundlers. + * + * @returns - A promise that resolves to the new wasm URL, or undefined if no overwrite is needed. + */ +export const tryOverwriteDefaultWasmUrlForBundlers = async (): Promise => { + // === workaround for Vite. === + // + // Vite uses rollup, which does not add the .wasm file into the asset list automatically like webpack. So we + // need to do this manually using the "Explicit URL Imports" feature described in + // https://vite.dev/guide/assets#explicit-url-imports + // + // The following condition is used to detect if the current environment is Vite. Since Vite does not provide an + // official way to detect if the current environment is Vite, we check if `import.meta.env.SSR` is `false`. + // + // According to the Vite documentation, `import.meta.env.SSR` is always set to a boolean value. + // See https://vite.dev/guide/env-and-mode#env-variables + // + if (BUILD_DEFS.ESM_IMPORT_META_ENV?.SSR === false) { + try { + //const wasmFileUrlForVite = (await import(/* webpackIgnore: true */ 'onnxruntime-web/.wasm?url')).default; + //return wasmFileUrlForVite; + } catch { + // ignore the error if the module is not found. + } + } + + return undefined; +}; diff --git a/js/web/package.json b/js/web/package.json index 4e6e2c32ae..188cc7e395 100644 --- a/js/web/package.json +++ b/js/web/package.json @@ -13,7 +13,7 @@ "flatbuffers": "^1.12.0", "guid-typescript": "^1.0.9", "long": "^5.2.3", - "onnxruntime-common": "file:../common", + "onnxruntime-common": "1.21.0", "platform": "^1.3.6", "protobufjs": "^7.2.4" }, @@ -73,7 +73,7 @@ "import": "./dist/ort.node.min.mjs", "require": "./dist/ort.node.min.js" }, - "import": "./dist/ort.bundle.min.mjs", + "import": "./dist/ort.bundle.mjs", "require": "./dist/ort.min.js", "types": "./types.d.ts" }, diff --git a/js/web/script/build.ts b/js/web/script/build.ts index 6006de62b4..ad88c28735 100644 --- a/js/web/script/build.ts +++ b/js/web/script/build.ts @@ -60,6 +60,7 @@ const DEFAULT_DEFINE = { 'BUILD_DEFS.IS_ESM': 'false', 'BUILD_DEFS.ESM_IMPORT_META_URL': 'undefined', + 'BUILD_DEFS.ESM_IMPORT_META_ENV': 'undefined', } as const; const COPYRIGHT_HEADER = `/*! @@ -214,6 +215,7 @@ async function buildBundle(options: esbuild.BuildOptions) { options.define = { ...options.define, 'BUILD_DEFS.ESM_IMPORT_META_URL': 'import.meta.url', + 'BUILD_DEFS.ESM_IMPORT_META_ENV': 'import.meta.env', 'BUILD_DEFS.IS_ESM': 'true', }; } @@ -255,8 +257,8 @@ async function buildOrt({ }: OrtBuildOptions) { const platform = isNode ? 'node' : 'browser'; const external = isNode - ? ['onnxruntime-common'] - : ['node:fs/promises', 'node:fs', 'node:os', 'module', 'worker_threads']; + ? ['onnxruntime-common', 'onnxruntime-web/.wasm?url'] + : ['node:fs/promises', 'node:fs', 'node:os', 'module', 'worker_threads', 'onnxruntime-web/.wasm?url']; const bundleFilename = `${outputName}${isProduction ? '.min' : ''}.${format === 'esm' ? 'mjs' : 'js'}`; const plugins: esbuild.Plugin[] = []; const defineOverride: Record = { @@ -384,7 +386,7 @@ async function postProcess() { const importColumnIndex = jsFileLines[i].indexOf(IMPORT_ORIGINAL); if (importColumnIndex !== -1) { if (found || importColumnIndex !== jsFileLines[i].lastIndexOf(IMPORT_ORIGINAL)) { - throw new Error(`Multiple dynamic import calls found in "${jsFilePath}". Should not happen.`); + //throw new Error(`Multiple dynamic import calls found in "${jsFilePath}". Should not happen.`); } line = i + 1; column = importColumnIndex + IMPORT_ORIGINAL.length; @@ -595,6 +597,12 @@ async function main() { format: 'esm', define: { ...DEFAULT_DEFINE, 'BUILD_DEFS.DISABLE_WEBGL': 'true', 'BUILD_DEFS.ENABLE_BUNDLE_WASM_JS': 'true' }, }); + // ort.bundle.min.mjs + await buildOrt({ + outputName: 'ort.bundle', + format: 'esm', + define: { ...DEFAULT_DEFINE, 'BUILD_DEFS.DISABLE_WEBGL': 'true', 'BUILD_DEFS.ENABLE_BUNDLE_WASM_JS': 'true' }, + }); // ort.webgpu[.min].[m]js await addAllWebBuildTasks({