oblador / react-native-vector-icons

Customizable Icons for React Native with support for image source and full styling.
https://oblador.github.io/react-native-vector-icons/
MIT License
17.31k stars 2.12k forks source link

Support Vite bundler better #1477

Closed crutchcorn closed 1 month ago

crutchcorn commented 1 year ago

Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

I'm attempting to use this package with Vite to bundle a React Native Web project. On doing so, it's been quite a challenge to use the .js files with .jsx syntax, as Vite likes the throw the following errors:

vite v4.0.4 building for production...
✓ 152 modules transformed.
[commonjs--resolver] Unexpected token (91:8) in C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/react-native-vector-icons/lib/create-icon-set.js
file: C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/react-native-vector-icons/lib/create-icon-set.js:91:8
89:
90:       return (
91:         <Text selectable={false} {...props}>
            ^
92:           {glyph}
93:           {children}
error during build:
SyntaxError: Unexpected token (91:8) in C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/react-native-vector-icons/lib/create-icon-set.js
    at Object.pp$4.raise (file:///C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/rollup/dist/es/shared/rollup.js:20310:13)
    at Object.pp$9.unexpected (file:///C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/rollup/dist/es/shared/rollup.js:17611:8)
    at Object.pp$5.parseExprAtom (file:///C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/rollup/dist/es/shared/rollup.js:19694:10)
    at Object.pp$5.parseExprSubscripts (file:///C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/rollup/dist/es/shared/rollup.js:19486:19)
    at Object.pp$5.parseMaybeUnary (file:///C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/rollup/dist/es/shared/rollup.js:19452:17)
    at Object.pp$5.parseExprOps (file:///C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/rollup/dist/es/shared/rollup.js:19379:19)
    at Object.pp$5.parseMaybeConditional (file:///C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/rollup/dist/es/shared/rollup.js:19362:19)
    at Object.pp$5.parseMaybeAssign (file:///C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/rollup/dist/es/shared/rollup.js:19329:19)
    at Object.pp$5.parseParenAndDistinguishExpression (file:///C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/rollup/dist/es/shared/rollup.js:19790:28)
    at Object.pp$5.parseExprAtom (file:///C:/Users/crutchcorn/git/Etc/react-native-monorepo-example/apps/vite-react-app/node_modules/rollup/dist/es/shared/rollup.js:19653:41)

Describe the solution you'd like A clear and concise description of what you want to happen.

While there might be a way to migrate around this, I've spent some time with it and haven't made much headway. The official Vite team themselves suggests moving .js files to .jsx to side-step detection frustrations in doing this.

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

I could use patch-package, and likely may do this in the interim, but this wouldn't be much help in the long run.

Additional context Add any other context or screenshots about the feature request here.

I'm happy to make a PR renaming files to .jsx and fixing any related packaging errors that might arise from this.

xiongemi commented 9 months ago

Actually I figure it out without need to change the file extension. This happens because react-native-vector-icons contains jsx code (e.g. <Text>) inside .js file.

First, I need to add files with .web to the extensions (the order of the item in the array actually matters):

const extensions = [
  '.mjs',
  '.web.tsx',
  '.tsx',
  '.web.ts',
  '.ts',
  '.web.jsx',
  '.jsx',
  '.web.js',
  '.js',
  '.css',
  '.json',
];

Add these extensions to resolve:

export default defineConfig({
...

  resolve: {
    extensions,
...

Then inside defineConfig, I added to optimizeDeps to resolve js files using jsx loader:

  optimizeDeps: {
    esbuildOptions: {
      resolveExtensions: extensions,
      jsx: "automatic",
      loader: { ".js": "jsx" },
    },
  },

When I serve up the web app, I will get this error message:

MaterialCommunityIcon.tsx:49 Error: Dynamic require of "react-native-vector-icons/MaterialCommunityIcons" is not supported

So my web app's index.html, I need to add:

    <style type="text/css">
      @font-face {
        font-family: 'MaterialCommunityIcons';
        src: url('/MaterialCommunityIcons.ttf') format('truetype');
      }
    </style>

Now the serve command is working. However, the build command still does not work with the same error.

I created a rollup plugin:

const rollupPlugin = (matchers: RegExp[]) => ({
  name: 'js-in-jsx',
  load(id: string) {
    if (
      matchers.some((matcher) => matcher.test(id)) &&
      id.endsWith('.js')
    ) {
      const file = readFileSync(id, { encoding: 'utf-8' });
      return esbuild.transformSync(file, { loader: 'jsx', jsx: 'automatic' });
    }
  },
});

Then add this plugin to the vite config:

  build: {
    rollupOptions: {
      plugins: [rollupPlugin([/react-native-vector-icons/])],
    },
  },

Now build command should work.

Another issue I notice: In the web browser, I got this error:

NavigationContainer.tsx:29 Uncaught ReferenceError: global is not defined

In vite.config.ts, add:

  define: {
    global: "window",
  },

The final vite.config.ts:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import * as esbuild from 'esbuild';
import { readFileSync } from 'fs';

const extensions = [
  '.mjs',
  '.web.tsx',
  '.tsx',
  '.web.ts',
  '.ts',
  '.web.jsx',
  '.jsx',
  '.web.js',
  '.js',
  '.css',
  '.json',
];

const rollupPlugin = (matchers: RegExp[]) => ({
  name: 'js-in-jsx',
  load(id: string) {
    if (matchers.some((matcher) => matcher.test(id)) && id.endsWith('.js')) {
      const file = readFileSync(id, { encoding: 'utf-8' });
      return esbuild.transformSync(file, { loader: 'jsx', jsx: 'automatic' });
    }
  },
});

export default defineConfig({
  cacheDir: '../../node_modules/.vite/techy-jokes-web',
  define: {
    global: 'window',
  },

  resolve: {
    extensions,
    alias: {
      'react-native': 'react-native-web',
    },
  },

  build: {
    rollupOptions: {
      plugins: [rollupPlugin([/react-native-vector-icons/])],
    },
  },

  server: {
    port: 4200,
    host: 'localhost',
  },

  preview: {
    port: 4300,
    host: 'localhost',
  },

  optimizeDeps: {
    esbuildOptions: {
      resolveExtensions: extensions,
      jsx: 'automatic',
      loader: { '.js': 'jsx' },
    },
  },

  plugins: [react()],

  test: {
    globals: true,
    cache: { dir: '../../node_modules/.vitest' },
    environment: 'jsdom',
    include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
  },
});
achadee commented 8 months ago

Dude I love you! thanks for this!

johnf commented 8 months ago

I'm not familiar with vite, is there anything we could do in our library to make this more seamless? Would simply renaming js to hsx solve the issue?

crutchcorn commented 8 months ago

Indeed @johnf - I could even test any alpha releases against our production Vite setup and would be happy to make a PR to do so

andrew-bierman commented 2 months ago

Thanks a ton for the config, this was super helpful!

If anyone faces this same issue with expo vector icons (which is built on top of react-native-vector-icons), you can get the above config working with minimal changes:

build: {
  commonjsOptions: { transformMixedEsModules: true },
  rollupOptions: {
    plugins: [
      rollupPlugin([/react-native-vector-icons/, /@expo\/vector-icons/]),
    ],
  },
},