# relottie Usage Guide
A comprehensive guide to using relottie packages. Learn about prerequisites, installation, the basic processing pipeline, and how to use plugins for Lottie animations.

# Guide: Using relottie

This guide builds upon the [Quick Start](/docs/tools/relottie/getting-started/quick-start) and dives deeper into setting up your environment and using the core `relottie` processor programmatically.

## Prerequisites

- **Node.js:** relottie requires a maintained version of Node.js. As noted in the source documentation, versions **16.0+** and **18.0+** are tested and recommended.
- **Package Manager:** `npm` or `yarn` for installing packages.
- **TypeScript (Recommended):** The relottie ecosystem is fully typed with TypeScript. While you can use it with JavaScript, using TypeScript provides better autocompletion and helps prevent errors, especially when [creating plugins](/docs/tools/relottie/guides/creating-plugins).

## Installation

The primary package you'll need for basic processing is `@lottiefiles/relottie`. This package bundles the core processor, the Lottie parser ([`@lottiefiles/relottie-parse`](/docs/tools/relottie/api/relottie-parse)), and the Lottie stringifier ([`@lottiefiles/relottie-stringify`](/docs/tools/relottie/api/relottie-stringify)).

For creating plugins and interacting with the Lottie Abstract Syntax Tree (LAST), you'll also need `@lottiefiles/last` for type definitions and `unist-util-visit` for tree traversal. If you are working in a TypeScript project, you'll likely also need `unified` and `vfile` as development dependencies.

```bash
npm install @lottiefiles/relottie @lottiefiles/last unist-util-visit
# For TypeScript projects, also consider adding:
npm install unified vfile --save-dev
# or
yarn add @lottiefiles/relottie @lottiefiles/last unist-util-visit
# For TypeScript projects, also consider adding:
yarn add unified vfile --dev
```

## Basic Processing Pipeline

`relottie` works by processing content through a pipeline managed by the underlying [unified](https://unifiedjs.com/) engine. The simplest pipeline involves:

1. **Parsing:** Converting input Lottie JSON into a [LAST](/docs/tools/relottie/guides/working-with-last) tree.
2. **Stringifying:** Converting the LAST tree back into output Lottie JSON.

The `@lottiefiles/relottie` package provides a convenient way to set up this basic pipeline.

### Example: Processing with a Simple Plugin

Let's adapt the version update plugin from the [Quick Start](/docs/tools/relottie/getting-started/quick-start) guide to demonstrate the processing pipeline with a transformation. This example will parse a Lottie JSON string, update its version using a custom plugin, and then stringify it back to a JSON string.

```typescript
import { TITLES } from "@lottiefiles/last";
import type { Root, Attribute } from "@lottiefiles/last";
import { relottie } from "@lottiefiles/relottie";
import type { Plugin } from "unified";
import { CONTINUE, EXIT, visit } from "unist-util-visit";
import type { Data, VFile } from "vfile";

const { string: ST } = TITLES;

// Define potential options for our plugin
interface Options {
  ignore: boolean;
  version: string;
}

// Define the type for plugin options, making them all optional
export type PluginOptions = Partial<Options>;

// Define default options for the plugin
export const DEFAULT_OPTIONS: Options = {
  ignore: false,
  version: "6.0.0", // Default version to set
};

// Define a custom data structure to be attached to the VFile's `data` property.
// This allows plugins to pass information to each other or to the calling process.
export interface FileData extends Data {
  updateVersionData?: {
    // Optional property
    oldVersion?: string; // Optional property
    newVersion?: string; // Optional property
  };
}

// Define our simple plugin to update the Lottie version
const updateVersionPlugin: Plugin<[PluginOptions?], Root> = (ops: PluginOptions = {}) => {
  // Merge provided options with defaults
  const options: Options = { ...DEFAULT_OPTIONS, ...ops };
  // Prepare an object to store data related to this plugin's operation
  const pluginOperationData: FileData["updateVersionData"] = {};

  // The transformer function that unified will call with the AST (tree) and file (VFile)
  function transformer(tree: Root, file: VFile): void {
    if (options.ignore) {
      console.log("Version update ignored due to plugin options.");
      return;
    }

    visit(tree, "attribute", (node: Attribute, _parent) => {
      if (node.title !== ST.version) return CONTINUE; // Skip if not the version attribute

      const primitiveNode = node.children[0];

      if (!primitiveNode) {
        console.warn("Version attribute is missing a primitive node. Skipping update.");
        return CONTINUE;
      }

      if (typeof primitiveNode.value !== "string") {
        console.warn("Version attribute value is not a string. Skipping update.");
        return CONTINUE;
      }

      // Store the old version and the new version
      pluginOperationData.oldVersion = primitiveNode.value;
      pluginOperationData.newVersion = options.version;

      // Update the version value
      primitiveNode.value = options.version;

      console.log(`Version updated from ${pluginOperationData.oldVersion} to ${primitiveNode.value}`);

      return EXIT; // Stop traversing, version found and updated
    });

    // Attach the collected data to the VFile for potential use by other plugins or the main process
    // Ensure 'file.data' is initialized if it's not already
    file.data = file.data || {};
    file.data.updateVersionData = pluginOperationData;
  }

  return transformer;
};

const inputLottie = '{"v":"5.5.7","nm":"Example Animation","w":512,"h":512,"layers":[]}';

async function processLottieWithPlugin() {
  try {
    // Create a relottie processor, use our plugin with options, then process
    const file = await relottie().use(updateVersionPlugin, { version: "7.0.0", ignore: false }).process(inputLottie);

    const outputLottie = String(file); // Get the stringified Lottie JSON

    console.log("\\nProcessed Lottie:");
    console.log(outputLottie);

    // Access data attached by the plugin via VFile.data
    const processedFileData = file.data as FileData;
    if (processedFileData.updateVersionData) {
      console.log("\\nPlugin Operation Data:");
      console.log(` - Old version: ${processedFileData.updateVersionData.oldVersion}`);
      console.log(` - New version: ${processedFileData.updateVersionData.newVersion}`);
    }
  } catch (error) {
    console.error("Error processing Lottie:", error);
  }
}

processLottieWithPlugin();
```

**Running this script (e.g., with `npx ts-node your-script.ts`) would output something like:**

```text
Version updated from 5.5.7 to 7.0.0

Processed Lottie:
{"v":"7.0.0","nm":"Example Animation","w":512,"h":512,"layers":[]}

Plugin Operation Data:
 - Old version: 5.5.7
 - New version: 7.0.0
```

**Explanation of Key Parts:**

- **Imports:**
  - `TITLES` from `@lottiefiles/last`: Provides standardized semantic titles for Lottie properties, ensuring consistent access.
  - `Root`, `Attribute` from `@lottiefiles/last`: TypeScript types for the AST nodes.
  - `relottie` from `@lottiefiles/relottie`: The main entry point to create a processor.
  - `Plugin` from `unified`: The base type for creating plugins.
  - `CONTINUE`, `EXIT`, `visit` from `unist-util-visit`: Utilities for traversing the AST. `visit` is used to find specific nodes, `CONTINUE` tells the visitor to keep searching, and `EXIT` stops the traversal.
  - `Data`, `VFile` from `vfile`: `VFile` is a virtual file representation used by `unified` to carry the content and metadata through the pipeline. `Data` is an interface for the `VFile.data` property.
- **`Options`, `PluginOptions`, `DEFAULT_OPTIONS`:** These define the configuration that can be passed to our `updateVersionPlugin`. `PluginOptions` (using `Partial<Options>`) makes all options optional, and `DEFAULT_OPTIONS` provides sensible defaults.
- **`FileData`:** This interface extends `VFile`'s `Data` type to define a custom structure (`updateVersionData`) where our plugin can store information it gathered or produced. This data can be accessed by subsequent plugins or by the code that initiated the processing.
- **`updateVersionPlugin`:**
  - It's a function that accepts `PluginOptions` and returns a `transformer` function.
  - The `transformer` function is the core of the plugin. It receives the `tree` (the AST, typed as `Root`) and the `file` (a `VFile` instance).
  - `visit(tree, "attribute", ...)`: This traverses the AST, looking for nodes of type `"attribute"`. The callback function is executed for each `Attribute` node found.
  - `if (node.title !== ST.version) return CONTINUE;`: This checks if the current attribute's semantic `title` is the Lottie version (using `ST.version` from `TITLES`).
  - `primitiveNode.value = options.version;`: Modifies the Lottie version in the AST.
  - `file.data.updateVersionData = pluginOperationData;`: Attaches the collected data (old and new version) to `file.data`.
- **`processLottieWithPlugin` Function:**
  - `relottie()`: Creates a new processor instance.
  - `.use(updateVersionPlugin, { version: "7.0.0", ignore: false })`: Registers our plugin and passes options to it.
  - `.process(inputLottie)`: Starts the processing.

This example illustrates how `relottie` parses the input, allows a plugin to modify the AST, and then stringifies the modified AST back into a Lottie JSON string. The `VFile` object plays a crucial role in carrying both the content and any metadata or operational data through this pipeline.

## Understanding the `process` Method

- `relottie()` creates a new processor instance configured with the default Lottie parser and stringifier.
- `.process(input)` takes the input Lottie JSON (as a string, Buffer, or [VFile](https://github.com/vfile/vfile)) and runs it through the pipeline. This is an asynchronous operation.
  - **Input**: The `input` can be:
    - A Lottie JSON string.
    - A Node.js `Buffer` containing Lottie JSON data.
    - A `VFile` instance. If you provide a `VFile`, `relottie` can use its `path` for error messages and its `value` as the content to process. Plugins can also read from and write to `vfile.data`.
- It returns a `Promise` that resolves to a `VFile` object representing the processed file. This `VFile` object will contain:
  - The processed Lottie content (accessible via `String(file)` or `file.value`).
  - Any messages (warnings, errors) generated during processing (on `file.messages`).
  - Any data attached by plugins to `file.data`.
- `String(file)` or `file.toString()` extracts the resulting Lottie JSON string from the `VFile`.

## Adding Plugins (`use`)

The real power of relottie comes from adding **plugins** to the pipeline using the `.use()` method. Plugins are inserted between the parsing and stringifying steps, allowing you to inspect, analyze, or modify the [LAST](/docs/tools/relottie/guides/working-with-last) tree.

You can chain multiple `.use()` calls to apply several plugins in sequence. Each plugin will operate on the AST as modified by the preceding plugins.

```typescript
import { relottie } from "@lottiefiles/relottie";
import type { Plugin, Transformer } from "unified";
import type { Root, Attribute } from "@lottiefiles/last";
import { visit } from "unist-util-visit";
import type { VFile } from "vfile";

// Assume updateVersionPlugin is defined as in the previous example

// Another simple plugin: counts the number of top-level layers
interface LayerCounterFileData extends VFile { // Extends VFile, or use 'Data' for file.data
  data: {
    layerCount?: number;
  }
}

const countLayersPlugin: Plugin<[], Root> = () => {
  const transformer: Transformer<Root, Root> = (tree: Root, file: LayerCounterFileData) => {
    let count = 0;
    visit(tree, "collection", (node) => {
      if (node.key === "layers" && node.title === "composition-children") {
        // Assuming the direct children of the 'layers' collection's ArrayNode are layer objects
        const arrayNode = node.children[0];
        if (arrayNode && arrayNode.type === "array") {
          count = arrayNode.children.length;
        }
        return false; // Stop traversal for this branch
      }
    });
    file.data = file.data || {}; // Ensure data object exists
    file.data.layerCount = count;
    console.log(`Layer Counter Plugin: Found ${count} layers.`);
  };
  return transformer;
};


const inputLottie = '{"v":"5.5.7", "nm":"Example With Layers", "w":100, "h":100, "layers":[{"ty":4},{"ty":1}]}\';

async function processWithMultiplePlugins() {
  try {
    const file = await relottie()
      .use(updateVersionPlugin, { version: "8.0.0" }) // First plugin: update version
      .use(countLayersPlugin) // Second plugin: count layers
      .process(inputLottie);

    console.log("\\nFinal Lottie JSON:");
    console.log(String(file));

    // Access data from both plugins
    const processedFileData = file.data as FileData & LayerCounterFileData['data'];
    if (processedFileData.updateVersionData) {
      console.log("\\nVersion Plugin Data:");
      console.log(` - Old version: ${processedFileData.updateVersionData.oldVersion}`);
      console.log(` - New version: ${processedFileData.updateVersionData.newVersion}`);
    }
    if (typeof processedFileData.layerCount === 'number') {
      console.log("\\nLayer Counter Plugin Data:");
      console.log(` - Layer count: ${processedFileData.layerCount}`);
    }

  } catch (error) {
    console.error("Error during multi-plugin processing:", error);
  }
}

processWithMultiplePlugins();
```

**Expected Output (example):**

```text
Version updated from 5.5.7 to 8.0.0
Layer Counter Plugin: Found 2 layers.

Final Lottie JSON:
{"v":"8.0.0","nm":"Example With Layers","w":100,"h":100,"layers":[{"ty":4},{"ty":1}]}

Version Plugin Data:
 - Old version: 5.5.7
 - New version: 8.0.0

Layer Counter Plugin Data:
 - Layer count: 2
```

In this chained example:

1. `updateVersionPlugin` runs first, modifying the version in the AST and attaching `updateVersionData` to `file.data`.
2. `countLayersPlugin` runs next, operating on the AST _already modified by `updateVersionPlugin`_. It counts the layers and attaches `layerCount` to `file.data`.
3. The final `file.data` contains information from both plugins.

## Next Steps

Now that you have a basic understanding of setting up relottie and its processing pipeline, you can explore more specific tasks:

- **[Guide: Analyzing Lottie Files](/docs/tools/relottie/guides/analyzing-lottie-files):** Use plugins to extract information.
- **[Guide: Modifying Lottie Files](/docs/tools/relottie/guides/modifying-lottie-files):** Learn techniques for changing Lottie content.
- **[Guide: Working with LAST](/docs/tools/relottie/guides/working-with-last):** Understand the tree structure in more detail.
- **[API Reference](/docs/tools/relottie/api):** Consult the detailed API documentation.
