mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-25 18:51:35 +00:00
62 lines
2.4 KiB
Rust
62 lines
2.4 KiB
Rust
//! Runtime detection of whether we're in node.js or a browser.
|
|
|
|
use wasm_bindgen::prelude::*;
|
|
use js_sys::{Array, Function};
|
|
|
|
#[wasm_bindgen]
|
|
extern {
|
|
type This;
|
|
#[wasm_bindgen(method, getter, structural, js_name = self)]
|
|
fn self_(me: &This) -> JsValue;
|
|
}
|
|
|
|
/// Returns whether it's likely we're executing in a browser environment, as
|
|
/// opposed to node.js.
|
|
pub fn is_browser() -> bool {
|
|
// This is a bit tricky to define. The basic crux of this is that we want to
|
|
// test if the `self` identifier is defined. That is defined in browsers
|
|
// (and web workers!) but not in Node. To that end you might expect:
|
|
//
|
|
// #[wasm_bindgen]
|
|
// extern {
|
|
// #[wasm_bindgen(js_name = self)]
|
|
// static SELF: JsValue;
|
|
// }
|
|
//
|
|
// *SELF != JsValue::undefined()
|
|
//
|
|
// this currently, however, throws a "not defined" error in JS because the
|
|
// generated function looks like `function() { return self; }` which throws
|
|
// an error in Node because `self` isn't defined.
|
|
//
|
|
// To work around this limitation we instead lookup the value of `self`
|
|
// through the `this` object, basically generating `this.self`.
|
|
//
|
|
// Unfortunately that's also hard to do! In ESM modes the top-level `this`
|
|
// object is undefined, meaning that we can't just generate a function that
|
|
// returns `this.self` as it'll throw "can't access field `self` of
|
|
// `undefined`" whenever ESMs are being used.
|
|
//
|
|
// So finally we reach the current implementation. According to
|
|
// StackOverflow you can access the global object via:
|
|
//
|
|
// const global = Function('return this')();
|
|
//
|
|
// I think that's because the manufactured function isn't in "strict" mode.
|
|
// It also turns out that non-strict functions will ignore `undefined`
|
|
// values for `this` when using the `apply` function. Add it all up, and you
|
|
// get the below code:
|
|
//
|
|
// * Manufacture a function
|
|
// * Call `apply` where we specify `this` but the function ignores it
|
|
// * Once we have `this`, use a structural getter to get the value of `self`
|
|
// * Last but not least, test whether `self` is defined or not.
|
|
//
|
|
// Whew!
|
|
let this = Function::new_no_args("return this")
|
|
.apply(&JsValue::undefined(), &Array::new())
|
|
.unwrap();
|
|
assert!(this != JsValue::undefined());
|
|
This::from(this).self_() != JsValue::undefined()
|
|
}
|