# @uxf/icons-generator

CLI that bundles a project's SVG icons into a single sprite and generates the matching TypeScript definitions that power `@uxf/ui`'s `<Icon>` component.

## When to use

Use it in any UXF web project that renders icons through `@uxf/ui/icon`. From a declarative config (`icons.config.js`) of inline SVGs and/or Font Awesome Pro references it produces an SVG sprite, one standalone SVG per icon, and a generated `icons.ts` that:

- exports `ICONS` (per-icon `{ w, h }` map) and `ICONS_VERSION` (md5 of the sprite), and
- augments `@uxf/ui/icon/theme`'s `IconsSet` interface, so `IconName` (`keyof IconsSet`) autocompletes every icon you declared.

This is a build-time dev tool run via the `icons-gen` binary. The runtime `<Icon>` component itself lives in `@uxf/ui/icon`, not here.

## Installation

```
yarn add -D @uxf/icons-generator
```

Requires Node `>= 24`. Peer dependency: `@uxf/core` (`11.114.0`). To use the Font Awesome Pro adapter, additionally install the per-style FA packages you reference (see [Providers](#providers--font-awesome-pro-adapter)).

## Quick start

1. Create `icons.config.js` in your project root:

    ```js
    /** @type {import('@uxf/icons-generator/src/types').IconsConfig} */
    module.exports = {
        generatedDirectory: "/public/icons-generated/",
        icons: {
            flame: {
                width: 43,
                height: 48,
                data: `<path fill="#fff" d="M30.84 20.51a1.51 1.51 0 0 0-1.16-.71..." />`,
            },
        },
    };
    ```

2. Run the generator:

    ```bash
    icons-gen
    ```

3. Wire the generated `ICONS` + sprite into `@uxf/ui` and render icons (see [Integration](#integration-with-uxfuiicon)).

## CLI

```bash
icons-gen [options]
```

| Flag           | Alias | Default           | Description                                   |
| -------------- | ----- | ----------------- | --------------------------------------------- |
| `--configFile` | `-c`  | `icons.config.js` | Path to the config file, resolved from `cwd`. |
| `--help`       | `-h`  | —                 | Print help and exit.                          |

```bash
icons-gen --configFile=custom.icons.config.js
```

## Configuration

The config file exports an `IconsConfig` object via `module.exports`.

| Key                       | Type                                                                           | Default                                   | Required | Description                                                                                                  |
| ------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------ |
| `icons`                   | `Partial<Record<string, SimpleIcon \| SizedIcon \| IconFromProviderFunction>>` | —                                         | Yes      | Icons to generate, keyed by icon name (see [Icon types](#icon-types)).                                       |
| `generatedDirectory`      | `string`                                                                       | `"/public/icons-generated/"`              | No       | Output dir (relative to `cwd`) for the sprite and standalone SVG files. Must start/end with `/`.             |
| `configDirectory`         | `string`                                                                       | `"/src/config/"`                          | No       | Output dir (relative to `cwd`) for the generated `icons.ts` and provider fallbacks. Must start/end with `/`. |
| `spriteFileName`          | `string`                                                                       | `"_icon-sprite.svg"`                      | No       | Sprite file name written into `generatedDirectory`.                                                          |
| `typeName`                | `string`                                                                       | `"IconsSet"`                              | No       | Name of the `keyof typeof ICONS` type exported by the generated file.                                        |
| `typescript`              | `boolean`                                                                      | `true`                                    | No       | Emit `icons.ts` vs `icons.js`. See [Gotchas](#gotchas).                                                      |
| `moduleDefinition`        | `ModuleDefinition \| false`                                                    | augments `@uxf/ui/icon/theme` (see below) | No       | Controls the `declare module` type augmentation; `false` disables it.                                        |
| `customDefinitionContent` | `string`                                                                       | —                                         | No       | Extra content appended verbatim to the end of the generated definition file.                                 |

### Icon types

#### SimpleIcon

A single-size icon.

```ts
type SimpleIcon = {
    data: string;
    height: number;
    width: number;
};
```

```js
flame: {
    width: 43,
    height: 48,
    data: `<path fill="#fff" d="M30.84 20.51a1.51 1.51 0 0 0-1.16-.71..." />`,
}
```

#### SizedIcon

Different SVG data per pixel size. Each size produces its own sprite symbol (`icon-sprite--<name>_<size>`) and standalone file (`<name>_<size>.svg`).

```ts
type SizedIcon = Record<number, string>;
```

```js
logo: {
    24: `<path fill="#fff" d="..." />`,
    48: `<path fill="#fff" d="..." />`,
}
```

#### IconFromProviderFunction

A function that resolves an icon from a provider (e.g. `faPro.icon(...)`). See [Providers](#providers--font-awesome-pro-adapter).

```ts
type IconFromProviderFunction = (config: _IconsConfig) => { width: number; height: number; path: string };
```

### Module augmentation (`moduleDefinition`)

```ts
type ModuleDefinition = {
    moduleName: string;
    typeName: string;
    format: "type" | "interface";
};
```

Defaults to `{ moduleName: "@uxf/ui/icon/theme", typeName: "IconsSet", format: "interface" }`. With these defaults the generated file emits:

```ts
declare module "@uxf/ui/icon/theme" {
    interface IconsSet {
        flame: true;
        // ...one line per icon
    }
}
```

This augmentation is what makes `@uxf/ui/icon`'s `IconName` (`keyof IconsSet`) aware of your icons. Set `moduleDefinition: false` to skip it.

## Providers — Font Awesome Pro adapter

The `faPro` adapter reads icon data from the per-style Font Awesome packages. Install only the styles you actually use.

| Namespace                 | Package                                        |
| ------------------------- | ---------------------------------------------- |
| `brands.*`                | `@fortawesome/free-brands-svg-icons`           |
| `regular.*`               | `@fortawesome/pro-regular-svg-icons`           |
| `solid.*`                 | `@fortawesome/pro-solid-svg-icons`             |
| `light.*`                 | `@fortawesome/pro-light-svg-icons`             |
| `thin.*`                  | `@fortawesome/pro-thin-svg-icons`              |
| `duotone.*`               | `@fortawesome/pro-duotone-svg-icons`           |
| `duotone-regular.*`       | `@fortawesome/duotone-regular-svg-icons`       |
| `duotone-light.*`         | `@fortawesome/duotone-light-svg-icons`         |
| `duotone-thin.*`          | `@fortawesome/duotone-thin-svg-icons`          |
| `sharp-regular.*`         | `@fortawesome/sharp-regular-svg-icons`         |
| `sharp-solid.*`           | `@fortawesome/sharp-solid-svg-icons`           |
| `sharp-light.*`           | `@fortawesome/sharp-light-svg-icons`           |
| `sharp-thin.*`            | `@fortawesome/sharp-thin-svg-icons`            |
| `sharp-duotone-regular.*` | `@fortawesome/sharp-duotone-regular-svg-icons` |
| `sharp-duotone-solid.*`   | `@fortawesome/sharp-duotone-solid-svg-icons`   |
| `sharp-duotone-light.*`   | `@fortawesome/sharp-duotone-light-svg-icons`   |
| `sharp-duotone-thin.*`    | `@fortawesome/sharp-duotone-thin-svg-icons`    |

An icon is referenced as `"<namespace>.<kebab-icon-name>"` (e.g. `"regular.calendar-check"`). The legacy `@fortawesome/fontawesome-pro` monolith is no longer supported — the adapter throws if it is installed, so uninstall it.

### Local development setup

```bash
# Set registry and token
npm config set "@fortawesome:registry" https://npm.fontawesome.com/
npm config set "//npm.fontawesome.com/:_authToken" YOUR_TOKEN

# Install only the styles you use
npm install --save-dev @fortawesome/pro-regular-svg-icons @fortawesome/free-brands-svg-icons
```

### Usage

```js
const { faPro } = require("@uxf/icons-generator/src/providers/fa-pro");

module.exports = {
    generatedDirectory: "/public/icons-generated/",
    icons: {
        // keeps the default name, e.g. "faPro_brands.linkedin"
        ...faPro.adapter(["brands.linkedin"]),
        // or assign a custom name
        twitter: faPro.icon("brands.twitter"),
    },
};
```

`faPro.adapter([...])` names each icon `faPro_<namespace>.<name>`, while `faPro.icon(...)` lets you assign a custom key.

### Without the private key

If your project already has generated icons, the Font Awesome Pro packages are not required to rebuild them. On each successful resolve the adapter caches the icon into `<configDirectory>/icons-fallbacks/faPro.json`; when a package is missing, it reads from that fallback file instead. Existing icons keep working, but **adding new** provider icons still requires the corresponding FA package installed.

## Generated output

Running `icons-gen` (re)writes:

- `<generatedDirectory>/<spriteFileName>` — the SVG sprite: one `<symbol id="icon-sprite--<name>">` per icon (sized icons: `icon-sprite--<name>_<size>`).
- `<generatedDirectory>/<name>.svg` — one standalone SVG per icon (sized: `<name>_<size>.svg`). SVGs for icons removed from the config are cleaned up on the next run.
- `<configDirectory>/icons.ts` — the definition file (see below).
- `<configDirectory>/icons-fallbacks/<provider>.json` — cached provider icon data (e.g. `faPro.json`).

The definition file exports:

```ts
// this file is generated automatically, do not change anything manually in the contents of this file

export const ICONS_VERSION = "<md5 of the sprite file>";

export const ICONS = {
    flame: { w: 43, h: 48 },
    logo: [24, 48],
    // ...
} as const;

export type IconsSet = keyof typeof ICONS; // name comes from `typeName`

declare module "@uxf/ui/icon/theme" {
    // omitted when moduleDefinition: false
    interface IconsSet {
        flame: true;
        // ...
    }
}
```

## Integration with `@uxf/ui/icon`

1. Run `icons-gen` (wire it into a `gen`/prebuild script).
2. Pass the generated `ICONS` and sprite path to `@uxf/ui`'s `UiContextProvider`. Because `generatedDirectory` lives under `public/`, the browser URL drops that segment (`/public/icons-generated/…` → `/icons-generated/…`):

    ```tsx
    import { UiContextProvider, UiContextType } from "@uxf/ui/context";
    import { ICONS, ICONS_VERSION } from "@/config/icons";

    const uiConfig: UiContextType = {
        icon: {
            iconsConfig: ICONS,
            spriteFilePath: `/icons-generated/_icon-sprite.svg?v=${ICONS_VERSION}`,
        },
        // ...other UI context options (colorScheme, localeConfig, rasterImage, translationFn)
    };
    ```

3. Render icons via `@uxf/ui`'s `<Icon>`. The `name` prop autocompletes every generated icon thanks to the module augmentation:

    ```tsx
    import { Icon } from "@uxf/ui/icon";

    <Icon name="flame" size={24} />;
    ```

4. (Optional) Preload the sprite:

    ```tsx
    <link as="image" href={`/icons-generated/_icon-sprite.svg?v=${ICONS_VERSION}`} rel="preload" type="image/svg+xml" />
    ```

## Gotchas

- **Dev/build tool only.** The `<Icon>` runtime component is `@uxf/ui/icon`; this package just generates the sprite and types.
- **Paths are `cwd`-relative and need slashes.** `configDirectory` and `generatedDirectory` are joined onto `process.cwd()`, so both must start and end with `/`.
- **`typescript: false` currently has no effect.** The generator always emits `icons.ts` — the flag falls back to `true` internally.
- **The `faPro` provider is a deep import:** `@uxf/icons-generator/src/providers/fa-pro` (the published files preserve the `src/` layout). The `IconsConfig` type is at `@uxf/icons-generator/src/types`.
- **Keep the default `moduleName`** (`@uxf/ui/icon/theme`) unless you intentionally augment a different module; changing it breaks the `@uxf/ui` `IconName` inference.

## Links

- Repository: [gitlab.com/uxf-npm/icons-generator](https://gitlab.com/uxf-npm/icons-generator)
- Consumed by `@uxf/ui/icon` (the `<Icon>` component and `IconsSet`/`IconName` types).
