Repository navigation
fix(nx): track local generator and executor entry points (#2117) - #2121
Conversation
commit: |
| title, | ||
| enablers, | ||
| isEnabled, | ||
| packageJsonPath: getNxPackageConfiguration, |
There was a problem hiding this comment.
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.
| 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))); |
There was a problem hiding this comment.
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:
schematicsandbuildersas aliases, inpackage.json(generators, executors) and in the collection files (generators, executors)implementationinstead offactoryfor generators- an export name after
#, as in./src/bar/executor#run batchImplementationfor executors
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
left a comment
There was a problem hiding this comment.
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.
|
🚀 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. |
Problem
Knip reports the factory and implementation files of local Nx plugins as unused. Nx declares these files indirectly: a plugin’s
package.jsonpoints togenerators.jsonorexecutors.json, which in turn contains their paths.Solution
The Nx plugin now follows those manifest references and adds each generator
factoryand executorimplementationas 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