From 1d7465b02e35967590c9b7afb2cee785f38d4238 Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Mon, 16 Dec 2024 11:38:22 -0800 Subject: [PATCH] feat(core): not exit when one plugin installation failed (#28684) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Current Behavior it tries to install all plugins at once. if failed, it will stop the remaining plugins from being installed. ## Expected Behavior it will continue if one plugin failed. success message: Screenshot 2024-12-11 at 11 36 14 AM failed message: Screenshot 2024-12-12 at 2 58 17 PM ## Related Issue(s) Fixes # --- e2e/eslint/src/linter.test.ts | 2 +- packages/devkit/src/utils/add-plugin.ts | 15 +- .../src/utils/typescript/ts-solution-setup.ts | 4 +- packages/nx/src/command-line/add/add.ts | 96 +++---- packages/nx/src/command-line/import/import.ts | 123 ++++++--- .../import/utils/needs-install.ts | 44 --- .../import/utils/prepare-source-repo.ts | 3 +- .../command-line/init/configure-plugins.ts | 253 ++++++++++++++++++ packages/nx/src/command-line/init/init-v2.ts | 52 ++-- 9 files changed, 402 insertions(+), 190 deletions(-) delete mode 100644 packages/nx/src/command-line/import/utils/needs-install.ts create mode 100644 packages/nx/src/command-line/init/configure-plugins.ts diff --git a/e2e/eslint/src/linter.test.ts b/e2e/eslint/src/linter.test.ts index 2a3c1faa1e580..859dbe989547e 100644 --- a/e2e/eslint/src/linter.test.ts +++ b/e2e/eslint/src/linter.test.ts @@ -772,7 +772,7 @@ describe('Linter', () => { const mylib = uniq('mylib'); runCLI( - `generate @nx/node:app --name=${myapp} --linter=eslint --directory="." --e2eTestRunner=jest --no-interactive` + `generate @nx/node:app --name=${myapp} --linter=eslint --directory="." --unitTestRunner=jest --e2eTestRunner=jest --no-interactive` ); runCLI('reset', { env: { CI: 'false' } }); verifySuccessfulStandaloneSetup(myapp); diff --git a/packages/devkit/src/utils/add-plugin.ts b/packages/devkit/src/utils/add-plugin.ts index 09a3d772cd90d..e8fe33082f99d 100644 --- a/packages/devkit/src/utils/add-plugin.ts +++ b/packages/devkit/src/utils/add-plugin.ts @@ -15,8 +15,9 @@ import { writeJson, } from 'nx/src/devkit-exports'; import { + isProjectConfigurationsError, + isProjectsWithNoNameError, LoadedNxPlugin, - ProjectConfigurationsError, retrieveProjectConfigurations, } from 'nx/src/devkit-internals'; @@ -130,8 +131,12 @@ async function _addPluginInternal( ); } catch (e) { // Errors are okay for this because we're only running 1 plugin - if (e instanceof ProjectConfigurationsError) { + if (isProjectConfigurationsError(e)) { projConfigs = e.partialProjectConfigurationsResult; + // ignore errors from projects with no name + if (!e.errors.every(isProjectsWithNoNameError)) { + throw e; + } } else { throw e; } @@ -171,8 +176,12 @@ async function _addPluginInternal( ); } catch (e) { // Errors are okay for this because we're only running 1 plugin - if (e instanceof ProjectConfigurationsError) { + if (isProjectConfigurationsError(e)) { projConfigs = e.partialProjectConfigurationsResult; + // ignore errors from projects with no name + if (!e.errors.every(isProjectsWithNoNameError)) { + throw e; + } } else { throw e; } diff --git a/packages/js/src/utils/typescript/ts-solution-setup.ts b/packages/js/src/utils/typescript/ts-solution-setup.ts index 4a396cfeaa3da..92b49b153981d 100644 --- a/packages/js/src/utils/typescript/ts-solution-setup.ts +++ b/packages/js/src/utils/typescript/ts-solution-setup.ts @@ -98,7 +98,9 @@ export function assertNotUsingTsSolutionSetup( ], }); - process.exit(1); + throw new Error( + `The ${artifactString} doesn't yet support the existing TypeScript setup. See the error above.` + ); } export function findRuntimeTsConfigName( diff --git a/packages/nx/src/command-line/add/add.ts b/packages/nx/src/command-line/add/add.ts index b6962ee6f1f74..0f6f4d1172898 100644 --- a/packages/nx/src/command-line/add/add.ts +++ b/packages/nx/src/command-line/add/add.ts @@ -1,10 +1,9 @@ import { exec } from 'child_process'; import { existsSync } from 'fs'; import * as ora from 'ora'; -import { isAngularPluginInstalled } from '../../adapter/angular-json'; -import type { GeneratorsJsonEntry } from '../../config/misc-interfaces'; +import * as yargsParser from 'yargs-parser'; import { readNxJson, type NxJsonConfiguration } from '../../config/nx-json'; -import { runNxAsync, runNxSync } from '../../utils/child-process'; +import { runNxAsync } from '../../utils/child-process'; import { writeJsonFile } from '../../utils/fileutils'; import { logger } from '../../utils/logger'; import { output } from '../../utils/output'; @@ -14,12 +13,15 @@ import { getPackageManagerVersion, } from '../../utils/package-manager'; import { handleErrors } from '../../utils/handle-errors'; -import { getPluginCapabilities } from '../../utils/plugins'; import { nxVersion } from '../../utils/versions'; import { workspaceRoot } from '../../utils/workspace-root'; import type { AddOptions } from './command-object'; import { normalizeVersionForNxJson } from '../init/implementation/dot-nx/add-nx-scripts'; import { gte } from 'semver'; +import { + installPlugin, + getFailedToInstallPluginErrorMessages, +} from '../init/configure-plugins'; export function addHandler(options: AddOptions): Promise { return handleErrors(options.verbose, async () => { @@ -109,54 +111,47 @@ async function initializePlugin( options: AddOptions, nxJson: NxJsonConfiguration ): Promise { - const capabilities = await getPluginCapabilities(workspaceRoot, pkgName, {}); - const generators = capabilities?.generators; - if (!generators) { - output.log({ - title: `No generators found in ${pkgName}. Skipping initialization.`, - }); - return; - } + const parsedCommandArgs: { [key: string]: any } = yargsParser( + options.__overrides_unparsed__, + { + configuration: { + 'parse-numbers': false, + 'parse-positional-numbers': false, + 'dot-notation': false, + 'camel-case-expansion': false, + }, + } + ); - const initGenerator = findInitGenerator(generators); - if (!initGenerator) { - output.log({ - title: `No "init" generator found in ${pkgName}. Skipping initialization.`, - }); - return; + if (coreNxPluginVersions.has(pkgName)) { + parsedCommandArgs.keepExistingVersions = true; + + if ( + options.updatePackageScripts || + (options.updatePackageScripts === undefined && + nxJson.useInferencePlugins !== false && + process.env.NX_ADD_PLUGINS !== 'false') + ) { + parsedCommandArgs.updatePackageScripts = true; + } } const spinner = ora(`Initializing ${pkgName}...`); spinner.start(); try { - const args = []; - if (coreNxPluginVersions.has(pkgName)) { - args.push(`--keepExistingVersions`); - - if ( - options.updatePackageScripts || - (options.updatePackageScripts === undefined && - nxJson.useInferencePlugins !== false && - process.env.NX_ADD_PLUGINS !== 'false') - ) { - args.push(`--updatePackageScripts`); - } - } - - if (options.__overrides_unparsed__.length) { - args.push(...options.__overrides_unparsed__); - } - - runNxSync(`g ${pkgName}:${initGenerator} ${args.join(' ')}`, { - stdio: [0, 1, 2], - }); + await installPlugin( + pkgName, + workspaceRoot, + options.verbose, + parsedCommandArgs + ); } catch (e) { spinner.fail(); output.addNewline(); - logger.error(e); output.error({ - title: `Failed to initialize ${pkgName}. Please check the error above for more details.`, + title: `Failed to initialize ${pkgName}`, + bodyLines: getFailedToInstallPluginErrorMessages(e), }); process.exit(1); } @@ -164,25 +159,6 @@ async function initializePlugin( spinner.succeed(); } -function findInitGenerator( - generators: Record -): string | undefined { - if (generators['init']) { - return 'init'; - } - - const angularPluginInstalled = isAngularPluginInstalled(); - if (angularPluginInstalled && generators['ng-add']) { - return 'ng-add'; - } - - return Object.keys(generators).find( - (name) => - generators[name].aliases?.includes('init') || - (angularPluginInstalled && generators[name].aliases?.includes('ng-add')) - ); -} - function parsePackageSpecifier( packageSpecifier: string ): [pkgName: string, version: string] { diff --git a/packages/nx/src/command-line/import/import.ts b/packages/nx/src/command-line/import/import.ts index fb46b0996cdc0..99386c0a24a61 100644 --- a/packages/nx/src/command-line/import/import.ts +++ b/packages/nx/src/command-line/import/import.ts @@ -7,7 +7,7 @@ import { tmpdir } from 'tmp'; import { prompt } from 'enquirer'; import { output } from '../../utils/output'; import * as createSpinner from 'ora'; -import { detectPlugins, installPlugins } from '../init/init-v2'; +import { detectPlugins } from '../init/init-v2'; import { readNxJson } from '../../config/nx-json'; import { workspaceRoot } from '../../utils/workspace-root'; import { @@ -24,11 +24,11 @@ import { runInstall } from '../init/implementation/utils'; import { getBaseRef } from '../../utils/command-line-utils'; import { prepareSourceRepo } from './utils/prepare-source-repo'; import { mergeRemoteSource } from './utils/merge-remote-source'; -import { - getPackagesInPackageManagerWorkspace, - needsInstall, -} from './utils/needs-install'; import { minimatch } from 'minimatch'; +import { + configurePlugins, + runPackageManagerInstallPlugins, +} from '../init/configure-plugins'; const importRemoteName = '__tmp_nx_import__'; @@ -60,7 +60,7 @@ export interface ImportOptions { export async function importHandler(options: ImportOptions) { process.env.NX_RUNNING_NX_IMPORT = 'true'; - let { sourceRepository, ref, source, destination } = options; + let { sourceRepository, ref, source, destination, verbose } = options; const destinationGitClient = new GitRepository(process.cwd()); if (await destinationGitClient.hasUncommittedChanges()) { @@ -219,11 +219,6 @@ export async function importHandler(options: ImportOptions) { } const packageManager = detectPackageManager(workspaceRoot); - - const originalPackageWorkspaces = await getPackagesInPackageManagerWorkspace( - packageManager - ); - const sourceIsNxWorkspace = existsSync(join(sourceGitClient.root, 'nx.json')); const relativeDestination = relative( @@ -287,42 +282,30 @@ export async function importHandler(options: ImportOptions) { destinationGitClient ); - // If install fails, we should continue since the errors could be resolved later. - let installFailed = false; - if (plugins.length > 0) { - try { - output.log({ title: 'Installing Plugins' }); - installPlugins(workspaceRoot, plugins, pmc, updatePackageScripts); - - await destinationGitClient.amendCommit(); - } catch (e) { - installFailed = true; - output.error({ - title: `Install failed: ${e.message || 'Unknown error'}`, - bodyLines: [e.stack], - }); - } - } else if (await needsInstall(packageManager, originalPackageWorkspaces)) { - try { - output.log({ - title: 'Installing dependencies for imported code', - }); - - runInstall(workspaceRoot, getPackageManagerCommand(packageManager)); - - await destinationGitClient.amendCommit(); - } catch (e) { - installFailed = true; - output.error({ - title: `Install failed: ${e.message || 'Unknown error'}`, - bodyLines: [e.stack], - }); + let installed = await runInstallDestinationRepo( + packageManager, + destinationGitClient + ); + + if (installed && plugins.length > 0) { + installed = await runPluginsInstall(plugins, pmc, destinationGitClient); + if (installed) { + const { succeededPlugins } = await configurePlugins( + plugins, + updatePackageScripts, + pmc, + workspaceRoot, + verbose + ); + if (succeededPlugins.length > 0) { + await destinationGitClient.amendCommit(); + } } } console.log(await destinationGitClient.showStat()); - if (installFailed) { + if (installed === false) { const pmc = getPackageManagerCommand(packageManager); output.warn({ title: `The import was successful, but the install failed`, @@ -397,6 +380,62 @@ async function createTemporaryRemote( } /** + * Run install for the imported code and plugins + * @returns true if the install failed + */ +async function runInstallDestinationRepo( + packageManager: PackageManager, + destinationGitClient: GitRepository +): Promise { + let installed = true; + try { + output.log({ + title: 'Installing dependencies for imported code', + }); + runInstall(workspaceRoot, getPackageManagerCommand(packageManager)); + await destinationGitClient.amendCommit(); + } catch (e) { + installed = false; + output.error({ + title: `Install failed: ${e.message || 'Unknown error'}`, + bodyLines: [e.stack], + }); + } + return installed; +} + +async function runPluginsInstall( + plugins: string[], + pmc: PackageManagerCommands, + destinationGitClient: GitRepository +) { + let installed = true; + output.log({ title: 'Installing Plugins' }); + try { + runPackageManagerInstallPlugins(workspaceRoot, pmc, plugins); + await destinationGitClient.amendCommit(); + } catch (e) { + installed = false; + output.error({ + title: `Install failed: ${e.message || 'Unknown error'}`, + bodyLines: [ + 'The following plugins were not installed:', + ...plugins.map((p) => `- ${chalk.bold(p)}`), + e.stack, + ], + }); + output.error({ + title: `To install the plugins manually`, + bodyLines: [ + 'You may need to run commands to install the plugins:', + ...plugins.map((p) => `- ${chalk.bold(pmc.exec + ' nx add ' + p)}`), + ], + }); + } + return installed; +} + +/* * If the user imports a project that isn't in the workspaces entry, we should add that path to the workspaces entry. */ async function handleMissingWorkspacesEntry( diff --git a/packages/nx/src/command-line/import/utils/needs-install.ts b/packages/nx/src/command-line/import/utils/needs-install.ts deleted file mode 100644 index e65f3eec6c644..0000000000000 --- a/packages/nx/src/command-line/import/utils/needs-install.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { - isWorkspacesEnabled, - PackageManager, -} from '../../../utils/package-manager'; -import { workspaceRoot } from '../../../utils/workspace-root'; -import { getGlobPatternsFromPackageManagerWorkspaces } from '../../../plugins/package-json'; -import { globWithWorkspaceContext } from '../../../utils/workspace-context'; - -export async function getPackagesInPackageManagerWorkspace( - packageManager: PackageManager -) { - if (!isWorkspacesEnabled(packageManager, workspaceRoot)) { - return new Set(); - } - const patterns = getGlobPatternsFromPackageManagerWorkspaces(workspaceRoot); - return new Set(await globWithWorkspaceContext(workspaceRoot, patterns)); -} - -export async function needsInstall( - packageManager: PackageManager, - originalPackagesInPackageManagerWorkspaces: Set -) { - if (!isWorkspacesEnabled(packageManager, workspaceRoot)) { - return false; - } - - const updatedPackagesInPackageManagerWorkspaces = - await getPackagesInPackageManagerWorkspace(packageManager); - - if ( - updatedPackagesInPackageManagerWorkspaces.size !== - originalPackagesInPackageManagerWorkspaces.size - ) { - return true; - } - - for (const pkg of updatedPackagesInPackageManagerWorkspaces) { - if (!originalPackagesInPackageManagerWorkspaces.has(pkg)) { - return true; - } - } - - return false; -} diff --git a/packages/nx/src/command-line/import/utils/prepare-source-repo.ts b/packages/nx/src/command-line/import/utils/prepare-source-repo.ts index ebd9586e2efbc..37ae73d8e2d5a 100644 --- a/packages/nx/src/command-line/import/utils/prepare-source-repo.ts +++ b/packages/nx/src/command-line/import/utils/prepare-source-repo.ts @@ -1,6 +1,5 @@ import * as createSpinner from 'ora'; -import { dirname, join, relative } from 'path'; -import { mkdir, rm } from 'node:fs/promises'; +import { join, relative } from 'path'; import { GitRepository } from '../../../utils/git-utils'; export async function prepareSourceRepo( diff --git a/packages/nx/src/command-line/init/configure-plugins.ts b/packages/nx/src/command-line/init/configure-plugins.ts new file mode 100644 index 0000000000000..2c5aa1e035522 --- /dev/null +++ b/packages/nx/src/command-line/init/configure-plugins.ts @@ -0,0 +1,253 @@ +import * as createSpinner from 'ora'; +import { bold } from 'chalk'; + +import { + getPackageManagerCommand, + PackageManagerCommands, +} from '../../utils/package-manager'; +import { GitRepository } from '../../utils/git-utils'; +import { output } from '../../utils/output'; +import { flushChanges, FsTree } from '../../generators/tree'; +import { + Generator as NxGenerator, + GeneratorCallback, + GeneratorsJsonEntry, +} from '../../config/misc-interfaces'; +import { getGeneratorInformation } from '../generate/generator-utils'; +import { workspaceRoot } from '../../utils/workspace-root'; +import { addDepsToPackageJson, runInstall } from './implementation/utils'; +import { getPluginCapabilities } from '../../utils/plugins'; +import { isAngularPluginInstalled } from '../../adapter/angular-json'; +import { + isAggregateCreateNodesError, + isProjectConfigurationsError, + isProjectsWithNoNameError, +} from '../../project-graph/error-types'; + +export function runPackageManagerInstallPlugins( + repoRoot: string, + pmc: PackageManagerCommands = getPackageManagerCommand(), + plugins: string[] +) { + if (plugins.length === 0) { + return; + } + addDepsToPackageJson(repoRoot, plugins); + runInstall(repoRoot, pmc); +} + +/** + * Installs a plugin by running its init generator. It will change the file system tree passed in. + * @param plugin The name of the plugin to install + * @param repoRoot repo root + * @param verbose verbose + * @param options options passed to init generator + * @returns void + */ +export async function installPlugin( + plugin: string, + repoRoot: string = workspaceRoot, + verbose: boolean = false, + options: { [k: string]: any } +): Promise { + const host = new FsTree(repoRoot, verbose, `install ${plugin}`); + const capabilities = await getPluginCapabilities(repoRoot, plugin, {}); + const generators = capabilities?.generators; + if (!generators) { + throw new Error(`No generators found in ${plugin}.`); + } + + const initGenerator = findInitGenerator(generators); + if (!initGenerator) { + output.log({ + title: `No "init" generator found in ${plugin}. Skipping initialization.`, + }); + return; + } + const { implementationFactory } = getGeneratorInformation( + plugin, + initGenerator, + repoRoot, + {} + ); + + const implementation: NxGenerator = implementationFactory(); + const task: GeneratorCallback | void = await implementation(host, options); + flushChanges(repoRoot, host.listChanges()); + if (task) { + await task(); + } +} + +/** + * Install plugins + * Get the implementation of the plugin's init generator and run it + * @returns a list of succeeded plugins and a map of failed plugins to errors + */ +export async function installPlugins( + plugins: string[], + updatePackageScripts: boolean, + repoRoot: string = workspaceRoot, + verbose: boolean = false +): Promise<{ + succeededPlugins: string[]; + failedPlugins: { [plugin: string]: Error }; +}> { + if (plugins.length === 0) { + return { + succeededPlugins: [], + failedPlugins: {}, + }; + } + const spinner = createSpinner(); + let succeededPlugins = []; + const failedPlugins: { + [pluginName: string]: Error; + } = {}; + + for (const plugin of plugins) { + try { + spinner.start('Installing plugin ' + plugin); + await installPlugin(plugin, repoRoot, verbose, { + keepExistingVersions: true, + updatePackageScripts, + addPlugin: true, + skipFormat: false, + skipPackageJson: false, + }); + succeededPlugins.push(plugin); + spinner.succeed('Installed plugin ' + plugin); + } catch (e) { + failedPlugins[plugin] = e; + spinner.fail('Failed to install plugin ' + plugin); + } + } + + return { + succeededPlugins, + failedPlugins, + }; +} + +/** + * Configures plugins, installs them, and outputs the results + * @returns a list of succeeded plugins and a map of failed plugins to errors + */ +export async function configurePlugins( + plugins: string[], + updatePackageScripts: boolean, + pmc: PackageManagerCommands, + repoRoot: string = workspaceRoot, + verbose: boolean = false +): Promise<{ + succeededPlugins: string[]; + failedPlugins: { [plugin: string]: Error }; +}> { + if (plugins.length === 0) { + return { + succeededPlugins: [], + failedPlugins: {}, + }; + } + + output.log({ title: '🔨 Configuring plugins' }); + let { succeededPlugins, failedPlugins } = await installPlugins( + plugins, + updatePackageScripts, + repoRoot, + verbose + ); + + if (succeededPlugins.length > 0) { + output.success({ + title: 'Installed Plugins', + bodyLines: succeededPlugins.map((p) => `- ${bold(p)}`), + }); + } + if (Object.keys(failedPlugins).length > 0) { + output.error({ + title: `Failed to install plugins`, + bodyLines: [ + 'The following plugins were not installed:', + ...Object.keys(failedPlugins).map((p) => `- ${bold(p)}`), + ], + }); + Object.entries(failedPlugins).forEach(([plugin, error]) => { + output.error({ + title: `Failed to install ${plugin}`, + bodyLines: getFailedToInstallPluginErrorMessages(error), + }); + }); + output.error({ + title: `To install the plugins manually`, + bodyLines: [ + 'You may need to run commands to install the plugins:', + ...Object.keys(failedPlugins).map( + (p) => `- ${bold(pmc.exec + ' nx add ' + p)}` + ), + ], + }); + } + return { succeededPlugins, failedPlugins }; +} + +function findInitGenerator( + generators: Record +): string | undefined { + if (generators['init']) { + return 'init'; + } + + const angularPluginInstalled = isAngularPluginInstalled(); + if (angularPluginInstalled && generators['ng-add']) { + return 'ng-add'; + } + + return Object.keys(generators).find( + (name) => + generators[name].aliases?.includes('init') || + (angularPluginInstalled && generators[name].aliases?.includes('ng-add')) + ); +} + +export function getFailedToInstallPluginErrorMessages(e: any): string[] { + const errorBodyLines = []; + if (isProjectConfigurationsError(e) && e.errors.length > 0) { + for (const error of e.errors) { + if (isAggregateCreateNodesError(error)) { + const innerErrors = error.errors; + for (const [file, e] of innerErrors) { + if (file) { + errorBodyLines.push(` - ${bold(file)}: ${e.message}`); + } else { + errorBodyLines.push(` - ${e.message}`); + } + if (e.stack) { + const innerStackTrace = + ' ' + e.stack.split('\n')?.join('\n '); + errorBodyLines.push(innerStackTrace); + } + } + } else if (!isProjectsWithNoNameError(error)) { + // swallow ProjectsWithNameError + if (error.message) { + errorBodyLines.push(` - ${error.message}`); + } + if (error.stack) { + const innerStackTrace = + ' ' + error.stack.split('\n')?.join('\n '); + errorBodyLines.push(innerStackTrace); + } + } + } + } else { + if (e.message) { + errorBodyLines.push(` - ${e.message}`); + } + if (e.stack) { + const innerStackTrace = ' ' + e.stack.split('\n')?.join('\n '); + errorBodyLines.push(innerStackTrace); + } + } + return errorBodyLines; +} diff --git a/packages/nx/src/command-line/init/init-v2.ts b/packages/nx/src/command-line/init/init-v2.ts index 33e29ced07540..f9f0eb088bb69 100644 --- a/packages/nx/src/command-line/init/init-v2.ts +++ b/packages/nx/src/command-line/init/init-v2.ts @@ -1,26 +1,21 @@ import { existsSync } from 'fs'; + import { PackageJson } from '../../utils/package-json'; import { prerelease } from 'semver'; import { output } from '../../utils/output'; -import { - getPackageManagerCommand, - PackageManagerCommands, -} from '../../utils/package-manager'; +import { getPackageManagerCommand } from '../../utils/package-manager'; import { generateDotNxSetup } from './implementation/dot-nx/add-nx-scripts'; import { runNxSync } from '../../utils/child-process'; import { readJsonFile } from '../../utils/fileutils'; import { nxVersion } from '../../utils/versions'; import { - addDepsToPackageJson, createNxJsonFile, initCloud, isMonorepo, printFinalMessage, - runInstall, updateGitIgnore, } from './implementation/utils'; import { prompt } from 'enquirer'; -import { execSync } from 'child_process'; import { addNxToAngularCliRepo } from './implementation/angular'; import { globWithWorkspaceContextSync } from '../../utils/workspace-context'; import { connectExistingRepoToNxCloudPrompt } from '../connect/connect-to-nx-cloud'; @@ -28,41 +23,17 @@ import { addNxToNpmRepo } from './implementation/add-nx-to-npm-repo'; import { addNxToMonorepo } from './implementation/add-nx-to-monorepo'; import { NxJsonConfiguration, readNxJson } from '../../config/nx-json'; import { getPackageNameFromImportPath } from '../../utils/get-package-name-from-import-path'; +import { + configurePlugins, + runPackageManagerInstallPlugins, +} from './configure-plugins'; export interface InitArgs { interactive: boolean; nxCloud?: boolean; useDotNxInstallation?: boolean; integrated?: boolean; // For Angular projects only -} - -export function installPlugins( - repoRoot: string, - plugins: string[], - pmc: PackageManagerCommands, - updatePackageScripts: boolean -) { - if (plugins.length === 0) { - return; - } - - addDepsToPackageJson(repoRoot, plugins); - - runInstall(repoRoot, pmc); - - output.log({ title: '🔨 Configuring plugins' }); - for (const plugin of plugins) { - execSync( - `${pmc.exec} nx g ${plugin}:init --keepExistingVersions ${ - updatePackageScripts ? '--updatePackageScripts' : '' - }`, - { - stdio: [0, 1, 2], - cwd: repoRoot, - windowsHide: false, - } - ); - } + verbose?: boolean; } export async function initHandler(options: InitArgs): Promise { @@ -146,7 +117,14 @@ export async function initHandler(options: InitArgs): Promise { output.log({ title: '📦 Installing Nx' }); - installPlugins(repoRoot, plugins, pmc, updatePackageScripts); + runPackageManagerInstallPlugins(repoRoot, pmc, plugins); + await configurePlugins( + plugins, + updatePackageScripts, + pmc, + repoRoot, + options.verbose + ); if (useNxCloud) { output.log({ title: '🛠️ Setting up Nx Cloud' });