Replace Webpack with Rsbuild
This commit is contained in:
@@ -71,6 +71,9 @@ artifacts
|
||||
# Expo
|
||||
.expo/
|
||||
dist/
|
||||
|
||||
# Rsbuild
|
||||
.rsbuild/
|
||||
*.jks
|
||||
*.p8
|
||||
*.p12
|
||||
@@ -121,7 +124,9 @@ src/locale/locales/**/*.js
|
||||
bskyogcard/src/assets/fonts/noto-*
|
||||
|
||||
# bskyweb build output
|
||||
bskyweb/static/js/
|
||||
bskyweb/static/media/*.webp
|
||||
bskyweb/static/media/*.jpg
|
||||
bskyweb/static/media/*.png
|
||||
bskyweb/static/media/*.svg
|
||||
bskyweb/static/media/*.woff2
|
||||
|
||||
+8
-7
@@ -26,10 +26,10 @@
|
||||
"android:prod": "expo run:android --variant release",
|
||||
"android:profile": "BSKY_PROFILE=1 expo run:android --variant release",
|
||||
"ios": "expo run:ios",
|
||||
"web": "expo start --web",
|
||||
"web": "rsbuild dev",
|
||||
"use-build-number": "./scripts/useBuildNumberEnv.sh",
|
||||
"use-build-number-with-bump": "./scripts/useBuildNumberEnvWithBump.sh",
|
||||
"build-web": "expo export:web && node ./scripts/post-web-build.js",
|
||||
"build-web": "rsbuild build && node ./scripts/post-web-build.js",
|
||||
"build-all": "yarn intl:build && yarn use-build-number-with-bump eas build --platform all",
|
||||
"build-ios": "yarn use-build-number-with-bump eas build -p ios",
|
||||
"build-android": "yarn use-build-number-with-bump eas build -p android",
|
||||
@@ -68,8 +68,8 @@
|
||||
"export": "npx expo export --dump-sourcemap && yarn upload-native-sourcemaps",
|
||||
"upload-native-sourcemaps": "npx sentry-expo-upload-sourcemaps dist",
|
||||
"make-deploy-bundle": "bash scripts/bundleUpdate.sh",
|
||||
"generate-webpack-stats-file": "EXPO_PUBLIC_GENERATE_STATS=1 yarn build-web",
|
||||
"open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web",
|
||||
"analyze": "BUNDLE_ANALYZE=true yarn build-web",
|
||||
"analyze:open": "BUNDLE_ANALYZE=server yarn build-web",
|
||||
"icons:optimize": "svgo -f ./assets/icons"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -82,7 +82,6 @@
|
||||
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
"@expo/html-elements": "^0.12.5",
|
||||
"@expo/webpack-config": "^19.0.1",
|
||||
"@floating-ui/dom": "^1.6.3",
|
||||
"@floating-ui/react-dom": "^2.0.8",
|
||||
"@formatjs/intl-displaynames": "^6.8.13",
|
||||
@@ -233,10 +232,12 @@
|
||||
"@expo/config-plugins": "~54.0.1",
|
||||
"@lingui/cli": "^4.14.1",
|
||||
"@lingui/macro": "^4.14.1",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
|
||||
"@react-native/babel-preset": "0.81.5",
|
||||
"@react-native/eslint-config": "^0.81.5",
|
||||
"@react-native/typescript-config": "^0.81.5",
|
||||
"@rsbuild/core": "^1.7.2",
|
||||
"@rsbuild/plugin-babel": "^1.0.6",
|
||||
"@rsbuild/plugin-react": "^1.4.2",
|
||||
"@sentry/webpack-plugin": "^3.2.2",
|
||||
"@testing-library/jest-native": "^5.4.3",
|
||||
"@testing-library/react-native": "^13.2.0",
|
||||
@@ -278,7 +279,7 @@
|
||||
"ts-node": "^10.9.1",
|
||||
"ts-plugin-sort-import-suggestions": "^1.0.4",
|
||||
"typescript": "^5.9.2",
|
||||
"webpack-bundle-analyzer": "^4.10.1"
|
||||
"webpack": "^5.104.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"@react-native/babel-preset": "0.81.5",
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
diff --git a/node_modules/react-native-uitextview/ios/RNUITextViewShadow.swift b/node_modules/react-native-uitextview/ios/RNUITextViewShadow.swift
|
||||
index c34ba71..13d576a 100644
|
||||
index c34ba71..b1c4d35 100644
|
||||
--- a/node_modules/react-native-uitextview/ios/RNUITextViewShadow.swift
|
||||
+++ b/node_modules/react-native-uitextview/ios/RNUITextViewShadow.swift
|
||||
@@ -159,13 +159,23 @@ class RNUITextViewShadow: RCTShadowView {
|
||||
let maxSize = CGSize(width: CGFloat(maxWidth), height: CGFloat(MAXFLOAT))
|
||||
let textSize = self.attributedText.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, context: nil)
|
||||
|
||||
|
||||
- var totalLines = self.lineHeight == 0.0 ? 0 : Int(ceil(textSize.height / self.lineHeight))
|
||||
-
|
||||
- if self.numberOfLines != 0, totalLines > self.numberOfLines {
|
||||
@@ -31,3 +31,19 @@ index c34ba71..13d576a 100644
|
||||
return YGSize(width: Float(self.frameSize.width), height: Float(self.frameSize.height))
|
||||
}
|
||||
|
||||
diff --git a/node_modules/react-native-uitextview/src/Text.web.tsx b/node_modules/react-native-uitextview/src/Text.web.tsx
|
||||
new file mode 100644
|
||||
index 0000000..0455396
|
||||
--- /dev/null
|
||||
+++ b/node_modules/react-native-uitextview/src/Text.web.tsx
|
||||
@@ -0,0 +1,10 @@
|
||||
+import {Text, TextProps} from 'react-native'
|
||||
+
|
||||
+// Web fallback - just use the standard Text component
|
||||
+export function UITextView(props: TextProps & {uiTextView?: boolean}) {
|
||||
+ return <Text {...props} />
|
||||
+}
|
||||
+
|
||||
+// These are not used on web, but export them for compatibility
|
||||
+export const RNUITextView = Text
|
||||
+export const RNUITextViewChild = Text
|
||||
|
||||
+57
-14
@@ -9,10 +9,38 @@ const templateFile = path.join(
|
||||
'scripts.html',
|
||||
)
|
||||
|
||||
const {entrypoints} = require(
|
||||
path.join(projectRoot, 'web-build/asset-manifest.json'),
|
||||
)
|
||||
// Support both webpack (asset-manifest.json) and rsbuild (manifest.json) formats
|
||||
function getEntrypoints() {
|
||||
const webpackManifestPath = path.join(
|
||||
projectRoot,
|
||||
'web-build/asset-manifest.json',
|
||||
)
|
||||
const rsbuildManifestPath = path.join(projectRoot, 'web-build/manifest.json')
|
||||
|
||||
// Try webpack format first
|
||||
if (fs.existsSync(webpackManifestPath)) {
|
||||
const manifest = require(webpackManifestPath)
|
||||
if (manifest.entrypoints) {
|
||||
console.log('Using webpack manifest format')
|
||||
return manifest.entrypoints
|
||||
}
|
||||
}
|
||||
|
||||
// Try rsbuild format
|
||||
if (fs.existsSync(rsbuildManifestPath)) {
|
||||
const manifest = require(rsbuildManifestPath)
|
||||
if (manifest.entries?.index?.initial) {
|
||||
console.log('Using rsbuild manifest format')
|
||||
const initial = manifest.entries.index.initial
|
||||
// Combine JS and CSS entrypoints, CSS first for proper loading order
|
||||
return [...(initial.css || []), ...(initial.js || [])]
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('No valid manifest found in web-build/')
|
||||
}
|
||||
|
||||
const entrypoints = getEntrypoints()
|
||||
console.log(`Found ${entrypoints.length} entrypoints`)
|
||||
console.log(`Writing ${templateFile}`)
|
||||
|
||||
@@ -33,16 +61,31 @@ const outputFile = entrypoints
|
||||
.join('\n')
|
||||
fs.writeFileSync(templateFile, outputFile)
|
||||
|
||||
function copyFiles(sourceDir, targetDir) {
|
||||
const files = fs.readdirSync(path.join(projectRoot, sourceDir))
|
||||
files.forEach(file => {
|
||||
const sourcePath = path.join(projectRoot, sourceDir, file)
|
||||
const targetPath = path.join(projectRoot, targetDir, file)
|
||||
fs.copyFileSync(sourcePath, targetPath)
|
||||
console.log(`Copied ${sourcePath} to ${targetPath}`)
|
||||
})
|
||||
function copyFilesRecursive(sourceDir, targetDir) {
|
||||
const sourcePath = path.join(projectRoot, sourceDir)
|
||||
const targetPath = path.join(projectRoot, targetDir)
|
||||
|
||||
// Ensure target directory exists
|
||||
fs.mkdirSync(targetPath, {recursive: true})
|
||||
|
||||
const entries = fs.readdirSync(sourcePath, {withFileTypes: true})
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.join(sourcePath, entry.name)
|
||||
const destPath = path.join(targetPath, entry.name)
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
// Recursively copy subdirectory
|
||||
copyFilesRecursive(
|
||||
path.join(sourceDir, entry.name),
|
||||
path.join(targetDir, entry.name),
|
||||
)
|
||||
} else {
|
||||
fs.copyFileSync(srcPath, destPath)
|
||||
console.log(`Copied ${srcPath} to ${destPath}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
copyFiles('web-build/static/js', 'bskyweb/static/js')
|
||||
copyFiles('web-build/static/css', 'bskyweb/static/css')
|
||||
copyFiles('web-build/static/media', 'bskyweb/static/media')
|
||||
copyFilesRecursive('web-build/static/js', 'bskyweb/static/js')
|
||||
copyFilesRecursive('web-build/static/css', 'bskyweb/static/css')
|
||||
copyFilesRecursive('web-build/static/media', 'bskyweb/static/media')
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
const createExpoWebpackConfigAsync = require('@expo/webpack-config')
|
||||
const {withAlias} = require('@expo/webpack-config/addons')
|
||||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
|
||||
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer')
|
||||
const {sentryWebpackPlugin} = require('@sentry/webpack-plugin')
|
||||
const {version} = require('./package.json')
|
||||
|
||||
const GENERATE_STATS = process.env.EXPO_PUBLIC_GENERATE_STATS === '1'
|
||||
const OPEN_ANALYZER = process.env.EXPO_PUBLIC_OPEN_ANALYZER === '1'
|
||||
|
||||
const reactNativeWebWebviewConfiguration = {
|
||||
test: /postMock.html$/,
|
||||
use: {
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[name].[ext]',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = async function (env, argv) {
|
||||
let config = await createExpoWebpackConfigAsync(env, argv)
|
||||
config = withAlias(config, {
|
||||
'react-native$': 'react-native-web',
|
||||
'react-native-webview': 'react-native-web-webview',
|
||||
// Force ESM version
|
||||
'unicode-segmenter/grapheme': require
|
||||
.resolve('unicode-segmenter/grapheme')
|
||||
.replace(/\.cjs$/, '.js'),
|
||||
'react-native-gesture-handler': false, // RNGH should not be used on web, so let's cause a build error if it sneaks in
|
||||
})
|
||||
config.module.rules = [
|
||||
...(config.module.rules || []),
|
||||
reactNativeWebWebviewConfiguration,
|
||||
]
|
||||
if (env.mode === 'development') {
|
||||
config.plugins.push(new ReactRefreshWebpackPlugin())
|
||||
} else {
|
||||
// Support static CDN for chunks
|
||||
config.output.publicPath = 'auto'
|
||||
}
|
||||
|
||||
if (GENERATE_STATS || OPEN_ANALYZER) {
|
||||
config.plugins.push(
|
||||
new BundleAnalyzerPlugin({
|
||||
openAnalyzer: OPEN_ANALYZER,
|
||||
generateStatsFile: true,
|
||||
statsFilename: '../stats.json',
|
||||
analyzerMode: OPEN_ANALYZER ? 'server' : 'json',
|
||||
defaultSizes: 'parsed',
|
||||
}),
|
||||
)
|
||||
}
|
||||
if (process.env.SENTRY_AUTH_TOKEN) {
|
||||
config.plugins.push(
|
||||
sentryWebpackPlugin({
|
||||
org: 'blueskyweb',
|
||||
project: 'app',
|
||||
authToken: process.env.SENTRY_AUTH_TOKEN,
|
||||
release: {
|
||||
// fallback needed for Render.com deployments
|
||||
name: process.env.SENTRY_RELEASE || version,
|
||||
dist: process.env.SENTRY_DIST,
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
return config
|
||||
}
|
||||
Reference in New Issue
Block a user