Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[js/web] fix package export for bundlers #23257

Merged
merged 16 commits into from
Jan 9, 2025
Merged

[js/web] fix package export for bundlers #23257

merged 16 commits into from
Jan 9, 2025

Conversation

fs-eire
Copy link
Contributor

@fs-eire fs-eire commented Jan 6, 2025

Description

This PR tries to fix #22615. (see detailed description in the issue)

A perfect solution would be too difficult to make, because there are a huge number of combinations of usage scenarios, including combinations of development framework, bundler, dev/prod mode, and so on.

This PR is using the following approach:

  • Introduce a new type of end to end test: export test. This type of tests are complete web apps that use popular web development frameworks, and the tests are using puppeteer to run the apps and check if the apps can run without error.
    • added one nextjs based web app and one vite based web app.
  • In the test, perform the following test steps:
    • npm install for packages built locally
    • npm run dev to start dev server and use puppeteer to launch the browser to test
    • npm run build && npm run start to test prod build and use puppeteer to launch the browser to test
  • Make changes to ort-web, including:
    • special handling on Webpack's behavior of rewriting import.meta.url to a file:// string
    • revise build definitions
    • fix wasm URL for proxy, if used in a bundled build

@fs-eire fs-eire marked this pull request as ready for review January 6, 2025 09:47
@fs-eire fs-eire requested review from Copilot and guschmue January 6, 2025 09:48
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 30 out of 45 changed files in this pull request and generated 5 comments.

Files not reviewed (15)
  • js/web/test/e2e/exports/testcases/nextjs-default/.gitignore: Language not supported
  • js/web/test/e2e/exports/testcases/nextjs-default/app/globals.css: Language not supported
  • js/web/test/e2e/exports/testcases/nextjs-default/app/page.module.css: Language not supported
  • js/web/test/e2e/exports/testcases/nextjs-default/jsconfig.json: Language not supported
  • js/web/test/e2e/exports/testcases/nextjs-default/package.json: Language not supported
  • js/web/test/e2e/exports/testcases/nextjs-default/components/onnx-helper.js: Evaluated as low risk
  • js/web/test/e2e/exports/testcases/nextjs-default/components/onnx-test-bar.js: Evaluated as low risk
  • js/web/test/e2e/exports/testcases/nextjs-default/app/page.js: Evaluated as low risk
  • js/web/lib/backend-wasm.ts: Evaluated as low risk
  • js/web/lib/wasm/wasm-factory.ts: Evaluated as low risk
  • js/web/test/e2e/exports/README.md: Evaluated as low risk
  • js/web/lib/wasm/proxy-wrapper.ts: Evaluated as low risk
  • js/web/script/build.ts: Evaluated as low risk
  • js/web/test/e2e/exports/testcases/nextjs-default/app/layout.js: Evaluated as low risk
  • js/web/test/e2e/exports/testcases/nextjs-default/next.config.mjs: Evaluated as low risk
Comments suppressed due to low confidence (5)

js/web/test/e2e/exports/testcases/nextjs-default.md:15

  • [nitpick] The term 'App Router' should be 'app router' to match the casing used in the rest of the document.
√ Would you like to use App Router? (recommended) ... No

js/web/test/e2e/exports/main.js:30

  • Ensure that the ANSI escape codes for colored output are intentional and correctly handled in environments that might not support them.
await runDevTest('vite-default', '\x1b[32m➜\x1b[39m  \x1b[1mLocal\x1b[22m:', 5173);

js/web/test/e2e/exports/main.js:31

  • Ensure that the ANSI escape codes for colored output are intentional and correctly handled in environments that might not support them.
await runProdTest('vite-default', '\x1b[32m➜\x1b[39m  \x1b[1mLocal\x1b[22m:', 4173);

js/web/lib/wasm/wasm-utils-import.ts:1

  • The preload function should have an explicit return type defined for better readability and maintainability.
preload = async (absoluteUrl: string): Promise<string> => {

js/web/lib/wasm/wasm-utils-import.ts:12

  • [nitpick] The error handling in the normalizeUrl function should provide more context to help with debugging.
normalizeUrl = (filename: string, prefixOverride?: string) => {

js/web/test/e2e/exports/testcases/nextjs-default.md Outdated Show resolved Hide resolved
js/web/test/e2e/exports/test.js Show resolved Hide resolved
js/web/test/e2e/exports/test.js Show resolved Hide resolved
js/web/test/e2e/exports/test.js Show resolved Hide resolved
js/web/test/e2e/exports/test.js Show resolved Hide resolved
@fs-eire fs-eire merged commit 0627a6c into main Jan 9, 2025
61 of 63 checks passed
@fs-eire fs-eire deleted the fs-eire/web-export branch January 9, 2025 19:01
guschmue pushed a commit that referenced this pull request Jan 12, 2025
### Description
<!-- Describe your changes. -->

This PR tries to fix #22615. (see detailed description in the issue)

A perfect solution would be too difficult to make, because there are a
huge number of combinations of usage scenarios, including combinations
of development framework, bundler, dev/prod mode, and so on.

This PR is using the following approach:
- Introduce a new type of end to end test: export test. This type of
tests are complete web apps that use popular web development frameworks,
and the tests are using puppeteer to run the apps and check if the apps
can run without error.
  - added one nextjs based web app and one vite based web app.
- In the test, perform the following test steps:
  - `npm install` for packages built locally
- `npm run dev` to start dev server and use puppeteer to launch the
browser to test
- `npm run build && npm run start` to test prod build and use puppeteer
to launch the browser to test
- Make changes to ort-web, including:
- special handling on Webpack's behavior of rewriting `import.meta.url`
to a `file://` string
  - revise build definitions
  - fix wasm URL for proxy, if used in a bundled build
@xenova
Copy link

xenova commented Jan 15, 2025

I upgraded but ran into an issue where the ort-wasm-simd-threaded.jsep.mjs file was not included in the bundle.

huggingface/transformers.js#1150

@fs-eire
Copy link
Contributor Author

fs-eire commented Jan 15, 2025

I upgraded but ran into an issue where the ort-wasm-simd-threaded.jsep.mjs file was not included in the bundle.

huggingface/transformers.js#1150

Did you override env.wasm.wasmPaths in transformer.js?

The expected build-time behavior is, if importing onnxruntime-web as ESM, the file ort.bundle.min.js (which includes ort-wasm-simd-threaded.jsep.mjs) is picked for processing. all the content will be a part of dist/transformers.js

The expected run-time behavior is:

  • If specified env.wasm.wasmPaths:

    1. If specified as a string (URL prefix): it will always try to dynamically load {PREFIX}/ort-wasm-simd-threaded.jsep.mjs and {PREFIX}/ort-wasm-simd-threaded.jsep.wasm
    2. If specified as an object { mjs: "{MJS_URL}" }: will dynamically load {MJS_URL} and infer URL of wasm from {MJS_URL}
    3. If specified as an object { wasm: "{WASM_URL}" }: will use bundled mjs and will load wasm from {WASM_URL}
    4. If specified as an object { mjs: "{MJS_URL}", wasm: "{WASM_URL}" }: will load mjs and wasm using the specified URL
  • If not specified env.wasm.wasmPaths: will use the bundled mjs and use import.meta.url to infer URL of wasm

I think transformer.js explicitly set env.wasm.wasmPaths to a CDN prefix so it's the case (1). It should be able to make it not depend on this file if using (3).

@fs-eire
Copy link
Contributor Author

fs-eire commented Jan 15, 2025

@xenova BTW, transformerjs 3.3.1 failed to build on windows because rm and cp is not available

@xenova
Copy link

xenova commented Jan 15, 2025

@xenova BTW, transformerjs 3.3.1 failed to build on windows because rm and cp is not available

Thanks for spotting! I had to publish a hot-fix since there were issues when running in-browser, so I'll revert this soon and integrate a more permanent fix (hopefully after onnxruntime-web is fixed)?

@fs-eire
Copy link
Contributor Author

fs-eire commented Jan 15, 2025

@xenova BTW, transformerjs 3.3.1 failed to build on windows because rm and cp is not available

Thanks for spotting! I had to publish a hot-fix since there were issues when running in-browser, so I'll revert this soon and integrate a more permanent fix (hopefully after onnxruntime-web is fixed)?

I assume latest dev package should have no problem. Please let me know if you observed any unexpected behavior (both build time and run time)

@xenova
Copy link

xenova commented Jan 15, 2025

The issue (which also happens with https://www.npmjs.com/package/onnxruntime-web/v/1.21.0-dev.20250114-228dd16893) appears to be that webpack is unable to identify that the .mjs file needs to be linked too, so when building transformers.js, that file isn't copied correctly.

Similarly, the bundle file is copied, even though it's not necessary since transformers.js includes it.

@fs-eire
Copy link
Contributor Author

fs-eire commented Jan 15, 2025

The issue (which also happens with https://www.npmjs.com/package/onnxruntime-web/v/1.21.0-dev.20250114-228dd16893) appears to be that webpack is unable to identify that the .mjs file needs to be linked too, so when building transformers.js, that file isn't copied correctly.

Similarly, the bundle file is copied, even though it's not necessary since transformers.js includes it.

I think this is expected. As I explained above, when webpack builds transformer.js, it finds import * from "onnxruntime-web" and this resolves to ort.bundle.min.mjs. "ort.bundle.min.mjs" is a file that already contains the content of ort-wasm-simd-threaded.jsep.mjs, so it does not depends on file ort-wasm-simd-threaded.jsep.mjs. It is expected that webpack does not copy this file.

This should work for bundlers if env.wasm.wasmPaths is not set, or it's set to an object which contains only "wasm" property (as described above for case (3))

if env.wasm.wasmPaths is set to a path prefix or an object containing "mjs" property (case 1, 2, 4), it means user want to explicitly set the path of the ort-wasm-simd-threaded.jsep.mjs file. ort.bundle.min.mjs will ignore its bundled mjs and load from the specific path instead.

@xenova
Copy link

xenova commented Jan 15, 2025

The issue (which also happens with https://www.npmjs.com/package/onnxruntime-web/v/1.21.0-dev.20250114-228dd16893) appears to be that webpack is unable to identify that the .mjs file needs to be linked too, so when building transformers.js, that file isn't copied correctly.
Similarly, the bundle file is copied, even though it's not necessary since transformers.js includes it.

I think this is expected. As I explained above, when webpack builds transformer.js, it finds import * from "onnxruntime-web" and this resolves to ort.bundle.min.mjs. "ort.bundle.min.mjs" is a file that already contains the content of ort-wasm-simd-threaded.jsep.mjs, so it does not depends on file ort-wasm-simd-threaded.jsep.mjs. It is expected that webpack does not copy this file.

This should work for bundlers if env.wasm.wasmPaths is not set, or it's set to an object which contains only "wasm" property (as described above for case (3))

if env.wasm.wasmPaths is set to a path prefix or an object containing "mjs" property (case 1, 2, 4), it means user want to explicitly set the path of the ort-wasm-simd-threaded.jsep.mjs file. ort.bundle.min.mjs will ignore its bundled mjs and load from the specific path instead.

Ah I see - thanks for that explanation. So, do you have any recommendations for how to improve this in Transformers.js? If possible, I'd like to bundle everything non-wasm related into the transformers.js file. If you know how to do this, would you consider opening a PR? Otherwise, I can give it a go.

@fs-eire
Copy link
Contributor Author

fs-eire commented Jan 15, 2025

The issue (which also happens with https://www.npmjs.com/package/onnxruntime-web/v/1.21.0-dev.20250114-228dd16893) appears to be that webpack is unable to identify that the .mjs file needs to be linked too, so when building transformers.js, that file isn't copied correctly.
Similarly, the bundle file is copied, even though it's not necessary since transformers.js includes it.

I think this is expected. As I explained above, when webpack builds transformer.js, it finds import * from "onnxruntime-web" and this resolves to ort.bundle.min.mjs. "ort.bundle.min.mjs" is a file that already contains the content of ort-wasm-simd-threaded.jsep.mjs, so it does not depends on file ort-wasm-simd-threaded.jsep.mjs. It is expected that webpack does not copy this file.
This should work for bundlers if env.wasm.wasmPaths is not set, or it's set to an object which contains only "wasm" property (as described above for case (3))
if env.wasm.wasmPaths is set to a path prefix or an object containing "mjs" property (case 1, 2, 4), it means user want to explicitly set the path of the ort-wasm-simd-threaded.jsep.mjs file. ort.bundle.min.mjs will ignore its bundled mjs and load from the specific path instead.

Ah I see - thanks for that explanation. So, do you have any recommendations for how to improve this in Transformers.js? If possible, I'd like to bundle everything non-wasm related into the transformers.js file. If you know how to do this, would you consider opening a PR? Otherwise, I can give it a go.

The major possible use cases are:

  1. import from CDN directly
    This may happen in some examples that uses transformers.js directly in a <script> tag.
  2. import in a webpack based web app framework
    For example, a typical Next.js app
  3. import in a rollup/esbuild based web app framework
    For example, a typical Vite/Nuxt app
  4. import inside a service worker

Could you find me some tranformersjs examples links for those use case, so that I can start work on them?

@xenova
Copy link

xenova commented Jan 16, 2025

Sure, here's a repo containing our example projects: https://github.com/huggingface/transformers.js-examples/tree/migrate-examples (on the migrate-examples branch) since I'm in the progress of moving some repos from the transformers.js repo to there.

Some examples:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Web] Feature Request: workaround for bundlers when using onnxruntime-web
3 participants