examples icon indicating copy to clipboard operation
examples copied to clipboard

Expo yarn workspaces setup doesn't work for web

Open tuncaulubilge opened this issue 3 years ago • 10 comments

Describe the bug The support for expo web in with-yarn-workspaces example is limited. Noticed two main issues:

  • Updates in the shared package is not reflected in the web builds (even with a manual reload). To apply any change in the shared package, we need to restart the whole expo app.
  • Babel is not configured out of the box for the webpack config, so using any react-native components inside the shared package causes issues. This second issue can be remedied by dangerouslyAddModulePathsToTranspile config inside webpack.config.js (see https://www.npmjs.com/package/@expo/webpack-config/v/0.11.4#include-modules)

To Reproduce

  1. Install expo with template with-yarn-workspaces
  2. Run expo web inside one of the apps
  3. Make a change in one of the packages inside packages/
  4. Observe the change not reflected.
  5. Kill expo and restart to observe the change.

Expected behavior Expo web should work just like the expo android and ios on the monorepo, where any change gets reflected without the need to kill expo

Desktop (please complete the following information):

  • OS: macOS Catalina 10.15.7
  • Browser Chrome v89
  • Expo Version 40 (latest) with a fresh installation using the template with-yarn-workspaces

tuncaulubilge avatar Apr 13 '21 14:04 tuncaulubilge

Hi there! Thanks so much for this. I think there are a couple things here we can improve for expo yarn workspaces.

While we fix this stuff - can you try pasting this webpack config (in your app directory e.g first-app/webpack.config.js) and let me know if it works for you?

The config.resolve.symlinks = true seems to get the reload behaviour you are looking for, I suspect you've figured out the rest already

const createExpoWebpackConfigAsync = require("@expo/webpack-config");

// Expo CLI will await this method so you can optionally return a promise.
module.exports = async function (env, argv) {
  const config = await createExpoWebpackConfigAsync({
    ...env,
    babel: {
      dangerouslyAddModulePathsToTranspile: [
        "first-package",
        "second-package",
      ]
    }
  }, argv);

  config.resolve.symlinks = true;
  
  return config;
};

ajsmth avatar Apr 13 '21 19:04 ajsmth

Hi Andy, thanks for the quick reply.

What if I need those symlinked packages to be transpiled as well? (which is probably the case for most monorepos). I'm building a shared component package to be used in multiple expo apps.

Transpilation of the shared package works with dangerouslyAddModulePathsToTranspile configuration, but once I add the symlink config, I get You may need an appropriate loader to handle this file type, error again. I guess I can run a separate babel watcher in my component package and re-transpile it whenever there is a change, but that kinda defeats the purpose of a monorepo. Do you know any suggestions on how to resolve this?

tuncaulubilge avatar Apr 14 '21 09:04 tuncaulubilge

Hello again - thanks for bringing this up! It took some digging but you're absolutely right, this is not quite the right configuration. As a workaround, I believe you can specify the symlinked package folder name instead of whatever alias you are using for your package:

const createExpoWebpackConfigAsync = require("@expo/webpack-config");

// Expo CLI will await this method so you can optionally return a promise.
module.exports = async function (env, argv) {
  const config = await createExpoWebpackConfigAsync(
    {
      ...env,
      babel: {
        dangerouslyAddModulePathsToTranspile: [
          // "@yew/first-package", // remove this package alias 
          "first-package" // <- points to the actual folder packages/first-package
        ],
      },
    },
    argv
  );

  config.resolve.symlinks = true;

  return config;
};

In your apps you should still import these under their configured package names like this:

import { StatusBar } from "expo-status-bar";
import React from "react";
import { StyleSheet, Text, View } from "react-native";

// import using configured package name:
import MyCoolView from "@yew/first-package";

export default function App() {
  return (
    <View style={styles.container}>
      <Text>Hello from the first application</Text>
      <MyCoolView text="Hi there" />
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

I'm still looking around to see if there's a better option, but I hope this works for you for now. Also, let me know if that makes sense!

ajsmth avatar Apr 14 '21 18:04 ajsmth

That worked, thanks Andy! This is how my webpack.config.js looks like now:

const path = require("path");
const fs = require("fs");
const createExpoWebpackConfigAsync = require("@expo/webpack-config");

const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);

module.exports = async function (env, argv) {
  const config = await createExpoWebpackConfigAsync(
    {
      ...env,
      babel: {
        dangerouslyAddModulePathsToTranspile: [
          // Ensure the shared packages are transpiled.
          resolveApp("../../packages/core"),
        ],
      },
    },
    argv
  );
  config.resolve.symlinks = true;

  return config;
};

tuncaulubilge avatar Apr 15 '21 09:04 tuncaulubilge

Awesome, glad to hear! Thanks for bringing this up, we'll try and iron this out for others who might want to use this example repo in the future!

ajsmth avatar Apr 15 '21 17:04 ajsmth

Following up on this - as of v1.5.1 you can use the webpack config export from expo-yarn-workspaces to take care of this configuration for you:

https://github.com/expo/expo/tree/master/packages/expo-yarn-workspaces#usage-with-expo-web

ajsmth avatar Apr 20 '21 17:04 ajsmth

Seems like running yarn on windows doesn't work:

C:\Users\eugen\Coding\Webdev\Learning\rn-yarn-workspaces>yarn
yarn install v1.22.11
[1/4] Resolving packages...
success Already up-to-date.
$ expo-yarn-workspaces check-workspace-dependencies && yarn workspaces run build
✅  Verified that all workspace dependencies are symlinked.
yarn workspaces v1.22.11

> mobile
yarn run v1.22.11
$ echo 'Nothing to build'
'Nothing to build'
Done in 0.12s.

> expo-custom
yarn run v1.22.11
$ expo-module build
C:\Users\eugen\Coding\Webdev\Learning\rn-yarn-workspaces\node_modules\expo-module-scripts\bin\expo-module-build:3
set -eo pipefail
        ^^^^^^^^

SyntaxError: Unexpected identifier
    at wrapSafe (internal/modules/cjs/loader.js:988:16)
    at Module._compile (internal/modules/cjs/loader.js:1036:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1101:10)
    at Module.load (internal/modules/cjs/loader.js:937:32)
    at Function.Module._load (internal/modules/cjs/loader.js:778:12)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:76:12)
    at internal/main/run_main_module.js:17:47
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed.
Exit code: 1
Command: C:\Program Files\nodejs\node.exe
Arguments: C:\Users\eugen\AppData\Roaming\npm\node_modules\yarn\lib\cli.js run build
Directory: C:\Users\eugen\Coding\Webdev\Learning\rn-yarn-workspaces\packages\expo-custom
Output:

info Visit https://yarnpkg.com/en/docs/cli/workspaces for documentation about this command.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.

SushiWaUmai avatar Oct 27 '21 17:10 SushiWaUmai

@ajsmth According to the expo monorepos guide we shouldn't be using expo-yarn-workspaces. Do you know if that guidance is correct?

I actually came to this issue by following that guide and still having issues with expo web - your advice with dangerouslyAddModulePathsToTranspile helped me out.

chriscoomber avatar Mar 03 '23 14:03 chriscoomber

Same as @chriscoomber above, I found this post looking for hot-reload support for web. The dangerouslyAddModulePathsToTranspile solution works for me, but I wonder if this support can be added to @expo/webpack-config, since that is the recommended approach now, and usage of expo-yarn-workspaces is explicitly discouraged.

Here is the config that I'm using now:

const createExpoWebpackConfigAsync = require('@expo/webpack-config');
const path = require('path');

module.exports = async function (env, argv) {
    const config = await createExpoWebpackConfigAsync(
        {
            ...env,
            babel: {
                dangerouslyAddModulePathsToTranspile: [
                    '../my-local-package'
                ]
            }
        },
        argv,
    );

    config.resolve.symlinks = true;

    return config;
};

seanadkinson avatar Oct 18 '23 18:10 seanadkinson

Using Metro bundler (instead of webpack) as described in a related issue worked for me.

JM-CF avatar Nov 25 '23 01:11 JM-CF