Azure / azure-sdk-for-js

This repository is for active development of the Azure SDK for JavaScript (NodeJS & Browser). For consumers of the SDK we recommend visiting our public developer docs at https://docs.microsoft.com/javascript/azure/ or our versioned developer docs at https://azure.github.io/azure-sdk-for-js.
MIT License
2.03k stars 1.19k forks source link

[Expo, React Native, Android, iOS] ReferenceError: Can't find variable: document #30411

Open BubbleTrouble14 opened 1 month ago

BubbleTrouble14 commented 1 month ago

Describe the bug Expo-Example When running the example repo I get the error ( ReferenceError: Can't find variable: document ). Which is caused to not being a polyfill for document.

To Reproduce

  1. Clone example
  2. Install node modules
  3. run npx expo prebuild
  4. run android

Expected behavior Should not cause an error

Screenshots image

jeremymeng commented 1 month ago

Thanks for the report @BubbleTrouble14! We will take a look shortly.

jeremymeng commented 1 month ago

Previously our xml.js for react-native is mapped to the esm version, so fast-xml-parser is used instead of the browser XML document APIs.

https://github.com/Azure/azure-sdk-for-js/blob/3c750ba3cb19f47e50b4572248d00f26e13c5348/sdk/core/core-xml/package.json#L13

However now that we switched to conditional export, it requires a new beta feature from react-native

config.resolver.unstable_enablePackageExports = true;

Also note that another fix to an core-amqp react-native issue hasn't been released yet: https://github.com/Azure/azure-sdk-for-js/issues/30065

One possible workaround is to override transitive dependencies to earlier versions, for example

  "resolutions": {
    "@azure/core-amqp": "4.2.2",
    "@azure/core-rest-pipeline": "1.14.0",
    "@azure/core-util": "1.7.0",
    "@azure/logger": "1.0.4"
  }
BubbleTrouble14 commented 1 month ago

Adding the config.resolver.unstable_enablePackageExports = true; definitely fixed the first issue. I also tried fixing the other issue with the resolutions example. Which didn't seem to quite fix the problem yet though. Getting the error that it cannot read property 'importKey' of undefined. But I assume the new version will be released soon. So I will then just wait. Thanks for the help.

jeremymeng commented 1 month ago

Getting the error that it cannot read property 'importKey' of undefined.

This error indicates that the crypto polyfill doesn't work as expected. It is likely a different issue than #30065. I will keep looking

BubbleTrouble14 commented 1 month ago

I've been diving a little deeper myself. It seems like none of the polyfils yet support subtle.importKey for HMAC in react native. Also react-native-quick-crypto doesn't support it either. I also changed buffer to use @craftzdog/react-native-buffer and crypto to react-native-quick-crypto. And removed the node-libs-react-native so one only adds the polyfills one needs. Also atob and btoa is supported natively in react-native > 0.74.

Setting globals:

import { Buffer } from "@craftzdog/react-native-buffer";
import QuickCrypto from "react-native-quick-crypto";
import process from "process/browser";
import os from "os-browserify/browser";
import path from "path-browserify";

// @ts-expect-error copyBytesFrom and poolSizets are missing from react-native-buffer
global.Buffer = Buffer;

// @ts-expect-error subtle isn't fully implemented and Cryptokey is missing
global.crypto = QuickCrypto;

// @ts-expect-error
global.process = process;
global.os = os;
global.path = path;

metro.config.js

// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require("expo/metro-config");

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

config.resolver = {
  ...config.resolver,
  unstable_enablePackageExports: true,
  extraNodeModules: {
    ...config.resolver.extraNodeModules,
    crypto: require.resolve("react-native-quick-crypto"),
    buffer: require.resolve("@craftzdog/react-native-buffer"),
    os: require.resolve("os-browserify/browser"),
    process: require.resolve("process/browser"),
    path: require.resolve("path-browserify"),
  },
};

module.exports = config;
jeremymeng commented 1 month ago

@BubbleTrouble14 I've been struggling with expo projects and android emulators recently. Some code worked in one project but not the other and I have no clues why yet. In one working test project I am able to send and receive Service Bus messages using isomorphic-webcrypto for crypto polyfill

/* @type {import('expo/metro-config').MetroConfig} / const config = getDefaultConfig(__dirname);

config.resolver = { ...config.resolver, unstable_enablePackageExports: true, extraNodeModules: { ...config.resolver.extraNodeModules, crypto: require.resolve("isomorphic-webcrypto/src/react-native"), buffer: require.resolve("buffer"), os: require.resolve("os-browserify/browser"), process: require.resolve("process/browser"), path: require.resolve("path-browserify"), }, };

module.exports = config;


* before SDK code
```js
import "react-native-get-random-values";
const getRandomValues = global.crypto.getRandomValues;
import * as crypto from "crypto";
globalThis.crypto = crypto;
globalThis.crypto.getRandomValues = getRandomValues;

I am still working through my test projects and hope to come out with one that works reliably.

BubbleTrouble14 commented 1 month ago

@jeremymeng I was able to get it running now with expo myself. There are problems with using isomorphic-webcrypto atm with expo/react-tive, so I ended up getting it working with the fork. I had to then also add in the util npm package as well. Ideally using react-native-quick-crypto would probably be the best if it had the HMAC subtle added. As one would then be able to have a single package for crypto with a secure random. Here is my current setup:

{
  "name": "test-service-bus",
  "version": "1.0.0",
  "main": "expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "@azure/event-hubs": "^5.12.0",
    "@azure/service-bus": "^7.9.5",
    "@craftzdog/react-native-buffer": "^6.0.5",
    "expo": "~51.0.22",
    "expo-status-bar": "~1.12.1",
    "isomorphic-webcrypto": "https://github.com/reyamir/isomorphic-webcrypto.git",
    "os-browserify": "^0.3.0",
    "path-browserify": "^1.0.1",
    "process": "^0.11.10",
    "react": "18.2.0",
    "react-native": "0.74.3",
    "react-native-get-random-values": "^1.11.0",
    "util": "^0.12.5"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@babel/plugin-proposal-async-generator-functions": "^7.20.7",
    "@types/react": "~18.2.45",
    "babel-plugin-inline-dotenv": "^1.7.0",
    "typescript": "^5.1.3"
  },
  "private": true
}
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require("expo/metro-config");

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

config.resolver = {
  ...config.resolver,
  unstable_enablePackageExports: true,
  extraNodeModules: {
    ...config.resolver.extraNodeModules,
    crypto: require.resolve("isomorphic-webcrypto/src/react-native"),
    buffer: require.resolve("@craftzdog/react-native-buffer"),
    os: require.resolve("os-browserify/browser"),
    process: require.resolve("process/browser"),
    path: require.resolve("path-browserify"),
  },
};

module.exports = config;
import { Buffer } from "@craftzdog/react-native-buffer";
import "react-native-get-random-values";
const getRandomValues = global.crypto.getRandomValues;
import * as crypto from "crypto";

// @ts-expect-error copyBytesFrom and poolSizets are missing from react-native-buffer
global.Buffer = Buffer;

// @ts-expect-error
global.crypto = crypto;
global.crypto.getRandomValues = getRandomValues;

if (typeof process.nextTick == "undefined") {
  process.nextTick = setImmediate;
}
jeremymeng commented 1 month ago

We just published newer version of affected packages so that the beta feature unstable_enablePackageExports: true, is no longer required.