> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/nrwl/nx/llms.txt
> Use this file to discover all available pages before exploring further.

# Create an Nx Plugin

> Build a publishable Nx plugin that integrates a tool or framework, infers tasks from config files, and can be shared across repositories.

An Nx plugin is an npm package that extends Nx with generators, executors, and project graph logic. Plugins can be used locally within a single workspace or published to npm and shared across multiple repos.

## Scaffolding a plugin

<Tabs>
  <Tab title="New workspace">
    ```bash theme={null}
    npx create-nx-plugin my-plugin
    ```

    This creates a new Nx workspace with a plugin already configured.
  </Tab>

  <Tab title="Existing workspace">
    ```bash theme={null}
    npx nx add @nx/plugin
    npx nx g @nx/plugin:plugin tools/my-plugin
    ```
  </Tab>
</Tabs>

## Plugin structure

After generation, the plugin folder contains:

```
tools/my-plugin/
├── src/
│   ├── generators/
│   │   └── my-generator/
│   │       ├── generator.ts
│   │       ├── generator.spec.ts
│   │       ├── schema.d.ts
│   │       └── schema.json
│   ├── executors/
│   │   └── my-executor/
│   │       ├── executor.ts
│   │       ├── executor.spec.ts
│   │       ├── schema.d.ts
│   │       └── schema.json
│   └── index.ts          ← plugin entry point
├── generators.json        ← registry of all generators
├── executors.json         ← registry of all executors
├── package.json
└── tsconfig.json
```

The `index.ts` file is the plugin entry point registered in `nx.json`. Export `createNodesV2` and `createDependencies` from this file to extend the project graph.

## Adding generators and executors

```bash theme={null}
# Add a generator
npx nx g @nx/plugin:generator tools/my-plugin/src/generators/application

# Add an executor
npx nx g @nx/plugin:executor tools/my-plugin/src/executors/build
```

See [local generators](/extending-nx/local-generators) and [local executors](/extending-nx/local-executors) for detailed implementation guidance.

## Inferring tasks with createNodesV2

The most powerful feature of a plugin is the ability to automatically create tasks for any project that has a particular config file. This removes the need for users to manually define targets in `project.json`.

Export `createNodesV2` from your plugin's `index.ts`:

```typescript theme={null}
// tools/my-plugin/src/index.ts
import {
  CreateNodesContextV2,
  CreateNodesV2,
  TargetConfiguration,
  createNodesFromFiles,
  joinPathFragments,
} from '@nx/devkit';
import { readdirSync, readFileSync } from 'fs';
import { dirname, join, resolve } from 'path';

export interface MyPluginOptions {
  buildTargetName?: string;
  devTargetName?: string;
}

// Glob pattern that identifies which files trigger this plugin
const configGlob = '**/my-tool.config.{js,ts}';

export const createNodesV2: CreateNodesV2<MyPluginOptions> = [
  configGlob,
  async (configFiles, options, context) => {
    return await createNodesFromFiles(
      (configFile, options, context) =>
        createNodesInternal(configFile, options ?? {}, context),
      configFiles,
      options,
      context
    );
  },
];

async function createNodesInternal(
  configFilePath: string,
  options: MyPluginOptions,
  context: CreateNodesContextV2
) {
  const projectRoot = dirname(configFilePath);

  // Only create a project if a package.json or project.json exists alongside the config
  const siblingFiles = readdirSync(join(context.workspaceRoot, projectRoot));
  if (
    !siblingFiles.includes('package.json') &&
    !siblingFiles.includes('project.json')
  ) {
    return {};
  }

  const buildTarget: TargetConfiguration = {
    command: 'my-tool build',
    options: { cwd: projectRoot },
    cache: true,
    inputs: [
      `{projectRoot}/my-tool.config.js`,
      `{projectRoot}/src/**/*`,
      { externalDependencies: ['my-tool'] },
    ],
    outputs: ['{projectRoot}/dist'],
  };

  return {
    projects: {
      [projectRoot]: {
        targets: {
          [options.buildTargetName ?? 'build']: buildTarget,
        },
      },
    },
  };
}
```

## Registering your plugin in nx.json

Add the plugin to the `plugins` array in `nx.json`. You can pass options that are forwarded to your `createNodesV2` function:

```json theme={null}
// nx.json
{
  "plugins": [
    {
      "plugin": "@myorg/my-plugin",
      "options": {
        "buildTargetName": "build",
        "devTargetName": "dev"
      }
    }
  ]
}
```

For a local plugin (not published to npm), reference the path directly:

```json theme={null}
{
  "plugins": [
    {
      "plugin": "./tools/my-plugin/src/index.ts"
    }
  ]
}
```

## Creating an init generator

If you create a generator named `init`, Nx runs it automatically when someone installs your plugin with `nx add`. Use it to register the plugin in `nx.json`:

```typescript theme={null}
// tools/my-plugin/src/generators/init/generator.ts
import { formatFiles, readNxJson, Tree, updateNxJson } from '@nx/devkit';

export async function initGenerator(tree: Tree, options: {}) {
  const nxJson = readNxJson(tree) ?? {};

  const hasPlugin = nxJson.plugins?.some((p) =>
    typeof p === 'string' ? p === '@myorg/my-plugin' : p.plugin === '@myorg/my-plugin'
  );

  if (!hasPlugin) {
    nxJson.plugins = [
      ...(nxJson.plugins ?? []),
      {
        plugin: '@myorg/my-plugin',
        options: {
          buildTargetName: 'build',
          devTargetName: 'dev',
        },
      },
    ];
  }

  updateNxJson(tree, nxJson);
  await formatFiles(tree);
}

export default initGenerator;
```

## Publishing a plugin to npm

<Steps>
  <Step title="Build the plugin">
    ```bash theme={null}
    nx build my-plugin
    ```
  </Step>

  <Step title="Update package.json">
    Make sure your `package.json` has the correct `name`, `version`, `main`, `generators`, and `executors` fields:

    ```json theme={null}
    {
      "name": "@myorg/my-plugin",
      "version": "1.0.0",
      "main": "./src/index.js",
      "generators": "./generators.json",
      "executors": "./executors.json"
    }
    ```
  </Step>

  <Step title="Publish to npm">
    ```bash theme={null}
    npm publish dist/tools/my-plugin --access public
    ```
  </Step>

  <Step title="Install in another workspace">
    ```bash theme={null}
    nx add @myorg/my-plugin
    ```

    If you have an `init` generator, it runs automatically.
  </Step>
</Steps>

<Tip>
  The generated plugin comes with an e2e test suite that spins up a local Verdaccio registry, publishes your plugin, creates a fresh Nx workspace, and installs your plugin — exactly mirroring the real user experience.
</Tip>

## Testing your plugin

The plugin generator creates an e2e project alongside your plugin. Run it with:

```bash theme={null}
nx e2e my-plugin-e2e
```

For unit testing the inferred task logic, use `nx show project` to inspect the computed project configuration:

```bash theme={null}
nx show project my-app --json
```

In e2e tests you can assert on this output:

```typescript theme={null}
it('should infer build target', () => {
  const projectDetails = JSON.parse(
    execSync('nx show project my-app --json', {
      cwd: projectDirectory,
    }).toString()
  );

  expect(projectDetails.targets.build).toMatchObject({
    cache: true,
    executor: 'nx:run-commands',
    outputs: ['{projectRoot}/dist'],
  });
});
```
