callstack / repack

A Webpack-based toolkit to build your React Native application with full support of Webpack ecosystem.
https://re-pack.dev
MIT License
1.48k stars 107 forks source link

Re.pack Rspack (5.0.0-alpha.0) - Panic occurred at runtime. Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks. #748

Closed sahajarora1286 closed 1 week ago

sahajarora1286 commented 1 month ago

Describe the bug

I am trying to upgrade from @callstack/repack v4 to v5.0.0-alpha.0. I have followed the templates in the repack example as well as the official webpack -> rspack migration documentation to migrate from webpack to rspack.

I have a react-native super-app that leverages module-federation and exists as a monorepo.

Here's what my rspack.config.mjs file looks like for the host:

export default (env) => {
  console.log('using mobile webpack config...');
  const {
    mode = 'development',
    context = Repack.getDirname(import.meta.url),
    entry = './index.js',
    platform = process.env.PLATFORM,
    minimize = mode === 'production',
    devServer = undefined,
    bundleFilename = undefined,
    sourceMapFilename = undefined,
    assetsPath = undefined,
    reactNativePath = new URL('./node_modules/react-native', import.meta.url)
      .pathname,
  } = env;
  const dirname = Repack.getDirname(import.meta.url);

  if (!platform) {
    throw new Error('Missing platform');
  }

  /**
   * Depending on your Babel configuration you might want to keep it.
   * If you don't use `env` in your Babel config, you can remove it.
   *
   * Keep in mind that if you remove it you should set `BABEL_ENV` or `NODE_ENV`
   * to `development` or `production`. Otherwise your production code might be compiled with
   * in development mode by Babel.
   */
  process.env.BABEL_ENV = mode;

  return {
    mode,
    /**
     * This should be always `false`, since the Source Map configuration is done
     * by `SourceMapDevToolPlugin`.
     */
    devtool: false,
    context,
    /**
     * `getInitializationEntries` will return necessary entries with setup and initialization code.
     * If you don't want to use Hot Module Replacement, set `hmr` option to `false`. By default,
     * HMR will be enabled in development mode.
     */
    entry,
    resolve: {
      /**
       * `getResolveOptions` returns additional resolution configuration for React Native.
       * If it's removed, you won't be able to use `<file>.<platform>.<ext>` (eg: `file.ios.js`)
       * convention and some 3rd-party libraries that specify `react-native` field
       * in their `package.json` might not work correctly.
       */
      ...Repack.getResolveOptions(platform),
      tsConfig: './tsconfig.json',
    },
    /**
     * Configures output.
     * It's recommended to leave it as it is unless you know what you're doing.
     * By default Webpack will emit files into the directory specified under `path`. In order for the
     * React Native app use them when bundling the `.ipa`/`.apk`, they need to be copied over with
     * `Repack.OutputPlugin`, which is configured by default inside `Repack.RepackPlugin`.
     */
    output: {
      clean: true,
      hashFunction: 'xxhash64',
      path: path.join(dirname, 'build/generated', platform),
      filename: 'index.bundle',
      chunkFilename: '[name].chunk.bundle',
      publicPath: Repack.getPublicPath({ platform, devServer }),
    },
    /**
     * Configures optimization of the built bundle.
     */
    optimization: {
      /** Enables minification based on values passed from React Native CLI or from fallback. */
      minimize,
      /** Configure minimizer to process the bundle. */
      minimizer: [
        new TerserPlugin({
          test: /\.(js)?bundle(\?.*)?$/i,
          /**
           * Prevents emitting text file with comments, licenses etc.
           * If you want to gather in-file licenses, feel free to remove this line or configure it
           * differently.
           */
          extractComments: false,
          terserOptions: {
            format: {
              comments: false,
            },
          },
        }),
      ],
      chunkIds: 'named',
    },
    module: {
      rules: [
        Repack.REACT_NATIVE_LOADING_RULES,
        Repack.NODE_MODULES_LOADING_RULES,
        /* repack is symlinked to a local workspace */
        {
          test: /\.[jt]sx?$/,
          type: 'javascript/auto',
          include: [/repack[/\\]dist/],
          use: {
            loader: 'builtin:swc-loader',
            options: {
              env: { targets: { 'react-native': '0.74' } },
              jsc: { externalHelpers: true },
            },
          },
        },
        /* codebase rules */
        {
          test: /\.[jt]sx?$/,
          type: 'javascript/auto',
          exclude: [/node_modules/, /repack[/\\]dist/],
          use: {
            loader: 'builtin:swc-loader',
            /** @type {import('@rspack/core').SwcLoaderOptions} */
            options: {
              sourceMaps: true,
              env: {
                targets: { 'react-native': '0.74' },
              },
              jsc: {
                parser: {
                  syntax: 'typescript',
                },
                externalHelpers: true,
                transform: {
                  react: {
                    runtime: 'automatic',
                  },
                },
              },
            },
          },
        },
        {
          test: Repack.getAssetExtensionsRegExp(Repack.ASSET_EXTENSIONS),
          use: {
            loader: '@callstack/repack/assets-loader',
            options: {
              platform,
              devServerEnabled: Boolean(devServer),
            },
          },
        },
      ],
    },
    plugins: [
      new rspack.IgnorePlugin({ resourceRegExp: /@react-native-masked-view/ }),
      /**
       * Configure other required and additional plugins to make the bundle
       * work in React Native and provide good development experience with
       * sensible defaults.
       *
       * `Repack.RepackPlugin` provides some degree of customization, but if you
       * need more control, you can replace `Repack.RepackPlugin` with plugins
       * from `Repack.plugins`.
       */
      new Repack.RepackPlugin({
        context,
        mode,
        platform,
        devServer,
        output: {
          bundleFilename,
          sourceMapFilename,
          assetsPath,
        },
      }),
      new Repack.plugins.ModuleFederationPlugin({
        name: 'host',
        shared: {
          react: {
            ...Repack.Federated.SHARED_REACT,
            requiredVersion: '18.2.0',
            eager: true,
          },
          'react-native': {
            ...Repack.Federated.SHARED_REACT_NATIVE,
            requiredVersion: '0.74',
            eager: true,
          },
          reselect: {
            singleton: true,
            requiredVersion: monorepoDeps['reselect'],
            eager: true,
          },
          'react-redux': {
            singleton: true,
            requiredVersion: monorepoDeps['react-redux'],
            eager: true,
          },
          '@reduxjs/toolkit': {
            singleton: true,
            requiredVersion: monorepoDeps['@reduxjs/toolkit'],
            eager: true,
          },
          '@reduxjs/toolkit/query': {
            singleton: true,
            requiredVersion: monorepoDeps['@reduxjs/toolkit/query'],
            eager: true,
          },
          '@reduxjs/toolkit/query/react': {
            singleton: true,
            requiredVersion: monorepoDeps['@reduxjs/toolkit/query/react'],
            eager: true,
          },
          '@react-navigation/native': {
            singleton: true,
            requiredVersion: monorepoDeps['@react-navigation/native'],
            eager: true,
          },
          '@react-navigation/native-stack': {
            singleton: true,
            requiredVersion: monorepoDeps['@react-navigation/native-stack'],
            eager: true,
          },
          'react-native-screens': {
            singleton: true,
            requiredVersion: monorepoDeps['react-native-screens'],
            eager: true,
          },
          'react-native-safe-area-context': {
            singleton: true,
            requiredVersion: monorepoDeps['react-native-safe-area-context'],
            eager: true,
          },
          'redux-persist': {
            singleton: true,
            requiredVersion: monorepoDeps['redux-persist'],
            eager: true,
          },
          '@react-native-async-storage/async-storage': {
            singleton: true,
            requiredVersion:
              monorepoDeps['@react-native-async-storage/async-storage'],
            eager: true,
          },
        },
      }),
      new rspack.EnvironmentPlugin({ MF_CACHE: null }),
      process.env.RSDOCTOR && new RsdoctorRspackPlugin(),
    ].filter(Boolean),
  };
};

When I try to start the host app using the command: react-native webpack-start --webpackConfig ./rspack.config.mjs --host 127.0.0.1, I get the following error:

Panic occurred at runtime. Please file an issue on GitHub with the backtrace below: https://github.com/web-infra-dev/rspack/issues
Message:  Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks.
Location: crates/rspack_binding_options/src/options/raw_devtool.rs:99

My motivation, and need, to get to Rspack from Webpack is to lower the build time it currently takes with webpack. The fact that with callstack/repack v4 we need to use webpack and babel-loader to transpile huge node_module files (for react-native compatibility) is a pain and leads to really slow compilation of the host and other mini-apps that have bare minimal code (2-4 source files). I have learnt that swc is a much faster alternative to babel-loader and hence I'd love to be able to leverage it in my project setup.

Any help with resolving this issue and getting my project to successfully work with @callstack/repack v5.0.0-alpha.0 and rspac + swc-loader would be highly appreciated.

Thanks !

System Info

System:
  OS: macOS 14.5
  CPU: (10) arm64 Apple M1 Max
  Memory: 104.30 MB / 64.00 GB
  Shell:
    version: 3.2.57
    path: /bin/bash
Binaries:
  Node:
    version: 18.18.0
    path: ~/.nvm/versions/node/v18.18.0/bin/node
  Yarn:
    version: 1.19.0
    path: /opt/homebrew/bin/yarn
  npm:
    version: 9.8.1
    path: ~/.nvm/versions/node/v18.18.0/bin/npm
  Watchman: Not Found
Managers:
  CocoaPods:
    version: 1.15.2
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK: Not Found
  Android SDK: Not Found
IDEs:
  Android Studio: 2021.3 AI-213.7172.25.2113.9123335
  Xcode:
    version: /undefined
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.5
    path: /usr/bin/javac
  Ruby:
    version: 2.6.10
    path: /usr/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react: Not Found
  react-native:
    installed: 0.74.5
    wanted: "0.74"
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

Re.Pack Version

5.0.0-alpha.0

Reproduction

n/a

Steps to reproduce

Use the following config for rspack.config.js :

import path from 'path';
import TerserPlugin from 'terser-webpack-plugin';
import * as Repack from '@callstack/repack';
import monorepoPackage from '../../package.json' assert { type: 'json' };
import rspack from '@rspack/core';
import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin';

const monorepoDeps = monorepoPackage.dependencies;

/**
 * More documentation, installation, usage, motivation and differences with Metro is available at:
 * https://github.com/callstack/repack/blob/main/README.md
 *
 * The API documentation for the functions and plugins used in this file is available at:
 * https://re-pack.netlify.app/
 */

/**
 * Webpack configuration.
 * You can also export a static object or a function returning a Promise.
 *
 * @param env Environment options passed from either Webpack CLI or React Native CLI
 *            when running with `react-native start/bundle`.
 */
export default (env) => {
  console.log('using mobile webpack config...');
  const {
    mode = 'development',
    context = Repack.getDirname(import.meta.url),
    entry = './index.js',
    platform = process.env.PLATFORM,
    minimize = mode === 'production',
    devServer = undefined,
    bundleFilename = undefined,
    sourceMapFilename = undefined,
    assetsPath = undefined,
    reactNativePath = new URL('./node_modules/react-native', import.meta.url)
      .pathname,
  } = env;
  const dirname = Repack.getDirname(import.meta.url);

  if (!platform) {
    throw new Error('Missing platform');
  }

  /**
   * Depending on your Babel configuration you might want to keep it.
   * If you don't use `env` in your Babel config, you can remove it.
   *
   * Keep in mind that if you remove it you should set `BABEL_ENV` or `NODE_ENV`
   * to `development` or `production`. Otherwise your production code might be compiled with
   * in development mode by Babel.
   */
  process.env.BABEL_ENV = mode;

  return {
    mode,
    /**
     * This should be always `false`, since the Source Map configuration is done
     * by `SourceMapDevToolPlugin`.
     */
    devtool: false,
    context,
    /**
     * `getInitializationEntries` will return necessary entries with setup and initialization code.
     * If you don't want to use Hot Module Replacement, set `hmr` option to `false`. By default,
     * HMR will be enabled in development mode.
     */
    entry,
    resolve: {
      /**
       * `getResolveOptions` returns additional resolution configuration for React Native.
       * If it's removed, you won't be able to use `<file>.<platform>.<ext>` (eg: `file.ios.js`)
       * convention and some 3rd-party libraries that specify `react-native` field
       * in their `package.json` might not work correctly.
       */
      ...Repack.getResolveOptions(platform),
      tsConfig: './tsconfig.json',
    },
    /**
     * Configures output.
     * It's recommended to leave it as it is unless you know what you're doing.
     * By default Webpack will emit files into the directory specified under `path`. In order for the
     * React Native app use them when bundling the `.ipa`/`.apk`, they need to be copied over with
     * `Repack.OutputPlugin`, which is configured by default inside `Repack.RepackPlugin`.
     */
    output: {
      clean: true,
      hashFunction: 'xxhash64',
      path: path.join(dirname, 'build/generated', platform),
      filename: 'index.bundle',
      chunkFilename: '[name].chunk.bundle',
      publicPath: Repack.getPublicPath({ platform, devServer }),
    },
    /**
     * Configures optimization of the built bundle.
     */
    optimization: {
      /** Enables minification based on values passed from React Native CLI or from fallback. */
      minimize,
      /** Configure minimizer to process the bundle. */
      minimizer: [
        new TerserPlugin({
          test: /\.(js)?bundle(\?.*)?$/i,
          /**
           * Prevents emitting text file with comments, licenses etc.
           * If you want to gather in-file licenses, feel free to remove this line or configure it
           * differently.
           */
          extractComments: false,
          terserOptions: {
            format: {
              comments: false,
            },
          },
        }),
      ],
      chunkIds: 'named',
    },
    module: {
      rules: [
        Repack.REACT_NATIVE_LOADING_RULES,
        Repack.NODE_MODULES_LOADING_RULES,
        /* repack is symlinked to a local workspace */
        {
          test: /\.[jt]sx?$/,
          type: 'javascript/auto',
          include: [/repack[/\\]dist/],
          use: {
            loader: 'builtin:swc-loader',
            options: {
              env: { targets: { 'react-native': '0.74' } },
              jsc: { externalHelpers: true },
            },
          },
        },
        /* codebase rules */
        {
          test: /\.[jt]sx?$/,
          type: 'javascript/auto',
          exclude: [/node_modules/, /repack[/\\]dist/],
          use: {
            loader: 'builtin:swc-loader',
            /** @type {import('@rspack/core').SwcLoaderOptions} */
            options: {
              sourceMaps: true,
              env: {
                targets: { 'react-native': '0.74' },
              },
              jsc: {
                parser: {
                  syntax: 'typescript',
                },
                externalHelpers: true,
                transform: {
                  react: {
                    runtime: 'automatic',
                  },
                },
              },
            },
          },
        },
        {
          test: Repack.getAssetExtensionsRegExp(Repack.ASSET_EXTENSIONS),
          use: {
            loader: '@callstack/repack/assets-loader',
            options: {
              platform,
              devServerEnabled: Boolean(devServer),
            },
          },
        },
      ],
    },
    plugins: [
      new rspack.IgnorePlugin({ resourceRegExp: /@react-native-masked-view/ }),
      /**
       * Configure other required and additional plugins to make the bundle
       * work in React Native and provide good development experience with
       * sensible defaults.
       *
       * `Repack.RepackPlugin` provides some degree of customization, but if you
       * need more control, you can replace `Repack.RepackPlugin` with plugins
       * from `Repack.plugins`.
       */
      new Repack.RepackPlugin({
        context,
        mode,
        platform,
        devServer,
        output: {
          bundleFilename,
          sourceMapFilename,
          assetsPath,
        },
      }),
      new Repack.plugins.ModuleFederationPlugin({
        name: 'host',
        shared: {
          react: {
            ...Repack.Federated.SHARED_REACT,
            requiredVersion: '18.2.0',
            eager: true,
          },
          'react-native': {
            ...Repack.Federated.SHARED_REACT_NATIVE,
            requiredVersion: '0.74',
            eager: true,
          },
          reselect: {
            singleton: true,
            requiredVersion: monorepoDeps['reselect'],
            eager: true,
          },
          'react-redux': {
            singleton: true,
            requiredVersion: monorepoDeps['react-redux'],
            eager: true,
          },
          '@reduxjs/toolkit': {
            singleton: true,
            requiredVersion: monorepoDeps['@reduxjs/toolkit'],
            eager: true,
          },
          '@reduxjs/toolkit/query': {
            singleton: true,
            requiredVersion: monorepoDeps['@reduxjs/toolkit/query'],
            eager: true,
          },
          '@reduxjs/toolkit/query/react': {
            singleton: true,
            requiredVersion: monorepoDeps['@reduxjs/toolkit/query/react'],
            eager: true,
          },
          '@react-navigation/native': {
            singleton: true,
            requiredVersion: monorepoDeps['@react-navigation/native'],
            eager: true,
          },
          '@react-navigation/native-stack': {
            singleton: true,
            requiredVersion: monorepoDeps['@react-navigation/native-stack'],
            eager: true,
          },
          'react-native-screens': {
            singleton: true,
            requiredVersion: monorepoDeps['react-native-screens'],
            eager: true,
          },
          'react-native-safe-area-context': {
            singleton: true,
            requiredVersion: monorepoDeps['react-native-safe-area-context'],
            eager: true,
          },
          'redux-persist': {
            singleton: true,
            requiredVersion: monorepoDeps['redux-persist'],
            eager: true,
          },
          '@react-native-async-storage/async-storage': {
            singleton: true,
            requiredVersion:
              monorepoDeps['@react-native-async-storage/async-storage'],
            eager: true,
          },
        },
      }),
      new rspack.EnvironmentPlugin({ MF_CACHE: null }),
      process.env.RSDOCTOR && new RsdoctorRspackPlugin(),
    ].filter(Boolean),
  };
};

Try to start the dev server using: react-native webpack-start --webpackConfig ./rspack.config.mjs

jbroma commented 1 month ago

Hi @sahajarora1286, could you please create a reproduction repository with this issue? It really saves a lot of time and allows us to handle issues more swiftly instead of trying to recreate this on our own, thanks!

github-actions[bot] commented 3 weeks ago

This issue has been marked as stale because it has been inactive for 30 days. Please update this issue or it will be automatically closed in 14 days.

jbroma commented 1 week ago

Closing since no repro was provided and the issue is about a non-stable version of Re.Pack. Happy to revisit the issue if it's still a problem in the future.