I am combining my React web application with my Expo React Native mobile app into a monorepo using yarn workspaces because they both need to share some utils, queries, etc. My file structure is something like this:
<code>- ui
- apps
- mobile
- package.json
- web
- package.json
- packages
- shared
- package.json
- package.json
- node_modules
</code>
- ui
- apps
- mobile
- package.json
- web
- package.json
- packages
- shared
- package.json
- package.json
- node_modules
Mobile works fine with Expo but when I try running my web code, I get this:
<code>Module not found: Error: Can't resolve 'crypto' in 'ui/node_modules/expo-modules-core/build/uuid'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "crypto": require.resolve("crypto-browserify") }'
- install 'crypto-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "crypto": false }
<code>Module not found: Error: Can't resolve 'crypto' in 'ui/node_modules/expo-modules-core/build/uuid'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "crypto": require.resolve("crypto-browserify") }'
- install 'crypto-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "crypto": false }
</code>
Module not found: Error: Can't resolve 'crypto' in 'ui/node_modules/expo-modules-core/build/uuid'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "crypto": require.resolve("crypto-browserify") }'
- install 'crypto-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "crypto": false }
<code>Module build failed (from ../../node_modules/babel-loader/lib/index.js):
SyntaxError: ui/node_modules/expo-router/build/ExpoRoot.js: Support for the experimental syntax 'jsx' isn't currently enabled (54:17):
53 | const wrapper = ({ children }) => {
> 54 | return (<ParentWrapper>
55 | <react_native_safe_area_context_1.SafeAreaProvider
57 | initialMetrics={INITIAL_METRICS}>
Add @babel/preset-react (https://github.com/babel/babel/tree/main/packages/babel-preset-react) to the 'presets' section of your Babel config to enable transformation.
If you want to leave it as-is, add @babel/plugin-syntax-jsx (https://github.com/babel/babel/tree/main/packages/babel-plugin-syntax-jsx) to the 'plugins' section to enable parsing.
If you already added the plugin for this syntax to your config, it's possible that your config isn't being loaded.
You can re-run Babel with the BABEL_SHOW_CONFIG_FOR environment variable to show the loaded configuration:
npx cross-env BABEL_SHOW_CONFIG_FOR=ui/node_modules/expo-router/build/ExpoRoot.js <your build command>
See https://babeljs.io/docs/configuration#print-effective-configs for more info.
<code>Module build failed (from ../../node_modules/babel-loader/lib/index.js):
SyntaxError: ui/node_modules/expo-router/build/ExpoRoot.js: Support for the experimental syntax 'jsx' isn't currently enabled (54:17):
52 | */
53 | const wrapper = ({ children }) => {
> 54 | return (<ParentWrapper>
| ^
55 | <react_native_safe_area_context_1.SafeAreaProvider
56 | // SSR support
57 | initialMetrics={INITIAL_METRICS}>
Add @babel/preset-react (https://github.com/babel/babel/tree/main/packages/babel-preset-react) to the 'presets' section of your Babel config to enable transformation.
If you want to leave it as-is, add @babel/plugin-syntax-jsx (https://github.com/babel/babel/tree/main/packages/babel-plugin-syntax-jsx) to the 'plugins' section to enable parsing.
If you already added the plugin for this syntax to your config, it's possible that your config isn't being loaded.
You can re-run Babel with the BABEL_SHOW_CONFIG_FOR environment variable to show the loaded configuration:
npx cross-env BABEL_SHOW_CONFIG_FOR=ui/node_modules/expo-router/build/ExpoRoot.js <your build command>
See https://babeljs.io/docs/configuration#print-effective-configs for more info.
</code>
Module build failed (from ../../node_modules/babel-loader/lib/index.js):
SyntaxError: ui/node_modules/expo-router/build/ExpoRoot.js: Support for the experimental syntax 'jsx' isn't currently enabled (54:17):
52 | */
53 | const wrapper = ({ children }) => {
> 54 | return (<ParentWrapper>
| ^
55 | <react_native_safe_area_context_1.SafeAreaProvider
56 | // SSR support
57 | initialMetrics={INITIAL_METRICS}>
Add @babel/preset-react (https://github.com/babel/babel/tree/main/packages/babel-preset-react) to the 'presets' section of your Babel config to enable transformation.
If you want to leave it as-is, add @babel/plugin-syntax-jsx (https://github.com/babel/babel/tree/main/packages/babel-plugin-syntax-jsx) to the 'plugins' section to enable parsing.
If you already added the plugin for this syntax to your config, it's possible that your config isn't being loaded.
You can re-run Babel with the BABEL_SHOW_CONFIG_FOR environment variable to show the loaded configuration:
npx cross-env BABEL_SHOW_CONFIG_FOR=ui/node_modules/expo-router/build/ExpoRoot.js <your build command>
See https://babeljs.io/docs/configuration#print-effective-configs for more info.
I’ve tried everything and nothing seems to fix this.
For the first error, I have only been able to fix it using react-app-rewired and adding a fallback for the ‘crypto’ module as a config override, but I’d rather avoid having to rely on react-app-rewired if there is a better solution
For the second one, I already have the babel preset as you can see in my babel.config.json
in ui/apps/web
and it still doesn’t work
"plugins": ["@babel/plugin-syntax-jsx"]
<code>{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
],
"@babel/preset-react"
],
"plugins": ["@babel/plugin-syntax-jsx"]
}
</code>
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
],
"@babel/preset-react"
],
"plugins": ["@babel/plugin-syntax-jsx"]
}
For reference, these are my package.json
(s):
<code> // ui/package.json
<code> // ui/package.json
{
"private": true,
"name": "monorepo",
"version": "1.0.0",
"workspaces": [
"apps/*",
"packages/*"
]
}
</code>
// ui/package.json
{
"private": true,
"name": "monorepo",
"version": "1.0.0",
"workspaces": [
"apps/*",
"packages/*"
]
}
"name": "monorepo-mobile",
"reset-project": "node ./scripts/reset-project.js",
"android": "expo run:android",
"web": "expo start --web",
"test": "jest --watchAll",
"@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "^1.24.0",
"@react-navigation/native": "^6.0.2",
"@tanstack/react-query": "^5.51.1",
"expo-constants": "~16.0.2",
"expo-linking": "~6.3.1",
"expo-localization": "~15.0.3",
"expo-router": "~3.5.18",
"expo-splash-screen": "~0.27.5",
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7",
"expo-web-browser": "~13.0.3",
"react-i18next": "^14.1.2",
"react-native": "0.74.3",
"react-native-gesture-handler": "~2.16.1",
"react-native-heroicons": "^4.0.0",
"react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.1",
"react-native-screens": "3.31.1",
"react-native-svg": "^15.4.0",
"react-native-web": "~0.19.10"
"@babel/core": "^7.20.0",
"@types/jest": "^29.5.12",
"@types/react": "~18.2.45",
"@types/react-test-renderer": "^18.0.7",
"react-test-renderer": "18.3.1",
<code>{
"name": "monorepo-mobile",
"main": "index.js",
"version": "1.0.0",
"scripts": {
"start": "expo start",
"reset-project": "node ./scripts/reset-project.js",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"test": "jest --watchAll",
"lint": "expo lint"
},
"jest": {
"preset": "jest-expo"
},
"dependencies": {
"@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "^1.24.0",
"@react-navigation/native": "^6.0.2",
"@tanstack/react-query": "^5.51.1",
"@monorepo/shared": "*",
"axios": "^1.7.2",
"expo": "~51.0.20",
"expo-constants": "~16.0.2",
"expo-font": "~12.0.8",
"expo-linking": "~6.3.1",
"expo-localization": "~15.0.3",
"expo-router": "~3.5.18",
"expo-splash-screen": "~0.27.5",
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7",
"expo-web-browser": "~13.0.3",
"i18next": "^23.12.1",
"nativewind": "^2.0.11",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-i18next": "^14.1.2",
"react-native": "0.74.3",
"react-native-gesture-handler": "~2.16.1",
"react-native-heroicons": "^4.0.0",
"react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.1",
"react-native-screens": "3.31.1",
"react-native-svg": "^15.4.0",
"react-native-web": "~0.19.10"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/jest": "^29.5.12",
"@types/react": "~18.2.45",
"@types/react-test-renderer": "^18.0.7",
"jest": "^29.2.1",
"jest-expo": "~51.0.3",
"react-test-renderer": "18.3.1",
"tailwindcss": "3.3.2",
"typescript": "~5.3.3"
},
"private": true
}
</code>
{
"name": "monorepo-mobile",
"main": "index.js",
"version": "1.0.0",
"scripts": {
"start": "expo start",
"reset-project": "node ./scripts/reset-project.js",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"test": "jest --watchAll",
"lint": "expo lint"
},
"jest": {
"preset": "jest-expo"
},
"dependencies": {
"@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "^1.24.0",
"@react-navigation/native": "^6.0.2",
"@tanstack/react-query": "^5.51.1",
"@monorepo/shared": "*",
"axios": "^1.7.2",
"expo": "~51.0.20",
"expo-constants": "~16.0.2",
"expo-font": "~12.0.8",
"expo-linking": "~6.3.1",
"expo-localization": "~15.0.3",
"expo-router": "~3.5.18",
"expo-splash-screen": "~0.27.5",
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7",
"expo-web-browser": "~13.0.3",
"i18next": "^23.12.1",
"nativewind": "^2.0.11",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-i18next": "^14.1.2",
"react-native": "0.74.3",
"react-native-gesture-handler": "~2.16.1",
"react-native-heroicons": "^4.0.0",
"react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.1",
"react-native-screens": "3.31.1",
"react-native-svg": "^15.4.0",
"react-native-web": "~0.19.10"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/jest": "^29.5.12",
"@types/react": "~18.2.45",
"@types/react-test-renderer": "^18.0.7",
"jest": "^29.2.1",
"jest-expo": "~51.0.3",
"react-test-renderer": "18.3.1",
"tailwindcss": "3.3.2",
"typescript": "~5.3.3"
},
"private": true
}
<code> // ui/apps/web/package.json
"name": "autoguardias-web",
"proxy": "http://127.0.0.1:5000",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@headlessui/react": "^1.7.18",
"@heroicons/react": "^2.0.16",
"@mui/material": "^5.12.1",
"@react-pdf/renderer": "^3.3.8",
"@tanstack/react-query": "^4.29.7",
"@tanstack/react-query-devtools": "^4.29.7",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"chartjs-adapter-moment": "^1.0.1",
"chartjs-plugin-annotation": "^3.0.1",
"i18next-browser-languagedetector": "^7.1.0",
"react-calendly": "^4.1.1",
"react-chartjs-2": "^5.2.0",
"react-i18next": "^13.0.3",
"react-joyride": "^2.7.2",
"react-number-format": "^5.1.4",
"react-router-dom": "^6.8.2",
"react-scripts": "5.0.1",
"react-spring": "^9.7.1",
"react-tooltip": "^5.22.0",
"react-youtube": "^10.1.0",
"start": "react-scripts start",
"build": "react-scripts build",
"lint": "eslint "src/**/*.js"",
"lintfix": "eslint --fix "src/**/*.js"",
"test": "react-scripts test",
"eject": "react-scripts eject",
"build:staging": "env-cmd -f .env.staging npm run build"
"last 1 firefox version",
"@babel/core": "^7.22.5",
"@babel/eslint-parser": "^7.22.5",
"@babel/plugin-syntax-jsx": "^7.22.5",
"@babel/plugin-syntax-typescript": "^7.22.5",
"@babel/preset-env": "^7.22.5",
"@babel/preset-react": "^7.22.5",
"@tailwindcss/forms": "^0.5.3",
"autoprefixer": "^10.4.13",
"eslint-config-airbnb": "^19.0.4",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
<code> // ui/apps/web/package.json
{
"name": "autoguardias-web",
"version": "0.1.0",
"private": true,
"proxy": "http://127.0.0.1:5000",
"dependencies": {
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@headlessui/react": "^1.7.18",
"@heroicons/react": "^2.0.16",
"@mui/material": "^5.12.1",
"@react-pdf/renderer": "^3.3.8",
"@tanstack/react-query": "^4.29.7",
"@tanstack/react-query-devtools": "^4.29.7",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.3.4",
"bcrypt": "^5.1.0",
"bcryptjs": "^2.4.3",
"chart.js": "^4.4.3",
"chartjs-adapter-moment": "^1.0.1",
"chartjs-plugin-annotation": "^3.0.1",
"clsx": "^1.2.1",
"env-cmd": "^10.1.0",
"i18next": "^23.4.1",
"i18next-browser-languagedetector": "^7.1.0",
"install": "^0.13.0",
"moment": "^2.30.1",
"npm": "^10.1.0",
"react": "^18.3.1",
"react-calendly": "^4.1.1",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.3.1",
"react-i18next": "^13.0.3",
"react-joyride": "^2.7.2",
"react-number-format": "^5.1.4",
"react-pdf": "^7.7.0",
"react-router-dom": "^6.8.2",
"react-scripts": "5.0.1",
"react-spring": "^9.7.1",
"react-tooltip": "^5.22.0",
"react-youtube": "^10.1.0",
"uuid": "^9.0.0",
"web-vitals": "^2.1.4"
},
"engines": {
"node": ">=12.0.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"lint": "eslint "src/**/*.js"",
"lintfix": "eslint --fix "src/**/*.js"",
"test": "react-scripts test",
"eject": "react-scripts eject",
"build:staging": "env-cmd -f .env.staging npm run build"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/cli": "^7.22.5",
"@babel/core": "^7.22.5",
"@babel/eslint-parser": "^7.22.5",
"@babel/plugin-syntax-jsx": "^7.22.5",
"@babel/plugin-syntax-typescript": "^7.22.5",
"@babel/preset-env": "^7.22.5",
"@babel/preset-react": "^7.22.5",
"@tailwindcss/forms": "^0.5.3",
"autoprefixer": "^10.4.13",
"eslint": "^8.42.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"postcss": "^8.4.21",
"tailwindcss": "^3.2.7"
}
}
</code>
// ui/apps/web/package.json
{
"name": "autoguardias-web",
"version": "0.1.0",
"private": true,
"proxy": "http://127.0.0.1:5000",
"dependencies": {
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@headlessui/react": "^1.7.18",
"@heroicons/react": "^2.0.16",
"@mui/material": "^5.12.1",
"@react-pdf/renderer": "^3.3.8",
"@tanstack/react-query": "^4.29.7",
"@tanstack/react-query-devtools": "^4.29.7",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.3.4",
"bcrypt": "^5.1.0",
"bcryptjs": "^2.4.3",
"chart.js": "^4.4.3",
"chartjs-adapter-moment": "^1.0.1",
"chartjs-plugin-annotation": "^3.0.1",
"clsx": "^1.2.1",
"env-cmd": "^10.1.0",
"i18next": "^23.4.1",
"i18next-browser-languagedetector": "^7.1.0",
"install": "^0.13.0",
"moment": "^2.30.1",
"npm": "^10.1.0",
"react": "^18.3.1",
"react-calendly": "^4.1.1",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.3.1",
"react-i18next": "^13.0.3",
"react-joyride": "^2.7.2",
"react-number-format": "^5.1.4",
"react-pdf": "^7.7.0",
"react-router-dom": "^6.8.2",
"react-scripts": "5.0.1",
"react-spring": "^9.7.1",
"react-tooltip": "^5.22.0",
"react-youtube": "^10.1.0",
"uuid": "^9.0.0",
"web-vitals": "^2.1.4"
},
"engines": {
"node": ">=12.0.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"lint": "eslint "src/**/*.js"",
"lintfix": "eslint --fix "src/**/*.js"",
"test": "react-scripts test",
"eject": "react-scripts eject",
"build:staging": "env-cmd -f .env.staging npm run build"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/cli": "^7.22.5",
"@babel/core": "^7.22.5",
"@babel/eslint-parser": "^7.22.5",
"@babel/plugin-syntax-jsx": "^7.22.5",
"@babel/plugin-syntax-typescript": "^7.22.5",
"@babel/preset-env": "^7.22.5",
"@babel/preset-react": "^7.22.5",
"@tailwindcss/forms": "^0.5.3",
"autoprefixer": "^10.4.13",
"eslint": "^8.42.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"postcss": "^8.4.21",
"tailwindcss": "^3.2.7"
}
}
<code> // ui/packages/shared/package.json
"name": "@monorepo/shared",
"@babel/preset-react": "^7.24.7",
"@tanstack/react-query": "^5.51.1",
"expo-router": "~3.5.18",
"i18next-browser-languagedetector": "^8.0.0",
"react-i18next": "^14.1.2",
"react-native": "0.74.3",
"@react-native-async-storage/async-storage": "^1.24.0"
"@babel/preset-react": "^7.24.7"
<code> // ui/packages/shared/package.json
{
"name": "@monorepo/shared",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"@babel/preset-react": "^7.24.7",
"@tanstack/react-query": "^5.51.1",
"expo-router": "~3.5.18",
"axios": "^1.7.2",
"i18next": "^23.11.5",
"i18next-browser-languagedetector": "^8.0.0",
"react-i18next": "^14.1.2",
"react-native": "0.74.3",
"@react-native-async-storage/async-storage": "^1.24.0"
},
"devDependencies": {
"@babel/preset-react": "^7.24.7"
}
}
</code>
// ui/packages/shared/package.json
{
"name": "@monorepo/shared",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"@babel/preset-react": "^7.24.7",
"@tanstack/react-query": "^5.51.1",
"expo-router": "~3.5.18",
"axios": "^1.7.2",
"i18next": "^23.11.5",
"i18next-browser-languagedetector": "^8.0.0",
"react-i18next": "^14.1.2",
"react-native": "0.74.3",
"@react-native-async-storage/async-storage": "^1.24.0"
},
"devDependencies": {
"@babel/preset-react": "^7.24.7"
}
}
Any help is appreciated! Thanks in advance