> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fallow.tools/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom plugins

> Create custom fallow plugins for internal frameworks using declarative JSON or TOML files. Define entry points, config files, and used exports.

Create a declarative plugin file for any internal framework or tool fallow doesn't cover. Plugins are JSON or TOML files that tell fallow about your framework's conventions.

<Tip>
  Start with JSONC format for `$schema` support and IDE autocomplete.
</Tip>

## Quick start

<Steps>
  <Step title="Create the plugin file">
    Create a `fallow-plugin-<name>.jsonc` file in your project root:

    ```jsonc theme={null}
    {
      "$schema": "./plugin-schema.json",
      "name": "my-framework",
      "enablers": ["my-framework"],
      "entryPoints": ["src/routes/**/*.{ts,tsx}"],
      "alwaysUsed": ["src/setup.ts"],
      "toolingDependencies": ["my-framework-cli"],
      "usedExports": [
        { "pattern": "src/routes/**/*.{ts,tsx}", "exports": ["default", "loader", "action"] }
      ]
    }
    ```

    Fallow auto-discovers `fallow-plugin-*` files in your project root.
  </Step>

  <Step title="Configure the fields">
    Customize the plugin fields for your framework. Only `name` is required. Add the fields relevant to your use case.

    ### Required

    | Field  | Type   | Description                                           |
    | :----- | :----- | :---------------------------------------------------- |
    | `name` | string | Unique plugin name (shown in `fallow list --plugins`) |

    ### Optional

    | Field                 | Type                  | Description                                                                                                                                                 |
    | :-------------------- | :-------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `enablers`            | string\[]             | Package names that activate this plugin                                                                                                                     |
    | `entryPoints`         | string\[]             | Glob patterns for entry point files                                                                                                                         |
    | `configPatterns`      | string\[]             | Glob patterns for config files (always-used)                                                                                                                |
    | `alwaysUsed`          | string\[]             | Files always considered used                                                                                                                                |
    | `toolingDependencies` | string\[]             | Packages used via CLI, not imports                                                                                                                          |
    | `detection`           | object                | Rich activation logic (see below)                                                                                                                           |
    | `usedExports`         | object\[]             | Exports always considered used                                                                                                                              |
    | `usedClassMembers`    | (string \| object)\[] | Class method/property names the framework invokes at runtime; scoped objects `{ extends?, implements?, members }` match only classes with matching heritage |
  </Step>

  <Step title="Verify the plugin">
    Confirm fallow picks up your plugin:

    ```bash theme={null}
    fallow list --plugins
    ```
  </Step>
</Steps>

## Supported formats

| Format | Extension | Comments         |
| :----- | :-------- | :--------------- |
| JSONC  | `.jsonc`  | `//` and `/* */` |
| JSON   | `.json`   | No               |
| TOML   | `.toml`   | `#`              |

## Detection strategies

Plugins can use simple enablers or rich detection logic with boolean combinators.

<Accordion title="Simple detection (enablers)">
  The simplest way to activate a plugin. Package names are checked against `package.json`. The plugin activates if any enabler matches:

  ```jsonc theme={null}
  {
    "enablers": ["my-framework", "@myorg/"]  // prefix matching with trailing /
  }
  ```

  A trailing `/` enables prefix matching, so `@myorg/` matches any package in the `@myorg` scope.
</Accordion>

<Accordion title="File-based detection">
  Activate a plugin when specific config files exist in the project:

  ```jsonc theme={null}
  {
    "detection": { "type": "fileExists", "pattern": "nuxt.config.*" }
  }
  ```
</Accordion>

<Accordion title="Combined detection (boolean logic)">
  Combine multiple conditions with `all` (AND), `any` (OR), or `not`:

  ```jsonc theme={null}
  {
    "detection": {
      "type": "all",
      "conditions": [
        { "type": "dependency", "package": "@my-org/core" },
        { "type": "fileExists", "pattern": "my-org.config.*" }
      ]
    }
  }
  ```

  This plugin only activates when both `@my-org/core` is installed **and** a `my-org.config.*` file exists.
</Accordion>

## Used exports

Mark specific exports as always-used for convention-based frameworks. These are exports consumed by name at runtime:

```jsonc theme={null}
{
  "usedExports": [
    { "pattern": "src/routes/**/*.{ts,tsx}", "exports": ["default", "loader", "action", "meta"] }
  ]
}
```

## Used class members

Mark class method or property names that the framework invokes at runtime via an interface or contract pattern. Listed names extend fallow's built-in Angular and React lifecycle allowlist, so members implementing these contracts are never flagged as unused class members.

Each entry is either a plain member name (global suppression) or a scoped object that only matches classes whose heritage clause includes the configured `extends` or `implements` identifier:

```jsonc theme={null}
{
  "name": "ag-grid",
  "enablers": ["ag-grid-angular"],
  "usedClassMembers": [
    "agInit",
    { "implements": "ICellRendererAngularComp", "members": ["refresh"] }
  ]
}
```

Plain names like `agInit` are unique enough to suppress globally. Common names like `refresh` or `execute` would produce false negatives on unrelated classes, so scope them with `implements` or `extends`. A scoped rule requires at least one of `extends` or `implements`; an unconstrained object rule is rejected at load time.

This is the right tool for libraries that call methods on consumer classes reflectively: ag-Grid's `AgFrameworkComponent<T>`, TypeORM's `MigrationInterface` (`up`, `down`), Web Components (`connectedCallback`, `disconnectedCallback`, `attributeChangedCallback`), or any strategy/plugin pattern where the library invokes your methods rather than your code doing so directly.

The allowlist only applies to class methods and properties. Enum members with the same names are still checked.

For project-wide allowlists that apply regardless of which packages are installed, use the top-level `usedClassMembers` field in your fallow config instead. See [Configuration overview](/configuration/overview).

## Discovery order

Fallow searches for plugin files in this order:

1. **Explicit paths** from the `plugins` config field
2. **`.fallow/plugins/`** directory
3. **Project root**: `fallow-plugin-*.{jsonc,json,toml}` files

### Using the `plugins` config field

```jsonc theme={null}
// .fallowrc.json
{
  "plugins": [
    "tools/fallow-plugins/",
    "vendor/my-plugin.jsonc"
  ]
}
```

## Examples

### React Router

```jsonc theme={null}
{
  "$schema": "./plugin-schema.json",
  "name": "react-router",
  "enablers": ["react-router", "@tanstack/react-router"],
  "entryPoints": ["src/routes/**/*.{ts,tsx}", "app/routes/**/*.{ts,tsx}"],
  "configPatterns": ["react-router.config.{ts,js}"],
  "toolingDependencies": ["@react-router/dev"],
  "usedExports": [
    { "pattern": "{src,app}/routes/**/*.{ts,tsx}", "exports": ["default", "loader", "action", "meta", "handle", "shouldRevalidate"] }
  ]
}
```

### ag-Grid Angular

```jsonc theme={null}
{
  "$schema": "./plugin-schema.json",
  "name": "ag-grid",
  "enablers": ["ag-grid-angular"],
  "usedClassMembers": [
    "agInit",
    { "implements": "ICellRendererAngularComp", "members": ["refresh"] }
  ]
}
```

ag-Grid Angular cell renderers implement `ICellRendererAngularComp`, which requires `agInit(params)` and `refresh(params)` methods that ag-Grid calls at runtime. `agInit` is unique to ag-Grid so it stays global. `refresh` is a common method name, so scoping it to classes that `implements ICellRendererAngularComp` keeps suppression off unrelated `refresh()` methods elsewhere in the codebase.

### Internal tooling

```jsonc theme={null}
{
  "$schema": "./plugin-schema.json",
  "name": "our-build-system",
  "enablers": ["@internal/build"],
  "configPatterns": ["build.config.{ts,js}", ".buildrc"],
  "alwaysUsed": ["scripts/build/**/*.ts", "config/**/*.ts"],
  "toolingDependencies": ["@internal/build", "@internal/lint-rules"]
}
```

<Info>
  External plugins cover most use cases. AST-based config parsing (like the built-in ESLint or Vite plugins do) requires a built-in Rust plugin.
</Info>

## JSON Schema

Generate the schema locally for IDE autocomplete. The schema is generated dynamically and doesn't exist at a remote URL:

```bash theme={null}
fallow plugin-schema > plugin-schema.json
```

Then reference it in your plugin file:

```jsonc theme={null}
{
  "$schema": "./plugin-schema.json"
}
```

## See also

<CardGroup cols={2}>
  <Card title="Built-in plugins" icon="puzzle-piece" href="/frameworks/built-in">
    Browse the 95 built-in framework plugins.
  </Card>

  <Card title="fallow list" icon="list" href="/cli/list">
    Verify active plugins and inspect project metadata.
  </Card>
</CardGroup>
