### Installation and Execution Source: https://github.com/rapidnative/reactnative-run/blob/main/expo-server/README.md Standard commands to install dependencies and start the server for local development. ```bash npm install node server.js # Open in Expo Go: # exp://:8088 # Or on iOS Simulator: # xcrun simctl openurl booted "exp://:8088" ``` -------------------------------- ### Start Development Server Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/example.md Run this command from the repository root to start all services for the example app. Alternatively, navigate to the example directory and run it standalone if reactnative-esm is already running. ```bash # From the repo root (starts all services) npm run dev # Or standalone (requires reactnative-esm running separately) cd browser-metro/example npm run dev ``` -------------------------------- ### Start the example application Source: https://github.com/rapidnative/reactnative-run/blob/main/CLAUDE.md Launches the Vite-based demo application, which includes an editor and preview iframes, on port 5201. ```bash cd browser-metro/example && npm run dev ``` -------------------------------- ### Start Development Services Source: https://github.com/rapidnative/reactnative-run/blob/main/README.md Commands to install dependencies and launch the development environment services. ```bash # Install dependencies npm install # Start everything (ESM server + library watch + playground + website) npm run dev # Or start individual services npm run dev:bundler # ESM server + browser-metro + playground npm run dev:website # Next.js website only ``` -------------------------------- ### Example Project Structure Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/example.md This outlines the directory structure of the example application, including entry points, source files, scripts, user projects, and configuration files. ```tree example/ index.html # Vite entry point src/ main.tsx # React root App.tsx # Main app (editor, preview iframes, console, HTML blob builder) App.css # Catppuccin dark theme styles (incl. rich error display) bundler.worker.ts # Web worker orchestrator (creates per-target bundler instances) editor-fs.ts # EditorFS with change tracking for watch mode plugins/ web-plugin.ts # Aliases, shims, globals for web target expo-web.ts # React import injection + react-native alias for Expo scripts/ build-projects.ts # Generates projects.json from user_projects/ user_projects/ # Sample projects basic/ # Plain JS with lodash typescript/ # TypeScript project react/ # React app with components public/ projects.json # Generated file (gitignored) package.json vite.config.ts tsconfig.json ``` -------------------------------- ### Example URL Requests Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/reactnative-esm.md These examples demonstrate various ways to request packages, including specific versions and subpaths. ```http /pkg/lodash ``` ```http /pkg/lodash@4.17.21 ``` ```http /pkg/react-dom/client ``` ```http /pkg/react-dom@19/client ``` ```http /pkg/@scope/name ``` ```http /pkg/@scope/name@1.0/sub ``` -------------------------------- ### Quick Start with Bundler Source: https://github.com/rapidnative/reactnative-run/blob/main/browser-metro/README.md Initialize a Bundler instance with a VirtualFS and perform a one-shot bundle of an entry file. ```typescript import { Bundler, VirtualFS, typescriptTransformer } from "browser-metro"; import type { BundlerConfig, FileMap } from "browser-metro"; const files: FileMap = { "/index.ts": 'import { greet } from "./utils";\nconsole.log(greet("World"));', "/utils.ts": 'export function greet(name: string) { return "Hello, " + name; }', }; const bundler = new Bundler(new VirtualFS(files), { resolver: { sourceExts: ["ts", "tsx", "js", "jsx"] }, transformer: typescriptTransformer, server: { packageServerUrl: "https://esm.reactnative.run" }, }); const code = await bundler.bundle("/index.ts"); // code is a self-executing bundle with inline source map ``` -------------------------------- ### Install Project Dependencies Source: https://github.com/rapidnative/reactnative-run/blob/main/browser-metro/example/user_projects/expo/README.md Run this command in your project directory to install all necessary npm packages. ```bash npm install ``` -------------------------------- ### Start Package Server Locally Source: https://context7.com/rapidnative/reactnative-run/llms.txt Commands to initialize the local package server for on-demand bundling. ```bash # Start the package server locally cd reactnative-esm && npm start # Server runs at http://localhost:5200 # Production server ``` -------------------------------- ### Project Switching URL Examples Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/example.md Demonstrates how to switch between different projects using URL query parameters. ```url http://localhost:5201/ -> basic (default) ``` ```url http://localhost:5201/?project=typescript -> typescript ``` ```url http://localhost:5201/?project=react -> react ``` -------------------------------- ### Usage Instructions Source: https://github.com/rapidnative/reactnative-run/blob/main/expo-server/README.md Instructions on how to install and run the Expo Native Server. ```APIDOC ## Usage ### Installation ```bash npm install ``` ### Running the Server ```bash node server.js ``` ### Opening in Expo Go **On a physical device or simulator:** `exp://:8088` **On iOS Simulator:** ```bash xcrun simctl openurl booted "exp://:8088" ``` ``` -------------------------------- ### Example URL Mappings Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/esm-server/page.mdx Illustrates how different URL formats map to package names, versions, and subpaths. ```text | URL | |---|---| | `/pkg/lodash` | lodash | | `/pkg/lodash@4.17.21` | lodash | 4.17.21 | | `/pkg/react-dom/client` | react-dom | latest | /client | | `/pkg/react-dom@19/client` | react-dom | 19 | /client | | `/pkg/@scope/name@1.0/sub` | @scope/name | 1.0 | /sub | ``` -------------------------------- ### Start the development server Source: https://github.com/rapidnative/reactnative-run/blob/main/website/README.md Commands to launch the local development environment using different package managers. ```bash npm run dev # or yarn dev # or pnpm dev # or bun dev ``` -------------------------------- ### ESM Package Server Endpoints Source: https://github.com/rapidnative/reactnative-run/blob/main/README.md Example GET requests for fetching npm packages via the ESM server. ```text GET /pkg/lodash -> lodash@latest GET /pkg/lodash@4.17.21 -> lodash@4.17.21 GET /pkg/react-dom/client -> react-dom@latest, subpath /client GET /pkg/@scope/name@1.0/sub -> scoped package with subpath ``` -------------------------------- ### npm Package Resolution Examples Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/resolution/page.mdx Explains how imports that do not start with '.' or '/' are treated as npm packages. This includes direct package names, subpath imports, and scoped packages. ```text require("react") → npm package "react" require("lodash/chunk") → npm package "lodash", subpath "/chunk" require("@expo/vector-icons") → scoped npm package ``` -------------------------------- ### Install browser-metro Source: https://github.com/rapidnative/reactnative-run/blob/main/browser-metro/README.md Use npm to add the package to your project dependencies. ```bash npm install browser-metro ``` -------------------------------- ### Run Server Commands Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/reactnative-esm.md Commands to start the server in development mode with auto-reload or in production mode. ```bash # Development (with auto-reload) npm run dev --prefix reactnative-esm # Production npm start --prefix reactnative-esm ``` -------------------------------- ### ESM Package Server URLs Source: https://github.com/rapidnative/reactnative-run/blob/main/browser-metro/README.md Example endpoints for fetching npm packages from the ESM server. ```text https://esm.reactnative.run/pkg/lodash@4.17.21 https://esm.reactnative.run/pkg/react-dom@19/client ``` -------------------------------- ### Bundle a project Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api/bundler/page.mdx Generate a self-executing bundle string starting from a specified entry file. ```typescript const code = await bundler.bundle("/index.ts"); // code is a self-executing bundle string with inline source map ``` -------------------------------- ### Initialize createDataBxPathPlugin Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api/plugins/page.mdx Example of initializing the built-in plugin for injecting click-to-source attributes. ```typescript import { createDataBxPathPlugin } from "browser-metro"; const plugin = createDataBxPathPlugin(); // Use in config: plugins: [plugin] ``` -------------------------------- ### Start the Expo Development Server Source: https://github.com/rapidnative/reactnative-run/blob/main/browser-metro/example/user_projects/expo/README.md Initiates the Expo development server, providing options to open the app on various platforms or emulators. ```bash npx expo start ``` -------------------------------- ### Run Expo Server in Standalone Mode Source: https://github.com/rapidnative/reactnative-run/blob/main/expo-server/README.md Starts the server to serve its own generated bundle. ```bash node server.js ``` -------------------------------- ### Run local ESM server Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/quick-start/page.mdx Commands to start the local ESM package server for development. ```bash cd reactnative-esm npm install npm start # Server runs on http://localhost:5200 ``` -------------------------------- ### Start the package server Source: https://github.com/rapidnative/reactnative-run/blob/main/CLAUDE.md Initializes the reactnative-esm server on port 5200 to handle on-demand bundling of npm packages. ```bash cd reactnative-esm && npm start ``` -------------------------------- ### Extension Resolution Example Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/resolution/page.mdx Shows how the resolver attempts to find a file by trying different extensions defined in `sourceExts`. The first matching extension is used. ```text /src/utils → not found /src/utils.ts → found! ✓ ``` -------------------------------- ### TypeScript Type Stripping Example Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/example.md Demonstrates type annotations and import conversion in a TypeScript project. ```typescript import { greet } from "./utils"; const name: string = "World"; console.log(greet(name)); const numbers: number[] = [1, 2, 3, 4, 5]; console.log("Doubled:", numbers.map((n: number) => n * 2)); ``` -------------------------------- ### Example Project JSON Structure Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/example.md This JSON structure represents a project's file map, keyed by absolute paths. It's fetched on startup and used to load project files. ```json { "basic": { "/index.js": "var _ = require('lodash');\n...", "/utils.js": "module.exports = function greet(name) {...}", "/package.json": "{\"dependencies\":{\"lodash\":\"^4.17.21\"}}" }, "typescript": { ... }, "react": { ... } } ``` -------------------------------- ### List Files in VirtualFS Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api/virtual-fs/page.mdx Get a list of all file paths currently present in the VirtualFS. ```typescript vfs.list(); // ["/index.js", "/utils.js"] ``` -------------------------------- ### Bundle preamble Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api/bundler/page.mdx The standard preamble included at the start of every emitted bundle to initialize the process environment. ```javascript var process = globalThis.process || {}; process.env = process.env || {}; process.env.NODE_ENV = process.env.NODE_ENV || "development"; ``` -------------------------------- ### Resolver Configuration Example Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/resolution/page.mdx Configures the resolver with custom source extensions and path aliases. `sourceExts` allows dynamic extension resolution, and `paths` enables shorthand imports for directories. ```typescript const config: BundlerConfig = { resolver: { sourceExts: ["ts", "tsx", "js", "jsx"], paths: { "@/*": ["./*"], // TypeScript path aliases }, }, // ... }; ``` -------------------------------- ### Basic JavaScript Project Example Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/example.md A sample JavaScript project demonstrating CommonJS require statements and the use of the lodash npm package. ```javascript var _ = require("lodash"); var greet = require("./utils"); console.log(greet("World")); console.log("Shuffled:", _.shuffle([1, 2, 3, 4, 5])); ``` -------------------------------- ### Index File Resolution Example Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/resolution/page.mdx Illustrates how the resolver looks for index files (e.g., `index.ts`) when a path resolves to a directory. This allows directories to be treated as modules. ```text /src/components → not found /src/components/index.ts → found! ✓ ``` -------------------------------- ### Relative Import Resolution Examples Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/resolution/page.mdx Demonstrates how relative imports are resolved based on the importing file's directory. './' resolves from the current directory, and '../' resolves from the parent directory. ```text require("./utils") → resolves from /src/ require("../lib") → resolves from / ``` -------------------------------- ### Local Development Commands Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/esm-server/page.mdx Lists the npm commands to install dependencies and run the reactnative-esm server locally in development or production mode. ```bash cd reactnative-esm npm install npm run dev # with auto-reload npm start # production # Server runs on http://localhost:5200 ``` -------------------------------- ### Fetch individual packages Source: https://context7.com/rapidnative/reactnative-run/llms.txt Retrieve pre-bundled packages or specific subpaths using the GET /pkg/:specifier endpoint. ```typescript // Individual package requests (GET /pkg/:specifier) // Fetch lodash@latest const lodash = await fetch("https://esm.reactnative.run/pkg/lodash"); // Returns: pre-bundled lodash as IIFE // Fetch specific version const lodashPinned = await fetch("https://esm.reactnative.run/pkg/lodash@4.17.21"); // Fetch subpath export const reactDomClient = await fetch("https://esm.reactnative.run/pkg/react-dom@19.0.0/client"); // Returns: react-dom/client bundle (react-dom externalized) // Fetch scoped package with subpath const navCore = await fetch("https://esm.reactnative.run/pkg/@react-navigation/native@7.0.0/src/index"); // Response headers include externalized dependencies for version pinning: // X-Externals: {"react":"19.0.0","react-dom":"19.0.0"} ``` -------------------------------- ### Externalized Dependencies Header Example Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/esm-server/page.mdx Shows the format of the X-Externals header, which specifies pinned versions of externalized dependencies to prevent version mismatches. ```json {"react":"19.1.0","react-dom":"19.1.0","memoize-one":"4.1.0"} ``` -------------------------------- ### Get entry file from VirtualFS Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/api.md Finds the first file matching common entry point names (e.g., index.js). Returns null if no entry file is found. ```typescript vfs.getEntryFile(); // "/index.js" ``` -------------------------------- ### Project File Map Example Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/architecture/page.mdx Illustrates the structure of a FileMap, which is a flat object mapping absolute file paths to their source code strings. This is used to load project files into memory. ```typescript { "/index.ts": "import { greet } from './utils';\n...", "/utils.ts": "export function greet(name: string) {...}", "/package.json": "{ \"dependencies\": {} }" } ``` -------------------------------- ### Bundler Configuration with Environment Variables Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/architecture.md Example TypeScript configuration for the bundler, demonstrating how to inject public environment variables into the bundle preamble. Only variables with 'EXPO_PUBLIC_' or 'NEXT_PUBLIC_' prefixes are included. ```typescript const config: BundlerConfig = { // ... env: { EXPO_PUBLIC_API_URL: "https://api.example.com", NEXT_PUBLIC_SITE_NAME: "My App", SECRET_KEY: "abc123", // filtered out -- not public }, }; ``` -------------------------------- ### Define GET and POST Handlers for an API Route Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api-routes/page.mdx This example shows how to define GET and POST request handlers for an API route. The GET handler returns a JSON message with a timestamp, while the POST handler echoes back the received JSON body. ```typescript // /app/api/hello+api.ts export function GET(request: Request) { return Response.json({ message: "Hello from the API!", timestamp: Date.now(), }); } export function POST(request: Request) { const body = await request.json(); return Response.json({ received: body, echo: true, }); } ``` -------------------------------- ### Reset Project to Starter Code Source: https://github.com/rapidnative/reactnative-run/blob/main/browser-metro/example/user_projects/expo/README.md This command moves the existing starter code to 'app-example' and creates a clean 'app' directory for new development. ```bash npm run reset-project ``` -------------------------------- ### Initialize and Use VirtualFS Source: https://context7.com/rapidnative/reactnative-run/llms.txt Demonstrates initializing VirtualFS with a file map, reading, writing, checking existence, listing files, and detecting the entry file. Ensure file paths are absolute. ```typescript import { VirtualFS } from "browser-metro"; import type { FileMap } from "browser-metro"; // Initialize with a file map (absolute paths to content) const files: FileMap = { "/index.tsx": { content: 'import App from "./App";\nexport default App;', isExternal: false }, "/App.tsx": { content: 'export default function App() { return
Hello
; }', isExternal: false }, "/utils.ts": { content: 'export const add = (a: number, b: number) => a + b;', isExternal: false }, "/package.json": { content: '{"dependencies": {"react": "^19.0.0"}}', isExternal: false }, }; const vfs = new VirtualFS(files); // Read file contents (returns undefined if not found) const indexContent = vfs.read("/index.tsx"); // Result: 'import App from "./App";\nexport default App;' const missing = vfs.read("/nonexistent.ts"); // Result: undefined // Write or overwrite a file vfs.write("/newfile.ts", "export const PI = 3.14159;"); // Check if file exists console.log(vfs.exists("/index.tsx")); // true console.log(vfs.exists("/nope.ts")); // false // List all file paths const allPaths = vfs.list(); // Result: ["/index.tsx", "/App.tsx", "/utils.ts", "/package.json", "/newfile.ts"] // Auto-detect entry file (tries index.js/ts/tsx/jsx, then App.*, then package.json main) const entryFile = vfs.getEntryFile(); // Result: "/index.tsx" // Get a copy of the internal file map const fileMap = vfs.toFileMap(); // Compare current state with new files to detect changes const newFiles: FileMap = { "/index.tsx": { content: 'import App from "./App";\nexport default App;', isExternal: false }, "/App.tsx": { content: 'export default function App() { return
Updated!
; }', isExternal: false }, "/brand-new.ts": { content: 'export const NEW = true;', isExternal: false }, }; const changes = vfs.diff(newFiles); // Result: [ // { path: "/App.tsx", type: "update" }, // { path: "/utils.ts", type: "delete" }, // { path: "/package.json", type: "delete" }, // { path: "/newfile.ts", type: "delete" }, // { path: "/brand-new.ts", type: "create" } // ] // Delete a file const deleted = vfs.delete("/newfile.ts"); // true const notDeleted = vfs.delete("/missing.ts"); // false // Replace all files at once vfs.replaceAll(newFiles); ``` -------------------------------- ### Babel-based Transformer Example Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/transformation/page.mdx An example of a custom transformer implementation using Babel for TypeScript and React compilation. ```typescript import { transform } from "@babel/standalone"; const babelTransformer: Transformer = { transform({ src, filename }) { const presets = ["env"]; if (filename.endsWith(".tsx") || filename.endsWith(".ts")) { presets.push("typescript"); } if (filename.endsWith(".tsx") || filename.endsWith(".jsx")) { presets.push("react"); } const result = transform(src, { filename, presets, sourceType: "module" }); return { code: result.code }; } }; ``` -------------------------------- ### Initialize and Use Resolver Source: https://context7.com/rapidnative/reactnative-run/llms.txt Demonstrates setting up a Resolver with a VirtualFS and configuring path aliases, followed by path resolution and file identification methods. ```typescript import { Resolver, VirtualFS } from "browser-metro"; import type { ResolverConfig } from "browser-metro"; const files = { "/src/index.ts": { content: "import './utils';", isExternal: false }, "/src/utils.ts": { content: "export const x = 1;", isExternal: false }, "/src/components/index.tsx": { content: "export * from './Button';", isExternal: false }, "/src/components/Button.tsx": { content: "export const Button = () => {};", isExternal: false }, "/lib/helpers.js": { content: "module.exports = {};", isExternal: false }, }; const vfs = new VirtualFS(files); const config: ResolverConfig = { sourceExts: ["ts", "tsx", "js", "jsx"], // tsconfig path aliases (optional) paths: { "@/*": ["./*"], // @/utils -> /utils "@components/*": ["./src/components/*"], }, }; const resolver = new Resolver(vfs, config); // Check if a require target is an npm package console.log(resolver.isNpmPackage("lodash")); // true console.log(resolver.isNpmPackage("react")); // true console.log(resolver.isNpmPackage("./utils")); // false console.log(resolver.isNpmPackage("/src/index")); // false console.log(resolver.isNpmPackage("@/utils")); // false (path alias) // Resolve relative paths against importing file's directory const resolved1 = resolver.resolvePath("/src/index.ts", "./utils"); console.log(resolved1); // "/src/utils" const resolved2 = resolver.resolvePath("/src/components/Button.tsx", "../lib/helpers"); console.log(resolved2); // "/lib/helpers" // Path alias expansion const resolved3 = resolver.resolvePath("/src/index.ts", "@/lib/helpers"); console.log(resolved3); // "/lib/helpers" const resolved4 = resolver.resolvePath("/src/index.ts", "@components/Button"); console.log(resolved4); // "/src/components/Button" // Resolve to actual file path (with extension and index file resolution) const actual1 = resolver.resolveFile("/src/utils"); console.log(actual1); // "/src/utils.ts" (added .ts extension) const actual2 = resolver.resolveFile("/src/components"); console.log(actual2); // "/src/components/index.tsx" (index file) const actual3 = resolver.resolveFile("/src/missing"); console.log(actual3); // null (file not found) // Resolution order: // 1. Exact match: /src/utils // 2. With extension: /src/utils.ts, /src/utils.tsx, /src/utils.js, /src/utils.jsx // 3. Index file: /src/utils/index.ts, /src/utils/index.tsx, etc. // Check if a path is an asset file (images, fonts, etc.) console.log(resolver.isAssetFile("/images/logo.png")); // true console.log(resolver.isAssetFile("/fonts/custom.ttf")); // true console.log(resolver.isAssetFile("/src/App.tsx")); // false ``` -------------------------------- ### Bundle a project with Bundler Source: https://context7.com/rapidnative/reactnative-run/llms.txt Demonstrates initializing a VirtualFS, configuring the Bundler with transformers and environment variables, and executing the bundle in an iframe. ```typescript import { Bundler, VirtualFS, typescriptTransformer } from "browser-metro"; import type { BundlerConfig, FileMap } from "browser-metro"; // Set up project files const files: FileMap = { "/index.tsx": { content: ` import React from 'react'; import { greet } from './utils'; export default function App() { return
{greet("World")}
; } `, isExternal: false }, "/utils.ts": { content: ` export function greet(name: string): string { return \`Hello, \${name}!\`; } `, isExternal: false }, "/package.json": { content: JSON.stringify({ dependencies: { "react": "^19.0.0", "react-dom": "^19.0.0" } }), isExternal: false }, }; const vfs = new VirtualFS(files); // Configure the bundler const config: BundlerConfig = { resolver: { sourceExts: ["ts", "tsx", "js", "jsx"], // Optional: tsconfig path aliases paths: { "@/*": ["./*"] }, }, transformer: typescriptTransformer, server: { packageServerUrl: "https://esm.reactnative.run" }, // Optional: inject environment variables (only EXPO_PUBLIC_* and NEXT_PUBLIC_* are included) env: { EXPO_PUBLIC_API_URL: "https://api.example.com", }, }; const bundler = new Bundler(vfs, config); // Bundle the project (returns executable JavaScript string) const bundle = await bundler.bundle("/index.tsx"); // The bundle is a self-executing IIFE with: // - process.env polyfill with injected env vars // - CommonJS module loader // - All transformed local modules // - Pre-bundled npm packages // - Inline source map for debugging console.log(bundle.slice(0, 500)); // Output: var process = globalThis.process || {}; // process.env = process.env || {}; // process.env.NODE_ENV = "development"; // process.env.EXPO_PUBLIC_API_URL = "https://api.example.com"; // (function(modules) { // var cache = {}; // function require(id) { ... } // require("/index.tsx"); // })({ // "/index.tsx": function(module, exports, require) { ... }, // ... // }); // //# sourceMappingURL=data:application/json;base64,... // Execute in an iframe or via eval (for development) const iframe = document.createElement('iframe'); iframe.srcdoc = ``; document.body.appendChild(iframe); // Transform a single file without bundling const jsCode = bundler.transformFile("/utils.ts", ` export const double = (n: number) => n * 2; `); // Result: CommonJS code with exports and require() ``` -------------------------------- ### Initialize and bundle with browser-metro Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/quick-start/page.mdx Create a virtual filesystem, configure the bundler, and perform an initial bundle. ```typescript import { Bundler, VirtualFS, typescriptTransformer } from "browser-metro"; import type { BundlerConfig, FileMap } from "browser-metro"; // 1. Create a virtual filesystem const files: FileMap = { "/index.ts": 'import { greet } from "./utils";\nconsole.log(greet("World"));', "/utils.ts": 'export function greet(name: string) { return "Hello, " + name; }', }; const vfs = new VirtualFS(files); // 2. Configure the bundler const config: BundlerConfig = { resolver: { sourceExts: ["ts", "tsx", "js", "jsx"] }, transformer: typescriptTransformer, server: { packageServerUrl: "https://esm.reactnative.run" }, }; // 3. Bundle const bundler = new Bundler(vfs, config); const code = await bundler.bundle("/index.ts"); // 4. Execute (e.g. in an iframe) ``` -------------------------------- ### Initialize Bundler Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/api.md Configure and instantiate the main one-shot bundler. Requires a VirtualFS and a BundlerConfig. ```typescript import { Bundler, VirtualFS, typescriptTransformer } from "browser-metro"; import type { BundlerConfig } from "browser-metro"; const config: BundlerConfig = { resolver: { sourceExts: ["ts", "tsx", "js", "jsx"] }, transformer: typescriptTransformer, server: { packageServerUrl: "http://localhost:5200" }, }; const bundler = new Bundler(vfs, config); ``` -------------------------------- ### Initialize Bundler and Bundle Entry Point Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/batch-fetching/page.mdx Configures the Bundler with a virtual file system and TypeScript transformer, then triggers the bundling process. ```typescript import { Bundler, VirtualFS, typescriptTransformer } from "browser-metro"; const bundler = new Bundler(vfs, { resolver: { sourceExts: ["ts", "tsx", "js", "jsx"] }, transformer: typescriptTransformer, server: { packageServerUrl: "https://esm.reactnative.run" }, }); // bundle() automatically calls prefetchDependencies() internally const code = await bundler.bundle("/index.tsx"); ``` -------------------------------- ### Build Project Components Source: https://github.com/rapidnative/reactnative-run/blob/main/README.md Commands to build the various components of the project for production. ```bash # Build everything (browser-metro + playground + website) npm run build # Build playground and copy to website npm run build:playground # Build website only npm run build:website # Build browser-metro library only npm run build:metro ``` -------------------------------- ### Bundler Class Usage Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api/bundler/page.mdx Demonstrates how to initialize the Bundler class with a configuration object. ```APIDOC ## Bundler Class Initialization ### Description Initializes the `Bundler` class with a `VirtualFS` instance and a `BundlerConfig` object. ### Usage Example ```typescript import { Bundler, VirtualFS, typescriptTransformer } from "browser-metro"; import type { BundlerConfig } from "browser-metro"; const config: BundlerConfig = { resolver: { sourceExts: ["ts", "tsx", "js", "jsx"] }, transformer: typescriptTransformer, server: { packageServerUrl: "https://esm.reactnative.run" }, }; const bundler = new Bundler(vfs, config); ``` ### Parameters - **vfs** (`VirtualFS`) - Required - An instance of `VirtualFS` representing the file system. - **config** (`BundlerConfig`) - Required - Configuration object for the bundler. ``` -------------------------------- ### async build(entryFile: string) Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api/incremental-bundler/page.mdx Performs the initial full build of the project. This method must be called before any rebuild operations. ```APIDOC ## async build(entryFile: string) ### Description Performs the initial full build. Must be called before `rebuild()`. ### Parameters #### Path Parameters - **entryFile** (string) - Required - The path to the entry file to build. ### Response #### Success Response - **bundle** (string) - The full bundle string. - **type** (string) - The build type, which is "full" for initial builds. - **hmrUpdate** (null) - Always null for initial builds. ``` -------------------------------- ### Implement transformOutput hook Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api/plugins/page.mdx Example of modifying the bundled output, such as injecting logging statements into every module. ```typescript const loggingPlugin: BundlerPlugin = { name: "logging", transformOutput({ code, filename }) { // Add a log statement at the top of every module return { code: `console.log("Loading: ${filename}"); ${code}`, }; }, }; ``` -------------------------------- ### Adding a New Project Directory Structure Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/example.md Shows the required directory structure for adding a new user project, including entry file, additional files, and package.json. ```bash user_projects/myproject/ index.ts # Entry file utils.ts # Additional files package.json # { "dependencies": { ... } } ``` -------------------------------- ### Initialize the Bundler Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api/bundler/page.mdx Configure and instantiate the Bundler class with a VirtualFS and BundlerConfig. ```typescript import { Bundler, VirtualFS, typescriptTransformer } from "browser-metro"; import type { BundlerConfig } from "browser-metro"; const config: BundlerConfig = { resolver: { sourceExts: ["ts", "tsx", "js", "jsx"] }, transformer: typescriptTransformer, server: { packageServerUrl: "https://esm.reactnative.run" }, }; const bundler = new Bundler(vfs, config); ``` -------------------------------- ### POST /bundle-deps Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/batch-fetching/page.mdx Submits a list of dependencies to be bundled by the server. This is used when a cache miss occurs on the GET request. ```APIDOC ## POST /bundle-deps ### Description Triggers the server to install and bundle the provided dependencies. The server performs an npm install, bundles the packages using esbuild, and caches the result for future requests. ### Method POST ### Endpoint /bundle-deps ### Request Body - **hash** (string) - Required - The 16-character SHA-256 hex hash of the dependencies. - **dependencies** (object) - Required - A map of package names to their version strings. ### Request Example { "hash": "a1b2c3d4e5f67890", "dependencies": { "react": "19.1.0", "react-dom": "19.1.0", "expo-router": "~6.0.12", "expo": "~54.0.33" } } ### Response #### Success Response (200) - **body** (string) - The bundled JS response containing the requested dependencies. ``` -------------------------------- ### Extension-Specific Routing Transformer Example Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/transformation/page.mdx A custom transformer that routes transformations based on file extensions, falling back to the typescriptTransformer. ```typescript const routingTransformer: Transformer = { transform({ src, filename }) { const ext = filename.slice(filename.lastIndexOf(".")); switch (ext) { case ".svelte": return svelteTransformer.transform({ src, filename }); case ".vue": return vueTransformer.transform({ src, filename }); default: return typescriptTransformer.transform({ src, filename }); } } }; ``` -------------------------------- ### Extend Bundler with Custom Plugins Source: https://context7.com/rapidnative/reactnative-run/llms.txt Demonstrates creating plugins for source/output transformation, module resolution, aliasing, and shimming. Plugins are executed in the order provided in the BundlerConfig. ```typescript import { Bundler, VirtualFS, typescriptTransformer, createDataBxPathPlugin } from "browser-metro"; import type { BundlerPlugin, BundlerConfig } from "browser-metro"; // Built-in plugin: data-bx-path injection for click-to-source const dataBxPathPlugin = createDataBxPathPlugin(); // Injects data-bx-path="filename:line:col" into JSX elements // - Lowercase tags (
) get data-bx-path attribute // - Uppercase tags () get dataSet={{"bx-path": "..."}} for React Native Web // Custom plugin: Add logging to all components const loggingPlugin: BundlerPlugin = { name: "logging", // Runs BEFORE Sucrase (JSX still intact) transformSource({ src, filename }) { if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return null; // Add a comment at the top return { src: `// Transformed: ${filename}\n${src}` }; }, // Runs AFTER Sucrase (CommonJS output) transformOutput({ code, filename }) { if (!filename.includes("/components/")) return null; // Add logging to component files const logStatement = `console.log("[Component] ${filename} loaded");\n`; return { code: logStatement + code }; }, }; // Plugin for custom module resolution const customResolverPlugin: BundlerPlugin = { name: "custom-resolver", // Return resolved path, npm package name, or null to fall through resolveRequest(context, moduleName) { // Redirect internal paths if (moduleName === "legacy-api") { return "/src/api/v2"; // Resolve to local path } // Redirect to different npm package if (moduleName === "old-lodash") { return "lodash"; // Use npm package instead } return null; // Fall through to default resolution }, }; // Plugin for module aliases (redirect require() calls) const aliasPlugin: BundlerPlugin = { name: "aliases", moduleAliases() { return { // require("react-native") → require("react-native-web") "react-native": "react-native-web", // require("@company/legacy") → require("@company/modern") "@company/legacy": "@company/modern", }; }, }; // Plugin for module shims (replace npm packages with inline code) const shimPlugin: BundlerPlugin = { name: "shims", shimModules() { return { // Replace react-native-gesture-handler with a no-op "react-native-gesture-handler": ` module.exports = { GestureHandlerRootView: function(props) { return props.children; }, gestureHandlerRootHOC: function(Component) { return Component; }, }; `, // Stub expo-constants for web "expo-constants": ` module.exports = { default: { expoConfig: { name: "app", slug: "app" }, manifest: null, }, }; `, }; }, }; // Combine plugins (order matters!) const config: BundlerConfig = { resolver: { sourceExts: ["ts", "tsx", "js", "jsx"] }, transformer: typescriptTransformer, server: { packageServerUrl: "https://esm.reactnative.run" }, plugins: [ dataBxPathPlugin, // First: inject paths before any modifications loggingPlugin, // Then: add logging customResolverPlugin, aliasPlugin, shimPlugin, ], }; const vfs = new VirtualFS({ "/index.tsx": { content: ` import React from 'react'; import { View, Text } from 'react-native'; // Will use react-native-web export default function App() { return ( Hello from React Native Web! ); } `, isExternal: false }, "/package.json": { content: JSON.stringify({ dependencies: { "react": "^19.0.0", "react-native-web": "^0.19.0" } }), isExternal: false }, }); const bundler = new Bundler(vfs, config); const bundle = await bundler.bundle("/index.tsx"); ``` -------------------------------- ### Initialize VirtualFS Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api/virtual-fs/page.mdx Instantiate the VirtualFS class with an initial file map. The file map is an object where keys are file paths and values are file contents. ```typescript import { VirtualFS } from "browser-metro"; import type { FileMap } from "browser-metro"; const files: FileMap = { "/index.js": 'console.log("hello");', "/utils.js": 'module.exports = { add: (a, b) => a + b };', }; const vfs = new VirtualFS(files); ``` -------------------------------- ### Initialize Bundler Source: https://github.com/rapidnative/reactnative-run/blob/main/README.md Configures the Bundler with a VirtualFS and TypeScript transformer. ```typescript import { Bundler, VirtualFS, typescriptTransformer } from "browser-metro"; const files = { "/index.ts": 'console.log("Hello from browser-metro!");', }; const bundler = new Bundler(new VirtualFS(files), { resolver: { sourceExts: ["ts", "tsx", "js", "jsx"] }, transformer: typescriptTransformer, server: { packageServerUrl: "https://esm.reactnative.run" }, }); const code = await bundler.bundle("/index.ts"); ``` -------------------------------- ### GET /bundle-deps/:hash Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/batch-fetching/page.mdx Retrieves a cached bundle of dependencies based on a SHA-256 hash. If the bundle is not found in the cache, a 404 error is returned. ```APIDOC ## GET /bundle-deps/:hash ### Description Retrieves a pre-bundled set of dependencies identified by a specific hash. This endpoint is designed to be cached by CDNs. ### Method GET ### Endpoint /bundle-deps/:hash ### Parameters #### Path Parameters - **hash** (string) - Required - The 16-character SHA-256 hex hash of the sorted dependency list. ### Response #### Success Response (200) - **body** (string) - A concatenated JS response containing bundled packages delimited by `@dep-start` and `@dep-end` markers. #### Error Response (404) - Returned if the bundle does not exist in the cache. ``` -------------------------------- ### Perform Initial Full Build Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api/incremental-bundler/page.mdx Call `build()` to perform the initial full bundle. This must be done before calling `rebuild()`. ```typescript const result = await bundler.build("/index.tsx"); // result.bundle -- full bundle string // result.type -- "full" // result.hmrUpdate -- null (initial build) ``` -------------------------------- ### X-Externals Header Example Source: https://github.com/rapidnative/reactnative-run/blob/main/docs/reactnative-esm.md This header is returned by reactnative-esm to specify pinned versions of externalized dependencies. The browser-metro bundler uses this to resolve transitive dependencies. ```json X-Externals: {"react":"19.1.0","react-dom":"19.1.0","memoize-one":"4.1.0"} ``` -------------------------------- ### Plugin resolveRequest Hook Example Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/resolution/page.mdx Demonstrates how a plugin can intercept and modify module resolution. The `resolveRequest` hook allows redirecting module imports or falling back to default resolution. ```typescript const myPlugin: BundlerPlugin = { name: "my-plugin", resolveRequest(context, moduleName) { if (moduleName === "react-native") { return "react-native-web"; // redirect } return null; // fall through to default resolution }, }; ``` -------------------------------- ### Bundle Preamble and Environment Variables Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/api/bundler/page.mdx Describes the bundle preamble and how environment variables are injected. ```APIDOC ## Bundle Preamble and Environment Variable Injection ### Description Every emitted bundle is prefixed with a preamble that sets up the `process.env` object. Environment variables can be injected via the `env` config option, with security filtering applied. ### Bundle Preamble Example ```javascript var process = globalThis.process || {}; process.env = process.env || {}; process.env.NODE_ENV = process.env.NODE_ENV || "development"; ``` ### Environment Variable Injection The `env` config option allows for injecting environment variables. For security reasons, only variables with specific public prefixes are included: - `EXPO_PUBLIC_*` - `NEXT_PUBLIC_*` Variables without these prefixes are filtered out. ### Configuration Example ```typescript const config: BundlerConfig = { // ... other config options env: { EXPO_PUBLIC_API_URL: "https://api.example.com", SECRET_KEY: "abc123", // This will be filtered out }, }; ``` ``` -------------------------------- ### Interact with VirtualFS API Source: https://github.com/rapidnative/reactnative-run/blob/main/website/src/app/docs/virtual-fs/page.mdx Demonstrates common filesystem operations like reading, writing, checking existence, and listing files within the VirtualFS instance. ```typescript import { VirtualFS } from "browser-metro"; const vfs = new VirtualFS(files); // Read a file vfs.read("/index.tsx"); // returns content string or undefined // Write a file (create or overwrite) vfs.write("/new-file.ts", "export const x = 1;"); // Check existence vfs.exists("/index.tsx"); // true // List all files vfs.list(); // ["/index.tsx", "/App.tsx", "/package.json", "/new-file.ts"] // Get a copy of the internal file map vfs.toFileMap(); // { "/index.tsx": "...", ... } // Find entry file (tries /index.js, /index.ts, /index.tsx, /index.jsx) vfs.getEntryFile(); // "/index.tsx" ``` -------------------------------- ### Batch Dependency Bundling Source: https://context7.com/rapidnative/reactnative-run/llms.txt Bundle multiple project dependencies into a single JavaScript file. This endpoint supports both GET requests for cached bundles and POST requests to generate new bundles. ```APIDOC ## POST /bundle-deps ### Description Bundles all project dependencies into a single, optimized JavaScript file. It first attempts to retrieve a cached bundle using a hash of the dependencies. If not found, it generates and returns a new bundle. ### Method POST ### Endpoint `/bundle-deps` ### Parameters #### Query Parameters - **hash** (string) - Required - A SHA-256 hash (first 16 characters) of the sorted dependency list, used for cache lookup. #### Request Body - **hash** (string) - Required - The computed hash of the dependencies. - **dependencies** (object) - Required - An object where keys are package names and values are their version specifiers (e.g., `{"react":"^19.0.0"}`). ### Request Example ```typescript const dependencies = { "react": "^19.0.0", "react-dom": "^19.0.0", "lodash": "^4.17.21", "@tanstack/react-query": "^5.0.0", }; // Compute hash for cache lookup async function hashDeps(deps: Record): Promise { const sorted = Object.keys(deps).sort().map(k => `${k}@${deps[k]}`).join(","); const encoder = new TextEncoder(); const data = encoder.encode(`v2:${sorted}`); const hashBuffer = await crypto.subtle.digest("SHA-256", data); return Array.from(new Uint8Array(hashBuffer)) .map(b => b.toString(16).padStart(2, "0")) .join("") .slice(0, 16); } const hash = await hashDeps(dependencies); // Try GET first (CDN cacheable) let response = await fetch(`https://esm.reactnative.run/bundle-deps/${hash}`); if (!response.ok) { // POST to build the bundle response = await fetch("https://esm.reactnative.run/bundle-deps", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ hash, dependencies }), }); } const bundleText = await response.text(); ``` ### Response #### Success Response (200) - **Body** (string) - A combined JavaScript bundle containing all specified dependencies. The bundle includes metadata comments like `@dep-bundle`, `@dep-manifest`, `@dep-count`, `@dep-start`, and `@dep-end` for each dependency. #### Response Example ``` // @dep-bundle // @dep-manifest {"react":"19.0.0","react-dom":"19.0.0",...} // @dep-count 6 // @dep-start react // @dep-end react // @dep-start react-dom // @dep-end react-dom ... ``` ### Client-side Parsing Example ```typescript function parseDepBundle(text: string): { manifest: Record; packages: Record; } { const manifest: Record = {}; const packages: Record = {}; const manifestMatch = text.match(/\/\/ @dep-manifest ({[^}]+})/); if (manifestMatch) { Object.assign(manifest, JSON.parse(manifestMatch[1])); } const depRegex = /\/\/ @dep-start ([^\n]+)\n([\s\S]*?)\/\/ @dep-end \1/g; let match; while ((match = depRegex.exec(text)) !== null) { packages[match[1]] = match[2].trim(); } return { manifest, packages }; } const { manifest, packages } = parseDepBundle(bundleText); console.log("Resolved versions:", manifest); // { "react": "19.0.0", "react-dom": "19.0.0", "lodash": "4.17.21", ... } console.log("Package count:", Object.keys(packages).length); ``` ```