Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/generated/manifests/menus.json
Original file line number Diff line number Diff line change
Expand Up @@ -6171,6 +6171,14 @@
"isExternal": false,
"disableCollapsible": false
},
{
"id": "create-nodes",
"path": "/reference/core-api/plugin/generators/create-nodes",
"name": "create-nodes",
"children": [],
"isExternal": false,
"disableCollapsible": false
},
{
"id": "plugin-lint-checks",
"path": "/reference/core-api/plugin/generators/plugin-lint-checks",
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/manifests/new-nx-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -3837,6 +3837,15 @@
"path": "/reference/core-api/plugin/generators/executor",
"type": "generator"
},
"/reference/core-api/plugin/generators/create-nodes": {
"description": "Create an Inference Plugin (createNodes) for an Nx Plugin.",
"file": "generated/packages/plugin/generators/create-nodes.json",
"hidden": false,
"name": "create-nodes",
"originalFilePath": "/packages/plugin/src/generators/create-nodes/schema.json",
"path": "/reference/core-api/plugin/generators/create-nodes",
"type": "generator"
},
"/reference/core-api/plugin/generators/plugin-lint-checks": {
"description": "Adds linting configuration to validate common json files for nx plugins.",
"file": "generated/packages/plugin/generators/plugin-lint-checks.json",
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/packages-metadata.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 54 additions & 0 deletions docs/generated/packages/plugin/generators/create-nodes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "create-nodes",
"factory": "./src/generators/create-nodes/create-nodes",
"schema": {
"$schema": "https://json-schema.org/schema",
"cli": "nx",
"$id": "NxPluginCreateNodes",
"title": "Create an Inference Plugin (createNodes) for an Nx Plugin",
"description": "Create an Inference Plugin (createNodes) for an Nx Plugin.",
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The file path to the plugin. Relative to the current working directory.",
"x-prompt": "What is the plugin file path?",
"$default": { "$source": "argv", "index": 0 },
"x-priority": "important"
},
"name": { "type": "string", "description": "The plugin name." },
"targetName": {
"type": "string",
"description": "The default name for the target created by this plugin.",
"default": "echo",
"x-priority": "important"
},
"configFile": {
"type": "string",
"description": "The configuration file glob pattern to match (e.g., '**/my-config.json').",
"default": "**/.my-plugin-config.json",
"x-priority": "important"
},
"skipReadme": {
"type": "boolean",
"default": false,
"description": "Do not create or update README.md with plugin documentation."
},
"skipFormat": {
"type": "boolean",
"description": "Skip formatting files.",
"default": false,
"x-priority": "internal"
}
},
"required": ["path"],
"additionalProperties": false,
"presets": []
},
"description": "Create an Inference Plugin (createNodes) for an Nx Plugin.",
"implementation": "/packages/plugin/src/generators/create-nodes/create-nodes.ts",
"aliases": [],
"hidden": false,
"path": "/packages/plugin/src/generators/create-nodes/schema.json",
"type": "generator"
}
6 changes: 6 additions & 0 deletions docs/generated/packages/plugin/generators/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@
"useProjectJson": {
"type": "boolean",
"description": "Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file."
},
"includeCreateNodes": {
"type": "boolean",
"description": "Generate an Inference Plugin (createNodes) example with the plugin.",
"default": true,
"x-priority": "important"
}
},
"required": ["directory"],
Expand Down
1 change: 1 addition & 0 deletions docs/shared/reference/sitemap.md
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,7 @@
- [migration](/reference/core-api/plugin/generators/migration)
- [generator](/reference/core-api/plugin/generators/generator)
- [executor](/reference/core-api/plugin/generators/executor)
- [create-nodes](/reference/core-api/plugin/generators/create-nodes)
- [plugin-lint-checks](/reference/core-api/plugin/generators/plugin-lint-checks)
- [preset](/reference/core-api/plugin/generators/preset)
- [web](/reference/core-api/web)
Expand Down
5 changes: 5 additions & 0 deletions packages/plugin/generators.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
"schema": "./src/generators/executor/schema.json",
"description": "Create an executor for an Nx Plugin."
},
"create-nodes": {
"factory": "./src/generators/create-nodes/create-nodes",
"schema": "./src/generators/create-nodes/schema.json",
"description": "Create an Inference Plugin (createNodes) for an Nx Plugin."
},
"plugin-lint-checks": {
"factory": "./src/generators/lint-checks/generator",
"schema": "./src/generators/lint-checks/schema.json",
Expand Down
131 changes: 131 additions & 0 deletions packages/plugin/src/generators/create-nodes/create-nodes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import 'nx/src/internal-testing-utils/mock-project-graph';

import { Tree } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { pluginGenerator } from '../plugin/plugin';
import { createNodesGenerator } from './create-nodes';
import { setCwd } from '@nx/devkit/internal-testing-utils';

describe('create-nodes generator', () => {
let tree: Tree;
let projectName: string;

beforeEach(async () => {
projectName = 'my-plugin';
tree = createTreeWithEmptyWorkspace();
setCwd('');
await pluginGenerator(tree, {
directory: projectName,
unitTestRunner: 'jest',
linter: 'eslint',
compiler: 'tsc',
includeCreateNodes: false,
});
});

it('should generate files', async () => {
await createNodesGenerator(tree, {
name: 'my-plugin',
path: 'my-plugin/src/plugins/my-plugin/plugin',
});

expect(
tree.exists('my-plugin/src/plugins/my-plugin/plugin.ts')
).toBeTruthy();
});

it('should create README when it does not exist', async () => {
await createNodesGenerator(tree, {
name: 'my-plugin',
path: 'my-plugin/src/plugins/my-plugin/plugin',
});

const readmePath = 'my-plugin/README.md';
expect(tree.exists(readmePath)).toBeTruthy();

const readmeContent = tree.read(readmePath, 'utf-8');
expect(readmeContent).toContain('## What is an Inference Plugin?');
expect(readmeContent).toContain('## How This Plugin Works');
});

it('should append to existing README without duplicating content', async () => {
const readmePath = 'my-plugin/README.md';
tree.write(readmePath, '# My Plugin\n\nExisting content');

await createNodesGenerator(tree, {
name: 'my-plugin',
path: 'my-plugin/src/plugins/my-plugin/plugin',
});

const readmeContent = tree.read(readmePath, 'utf-8');
expect(readmeContent).toContain('# My Plugin');
expect(readmeContent).toContain('Existing content');
expect(readmeContent).toContain('## What is an Inference Plugin?');

// Run generator again to ensure no duplication
await createNodesGenerator(tree, {
name: 'another-plugin',
path: 'my-plugin/src/plugins/another-plugin/plugin',
});

const updatedContent = tree.read(readmePath, 'utf-8');
const matches = updatedContent.match(/## What is an Inference Plugin\?/g);
expect(matches?.length).toBe(1);
});

it('should skip README when skipReadme is true', async () => {
await createNodesGenerator(tree, {
name: 'my-plugin',
path: 'my-plugin/src/plugins/my-plugin/plugin',
skipReadme: true,
});

// README exists from plugin generator, but should not contain our inference plugin content
const readmePath = 'my-plugin/README.md';
expect(tree.exists(readmePath)).toBeTruthy();
const readmeContent = tree.read(readmePath, 'utf-8');
expect(readmeContent).not.toContain('## What is an Inference Plugin?');
});

it('should use custom targetName', async () => {
await createNodesGenerator(tree, {
name: 'my-plugin',
path: 'my-plugin/src/plugins/my-plugin/plugin',
targetName: 'custom-target',
});

const pluginContent = tree.read(
'my-plugin/src/plugins/my-plugin/plugin.ts',
'utf-8'
);
expect(pluginContent).toContain(
"targetName: options.targetName ?? 'custom-target'"
);
expect(pluginContent).toContain("@default 'custom-target'");
});

it('should use custom configFile pattern', async () => {
await createNodesGenerator(tree, {
name: 'my-plugin',
path: 'my-plugin/src/plugins/my-plugin/plugin',
configFile: '**/custom.config.json',
});

const pluginContent = tree.read(
'my-plugin/src/plugins/my-plugin/plugin.ts',
'utf-8'
);
expect(pluginContent).toContain("'**/custom.config.json'");
});

it('should handle path with file extension', async () => {
await createNodesGenerator(tree, {
name: 'my-plugin',
path: 'my-plugin/src/plugins/my-plugin/plugin.ts',
});

expect(
tree.exists('my-plugin/src/plugins/my-plugin/plugin.ts')
).toBeTruthy();
});
});
Loading
Loading