fix(core): list local plugins (#11410)
This commit is contained in:
parent
f8da1ad42b
commit
db97728793
@ -1,23 +1,26 @@
|
||||
import * as chalk from 'chalk';
|
||||
import { prompt } from 'enquirer';
|
||||
import { readJsonFile } from 'nx/src/utils/fileutils';
|
||||
|
||||
import { readNxJson } from '../config/configuration';
|
||||
import { NxJsonConfiguration } from '../config/nx-json';
|
||||
import { ProjectsConfigurations } from '../config/workspace-json-project-json';
|
||||
import { Workspaces } from '../config/workspaces';
|
||||
import { FileChange, flushChanges, FsTree } from '../generators/tree';
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
readProjectsConfigurationFromProjectGraph,
|
||||
} from '../project-graph/project-graph';
|
||||
import { logger } from '../utils/logger';
|
||||
import {
|
||||
combineOptionsForGenerator,
|
||||
handleErrors,
|
||||
Options,
|
||||
Schema,
|
||||
} from '../utils/params';
|
||||
import { Workspaces } from '../config/workspaces';
|
||||
import { FileChange, flushChanges, FsTree } from '../generators/tree';
|
||||
import { logger } from '../utils/logger';
|
||||
import * as chalk from 'chalk';
|
||||
import { workspaceRoot } from '../utils/workspace-root';
|
||||
import { NxJsonConfiguration } from '../config/nx-json';
|
||||
import { getLocalWorkspacePlugins } from '../utils/plugins/local-plugins';
|
||||
import { printHelp } from '../utils/print-help';
|
||||
import { prompt } from 'enquirer';
|
||||
import { readJsonFile } from 'nx/src/utils/fileutils';
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
readProjectsConfigurationFromProjectGraph,
|
||||
} from '../project-graph/project-graph';
|
||||
import { readNxJson } from '../config/configuration';
|
||||
import { workspaceRoot } from '../utils/workspace-root';
|
||||
|
||||
export interface GenerateOptions {
|
||||
collectionName: string;
|
||||
@ -44,18 +47,21 @@ export function printChanges(fileChanges: FileChange[]) {
|
||||
async function promptForCollection(
|
||||
generatorName: string,
|
||||
ws: Workspaces,
|
||||
interactive: boolean
|
||||
) {
|
||||
interactive: boolean,
|
||||
projectsConfiguration: ProjectsConfigurations
|
||||
): Promise<string> {
|
||||
const packageJson = readJsonFile(`${workspaceRoot}/package.json`);
|
||||
const collections = Array.from(
|
||||
const localPlugins = getLocalWorkspacePlugins(projectsConfiguration);
|
||||
|
||||
const installedCollections = Array.from(
|
||||
new Set([
|
||||
...Object.keys(packageJson.dependencies || {}),
|
||||
...Object.keys(packageJson.devDependencies || {}),
|
||||
])
|
||||
);
|
||||
const choicesMap = new Set<string>();
|
||||
|
||||
for (const collectionName of collections) {
|
||||
const choicesMap = new Set<string>();
|
||||
for (const collectionName of installedCollections) {
|
||||
try {
|
||||
const { resolvedCollectionName, normalizedGeneratorName } =
|
||||
ws.readGenerator(collectionName, generatorName);
|
||||
@ -64,14 +70,44 @@ async function promptForCollection(
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const choices = Array.from(choicesMap);
|
||||
|
||||
const choicesFromLocalPlugins: {
|
||||
name: string;
|
||||
message: string;
|
||||
value: string;
|
||||
}[] = [];
|
||||
for (const [name] of localPlugins) {
|
||||
try {
|
||||
const { resolvedCollectionName, normalizedGeneratorName } =
|
||||
ws.readGenerator(name, generatorName);
|
||||
const value = `${resolvedCollectionName}:${normalizedGeneratorName}`;
|
||||
if (!choicesMap.has(value)) {
|
||||
choicesFromLocalPlugins.push({
|
||||
name: value,
|
||||
message: chalk.bold(value),
|
||||
value,
|
||||
});
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
if (choicesFromLocalPlugins.length) {
|
||||
choicesFromLocalPlugins[choicesFromLocalPlugins.length - 1].message += '\n';
|
||||
}
|
||||
const choices = (
|
||||
choicesFromLocalPlugins as (
|
||||
| string
|
||||
| {
|
||||
name: string;
|
||||
message: string;
|
||||
value: string;
|
||||
}
|
||||
)[]
|
||||
).concat(...choicesMap);
|
||||
if (choices.length === 1) {
|
||||
return choices[0];
|
||||
return typeof choices[0] === 'string' ? choices[0] : choices[0].value;
|
||||
} else if (!interactive && choices.length > 1) {
|
||||
throwInvalidInvocation(choices);
|
||||
throwInvalidInvocation(Array.from(choicesMap));
|
||||
} else if (interactive && choices.length > 1) {
|
||||
const noneOfTheAbove = `None of the above`;
|
||||
const noneOfTheAbove = `\nNone of the above`;
|
||||
choices.push(noneOfTheAbove);
|
||||
let { generator, customCollection } = await prompt<{
|
||||
generator: string;
|
||||
@ -81,7 +117,8 @@ async function promptForCollection(
|
||||
name: 'generator',
|
||||
message: `Which generator would you like to use?`,
|
||||
type: 'autocomplete',
|
||||
choices,
|
||||
// enquirer's typings are incorrect here... It supports (string | Choice)[], but is typed as (string[] | Choice[])
|
||||
choices: choices as string[],
|
||||
},
|
||||
{
|
||||
name: 'customCollection',
|
||||
@ -135,7 +172,8 @@ async function convertToGenerateOptions(
|
||||
generatorOptions: { [p: string]: any },
|
||||
ws: Workspaces,
|
||||
defaultCollectionName: string,
|
||||
mode: 'generate' | 'new'
|
||||
mode: 'generate' | 'new',
|
||||
projectsConfiguration?: ProjectsConfigurations
|
||||
): Promise<GenerateOptions> {
|
||||
let collectionName: string | null = null;
|
||||
let generatorName: string | null = null;
|
||||
@ -152,7 +190,8 @@ async function convertToGenerateOptions(
|
||||
const generatorString = await promptForCollection(
|
||||
generatorDescriptor,
|
||||
ws,
|
||||
interactive
|
||||
interactive,
|
||||
projectsConfiguration
|
||||
);
|
||||
const parsedGeneratorString = parseGeneratorString(generatorString);
|
||||
collectionName = parsedGeneratorString.collection;
|
||||
@ -297,7 +336,8 @@ export async function generate(cwd: string, args: { [k: string]: any }) {
|
||||
args,
|
||||
ws,
|
||||
readDefaultCollection(nxJson),
|
||||
'generate'
|
||||
'generate',
|
||||
projectsConfiguration
|
||||
);
|
||||
const { normalizedGeneratorName, schema, implementationFactory, aliases } =
|
||||
ws.readGenerator(opts.collectionName, opts.generatorName);
|
||||
|
||||
@ -9,6 +9,14 @@ import {
|
||||
listInstalledPlugins,
|
||||
listPluginCapabilities,
|
||||
} from '../utils/plugins';
|
||||
import {
|
||||
getLocalWorkspacePlugins,
|
||||
listLocalWorkspacePlugins,
|
||||
} from '../utils/plugins/local-plugins';
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
readProjectsConfigurationFromProjectGraph,
|
||||
} from '../project-graph/project-graph';
|
||||
|
||||
export interface ListArgs {
|
||||
/** The name of an installed plugin to query */
|
||||
@ -36,12 +44,18 @@ export async function listHandler(args: ListArgs): Promise<void> {
|
||||
|
||||
return [];
|
||||
});
|
||||
const projectGraph = await createProjectGraphAsync();
|
||||
|
||||
const localPlugins = getLocalWorkspacePlugins(
|
||||
readProjectsConfigurationFromProjectGraph(projectGraph)
|
||||
);
|
||||
const installedPlugins = getInstalledPluginsFromPackageJson(
|
||||
workspaceRoot,
|
||||
corePlugins,
|
||||
communityPlugins
|
||||
);
|
||||
|
||||
listLocalWorkspacePlugins(localPlugins);
|
||||
listInstalledPlugins(installedPlugins);
|
||||
listCorePlugins(installedPlugins, corePlugins);
|
||||
listCommunityPlugins(installedPlugins, communityPlugins);
|
||||
|
||||
@ -8,6 +8,11 @@ import {
|
||||
} from '../utils/package-manager';
|
||||
import { readJsonFile } from '../utils/fileutils';
|
||||
import { PackageJson, readModulePackageJson } from '../utils/package-json';
|
||||
import { getLocalWorkspacePlugins } from '../utils/plugins/local-plugins';
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
readProjectsConfigurationFromProjectGraph,
|
||||
} from '../project-graph/project-graph';
|
||||
|
||||
export const packagesWeCareAbout = [
|
||||
'nx',
|
||||
@ -49,7 +54,7 @@ export const patternsWeIgnoreInCommunityReport: Array<string | RegExp> = [
|
||||
* Must be run within an Nx workspace
|
||||
*
|
||||
*/
|
||||
export function reportHandler() {
|
||||
export async function reportHandler() {
|
||||
const pm = detectPackageManager();
|
||||
const pmVersion = getPackageManagerVersion(pm);
|
||||
|
||||
@ -66,6 +71,22 @@ export function reportHandler() {
|
||||
|
||||
bodyLines.push('---------------------------------------');
|
||||
|
||||
try {
|
||||
const projectGraph = await createProjectGraphAsync();
|
||||
bodyLines.push('Local workspace plugins:');
|
||||
const plugins = getLocalWorkspacePlugins(
|
||||
readProjectsConfigurationFromProjectGraph(projectGraph)
|
||||
).keys();
|
||||
for (const plugin of plugins) {
|
||||
bodyLines.push(`\t ${chalk.green(plugin)}`);
|
||||
}
|
||||
bodyLines.push(...plugins);
|
||||
} catch {
|
||||
bodyLines.push('Unable to construct project graph');
|
||||
}
|
||||
|
||||
bodyLines.push('---------------------------------------');
|
||||
|
||||
const communityPlugins = findInstalledCommunityPlugins();
|
||||
bodyLines.push('Community plugins:');
|
||||
communityPlugins.forEach((p) => {
|
||||
|
||||
@ -6,7 +6,11 @@ import { Workspaces } from '../config/workspaces';
|
||||
|
||||
import { workspaceRoot } from './workspace-root';
|
||||
import { readJsonFile } from '../utils/fileutils';
|
||||
import { PackageJson, readModulePackageJson } from './package-json';
|
||||
import {
|
||||
PackageJson,
|
||||
readModulePackageJson,
|
||||
readModulePackageJsonWithoutFallbacks,
|
||||
} from './package-json';
|
||||
import { registerTsProject } from './register';
|
||||
import {
|
||||
ProjectConfiguration,
|
||||
@ -118,7 +122,7 @@ export function readPluginPackageJson(
|
||||
json: PackageJson;
|
||||
} {
|
||||
try {
|
||||
const result = readModulePackageJson(pluginName, paths);
|
||||
const result = readModulePackageJsonWithoutFallbacks(pluginName, paths);
|
||||
return {
|
||||
json: result.packageJson,
|
||||
path: result.path,
|
||||
|
||||
@ -106,6 +106,41 @@ export function buildTargetFromScript(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses `require.resolve` to read the package.json for a module.
|
||||
*
|
||||
* This will fail if the module doesn't export package.json
|
||||
*
|
||||
* @returns package json contents and path
|
||||
*/
|
||||
export function readModulePackageJsonWithoutFallbacks(
|
||||
moduleSpecifier: string,
|
||||
requirePaths = [workspaceRoot]
|
||||
): {
|
||||
packageJson: PackageJson;
|
||||
path: string;
|
||||
} {
|
||||
const packageJsonPath: string = require.resolve(
|
||||
`${moduleSpecifier}/package.json`,
|
||||
{
|
||||
paths: requirePaths,
|
||||
}
|
||||
);
|
||||
const packageJson: PackageJson = readJsonFile(packageJsonPath);
|
||||
|
||||
return {
|
||||
path: packageJsonPath,
|
||||
packageJson,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the package.json file for a specified module.
|
||||
*
|
||||
* Includes a fallback that accounts for modules that don't export package.json
|
||||
*
|
||||
* @returns package json contents and path
|
||||
*/
|
||||
export function readModulePackageJson(
|
||||
moduleSpecifier: string,
|
||||
requirePaths = [workspaceRoot]
|
||||
@ -117,14 +152,13 @@ export function readModulePackageJson(
|
||||
let packageJson: PackageJson;
|
||||
|
||||
try {
|
||||
packageJsonPath = require.resolve(`${moduleSpecifier}/package.json`, {
|
||||
paths: requirePaths,
|
||||
});
|
||||
packageJson = readJsonFile(packageJsonPath);
|
||||
({ path: packageJsonPath, packageJson } =
|
||||
readModulePackageJsonWithoutFallbacks(moduleSpecifier, requirePaths));
|
||||
} catch {
|
||||
const entryPoint = require.resolve(moduleSpecifier, {
|
||||
paths: requirePaths,
|
||||
});
|
||||
|
||||
let moduleRootPath = dirname(entryPoint);
|
||||
packageJsonPath = join(moduleRootPath, 'package.json');
|
||||
|
||||
|
||||
70
packages/nx/src/utils/plugins/local-plugins.ts
Normal file
70
packages/nx/src/utils/plugins/local-plugins.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import * as chalk from 'chalk';
|
||||
import { output } from '../output';
|
||||
import type { CommunityPlugin, CorePlugin, PluginCapabilities } from './models';
|
||||
import { getPluginCapabilities } from './plugin-capabilities';
|
||||
import { hasElements } from './shared';
|
||||
import { readJsonFile } from '../fileutils';
|
||||
import { PackageJson, readModulePackageJson } from '../package-json';
|
||||
import { ProjectsConfigurations } from 'nx/src/config/workspace-json-project-json';
|
||||
import { join } from 'path';
|
||||
import { workspaceRoot } from '../workspace-root';
|
||||
import { existsSync } from 'fs';
|
||||
import { ExecutorsJson, GeneratorsJson } from 'nx/src/config/misc-interfaces';
|
||||
|
||||
export function getLocalWorkspacePlugins(
|
||||
projectsConfiguration: ProjectsConfigurations
|
||||
): Map<string, PluginCapabilities> {
|
||||
const plugins: Map<string, PluginCapabilities> = new Map();
|
||||
for (const project of Object.values(projectsConfiguration.projects)) {
|
||||
const packageJsonPath = join(workspaceRoot, project.root, 'package.json');
|
||||
if (existsSync(packageJsonPath)) {
|
||||
const packageJson: PackageJson = readJsonFile(packageJsonPath);
|
||||
const capabilities: Partial<PluginCapabilities> = {};
|
||||
const generatorsPath = packageJson.generators ?? packageJson.schematics;
|
||||
const executorsPath = packageJson.executors ?? packageJson.builders;
|
||||
if (generatorsPath) {
|
||||
const file = readJsonFile<GeneratorsJson>(
|
||||
join(workspaceRoot, project.root, generatorsPath)
|
||||
);
|
||||
capabilities.generators = file.generators ?? file.schematics;
|
||||
}
|
||||
if (executorsPath) {
|
||||
const file = readJsonFile<ExecutorsJson>(
|
||||
join(workspaceRoot, project.root, executorsPath)
|
||||
);
|
||||
capabilities.executors = file.executors ?? file.builders;
|
||||
}
|
||||
if (capabilities.executors || capabilities.generators) {
|
||||
plugins.set(packageJson.name, {
|
||||
executors: capabilities.executors ?? {},
|
||||
generators: capabilities.generators ?? {},
|
||||
name: packageJson.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
export function listLocalWorkspacePlugins(
|
||||
installedPlugins: Map<string, PluginCapabilities>
|
||||
) {
|
||||
const bodyLines: string[] = [];
|
||||
|
||||
for (const [, p] of installedPlugins) {
|
||||
const capabilities = [];
|
||||
if (hasElements(p.executors)) {
|
||||
capabilities.push('executors');
|
||||
}
|
||||
if (hasElements(p.generators)) {
|
||||
capabilities.push('generators');
|
||||
}
|
||||
bodyLines.push(`${chalk.bold(p.name)} (${capabilities.join()})`);
|
||||
}
|
||||
|
||||
output.log({
|
||||
title: `Local workspace plugins:`,
|
||||
bodyLines: bodyLines,
|
||||
});
|
||||
}
|
||||
@ -6,7 +6,7 @@ import type { PluginCapabilities } from './models';
|
||||
import { hasElements } from './shared';
|
||||
import { readJsonFile } from '../fileutils';
|
||||
import { getPackageManagerCommand } from '../package-manager';
|
||||
import { readModulePackageJson } from '../package-json';
|
||||
import { readPluginPackageJson } from '../nx-plugin';
|
||||
|
||||
function tryGetCollection<T extends object>(
|
||||
packageJsonPath: string,
|
||||
@ -30,8 +30,8 @@ export function getPluginCapabilities(
|
||||
pluginName: string
|
||||
): PluginCapabilities | null {
|
||||
try {
|
||||
const { packageJson, path: packageJsonPath } =
|
||||
readModulePackageJson(pluginName);
|
||||
const { json: packageJson, path: packageJsonPath } =
|
||||
readPluginPackageJson(pluginName);
|
||||
return {
|
||||
name: pluginName,
|
||||
generators:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user