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 110 forks source link

View config getter callback for component `RNSVGPath` must be a function (received `undefined`) #396

Closed MinhNgoc-HRI closed 1 year ago

MinhNgoc-HRI commented 1 year ago

Environment

System:
    OS: macOS 12.6.6
    CPU: (8) arm64 Apple M1
    Memory: 1.32 GB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 18.16.0 - ~/.nvm/versions/node/v18.16.0/bin/node
    Yarn: 3.6.0 - /opt/homebrew/bin/yarn
    npm: 9.5.1 - ~/.nvm/versions/node/v18.16.0/bin/npm
    Watchman: 2023.06.12.00 - /opt/homebrew/bin/watchman
  Managers:
    CocoaPods: 1.12.1 - /opt/homebrew/bin/pod
  SDKs:
    iOS SDK:
      Platforms: DriverKit 22.2, iOS 16.2, macOS 13.1, tvOS 16.1, watchOS 9.1
    Android SDK:
      API Levels: 30, 31, 33, 34
      Build Tools: 30.0.3, 31.0.0, 33.0.0, 34.0.0
      System Images: android-34 | Google APIs ARM 64 v8a
      Android NDK: Not Found
  IDEs:
    Android Studio: 2022.2 AI-222.4459.24.2221.10121639
    Xcode: 14.2/14C18 - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.19 - /Users/minhngoc/.sdkman/candidates/java/current/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.1.0 => 18.1.0 
    react-native: 0.70.6 => 0.70.6 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Description

There is an error when using react-native-svg

Error:

 [Invariant Violation: View config getter callback for component `RNSVGPath` must be a function (received `undefined`).] {"componentStack": "

mini app webpack.config.mjs :

import path from 'path';
import TerserPlugin from 'terser-webpack-plugin';
import * as Repack from '@callstack/repack';
import {deps} from '../../shared/dependencies.mjs';
/**
 * 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 => {
  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');
  }

  /**
   * Using Module Federation might require disabling hmr.
   * Uncomment below to set `devServer.hmr` to `false`.
   *
   * Keep in mind that `devServer` object is not available
   * when running `webpack-bundle` command. Be sure
   * to check its value to avoid accessing undefined value,
   * otherwise an error might occur.
   */
  // if (devServer) {
  //   devServer.hmr = false;
  // }

  /**
   * 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: [
      ...Repack.getInitializationEntries(reactNativePath, {
        hmr: devServer && devServer.hmr,
      }),
      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),

      /**
       * Uncomment this to ensure all `react-native*` imports will resolve to the same React Native
       * dependency. You might need it when using workspaces/monorepos or unconventional project
       * structure. For simple/typical project you won't need it.
       */
      // alias: {
      //   'react-native': reactNativePath,
      // },
    },
    /**
     * 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: {
      /**
       * This rule will process all React Native related dependencies with Babel.
       * If you have a 3rd-party dependency that you need to transpile, you can add it to the
       * `include` list.
       *
       * You can also enable persistent caching with `cacheDirectory` - please refer to:
       * https://github.com/babel/babel-loader#options
       */
      rules: [
        {
          test: /\.[jt]sx?$/,
          include: [
            /node_modules(.*[/\\])+react\//,
            /node_modules(.*[/\\])+react-native/,
            /node_modules(.*[/\\])+@react-native/,
            /node_modules(.*[/\\])+react-freeze/,
            /node_modules(.*[/\\])+@react-navigation/,
            /node_modules(.*[/\\])+@react-native-community/,
            /node_modules(.*[/\\])+@expo/,
            /node_modules(.*[/\\])+pretty-format/,
            /node_modules(.*[/\\])+metro/,
            /node_modules(.*[/\\])+abort-controller/,
            /node_modules(.*[/\\])+@callstack\/repack/,
            /node_modules(.*[/\\])+pmn-rn-component/,
            // /node_modules(.*[/\\])+react-native-fast-image/,
            // /node_modules(.*[/\\])+react-native-gesture-handler/,
            // /node_modules(.*[/\\])+react-native-reanimated/,
            // /node_modules(.*[/\\])+react-native-paper/,
            // /node_modules(.*[/\\])+react-native-safe-area-context/,
            // /node_modules(.*[/\\])+react-native-screens/,
            // /node_modules(.*[/\\])+react-native-tab-view/,
            // /node_modules(.*[/\\])+react-native-vector-icons/,
            // /node_modules(.*[/\\])+react-native-keyboard-aware-scroll-view/,
            // /node_modules(.*[/\\])+react-native-reanimated-carousel/,
            // /node_modules(.*[/\\])+react-native-svg/,
          ],
          use: 'babel-loader',
        },
        /**
         * Here you can adjust loader that will process your files.
         *
         * You can also enable persistent caching with `cacheDirectory` - please refer to:
         * https://github.com/babel/babel-loader#options
         */
        {
          test: /\.[jt]sx?$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
            options: {
              /** Add React Refresh transform only when HMR is enabled. */
              plugins:
                devServer && devServer.hmr
                  ? ['module:react-refresh/babel']
                  : undefined,
            },
          },
        },
        /**
         * This loader handles all static assets (images, video, audio and others), so that you can
         * use (reference) them inside your application.
         *
         * If you wan to handle specific asset type manually, filter out the extension
         * from `ASSET_EXTENSIONS`, for example:
         * ```
         * Repack.ASSET_EXTENSIONS.filter((ext) => ext !== 'svg')
         * ```
         */
        {
          test: Repack.getAssetExtensionsRegExp(
            Repack.ASSET_EXTENSIONS.filter(ext => ext !== 'svg'),
          ),
          // exclude: [
          //   path.join(dirname, 'src/assets/images'),
          //   path.join(dirname, 'src/assets/icons'),
          // ],
          use: {
            loader: '@callstack/repack/assets-loader',
            options: {
              inline: true,
              platform,
              devServerEnabled: Boolean(devServer),
              /**
               * Defines which assets are scalable - which assets can have
               * scale suffixes: `@1x`, `@2x` and so on.
               * By default all images are scalable.
               */
              scalableAssetExtensions: Repack.SCALABLE_ASSETS,
              // remote: {
              //   enabled: true,
              //   publicPath: 'http://localhost:3000',
              // },
            },
          },
        },
        // {
        //   test: /\.[jt]sx?$/,
        //   include: [/src\/assets\/icons\/IconFB/],
        //   use: {
        //     loader: '@svgr/webpack',
        //     options: {
        //       native: true,
        //       dimensions: false,
        //     },
        //   },
        // },
        {
          test: /\.svg$/,
          use: [
            {
              loader: '@svgr/webpack',
              options: {
                native: true,
                dimensions: false,
              },
            },
          ],
        },
      ],
    },
    plugins: [
      /**
       * 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: 'myVideo',
        exposes: {
          './App': './App.tsx',
        },
        shared: deps,
      }),
    ],
  };
};

SVG component:

import {widthLize} from 'pmn-rn-component';
import React from 'react';
import Svg, {Path} from 'react-native-svg';

type Props = {
  width?: number;
  height?: number;
  color?: string;
};

const IconEye = ({width = 24, height = 24, color = '#8A8B93'}: Props) => {
  return (
    <Svg
      width={widthLize(width)}
      height={widthLize(height)}
      viewBox="0 0 24 24"
      fill="none">
      <Path
        d="M3 12C3 12 6.27273 5.45459 12 5.45459C17.7273 5.45459 21 12 21 12C21 12 17.7273 18.5455 12 18.5455C6.27273 18.5455 3 12 3 12Z"
        stroke={color}
        stroke-width="1.5"
        stroke-linecap="round"
        stroke-linejoin="round"
      />
      <Path
        d="M12 14.4545C13.3556 14.4545 14.4545 13.3556 14.4545 12C14.4545 10.6443 13.3556 9.54541 12 9.54541C10.6443 9.54541 9.54541 10.6443 9.54541 12C9.54541 13.3556 10.6443 14.4545 12 14.4545Z"
        stroke={color}
        stroke-width="1.5"
        stroke-linecap="round"
        stroke-linejoin="round"
      />
    </Svg>
  );
};

export default IconEye;

Reproducible Demo

Ảnh chụp Màn hình 2023-07-07 lúc 13 01 11
RafikiTiki commented 1 year ago

Hello, by a Reproduction Repo I meant a new GitHub repository with a codebase in which you can reliably reproduce the problem. I need that to look into the problem you're reporting here.

MinhNgoc-HRI commented 1 year ago

Hello, by a Reproduction Repo I meant a new GitHub repository with a codebase in which you can reliably reproduce the problem. I need that to look into the problem you're reporting here.

Hi @RafikiTiki , this is my repo: https://github.com/ngnm1009/superApp When I run the mini app project, it works fine, but when I run it with the host app , I facing an error

RafikiTiki commented 1 year ago

Thanks for the repro repo, I will investigate it.

jbroma commented 1 year ago

Hey @ngnm1009,

I investigated this issue a little bit and it seems that unless we explicitly import react-native-svg in host app, it will be dropped from the bundle, therefore JS part of it won't be available in runtime.

For now, to resolve your issue please add the following import in host app's App.tsx:

import 'react-native-svg'

I will need to dig deeper and investigate whether there is a nicer solution for it

MinhNgoc-HRI commented 1 year ago

Hey @ngnm1009,

I investigated this issue a little bit and it seems that unless we explicitly import react-native-svg in host app, it will be dropped from the bundle, therefore JS part of it won't be available in runtime.

For now, to resolve your issue please add the following import in host app's App.tsx:

import 'react-native-svg'

I will need to dig deeper and investigate whether there is a nicer solution for it

Hi @jbroma , thank for your answer. it work

asingh-lifesize commented 6 months ago

I am still facing the same issue

prokopsimek commented 3 months ago

It's still the same for a newly generated nx/expo mobile app.