# Babel

Compile literal style definitions for web or native inside a Babel pipeline.

The `zyzz` plugin from `zyzz/babel` rewrites authoring calls before the application's presets lower TypeScript and JSX. Web transforms return CSS on Babel's result metadata, and native transforms emit executable style tables. It requires Babel 7.

```ts title="build.ts"
import { transformSync } from '@babel/core'
import { zyzz } from 'zyzz/babel'

const source =
  "import { style } from 'zyzz'\nexport const card = style({ color: 'red' })"

const result = transformSync(source, {
  filename: 'src/styles.ts',
  // Runs before the TypeScript preset
  plugins: [[zyzz, { target: 'web' }]],
  presets: ['@babel/preset-typescript'],
})
```

## Signature

```ts
// Web compilation
plugins: [[zyzz, { target: 'web', ...options }]]

// Native compilation
plugins: [[zyzz, { target: 'native', platform, ...options }]]
```

The options accept either the web or native branch, exported as `WebOptions` and `NativeOptions` and joined as `Options`.

## Parameters

### options.target

* **Type:** `'web' | 'native'`
* **Default:** `'native'`

Selects the compiler. Web output includes CSS metadata, and native output contains style tables. Omitting `target` selects native compilation for existing configurations.

```ts
// Emits rewritten JavaScript and CSS metadata
const plugins = [[zyzz, { target: 'web' }]]
```

### options.cssOutput

* **Type:** `'atomic' | 'grouped'`
* **Default:** `'atomic'`

Web only. Atomic output emits one class per declaration, and grouped output emits one class per definition. The [CSS Output](/docs/guides/css-output) guide compares them.

```ts
// One rule per definition
const plugins = [[zyzz, { cssOutput: 'grouped', target: 'web' }]]
```

### options.moduleId

* **Type:** `string`

The portable identity used for class names and stylesheet ownership. Web transforms default to the filename relative to Babel's `root`. Files outside that root need an explicit stable, package-relative ID. Native graph compilation requires an ID that [`options.modules`](#optionsmodules) contains.

```ts
// Names a module outside Babel's root
const plugins = [[zyzz, { moduleId: 'ui/Button.ts', target: 'web' }]]
```

### options.reset

* **Type:** `boolean`
* **Default:** `false`

Web only. Adds `import 'zyzz/reset.css'` to each transformed module that does not already import it. The consuming bundler must support CSS imports.

```ts
// Imports the reset from each compiled module
const plugins = [[zyzz, { reset: true, target: 'web' }]]
```

### options.platform

* **Type:** `'ios' | 'android'`

Native only, and required. Selects platform branches in native output.

```ts
// Compiles iOS tables
const plugins = [[zyzz, { platform: 'ios', target: 'native' }]]
```

### options.colorScheme

* **Type:** `'light' | 'dark'`

Native only. Fixes the scheme at build time. Without it, both schemes compile and the [`Provider`](/docs/api/react-native/Provider) selects one at runtime.

```ts
// Compiles only dark values
const plugins = [[zyzz, { colorScheme: 'dark', platform: 'ios' }]]
```

### options.fonts

* **Type:** `Readonly<Record<string, string>>`

Native only. Maps exact authored `fontFamily` strings to registered native family names, and an unmapped family fails compilation. The application loads the fonts.

```ts
// Authored `fontFamily: 'Pilat, Arial, sans-serif'` renders with Pilat
const fonts = { 'Pilat, Arial, sans-serif': 'Pilat' }
const plugins = [[zyzz, { fonts, platform: 'ios' }]]
```

### options.units

* **Type:** `{ px?: number, rem?: number }`
* **Default:** `{ px: 1 }`

Native only. Positive scales that convert authored lengths to logical units. `rem` lengths fail compilation without a `rem` scale.

```ts
// `1rem` compiles to 16 logical units
const plugins = [[zyzz, { platform: 'ios', units: { rem: 16 } }]]
```

### options.modules

* **Type:** `Readonly<Record<string, string>>`

Native only. The closed source graph, keyed by package-relative module ID, including the transformed module. Without `colorScheme`, styles that read configured helpers from other graph modules compile. A fixed scheme compiles only modules that import `zyzz` directly.

```ts
// Sources keyed by module ID, including the transformed module
const modules = { 'app/Card.ts': card, 'app/zyzz.config.ts': config }
const plugins = [[zyzz, { moduleId: 'app/Card.ts', modules, platform: 'ios' }]]
```

### options.imports

* **Type:** `Readonly<Record<string, Readonly<Record<string, string | null>>>>`

Native only. The host's resolution of every static import in [`options.modules`](#optionsmodules), keyed by importing module ID and then by specifier. Each specifier maps to a module ID, or to `null` for an external package such as `zyzz`.

```ts title="build.ts"
import { transformSync } from '@babel/core'
import { zyzz } from 'zyzz/babel'

const card = "import { style } from './zyzz.config.js'\nexport const card = style({ padding: 'md' })"
const config = "import { defineConfig } from 'zyzz'\nexport const { style } = defineConfig({ vars: { spacing: { md: '16px' } } })"
const modules = { 'app/Card.ts': card, 'app/zyzz.config.ts': config }
// Resolves the relative import and leaves `zyzz` external
const imports = {
  'app/Card.ts': { './zyzz.config.js': 'app/zyzz.config.ts' },
  'app/zyzz.config.ts': { zyzz: null },
}

const result = transformSync(card, {
  filename: 'Card.ts',
  plugins: [[zyzz, { imports, moduleId: 'app/Card.ts', modules, platform: 'ios' }]],
  presets: ['@babel/preset-typescript'],
})
```

### options.contracts

* **Type:** `Readonly<Record<string, string>>`

Native only. Serialized `.zyzz.json` contracts keyed by host-resolved module ID, for packages that publish compiled JavaScript. [`options.imports`](#optionsimports) maps specifiers to these IDs, and the compiled modules stay external.

```ts
// The contract published beside a package's compiled entry
const contracts = { 'ui/index.js': uiContract }
const plugins = [[zyzz, { contracts, imports, modules, moduleId, platform: 'ios' }]]
```

## Returns

### metadata.zyzz

* **Type:** `WebMetadata | undefined`

Web transforms set `css`, `cssMap`, and `moduleId` on `result.metadata.zyzz`. The build writes and loads the CSS, keeps stylesheet order, and resolves relative URLs against the source module. Absent metadata or empty CSS removes the module's previous stylesheet.

```ts title="build.ts"
import { transformSync } from '@babel/core'
import { zyzz } from 'zyzz/babel'

const source =
  "import { style } from 'zyzz'\nexport const card = style({ color: 'red' })"
const result = transformSync(source, {
  filename: 'src/styles.ts',
  plugins: [[zyzz, { target: 'web' }]],
})

// The build owns writing this stylesheet
const css = result?.metadata?.zyzz?.css
```

Babel's JavaScript map and `cssMap` are separate, and both point to the authored source. Native transforms and ordinary modules have no `metadata.zyzz`.

## Errors

Authoring modules require a filename, and unsupported target semantics fail compilation.

Without a supplied [module graph](#optionsmodules), web and fixed-scheme native transforms compile one module at a time. A configuration must then stay local to its module, and `Config` imports and re-exported helpers are rejected. Every mode rejects `zyzz/default`.

A graph import without an [`options.imports`](#optionsimports) entry fails with a missing host resolution. [Metro](/docs/api/metro) supplies the graph options during bundling.

```ts title="build.ts"
import { transformSync } from '@babel/core'
import { zyzz } from 'zyzz/babel'

// Throws because an exported configuration needs a module graph
const source =
  "import { defineConfig } from 'zyzz'\nexport const { style } = defineConfig({})"
transformSync(source, {
  filename: 'src/config.ts',
  plugins: [[zyzz, { target: 'web' }]],
})
```

## React Native

Native output contains style tables and bindings, without CSS. Shared `lineHeight` numbers multiply the font size, while values inside native target branches stay absolute. Expo projects use [Metro](/docs/api/metro), which selects the platform and chains the transformer.

```ts title="build.ts"
import { transformSync } from '@babel/core'
import { zyzz } from 'zyzz/babel'

const source =
  "import { style } from 'zyzz'\nexport const title = style({ fontSize: '1rem' })"
const result = transformSync(source, {
  filename: 'Styles.ts',
  // Compiles iOS tables for both schemes
  plugins: [[zyzz, { platform: 'ios', target: 'native', units: { rem: 16 } }]],
  presets: ['babel-preset-expo'],
})
```
