Replace Webpack with Rsbuild
This commit is contained in:
@@ -71,6 +71,9 @@ artifacts
|
|||||||
# Expo
|
# Expo
|
||||||
.expo/
|
.expo/
|
||||||
dist/
|
dist/
|
||||||
|
|
||||||
|
# Rsbuild
|
||||||
|
.rsbuild/
|
||||||
*.jks
|
*.jks
|
||||||
*.p8
|
*.p8
|
||||||
*.p12
|
*.p12
|
||||||
@@ -121,7 +124,9 @@ src/locale/locales/**/*.js
|
|||||||
bskyogcard/src/assets/fonts/noto-*
|
bskyogcard/src/assets/fonts/noto-*
|
||||||
|
|
||||||
# bskyweb build output
|
# bskyweb build output
|
||||||
|
bskyweb/static/js/
|
||||||
bskyweb/static/media/*.webp
|
bskyweb/static/media/*.webp
|
||||||
bskyweb/static/media/*.jpg
|
bskyweb/static/media/*.jpg
|
||||||
bskyweb/static/media/*.png
|
bskyweb/static/media/*.png
|
||||||
bskyweb/static/media/*.svg
|
bskyweb/static/media/*.svg
|
||||||
|
bskyweb/static/media/*.woff2
|
||||||
|
|||||||
+8
-7
@@ -26,10 +26,10 @@
|
|||||||
"android:prod": "expo run:android --variant release",
|
"android:prod": "expo run:android --variant release",
|
||||||
"android:profile": "BSKY_PROFILE=1 expo run:android --variant release",
|
"android:profile": "BSKY_PROFILE=1 expo run:android --variant release",
|
||||||
"ios": "expo run:ios",
|
"ios": "expo run:ios",
|
||||||
"web": "expo start --web",
|
"web": "rsbuild dev",
|
||||||
"use-build-number": "./scripts/useBuildNumberEnv.sh",
|
"use-build-number": "./scripts/useBuildNumberEnv.sh",
|
||||||
"use-build-number-with-bump": "./scripts/useBuildNumberEnvWithBump.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-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-ios": "yarn use-build-number-with-bump eas build -p ios",
|
||||||
"build-android": "yarn use-build-number-with-bump eas build -p android",
|
"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",
|
"export": "npx expo export --dump-sourcemap && yarn upload-native-sourcemaps",
|
||||||
"upload-native-sourcemaps": "npx sentry-expo-upload-sourcemaps dist",
|
"upload-native-sourcemaps": "npx sentry-expo-upload-sourcemaps dist",
|
||||||
"make-deploy-bundle": "bash scripts/bundleUpdate.sh",
|
"make-deploy-bundle": "bash scripts/bundleUpdate.sh",
|
||||||
"generate-webpack-stats-file": "EXPO_PUBLIC_GENERATE_STATS=1 yarn build-web",
|
"analyze": "BUNDLE_ANALYZE=true yarn build-web",
|
||||||
"open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web",
|
"analyze:open": "BUNDLE_ANALYZE=server yarn build-web",
|
||||||
"icons:optimize": "svgo -f ./assets/icons"
|
"icons:optimize": "svgo -f ./assets/icons"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -82,7 +82,6 @@
|
|||||||
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
|
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
|
||||||
"@emoji-mart/react": "^1.1.1",
|
"@emoji-mart/react": "^1.1.1",
|
||||||
"@expo/html-elements": "^0.12.5",
|
"@expo/html-elements": "^0.12.5",
|
||||||
"@expo/webpack-config": "^19.0.1",
|
|
||||||
"@floating-ui/dom": "^1.6.3",
|
"@floating-ui/dom": "^1.6.3",
|
||||||
"@floating-ui/react-dom": "^2.0.8",
|
"@floating-ui/react-dom": "^2.0.8",
|
||||||
"@formatjs/intl-displaynames": "^6.8.13",
|
"@formatjs/intl-displaynames": "^6.8.13",
|
||||||
@@ -233,10 +232,12 @@
|
|||||||
"@expo/config-plugins": "~54.0.1",
|
"@expo/config-plugins": "~54.0.1",
|
||||||
"@lingui/cli": "^4.14.1",
|
"@lingui/cli": "^4.14.1",
|
||||||
"@lingui/macro": "^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/babel-preset": "0.81.5",
|
||||||
"@react-native/eslint-config": "^0.81.5",
|
"@react-native/eslint-config": "^0.81.5",
|
||||||
"@react-native/typescript-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",
|
"@sentry/webpack-plugin": "^3.2.2",
|
||||||
"@testing-library/jest-native": "^5.4.3",
|
"@testing-library/jest-native": "^5.4.3",
|
||||||
"@testing-library/react-native": "^13.2.0",
|
"@testing-library/react-native": "^13.2.0",
|
||||||
@@ -278,7 +279,7 @@
|
|||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"ts-plugin-sort-import-suggestions": "^1.0.4",
|
"ts-plugin-sort-import-suggestions": "^1.0.4",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.2",
|
||||||
"webpack-bundle-analyzer": "^4.10.1"
|
"webpack": "^5.104.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@react-native/babel-preset": "0.81.5",
|
"@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
|
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
|
--- a/node_modules/react-native-uitextview/ios/RNUITextViewShadow.swift
|
||||||
+++ b/node_modules/react-native-uitextview/ios/RNUITextViewShadow.swift
|
+++ b/node_modules/react-native-uitextview/ios/RNUITextViewShadow.swift
|
||||||
@@ -159,13 +159,23 @@ class RNUITextViewShadow: RCTShadowView {
|
@@ -159,13 +159,23 @@ class RNUITextViewShadow: RCTShadowView {
|
||||||
let maxSize = CGSize(width: CGFloat(maxWidth), height: CGFloat(MAXFLOAT))
|
let maxSize = CGSize(width: CGFloat(maxWidth), height: CGFloat(MAXFLOAT))
|
||||||
let textSize = self.attributedText.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, context: nil)
|
let textSize = self.attributedText.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, context: nil)
|
||||||
|
|
||||||
- var totalLines = self.lineHeight == 0.0 ? 0 : Int(ceil(textSize.height / self.lineHeight))
|
- var totalLines = self.lineHeight == 0.0 ? 0 : Int(ceil(textSize.height / self.lineHeight))
|
||||||
-
|
-
|
||||||
- if self.numberOfLines != 0, totalLines > self.numberOfLines {
|
- 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))
|
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',
|
'scripts.html',
|
||||||
)
|
)
|
||||||
|
|
||||||
const {entrypoints} = require(
|
// Support both webpack (asset-manifest.json) and rsbuild (manifest.json) formats
|
||||||
path.join(projectRoot, 'web-build/asset-manifest.json'),
|
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(`Found ${entrypoints.length} entrypoints`)
|
||||||
console.log(`Writing ${templateFile}`)
|
console.log(`Writing ${templateFile}`)
|
||||||
|
|
||||||
@@ -33,16 +61,31 @@ const outputFile = entrypoints
|
|||||||
.join('\n')
|
.join('\n')
|
||||||
fs.writeFileSync(templateFile, outputFile)
|
fs.writeFileSync(templateFile, outputFile)
|
||||||
|
|
||||||
function copyFiles(sourceDir, targetDir) {
|
function copyFilesRecursive(sourceDir, targetDir) {
|
||||||
const files = fs.readdirSync(path.join(projectRoot, sourceDir))
|
const sourcePath = path.join(projectRoot, sourceDir)
|
||||||
files.forEach(file => {
|
const targetPath = path.join(projectRoot, targetDir)
|
||||||
const sourcePath = path.join(projectRoot, sourceDir, file)
|
|
||||||
const targetPath = path.join(projectRoot, targetDir, file)
|
// Ensure target directory exists
|
||||||
fs.copyFileSync(sourcePath, targetPath)
|
fs.mkdirSync(targetPath, {recursive: true})
|
||||||
console.log(`Copied ${sourcePath} to ${targetPath}`)
|
|
||||||
})
|
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')
|
copyFilesRecursive('web-build/static/js', 'bskyweb/static/js')
|
||||||
copyFiles('web-build/static/css', 'bskyweb/static/css')
|
copyFilesRecursive('web-build/static/css', 'bskyweb/static/css')
|
||||||
copyFiles('web-build/static/media', 'bskyweb/static/media')
|
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