diff --git a/packages/nx/src/adapter/ngcli-adapter.ts b/packages/nx/src/adapter/ngcli-adapter.ts index e0332badb6..d88c92b88c 100644 --- a/packages/nx/src/adapter/ngcli-adapter.ts +++ b/packages/nx/src/adapter/ngcli-adapter.ts @@ -48,7 +48,6 @@ import { toOldFormat, } from './angular-json'; import { normalizeExecutorSchema } from '../command-line/run/executor-utils'; -import { Workspaces } from '../config/workspaces'; import { CustomHasher, Executor, @@ -1033,8 +1032,6 @@ async function getWrappedWorkspaceNodeModulesArchitectHost( } = await import('@angular-devkit/architect/node'); class WrappedWorkspaceNodeModulesArchitectHost extends AngularWorkspaceNodeModulesArchitectHost { - private workspaces = new Workspaces(this.root); - constructor(private workspace, private root) { super(workspace, root); } @@ -1064,7 +1061,7 @@ async function getWrappedWorkspaceNodeModulesArchitectHost( const { json: packageJson, path: packageJsonPath } = readPluginPackageJson( nodeModule, - this.workspaces['resolvePaths'].bind(this.workspaces)() + this.root ? [this.root, __dirname] : [__dirname] ); const executorsFile = packageJson.executors ?? packageJson.builders; diff --git a/packages/nx/src/command-line/generate/generate.ts b/packages/nx/src/command-line/generate/generate.ts index 168f0df05a..8804f0b373 100644 --- a/packages/nx/src/command-line/generate/generate.ts +++ b/packages/nx/src/command-line/generate/generate.ts @@ -23,6 +23,7 @@ import { NxJsonConfiguration } from '../../config/nx-json'; import { findInstalledPlugins } from '../../utils/plugins/installed-plugins'; import type { Arguments } from 'yargs'; import { output } from '../../utils/output'; +import { getGeneratorInformation } from './generator-utils'; export interface GenerateOptions { collectionName: string; @@ -69,7 +70,7 @@ async function promptForCollection( resolvedCollectionName, normalizedGeneratorName, generatorConfiguration: { ['x-deprecated']: deprecated, hidden }, - } = ws.readGenerator(collectionName, generatorName); + } = getGeneratorInformation(collectionName, generatorName, workspaceRoot); if (hidden) { continue; } @@ -94,7 +95,7 @@ async function promptForCollection( resolvedCollectionName, normalizedGeneratorName, generatorConfiguration: { ['x-deprecated']: deprecated, hidden }, - } = ws.readGenerator(name, generatorName); + } = getGeneratorInformation(name, generatorName, workspaceRoot); if (hidden) { continue; } @@ -156,7 +157,7 @@ async function promptForCollection( return true; } try { - ws.readGenerator(value, generatorName); + getGeneratorInformation(value, generatorName, workspaceRoot); return true; } catch { logger.error(`\nCould not find ${value}:${generatorName}`); @@ -325,7 +326,11 @@ export async function generate(cwd: string, args: { [k: string]: any }) { ['x-deprecated']: deprecated, ['x-use-standalone-layout']: isStandalonePreset, }, - } = ws.readGenerator(opts.collectionName, opts.generatorName); + } = getGeneratorInformation( + opts.collectionName, + opts.generatorName, + workspaceRoot + ); if (deprecated) { logger.warn( @@ -363,7 +368,13 @@ export async function generate(cwd: string, args: { [k: string]: any }) { verbose ); - if (ws.isNxGenerator(opts.collectionName, normalizedGeneratorName)) { + if ( + getGeneratorInformation( + opts.collectionName, + normalizedGeneratorName, + workspaceRoot + ).isNxGenerator + ) { const host = new FsTree( workspaceRoot, verbose, diff --git a/packages/nx/src/command-line/generate/generator-utils.ts b/packages/nx/src/command-line/generate/generator-utils.ts new file mode 100644 index 0000000000..5712cae6a5 --- /dev/null +++ b/packages/nx/src/command-line/generate/generator-utils.ts @@ -0,0 +1,147 @@ +import { dirname, join } from 'path'; +import { + Generator, + GeneratorsJson, + GeneratorsJsonEntry, +} from '../../config/misc-interfaces'; +import { + getImplementationFactory, + resolveSchema, +} from '../../config/schema-utils'; +import { readJsonFile } from '../../utils/fileutils'; +import { readPluginPackageJson } from '../../utils/nx-plugin'; +import { getNxRequirePaths } from '../../utils/installation-directory'; + +export function getGeneratorInformation( + collectionName: string, + generatorName: string, + root: string | null +): { + resolvedCollectionName: string; + normalizedGeneratorName: string; + schema: any; + implementationFactory: () => Generator; + isNgCompat: boolean; + isNxGenerator: boolean; + generatorConfiguration: GeneratorsJsonEntry; +} { + try { + const { + generatorsFilePath, + generatorsJson, + resolvedCollectionName, + normalizedGeneratorName, + } = readGeneratorsJson(collectionName, generatorName, root); + const generatorsDir = dirname(generatorsFilePath); + const generatorConfig = + generatorsJson.generators?.[normalizedGeneratorName] || + generatorsJson.schematics?.[normalizedGeneratorName]; + const isNgCompat = !generatorsJson.generators?.[normalizedGeneratorName]; + const schemaPath = resolveSchema(generatorConfig.schema, generatorsDir); + const schema = readJsonFile(schemaPath); + if (!schema.properties || typeof schema.properties !== 'object') { + schema.properties = {}; + } + generatorConfig.implementation = + generatorConfig.implementation || generatorConfig.factory; + const implementationFactory = getImplementationFactory( + generatorConfig.implementation, + generatorsDir + ); + const normalizedGeneratorConfiguration: GeneratorsJsonEntry = { + ...generatorConfig, + aliases: generatorConfig.aliases ?? [], + hidden: !!generatorConfig.hidden, + }; + return { + resolvedCollectionName, + normalizedGeneratorName, + schema, + implementationFactory, + isNgCompat, + isNxGenerator: !isNgCompat, + generatorConfiguration: normalizedGeneratorConfiguration, + }; + } catch (e) { + throw new Error( + `Unable to resolve ${collectionName}:${generatorName}.\n${e.message}` + ); + } +} + +export function readGeneratorsJson( + collectionName: string, + generator: string, + root: string | null +): { + generatorsFilePath: string; + generatorsJson: GeneratorsJson; + normalizedGeneratorName: string; + resolvedCollectionName: string; +} { + let generatorsFilePath; + if (collectionName.endsWith('.json')) { + generatorsFilePath = require.resolve(collectionName, { + paths: root ? [root, __dirname] : [__dirname], + }); + } else { + const { json: packageJson, path: packageJsonPath } = readPluginPackageJson( + collectionName, + root ? [root, __dirname] : [__dirname] + ); + const generatorsFile = packageJson.generators ?? packageJson.schematics; + + if (!generatorsFile) { + throw new Error( + `The "${collectionName}" package does not support Nx generators.` + ); + } + + generatorsFilePath = require.resolve( + join(dirname(packageJsonPath), generatorsFile) + ); + } + const generatorsJson = readJsonFile(generatorsFilePath); + + let normalizedGeneratorName = + findFullGeneratorName(generator, generatorsJson.generators) || + findFullGeneratorName(generator, generatorsJson.schematics); + + if (!normalizedGeneratorName) { + for (let parent of generatorsJson.extends || []) { + try { + return readGeneratorsJson(parent, generator, root); + } catch (e) {} + } + + throw new Error( + `Cannot find generator '${generator}' in ${generatorsFilePath}.` + ); + } + return { + generatorsFilePath, + generatorsJson, + normalizedGeneratorName, + resolvedCollectionName: collectionName, + }; +} + +function findFullGeneratorName( + name: string, + generators: { + [name: string]: { aliases?: string[] }; + } +) { + if (generators) { + for (let [key, data] of Object.entries<{ aliases?: string[] }>( + generators + )) { + if ( + key === name || + (data.aliases && (data.aliases as string[]).includes(name)) + ) { + return key; + } + } + } +} diff --git a/packages/nx/src/command-line/new/new.ts b/packages/nx/src/command-line/new/new.ts index ffcd7fc83e..ce12c1c7a4 100644 --- a/packages/nx/src/command-line/new/new.ts +++ b/packages/nx/src/command-line/new/new.ts @@ -1,6 +1,6 @@ -import { Workspaces } from '../../config/workspaces'; import { flushChanges, FsTree } from '../../generators/tree'; import { combineOptionsForGenerator, handleErrors } from '../../utils/params'; +import { getGeneratorInformation } from '../generate/generator-utils'; function removeSpecialFlags(generatorOptions: { [p: string]: any }): void { delete generatorOptions.interactive; @@ -11,14 +11,12 @@ function removeSpecialFlags(generatorOptions: { [p: string]: any }): void { } export async function newWorkspace(cwd: string, args: { [k: string]: any }) { - const ws = new Workspaces(null); - return handleErrors( process.env.NX_VERBOSE_LOGGING === 'true' || args.verbose, async () => { const isInteractive = args.interactive; const { normalizedGeneratorName, schema, implementationFactory } = - ws.readGenerator('@nx/workspace/generators.json', 'new'); + getGeneratorInformation('@nx/workspace/generators.json', 'new', null); removeSpecialFlags(args); const combinedOpts = await combineOptionsForGenerator( args, diff --git a/packages/nx/src/config/workspaces.ts b/packages/nx/src/config/workspaces.ts index 93ff3efca1..bcc07de1cb 100644 --- a/packages/nx/src/config/workspaces.ts +++ b/packages/nx/src/config/workspaces.ts @@ -6,11 +6,7 @@ import { performance } from 'perf_hooks'; import { workspaceRoot } from '../utils/workspace-root'; import { readJsonFile, readYamlFile } from '../utils/fileutils'; import { logger, NX_PREFIX } from '../utils/logger'; -import { - loadNxPlugins, - loadNxPluginsSync, - readPluginPackageJson, -} from '../utils/nx-plugin'; +import { loadNxPlugins, loadNxPluginsSync } from '../utils/nx-plugin'; import type { NxJsonConfiguration, TargetDefaults } from './nx-json'; import { @@ -18,11 +14,6 @@ import { ProjectsConfigurations, TargetConfiguration, } from './workspace-json-project-json'; -import { - Generator, - GeneratorsJson, - GeneratorsJsonEntry, -} from './misc-interfaces'; import { PackageJson } from '../utils/package-json'; import { output } from '../utils/output'; import { joinPathFragments } from '../utils/path'; @@ -36,7 +27,6 @@ import { findProjectForPath, normalizeProjectRoot, } from '../project-graph/utils/find-project-for-path'; -import { getImplementationFactory, resolveSchema } from './schema-utils'; export class Workspaces { private cachedProjectsConfig: ProjectsConfigurations; @@ -164,69 +154,6 @@ export class Workspaces { return projects; } - isNxGenerator(collectionName: string, generatorName: string) { - return !this.readGenerator(collectionName, generatorName).isNgCompat; - } - - readGenerator( - collectionName: string, - generatorName: string - ): { - resolvedCollectionName: string; - normalizedGeneratorName: string; - schema: any; - implementationFactory: () => Generator; - isNgCompat: boolean; - /** - * @deprecated(v16): This will be removed in v16, use generatorConfiguration.aliases - */ - aliases: string[]; - generatorConfiguration: GeneratorsJsonEntry; - } { - try { - const { - generatorsFilePath, - generatorsJson, - resolvedCollectionName, - normalizedGeneratorName, - } = this.readGeneratorsJson(collectionName, generatorName); - const generatorsDir = path.dirname(generatorsFilePath); - const generatorConfig = - generatorsJson.generators?.[normalizedGeneratorName] || - generatorsJson.schematics?.[normalizedGeneratorName]; - const isNgCompat = !generatorsJson.generators?.[normalizedGeneratorName]; - const schemaPath = resolveSchema(generatorConfig.schema, generatorsDir); - const schema = readJsonFile(schemaPath); - if (!schema.properties || typeof schema.properties !== 'object') { - schema.properties = {}; - } - generatorConfig.implementation = - generatorConfig.implementation || generatorConfig.factory; - const implementationFactory = getImplementationFactory( - generatorConfig.implementation, - generatorsDir - ); - const normalizedGeneratorConfiguration: GeneratorsJsonEntry = { - ...generatorConfig, - aliases: generatorConfig.aliases ?? [], - hidden: !!generatorConfig.hidden, - }; - return { - resolvedCollectionName, - normalizedGeneratorName, - schema, - implementationFactory, - isNgCompat, - aliases: generatorConfig.aliases || [], - generatorConfiguration: normalizedGeneratorConfiguration, - }; - } catch (e) { - throw new Error( - `Unable to resolve ${collectionName}:${generatorName}.\n${e.message}` - ); - } - } - hasNxJson(): boolean { const nxJson = path.join(this.root, 'nx.json'); return existsSync(nxJson); @@ -262,64 +189,6 @@ export class Workspaces { } } } - - private readGeneratorsJson( - collectionName: string, - generator: string - ): { - generatorsFilePath: string; - generatorsJson: GeneratorsJson; - normalizedGeneratorName: string; - resolvedCollectionName: string; - } { - let generatorsFilePath; - if (collectionName.endsWith('.json')) { - generatorsFilePath = require.resolve(collectionName, { - paths: this.resolvePaths(), - }); - } else { - const { json: packageJson, path: packageJsonPath } = - readPluginPackageJson(collectionName, this.resolvePaths()); - const generatorsFile = packageJson.generators ?? packageJson.schematics; - - if (!generatorsFile) { - throw new Error( - `The "${collectionName}" package does not support Nx generators.` - ); - } - - generatorsFilePath = require.resolve( - path.join(path.dirname(packageJsonPath), generatorsFile) - ); - } - const generatorsJson = readJsonFile(generatorsFilePath); - - let normalizedGeneratorName = - findFullGeneratorName(generator, generatorsJson.generators) || - findFullGeneratorName(generator, generatorsJson.schematics); - - if (!normalizedGeneratorName) { - for (let parent of generatorsJson.extends || []) { - try { - return this.readGeneratorsJson(parent, generator); - } catch (e) {} - } - - throw new Error( - `Cannot find generator '${generator}' in ${generatorsFilePath}.` - ); - } - return { - generatorsFilePath, - generatorsJson, - normalizedGeneratorName, - resolvedCollectionName: collectionName, - }; - } - - private resolvePaths() { - return this.root ? [this.root, __dirname] : [__dirname]; - } } function findMatchingProjectInCwd( @@ -335,26 +204,6 @@ function findMatchingProjectInCwd( return matchingProject; } -function findFullGeneratorName( - name: string, - generators: { - [name: string]: { aliases?: string[] }; - } -) { - if (generators) { - for (let [key, data] of Object.entries<{ aliases?: string[] }>( - generators - )) { - if ( - key === name || - (data.aliases && (data.aliases as string[]).includes(name)) - ) { - return key; - } - } - } -} - /** * Pulled from toFileName in names from @nx/devkit. * Todo: Should refactor, not duplicate.