Skip to main content
Glama
nrwl

Nx MCP Server

Official
by nrwl
start-migration.ts8.66 kB
import { gt, gte, NxVersion } from '@nx-console/nx-version'; import { getNxWorkspacePath } from '@nx-console/vscode-configuration'; import { getNxVersion } from '@nx-console/vscode-nx-workspace'; import { logAndShowError } from '@nx-console/vscode-output-channels'; import { CliTask } from '@nx-console/vscode-tasks/src/lib/cli-task'; import { getPackageInfo, PackageInformationResponse, } from '@nx-console/vscode-utils'; import { join } from 'path'; import { major, rcompare } from 'semver'; import { QuickPickItem, tasks, window } from 'vscode'; import { viewPackageJsonDiff } from '../git-extension/view-diff'; import { importMigrateUIApi } from './utils'; import { existsSync, writeFileSync } from 'fs'; import { getTelemetry } from '@nx-console/vscode-telemetry'; export async function startMigration(custom = false) { getTelemetry().logUsage('migrate.start'); const nxVersion = await getNxVersion(); let pkgInfo: PackageInformationResponse; try { pkgInfo = await getPackageInfo('nx'); } catch (e) { logAndShowError( 'Failed to retrieve version information from npm', `Error while retrieving Nx version information from npm: \n ${e}`, ); return; } let flags = []; let versionToMigrateTo: string | undefined; if (custom) { versionToMigrateTo = await promptForVersion(nxVersion, pkgInfo); if (!versionToMigrateTo) { return; } const continueMigration = await checkAndConfirmMultipleMajors( nxVersion, versionToMigrateTo, pkgInfo, ); if (!continueMigration) { return; } const flagsObject = await promptForCustomFlags(); if (!flagsObject) { return; } flags = Object.entries(flagsObject).map( ([key, value]) => `--${key}=${value}`, ); } else { versionToMigrateTo = getDefaultMigrateVersion(nxVersion, pkgInfo); } if (!versionToMigrateTo) { return; } const workspacePath = getNxWorkspacePath(); const command = `nx migrate ${versionToMigrateTo}`; const task = await CliTask.create({ command: 'migrate', positional: versionToMigrateTo, flags, }); await tasks.executeTask(task); let success = false; await new Promise((resolve) => { tasks.onDidEndTaskProcess((taskEndEvent) => { if (taskEndEvent.execution.task.name === command) { if (taskEndEvent.exitCode === 0) { success = true; } resolve(true); } }); }); if (!success) { window.showErrorMessage( 'Migration failed, see integrated terminal for more details.', ); return; } const migrationJsonPath = join(workspacePath, 'migrations.json'); if (!existsSync(migrationJsonPath)) { writeFileSync(migrationJsonPath, '{}'); } const migrateUiApi = await importMigrateUIApi(workspacePath); migrateUiApi.recordInitialMigrationMetadata( workspacePath, versionToMigrateTo, ); viewPackageJsonDiff(); } // if latest is 20.x, do the following // current: 20.x -> latest // current: 19.x -> latest // current: 18.x -> 19 // current: 17.x -> 18 function getDefaultMigrateVersion( nxVersion: NxVersion, pkgInfo: PackageInformationResponse, ): string { const currentMajor = nxVersion.major; const latestMajor = major(pkgInfo['dist-tags']?.['latest']).toString(); if (!latestMajor) { return 'latest'; } if ( currentMajor === parseInt(latestMajor) || currentMajor === parseInt(latestMajor) - 1 ) { return 'latest'; } return (currentMajor + 1).toString(); } async function promptForVersion( nxVersion: NxVersion, pkgInfo: PackageInformationResponse, ) { const quickpickOptions: QuickPickItem[] = []; if (pkgInfo['dist-tags']?.['latest'] !== nxVersion.full) { quickpickOptions.push({ label: 'latest' }); } quickpickOptions.push({ label: 'next' }); const possibleVersions = Object.entries(pkgInfo.versions) .filter( ([versionNum, versionInfo]) => !versionInfo.deprecated && gt(versionNum, nxVersion) && !versionNum.startsWith('9999') && !versionNum.startsWith('0.0.0-pr') && !versionNum.includes('canary') && !versionNum.includes('beta') && !versionNum.includes('rc'), ) .sort(([a], [b]) => rcompare(a, b)); const existingMajorOptions = new Set<string>(); possibleVersions.forEach(([versionNum]) => { const versionMajor = major(versionNum).toString(); if (existingMajorOptions.has(versionMajor)) { return; } existingMajorOptions.add(versionMajor); quickpickOptions.push({ label: versionNum }); }); const quickPick = window.createQuickPick(); quickPick.items = [ ...quickpickOptions, { label: '', description: 'Start typing to install a custom version' }, ]; quickPick.onDidChangeValue((value: string) => { quickPick.items = [ ...quickpickOptions, { label: quickPick.value, description: value ? 'Install custom version' : 'Start typing to install a custom version', }, ]; }); quickPick.placeholder = 'Select a version to migrate to'; return await new Promise<string | undefined>((resolve) => { quickPick.show(); quickPick.onDidAccept(() => { resolve(quickPick.selectedItems[0]?.label); quickPick.hide(); quickPick.dispose(); }); quickPick.onDidHide(() => { resolve(undefined); }); }); } async function checkAndConfirmMultipleMajors( nxVersion: NxVersion, versionToMigrateTo: string, pkgInfo: PackageInformationResponse, ): Promise<boolean> { try { if (pkgInfo['dist-tags'] && pkgInfo['dist-tags'][versionToMigrateTo]) { versionToMigrateTo = pkgInfo['dist-tags'][versionToMigrateTo]; } const targetMajor = major(versionToMigrateTo); const currentMajor = major(nxVersion.full); if (!targetMajor || !currentMajor) { return true; } if (targetMajor > currentMajor + 1) { return await window .showQuickPick([{ label: 'Yes' }, { label: 'No' }], { title: `Version ${versionToMigrateTo} is more than one major version ahead of the ${nxVersion.full}. This is not supported and may cause issues.`, placeHolder: `Do you want to continue?`, }) .then((selection) => selection?.label === 'Yes'); } return true; } catch (e) { return true; } } type MigrateFlagsToPrompt = { from?: string; to?: string; interactive?: boolean; }; async function promptForCustomFlags( selectedOptions?: MigrateFlagsToPrompt, ): Promise<MigrateFlagsToPrompt | undefined> { if (!selectedOptions) { selectedOptions = {}; } const quickPick = window.createQuickPick(); quickPick.placeholder = 'Enter custom flags or start migration'; quickPick.items = [ { label: 'Start Migration', }, { label: '--from', description: selectedOptions.from, detail: 'Use the provided versions for packages instead of the ones installed in node_modules', }, { label: '--to', description: selectedOptions.to, detail: 'Use the provided versions for packages instead of the ones calculated by the migrator', }, { label: '--interactive', description: selectedOptions.interactive ? 'true' : 'false', detail: 'Enable prompts to confirm whether to collect optional package updates and migrations.', }, ]; quickPick.show(); const selection = await new Promise<string>((resolve) => { quickPick.onDidAccept(() => { resolve(quickPick.selectedItems[0]?.label); quickPick.hide(); quickPick.dispose(); }); }); if (!selection) { return undefined; } if (selection === 'Start Migration') { return selectedOptions; } if (selection === '--from') { const from = await window.showInputBox({ prompt: 'Enter the version to migrate from', value: selectedOptions.from, }); return await promptForCustomFlags({ ...selectedOptions, from }); } else if (selection === '--to') { const to = await window.showInputBox({ prompt: 'Enter the version to migrate to', value: selectedOptions.to, }); return await promptForCustomFlags({ ...selectedOptions, to }); } else if (selection === '--interactive') { const interactive = await window.showQuickPick( [{ label: 'true' }, { label: 'false' }], { placeHolder: 'Enable prompts to confirm whether to collect optional package updates and migrations.', }, ); return await promptForCustomFlags({ ...selectedOptions, interactive: Boolean(interactive), }); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nrwl/nx-console'

If you have feedback or need assistance with the MCP directory API, please join our Discord server