tauri-apps / tauri

Build smaller, faster, and more secure desktop and mobile applications with a web frontend.
https://tauri.app
Apache License 2.0
84.02k stars 2.52k forks source link

[bug] `window.__TAURI_METADATA__` is undefined when importing "@tauri-apps/api" in an application that runs in a web browser #3509

Closed kevin-legion closed 2 years ago

kevin-legion commented 2 years ago

Describe the bug

When importing "@tauri-apps/api" in an application that runs on both Tauri and in web browsers, the application crashes in the latter environment with the following error: Uncaught TypeError: window.__TAURI_METADATA__ is undefined.

Reproduction

Add `import "@tauri-apps/api" at the top level of an application and run said application in a web browser.

Expected behavior

"@tauri-apps/api" should probably be importable in web browsers.

Platform and versions

# cargo.toml
tauri = { version = "1.0.0-rc.3", features = ["api-all"] }
// package.json
"@tauri-apps/api": "^1.0.0-rc.1",

Stack trace

No response

Additional context

It seems it happened with this commit: https://github.com/tauri-apps/tauri/commit/f5109e0c962e3d25404995194968bade1be33b16 as the appWindow constant now relies on window.__TAURI_METADATA__: https://github.com/tauri-apps/tauri/blob/dev/tooling/api/src/window.ts#L1160 which wasn't the case before: https://github.com/tauri-apps/tauri/blob/65ad5b5ef923bf1e6b6f078d794d071a04fcdf57/tooling/api/src/window.ts#L1143

Possible temporary workarounds

Possible fixes

const appWindow = new WebviewWindow(
  window.__TAURI_METADATA__?.__currentWindow.label,
  {
    // @ts-expect-error
    skip: true
  }
)
FabianLars commented 2 years ago

This is not a bug, tauri's scripts are not injected into browser windows (we don't have control over the browser at all). So even if appWindow would be fixed, everything else would still be broken.

See here for a possible workaround https://github.com/tauri-apps/tauri/issues/2849#issuecomment-970435644, although tbh i don't know if that's compatible with the rc versions right now.

Generally you can indeed do some if conditions like this, but if (window.__TAURI__) {} should be enough. We won't wrap all our APIs in that of course, but it's something the user can do if they use the same frontend for their app and a website (some also combine this with conditional compilation but i can't give an example for this).

JonasKruckenberg commented 2 years ago

Hey @FabianLars you forgot to plug https://tauri.studio/docs/api/js/modules/mocks 😁

@kevin-legion You can refer to the "Mocking Tauri APIs for testing" guide. The same techniques can be applied to other environments where Tauri APIs aren't available (i.e. a normal browser environment)

https://tauri.studio/docs/testing/mocking

FabianLars commented 2 years ago

Hey @FabianLars you forgot to plug https://tauri.studio/docs/api/js/modules/mocks 😁

I only do paid advertisements πŸ˜‚ Jokes aside you're absolutely correct and ngl I'm almost ashamed that i didn't play around with this myself yet.

JonasKruckenberg commented 2 years ago

I only do paid advertisements πŸ˜‚ Jokes aside you're absolutely correct and ngl I'm almost ashamed that i didn't play around with this myself yet.

Hahaha no worries, but it's pretty neat!

Anyway one thing to note @kevin-legion is that you'd need to import the @tauri-apps/api/window module after you mocked the windows. This means you mock windows in your entry file and make sure to ever only reference the window module in later ones. Or you use dynamic imports with the window module.

kevin-legion commented 2 years ago

Thank you @JonasKruckenberg and @FabianLars for the super quick response πŸ™‚

Sorry it wasn't clear in the original issue, but my point was that "@tauri-apps/api" should probably be importable in a web browser not usable. In the case where the application is a hermit app for example, it's pretty common to import the tauri module and to use it in a if (window.__TAURI_METADATA__) statement:

if (window.__TAURI_METADATA__) {
  invoke("a_rust_function", { x, y });
} else {
  doSomethingElse(x, y);
}

That way the application can easily access the whole js api exposed by Tauri and run (with some polyfill/fallback) in the browser. Our auth module for instance uses invoke in Tauri and performs the auth requests directly when in browser, that allows us to have 2 slightly different workflows one being optimized for the desktop, the other for the browser.

Referencing window.__TAURI_METADATA__ breaks the import in web browsers and makes the code a bit more cumbersome to write as there is no alternatives (https://github.com/tauri-apps/tauri-invoke-http for instance only exposes "invoke" and still requires an instance of the Tauri application to run locally or remotely which is not optimal).

As for the mocks, it could indeed fix the problem but it really sounds like a hack to me ^^.

JonasKruckenberg commented 2 years ago

Referencing window.__TAURI_METADATA__ breaks the import in web browsers and makes the code a bit more cumbersome to write as there is no alternatives (https://github.com/tauri-apps/tauri-invoke-http for instance only exposes "invoke" and still requires an instance of the Tauri application to run locally or remotely which is not optimal).

Ahh yes, we are aware of that, it is caused by the appWindow constant as you correctly identified. We're kinda stuck with it for support reasons until v2. Two things that consistently help though is importing by submodule (e.g. import import * as app from @tauri-apps/api/app instead of import {app} from @tauri-apps/api) and importing the window module dynamically.

It is only the window module that causes problems here.

kevin-legion commented 2 years ago

You're absolutely correct, it's another workaround to add to the list πŸ˜„

Until v2, and if the WebviewWindow class accepts a null/undefined value then new WebviewWindow(window.__TAURI_METADATA__?.__currentWindow.label) would work well in Tauri and in the browser (with the same behavior as before rc-1 which is probably acceptable?)

FabianLars commented 2 years ago

I think https://github.com/tauri-apps/tauri/pull/3572 should fix this too