npm package: @re2tools/re2-wasm-esm
Description: Google RE2 as WASM - ESM build with browser support (async init)
Latest version: 1.0.4

# re2-wasm-esm

Standalone ESM build of [Google RE2](https://github.com/google/re2) as WebAssembly with **browser and Node** support. **No dependency on the re2-wasm package** — all code and artifacts live in this repo.

📄 **[Full documentation](DOCUMENTATION.md)** — package layout, `wasm/` role, build, runtime (Node vs browser), API, using as common utils, updating upstream.

- **Source**: RE2 class and types are vendored under `src/re2-wasm/` (from [google/re2-wasm/src](https://github.com/google/re2-wasm/tree/main/src), Apache-2.0).
- **WASM**: `wasm/re2.wasm` and `wasm/re2-glue.js` (Emscripten glue) are committed in `wasm/`. Build copies them into `dist/` for publish and runtime.

So if upstream re2-wasm changes, this package does not change until you explicitly update the vendored files.

**Runtime:**

- **Browser**: Loads the committed WASM and glue asynchronously; call `await init()` before using RE2.
- **Node**: Uses the same committed WASM and glue from `dist/` (same as browser).

## Install

```bash
npm install @re2tools/re2-wasm-esm
```

## Usage

### Node (ESM)

```js
import { init, getRE2 } from "@re2tools/re2-wasm-esm";

await init();
const RE2 = getRE2();
const re = new RE2("a(b*)", "u");
console.log(re.exec("abbc")); // [ "abb", "bb", ... ]
```

### Browser

1. Ensure `dist/re2.wasm` and `dist/re2-glue.js` are served from the same origin (or pass URLs to `init()`).
2. Call `await init()` before any RE2 use.

```js
import { init, getRE2 } from "@re2tools/re2-wasm-esm";

await init(); // loads WASM; required in browser
const RE2 = getRE2();
const re = new RE2("a(b*)", "u");
re.test("abbc"); // true
```

With a bundler (Vite, Webpack, etc.), copy `node_modules/@re2tools/re2-wasm-esm/dist/re2.wasm` and `re2-glue.js` to your public/output directory, or pass a base URL:

```js
await init({ baseUrl: "https://your-cdn.com/@re2tools/re2-wasm-esm/" });
```

### React (16.8+, 17, 18)

This package is React-agnostic (no React dependency). Use it in React 16, 17, or 18 by calling `init()` once, then `getRE2()` in components or helpers.

**Option A — init in entry (recommended, React 16/17)**

```tsx
// src/index.tsx
import React from "react";
import ReactDOM from "react-dom";
import { init, getRE2 } from "@re2tools/re2-wasm-esm";
import App from "./App";

async function bootstrap() {
  await init();
  ReactDOM.render(<App />, document.getElementById("root"));
}
bootstrap();
```

**React 18** — use `createRoot` instead of `render`:

```tsx
import { createRoot } from "react-dom/client";
// after await init():
createRoot(document.getElementById("root")!).render(<App />);
```

**Option B — init in a component (React 16.8+ for hooks)**

```tsx
import { useState, useEffect } from "react";
import { init, getRE2 } from "@re2tools/re2-wasm-esm";

function useRE2() {
  const [ready, setReady] = useState(false);
  useEffect(() => {
    init().then(() => setReady(true));
  }, []);
  return ready ? getRE2() : null;
}

// In a component: const RE2 = useRE2(); use RE2 when non-null.
```

Use RE2 with the **`u`** (unicode) flag: `new RE2("pattern", "u")` or `new RE2(/pattern/u)`.

### Storybook (fix 404 for re2-glue.js / re2.wasm)

Storybook serves from its own dev server, so the loader can’t find `re2.wasm` and `re2-glue.js` unless you serve them and pass their base URL.

**1. Serve the files**

Copy the two files into a folder your app (or Storybook) serves as static:

```bash
mkdir -p public/re2
cp node_modules/@re2tools/re2-wasm-esm/dist/re2.wasm node_modules/@re2tools/re2-wasm-esm/dist/re2-glue.js public/re2/
```

If your project has no `public/`, use whatever directory Storybook uses for static files (e.g. `static/`), or add `staticDirs` in `.storybook/main.js`:

```js
// .storybook/main.js (or main.ts)
module.exports = {
  staticDirs: ['public'],  // or ['static']
  // ...
};
```

**2. Tell the loader where they are**

In `.storybook/preview.js` (or `preview.ts`), init with that base path:

```js
import { init } from "@re2tools/re2-wasm-esm";

await init({ baseUrl: "/re2/" });

export const parameters = { /* ... */ };
```

If your app is not at the root (e.g. Storybook base path), use that base: `init({ baseUrl: "/storybook/re2/" })` or use a relative URL that resolves to the same origin where the files are served.

### API

- **`init(options?: { wasmUrl?, glueUrl?, baseUrl? })`**  
  Initializes the runtime. In browser, loads WASM and glue script. Resolves when ready. Call once before `getRE2()`.

- **`getRE2()`**  
  Returns the RE2 class. Throws if `init()` has not been called.

- **`whenReady()`**  
  Returns a Promise that resolves to the WASM module when ready (for advanced use).

- **`getModule()`**  
  Returns the low-level WASM module (e.g. `WrappedRE2`). Throws if not ready.

RE2 API matches the standard `RegExp`-like interface: `exec()`, `test()`, `match()`, `replace()`, `search()`, `split()`, flags `g`, `i`, `m`, `u`, etc. RE2 requires the **`u`** (unicode) flag.

## Why

- [re2-wasm](https://github.com/google/re2-wasm) is Node-only (sync WASM, CommonJS). This package is a **standalone** fork: ESM, browser + Node, with all needed code and WASM committed here so nothing is pulled from re2-wasm at build or runtime.

### Updating the WASM (maintainers)

To refresh from upstream: build [google/re2-wasm](https://github.com/google/re2-wasm) and copy its `build/wasm/re2.wasm` and `build/wasm/re2.js` into this repo’s `wasm/` as `re2.wasm` and `re2-glue.js`.

## License

Apache-2.0 (same as re2-wasm).