diff --git a/Dockerfile.embedr b/Dockerfile.embedr index 5c87122efe..b39d406d45 100644 --- a/Dockerfile.embedr +++ b/Dockerfile.embedr @@ -22,7 +22,7 @@ ENV CI=true COPY . . # -# Generate the JavaScript webpack. NOTE: this will change +# Generate the JavaScript bundle. NOTE: this will change # RUN mkdir --parents $NVM_DIR && \ wget \ diff --git a/babel.config.js b/babel.config.js index b3875d02ab..5f16f38fa0 100644 --- a/babel.config.js +++ b/babel.config.js @@ -41,7 +41,6 @@ module.exports = function (api) { plugins: [ '@lingui/babel-plugin-lingui-macro', ['babel-plugin-react-compiler', {target: '19'}], - 'module:react-native-dotenv', // used by web build! can remove when we drop webpack [ 'module-resolver', { diff --git a/package.json b/package.json index d2b42b30e4..f1446c2f49 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,6 @@ "@emoji-mart/react": "^1.1.1", "@expo/html-elements": "^0.12.5", "@expo/metro-runtime": "~57.0.8", - "@expo/webpack-config": "^19.0.1", "@floating-ui/dom": "^1.6.3", "@floating-ui/react-dom": "^2.0.8", "@formatjs/intl-displaynames": "^7.3.9", @@ -274,11 +273,9 @@ "@crowdin/cli": "^4.14.1", "@lingui/babel-plugin-lingui-macro": "^5.9.2", "@lingui/cli": "^5.9.2", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", "@react-native/babel-preset": "0.86.0", "@react-native/jest-preset": "0.86.0", "@react-native/typescript-config": "^0.81.5", - "@sentry/webpack-plugin": "^3.2.2", "@testing-library/react-native": "^13.2.0", "@types/jest": "29.5.14", "@types/lodash.chunk": "^4.2.7", @@ -295,7 +292,6 @@ "eslint-plugin-bsky-internal": "link:lint-rules", "eslint-plugin-react-native-a11y": "^3.5.1", "eslint-plugin-simple-import-sort": "^13.0.0", - "file-loader": "6.2.0", "globals": "^17.0.0", "husky": "^9.1.7", "is-ci": "^3.0.1", @@ -306,12 +302,9 @@ "oxlint": "^1.73.0", "oxlint-tsgolint": "^7.0.2001", "prettier": "3.9.6", - "react-native-dotenv": "^3.4.11", - "react-refresh": "^0.14.0", "svgo": "^4.0.2", "ts-plugin-sort-import-suggestions": "^1.0.4", - "typescript": "npm:@typescript/typescript6@^6.0.2", - "webpack-bundle-analyzer": "^4.10.1" + "typescript": "npm:@typescript/typescript6@^6.0.2" }, "jest": { "preset": "jest-expo/ios", diff --git a/src/lib/media/video/mediabunny.ts b/src/lib/media/video/mediabunny.ts index edcbc805c1..7080df9a6d 100644 --- a/src/lib/media/video/mediabunny.ts +++ b/src/lib/media/video/mediabunny.ts @@ -3,13 +3,13 @@ type MediaBunnyModule = typeof import('mediabunny') let promise: Promise | undefined /** - * Lazily loads mediabunny into its own webpack chunk and caches the resulting + * Lazily loads mediabunny into its own async chunk and caches the resulting * promise so the module is fetched at most once. If the import fails (e.g. a * transient chunk-load error), the cached promise is reset so a later call can * retry the load. */ export function loadMediaBunny(): Promise { - promise ??= import(/* webpackChunkName: "mediabunny" */ 'mediabunny').catch( + promise ??= import('mediabunny').catch( e => { // reset so a later attempt can retry after a transient chunk-load failure promise = undefined diff --git a/tsconfig.json b/tsconfig.json index eab917283b..796279d035 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,5 +24,5 @@ } ] }, - "exclude": ["bskyweb", "bskyembed", "web-build"] + "exclude": ["bskyweb", "bskyembed", "web-build", "dist"] } diff --git a/web/index.html b/web/index.html deleted file mode 100644 index 67500c1d21..0000000000 --- a/web/index.html +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - - - - %WEB_TITLE% - - - - - - - - - - - - - -
- - -
-
-
- - diff --git a/web/reanimatedWebUtilsShim.js b/web/reanimatedWebUtilsShim.js deleted file mode 100644 index 645ad9ec1e..0000000000 --- a/web/reanimatedWebUtilsShim.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Replacement for react-native-reanimated's js-reanimated/webUtils.web.js, - * wired up via a webpack alias in webpack.config.js. - * - * The original file declares ES module exports but populates them with bare - * CommonJS `require()` calls wrapped in try/catch. Webpack parses the file as - * ESM and therefore leaves those `require`s untouched, so in the browser they - * throw ReferenceError, the try/catch swallows it, and createReactDOMStyle & - * co. stay undefined. That silently disables reanimated's DOM update path and - * _updatePropsJS later crashes with "Cannot convert undefined or null to - * object" on Object.keys(component.props). Importing the same react-native-web - * internals statically fixes the resolution. - */ -import createReactDOMStyle from 'react-native-web/dist/exports/StyleSheet/compiler/createReactDOMStyle' -import { - createTextShadowValue, - createTransformValue, -} from 'react-native-web/dist/exports/StyleSheet/preprocess' - -export {createReactDOMStyle, createTextShadowValue, createTransformValue} diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 24cfb2097a..0000000000 --- a/webpack.config.js +++ /dev/null @@ -1,168 +0,0 @@ -const path = require('path') - -const createExpoWebpackConfigAsync = require('@expo/webpack-config') -const webpack = require('webpack') -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]', - }, - }, -} - -// Walk a rule tree and wrap source-map-loader's filterSourceMappingUrl to -// drop sourcemap references matching the given path pattern. Mutates in place. -function patchSourceMapFilter(rules, pathPattern) { - if (!rules) return - for (const rule of rules) { - if (!rule || typeof rule !== 'object') continue - if (rule.oneOf) patchSourceMapFilter(rule.oneOf, pathPattern) - if (rule.rules) patchSourceMapFilter(rule.rules, pathPattern) - const uses = Array.isArray(rule.use) ? rule.use : rule.use ? [rule.use] : [] - for (const use of uses) { - if (!use?.loader?.includes('source-map-loader')) continue - const prev = use.options?.filterSourceMappingUrl - use.options = { - ...use.options, - filterSourceMappingUrl(url, resourcePath) { - if (pathPattern.test(resourcePath)) return 'remove' - return prev ? prev(url, resourcePath) : true - }, - } - } - } -} - -module.exports = async function (env, argv) { - env.babel = { - dangerouslyAddModulePathsToTranspile: ['@bsky.app/expo'], - } - let config = await createExpoWebpackConfigAsync(env, argv) - /* - * Expo only registers its own internal config as a cache build dependency, - * so changes to this file (e.g. aliases) don't invalidate the persistent - * filesystem cache and stale module resolutions get reused. Register this - * file so edits here always bust the cache. - */ - if (config.cache?.buildDependencies) { - config.cache.buildDependencies.config = [ - ...(config.cache.buildDependencies.config || []), - __filename, - ] - } - config = withAlias(config, { - 'react-native$': 'react-native-web', - 'react-native-webview': 'react-native-web-webview', - 'react-native-gesture-handler': false, // RNGH should not be used on web, so let's cause a build error if it sneaks in - '@sentry-internal/replay': false, // not used, ~300kb of dead weight - /* - * @sentry/react-native's tracing integration probes for expo-router via a - * try/catch require(). We don't use expo-router, so the module can't - * resolve and webpack warns on every build. Stubbing it to an empty - * module makes the probe return null (`mod?.store ?? null`) silently. - */ - 'expo-router/build/global-state/router-store': false, - /* - * react-native-svg's fetchData util imports the ~55KB `buffer` polyfill, - * but is only needed by SvgUri/SvgXml remote loading, which we don't use. - * Stubbing it out makes fetchText undefined, so it throws if ever called. - * The alias key must be an absolute path because the package imports it - * via a relative path internally. - */ - [path.join( - __dirname, - 'node_modules/react-native-svg/lib/module/utils/fetchData', - )]: false, - /* - * reanimated's webUtils.web.js mixes ESM exports with bare CommonJS - * require() calls in try/catch, which webpack leaves untranspiled - they - * throw at runtime and createReactDOMStyle & co. silently stay undefined, - * making _updatePropsJS crash on every animated style update. The shim - * imports the same react-native-web internals statically. See the shim - * file for details. - */ - [path.join( - __dirname, - 'node_modules/react-native-reanimated/lib/module/ReanimatedModule/js-reanimated/webUtils', - )]: path.join(__dirname, 'web/reanimatedWebUtilsShim.js'), - }) - - /* - * expo-font's serverContext.web.js imports `node:async_hooks` for SSR-only - * font collection, but webpack can't resolve `node:` URIs for web targets. - * Every call site is guarded by `typeof window === 'undefined'`, so in the - * browser bundle the module is dead code - strip the scheme prefix and stub - * the builtin out with an empty module. - */ - config.plugins.push( - new webpack.NormalModuleReplacementPlugin( - /^node:async_hooks$/, - resource => { - resource.request = 'async_hooks' - }, - ), - ) - config.resolve.fallback = {...config.resolve.fallback, async_hooks: false} - - // react-native-uuid ships sourceMappingURL comments but no .map files. - patchSourceMapFilter(config.module.rules, /react-native-uuid/) - config.module.rules = [ - ...(config.module.rules || []), - reactNativeWebWebviewConfiguration, - ] - if (env.mode === 'development') { - config.plugins.push(new ReactRefreshWebpackPlugin()) - // Reap zombie HMR WebSocket connections that linger after refresh. - // Without this, dead sockets exhaust the browser's per-origin connection - // pool and the dev server stops responding. - config.devServer.onListening = devServer => { - devServer.server.on('connection', socket => { - socket.setTimeout(10000) - socket.on('timeout', () => socket.destroy()) - }) - } - } 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', - // reasons balloon stats.json past Node's max string length, breaking bundle-size-diff in CI - statsOptions: OPEN_ANALYZER ? null : {reasons: false}, - }), - ) - } - 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 -}