powersync-ja / powersync-js

SDK that enables local-first and real-time reactive apps with embedded SQLite for JavaScript clients, including React Native and Web
https://www.powersync.com
Apache License 2.0
253 stars 13 forks source link

Support for react-native-web #249

Open jasonlewicki opened 1 month ago

jasonlewicki commented 1 month ago

I'm having trouble getting powersync installed and working with an expo universal app (react-native and react-native-web)

Error running powersync/react-native on web: Screenshot 2024-07-26 at 2 23 27 PM

text version of the link: https://github.com/expo/fyi/blob/main/fb-batched-bridge-config-web.md

jasonlewicki commented 1 month ago

If i use @powersync/web, i get the error: Uncaught SyntaxError: Cannot use 'import.meta' outside a module (at index.bundle?platform=web&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.routerRoot=src%2Fapp:258371:114)

which comes from here:

class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementation.WebStreamingSyncImplementation {
    constructor(options) {
      super(options);
      /**
       * Configure or connect to the shared sync worker.
       * This worker will manage all syncing operations remotely.
       */
      const syncWorker = new SharedWorker(new URL('../../worker/sync/SharedSyncImplementation.worker.js', import.meta.url), {
        /* @vite-ignore */
        name: `shared-sync-${this.webOptions.identifier}`,
        type: 'module'
      });
jasonlewicki commented 1 month ago

Additional info, issue raised on expo: https://github.com/expo/expo/issues/21099 https://github.com/expo/expo/issues/30323

jasonlewicki commented 1 month ago

here's a git repo reproducing the error:

https://github.com/fig-wealth/powersync-react-native-web

npx expo install npx expo start w to launch web

jasonlewicki commented 1 month ago

Metro does not support esm, probably wont until at least next year: https://github.com/facebook/metro/issues/916 https://github.com/facebook/metro/issues/886

any chance of making a commonjs transpile of powersync or changing the import.meta.url instances?

stevensJourney commented 1 month ago

Thanks for the detailed issue and example repository! We'll use your example to investigate the requirements - I'll keep you posted when we have an update.

jasonlewicki commented 1 month ago

I was missing a commit, i've since added it to the repo. apologies

jasonlewicki commented 1 month ago

To give you all an update, and potentially reduce the problem set, I've created another branch that conditionally incorporates @powersync/react-native

https://github.com/fig-wealth/powersync-react-native-web/tree/with-react-native

As far as I can tell, it works on iOS (at least saying the auth is wrong... [I have the self hosted demo running]) Screenshot 2024-07-29 at 2 01 35 PM

stevensJourney commented 1 month ago

We've done an initial investigation where we identified some hurdles and potential solutions for providing React Native web support. We have managed to get an example POC running using a UMD bundled version of the Web SDK. Support may be available in the near future.

jasonlewicki commented 1 month ago

That's great news. I can't wait 👍

jasonlewicki commented 1 month ago

The dev build has been working great so far. Thanks for all the hard work!

jasonlewicki commented 1 week ago

Hey team. We're trying to build for MVP over here: npx expo export -p w --clear

This is the error we are getting on load: Failed to fetch a worker script. Pretty sure it's powersync.

Here's a screen grab of the dist directory:

Screenshot 2024-08-30 at 10 57 41 AM

I'll try to get more information shortly.

jasonlewicki commented 1 week ago

Ok we're in business.

i have a git action:

# Copy public powersync files to dist directory
- name: Copy public files to dist directory
  run: |
    # Define the source and destination directories
    SOURCE_DIR="apps/client/dist"
    DEST_DIR="apps/client/dist/_expo/static/js/web"

    # Create the destination directory if it doesn't exist
    mkdir -p $DEST_DIR

    # Copy all files from the source to the destination
    cp -r $SOURCE_DIR/* $DEST_DIR

Moving the files into the _expo/web/js directory fixed it! This is probably just an artifact of the dev build.

ducpt-bili commented 1 week ago

hi @jasonlewicki , so does the powersync work on expo web now? Do we need to add more config like git action you paste above? Thanks

jasonlewicki commented 1 week ago

hi @jasonlewicki , so does the powersync work on expo web now? Do we need to add more config like git action you paste above? Thanks

@ducpt-bili Hey, yeah, until the team merges into main, there's a list of things you need to do to get it working:

install these:

"@powersync/attachments": "0.0.0-dev-20240812065227",
"@powersync/common": "0.0.0-dev-20240812065227",
"@powersync/kysely-driver": "0.0.0-dev-20240812065227",
"@powersync/react": "0.0.0-dev-20240812065227",
"@powersync/react-native": "0.0.0-dev-20240812065227",
"@powersync/web": "0.0.0-dev-20240812065227",

Then, add this script:

// This file is required for the dev build of powersync to work properly.
// It copies the necessary files from the node_modules/@powersync/web/dist directory to the public directory.
// Gets around the package being built with esm modules and not being able to be resolved by metro.
// This is a temporary solution until the package is updated.

const fs = require('fs');
const path = require('path');
// Source directory
const sourceDir = path.join(
  __dirname,
  'node_modules',
  '@powersync',
  'web',
  'dist',
);
const destDir = path.join(__dirname, 'public');
function copyRecursiveSync(src, dest) {
  if (fs.existsSync(src) && fs.statSync(src).isDirectory()) {
    // Create the destination directory if it doesn't exist
    if (!fs.existsSync(dest)) {
      fs.mkdirSync(dest, { recursive: true });
    }
    const files = fs.readdirSync(src);
    // Copy each file/directory
    files.forEach((file) => {
      const srcFile = path.join(src, file);
      const destFile = path.join(dest, file);
      if (fs.statSync(srcFile).isDirectory()) {
        copyRecursiveSync(srcFile, destFile);
      } else {
        fs.copyFileSync(srcFile, destFile);
      }
    });
  } else {
    // eslint-disable-next-line no-console
    console.error(
      `Source directory ${src} does not exist or is not a directory.`,
    );
  }
}
// Execute the copy
copyRecursiveSync(sourceDir, destDir);
// eslint-disable-next-line no-console
console.log(`Files copied from ${sourceDir} to ${destDir} successfully.`);

and run it.

then modify your metro config to add this:

const customResolveRequest = (context, moduleName, platform) => {
  if (platform === 'web') {
    if (
      ['react-native-prompt-android', '@powersync/react-native'].includes(
        moduleName,
      )
    ) {
      return {
        type: 'empty',
      };
    }
    const mapping = {
      'react-native': 'react-native-web',
      '@powersync/web': '@powersync/web/dist/index.umd.js',
      kysely: 'kysely/dist/cjs/index.js',
    };
    if (mapping[moduleName]) {
      // eslint-disable-next-line no-console
      console.log('remapping', moduleName);
      return context.resolveRequest(context, mapping[moduleName], platform);
    }
  } else if (['@powersync/web'].includes(moduleName)) {
    return {
      type: 'empty',
    };
  }

  // Ensure you call the default resolver.
  return context.resolveRequest(context, moduleName, platform);
};

the git action above is for production runs, where you copy/move the dev files into the proper dist directory

ducpt-bili commented 1 week ago

hi @jasonlewicki , thank you for your hard work.