# LAST Builder - Relottie API
Discover the last-builder package with utility functions to easily create Lottie Abstract Syntax Tree (LAST) nodes for Lottie generation or transformation.

# API Reference: `@lottiefiles/last-builder`

The `@lottiefiles/last-builder` package provides a set of composable utility functions designed to simplify the process of programmatically creating [LAST (Lottie Abstract Syntax Tree)](/docs/tools/relottie/guides/working-with-last) nodes and structures.

This is particularly useful when:

- Generating Lottie JSON files from scratch.
- Performing complex transformations within plugins that require adding or replacing nodes in the LAST tree.
- **Writing Unit Tests:** When testing relottie plugins, `last-builder` allows you to easily construct specific AST snippets or entire trees to use as input for your plugin's transformer function, isolating its logic without needing full Lottie JSON files.

## Installation

Install the package alongside `@lottiefiles/last` or `@lottiefiles/relottie`:

```bash
npm install @lottiefiles/last-builder
# or
yarn add @lottiefiles/last-builder
```

## Core Concept

Instead of manually constructing complex LAST node objects with all their required properties (`type`, `title`, `key`, `children`, etc.), `last-builder` offers short, named functions that create these nodes for you. You compose these functions together to build up the desired tree structure.

## Builder Functions

The source documentation highlights the following core builder functions through its example:

- **`rt(children, hasExpressions = false)`**: Creates a `Root` node. `children` is an array of top-level `Attribute`, `Element`, or `Collection` nodes. `hasExpressions` defaults to `false`.
- **`at(key, title, valueNode)`**: Creates an `Attribute` node.
  - `key`: The JSON key (string, e.g., `"v"`).
  - `title`: The semantic Lottie title from `TITLES` (e.g., `TITLES.string.version`).
  - `valueNode`: The child `Primitive` node (created using `pt`). Can also be an empty array `[]` if the attribute has no explicit value in some contexts.
- **`el(key, title, valueNode)`**: Creates an `Element` node.
  - `key`: The JSON key (string, e.g., `"meta"`).
  - `title`: The semantic Lottie title (e.g., `TITLES.element.metadata`).
  - `valueNode`: The child `ObjectNode` (created using `ob`). Can also be an empty array `[]`.
- **`ob(title, children)`**: Creates an `ObjectNode`.
  - `title`: The semantic Lottie title for the object itself (e.g., `TITLES.object.animationSettings`, or `TITLES.custom` for generic objects).
  - `children`: An array of `Member` nodes (`Attribute`, `Element`, `Collection`) within the object.
- **`cl(key, title, valueNode)`**: Creates a `Collection` node (example, consult package for exact name if different, e.g., `ct` or `col`).
  - `key`: The JSON key (string, e.g., `"layers"`).
  - `title`: The semantic Lottie title (e.g., `TITLES.collection.compositionChildren`).
  - `valueNode`: The child `ArrayNode` (created using `ar`). Can also be an empty array `[]`.
- **`ar(title, children)`**: Creates an `ArrayNode` (example, consult package for exact name if different).
  - `title`: The semantic Lottie title for the array (e.g., `TITLES.array.array`).
  - `children`: An array of `Primitive`, `ObjectNode`, or other `ArrayNode` instances.
- **`pt(value, valueType)`**: Creates a `Primitive` node.
  - `value`: The primitive value (`string`, `number`, `boolean`, `null`).
  - `valueType` (optional): Explicitly sets the `valueType` (`'string'`, `'number'`, `'boolean'`, `'null'`). If omitted, it's often inferred, but good practice to include for clarity.
- **`kn(value)`**: Creates a `KeyNode` (example, consult package for exact name if different). Used if you need to represent the key itself as a separate node object, though string keys are often accepted by `at`, `el`, `cl`.

_(Note: The exact names for all builder functions, like for `Collection` (`cl`), `ArrayNode` (`ar`), or `KeyNode` (`kn`), should be verified against the `@lottiefiles/last-builder` package's exports if not explicitly shown in examples. The `TITLES` object should be imported from `@lottiefiles/last` to provide the correct semantic titles.)_

## Usage Example

This example demonstrates how to build a simple LAST tree representing basic Lottie metadata using the builder functions:

```typescript
import { at, el, ob, pt, rt } from "@lottiefiles/last-builder";
import { TITLES } from "@lottiefiles/last"; // Import TITLES for semantic meaning
import type { Root } from "@lottiefiles/last"; // For type checking

// Build the LAST tree
const tree: Root = rt([
  // Create Root, default hasExpressions: false
  // Add version Attribute
  at("v", TITLES.string.version, pt("5.0.0", TITLES.string.version.startsWith("version") ? "string" : undefined)), // Example of providing valueType

  // Add metadata Element
  el(
    "meta", // key
    TITLES.element.metadata, // title
    // Create the child ObjectNode for the meta element
    ob(TITLES.object.custom, [
      // title for the ObjectNode (using 'custom' for a generic object container)
      // Attributes within the meta object
      at("a", TITLES.string.author, pt("LottieFiles")), // title from TITLES.string
      at("d", TITLES.string.description, pt("A lottie animation.")),
      at("g", TITLES.string.generator, pt("last-builder example")),
      at("data", TITLES.custom, pt(2)), // Example custom numeric data, using TITLES.custom for its title
    ])
  ),
]);

// Output the resulting LAST structure (for inspection)
// console.log(JSON.stringify(tree, null, 2));
```

This generated `tree` object can then be used within a relottie plugin (e.g., as input to a stringifier or as a tree to be modified) or stringified using `@lottiefiles/relottie-stringify`.

## Using `last-builder` for Unit Testing Plugins

When writing unit tests for your relottie plugins, `last-builder` is invaluable for creating precise AST inputs without needing to write full Lottie JSON strings and parse them.

Suppose you have a plugin that modifies the framerate of a Lottie animation:

```typescript
// --- your-plugin.ts (simplified example) ---
import type { Plugin, Transformer } from "unified";
import type { Root, Attribute, Primitive } from "@lottiefiles/last";
import { TITLES } from "@lottiefiles/last";
import { visit, EXIT } from "unist-util-visit";

export const setFrameratePlugin: Plugin<[{ fr: number }], Root> = (options) => {
  const transformer: Transformer<Root, Root> = (tree) => {
    visit(tree, "attribute", (node: Attribute) => {
      if (node.title === TITLES.number.framerate) {
        const valNode = node.children[0] as Primitive;
        if (valNode) valNode.value = options!.fr;
        return EXIT;
      }
    });
  };
  return transformer;
};
// --- end of your-plugin.ts ---

// --- your-plugin.test.ts ---
import { rt, at, pt, ob, el } from "@lottiefiles/last-builder";
import { TITLES } from "@lottiefiles/last";
import type { Root } from "@lottiefiles/last";
import { setFrameratePlugin } from "./your-plugin"; // Adjust path

// Mock or minimal unified processor for testing the transformer directly
import { unified } from "unified";

describe("setFrameratePlugin", () => {
  it("should update the framerate value", () => {
    // 1. Build a minimal AST with a framerate attribute using last-builder
    const initialTree: Root = rt([
      at(TITLES.number.framerate.slice(0, 2), TITLES.number.framerate, pt(30)), // key 'fr', title 'framerate', value 30
    ]);

    // 2. Create a unified processor and use the plugin with options
    const processor = unified().use(setFrameratePlugin, { fr: 60 });

    // 3. Run the transformer part of the plugin directly on the tree
    // For a full pipeline test, you might use processor.runSync(initialTree) or similar
    // but here we focus on the transformer's effect.
    // Note: unified().run() or .process() expects VFile or string, not direct tree for full flow.
    // To test transformer directly, you can extract it or use a simple runner.
    // For simplicity, we assume direct runnable access or a mock setup here.
    // A more robust way is to get the transformer function and call it:
    // const transformer = setFrameratePlugin({ fr: 60 });
    // if (typeof transformer === 'function') { // Check if it's a direct transformer or a plugin returning one
    //   transformer(initialTree, new VFile()); // VFile might be needed depending on plugin
    // }
    // Or let unified run it (more integrated test):
    const processedTree = processor.runSync(initialTree);

    // 4. Assert the change
    // We need to find the framerate attribute in the processedTree
    let newFramerateValue: number | undefined;
    visit(processedTree, "attribute", (node: Attribute) => {
      if (node.title === TITLES.number.framerate) {
        newFramerateValue = (node.children[0] as Primitive)?.value as number;
        return EXIT;
      }
    });

    expect(newFramerateValue).toBe(60);
  });
});
```

This approach allows for focused testing of a plugin's tree manipulation logic.

## Note on `position` Property

The `last-builder` functions are primarily concerned with constructing the logical structure of the AST (nodes, types, titles, children, values). They do **not** automatically add the `position` property (which details a node's location in a source file) to the nodes they create.

The `position` property is typically added by a **parser** (like `@lottiefiles/relottie-parse`) when it converts a Lottie JSON string into a LAST tree, as it has access to the source text and can calculate line, column, and offset information.

If you need `position` information on nodes created with `last-builder` (e.g., for highly specific testing scenarios or if generating a tree that should mimic a parsed one), you would have to manually construct the `Position` and `Point` objects (as defined by `unist` and exported by `@lottiefiles/last`) and assign them to the `position` field of your programmatically created nodes. This is generally not a primary use case for the builders themselves.

## Next Steps

- Refer to the [LAST Specification](/docs/tools/relottie/guides/working-with-last) for details on the nodes being created.
- See the [Modifying Lottie Files Guide](/docs/tools/relottie/guides/modifying-lottie-files) and [Creating Plugins Guide](/docs/tools/relottie/guides/creating-plugins) for scenarios where you might use this builder.
