├── .nvmrc ├── example ├── .watchmanconfig ├── src │ ├── navigators │ │ ├── index.ts │ │ ├── types.ts │ │ └── RootStack.tsx │ ├── screens │ │ ├── index.ts │ │ ├── MainScreen.tsx │ │ └── IntroScreen.tsx │ ├── constants │ │ └── index.ts │ ├── assets │ │ ├── logo.png │ │ ├── logo@1.5x.png │ │ ├── logo@1x.png │ │ ├── logo@2x.png │ │ ├── logo@3x.png │ │ ├── logo@0.75x.png │ │ └── logo.svg │ ├── components │ │ ├── index.ts │ │ ├── Section.tsx │ │ ├── Button.tsx │ │ └── Fade.tsx │ ├── theme │ │ ├── colors.ts │ │ └── index.ts │ ├── types │ │ ├── svg.d.ts │ │ └── images.d.ts │ └── App.tsx ├── app.json ├── .bundle │ └── config ├── android │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ └── drawable │ │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ ├── java │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ │ └── MainApplication.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── release │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ReactNativeFlipper.java │ │ ├── debug.keystore │ │ └── proguard-rules.pro │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── build.gradle │ └── gradle.properties ├── ios │ ├── example │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── AppDelegate.mm │ │ ├── PrivacyInfo.xcprivacy │ │ └── Info.plist │ ├── example.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── .xcode.env │ ├── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m │ └── Podfile ├── index.js ├── tsconfig.json ├── babel.config.js ├── __tests__ │ └── App.test.tsx ├── metro.config.js ├── Gemfile ├── index.web.js ├── tests │ └── setup.ts ├── jest.config.js ├── react-native-esbuild.config.js ├── .eslintrc.js ├── .gitignore └── package.json ├── .prettierignore ├── docs ├── .gitignore ├── public │ ├── logo.png │ ├── favicon.ico │ ├── social-card.png │ ├── logo-without-background.png │ └── logo.svg ├── pages │ ├── getting-started │ │ ├── _meta.json │ │ └── installation.md │ ├── _app.tsx │ ├── configuration │ │ ├── _meta.json │ │ ├── custom-plugins.md │ │ └── custom-transformation.md │ ├── limitations │ │ └── hot-module-replacement.md │ ├── _meta.json │ ├── native-setup │ │ ├── android.md │ │ └── ios.md │ ├── troubleshooting │ │ ├── basic.md │ │ └── new-architecture.mdx │ └── jest │ │ └── transformer.md ├── next-env.d.ts ├── next.config.js ├── tsconfig.json ├── package.json ├── styles │ └── global.css └── theme.config.tsx ├── packages ├── jest │ ├── .gitignore │ ├── lib │ │ ├── transformer │ │ │ └── index.ts │ │ ├── index.ts │ │ └── types.ts │ ├── tsconfig.json │ ├── build │ │ └── index.js │ ├── README.md │ ├── CHANGELOG.md │ └── package.json ├── core │ ├── lib │ │ ├── watcher │ │ │ └── index.ts │ │ ├── bundler │ │ │ ├── cache │ │ │ │ ├── index.ts │ │ │ │ ├── __tests__ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ └── cache.test.ts.snap │ │ │ │ └── CacheController.ts │ │ │ ├── plugins │ │ │ │ ├── metafilePlugin │ │ │ │ │ ├── index.ts │ │ │ │ │ └── metafilePlugin.ts │ │ │ │ ├── statusPlugin │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── templates.ts │ │ │ │ │ └── statusPlugin.ts │ │ │ │ └── index.ts │ │ │ ├── storages │ │ │ │ ├── index.ts │ │ │ │ ├── Storage.ts │ │ │ │ ├── SharedStorage.ts │ │ │ │ └── CacheStorage.ts │ │ │ ├── index.ts │ │ │ ├── helpers │ │ │ │ ├── index.ts │ │ │ │ ├── async.ts │ │ │ │ ├── __tests__ │ │ │ │ │ └── config.test.ts │ │ │ │ └── internal.ts │ │ │ ├── logo.ts │ │ │ └── events │ │ │ │ └── index.ts │ │ ├── shared.ts │ │ └── index.ts │ ├── tsconfig.json │ ├── build │ │ └── index.js │ ├── README.md │ ├── static │ │ └── templates │ │ │ └── index.html │ └── package.json ├── symbolicate │ ├── lib │ │ ├── helpers │ │ │ ├── index.ts │ │ │ └── codeStack.ts │ │ ├── index.ts │ │ └── types.ts │ ├── README.md │ ├── tsconfig.json │ ├── build │ │ └── index.js │ └── package.json ├── transformer │ ├── lib │ │ ├── helpers │ │ │ ├── index.ts │ │ │ └── transformer.ts │ │ ├── transformer │ │ │ ├── sucrase │ │ │ │ ├── index.ts │ │ │ │ └── sucrase.ts │ │ │ ├── swc │ │ │ │ ├── index.ts │ │ │ │ ├── swc.ts │ │ │ │ └── presets.ts │ │ │ ├── babel │ │ │ │ ├── index.ts │ │ │ │ ├── presets.ts │ │ │ │ └── babel.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ └── pipelines │ │ │ ├── index.ts │ │ │ └── pipeline.ts │ ├── build │ │ └── index.js │ ├── tsconfig.json │ ├── README.md │ ├── types │ │ └── lib.d.ts │ └── package.json ├── plugins │ ├── lib │ │ ├── reactNativeWebPlugin │ │ │ ├── helpers │ │ │ │ ├── index.ts │ │ │ │ └── template.ts │ │ │ ├── index.ts │ │ │ └── reactNativeWebPlugin.ts │ │ ├── reactNativeRuntimeTransformPlugin │ │ │ ├── helpers │ │ │ │ ├── index.ts │ │ │ │ └── caches.ts │ │ │ └── index.ts │ │ ├── assetRegisterPlugin │ │ │ ├── helpers │ │ │ │ ├── index.ts │ │ │ │ └── fs.ts │ │ │ └── index.ts │ │ ├── svgTransformPlugin │ │ │ ├── index.ts │ │ │ └── svgTransformPlugin.ts │ │ ├── shared.ts │ │ ├── index.ts │ │ └── types.ts │ ├── README.md │ ├── build │ │ └── index.js │ ├── tsconfig.json │ └── package.json ├── config │ ├── lib │ │ ├── common │ │ │ ├── index.ts │ │ │ ├── __tests__ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── common.test.ts.snap │ │ │ │ └── common.test.ts │ │ │ └── core.ts │ │ ├── index.ts │ │ ├── shares.ts │ │ └── types.ts │ ├── README.md │ ├── tsconfig.json │ ├── build │ │ └── index.js │ └── package.json ├── utils │ ├── README.md │ ├── lib │ │ ├── env.ts │ │ └── index.ts │ ├── tsconfig.json │ ├── build │ │ └── index.js │ └── package.json ├── cli │ ├── lib │ │ ├── constants │ │ │ └── index.ts │ │ ├── helpers │ │ │ ├── index.ts │ │ │ ├── errors.ts │ │ │ ├── cli.ts │ │ │ └── stdin.ts │ │ ├── shared.ts │ │ ├── commands │ │ │ ├── index.ts │ │ │ ├── cache.ts │ │ │ ├── serve.ts │ │ │ ├── bundle.ts │ │ │ └── start.ts │ │ ├── types.ts │ │ ├── presets │ │ │ └── index.ts │ │ ├── schema │ │ │ └── index.ts │ │ └── index.ts │ ├── __tests__ │ │ └── cli.test.ts │ ├── README.md │ ├── tsconfig.json │ ├── build │ │ └── index.js │ ├── scripts │ │ └── xcode.sh │ └── package.json ├── internal │ ├── README.md │ ├── lib │ │ ├── index.ts │ │ ├── __tests__ │ │ │ ├── __snapshots__ │ │ │ │ └── presets.test.ts.snap │ │ │ └── presets.test.ts │ │ ├── types.ts │ │ ├── helpers.ts │ │ └── defaults.ts │ ├── tsconfig.json │ ├── build │ │ └── index.js │ └── package.json └── dev-server │ ├── lib │ ├── constants │ │ └── index.ts │ ├── helpers │ │ ├── index.ts │ │ └── request.ts │ ├── index.ts │ ├── shared.ts │ ├── server │ │ ├── index.ts │ │ └── DevServer.ts │ └── middlewares │ │ ├── index.ts │ │ ├── indexPage.ts │ │ ├── __tests__ │ │ ├── mocks.ts │ │ └── serveAsset.test.ts │ │ └── symbolicate.ts │ ├── build │ └── index.js │ ├── tsconfig.json │ ├── README.md │ ├── types │ └── lib.d.ts │ └── package.json ├── .yarnrc.yml ├── .eslintignore ├── logo.png ├── main.png ├── .husky └── commit-msg ├── test └── setup.ts ├── .vscode └── settings.json ├── .prettierrc ├── nx.json ├── .github └── ISSUE_TEMPLATE │ ├── register-build-preset-request.md │ └── bug_report.md ├── shared └── index.mjs ├── .gitignore ├── types └── index.d.ts ├── .yarn └── patches │ └── dripsy-npm-4.3.8-208587309d.patch ├── LICENSE ├── tsconfig.json ├── package.json ├── .eslintrc.js ├── README.md └── jest.config.ts /.nvmrc: -------------------------------------------------------------------------------- 1 | v22.16.0 2 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.lock 2 | *.json 3 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | node_modules 3 | -------------------------------------------------------------------------------- /packages/jest/.gitignore: -------------------------------------------------------------------------------- 1 | # preset 2 | jest-preset.js 3 | -------------------------------------------------------------------------------- /example/src/navigators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './RootStack'; 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | enableGlobalCache: false 4 | -------------------------------------------------------------------------------- /packages/core/lib/watcher/index.ts: -------------------------------------------------------------------------------- 1 | export * from './FileSystemWatcher'; 2 | -------------------------------------------------------------------------------- /packages/symbolicate/lib/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './codeStack'; 2 | -------------------------------------------------------------------------------- /packages/transformer/lib/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './transformer'; 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # library bundles 2 | **/dist/* 3 | 4 | # docusaurus 5 | docs 6 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/cache/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CacheController'; 2 | -------------------------------------------------------------------------------- /packages/jest/lib/transformer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './createTransformer'; 2 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "displayName": "example" 4 | } 5 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/logo.png -------------------------------------------------------------------------------- /main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/main.png -------------------------------------------------------------------------------- /packages/transformer/lib/transformer/sucrase/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sucrase'; 2 | -------------------------------------------------------------------------------- /example/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /packages/plugins/lib/reactNativeWebPlugin/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './template'; 2 | -------------------------------------------------------------------------------- /example/src/screens/index.ts: -------------------------------------------------------------------------------- 1 | export * from './MainScreen'; 2 | export * from './IntroScreen'; 3 | -------------------------------------------------------------------------------- /packages/config/lib/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './core'; 2 | export * from '../shares'; 3 | -------------------------------------------------------------------------------- /packages/plugins/lib/reactNativeRuntimeTransformPlugin/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './caches'; 2 | -------------------------------------------------------------------------------- /packages/utils/README.md: -------------------------------------------------------------------------------- 1 | # `@react-native-esbuild/utils` 2 | 3 | > Utilities for @react-native-esbuild 4 | -------------------------------------------------------------------------------- /docs/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/docs/public/logo.png -------------------------------------------------------------------------------- /example/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | export const ROOT_FONT_SIZE = 16; 2 | export const CONTAINER_MAX_WIDTH = 600; 3 | -------------------------------------------------------------------------------- /packages/plugins/lib/assetRegisterPlugin/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fs'; 2 | export * from './path'; 3 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit 5 | -------------------------------------------------------------------------------- /docs/pages/getting-started/_meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "installation": "Installation", 3 | "commands": "Commands" 4 | } 5 | -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/docs/public/favicon.ico -------------------------------------------------------------------------------- /packages/core/lib/bundler/plugins/metafilePlugin/index.ts: -------------------------------------------------------------------------------- 1 | export { createMetafilePlugin } from './metafilePlugin'; 2 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/plugins/statusPlugin/index.ts: -------------------------------------------------------------------------------- 1 | export { createBuildStatusPlugin } from './statusPlugin'; 2 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/storages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CacheStorage'; 2 | export * from './SharedStorage'; 3 | -------------------------------------------------------------------------------- /packages/plugins/lib/svgTransformPlugin/index.ts: -------------------------------------------------------------------------------- 1 | export { createSvgTransformPlugin } from './svgTransformPlugin'; 2 | -------------------------------------------------------------------------------- /docs/public/social-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/docs/public/social-card.png -------------------------------------------------------------------------------- /example/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/src/assets/logo.png -------------------------------------------------------------------------------- /example/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Button'; 2 | export * from './Fade'; 3 | export * from './Section'; 4 | -------------------------------------------------------------------------------- /packages/cli/lib/constants/index.ts: -------------------------------------------------------------------------------- 1 | import pkg from '../../package.json'; 2 | 3 | export const VERSION = pkg.version; 4 | -------------------------------------------------------------------------------- /packages/cli/lib/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cli'; 2 | export * from './errors'; 3 | export * from './stdin'; 4 | -------------------------------------------------------------------------------- /packages/config/README.md: -------------------------------------------------------------------------------- 1 | # `@react-native-esbuild/config` 2 | 3 | > Shared configs for @react-native-esbuild/config 4 | -------------------------------------------------------------------------------- /packages/internal/README.md: -------------------------------------------------------------------------------- 1 | # `@react-native-esbuild/internal` 2 | 3 | > Internal configs and helpers for react-native 4 | -------------------------------------------------------------------------------- /packages/plugins/lib/assetRegisterPlugin/index.ts: -------------------------------------------------------------------------------- 1 | export { createAssetRegisterPlugin } from './assetRegisterPlugin'; 2 | -------------------------------------------------------------------------------- /packages/plugins/lib/reactNativeWebPlugin/index.ts: -------------------------------------------------------------------------------- 1 | export { createReactNativeWebPlugin } from './reactNativeWebPlugin'; 2 | -------------------------------------------------------------------------------- /packages/symbolicate/lib/index.ts: -------------------------------------------------------------------------------- 1 | export { symbolicateStackTrace } from './symbolicate'; 2 | export * from './helpers'; 3 | -------------------------------------------------------------------------------- /packages/transformer/lib/transformer/swc/index.ts: -------------------------------------------------------------------------------- 1 | export * from './swc'; 2 | export * as swcPresets from './presets'; 3 | -------------------------------------------------------------------------------- /packages/cli/__tests__/cli.test.ts: -------------------------------------------------------------------------------- 1 | describe('', () => { 2 | it('', () => { 3 | expect(1).toEqual(1); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/index.ts: -------------------------------------------------------------------------------- 1 | export { ReactNativeEsbuildBundler } from './bundler'; 2 | export type * from './events'; 3 | -------------------------------------------------------------------------------- /packages/dev-server/lib/constants/index.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_PORT = 8081; 2 | export const DEFAULT_HOST = 'localhost'; 3 | -------------------------------------------------------------------------------- /packages/dev-server/lib/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './request'; 2 | export { BundleResponse } from './BundleResponse'; 3 | -------------------------------------------------------------------------------- /packages/internal/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './presets'; 2 | export * from './defaults'; 3 | export type * from './types'; 4 | -------------------------------------------------------------------------------- /packages/transformer/lib/transformer/babel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './babel'; 2 | export * as babelPresets from './presets'; 3 | -------------------------------------------------------------------------------- /test/setup.ts: -------------------------------------------------------------------------------- 1 | import 'node-self'; 2 | 3 | beforeAll(() => { 4 | process.cwd = jest.fn().mockReturnValue('/user'); 5 | }); 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | example 3 | 4 | -------------------------------------------------------------------------------- /example/src/assets/logo@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/src/assets/logo@1.5x.png -------------------------------------------------------------------------------- /example/src/assets/logo@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/src/assets/logo@1x.png -------------------------------------------------------------------------------- /example/src/assets/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/src/assets/logo@2x.png -------------------------------------------------------------------------------- /example/src/assets/logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/src/assets/logo@3x.png -------------------------------------------------------------------------------- /packages/core/lib/bundler/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './async'; 2 | export * from './config'; 3 | export * from './internal'; 4 | -------------------------------------------------------------------------------- /packages/symbolicate/README.md: -------------------------------------------------------------------------------- 1 | # `@react-native-esbuild/symbolicate` 2 | 3 | > Symbolicate helpers for @react-native-esbuild 4 | -------------------------------------------------------------------------------- /packages/transformer/lib/transformer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './babel'; 2 | export * from './swc'; 3 | export * from './sucrase'; 4 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/src/assets/logo@0.75x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/src/assets/logo@0.75x.png -------------------------------------------------------------------------------- /packages/cli/lib/shared.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@react-native-esbuild/utils'; 2 | 3 | export const logger = new Logger('cli'); 4 | -------------------------------------------------------------------------------- /packages/core/lib/shared.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@react-native-esbuild/utils'; 2 | 3 | export const logger = new Logger('core'); 4 | -------------------------------------------------------------------------------- /packages/dev-server/lib/index.ts: -------------------------------------------------------------------------------- 1 | export { ReactNativeAppServer, ReactNativeWebServer } from './server'; 2 | export type * from './types'; 3 | -------------------------------------------------------------------------------- /packages/plugins/lib/shared.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@react-native-esbuild/utils'; 2 | 3 | export const logger = new Logger('plugins'); 4 | -------------------------------------------------------------------------------- /docs/public/logo-without-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/docs/public/logo-without-background.png -------------------------------------------------------------------------------- /packages/dev-server/lib/shared.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@react-native-esbuild/utils'; 2 | 3 | export const logger = new Logger('dev-server'); 4 | -------------------------------------------------------------------------------- /packages/cli/lib/commands/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bundle'; 2 | export * from './cache'; 3 | export * from './serve'; 4 | export * from './start'; 5 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /packages/jest/lib/index.ts: -------------------------------------------------------------------------------- 1 | import { createTransformer } from './transformer'; 2 | 3 | const factory = { createTransformer }; 4 | 5 | export default factory; 6 | -------------------------------------------------------------------------------- /packages/plugins/lib/reactNativeRuntimeTransformPlugin/index.ts: -------------------------------------------------------------------------------- 1 | export { createReactNativeRuntimeTransformPlugin } from './reactNativeRuntimeTransformPlugin'; 2 | -------------------------------------------------------------------------------- /docs/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../styles/global.css'; 2 | 3 | export default function App({ Component, pageProps }) { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /packages/config/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common'; 2 | export * from './shares'; 3 | export { OptionFlag } from './types'; 4 | export type * from './types'; 5 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/plugins/index.ts: -------------------------------------------------------------------------------- 1 | export { createMetafilePlugin } from './metafilePlugin'; 2 | export { createBuildStatusPlugin } from './statusPlugin'; 3 | -------------------------------------------------------------------------------- /packages/transformer/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './transformer'; 2 | export * from './pipelines'; 3 | export * from './helpers'; 4 | export type * from './types'; 5 | -------------------------------------------------------------------------------- /packages/dev-server/lib/server/index.ts: -------------------------------------------------------------------------------- 1 | export { ReactNativeAppServer } from './ReactNativeAppServer'; 2 | export { ReactNativeWebServer } from './ReactNativeWebServer'; 3 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | # `@react-native-esbuild/cli` 2 | 3 | > CLI tool for @react-native-esbuild 4 | 5 | [README](https://github.com/leegeunhyeok/react-native-esbuild) 6 | -------------------------------------------------------------------------------- /packages/transformer/lib/pipelines/index.ts: -------------------------------------------------------------------------------- 1 | export { AsyncTransformPipeline } from './AsyncTransformPipeline'; 2 | export { SyncTransformPipeline } from './SyncTransformPipeline'; 3 | -------------------------------------------------------------------------------- /packages/jest/lib/types.ts: -------------------------------------------------------------------------------- 1 | import type { SwcJestPresetOptions } from '@react-native-esbuild/transformer'; 2 | 3 | export type TransformerConfig = Omit; 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "deepmerge", 4 | "metafile", 5 | "middlewares", 6 | "dripsy" 7 | ], 8 | "typescript.tsdk": "node_modules/typescript/lib" 9 | } -------------------------------------------------------------------------------- /docs/pages/configuration/_meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "basic-configuration": "Basic Configuration", 3 | "custom-transformation": "Custom Transformation", 4 | "custom-plugins": "Custom Plugins" 5 | } 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leegeunhyeok/react-native-esbuild/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import { App } from './src/App'; 3 | import { name as appName } from './app.json'; 4 | 5 | AppRegistry.registerComponent(appName, () => App); 6 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/cache/__tests__/__snapshots__/cache.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CacheController getCacheDirectory should match snapshot 1`] = `"/cache"`; 4 | -------------------------------------------------------------------------------- /packages/plugins/README.md: -------------------------------------------------------------------------------- 1 | # `@react-native-esbuild/plugins` 2 | 3 | > Base plugins for @react-native-esbuild 4 | 5 | ## Custom plugins 6 | 7 | [README](https://github.com/leegeunhyeok/react-native-esbuild) 8 | -------------------------------------------------------------------------------- /packages/utils/lib/env.ts: -------------------------------------------------------------------------------- 1 | export const isCI = (): boolean => 2 | process.env.CI?.toLocaleLowerCase() === 'true' || process.env.CI === '1'; 3 | 4 | export const isTTY = (): boolean => process.stdout.isTTY; 5 | -------------------------------------------------------------------------------- /packages/dev-server/lib/middlewares/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hotReload'; 2 | export * from './indexPage'; 3 | export * from './serveAsset'; 4 | export * from './serveBundle'; 5 | export * from './symbolicate'; 6 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "jsx": "react-native" 6 | }, 7 | "include": ["src/**/*", "tests/*", "__tests__/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/config/lib/common/__tests__/__snapshots__/common.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`getBuildStatusCachePath should match snapshot 1`] = `"/root/.rne/build-status.json"`; 4 | -------------------------------------------------------------------------------- /packages/cli/lib/types.ts: -------------------------------------------------------------------------------- 1 | export interface RawArgv { 2 | [x: string]: unknown; 3 | _: (string | number)[]; 4 | $0: string; 5 | } 6 | 7 | export type Command = (argv: RawArgv, subCommand?: string) => Promise; 8 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "./dist", 6 | }, 7 | "include": ["./lib"], 8 | "exclude": ["./dist"] 9 | } 10 | -------------------------------------------------------------------------------- /docs/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /example/src/theme/colors.ts: -------------------------------------------------------------------------------- 1 | export const colors = { 2 | $esbuild: '#ffcf00', 3 | $danger: '#f44336', 4 | $text_primary: '#222222', 5 | $text_secondary: '#3a3a3a', 6 | $white: '#ffffff', 7 | $black: '#000000', 8 | } as const; 9 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | plugins: [ 4 | ['@babel/plugin-transform-private-methods', { loose: true }], 5 | 'react-native-reanimated/plugin', 6 | ], 7 | }; 8 | -------------------------------------------------------------------------------- /packages/plugins/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './assetRegisterPlugin'; 2 | export * from './reactNativeRuntimeTransformPlugin'; 3 | export * from './reactNativeWebPlugin'; 4 | export * from './svgTransformPlugin'; 5 | export type * from './types'; 6 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/storages/Storage.ts: -------------------------------------------------------------------------------- 1 | export abstract class Storage { 2 | protected data = new Map(); 3 | public abstract get(key: number): StorageData; 4 | public abstract clearAll(): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "trailingComma": "all", 6 | "bracketSpacing": true, 7 | "semi": true, 8 | "useTabs": false, 9 | "endOfLine": "lf", 10 | "arrowParens": "always" 11 | } 12 | -------------------------------------------------------------------------------- /example/ios/example/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/utils/lib/index.ts: -------------------------------------------------------------------------------- 1 | import colors from 'colors'; 2 | import { isCI, isTTY } from './env'; 3 | 4 | (isCI() || !isTTY()) && colors.disable(); 5 | 6 | export { default as colors } from 'colors'; 7 | export * from './env'; 8 | export { Logger, LogLevel } from './logger'; 9 | -------------------------------------------------------------------------------- /example/src/types/svg.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | // eslint-disable-next-line @typescript-eslint/consistent-type-imports 3 | const src: React.FC; 4 | // eslint-disable-next-line import/no-default-export 5 | export default src; 6 | } 7 | -------------------------------------------------------------------------------- /example/src/types/images.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png' { 2 | // eslint-disable-next-line @typescript-eslint/consistent-type-imports 3 | const value: import('react-native').ImageSourcePropType; 4 | // eslint-disable-next-line import/no-default-export 5 | export default value; 6 | } 7 | -------------------------------------------------------------------------------- /packages/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "./dist", 6 | "declaration": true, 7 | "emitDeclarationOnly": true 8 | }, 9 | "include": ["./lib"], 10 | "exclude": ["./dist"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "./dist", 6 | "declaration": true, 7 | "emitDeclarationOnly": true 8 | }, 9 | "include": ["./lib"], 10 | "exclude": ["./dist"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/jest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "./dist", 6 | "declaration": true, 7 | "emitDeclarationOnly": true 8 | }, 9 | "include": ["./lib"], 10 | "exclude": ["./dist"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "./dist", 6 | "declaration": true, 7 | "emitDeclarationOnly": true 8 | }, 9 | "include": ["./lib"], 10 | "exclude": ["./dist"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/internal/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "./dist", 6 | "declaration": true, 7 | "emitDeclarationOnly": true 8 | }, 9 | "include": ["./lib"], 10 | "exclude": ["./dist"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/symbolicate/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "./dist", 6 | "declaration": true, 7 | "emitDeclarationOnly": true 8 | }, 9 | "include": ["./lib"], 10 | "exclude": ["./dist"] 11 | } 12 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/core/build/index.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import { getEsbuildBaseOptions } from '../../../shared/index.mjs'; 3 | 4 | const buildOptions = getEsbuildBaseOptions(import.meta.dirname); 5 | 6 | esbuild.build(buildOptions).catch((error) => { 7 | console.error(error); 8 | process.exit(1); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/jest/build/index.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import { getEsbuildBaseOptions } from '../../../shared/index.mjs'; 3 | 4 | const buildOptions = getEsbuildBaseOptions(import.meta.dirname); 5 | 6 | esbuild.build(buildOptions).catch((error) => { 7 | console.error(error); 8 | process.exit(1); 9 | }); 10 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /packages/cli/lib/helpers/errors.ts: -------------------------------------------------------------------------------- 1 | import { logger } from '../shared'; 2 | 3 | export const handleUncaughtException = (error: Error): boolean => { 4 | switch (true) { 5 | case error.message.includes('EADDRINUSE'): 6 | logger.error(error.message); 7 | return true; 8 | } 9 | 10 | return false; 11 | }; 12 | -------------------------------------------------------------------------------- /packages/config/build/index.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import { getEsbuildBaseOptions } from '../../../shared/index.mjs'; 3 | 4 | const buildOptions = getEsbuildBaseOptions(import.meta.dirname); 5 | 6 | esbuild.build(buildOptions).catch((error) => { 7 | console.error(error); 8 | process.exit(1); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/internal/build/index.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import { getEsbuildBaseOptions } from '../../../shared/index.mjs'; 3 | 4 | const buildOptions = getEsbuildBaseOptions(import.meta.dirname); 5 | 6 | esbuild.build(buildOptions).catch((error) => { 7 | console.error(error); 8 | process.exit(1); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/plugins/build/index.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import { getEsbuildBaseOptions } from '../../../shared/index.mjs'; 3 | 4 | const buildOptions = getEsbuildBaseOptions(import.meta.dirname); 5 | 6 | esbuild.build(buildOptions).catch((error) => { 7 | console.error(error); 8 | process.exit(1); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/utils/build/index.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import { getEsbuildBaseOptions } from '../../../shared/index.mjs'; 3 | 4 | const buildOptions = getEsbuildBaseOptions(import.meta.dirname); 5 | 6 | esbuild.build(buildOptions).catch((error) => { 7 | console.error(error); 8 | process.exit(1); 9 | }); 10 | -------------------------------------------------------------------------------- /docs/pages/configuration/custom-plugins.md: -------------------------------------------------------------------------------- 1 | # Custom Plugins 2 | 3 | You can add additional [Esbuild plugins](https://esbuild.github.io/plugins). 4 | 5 | Add plugins to `plugins` in configuration. 6 | 7 | ```js 8 | // react-native-esbuild.config.js 9 | exports.default = { 10 | plugins: [someEsbuildPlugin]; 11 | }; 12 | ``` 13 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/core/lib/index.ts: -------------------------------------------------------------------------------- 1 | import pkg from '../package.json'; 2 | 3 | // Set core version to global. 4 | self._version = pkg.version; 5 | 6 | export { ReactNativeEsbuildBundler } from './bundler'; 7 | export type * from './types'; 8 | export type * from './bundler'; 9 | export type { CacheController } from './bundler/cache'; 10 | -------------------------------------------------------------------------------- /packages/dev-server/build/index.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import { getEsbuildBaseOptions } from '../../../shared/index.mjs'; 3 | 4 | const buildOptions = getEsbuildBaseOptions(import.meta.dirname); 5 | 6 | esbuild.build(buildOptions).catch((error) => { 7 | console.error(error); 8 | process.exit(1); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/dev-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "./dist", 6 | "declaration": true, 7 | "emitDeclarationOnly": true 8 | }, 9 | "files": ["types/lib.d.ts"], 10 | "include": ["./lib"], 11 | "exclude": ["./dist"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/plugins/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "./dist", 6 | "declaration": true, 7 | "emitDeclarationOnly": true 8 | }, 9 | "include": ["./lib", "../core/lib/transformers/babel.ts"], 10 | "exclude": ["./dist"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/symbolicate/build/index.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import { getEsbuildBaseOptions } from '../../../shared/index.mjs'; 3 | 4 | const buildOptions = getEsbuildBaseOptions(import.meta.dirname); 5 | 6 | esbuild.build(buildOptions).catch((error) => { 7 | console.error(error); 8 | process.exit(1); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/transformer/build/index.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import { getEsbuildBaseOptions } from '../../../shared/index.mjs'; 3 | 4 | const buildOptions = getEsbuildBaseOptions(import.meta.dirname); 5 | 6 | esbuild.build(buildOptions).catch((error) => { 7 | console.error(error); 8 | process.exit(1); 9 | }); 10 | -------------------------------------------------------------------------------- /example/__tests__/App.test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, it } from '@jest/globals'; 2 | import renderer, { act } from 'react-test-renderer'; 3 | import { App } from '../src/App'; 4 | 5 | describe('App', () => { 6 | it('renders correctly', async () => { 7 | await act(() => { 8 | renderer.create(); 9 | }); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); 2 | 3 | /** 4 | * Metro configuration 5 | * https://reactnative.dev/docs/metro 6 | * 7 | * @type {import('metro-config').MetroConfig} 8 | */ 9 | const config = {}; 10 | 11 | module.exports = mergeConfig(getDefaultConfig(import.meta.dirname), config); 12 | -------------------------------------------------------------------------------- /packages/config/lib/shares.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_ENTRY_POINT = 'index.js'; 2 | export const DEFAULT_WEB_ENTRY_POINT = 'index.web.js'; 3 | export const DEFAULT_OUTFILE = 'main.jsbundle'; 4 | 5 | export const GLOBAL_CACHE_DIR = 'react-native-esbuild'; 6 | export const LOCAL_CACHE_DIR = '.rne'; 7 | 8 | export const SUPPORT_PLATFORMS = ['android', 'ios', 'web'] as const; 9 | -------------------------------------------------------------------------------- /packages/transformer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "./dist", 6 | "declaration": true, 7 | "emitDeclarationOnly": true 8 | }, 9 | "files": ["../../types/index.d.ts", "types/lib.d.ts"], 10 | "include": ["./lib", "./tests"], 11 | "exclude": ["./dist"] 12 | } 13 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } 2 | plugins { id("com.facebook.react.settings") } 3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } 4 | rootProject.name = 'example' 5 | include ':app' 6 | includeBuild('../node_modules/@react-native/gradle-plugin') 7 | -------------------------------------------------------------------------------- /packages/cli/lib/commands/cache.ts: -------------------------------------------------------------------------------- 1 | import { ReactNativeEsbuildBundler } from '@react-native-esbuild/core'; 2 | import type { Command } from '../types'; 3 | 4 | /** 5 | * Cache management command. 6 | */ 7 | export const cache: Command = async (_argv, subCommand) => { 8 | if (subCommand === 'clean') { 9 | await ReactNativeEsbuildBundler.resetCache(); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /docs/next.config.js: -------------------------------------------------------------------------------- 1 | const withNextra = require('nextra')({ 2 | theme: 'nextra-theme-docs', 3 | themeConfig: './theme.config.tsx', 4 | }) 5 | 6 | module.exports = { 7 | eslint: { 8 | // Warning: This allows production builds to successfully complete even if 9 | // your project has ESLint errors. 10 | ignoreDuringBuilds: true, 11 | }, 12 | ...withNextra() 13 | }; 14 | -------------------------------------------------------------------------------- /docs/pages/limitations/hot-module-replacement.md: -------------------------------------------------------------------------------- 1 | # Hot Module Replacement 2 | 3 | Esbuild doesn't currently support Hot Module Replacement(HMR). 4 | 5 | Metro is implementing HMR capabilities based on [react-refresh](https://www.npmjs.com/package/react-refresh). I'll be looking at working with this, but as it is one of the complex implementations, unfortunately not sure when it will be available. 6 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /packages/cli/build/index.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import { getEsbuildBaseOptions } from '../../../shared/index.mjs'; 3 | 4 | const buildOptions = getEsbuildBaseOptions(import.meta.dirname, { 5 | banner: { 6 | js: '#!/usr/bin/env node', 7 | }, 8 | }); 9 | 10 | esbuild.build(buildOptions).catch((error) => { 11 | console.error(error); 12 | process.exit(1); 13 | }); 14 | -------------------------------------------------------------------------------- /example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby ">= 2.6.10" 5 | 6 | # Exclude problematic versions of cocoapods and activesupport that causes build failures. 7 | gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' 8 | gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' 9 | gem 'xcodeproj', '< 1.26.0' 10 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/nx/schemas/nx-schema.json", 3 | "targetDefaults": { 4 | "typecheck": { 5 | "dependsOn": ["^build"], 6 | "cache": false 7 | }, 8 | "build": { 9 | "dependsOn": ["^build"], 10 | "outputs": ["{projectRoot}/dist"], 11 | "cache": true 12 | } 13 | }, 14 | "defaultBase": "main", 15 | "neverConnectToCloud": true 16 | } 17 | -------------------------------------------------------------------------------- /example/index.web.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { AppRegistry } from 'react-native'; 4 | import { App } from './src/App'; 5 | import { name as appName } from './app.json'; 6 | 7 | AppRegistry.runApplication( 8 | AppRegistry.registerComponent(appName, () => App), 9 | { 10 | // eslint-disable-next-line no-undef -- web env 11 | rootTag: document.getElementById('root'), 12 | }, 13 | ); 14 | -------------------------------------------------------------------------------- /example/src/navigators/types.ts: -------------------------------------------------------------------------------- 1 | import type { ParamListBase } from '@react-navigation/native'; 2 | import type { StackScreenProps } from '@react-navigation/stack'; 3 | 4 | export interface RootStackScreens extends ParamListBase { 5 | Main: undefined; 6 | Intro: undefined; 7 | } 8 | 9 | export type RootStackProps = 10 | StackScreenProps; 11 | -------------------------------------------------------------------------------- /packages/internal/lib/__tests__/__snapshots__/presets.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`getAssetRegistrationScript should match snapshot 1`] = ` 4 | " 5 | module.exports = require('react-native/Libraries/Image/AssetRegistry').registerAsset({"__packager_asset":true,"name":"image","type":"png","scales":[1,2,3],"hash":"hash","httpServerLocation":"/image.png","width":0,"height":0}); 6 | " 7 | `; 8 | -------------------------------------------------------------------------------- /packages/symbolicate/lib/helpers/codeStack.ts: -------------------------------------------------------------------------------- 1 | import type { IncomingMessage } from 'node:http'; 2 | import type { Frame } from '../types'; 3 | 4 | export const parseStackFromRawBody = ( 5 | rawBody: IncomingMessage['rawBody'], 6 | ): Frame[] => { 7 | // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- `rawBody` is any type. 8 | const parsedStack = JSON.parse(rawBody) as { stack: Frame[] }; 9 | return parsedStack.stack; 10 | }; 11 | -------------------------------------------------------------------------------- /packages/transformer/README.md: -------------------------------------------------------------------------------- 1 | # `@react-native-esbuild/transformer` 2 | 3 | ## Usage 4 | 5 | ```ts 6 | import { 7 | stripFlowWithSucrase, 8 | transformWithBabel, 9 | transformWithSwc, 10 | minifyWithSwc, 11 | } from '@react-native-esbuild/transformer'; 12 | 13 | await stripFlowWithSucrase(code, context); 14 | await transformWithBabel(code, context, options); 15 | await transformWithSwc(code, context, options); 16 | await minifyWithSwc(code, context, options); 17 | ``` 18 | -------------------------------------------------------------------------------- /example/tests/setup.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-return -- mock */ 2 | import 'react-native-gesture-handler/jestSetup'; 3 | import { setUpTests } from 'react-native-reanimated'; 4 | 5 | setUpTests(); 6 | 7 | jest.mock('@react-navigation/devtools', () => ({ useFlipper: jest.fn() })); 8 | 9 | jest.mock('dripsy', () => ({ 10 | styled: jest.fn().mockImplementation((value) => () => value), 11 | makeTheme: jest.fn().mockImplementation((value) => value), 12 | })); 13 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /docs/pages/_meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "index": "Introduction", 3 | "getting-started": "Getting Started", 4 | "configuration": "Configuration", 5 | "native-setup": "Native Setup", 6 | "package-presets": "Package Presets", 7 | "web": "Web", 8 | "jest": "Testing with Jest", 9 | "troubleshooting": "Troubleshooting", 10 | "limitations": "Limitations", 11 | "contact": { 12 | "title": "Contact ↗", 13 | "type": "page", 14 | "href": "https://twitter.com/leegeunhyeok", 15 | "newWindow": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/register-build-preset-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Register build preset request 3 | about: Register preset configuration for build 3rd party packages 4 | title: '' 5 | labels: preset 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Package information** 11 | 12 | - [e.g. react-native-reanimated@3.0.0] 13 | 14 | **Configuration** 15 | 16 | ```js 17 | export default = { 18 | // configuration here 19 | }; 20 | ``` 21 | 22 | **Additional context** 23 | Add any other context or screenshots about the feature request here. 24 | -------------------------------------------------------------------------------- /example/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /packages/symbolicate/lib/types.ts: -------------------------------------------------------------------------------- 1 | export interface Frame { 2 | file: string; 3 | lineNumber: number; 4 | column: number; 5 | methodName: string; 6 | } 7 | 8 | export interface ConfiguredFrame extends Frame { 9 | __collapse?: boolean; 10 | } 11 | 12 | export interface CodeFrame { 13 | content: string; 14 | location: { 15 | column: number; 16 | row: number; 17 | }; 18 | fileName: string; 19 | } 20 | 21 | export interface SymbolicateResult { 22 | stack: ConfiguredFrame[]; 23 | codeFrame: CodeFrame | null; 24 | } 25 | -------------------------------------------------------------------------------- /shared/index.mjs: -------------------------------------------------------------------------------- 1 | import * as path from 'node:path'; 2 | 3 | /** 4 | * @param {string} entryFile filename 5 | * @param {import('esbuild').BuildOptions} options additional options 6 | * @returns {import('esbuild').BuildOptions} 7 | */ 8 | export function getEsbuildBaseOptions(packageDir, options = {}) { 9 | return { 10 | entryPoints: [path.resolve(packageDir, '../lib/index.ts')], 11 | outfile: 'dist/index.js', 12 | bundle: true, 13 | format: 'esm', 14 | platform: 'node', 15 | packages: 'external', 16 | ...options, 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /docs/pages/native-setup/android.md: -------------------------------------------------------------------------------- 1 | # Android 2 | 3 | ## Set Custom CLI 4 | 5 | Open `android/app/build.gradle` and add configuration. 6 | 7 | ### React Native >= 0.71.x 8 | 9 | Add `cliFile` to react configuration. 10 | 11 | ```go 12 | react { 13 | cliFile = file("../../node_modules/@react-native-esbuild/cli/dist/index.js") 14 | } 15 | ``` 16 | 17 | ### React Native <= 0.70.x 18 | 19 | Add `cliPath` to react configuration. 20 | 21 | ```go 22 | project.ext.react = [ 23 | cliPath: "../../node_modules/@react-native-esbuild/cli/dist/index.js" 24 | ] 25 | ``` 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # yarn 2 | .yarn/* 3 | !.yarn/patches 4 | !.yarn/plugins 5 | !.yarn/releases 6 | !.yarn/sdks 7 | !.yarn/versions 8 | 9 | # Swap the comments on the following lines if you wish to use zero-installs 10 | # In that case, don't forget to run `yarn config set enableGlobalCache false`! 11 | # Documentation here: https://yarnpkg.com/features/caching#zero-installs 12 | 13 | #!.yarn/cache 14 | .pnp.* 15 | 16 | # nx 17 | .nx 18 | 19 | node_modules/ 20 | 21 | # build result 22 | **/*/dist/ 23 | 24 | # macOS 25 | .DS_Store 26 | 27 | # test 28 | report.json 29 | coverage/ 30 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": false, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "incremental": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve" 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/config/lib/types.ts: -------------------------------------------------------------------------------- 1 | export type BundlerSupportPlatform = 'android' | 'ios' | 'web'; 2 | 3 | export interface BundleOptions { 4 | platform: BundlerSupportPlatform; 5 | entry: string; 6 | outfile: string; 7 | dev: boolean; 8 | minify: boolean; 9 | metafile: boolean; 10 | sourcemap?: string; 11 | assetsDir?: string; 12 | } 13 | 14 | /** 15 | * Flags for `BundleOptions` 16 | */ 17 | export enum OptionFlag { 18 | None = 0b00000000, 19 | PlatformAndroid = 0b00000001, 20 | PlatformIos = 0b00000010, 21 | PlatformWeb = 0b00000100, 22 | Dev = 0b00001000, 23 | Minify = 0b00010000, 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/helpers/async.ts: -------------------------------------------------------------------------------- 1 | import type { BundleResult, PromiseHandler } from '../../types'; 2 | 3 | export const createPromiseHandler = (): PromiseHandler => { 4 | let resolver: PromiseHandler['resolver'] | undefined; 5 | let rejecter: PromiseHandler['rejecter'] | undefined; 6 | 7 | const task = new Promise((resolve, _reject) => { 8 | resolver = resolve; 9 | rejecter = (reason: unknown) => { 10 | resolve({ result: null, error: reason as Error }); 11 | }; 12 | }); 13 | 14 | return { task, resolver, rejecter }; 15 | }; 16 | -------------------------------------------------------------------------------- /packages/internal/lib/types.ts: -------------------------------------------------------------------------------- 1 | import type { BundlerSupportPlatform } from '@react-native-esbuild/config'; 2 | 3 | export interface Asset { 4 | // `/path/to/asset/image.png` 5 | path: string; 6 | // `image.png` 7 | basename: string; 8 | // `image` 9 | name: string; 10 | // `.png` 11 | extension: string; 12 | // `png` 13 | type: string; 14 | // [1, 2, 3] 15 | scales: number[]; 16 | httpServerLocation: string; 17 | hash: string; 18 | dimensions: { width: number; height: number }; 19 | platform: BundlerSupportPlatform | null; 20 | } 21 | 22 | export type AssetScale = 0.75 | 1 | 1.5 | 2 | 3; 23 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | buildToolsVersion = "35.0.0" 4 | minSdkVersion = 24 5 | compileSdkVersion = 35 6 | targetSdkVersion = 34 7 | ndkVersion = "26.1.10909125" 8 | kotlinVersion = "1.9.24" 9 | } 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle") 16 | classpath("com.facebook.react:react-native-gradle-plugin") 17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") 18 | } 19 | } 20 | 21 | apply plugin: "com.facebook.react.rootproject" 22 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-native-esbuild/docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "installConfig": { 6 | "hoistingLimits": "dependencies" 7 | }, 8 | "description": "@react-native-esbuild document", 9 | "scripts": { 10 | "dev": "next dev", 11 | "build:docs": "next build", 12 | "start:docs": "next start" 13 | }, 14 | "dependencies": { 15 | "next": "^13.0.6", 16 | "nextra": "latest", 17 | "nextra-theme-docs": "latest", 18 | "react": "^18.2.0", 19 | "react-dom": "^18.2.0" 20 | }, 21 | "devDependencies": { 22 | "@types/node": "^22", 23 | "typescript": "^5.8.3" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/plugins/lib/types.ts: -------------------------------------------------------------------------------- 1 | import type { BundlerSupportPlatform } from '@react-native-esbuild/config'; 2 | 3 | // asset-register-plugin 4 | export interface AssetRegisterPluginConfig { 5 | assetExtensions?: string[]; 6 | } 7 | 8 | export interface SuffixPathResult { 9 | path: string; 10 | dirname: string; 11 | basename: string; 12 | extension: string; 13 | platform: BundlerSupportPlatform | null; 14 | } 15 | 16 | // react-native-runtime-transform-plugin 17 | export interface ReactNativeRuntimeTransformPluginConfig { 18 | injectScriptPaths?: string[]; 19 | } 20 | 21 | export interface CacheConfig { 22 | hash: string; 23 | mtimeMs: number; 24 | } 25 | -------------------------------------------------------------------------------- /packages/internal/lib/__tests__/presets.test.ts: -------------------------------------------------------------------------------- 1 | import { getAssetRegistrationScript } from '../presets'; 2 | 3 | describe('getAssetRegistrationScript', () => { 4 | let assetRegistrationScript: string; 5 | 6 | beforeEach(() => { 7 | assetRegistrationScript = getAssetRegistrationScript({ 8 | name: 'image', 9 | type: 'png', 10 | scales: [1, 2, 3], 11 | hash: 'hash', 12 | httpServerLocation: '/image.png', 13 | dimensions: { 14 | width: 0, 15 | height: 0, 16 | }, 17 | }); 18 | }); 19 | 20 | it('should match snapshot', () => { 21 | expect(assetRegistrationScript).toMatchSnapshot(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /example/src/components/Section.tsx: -------------------------------------------------------------------------------- 1 | import React, { type PropsWithChildren } from 'react'; 2 | import { styled, View, H2 } from 'dripsy'; 3 | 4 | type SectionProps = PropsWithChildren<{ 5 | title?: string; 6 | delay?: number; 7 | }>; 8 | 9 | const SectionContainer = styled(View)({ 10 | marginTop: '$04', 11 | gap: '$02', 12 | }); 13 | 14 | const SectionTitle = styled(H2, { 15 | defaultVariant: 'text.primary', 16 | })(); 17 | 18 | export function Section({ children, title }: SectionProps): React.JSX.Element { 19 | return ( 20 | 21 | {title ? {title} : null} 22 | {children} 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | // eslint-disable-next-line no-var -- Allow var. 3 | var self: Global & { 4 | // core config 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any -- `BundlerConfig` in @react-native-esbuild/core. 6 | _config: any; 7 | _version: string; 8 | // core 9 | shouldResetCache?: boolean; 10 | // logger 11 | logEnabled: boolean; 12 | logLevel: number; 13 | logTimestampFormat: string | null; 14 | }; 15 | } 16 | 17 | declare module 'http' { 18 | interface IncomingMessage { 19 | // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Allow any. 20 | rawBody: any; 21 | } 22 | } 23 | 24 | export {}; 25 | -------------------------------------------------------------------------------- /docs/styles/global.css: -------------------------------------------------------------------------------- 1 | .welcome-banner { 2 | position: relative; 3 | text-align: center; 4 | min-height: 320px; 5 | } 6 | 7 | .welcome-banner > img { 8 | width: 320px; 9 | display: inline-block; 10 | } 11 | 12 | .welcome-banner > img.animate-logo { 13 | animation: linear infinite roll 10s; 14 | } 15 | 16 | .welcome-banner > img.over-wrap { 17 | position: absolute; 18 | top: 0; 19 | left: 50%; 20 | transform: translateX(-50%); 21 | } 22 | 23 | .description-banner { 24 | margin-top: 16px; 25 | text-align: center; 26 | } 27 | 28 | .description-banner > img { 29 | display: inline-block; 30 | } 31 | 32 | @keyframes roll { 33 | to { 34 | transform: rotate(360deg); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/transformer/lib/transformer/babel/presets.ts: -------------------------------------------------------------------------------- 1 | import type { TransformOptions } from '@babel/core'; 2 | import type { TransformerOptionsPreset } from '../../types'; 3 | 4 | const getCommonPreset = (): TransformerOptionsPreset => { 5 | return (_context) => ({ 6 | minified: false, 7 | compact: false, 8 | sourceMaps: false, 9 | babelrc: false, 10 | highlightCode: !process.stdin.isTTY, 11 | }); 12 | }; 13 | 14 | const getFullyTransformPreset = 15 | (): TransformerOptionsPreset => { 16 | return (context) => ({ 17 | root: context.root, 18 | babelrc: true, 19 | }); 20 | }; 21 | 22 | export { getCommonPreset, getFullyTransformPreset }; 23 | -------------------------------------------------------------------------------- /packages/transformer/lib/transformer/sucrase/sucrase.ts: -------------------------------------------------------------------------------- 1 | import { transform, type Transform } from 'sucrase'; 2 | import type { SyncTransformer } from '../../types'; 3 | 4 | const TRANSFORM_FOR_STRIP_FLOW: Transform[] = ['flow', 'jsx']; 5 | const typeofImportRegExp = 6 | /import\s+typeof\s+[^;]+\s+from\s+['"][^'"]+['"];\s*/g; 7 | 8 | const stripFlowTypeofImportStatements = (code: string): string => { 9 | return code.replace(typeofImportRegExp, ''); 10 | }; 11 | 12 | export const stripFlowWithSucrase: SyncTransformer = (code, context) => { 13 | return stripFlowTypeofImportStatements( 14 | transform(code, { 15 | transforms: TRANSFORM_FOR_STRIP_FLOW, 16 | filePath: context.path, 17 | }).code, 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /example/src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { styled, Pressable, Text } from 'dripsy'; 3 | 4 | interface ButtonProps { 5 | label: string; 6 | onPress?: () => void; 7 | } 8 | 9 | const ButtonContainer = styled(Pressable)({ 10 | justifyContent: 'center', 11 | alignItems: 'center', 12 | width: '100%', 13 | height: 56, 14 | borderRadius: 8, 15 | backgroundColor: '#e2e2e2', 16 | }); 17 | 18 | const Label = styled(Text, { defaultVariant: 'text.button' })({ 19 | fontSize: '$button', 20 | }); 21 | 22 | export function Button({ label, onPress }: ButtonProps): React.JSX.Element { 23 | return ( 24 | 25 | 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /.yarn/patches/dripsy-npm-4.3.8-208587309d.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/core/index.ts b/src/core/index.ts 2 | index 1419f9162a578851a0dbcba565c57f61e68a379c..d27d9130f186868a212c3d7b309836b4bced4080 100644 3 | --- a/src/core/index.ts 4 | +++ b/src/core/index.ts 5 | @@ -4,7 +4,7 @@ export * from './provider' 6 | export * from './css/scales' 7 | export { useResponsiveValue } from './css/use-responsive-value' 8 | export { Styles, css } from './css' 9 | -export { DripsyFinalTheme as Theme } from './types-v2/declarations' 10 | +export { type DripsyFinalTheme as Theme } from './types-v2/declarations' 11 | export { styled } from './css/styled' 12 | export { useBreakpointIndex } from './css/breakpoint-context' 13 | export { getBreakpointIndex } from './css/get-breakpoint-index' 14 | -------------------------------------------------------------------------------- /docs/pages/native-setup/ios.md: -------------------------------------------------------------------------------- 1 | # iOS 2 | 3 | ## Set Custom CLI 4 | 5 | Open XCode, go to `Build Target > Build Phases > Bundle React Native code and images` and add `CLI_PATH` environment variable. 6 | 7 | ```bash 8 | set -e 9 | 10 | CLI_PATH="../node_modules/@react-native-esbuild/cli/dist/index.js" 11 | 12 | WITH_ENVIRONMENT="../node_modules/react-native/scripts/xcode/with-environment.sh" 13 | REACT_NATIVE_XCODE="../node_modules/react-native/scripts/react-native-xcode.sh" 14 | 15 | /bin/sh -c "$WITH_ENVIRONMENT $REACT_NATIVE_XCODE" 16 | ``` 17 | 18 | Export if you need to build from command line or external environment(eg. Fastlane, Bitrise, etc). 19 | 20 | ```bash 21 | export CLI_PATH="$PROJECT_DIR/node_modules/@react-native-esbuild/cli/dist/index.js" 22 | ``` 23 | -------------------------------------------------------------------------------- /example/jest.config.js: -------------------------------------------------------------------------------- 1 | const TRANSFORM_PACKAGES = [ 2 | 'react-native', 3 | 'jest-react-native', 4 | '@react-native', 5 | '@react-native-community', 6 | '@react-navigation', 7 | '@expo/html-elements', 8 | 'dripsy', 9 | ]; 10 | 11 | /** 12 | * @type {import('jest').Config} 13 | */ 14 | export default { 15 | preset: 'react-native', 16 | transform: { 17 | '^.+\\.(t|j)sx?$': '@react-native-esbuild/jest', 18 | }, 19 | transformIgnorePatterns: [ 20 | `node_modules/(?!${TRANSFORM_PACKAGES.join('|')})/`, 21 | ], 22 | testPathIgnorePatterns: ['dist'], 23 | coveragePathIgnorePatterns: ['node_modules'], 24 | collectCoverageFrom: [ 25 | 'src/**/*.{js,jsx,ts,tsx}', 26 | '!**/*.d.ts', 27 | '!**/index.ts', 28 | ], 29 | setupFilesAfterEnv: ['./tests/setup.ts'], 30 | }; 31 | -------------------------------------------------------------------------------- /packages/transformer/types/lib.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'hermes-parser' { 2 | import type { Node } from '@babel/core'; 3 | 4 | interface HermesParserOptions { 5 | babel?: boolean; 6 | allowReturnOutsideFunction?: boolean; 7 | flow?: 'all' | 'detect'; 8 | sourceFilename?: string | null; 9 | sourceType?: 'module' | 'script' | 'unambiguous'; 10 | tokens?: boolean; 11 | } 12 | 13 | /** 14 | * Returns `Node` when call with `babel: true` option. 15 | */ 16 | type MaybeBabelAstNode = Node; 17 | 18 | interface HermesParser { 19 | parse: (code: string, options: HermesParserOptions) => MaybeBabelAstNode; 20 | } 21 | 22 | declare function parse( 23 | code: string, 24 | options: HermesParserOptions, 25 | ): MaybeBabelAstNode; 26 | 27 | export const parse; 28 | } 29 | -------------------------------------------------------------------------------- /example/android/app/src/release/java/com/example/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.example; 8 | 9 | import android.content.Context; 10 | import com.facebook.react.ReactInstanceManager; 11 | 12 | /** 13 | * Class responsible of loading Flipper inside your React Native application. This is the release 14 | * flavor of it so it's empty as we don't want to load Flipper. 15 | */ 16 | public class ReactNativeFlipper { 17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 18 | // Do nothing as we don't want to initialize Flipper on Release. 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/cli/lib/helpers/cli.ts: -------------------------------------------------------------------------------- 1 | import { colors } from '@react-native-esbuild/utils'; 2 | import { logger } from '../shared'; 3 | 4 | export const getCommand = ( 5 | argv: RawArgv, 6 | position = 0, 7 | ): string => argv._[position]?.toString() ?? ''; 8 | 9 | // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Allow any type for debugging. 10 | export const printDebugOptions = >( 11 | options: T, 12 | ): void => { 13 | Object.entries(options).forEach(([key, value], index, entries) => { 14 | const isLast = entries.length - 1 === index; 15 | const pipe = `${isLast ? '╰' : '├'}─`; 16 | const keyValue = `${key}: ${JSON.stringify(value)}`; 17 | logger.debug(colors.gray(`${pipe} ${keyValue}${isLast ? '\n' : ''}`)); 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # `@react-native-esbuild/core` 2 | 3 | > Core of @react-native-esbuild 4 | 5 | ## Usage 6 | 7 | ```ts 8 | import { ReactNativeEsbuildBundler } from '@react-native-esbuild/core'; 9 | 10 | // Must be called first 11 | ReactNativeEsbuildBundler.bootstrap(); 12 | 13 | const bundler = new ReactNativeEsbuildBundler(); 14 | 15 | bundler 16 | .addPlugin(/* call ReactNativeEsbuildPluginCreator */) 17 | .addPlugin(/* call ReactNativeEsbuildPluginCreator */) 18 | .addPlugin(/* call ReactNativeEsbuildPluginCreator */); 19 | 20 | // initialize bundler 21 | await bundler.initialize(); 22 | 23 | // using `esbuild.build()` (write output to file system) 24 | await bundler.bundle(bundleOptions); 25 | 26 | // using `esbuild.context()` (in-memory output for dev-server) 27 | await bundler.getBundleResult(bundleOptions); 28 | ``` 29 | -------------------------------------------------------------------------------- /packages/plugins/lib/reactNativeWebPlugin/helpers/template.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | import path from 'node:path'; 3 | import { logger } from '../../shared'; 4 | 5 | export const generateIndexPage = async ( 6 | template: string, 7 | destinationDir: string, 8 | placeholders: Record = {}, 9 | ): Promise => { 10 | const destination = path.join(destinationDir, 'index.html'); 11 | logger.debug('generating index page', { 12 | template, 13 | destination, 14 | placeholders, 15 | }); 16 | 17 | const rawTemplate = await fs.readFile(template, 'utf-8'); 18 | await fs.writeFile( 19 | destination, 20 | Object.entries(placeholders).reduce((prev, [key, value]) => { 21 | return prev.replace(new RegExp(`{{${key}}}`, 'g'), value); 22 | }, rawTemplate), 23 | 'utf-8', 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /packages/cli/lib/presets/index.ts: -------------------------------------------------------------------------------- 1 | import type { ReactNativeEsbuildPluginCreator } from '@react-native-esbuild/core'; 2 | import { 3 | createAssetRegisterPlugin, 4 | createReactNativeRuntimeTransformPlugin, 5 | createReactNativeWebPlugin, 6 | createSvgTransformPlugin, 7 | } from '@react-native-esbuild/plugins'; 8 | 9 | // eslint-disable-next-line @typescript-eslint/no-explicit-any -- allow 10 | type AnyPlugin = any; 11 | 12 | const native: ReactNativeEsbuildPluginCreator[] = [ 13 | createAssetRegisterPlugin, 14 | createSvgTransformPlugin, 15 | createReactNativeRuntimeTransformPlugin, 16 | ]; 17 | 18 | const web: ReactNativeEsbuildPluginCreator[] = [ 19 | createReactNativeWebPlugin, 20 | createSvgTransformPlugin, 21 | createReactNativeRuntimeTransformPlugin, 22 | ]; 23 | 24 | export const presets = { native, web }; 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Clone '...' 16 | 2. Install dependencies 17 | 3. Build and see error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Build logs** 23 | If applicable, add logs to help explain your problem. 24 | 25 | **Environment (please complete the following information):** 26 | - OS: [e.g. macOS 13] 27 | - Node: [e.g. 8.0.0] 28 | - React Native: [e.g. 0.70.2] 29 | - React Native Esbuild: [e.g. 0.1.0] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /example/ios/exampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/src/navigators/RootStack.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStackNavigator } from '@react-navigation/stack'; 3 | import { IntroScreen, MainScreen } from '../screens'; 4 | import type { RootStackScreens } from './types'; 5 | 6 | const RootStack = createStackNavigator(); 7 | 8 | export function RootStackNavigator(): React.ReactElement { 9 | return ( 10 | 16 | 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/plugins/statusPlugin/templates.ts: -------------------------------------------------------------------------------- 1 | import { isTTY, colors } from '@react-native-esbuild/utils'; 2 | 3 | const summaryTemplateForTTY = ` 4 | ╭───────────╯ 5 | ├─ @warnings warnings 6 | ├─ @errors errors 7 | ╰─ @duration 8 | `.trim(); 9 | 10 | const summaryTemplateForNonTTY = ` 11 | > @warnings warnings 12 | > @errors errors 13 | > @durations 14 | `.trim(); 15 | 16 | export const getSummaryTemplate = (): string => { 17 | return colors.gray( 18 | isTTY() ? summaryTemplateForTTY : summaryTemplateForNonTTY, 19 | ); 20 | }; 21 | 22 | export const fromTemplate = ( 23 | template: string, 24 | placeholders: Record, 25 | ): string => { 26 | const templateText = Object.entries(placeholders).reduce( 27 | (template, [key, value]) => template.replace(`@${key}`, value), 28 | template, 29 | ); 30 | 31 | return `${templateText}\n`; 32 | }; 33 | -------------------------------------------------------------------------------- /docs/pages/getting-started/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | Requirements: `Node>=16` 4 | 5 | ```bash 6 | # using npm 7 | npm install -D @react-native-esbuild/cli 8 | 9 | # using yarn 10 | yarn add -D @react-native-esbuild/cli 11 | ``` 12 | 13 | ## Configuration 14 | 15 | Add `.rne` and `.swc` to `.gitignore`. 16 | 17 | ```sh 18 | # @react-native-esbuild's local cache directory for development environment 19 | .rne 20 | 21 | # swc plugins 22 | .swc 23 | ``` 24 | 25 | And create `react-native-esbuild.config.js` to project root. 26 | 27 | ```js 28 | /** 29 | * @type {import('@react-native-esbuild/core').Config} 30 | */ 31 | exports.default = {}; 32 | ``` 33 | 34 | for more details, go to [Configuration](/configuration/basic-configuration). 35 | 36 | ## Native Setup 37 | 38 | If you want to integrate with build processes, go to Native Setup([Android](/native-setup/android), [iOS](/native-setup/ios)) guide. 39 | -------------------------------------------------------------------------------- /packages/core/static/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React Native Esbuild 7 | 26 | 27 | 28 |

29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /example/react-native-esbuild.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('@react-native-esbuild/core').Config} 3 | */ 4 | export default { 5 | cache: true, 6 | logger: { 7 | timestamp: 'YYYY-MM-DD HH:mm:ss.SSS', 8 | }, 9 | transformer: { 10 | stripFlowPackageNames: ['react-native'], 11 | fullyTransformPackageNames: [], 12 | additionalTransformRules: { 13 | babel: [ 14 | { 15 | test: (path, code) => { 16 | return ( 17 | /node_modules\/react-native-reanimated\//.test(path) || 18 | code.includes('react-native-reanimated') 19 | ); 20 | }, 21 | options: { 22 | plugins: [ 23 | '@babel/plugin-transform-export-namespace-from', 24 | 'react-native-reanimated/plugin', 25 | ], 26 | babelrc: false, 27 | }, 28 | }, 29 | ], 30 | }, 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /packages/dev-server/lib/middlewares/indexPage.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | import path from 'node:path'; 3 | import { logger } from '../shared'; 4 | import type { DevServerMiddlewareCreator } from '../types'; 5 | 6 | const TAG = 'index-page-middleware'; 7 | 8 | export const createIndexPageMiddleware: DevServerMiddlewareCreator = () => { 9 | return function indexPageMiddleware(request, response, next) { 10 | if (request.url !== '/') { 11 | next(); 12 | return; 13 | } 14 | 15 | fs.readFile(path.resolve(__filename, '../../static/index.html'), { 16 | encoding: 'utf-8', 17 | }) 18 | .then((content) => { 19 | response.writeHead(200, { 'Content-Type': 'text/html' }).end(content); 20 | }) 21 | .catch((error) => { 22 | logger.error(`(${TAG}) cannot read index.html`, error as Error); 23 | response.writeHead(500).end(); 24 | }); 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | 5 | @implementation AppDelegate 6 | 7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 8 | { 9 | self.moduleName = @"example"; 10 | // You can add your custom initial props in the dictionary below. 11 | // They will be passed down to the ViewController used by React Native. 12 | self.initialProps = @{}; 13 | 14 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 15 | } 16 | 17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge{ 18 | return [self bundleURL]; 19 | } 20 | 21 | - (NSURL *)bundleURL 22 | { 23 | #if DEBUG 24 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 25 | #else 26 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 27 | #endif 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /packages/internal/lib/helpers.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { createRequire } from 'node:module'; 3 | import indent from 'indent-string'; 4 | 5 | const require = createRequire(import.meta.url); 6 | 7 | export const resolveFromRoot = ( 8 | targetPath: string, 9 | root = process.cwd(), 10 | ): string => { 11 | return require.resolve(targetPath, { 12 | paths: Array.from( 13 | new Set([ 14 | // Add current directory to module resolution path for workspace projects(eg. monorepo). 15 | path.join(root, 'node_modules'), 16 | ...(require.main?.paths ?? []), 17 | ]), 18 | ).filter(Boolean), 19 | }); 20 | }; 21 | 22 | export const wrapWithIIFE = (body: string, filepath: string): string => ` 23 | // ${filepath} 24 | (function (global) { 25 | ${indent(body, 2)} 26 | })(typeof globalThis !== 'undefined' ? globalThis : typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : this); 27 | `; 28 | -------------------------------------------------------------------------------- /packages/plugins/lib/svgTransformPlugin/svgTransformPlugin.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | import { transform } from '@svgr/core'; 3 | import type { ReactNativeEsbuildPluginCreator } from '@react-native-esbuild/core'; 4 | 5 | const NAME = 'svg-transform-plugin'; 6 | 7 | export const createSvgTransformPlugin: ReactNativeEsbuildPluginCreator = ( 8 | context, 9 | ) => ({ 10 | name: NAME, 11 | setup: (build): void => { 12 | const isNative = ['android', 'ios'].includes(context.platform); 13 | 14 | build.onLoad({ filter: /\.svg$/ }, async (args) => { 15 | const rawSvg = await fs.readFile(args.path, { encoding: 'utf8' }); 16 | return { 17 | contents: await transform( 18 | rawSvg, 19 | { 20 | plugins: ['@svgr/plugin-jsx'], 21 | native: isNative, 22 | }, 23 | { filePath: args.path }, 24 | ), 25 | loader: 'jsx', 26 | }; 27 | }); 28 | }, 29 | }); 30 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example 2 | 3 | import com.facebook.react.ReactActivity 4 | import com.facebook.react.ReactActivityDelegate 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate 7 | 8 | class MainActivity : ReactActivity() { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. This is used to schedule 12 | * rendering of the component. 13 | */ 14 | override fun getMainComponentName(): String = "example" 15 | 16 | /** 17 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] 18 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] 19 | */ 20 | override fun createReactActivityDelegate(): ReactActivityDelegate = 21 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/plugins/metafilePlugin/metafilePlugin.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | import path from 'node:path'; 3 | import type { BuildResult } from 'esbuild'; 4 | import { logger } from '../../../shared'; 5 | import type { ReactNativeEsbuildPluginCreator } from '../../../types'; 6 | 7 | const NAME = 'metafile-plugin'; 8 | 9 | export const createMetafilePlugin: ReactNativeEsbuildPluginCreator = ( 10 | context, 11 | ) => ({ 12 | name: NAME, 13 | setup: (build): void => { 14 | build.onEnd(async (result: BuildResult) => { 15 | const { metafile } = result; 16 | const filename = path.join( 17 | context.root, 18 | `metafile-${context.platform}-${new Date().getTime().toString()}.json`, 19 | ); 20 | 21 | if (metafile) { 22 | logger.debug('writing esbuild metafile', { destination: filename }); 23 | await fs.writeFile(filename, JSON.stringify(metafile), { 24 | encoding: 'utf-8', 25 | }); 26 | } 27 | }); 28 | }, 29 | }); 30 | -------------------------------------------------------------------------------- /packages/dev-server/lib/server/DevServer.ts: -------------------------------------------------------------------------------- 1 | import type { Server as HTTPServer } from 'node:http'; 2 | import type { ReactNativeEsbuildBundler } from '@react-native-esbuild/core'; 3 | import { DEFAULT_HOST, DEFAULT_PORT } from '../constants'; 4 | import type { DevServerOptions } from '../types'; 5 | 6 | export abstract class DevServer { 7 | protected initialized = false; 8 | protected server?: HTTPServer; 9 | protected bundler?: ReactNativeEsbuildBundler; 10 | protected devServerOptions: Required; 11 | 12 | constructor(devServerOptions: DevServerOptions) { 13 | this.devServerOptions = { 14 | root: devServerOptions.root ?? process.cwd(), 15 | port: devServerOptions.port ?? DEFAULT_PORT, 16 | host: devServerOptions.host ?? DEFAULT_HOST, 17 | }; 18 | } 19 | 20 | public abstract initialize( 21 | onPostSetup?: (bundler: ReactNativeEsbuildBundler) => void | Promise, 22 | ): Promise; 23 | 24 | public abstract listen(onListen?: () => void): Promise; 25 | } 26 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { SafeAreaProvider } from 'react-native-safe-area-context'; 3 | import { GestureHandlerRootView } from 'react-native-gesture-handler'; 4 | import { 5 | NavigationContainer, 6 | useNavigationContainerRef, 7 | } from '@react-navigation/native'; 8 | import { DripsyProvider } from 'dripsy'; 9 | import { RootStackNavigator } from './navigators'; 10 | import { themeLight } from './theme'; 11 | 12 | export function App(): React.JSX.Element { 13 | const navigationRef = useNavigationContainerRef(); 14 | 15 | useEffect(() => { 16 | console.log('Hello, world!'); 17 | }, []); 18 | 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /packages/dev-server/README.md: -------------------------------------------------------------------------------- 1 | # `@react-native-esbuild/dev-server` 2 | 3 | > Development server for @react-native-esbuild 4 | 5 | ## Usage 6 | 7 | ```ts 8 | import { 9 | ReactNativeAppServer, 10 | ReactNativeWebServer, 11 | } from '@react-native-esbuild/dev-server'; 12 | 13 | const serveOptions = { 14 | port: '8081', 15 | host: 'localhost', 16 | }; 17 | 18 | // For Native (Android, iOS) 19 | const server = await new ReactNativeAppServer( 20 | serveOptions, 21 | ).initialize((bundler) => { 22 | bundler 23 | .registerPlugin(/* plugin */) 24 | .registerPlugin(/* plugin */) 25 | .registerPlugin(/* plugin */); 26 | }); 27 | 28 | // For Web 29 | const server = await new ReactNativeWebServer( 30 | serveOptions, 31 | bundleOptions, 32 | ).initialize((bundler) => { 33 | bundler 34 | .registerPlugin(createSvgTransformPlugin()) 35 | .registerPlugin(createReactNativeRuntimeTransformPlugin()) 36 | .registerPlugin(createReactNativeWebPlugin()); 37 | }); 38 | 39 | server.listen(() => console.log('listening!')); 40 | ``` 41 | -------------------------------------------------------------------------------- /packages/jest/README.md: -------------------------------------------------------------------------------- 1 | # `@react-native-esbuild/jest` 2 | 3 | > react-native preset for jest powered by @react-native-esbuild 4 | 5 | ## Usage 6 | 7 | ```js 8 | exports.default = { 9 | preset: 'react-native', 10 | transform: { 11 | '^.+\\.(t|j)sx?$': '@react-native-esbuild/jest', 12 | }, 13 | }; 14 | 15 | // With transformer options(for enable custom instrument). 16 | exports.default = { 17 | preset: 'react-native', 18 | transform: { 19 | '^.+\\.(t|j)sx?$': ['@react-native-esbuild/jest', /* TransformerConfig */], 20 | }, 21 | }; 22 | ``` 23 | 24 | ```ts 25 | /** 26 | * @see {@link https://github.com/kwonoj/swc-plugin-coverage-instrument} 27 | */ 28 | interface TransformerConfig { 29 | experimental?: { 30 | customCoverageInstrumentation?: { 31 | coverageVariable?: string; 32 | compact: boolean; 33 | reportLogic: boolean; 34 | ignoreClassMethods: string[]; 35 | instrumentLog?: { 36 | level: string; 37 | enableTrace: boolean; 38 | }; 39 | }; 40 | }; 41 | } 42 | ``` 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Geunhyeok LEE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example/src/components/Fade.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, type PropsWithChildren } from 'react'; 2 | import type { ViewStyle } from 'react-native'; 3 | import Animated, { 4 | useSharedValue, 5 | useAnimatedStyle, 6 | withDelay, 7 | withTiming, 8 | interpolate, 9 | } from 'react-native-reanimated'; 10 | 11 | interface FadeProps { 12 | delay?: number; 13 | style?: ViewStyle; 14 | } 15 | 16 | const TRANSLATE_OFFSET = 50; 17 | 18 | export function Fade({ 19 | children, 20 | style, 21 | delay = 0, 22 | }: PropsWithChildren): React.ReactElement { 23 | const styleValue = useSharedValue(0); 24 | const animatedStyles = useAnimatedStyle(() => ({ 25 | opacity: styleValue.value, 26 | transform: [ 27 | { 28 | translateY: interpolate( 29 | styleValue.value, 30 | [0, 1], 31 | [TRANSLATE_OFFSET, 0], 32 | ), 33 | }, 34 | ], 35 | })); 36 | 37 | useEffect(() => { 38 | styleValue.value = withDelay(delay, withTiming(1)); 39 | }, [delay]); 40 | 41 | return ( 42 | {children} 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /example/ios/example/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryFileTimestamp 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | C617.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategoryUserDefaults 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | CA92.1 21 | 22 | 23 | 24 | NSPrivacyAccessedAPIType 25 | NSPrivacyAccessedAPICategorySystemBootTime 26 | NSPrivacyAccessedAPITypeReasons 27 | 28 | 35F9.1 29 | 30 | 31 | 32 | NSPrivacyCollectedDataTypes 33 | 34 | NSPrivacyTracking 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /docs/pages/troubleshooting/basic.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## On build time 4 | 5 | - When `No matching export in "xxx" for import "xxx"` occurs 6 | - Replace import statements to `type import` statements. 7 | ```ts 8 | // AS IS 9 | import { ValueIdentifier, TypeIdentifierA } from '...'; 10 | import TypeIdentifierB from '...'; 11 | 12 | // TO BE 13 | import { ValueIdentifier, type TypeIdentifierA } from '...'; 14 | import type TypeIdentifierB from '...'; 15 | ``` 16 | - When `Syntax Error` occurs 17 | - Target library may be using [Flow](https://flow.org). 18 | - Add the package name to `stripFlowPackageNames` in configuration file. 19 | - When unknown error occurs 20 | - Add the package name to `fullyTransformPackageNames` in configuration file. 21 | - Try rebuild with `--reset-cache` flag. 22 | - Please [report issue](https://github.com/leegeunhyeok/react-native-esbuild/issues) and share demo code for reproduce the issue. 23 | 24 | ## On runtime 25 | 26 | There're many reasons for runtime errors, making it difficult to determine the cause. 27 | 28 | Please [report issue](https://github.com/leegeunhyeok/react-native-esbuild/issues) and share demo code for reproduce the issue. 29 | -------------------------------------------------------------------------------- /example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('node:path'); 2 | 3 | const project = resolve(__dirname, 'tsconfig.json'); 4 | 5 | /** @type { import('eslint').ESLint.ConfigData } */ 6 | module.exports = { 7 | root: true, 8 | env: { 9 | node: true, 10 | }, 11 | plugins: ['prettier'], 12 | extends: [ 13 | require.resolve('@vercel/style-guide/eslint/node'), 14 | require.resolve('@vercel/style-guide/eslint/typescript'), 15 | ], 16 | parserOptions: { 17 | project, 18 | }, 19 | settings: { 20 | 'import/resolver': { 21 | typescript: { 22 | project, 23 | }, 24 | }, 25 | }, 26 | overrides: [ 27 | { 28 | files: ['*.ts?(x)', '*.js?(x)'], 29 | rules: { 30 | semi: ['error', 'always'], 31 | quotes: ['error', 'single'], 32 | 'object-curly-spacing': ['error', 'always'], 33 | 'array-bracket-spacing': 'off', 34 | 'no-console': 'off', 35 | 'unicorn/filename-case': 'off', 36 | 'prettier/prettier': 'error', 37 | }, 38 | }, 39 | { 40 | files: ['*.test.ts?(x)', '*.spec.js?(x)'], 41 | rules: { 42 | 'import/no-named-as-default-member': 'off', 43 | }, 44 | }, 45 | ], 46 | }; 47 | -------------------------------------------------------------------------------- /packages/dev-server/types/lib.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'metro-inspector-proxy' { 2 | import type { WebSocketServer } from 'ws'; 3 | 4 | class InspectorProxy { 5 | constructor(projectRoot: string); 6 | 7 | /** 8 | * Process HTTP request sent to server. We only respond to 2 HTTP requests: 9 | * 1. /json/version returns Chrome debugger protocol version that we use 10 | * 2. /json and /json/list returns list of page descriptions (list of inspectable apps). 11 | * This list is combined from all the connected devices. 12 | */ 13 | processRequest: ( 14 | request: IncomingMessage, 15 | response: ServerResponse, 16 | next: (error?: Error) => void, 17 | ) => void; 18 | 19 | /** 20 | * Adds websocket listeners to the provided HTTP/HTTPS server. 21 | */ 22 | createWebSocketListeners: (server: HTTPServer) => { 23 | ['/inspector/device']: WebSocketServer; 24 | ['/inspector/debug']: WebSocketServer; 25 | }; 26 | } 27 | 28 | export { InspectorProxy }; 29 | } 30 | 31 | declare module 'http' { 32 | interface IncomingMessage { 33 | // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Allow any. 34 | rawBody: any; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Modules */ 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "moduleDetection": "force", 7 | "resolveJsonModule": true, 8 | "isolatedModules": true, 9 | 10 | /* Type Checking */ 11 | "types": ["node", "jest"], 12 | "noFallthroughCasesInSwitch": true, 13 | "noImplicitReturns": true, 14 | "noUncheckedIndexedAccess": true, 15 | "noUnusedLocals": true, 16 | "noUnusedParameters": true, 17 | "strict": true, 18 | 19 | /* Language and Environment */ 20 | "experimentalDecorators": true, 21 | "jsx": "react-jsx", 22 | "lib": ["ESNext", "ESNext.AsyncIterable"], 23 | "target": "ESNext", 24 | 25 | /* Interop Constraints */ 26 | "esModuleInterop": true, 27 | "forceConsistentCasingInFileNames": true, 28 | 29 | /* Completeness */ 30 | "allowJs": false, 31 | "skipLibCheck": true, 32 | 33 | /* Others */ 34 | "stripInternal": true 35 | }, 36 | "files": ["types/index.d.ts"], 37 | "include": [ 38 | "packages/**/*", 39 | "example/**/*", 40 | "jest.config.ts", 41 | "test/setup.ts" 42 | ], 43 | "exclude": ["**/*/node_modules", "**/dist/*", "**/esm/*"] 44 | } 45 | -------------------------------------------------------------------------------- /packages/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-native-esbuild/config", 3 | "version": "0.1.0-beta.12", 4 | "type": "module", 5 | "description": "shared configs for @react-native-esbuild", 6 | "keywords": [ 7 | "react-native", 8 | "esbuild" 9 | ], 10 | "author": "leegeunhyeok ", 11 | "homepage": "https://github.com/leegeunhyeok/react-native-esbuild#readme", 12 | "license": "MIT", 13 | "main": "dist/index.js", 14 | "types": "dist/lib/index.d.ts", 15 | "scripts": { 16 | "prepack": "yarn cleanup && yarn build", 17 | "typecheck": "tsc --noEmit", 18 | "cleanup": "rimraf ./dist", 19 | "build": "node build/index.js && tsc" 20 | }, 21 | "directories": { 22 | "lib": "lib", 23 | "test": "__tests__" 24 | }, 25 | "files": [ 26 | "lib", 27 | "dist" 28 | ], 29 | "publishConfig": { 30 | "access": "public" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/leegeunhyeok/react-native-esbuild.git" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/leegeunhyeok/react-native-esbuild/issues" 38 | }, 39 | "devDependencies": { 40 | "esbuild": "^0.25.5", 41 | "typescript": "^5.8.3" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/logo.ts: -------------------------------------------------------------------------------- 1 | import { colors, isTTY } from '@react-native-esbuild/utils'; 2 | 3 | const LOGO = ` 4 | "88e "88e 5 | "88e "88e 6 | "88e "88e 7 | e88" e88" 8 | e88" e88" 9 | e88" e88" 10 | `; 11 | 12 | // Center column index of `LOGO` 13 | const LOGO_CENTER_X = 18; 14 | 15 | export const ESBUILD_LABEL = ' » esbuild '; 16 | const DESCRIPTION = 'An extremely fast bundler'; 17 | 18 | export const printLogo = (): void => { 19 | if (isTTY()) { 20 | process.stdout.write(`${colors.yellow(LOGO)}\n`); 21 | process.stdout.write( 22 | [ 23 | colors.bgYellow(colors.black(ESBUILD_LABEL)), 24 | colors.gray(DESCRIPTION), 25 | '\n', 26 | ].join(' '), 27 | ); 28 | } else { 29 | process.stdout.write(`${ESBUILD_LABEL.trim()} - ${DESCRIPTION}\n`); 30 | } 31 | }; 32 | 33 | export const printVersion = (): void => { 34 | const paddingForCenterAlign = new Array( 35 | Math.floor(LOGO_CENTER_X - (self._version as string).length / 2), 36 | ) 37 | .fill(' ') 38 | .join(''); 39 | process.stdout.write( 40 | `${isTTY() ? paddingForCenterAlign : ''}v${self._version}\n\n`, 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /packages/cli/lib/commands/serve.ts: -------------------------------------------------------------------------------- 1 | import { ReactNativeWebServer } from '@react-native-esbuild/dev-server'; 2 | import type { BundleOptions } from '@react-native-esbuild/config'; 3 | import { printDebugOptions } from '../helpers'; 4 | import { serveArgvSchema } from '../schema'; 5 | import { presets } from '../presets'; 6 | import { logger } from '../shared'; 7 | import type { Command } from '../types'; 8 | 9 | /** 10 | * Start dev server for web. 11 | */ 12 | export const serve: Command = async (argv): Promise => { 13 | const serveArgv = serveArgvSchema.parse(argv); 14 | const serveOptions = { 15 | host: serveArgv.host, 16 | port: serveArgv.port, 17 | }; 18 | 19 | const bundleOptions: Partial = { 20 | platform: 'web', 21 | entry: serveArgv['entry-file'], 22 | dev: serveArgv.dev, 23 | minify: serveArgv.minify, 24 | }; 25 | 26 | logger.debug('serve options'); 27 | printDebugOptions(serveOptions); 28 | logger.debug('bundle options'); 29 | printDebugOptions(bundleOptions); 30 | 31 | const server = await new ReactNativeWebServer( 32 | serveOptions, 33 | bundleOptions, 34 | ).initialize((bundler) => { 35 | presets.web.forEach(bundler.addPlugin.bind(bundler)); 36 | }); 37 | 38 | await server.listen(); 39 | }; 40 | -------------------------------------------------------------------------------- /packages/cli/lib/helpers/stdin.ts: -------------------------------------------------------------------------------- 1 | import readline from 'node:readline'; 2 | import { logger } from '../shared'; 3 | 4 | export const enableInteractiveMode = ( 5 | onKeypress?: (keyName: string) => void, 6 | ): boolean => { 7 | if ( 8 | !(process.stdin.isTTY && typeof process.stdin.setRawMode === 'function') 9 | ) { 10 | logger.debug('interactive mode is not supported in this environment'); 11 | return false; 12 | } 13 | 14 | /** 15 | * @see {@link https://nodejs.org/api/tty.html#readstreamsetrawmodemode} 16 | */ 17 | readline.emitKeypressEvents(process.stdin); 18 | process.stdin.setRawMode(true); 19 | process.stdin.setEncoding('utf8'); 20 | process.stdin.on( 21 | 'keypress', 22 | (_data, key: { ctrl: boolean; name: string }) => { 23 | const { ctrl, name } = key; 24 | 25 | // Handle shortcuts. 26 | if (ctrl) { 27 | switch (name) { 28 | // Ctrl + C: SIGINT 29 | case 'c': 30 | process.exit(0); 31 | break; 32 | 33 | // Ctrl + Z: SIGTSTP 34 | case 'z': 35 | process.emit('SIGTSTP', 'SIGTSTP'); 36 | break; 37 | } 38 | return; 39 | } 40 | 41 | onKeypress?.(name); 42 | }, 43 | ); 44 | 45 | return true; 46 | }; 47 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/cache/CacheController.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import type { Cache } from '../../types'; 4 | 5 | const OPTIONS = { encoding: 'utf-8' } as const; 6 | 7 | export class CacheController { 8 | private cache: Record = {}; 9 | 10 | constructor(private cacheDirectory: string) { 11 | try { 12 | fs.accessSync(cacheDirectory, fs.constants.R_OK | fs.constants.W_OK); 13 | } catch (_error) { 14 | fs.mkdirSync(cacheDirectory, { recursive: true }); 15 | } 16 | } 17 | 18 | public readFromMemory(key: string): Cache | undefined { 19 | return this.cache[key]; 20 | } 21 | 22 | public readFromFileSystem(hash: string): Promise { 23 | return fs.promises 24 | .readFile(path.join(this.cacheDirectory, hash), OPTIONS) 25 | .catch(() => null); 26 | } 27 | 28 | public writeToMemory(key: string, cacheData: Cache): void { 29 | this.cache[key] = cacheData; 30 | } 31 | 32 | public writeToFileSystem(hash: string, data: string): Promise { 33 | return fs.promises 34 | .writeFile(path.join(this.cacheDirectory, hash), data, OPTIONS) 35 | .catch(() => void 0); 36 | } 37 | 38 | public reset(): void { 39 | this.cache = {}; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Resolve react_native_pods.rb with node to allow for hoisting 2 | require Pod::Executable.execute_command('node', ['-p', 3 | 'require.resolve( 4 | "react-native/scripts/react_native_pods.rb", 5 | {paths: [process.argv[1]]}, 6 | )', __dir__]).strip 7 | 8 | platform :ios, min_ios_version_supported 9 | prepare_react_native_project! 10 | 11 | linkage = ENV['USE_FRAMEWORKS'] 12 | if linkage != nil 13 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 14 | use_frameworks! :linkage => linkage.to_sym 15 | end 16 | 17 | target 'example' do 18 | config = use_native_modules! 19 | 20 | use_react_native!( 21 | :path => config[:reactNativePath], 22 | # An absolute path to your application root. 23 | :app_path => "#{Pod::Config.instance.installation_root}/.." 24 | ) 25 | 26 | target 'exampleTests' do 27 | inherit! :complete 28 | # Pods for testing 29 | end 30 | 31 | post_install do |installer| 32 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 33 | react_native_post_install( 34 | installer, 35 | config[:reactNativePath], 36 | :mac_catalyst_enabled => false, 37 | # :ccache_enabled => true 38 | ) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /packages/jest/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.1.0-beta.12](https://github.com/leegeunhyeok/react-native-esbuild/compare/v0.1.0-beta.11...v0.1.0-beta.12) (2023-10-25) 7 | 8 | **Note:** Version bump only for package @react-native-esbuild/jest 9 | 10 | ## [0.1.0-beta.11](https://github.com/leegeunhyeok/react-native-esbuild/compare/v0.1.0-beta.10...v0.1.0-beta.11) (2023-10-25) 11 | 12 | **Note:** Version bump only for package @react-native-esbuild/jest 13 | 14 | ## [0.1.0-beta.10](https://github.com/leegeunhyeok/react-native-esbuild/compare/v0.1.0-beta.9...v0.1.0-beta.10) (2023-10-24) 15 | 16 | ### Features 17 | 18 | - **jest:** add @react-native-esbuild/jest ([53b1874](https://github.com/leegeunhyeok/react-native-esbuild/commit/53b1874fbdaa639b4f21ac0394285317075288c5)) 19 | 20 | ### Miscellaneous Chores 21 | 22 | - fix dependency versions ([f401dc8](https://github.com/leegeunhyeok/react-native-esbuild/commit/f401dc866ec231d91cd08b03b346837c150fc272)) 23 | 24 | ### Code Refactoring 25 | 26 | - function based transformer presets ([fb56af9](https://github.com/leegeunhyeok/react-native-esbuild/commit/fb56af93c9a014be97b53965001d7b62a1e74749)) 27 | -------------------------------------------------------------------------------- /packages/cli/scripts/xcode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # https://github.com/facebook/react-native/blob/v0.72.3/packages/react-native/scripts/react-native-xcode.sh 4 | 5 | case "$CONFIGURATION" in 6 | *Debug*) 7 | if [[ "$PLATFORM_NAME" == *simulator ]]; then 8 | if [[ "$FORCE_BUNDLING" ]]; then 9 | echo "FORCE_BUNDLING enabled; continuing to bundle." 10 | else 11 | echo "Skipping bundling in Debug for the Simulator (since the packager bundles for you). Use the FORCE_BUNDLING flag to change this behavior." 12 | exit 0; 13 | fi 14 | else 15 | echo "Bundling for physical device. Use the SKIP_BUNDLING flag to change this behavior." 16 | fi 17 | 18 | DEV=true 19 | ;; 20 | "") 21 | echo "$0 must be invoked by Xcode" 22 | exit 1 23 | ;; 24 | *) 25 | DEV=false 26 | ;; 27 | esac 28 | 29 | DEST=$CONFIGURATION_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH 30 | CLI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" 31 | ENTRY_FILE="$(cd "$CLI_DIR/../../.." && pwd)/index.js" 32 | BUNDLE_FILE="$DEST/main.jsbundle" 33 | 34 | node "$CLI_DIR/dist/index.js" bundle \ 35 | --entry-file=$ENTRY_FILE \ 36 | --bundle-output=$BUNDLE_FILE \ 37 | --assets-dest=$DEST \ 38 | --dev=$DEV \ 39 | --platform=ios \ 40 | --reset-cache \ 41 | --verbose 42 | -------------------------------------------------------------------------------- /docs/pages/jest/transformer.md: -------------------------------------------------------------------------------- 1 | # Testing with Jest 2 | 3 | `react-native-esbuild/jest` is a [Jest](https://jestjs.io) transformer. 4 | 5 | It can replace [babel-jest](https://github.com/facebook/react-native/blob/v0.72.6/packages/react-native/jest-preset.js#L18) and supports the React Native testing environment. 6 | 7 | ## Setup 8 | 9 | Requirements: `Jest>=27` 10 | 11 | ```bash 12 | # using npm 13 | npm install -D @react-native-esbuild/jest 14 | 15 | # using yarn 16 | yarn add -D @react-native-esbuild/jest 17 | ``` 18 | 19 | Open your `jest.config.js` and set `@react-native-esbuild/jest` as transformer. 20 | 21 | ```js 22 | // Many react-native npm modules unfortunately don't pre-compile their source code before publishing. 23 | // If you have npm dependencies that have to be transformed you can add the package name to list. 24 | const TRANSFORM_PACKAGES = [ 25 | 'react-native', 26 | 'jest-react-native', 27 | '@react-native', 28 | '@react-native-community', 29 | '@react-navigation', 30 | // ... 31 | ]; 32 | 33 | /** 34 | * @type {import('jest').Config} 35 | */ 36 | module.exports = { 37 | preset: 'react-native', 38 | transform: { 39 | '^.+\\.(t|j)sx?$': '@react-native-esbuild/jest', 40 | }, 41 | transformIgnorePatterns: [ 42 | `node_modules/(?!${TRANSFORM_PACKAGES.join('|')})/`, 43 | ], 44 | }; 45 | ``` 46 | -------------------------------------------------------------------------------- /packages/internal/lib/defaults.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @see {@link https://github.com/facebook/metro/blob/0.72.x/docs/Configuration.md#resolvermainfields} 3 | */ 4 | export const RESOLVER_MAIN_FIELDS = [ 5 | 'react-native', 6 | 'browser', 7 | 'main', 8 | 'module', 9 | ]; 10 | 11 | /** 12 | * @see {@link https://github.com/facebook/metro/blob/0.72.x/packages/metro-config/src/defaults/defaults.js} 13 | * @see {@link https://github.com/facebook/metro/blob/0.72.x/packages/metro-file-map/src/workerExclusionList.js} 14 | */ 15 | export const SOURCE_EXTENSIONS = [ 16 | '.tsx', 17 | '.ts', 18 | '.jsx', 19 | '.js', 20 | '.mjs', 21 | '.cjs', 22 | '.json', 23 | ]; 24 | 25 | export const IMAGE_EXTENSIONS = [ 26 | '.bmp', 27 | '.gif', 28 | '.ico', 29 | '.jpeg', 30 | '.jpg', 31 | '.png', 32 | '.tiff', 33 | '.tif', 34 | '.webp', 35 | ]; 36 | 37 | export const ASSET_EXTENSIONS = [ 38 | // Video extensions. 39 | '.avi', 40 | '.mp4', 41 | '.mpeg', 42 | '.mpg', 43 | '.ogv', 44 | '.webm', 45 | '.3gp', 46 | '.3g2', 47 | 48 | // Audio extensions. 49 | '.aac', 50 | '.midi', 51 | '.mid', 52 | '.mp3', 53 | '.oga', 54 | '.wav', 55 | '.3gp', 56 | '.3g2', 57 | 58 | // Font extensions. 59 | '.eot', 60 | '.otf', 61 | '.ttf', 62 | '.woff', 63 | '.woff2', 64 | 65 | // Image extensions. 66 | ...IMAGE_EXTENSIONS, 67 | ]; 68 | -------------------------------------------------------------------------------- /packages/transformer/lib/transformer/swc/swc.ts: -------------------------------------------------------------------------------- 1 | import { 2 | transform, 3 | transformSync, 4 | minify, 5 | type Options, 6 | type JsMinifyOptions, 7 | } from '@swc/core'; 8 | import type { AsyncTransformer, SyncTransformer } from '../../types'; 9 | 10 | export const transformWithSwc: AsyncTransformer = async ( 11 | code, 12 | context, 13 | preset, 14 | ) => { 15 | const { code: transformedCode } = await transform(code, preset?.(context)); 16 | 17 | if (typeof transformedCode !== 'string') { 18 | throw new Error('swc transformed source is empty'); 19 | } 20 | 21 | return transformedCode; 22 | }; 23 | 24 | export const transformSyncWithSwc: SyncTransformer = ( 25 | code, 26 | context, 27 | preset, 28 | ) => { 29 | const { code: transformedCode } = transformSync(code, preset?.(context)); 30 | 31 | if (typeof transformedCode !== 'string') { 32 | throw new Error('swc transformed source is empty'); 33 | } 34 | 35 | return transformedCode; 36 | }; 37 | 38 | export const minifyWithSwc: AsyncTransformer = async ( 39 | code, 40 | context, 41 | preset, 42 | ) => { 43 | const { code: minifiedCode } = await minify(code, preset?.(context)); 44 | 45 | if (typeof minifiedCode !== 'string') { 46 | throw new Error('swc minified source is empty'); 47 | } 48 | 49 | return minifiedCode; 50 | }; 51 | -------------------------------------------------------------------------------- /packages/internal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-native-esbuild/internal", 3 | "version": "0.1.0-beta.12", 4 | "type": "module", 5 | "description": "shared configs and helpers for internal of react-native", 6 | "keywords": [ 7 | "react-native", 8 | "esbuild" 9 | ], 10 | "author": "leegeunhyeok ", 11 | "homepage": "https://github.com/leegeunhyeok/react-native-esbuild#readme", 12 | "license": "MIT", 13 | "main": "dist/index.js", 14 | "types": "dist/lib/index.d.ts", 15 | "scripts": { 16 | "prepack": "yarn cleanup && yarn build", 17 | "cleanup": "rimraf ./dist", 18 | "typecheck": "tsc --noEmit", 19 | "build": "node build/index.js && tsc" 20 | }, 21 | "directories": { 22 | "lib": "lib", 23 | "test": "__tests__" 24 | }, 25 | "files": [ 26 | "lib", 27 | "dist" 28 | ], 29 | "publishConfig": { 30 | "access": "public" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/leegeunhyeok/react-native-esbuild.git" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/leegeunhyeok/react-native-esbuild/issues" 38 | }, 39 | "devDependencies": { 40 | "@react-native-esbuild/config": "workspace:*", 41 | "esbuild": "^0.25.5", 42 | "typescript": "^5.8.3" 43 | }, 44 | "dependencies": { 45 | "indent-string": "^4.0.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-native-esbuild/utils", 3 | "version": "0.1.0-beta.12", 4 | "type": "module", 5 | "description": "utilities for @react-native-esbuild", 6 | "keywords": [ 7 | "react-native", 8 | "esbuild" 9 | ], 10 | "author": "leegeunhyeok ", 11 | "homepage": "https://github.com/leegeunhyeok/react-native-esbuild#readme", 12 | "license": "MIT", 13 | "main": "dist/index.js", 14 | "types": "dist/lib/index.d.ts", 15 | "scripts": { 16 | "prepack": "yarn cleanup && yarn build", 17 | "cleanup": "rimraf ./dist", 18 | "typecheck": "tsc --noEmit", 19 | "build": "node build/index.js && tsc" 20 | }, 21 | "directories": { 22 | "lib": "lib", 23 | "test": "__tests__" 24 | }, 25 | "files": [ 26 | "lib", 27 | "dist" 28 | ], 29 | "publishConfig": { 30 | "access": "public" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/leegeunhyeok/react-native-esbuild.git" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/leegeunhyeok/react-native-esbuild/issues" 38 | }, 39 | "devDependencies": { 40 | "@faker-js/faker": "^8.1.0", 41 | "esbuild": "^0.25.5", 42 | "typescript": "^5.8.3" 43 | }, 44 | "dependencies": { 45 | "colors": "^1.4.0", 46 | "dayjs": "^1.11.10", 47 | "node-self": "^1.0.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/storages/SharedStorage.ts: -------------------------------------------------------------------------------- 1 | import type { BundlerSharedData } from '../../types'; 2 | import { Storage } from './Storage'; 3 | 4 | export class SharedStorage extends Storage { 5 | private getDefaultSharedData(): BundlerSharedData { 6 | return { 7 | watcher: { 8 | changed: null, 9 | stats: undefined, 10 | }, 11 | }; 12 | } 13 | 14 | public get(key: number): BundlerSharedData { 15 | if (this.data.has(key)) { 16 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Already `has()` checked. 17 | return this.data.get(key)!; 18 | } 19 | 20 | const sharedData = this.getDefaultSharedData(); 21 | this.data.set(key, sharedData); 22 | 23 | return sharedData; 24 | } 25 | 26 | public setValue(value: Partial): void { 27 | for (const sharedData of this.data.values()) { 28 | sharedData.watcher.changed = 29 | value.watcher?.changed ?? sharedData.watcher.changed; 30 | sharedData.watcher.stats = 31 | value.watcher?.stats ?? sharedData.watcher.stats; 32 | } 33 | } 34 | 35 | public clearAll(): Promise { 36 | for (const sharedData of this.data.values()) { 37 | sharedData.watcher.changed = null; 38 | sharedData.watcher.stats = undefined; 39 | } 40 | return Promise.resolve(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/plugins/lib/reactNativeRuntimeTransformPlugin/helpers/caches.ts: -------------------------------------------------------------------------------- 1 | import type { CacheController } from '@react-native-esbuild/core'; 2 | import type { CacheConfig } from '../../types'; 3 | 4 | export const getTransformedCodeFromInMemoryCache = ( 5 | controller: CacheController, 6 | cacheConfig: CacheConfig, 7 | ): string | null => { 8 | const inMemoryCache = controller.readFromMemory(cacheConfig.hash); 9 | // If file is not modified, use cache data instead. 10 | return inMemoryCache && inMemoryCache.modifiedAt === cacheConfig.mtimeMs 11 | ? inMemoryCache.data 12 | : null; 13 | }; 14 | 15 | export const getTransformedCodeFromFileSystemCache = async ( 16 | controller: CacheController, 17 | cacheConfig: CacheConfig, 18 | ): Promise => { 19 | const fsCache = await controller.readFromFileSystem(cacheConfig.hash); 20 | return fsCache ?? null; 21 | }; 22 | 23 | export const writeTransformedCodeToInMemoryCache = ( 24 | controller: CacheController, 25 | code: string, 26 | cacheConfig: CacheConfig, 27 | ): void => { 28 | controller.writeToMemory(cacheConfig.hash, { 29 | data: code, 30 | modifiedAt: cacheConfig.mtimeMs, 31 | }); 32 | }; 33 | 34 | export const writeTransformedCodeToFileSystemCache = ( 35 | controller: CacheController, 36 | code: string, 37 | cacheConfig: CacheConfig, 38 | ): Promise => { 39 | return controller.writeToFileSystem(cacheConfig.hash, code); 40 | }; 41 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/helpers/__tests__/config.test.ts: -------------------------------------------------------------------------------- 1 | import { faker } from '@faker-js/faker'; 2 | import { loadConfig, getConfigFromGlobal } from '../config'; 3 | import type { Config } from '../../../types'; 4 | 5 | describe('config helpers', () => { 6 | describe('loadConfig', () => { 7 | beforeEach(() => { 8 | self._config = undefined; 9 | }); 10 | 11 | afterAll(() => { 12 | delete self._config; 13 | }); 14 | 15 | describe('when call loadConfig', () => { 16 | beforeEach(() => { 17 | loadConfig(faker.system.filePath()); 18 | }); 19 | 20 | it('should caching config data to global', () => { 21 | expect(self._config).not.toBeUndefined(); 22 | }); 23 | }); 24 | }); 25 | 26 | describe('getConfigFromGlobal', () => { 27 | beforeEach(() => { 28 | delete self._config; 29 | }); 30 | 31 | describe('when call before load', () => { 32 | it('should throw error', () => { 33 | expect(() => getConfigFromGlobal()).toThrowError(); 34 | }); 35 | }); 36 | 37 | describe('when call after load', () => { 38 | let config: Config; 39 | 40 | beforeEach(() => { 41 | config = loadConfig(faker.system.filePath()); 42 | }); 43 | 44 | it('should return cached config object', () => { 45 | expect(getConfigFromGlobal()).toEqual(expect.objectContaining(config)); 46 | }); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/storages/CacheStorage.ts: -------------------------------------------------------------------------------- 1 | import os from 'node:os'; 2 | import fs from 'node:fs'; 3 | import path from 'node:path'; 4 | import { GLOBAL_CACHE_DIR } from '@react-native-esbuild/config'; 5 | import { logger } from '../../shared'; 6 | import { CacheController } from '../cache'; 7 | import { Storage } from './Storage'; 8 | 9 | const CACHE_DIRECTORY = path.join(os.tmpdir(), GLOBAL_CACHE_DIR); 10 | 11 | export class CacheStorage extends Storage { 12 | constructor() { 13 | super(); 14 | try { 15 | fs.accessSync(CACHE_DIRECTORY, fs.constants.R_OK | fs.constants.W_OK); 16 | } catch (_error) { 17 | logger.debug('cache directory is not exist or no access permission'); 18 | fs.mkdirSync(CACHE_DIRECTORY, { recursive: true }); 19 | } 20 | } 21 | 22 | public get(key: number): CacheController { 23 | if (this.data.has(key)) { 24 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Already `has()` checked. 25 | return this.data.get(key)!; 26 | } 27 | 28 | const controller = new CacheController(CACHE_DIRECTORY); 29 | this.data.set(key, controller); 30 | 31 | return controller; 32 | } 33 | 34 | public clearAll(): Promise { 35 | for (const controller of this.data.values()) { 36 | controller.reset(); 37 | } 38 | return fs.promises.rm(CACHE_DIRECTORY, { 39 | recursive: true, 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/symbolicate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-native-esbuild/symbolicate", 3 | "version": "0.1.0-beta.12", 4 | "type": "module", 5 | "description": "symbolicate utilities for @react-native-esbuild", 6 | "keywords": [ 7 | "react-native", 8 | "esbuild", 9 | "sourcemap", 10 | "symbolicate" 11 | ], 12 | "author": "leegeunhyeok ", 13 | "homepage": "https://github.com/leegeunhyeok/react-native-esbuild#readme", 14 | "license": "MIT", 15 | "main": "dist/index.js", 16 | "types": "dist/lib/index.d.ts", 17 | "scripts": { 18 | "prepack": "yarn cleanup && yarn build", 19 | "cleanup": "rimraf ./dist", 20 | "typecheck": "tsc --noEmit", 21 | "build": "node build/index.js && tsc" 22 | }, 23 | "directories": { 24 | "lib": "lib", 25 | "test": "__tests__" 26 | }, 27 | "files": [ 28 | "lib", 29 | "dist" 30 | ], 31 | "publishConfig": { 32 | "access": "public" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git+https://github.com/leegeunhyeok/react-native-esbuild.git" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/leegeunhyeok/react-native-esbuild/issues" 40 | }, 41 | "devDependencies": { 42 | "@types/babel__code-frame": "^7.0.6", 43 | "esbuild": "^0.25.5", 44 | "typescript": "^5.8.3" 45 | }, 46 | "dependencies": { 47 | "@babel/code-frame": "^7.27.1", 48 | "source-map": "^0.7.4" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/transformer/lib/pipelines/pipeline.ts: -------------------------------------------------------------------------------- 1 | import type { OnLoadArgs } from 'esbuild'; 2 | import md5 from 'md5'; 3 | import type { TransformStep, TransformerContext } from '../types'; 4 | 5 | export abstract class TransformPipeline> { 6 | protected steps: Step[] = []; 7 | protected onBeforeTransform?: Step; 8 | protected onAfterTransform?: Step; 9 | 10 | constructor(protected context: Omit) {} 11 | 12 | /** 13 | * Generate hash that contains the file path, modification time, and bundle options. 14 | * 15 | * `id` is combined(platform, dev, minify) bundle options value in `@react-native-esbuild`. 16 | * 17 | * hash = md5(id + modified time + file path + core version) 18 | * number + number + string + string 19 | */ 20 | protected getHash(id: number, filepath: string, modifiedAt: number): string { 21 | return md5(id + modifiedAt + filepath + self._version); 22 | } 23 | 24 | public beforeTransform(onBeforeTransform: Step): this { 25 | this.onBeforeTransform = onBeforeTransform; 26 | return this; 27 | } 28 | 29 | public afterTransform(onAfterTransform: Step): this { 30 | this.onAfterTransform = onAfterTransform; 31 | return this; 32 | } 33 | 34 | public addStep(step: Step): this { 35 | this.steps.push(step); 36 | return this; 37 | } 38 | 39 | abstract transform(code: string, args: OnLoadArgs): ReturnType; 40 | } 41 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | **/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | # 39 | node_modules/ 40 | npm-debug.log 41 | yarn-error.log 42 | 43 | # fastlane 44 | # 45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 46 | # screenshots whenever they are needed. 47 | # For more information about the recommended setup visit: 48 | # https://docs.fastlane.tools/best-practices/source-control/ 49 | 50 | **/fastlane/report.xml 51 | **/fastlane/Preview.html 52 | **/fastlane/screenshots 53 | **/fastlane/test_output 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # Ruby / CocoaPods 59 | **/Pods/ 60 | /vendor/bundle/ 61 | 62 | # Temporary files created by Metro to check the health of the file watcher 63 | .metro-health-check* 64 | 65 | # testing 66 | /coverage 67 | 68 | # yarn berry 69 | .yarn/* 70 | !.yarn/patches 71 | !.yarn/plugins 72 | !.yarn/releases 73 | !.yarn/sdks 74 | !.yarn/versions 75 | 76 | # @react-native-esbuild 77 | .rne 78 | .swc 79 | 80 | # @react-native-esbuild for web 81 | public/ 82 | -------------------------------------------------------------------------------- /packages/cli/lib/commands/bundle.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { ReactNativeEsbuildBundler } from '@react-native-esbuild/core'; 3 | import { 4 | DEFAULT_ENTRY_POINT, 5 | type BundleOptions, 6 | } from '@react-native-esbuild/config'; 7 | import { printDebugOptions } from '../helpers'; 8 | import { bundleArgvSchema } from '../schema'; 9 | import { presets } from '../presets'; 10 | import { logger } from '../shared'; 11 | import type { Command } from '../types'; 12 | 13 | /** 14 | * Create bundle command. 15 | */ 16 | export const bundle: Command = async (argv) => { 17 | const bundleArgv = bundleArgvSchema.parse(argv); 18 | const bundleOptions: Partial = { 19 | entry: path.resolve(bundleArgv['entry-file'] ?? DEFAULT_ENTRY_POINT), 20 | sourcemap: bundleArgv['sourcemap-output'], 21 | outfile: bundleArgv['bundle-output'], 22 | assetsDir: bundleArgv['assets-dest'], 23 | platform: bundleArgv.platform, 24 | dev: bundleArgv.dev, 25 | minify: bundleArgv.minify, 26 | metafile: bundleArgv.metafile, 27 | }; 28 | const root = bundleOptions.entry 29 | ? path.dirname(bundleOptions.entry) 30 | : process.cwd(); 31 | logger.debug('bundle options'); 32 | printDebugOptions(bundleOptions); 33 | 34 | const bundler = new ReactNativeEsbuildBundler(root); 35 | (bundleOptions.platform === 'web' ? presets.web : presets.native).forEach( 36 | bundler.addPlugin.bind(bundler), 37 | ); 38 | 39 | await bundler.initialize(); 40 | await bundler.bundle(bundleOptions); 41 | }; 42 | -------------------------------------------------------------------------------- /docs/theme.config.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { DocsThemeConfig, useConfig } from 'nextra-theme-docs' 3 | import { useRouter } from 'next/router' 4 | import Image from 'next/image' 5 | 6 | const config: DocsThemeConfig = { 7 | logo: ( 8 | <> 9 | logo 16 | React Native Esbuild 17 | 18 | ), 19 | sidebar: { 20 | defaultMenuCollapseLevel: 1, 21 | }, 22 | project: { 23 | link: 'https://github.com/leegeunhyeok/react-native-esbuild', 24 | }, 25 | docsRepositoryBase: 'https://github.com/leegeunhyeok/react-native-esbuild', 26 | footer: { 27 | text: 'React Native Esbuild', 28 | }, 29 | head: () => { 30 | const { asPath, defaultLocale, locale } = useRouter() 31 | const { frontMatter, title } = useConfig() 32 | const url = 33 | 'https://react-native-esbuild.vercel.app' + 34 | (defaultLocale === locale ? asPath : `/${locale}${asPath}`) 35 | 36 | return ( 37 | <> 38 | 39 | 40 | 41 | 45 | {`${title} - RNE`} 46 | 47 | ) 48 | } 49 | } 50 | 51 | export default config 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-esbuild", 3 | "private": true, 4 | "workspaces": [ 5 | "docs", 6 | "example", 7 | "packages/*" 8 | ], 9 | "installConfig": { 10 | "hoistingLimits": "workspaces" 11 | }, 12 | "scripts": { 13 | "prepare": "husky install", 14 | "build:all": "nx run-many --target=build", 15 | "typecheck:all": "nx run-many --target=typecheck", 16 | "test": "jest", 17 | "test:coverage": "jest --coverage --json --outputFile=coverage/report.json", 18 | "lint": "eslint packages", 19 | "example": "yarn --cwd example", 20 | "release": "nx release", 21 | "clean": "rimraf --glob \"packages/**/*/dist\" .nx" 22 | }, 23 | "devDependencies": { 24 | "@faker-js/faker": "^9.8.0", 25 | "@nx/js": "^21.1.2", 26 | "@swc/core": "^1.11.29", 27 | "@swc/jest": "^0.2.38", 28 | "@types/invariant": "^2.2.37", 29 | "@types/jest": "^29.5.14", 30 | "@types/node": "^22", 31 | "@vercel/style-guide": "^5", 32 | "esbuild": "^0.25.5", 33 | "eslint": "^8.49.0", 34 | "eslint-plugin-prettier": "^5.4.1", 35 | "husky": "^9.1.7", 36 | "jest": "^29.7.0", 37 | "node-self": "^1.0.2", 38 | "nx": "^21.1.2", 39 | "prettier": "^3.5.3", 40 | "react-native": "0.72.6", 41 | "rimraf": "^6.0.1", 42 | "ts-node": "^10.9.2", 43 | "typescript": "^5.8.3" 44 | }, 45 | "prettier": "@vercel/style-guide/prettier", 46 | "packageManager": "yarn@4.9.1", 47 | "resolutions": { 48 | "dripsy@^4.3.8": "patch:dripsy@npm%3A4.3.8#./.yarn/patches/dripsy-npm-4.3.8-208587309d.patch" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /docs/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-native-esbuild/cli", 3 | "version": "0.1.0-beta.12", 4 | "type": "module", 5 | "description": "cli tool for @react-native-esbuild", 6 | "keywords": [ 7 | "react-native", 8 | "esbuild" 9 | ], 10 | "author": "leegeunhyeok ", 11 | "homepage": "https://github.com/leegeunhyeok/react-native-esbuild#readme", 12 | "license": "MIT", 13 | "bin": { 14 | "rne": "./dist/index.js" 15 | }, 16 | "scripts": { 17 | "prepack": "yarn cleanup && yarn build", 18 | "cleanup": "rimraf ./dist", 19 | "typecheck": "tsc --noEmit", 20 | "build": "node build/index.js", 21 | "start": "tsc && node ./dist/index.js" 22 | }, 23 | "directories": { 24 | "lib": "lib", 25 | "test": "__tests__" 26 | }, 27 | "files": [ 28 | "lib", 29 | "dist", 30 | "scripts" 31 | ], 32 | "publishConfig": { 33 | "access": "public" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/leegeunhyeok/react-native-esbuild.git" 38 | }, 39 | "bugs": { 40 | "url": "https://github.com/leegeunhyeok/react-native-esbuild/issues" 41 | }, 42 | "dependencies": { 43 | "@react-native-esbuild/config": "workspace:*", 44 | "@react-native-esbuild/core": "workspace:*", 45 | "@react-native-esbuild/dev-server": "workspace:*", 46 | "@react-native-esbuild/plugins": "workspace:*", 47 | "@react-native-esbuild/utils": "workspace:*", 48 | "yargs": "^17.7.2", 49 | "zod": "^3.22.2" 50 | }, 51 | "devDependencies": { 52 | "@types/yargs": "^17.0.24", 53 | "esbuild": "^0.25.5", 54 | "typescript": "^5.8.3" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/transformer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-native-esbuild/transformer", 3 | "version": "0.1.0-beta.12", 4 | "type": "module", 5 | "description": "transformer for @react-native-esbuild", 6 | "keywords": [ 7 | "react-native", 8 | "esbuild", 9 | "sucrase", 10 | "babel" 11 | ], 12 | "author": "leegeunhyeok ", 13 | "homepage": "https://github.com/leegeunhyeok/react-native-esbuild#readme", 14 | "license": "MIT", 15 | "main": "dist/index.js", 16 | "types": "dist/lib/index.d.ts", 17 | "scripts": { 18 | "prepack": "yarn cleanup && yarn build", 19 | "cleanup": "rimraf ./dist", 20 | "typecheck": "tsc --noEmit", 21 | "build": "node build/index.js && tsc" 22 | }, 23 | "directories": { 24 | "lib": "lib", 25 | "test": "__tests__" 26 | }, 27 | "files": [ 28 | "lib" 29 | ], 30 | "publishConfig": { 31 | "access": "public" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "git+https://github.com/leegeunhyeok/react-native-esbuild.git" 36 | }, 37 | "bugs": { 38 | "url": "https://github.com/leegeunhyeok/react-native-esbuild/issues" 39 | }, 40 | "devDependencies": { 41 | "@types/md5": "^2.3.4", 42 | "esbuild": "^0.25.5", 43 | "typescript": "^5.8.3" 44 | }, 45 | "dependencies": { 46 | "@babel/core": "^7.25.2", 47 | "@react-native-esbuild/config": "workspace:*", 48 | "@swc/core": "^1.11.29", 49 | "@swc/helpers": "^0.5.17", 50 | "hermes-parser": "^0.28.1", 51 | "md5": "^2.3.0", 52 | "sucrase": "^3.35.0", 53 | "swc-plugin-coverage-instrument": "^0.0.27", 54 | "swc_mut_cjs_exports": "^10.7.0" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/jest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-native-esbuild/jest", 3 | "version": "0.1.0-beta.12", 4 | "type": "module", 5 | "description": "react-native preset for jest powered by @react-native-esbuild", 6 | "keywords": [ 7 | "react-native", 8 | "esbuild", 9 | "jest" 10 | ], 11 | "author": "leegeunhyeok ", 12 | "homepage": "https://github.com/leegeunhyeok/react-native-esbuild#readme", 13 | "license": "MIT", 14 | "main": "dist/index.js", 15 | "types": "dist/lib/index.d.ts", 16 | "scripts": { 17 | "prepack": "yarn cleanup && yarn build", 18 | "cleanup": "rimraf ./dist", 19 | "typecheck": "tsc --noEmit", 20 | "build": "node build/index.js && tsc" 21 | }, 22 | "directories": { 23 | "lib": "lib", 24 | "test": "__tests__" 25 | }, 26 | "files": [ 27 | "lib", 28 | "dist", 29 | "jest-preset.js" 30 | ], 31 | "publishConfig": { 32 | "access": "public" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git+https://github.com/leegeunhyeok/react-native-esbuild.git" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/leegeunhyeok/react-native-esbuild/issues" 40 | }, 41 | "peerDependencies": { 42 | "react-native": "*" 43 | }, 44 | "dependencies": { 45 | "@jest/create-cache-key-function": "^29.7.0", 46 | "@react-native-esbuild/core": "workspace:*", 47 | "@react-native-esbuild/internal": "workspace:*", 48 | "@react-native-esbuild/transformer": "workspace:*", 49 | "md5": "^2.3.0" 50 | }, 51 | "devDependencies": { 52 | "@jest/transform": "^29.7.0", 53 | "@types/md5": "^2.3.2", 54 | "esbuild": "^0.25.5", 55 | "typescript": "^5.8.3" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/core/lib/bundler/events/index.ts: -------------------------------------------------------------------------------- 1 | import EventEmitter from 'node:events'; 2 | import type { 3 | BundlerAdditionalData, 4 | BuildStatus, 5 | ReportableEvent, 6 | } from '../../types'; 7 | 8 | export class BundlerEventEmitter extends EventEmitter { 9 | declare addListener: ( 10 | type: EventType, 11 | listener: BundlerEventListener, 12 | ) => this; 13 | declare removeEventListener: ( 14 | type: EventType, 15 | listener: BundlerEventListener, 16 | ) => this; 17 | declare on: ( 18 | type: EventType, 19 | listener: BundlerEventListener, 20 | ) => this; 21 | declare off: ( 22 | type: EventType, 23 | listener: BundlerEventListener, 24 | ) => this; 25 | declare emit: ( 26 | type: EventType, 27 | payload: BundlerEventPayload[EventType], 28 | ) => boolean; 29 | } 30 | 31 | export type BundlerEventType = 32 | | 'build-start' 33 | | 'build-end' 34 | | 'build-status-change' 35 | | 'report'; 36 | 37 | export type BundlerEventListener = ( 38 | payload: BundlerEventPayload[EventType], 39 | ) => void; 40 | 41 | export interface BundlerEventPayload { 42 | 'build-start': { id: number; additionalData?: BundlerAdditionalData }; 43 | 'build-end': { 44 | id: number; 45 | revisionId: string; 46 | additionalData?: BundlerAdditionalData; 47 | }; 48 | 'build-status-change': BuildStatus & { 49 | id: number; 50 | additionalData?: BundlerAdditionalData; 51 | }; 52 | report: ReportableEvent; 53 | } 54 | -------------------------------------------------------------------------------- /packages/cli/lib/schema/index.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { SUPPORT_PLATFORMS } from '@react-native-esbuild/config'; 3 | import { z } from 'zod'; 4 | 5 | const resolvePath = (filepath: string): string => 6 | path.resolve(process.cwd(), filepath); 7 | 8 | export const baseArgvSchema = z.object({ 9 | verbose: z.boolean().optional(), 10 | config: z.string().optional(), 11 | 'reset-cache': z.boolean().optional(), 12 | }); 13 | 14 | export const startArgvSchema = z.object({ 15 | host: z.string().optional(), 16 | port: z.number().optional(), 17 | 'entry-file': z.string().transform(resolvePath).optional(), 18 | }); 19 | 20 | export const serveArgvSchema = z.object({ 21 | host: z.string().optional(), 22 | port: z.number().optional(), 23 | dev: z.boolean().optional(), 24 | minify: z.boolean().optional(), 25 | template: z.string().transform(resolvePath).optional(), 26 | 'entry-file': z.string().transform(resolvePath).optional(), 27 | }); 28 | 29 | export const bundleArgvSchema = z.object({ 30 | /** 31 | * Type infer issue with using `map()`. 32 | * 33 | * ```ts 34 | * // not work 35 | * z.union(SUPPORT_PLATFORMS.map(z.literal)), 36 | * ``` 37 | */ 38 | platform: z 39 | .union([ 40 | z.literal(SUPPORT_PLATFORMS[0]), 41 | z.literal(SUPPORT_PLATFORMS[1]), 42 | z.literal(SUPPORT_PLATFORMS[2]), 43 | ]) 44 | .optional(), 45 | dev: z.boolean().optional(), 46 | minify: z.boolean().optional(), 47 | metafile: z.boolean().optional(), 48 | 'entry-file': z.string().transform(resolvePath).optional(), 49 | 'sourcemap-output': z.string().transform(resolvePath).optional(), 50 | 'bundle-output': z.string().transform(resolvePath).optional(), 51 | 'assets-dest': z.string().optional(), 52 | }); 53 | -------------------------------------------------------------------------------- /packages/cli/lib/commands/start.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable quotes -- Allow quote in template literal */ 2 | import path from 'node:path'; 3 | import { ReactNativeAppServer } from '@react-native-esbuild/dev-server'; 4 | import { DEFAULT_ENTRY_POINT } from '@react-native-esbuild/config'; 5 | import { enableInteractiveMode, printDebugOptions } from '../helpers'; 6 | import { startArgvSchema } from '../schema'; 7 | import { presets } from '../presets'; 8 | import { logger } from '../shared'; 9 | import type { Command } from '../types'; 10 | 11 | /** 12 | * Start dev server for native. 13 | */ 14 | export const start: Command = async (argv) => { 15 | const startArgv = startArgvSchema.parse(argv); 16 | const entry = path.resolve(startArgv['entry-file'] ?? DEFAULT_ENTRY_POINT); 17 | const serveOptions = { 18 | host: startArgv.host, 19 | port: startArgv.port, 20 | }; 21 | logger.debug('start options'); 22 | printDebugOptions({ entry, ...serveOptions }); 23 | 24 | const server = await new ReactNativeAppServer(serveOptions).initialize( 25 | (bundler) => { 26 | presets.native.forEach(bundler.addPlugin.bind(bundler)); 27 | }, 28 | ); 29 | 30 | await server.listen(() => { 31 | if ( 32 | enableInteractiveMode((keyName) => { 33 | switch (keyName) { 34 | case 'r': 35 | logger.info(`sending 'reload' command...`); 36 | server.broadcastCommand('reload'); 37 | break; 38 | 39 | case 'd': 40 | logger.info(`sending 'devMenu' command...`); 41 | server.broadcastCommand('devMenu'); 42 | break; 43 | } 44 | }) 45 | ) { 46 | logger.info(`> press 'r' to reload`); 47 | logger.info(`> press 'd' to open developer menu`); 48 | logger.nl(); 49 | } 50 | }); 51 | }; 52 | -------------------------------------------------------------------------------- /packages/plugins/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-native-esbuild/plugins", 3 | "version": "0.1.0-beta.12", 4 | "type": "module", 5 | "description": "plugins for @react-native-esbuild", 6 | "keywords": [ 7 | "react-native", 8 | "esbuild" 9 | ], 10 | "author": "leegeunhyeok ", 11 | "homepage": "https://github.com/leegeunhyeok/react-native-esbuild#readme", 12 | "license": "MIT", 13 | "main": "dist/index.js", 14 | "types": "dist/lib/index.d.ts", 15 | "scripts": { 16 | "prepack": "yarn cleanup && yarn build", 17 | "cleanup": "rimraf ./dist", 18 | "typecheck": "tsc --noEmit", 19 | "build": "node build/index.js && tsc" 20 | }, 21 | "directories": { 22 | "lib": "lib", 23 | "test": "__tests__" 24 | }, 25 | "files": [ 26 | "lib", 27 | "dist" 28 | ], 29 | "publishConfig": { 30 | "access": "public" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/leegeunhyeok/react-native-esbuild.git" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/leegeunhyeok/react-native-esbuild/issues" 38 | }, 39 | "devDependencies": { 40 | "@faker-js/faker": "^8.1.0", 41 | "@types/invariant": "^2.2.36", 42 | "@types/md5": "^2.3.2", 43 | "deepmerge": "^4.3.1", 44 | "esbuild": "^0.25.5", 45 | "typescript": "^5.8.3" 46 | }, 47 | "dependencies": { 48 | "@react-native-esbuild/config": "workspace:*", 49 | "@react-native-esbuild/core": "workspace:*", 50 | "@react-native-esbuild/internal": "workspace:*", 51 | "@react-native-esbuild/transformer": "workspace:*", 52 | "@react-native-esbuild/utils": "workspace:*", 53 | "@svgr/core": "^8.1.0", 54 | "@svgr/plugin-jsx": "^8.1.0", 55 | "image-size": "^1.0.2", 56 | "invariant": "^2.2.4", 57 | "md5": "^2.3.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /example/ios/example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | 30 | NSAllowsArbitraryLoads 31 | 32 | NSAllowsLocalNetworking 33 | 34 | 35 | NSLocationWhenInUseUsageDescription 36 | 37 | UILaunchStoryboardName 38 | LaunchScreen 39 | UIRequiredDeviceCapabilities 40 | 41 | arm64 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UIViewControllerBasedStatusBarAppearance 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /packages/cli/lib/index.ts: -------------------------------------------------------------------------------- 1 | import { ReactNativeEsbuildBundler } from '@react-native-esbuild/core'; 2 | import { LogLevel } from '@react-native-esbuild/utils'; 3 | import { cli } from './cli'; 4 | import * as Commands from './commands'; 5 | import { getCommand, handleUncaughtException } from './helpers'; 6 | import { baseArgvSchema } from './schema'; 7 | import { logger } from './shared'; 8 | 9 | (async () => { 10 | const argv = await cli(); 11 | const options = baseArgvSchema.parse(argv); 12 | ReactNativeEsbuildBundler.bootstrap(options.config); 13 | ReactNativeEsbuildBundler.setGlobalLogLevel( 14 | options.verbose ? LogLevel.Trace : LogLevel.Info, 15 | ); 16 | self.shouldResetCache = options['reset-cache']; 17 | 18 | switch (getCommand(argv)) { 19 | case 'start': 20 | await Commands.start(argv); 21 | break; 22 | 23 | case 'serve': 24 | await Commands.serve(argv); 25 | break; 26 | 27 | case 'bundle': 28 | await Commands.bundle(argv); 29 | break; 30 | 31 | case 'cache': 32 | await Commands.cache(argv, getCommand(argv, 1)); 33 | break; 34 | 35 | case 'ram-bundle': 36 | // eslint-disable-next-line quotes -- Log message. 37 | logger.warn(`'ram-bundle' command is not supported`); 38 | process.exit(1); 39 | } 40 | })().catch((error) => { 41 | logger.error('cannot execute command', error as Error); 42 | process.exit(1); 43 | }); 44 | 45 | process 46 | .on('unhandledRejection', (reason, promise) => { 47 | logger.error('unhandled rejection at promise', undefined, { 48 | reason, 49 | promise, 50 | }); 51 | process.exit(1); 52 | }) 53 | .on('uncaughtException', (error) => { 54 | if (!handleUncaughtException(error)) { 55 | logger.error('uncaught exception thrown', error); 56 | } 57 | process.exit(1); 58 | }); 59 | -------------------------------------------------------------------------------- /example/src/screens/MainScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import { useSafeAreaInsets } from 'react-native-safe-area-context'; 4 | import { Container, View, H1, P } from 'dripsy'; 5 | import { Fade, Button } from '../components'; 6 | import type { RootStackProps } from '../navigators/types'; 7 | import LogoSvg from '../assets/logo.svg'; 8 | 9 | export function MainScreen({ 10 | navigation, 11 | }: RootStackProps<'Main'>): React.ReactElement { 12 | const { top, bottom } = useSafeAreaInsets(); 13 | 14 | const handlePressStartButton = (): void => { 15 | navigation.navigate('Intro'); 16 | }; 17 | 18 | return ( 19 | 20 | 21 | 22 |

React Native Esbuild

23 |

24 | ⚡️ An extremely fast bundler{'\n'}+{'\n'}React Native 25 |

26 |
27 | 28 |