Skip to content

fix(nx): track local generator and executor entry points (#2117) - #2121

Merged
webpro merged 6 commits into
webpro-nl:mainfrom
DreamLongYT:issue-2117
Oct 12, 2026
Merged

webpro merged 6 commits into
webpro-nl:mainfrom
DreamLongYT:issue-2117

Conversation

@DreamLongYT

@DreamLongYT DreamLongYT commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Knip reports the factory and implementation files of local Nx plugins as unused. Nx declares these files indirectly: a plugin’s package.json points to generators.json or executors.json, which in turn contains their paths.

Solution

The Nx plugin now follows those manifest references and adds each generator factory and executor implementation as an entry. For example, it recognizes paths like:

{
  "generators": {
    "foo": { "factory": "./src/foo/generator" }
  },
  "executors": {
    "bar": { "implementation": "./src/bar/executor" }
  }
}

The paths are resolved as modules, so extensionless paths such as these correctly match their source files. Added a regression fixture covering both generators and executors. The Nx tests, package build, and repository CI checks pass.

Fixes #2117

@pkg-pr-new

pkg-pr-new Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/knip@2121
npm i https://pkg.pr.new/@knip/language-server@2121
npm i https://pkg.pr.new/@knip/mcp@2121

commit: 82e8d8c

@webpro webpro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this up! The fixture matches the layout from #2117, and the new test fails without the change. The plugin needs a different approach, though. Details are inline.

Comment thread packages/knip/src/plugins/nx/index.ts Outdated
title,
enablers,
isEnabled,
packageJsonPath: getNxPackageConfiguration,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this override, along with getNxPackageConfiguration and NxPackageConfiguration. packageJsonPath tells core where a plugin's own configuration lives in package.json, which for Nx is the nx field. The generators and executors fields aren't Nx project configuration, and renaming them to generatorsFile and executorsFile produces an object that neither Nx nor knip defines. The function also always returns an object, so core no longer skips manifests without an nx field and runs resolveConfig on each workspace's package.json.

Comment thread packages/knip/src/plugins/nx/index.ts Outdated
Comment on lines +60 to +72
const metadataConfigs = [config.generatorsFile, config.executorsFile]
.filter((file): file is string => typeof file === 'string')
.map(file => toConfig('nx', join(options.configFileDir, file)));

const generatorEntries = Object.values(config.generators ?? {})
.map(generator => generator.factory)
.filter((factory): factory is string => typeof factory === 'string')
.map(factory => toDeferResolveEntry(join(options.configFileDir, factory)));

const executorEntries = Object.values(config.executors ?? {})
.map(executor => executor.implementation)
.filter((implementation): implementation is string => typeof implementation === 'string')
.map(implementation => toDeferResolveEntry(join(options.configFileDir, implementation)));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generators and executors fields are top-level package.json fields, which the resolve hook can read from options.manifest once per workspace. Loading the collection files there also keeps them out of resolveConfig, which handles project.json too, where generators holds generator defaults rather than a collection.

Nx also accepts:

Something like this would cover them:

const collections: [string, string][] = [
  ['generators', 'schematics'],
  ['executors', 'builders'],
];

const resolve: Resolve = async options => {
  const inputs: Input[] = [];
  for (const [key, alias] of collections) {
    const file = get(options.manifest, key) ?? get(options.manifest, alias);
    if (typeof file !== 'string') continue;
    const filePath = join(options.cwd, file);
    if (!isFile(filePath)) continue;
    const collection: NxCollection = await load(filePath);
    for (const entries of [collection[key], collection[alias]]) {
      for (const entry of Object.values(entries ?? {})) {
        if (typeof entry === 'string') continue;
        for (const id of [entry.implementation ?? entry.factory, entry.batchImplementation]) {
          if (id) inputs.push(toDeferResolveEntry(join(dirname(filePath), substringBefore(id, '#'))));
        }
      }
    }
  }
  return inputs;
};

With this in types.ts:

type NxCollectionEntry = string | { factory?: string; implementation?: string; batchImplementation?: string };

export type NxCollection = Record<string, Record<string, NxCollectionEntry> | undefined>;

A string entry points to an executor in another package, so it's skipped. Could the fixture also cover these forms?

@webpro webpro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reworking this! The resolve hook now picks up the aliases, # export names and batchImplementation, skips string entries, and the fixture covers all of them.

I pushed a small follow-up. Nx only falls back to factory when implementation is missing, so for a generator with both, only the implementation file is now an entry (with a fixture case). It also drops the two as casts, names the hook resolve like in the other plugins, and asserts the counters in the test.

@webpro
webpro merged commit 558407f into webpro-nl:main Oct 12, 2026
32 checks passed
@webpro

webpro commented Oct 18, 2026

Copy link
Copy Markdown
Member

🚀 This pull request is included in v6.42.0. See Release 6.42.0 for release notes.

Using Knip in a commercial project? Please consider becoming a sponsor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

💡 Nx plugin: read generators and executors from package.json

2 participants